ModernUO/Distribution/Data/Spawns/post-uoml/termur/Abyss.json
Kamron Batman 2f8a7afa57 chore(spawners): migrate spawn data to $type discriminator
- Rename "type" -> "$type" (first key, required for STJ polymorphism) in
  all 109 spawn JSON files (5,612 records).
- Convert "homeRange" + "location" -> "spawnBounds" object, dropping homeRange.
  Runtime keeps reading homeRange so un-migrated/external files still load.
- Add BoundsEquivalenceTests (hr=0/1/3/7) validating that the script's toBounds
  formula produces the same Rectangle3D as the runtime homeRange path.
- Add MigratedDataLoadTests loading a real migrated file as List<SpawnerDto>
  and round-tripping each DTO through ToSpawner().
- Add tools/spawner-json-migrate/migrate.mjs (offline Node script).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 16:17:35 -07:00

1014 lines
18 KiB
JSON

[
{
"$type": "Spawner",
"guid": "35fb2ebe-b2e6-4a64-af3a-ce1eaacd9a54",
"name": "Spawner (601)",
"location": [
795,
753,
53
],
"map": "TerMur",
"count": 8,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 780,
"y1": 738,
"z1": -128,
"x2": 811,
"y2": 769,
"z2": 128
},
"walkingRange": 15,
"entries": [
{
"name": "Hydra",
"maxCount": 8,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "8d0bc27a-c900-4d7f-8811-a52f386b6d0c",
"name": "Spawner (601)",
"location": [
745,
739,
16
],
"map": "TerMur",
"count": 1,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 740,
"y1": 734,
"z1": -128,
"x2": 751,
"y2": 745,
"z2": 128
},
"walkingRange": 5,
"entries": [
{
"name": "MaddeningHorror",
"maxCount": 1,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "f7e9ab99-b970-4be1-881b-9e7ef1af0d01",
"name": "Spawner (601)",
"location": [
749,
475,
-17
],
"map": "TerMur",
"count": 1,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 729,
"y1": 455,
"z1": -128,
"x2": 770,
"y2": 496,
"z2": 128
},
"walkingRange": 20,
"entries": [
{
"name": "SlasherOfVeils",
"maxCount": 1,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "dd5aea6e-c13b-40f4-949f-9f41eac96589",
"name": "Spawner (601)",
"location": [
326,
159,
20
],
"map": "TerMur",
"count": 1,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 316,
"y1": 149,
"z1": -128,
"x2": 337,
"y2": 170,
"z2": 128
},
"walkingRange": 10,
"entries": [
{
"name": "StygianDragon",
"maxCount": 1,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "5c81b759-1add-4e8a-b151-420aa778e236",
"name": "Spawner (601)",
"location": [
716,
720,
-11
],
"map": "TerMur",
"count": 24,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 691,
"y1": 695,
"z1": -128,
"x2": 742,
"y2": 746,
"z2": 128
},
"walkingRange": 25,
"entries": [
{
"name": "Snake",
"maxCount": 24,
"probability": 100
},
{
"name": "LavaSnake",
"maxCount": 24,
"probability": 100
},
{
"name": "CoralSnake",
"maxCount": 24,
"probability": 100
},
{
"name": "GiantSerpent",
"maxCount": 24,
"probability": 100
},
{
"name": "Coil",
"maxCount": 24,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "a02bf178-9eb0-48ff-ae18-d06958f798a8",
"name": "Spawner (601)",
"location": [
992,
337,
9
],
"map": "TerMur",
"count": 35,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 972,
"y1": 317,
"z1": -128,
"x2": 1013,
"y2": 358,
"z2": 128
},
"walkingRange": 20,
"entries": [
{
"name": "GreaterMongbat",
"maxCount": 35,
"probability": 100
},
{
"name": "Imp",
"maxCount": 35,
"probability": 100
},
{
"name": "Daemon",
"maxCount": 35,
"probability": 100
},
{
"name": "PitFiend",
"maxCount": 35,
"probability": 100
},
{
"name": "DevourerRenowned",
"maxCount": 35,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "3929df23-08d5-4650-89a1-6cb025fddbac",
"name": "Spawner (601)",
"location": [
982,
491,
-12
],
"map": "TerMur",
"count": 15,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 964,
"y1": 473,
"z1": -128,
"x2": 1001,
"y2": 510,
"z2": 128
},
"walkingRange": 18,
"entries": [
{
"name": "ClanChitterAssistant",
"maxCount": 15,
"probability": 100
},
{
"name": "ClanChitterTinkerer",
"maxCount": 15,
"probability": 100
},
{
"name": "RakktaviRenowned",
"maxCount": 15,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "941cbed7-bf9a-448d-ae6a-f741459f50d1",
"name": "Spawner (601)",
"location": [
981,
491,
-12
],
"map": "TerMur",
"count": 16,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 963,
"y1": 473,
"z1": -128,
"x2": 1000,
"y2": 510,
"z2": 128
},
"walkingRange": 18,
"entries": [
{
"name": "ClockworkScorpion",
"maxCount": 16,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "517bfdd3-f625-4e1c-af4b-8467810e6e46",
"name": "Spawner (601)",
"location": [
919,
502,
-12
],
"map": "TerMur",
"count": 15,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 899,
"y1": 482,
"z1": -128,
"x2": 940,
"y2": 523,
"z2": 128
},
"walkingRange": 20,
"entries": [
{
"name": "ClanRibbonSupplicant",
"maxCount": 15,
"probability": 100
},
{
"name": "ClanRibbonCourtier",
"maxCount": 15,
"probability": 100
},
{
"name": "VitaviRenowned",
"maxCount": 15,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "592811d2-8196-4006-a774-5658b4df3ecb",
"name": "Spawner (601)",
"location": [
918,
502,
-12
],
"map": "TerMur",
"count": 15,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 898,
"y1": 482,
"z1": -128,
"x2": 939,
"y2": 523,
"z2": 128
},
"walkingRange": 20,
"entries": [
{
"name": "ClanRibbonPlagueRat",
"maxCount": 15,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "8ff19011-9cc1-4aaf-a795-1867c6667123",
"name": "Spawner (601)",
"location": [
949,
555,
-14
],
"map": "TerMur",
"count": 15,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 929,
"y1": 535,
"z1": -128,
"x2": 970,
"y2": 576,
"z2": 128
},
"walkingRange": 20,
"entries": [
{
"name": "ClanScratchScrounger",
"maxCount": 15,
"probability": 100
},
{
"name": "ClanScratchHenchrat",
"maxCount": 15,
"probability": 100
},
{
"name": "TikitaviRenowned",
"maxCount": 15,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "1b0b5739-68fc-4048-b2be-10da4dee13b8",
"name": "Spawner (601)",
"location": [
950,
555,
-14
],
"map": "TerMur",
"count": 18,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 930,
"y1": 535,
"z1": -128,
"x2": 971,
"y2": 576,
"z2": 128
},
"walkingRange": 20,
"entries": [
{
"name": "ClanScratchSavageWolf",
"maxCount": 18,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "c33dadb8-e340-4fc6-9944-627054cf96f6",
"name": "Spawner (601)",
"location": [
974,
161,
-11
],
"map": "TerMur",
"count": 18,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 949,
"y1": 136,
"z1": -128,
"x2": 1000,
"y2": 187,
"z2": 128
},
"walkingRange": 25,
"entries": [
{
"name": "FireAnt",
"maxCount": 18,
"probability": 100
},
{
"name": "LavaSnake",
"maxCount": 18,
"probability": 100
},
{
"name": "LavaLizard",
"maxCount": 18,
"probability": 100
},
{
"name": "FireGargoyle",
"maxCount": 18,
"probability": 100
},
{
"name": "Efreet",
"maxCount": 18,
"probability": 100
},
{
"name": "LavaElemental",
"maxCount": 18,
"probability": 100
},
{
"name": "FireDaemon",
"maxCount": 18,
"probability": 100
},
{
"name": "FireElementalRenowned",
"maxCount": 18,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "dcc3ba35-0e14-48fe-8a1b-8a7e5b3e4c03",
"name": "Spawner (601)",
"location": [
581,
815,
-45
],
"map": "TerMur",
"count": 30,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 556,
"y1": 790,
"z1": -128,
"x2": 607,
"y2": 841,
"z2": 128
},
"walkingRange": 25,
"entries": [
{
"name": "EnslavedGrayGoblin",
"maxCount": 30,
"probability": 100
},
{
"name": "EnslavedGreenGoblin",
"maxCount": 30,
"probability": 100
},
{
"name": "EnslavedGoblinScout",
"maxCount": 30,
"probability": 100
},
{
"name": "EnslavedGoblinKeeper",
"maxCount": 30,
"probability": 100
},
{
"name": "EnslavedGreenGoblinAlchemist",
"maxCount": 30,
"probability": 100
},
{
"name": "EnslavedGoblinMage",
"maxCount": 30,
"probability": 100
},
{
"name": "GrayGoblinMageRenowned",
"maxCount": 30,
"probability": 100
},
{
"name": "GreenGoblinAlchemistRenowned",
"maxCount": 30,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "6dfe8446-6623-4133-8990-6d11813b2b0d",
"name": "Spawner (601)",
"location": [
526,
766,
-92
],
"map": "TerMur",
"count": 25,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 506,
"y1": 746,
"z1": -128,
"x2": 547,
"y2": 787,
"z2": 128
},
"walkingRange": 20,
"entries": [
{
"name": "LavaLizard",
"maxCount": 25,
"probability": 100
},
{
"name": "LavaSnake",
"maxCount": 25,
"probability": 100
},
{
"name": "FireAnt",
"maxCount": 25,
"probability": 100
},
{
"name": "HellCat",
"maxCount": 25,
"probability": 100
},
{
"name": "LavaSerpent",
"maxCount": 25,
"probability": 100
},
{
"name": "HellHound",
"maxCount": 25,
"probability": 100
},
{
"name": "LavaElemental",
"maxCount": 25,
"probability": 100
},
{
"name": "FireDaemon",
"maxCount": 25,
"probability": 100
},
{
"name": "FireDaemonRenowned",
"maxCount": 25,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "6d71c735-e0cb-414d-b687-10c1a4192856",
"name": "Spawner (601)",
"location": [
536,
657,
8
],
"map": "TerMur",
"count": 35,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 502,
"y1": 623,
"z1": -128,
"x2": 571,
"y2": 692,
"z2": 128
},
"walkingRange": 34,
"entries": [
{
"name": "Skeleton",
"maxCount": 35,
"probability": 100
},
{
"name": "Wraith",
"maxCount": 35,
"probability": 100
},
{
"name": "SkeletalMage",
"maxCount": 35,
"probability": 100
},
{
"name": "WailingBanshee",
"maxCount": 35,
"probability": 100
},
{
"name": "SkeletalLich",
"maxCount": 35,
"probability": 100
},
{
"name": "RottingCorpse",
"maxCount": 35,
"probability": 100
},
{
"name": "AncientLichRenowned",
"maxCount": 35,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "29d4421f-924d-47c3-abe5-45fe5e1be9a5",
"name": "Spawner (601)",
"location": [
601,
905,
-60
],
"map": "TerMur",
"count": 38,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 566,
"y1": 870,
"z1": -128,
"x2": 637,
"y2": 941,
"z2": 128
},
"walkingRange": 35,
"entries": [
{
"name": "LavaLizard",
"maxCount": 38,
"probability": 100
},
{
"name": "LavaSnake",
"maxCount": 38,
"probability": 100
},
{
"name": "FireAnt",
"maxCount": 38,
"probability": 100
},
{
"name": "HellHound",
"maxCount": 38,
"probability": 100
},
{
"name": "LavaSerpent",
"maxCount": 38,
"probability": 100
},
{
"name": "LavaElemental",
"maxCount": 38,
"probability": 100
},
{
"name": "FireDaemon",
"maxCount": 38,
"probability": 100
},
{
"name": "FireDaemonRenowned",
"maxCount": 38,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "7dac51b3-7c0a-4f22-8652-7e29ba875fee",
"name": "Spawner (601)",
"location": [
818,
927,
-15
],
"map": "TerMur",
"count": 1,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 803,
"y1": 912,
"z1": -128,
"x2": 834,
"y2": 943,
"z2": 128
},
"walkingRange": 15,
"entries": [
{
"name": "Medusa",
"maxCount": 1,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "c64053fc-272a-4fdd-9c6c-4f05a1ec02be",
"name": "Spawner (601)",
"location": [
684,
579,
-15
],
"map": "TerMur",
"count": 32,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 649,
"y1": 544,
"z1": -128,
"x2": 720,
"y2": 615,
"z2": 128
},
"walkingRange": 35,
"entries": [
{
"name": "CorrosiveSlime",
"maxCount": 32,
"probability": 100
},
{
"name": "AcidSlug",
"maxCount": 32,
"probability": 100
},
{
"name": "AcidElemental",
"maxCount": 32,
"probability": 100
},
{
"name": "InterredGrizzle",
"maxCount": 32,
"probability": 100
},
{
"name": "AcidElementalRenowned",
"maxCount": 32,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "a72aa352-96b2-4163-83c0-de180a8fb7e8",
"name": "Spawner (601)",
"location": [
440,
708,
24
],
"map": "TerMur",
"count": 30,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 405,
"y1": 673,
"z1": -128,
"x2": 476,
"y2": 744,
"z2": 128
},
"walkingRange": 35,
"entries": [
{
"name": "Pixie",
"maxCount": 30,
"probability": 100
},
{
"name": "Wisp",
"maxCount": 30,
"probability": 100
},
{
"name": "DarkWisp",
"maxCount": 30,
"probability": 100
},
{
"name": "PixieRenowned",
"maxCount": 30,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "875d5be5-9156-4e42-a41a-973aef764aee",
"name": "Spawner (601)",
"location": [
674,
828,
-109
],
"map": "TerMur",
"count": 30,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 649,
"y1": 803,
"z1": -128,
"x2": 700,
"y2": 854,
"z2": 128
},
"walkingRange": 25,
"entries": [
{
"name": "Skeleton",
"maxCount": 30,
"probability": 100
},
{
"name": "PatchworkSkeleton",
"maxCount": 30,
"probability": 100
},
{
"name": "SkeletalKnight",
"maxCount": 30,
"probability": 100
},
{
"name": "BoneKnight",
"maxCount": 30,
"probability": 100
},
{
"name": "SkeletalMage",
"maxCount": 30,
"probability": 100
},
{
"name": "BoneMagi",
"maxCount": 30,
"probability": 100
},
{
"name": "SkeletalLich",
"maxCount": 30,
"probability": 100
},
{
"name": "SkeletalDragonRenowned",
"maxCount": 30,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "11551b29-0ee5-4fae-8d9b-2fd60465b032",
"name": "Spawner (601)",
"location": [
887,
277,
3
],
"map": "TerMur",
"count": 35,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 867,
"y1": 257,
"z1": -128,
"x2": 908,
"y2": 298,
"z2": 128
},
"walkingRange": 20,
"entries": [
{
"name": "FairyDragon",
"maxCount": 35,
"probability": 100
},
{
"name": "Wyvern",
"maxCount": 35,
"probability": 100
},
{
"name": "ForgottenServant",
"maxCount": 35,
"probability": 100
},
{
"name": "WyvernRenowned",
"maxCount": 35,
"probability": 100
}
]
}
]