ModernUO/Distribution/Data/Spawns/shared/malas/Citadel.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

572 lines
10 KiB
JSON

[
{
"$type": "Spawner",
"guid": "114d80f2-07e6-49e3-9418-6e7c30291028",
"name": "Spawner (406)",
"location": [
142,
1921,
0
],
"map": "Malas",
"count": 4,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 137,
"y1": 1916,
"z1": -128,
"x2": 148,
"y2": 1927,
"z2": 128
},
"walkingRange": 5,
"entries": [
{
"name": "tigersclawthief",
"maxCount": 4,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "15b169f9-fe9e-4ef7-973b-cb0619b9937d",
"name": "Spawner (406)",
"location": [
82,
1875,
0
],
"map": "Malas",
"count": 4,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 77,
"y1": 1870,
"z1": -128,
"x2": 88,
"y2": 1881,
"z2": 128
},
"walkingRange": 5,
"entries": [
{
"name": "serpentsfangassassin",
"maxCount": 4,
"probability": 100
},
{
"name": "eliteninjawarrior",
"maxCount": 4,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "17abd9d2-3b7a-44ec-a724-57c202236788",
"name": "Spawner (406)",
"location": [
179,
1869,
0
],
"map": "Malas",
"count": 4,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 169,
"y1": 1859,
"z1": -128,
"x2": 190,
"y2": 1880,
"z2": 128
},
"walkingRange": 10,
"entries": [
{
"name": "serpentsfangassassin",
"maxCount": 4,
"probability": 100
},
{
"name": "tigersclawthief",
"maxCount": 4,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "28535caf-b979-4745-8a87-e62691d7cb11",
"name": "Spawner (406)",
"location": [
145,
1898,
0
],
"map": "Malas",
"count": 3,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 120,
"y1": 1873,
"z1": -128,
"x2": 171,
"y2": 1924,
"z2": 128
},
"walkingRange": 25,
"entries": [
{
"name": "serpentsfangassassin",
"maxCount": 3,
"probability": 100
},
{
"name": "tigersclawthief",
"maxCount": 3,
"probability": 100
},
{
"name": "eliteninjawarrior",
"maxCount": 3,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "2d629e57-63db-4610-b6d2-cf3bfd34f0b0",
"name": "Spawner (406)",
"location": [
181,
1883,
0
],
"map": "Malas",
"count": 8,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 176,
"y1": 1878,
"z1": -128,
"x2": 187,
"y2": 1889,
"z2": 128
},
"walkingRange": 5,
"entries": [
{
"name": "serpentsfangassassin",
"maxCount": 8,
"probability": 100
},
{
"name": "tigersclawthief",
"maxCount": 8,
"probability": 100
},
{
"name": "dragonsflamemage",
"maxCount": 8,
"probability": 100
},
{
"name": "eliteninjawarrior",
"maxCount": 8,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "347e85d2-c752-4abf-86c7-bda2a2051033",
"name": "Spawner (406)",
"location": [
183,
1961,
0
],
"map": "Malas",
"count": 2,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 163,
"y1": 1941,
"z1": -128,
"x2": 204,
"y2": 1982,
"z2": 128
},
"walkingRange": 20,
"entries": [
{
"name": "dragonsflamemage",
"maxCount": 2,
"probability": 100
},
{
"name": "serpentsfangassassin",
"maxCount": 2,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "364cc553-16d0-428d-bf97-061c3ddfd120",
"name": "Spawner (406)",
"location": [
170,
1974,
0
],
"map": "Malas",
"count": 3,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 165,
"y1": 1969,
"z1": -128,
"x2": 176,
"y2": 1980,
"z2": 128
},
"walkingRange": 5,
"entries": [
{
"name": "serpentsfangassassin",
"maxCount": 3,
"probability": 100
},
{
"name": "serpentsfanghighexecutioner",
"maxCount": 3,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "435ce67b-aad4-4ed0-b621-73ab4b6b0042",
"name": "Spawner (406)",
"location": [
81,
1892,
0
],
"map": "Malas",
"count": 4,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 76,
"y1": 1887,
"z1": -128,
"x2": 87,
"y2": 1898,
"z2": 128
},
"walkingRange": 5,
"entries": [
{
"name": "eliteninjawarrior",
"maxCount": 4,
"probability": 100
},
{
"name": "serpentsfangassassin",
"maxCount": 4,
"probability": 100
},
{
"name": "dragonsflamemage",
"maxCount": 4,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "4854c18d-7bf2-4f8c-a69f-7679204b6d7d",
"name": "Spawner (406)",
"location": [
155,
1869,
0
],
"map": "Malas",
"count": 4,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 145,
"y1": 1859,
"z1": -128,
"x2": 166,
"y2": 1880,
"z2": 128
},
"walkingRange": 10,
"entries": [
{
"name": "serpentsfangassassin",
"maxCount": 4,
"probability": 100
},
{
"name": "dragonsflamemage",
"maxCount": 4,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "50c87975-6021-46cb-bbe1-58c45ba776b7",
"name": "Spawner (406)",
"location": [
77,
1883,
0
],
"map": "Malas",
"count": 4,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 72,
"y1": 1878,
"z1": -128,
"x2": 83,
"y2": 1889,
"z2": 128
},
"walkingRange": 5,
"entries": [
{
"name": "dragonsflamemage",
"maxCount": 4,
"probability": 100
},
{
"name": "eliteninjawarrior",
"maxCount": 4,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "856c181e-183a-4231-acc8-5146301c56c6",
"name": "Spawner (406)",
"location": [
185,
1920,
0
],
"map": "Malas",
"count": 2,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 178,
"y1": 1913,
"z1": -128,
"x2": 193,
"y2": 1928,
"z2": 128
},
"walkingRange": 7,
"entries": [
{
"name": "magedragonsflamemage",
"maxCount": 2,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "9f02ef97-7699-4a65-8cd6-615aa50cc318",
"name": "Spawner (406)",
"location": [
139,
1868,
0
],
"map": "Malas",
"count": 7,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 131,
"y1": 1860,
"z1": -128,
"x2": 148,
"y2": 1877,
"z2": 128
},
"walkingRange": 8,
"entries": [
{
"name": "serpentsfangassassin",
"maxCount": 7,
"probability": 100
},
{
"name": "tigersclawthief",
"maxCount": 7,
"probability": 100
},
{
"name": "magedragonsflamemage",
"maxCount": 7,
"probability": 100
},
{
"name": "dragonsflamemage",
"maxCount": 7,
"probability": 100
},
{
"name": "eliteninjawarrior",
"maxCount": 7,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "b5fed5a1-35db-4f09-b4b6-8017146a8bf3",
"name": "Spawner (406)",
"location": [
174,
1900,
0
],
"map": "Malas",
"count": 5,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 154,
"y1": 1880,
"z1": -128,
"x2": 195,
"y2": 1921,
"z2": 128
},
"walkingRange": 20,
"entries": [
{
"name": "serpentsfangassassin",
"maxCount": 5,
"probability": 100
},
{
"name": "dragonsflamemage",
"maxCount": 5,
"probability": 100
},
{
"name": "eliteninjawarrior",
"maxCount": 5,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "dccae74d-bafa-40ff-9984-7a2a0af5d8de",
"name": "Spawner (406)",
"location": [
140,
1974,
0
],
"map": "Malas",
"count": 2,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 132,
"y1": 1966,
"z1": -128,
"x2": 149,
"y2": 1983,
"z2": 128
},
"walkingRange": 8,
"entries": [
{
"name": "magedragonsflamemage",
"maxCount": 2,
"probability": 100
},
{
"name": "serpentsfangassassin",
"maxCount": 2,
"probability": 100
}
]
},
{
"$type": "Spawner",
"guid": "fb6ee275-c5c2-4780-9180-76e3a4ac1a97",
"name": "Spawner (406)",
"location": [
134,
1947,
0
],
"map": "Malas",
"count": 2,
"minDelay": "00:05:00",
"maxDelay": "00:10:00",
"team": 0,
"spawnBounds": {
"x1": 127,
"y1": 1940,
"z1": -128,
"x2": 142,
"y2": 1955,
"z2": 128
},
"walkingRange": 7,
"entries": [
{
"name": "magedragonsflamemage",
"maxCount": 2,
"probability": 100
},
{
"name": "dragonsflamemage",
"maxCount": 2,
"probability": 100
}
]
}
]