## Summary Removes the dated `DynamicJson` JSON helper and migrates spawner JSON (de)serialization to a polymorphic `record SpawnerDto` hierarchy. `DynamicJson` was the last remaining consumer (regions moved off it in #1400). The key correctness improvement: **System.Text.Json deserializes plain DTO records, never a live `Item`.** Previously, deserializing directly into an `Item` meant STJ constructed world-registered objects *before* the data was validated — a malformed/hand-edited spawn file could leave orphaned spawner Items in the world save. Now a parse failure is GC-only; `dto.ToSpawner()` constructs the spawner only from a fully-validated DTO and self-cleans on failure. ## What changed - **New:** `SpawnerDto` (abstract) + `SpawnerDataDto` / `RegionSpawnerDto` / `ProximitySpawnerDto`, each marked with a reusable `[JsonDiscoverableType]` opt-in attribute. Auto-discovered at the `Configure` phase — no manual registration list (avoids the regions `Register<T>()` footgun), open to custom spawner subtypes. - **Symmetric mapping:** `BaseSpawner.ToDto()` (export) ⇄ `SpawnerDto.ToSpawner()` (import). `ToSpawner()` deletes-and-rethrows on any failure, so the importer can never orphan an Item. - **`SpawnerJsonSerializer`** wires `$type` polymorphism on the `SpawnerDto` root with loud collision/constructibility validation. - **Import/export commands** rewired to the typed DTO path (reflection `FindTypeByName`/`CreateInstance` removed). - **Data migration:** the 109 `Distribution/Data/Spawns/**` files moved from `"type"`→`"$type"` and legacy `homeRange`→`spawnBounds`. The runtime still *reads* legacy `homeRange` for external files. The `homeRange→spawnBounds` formula is proven equivalent to the runtime conversion (`BoundsEquivalenceTests`, hr=0/1/3/7). - **Deleted:** `Projects/Server/Json/DynamicJson.cs`. Sparse export output matches the legacy `ToJson` (nullable DTO properties + `WhenWritingNull`). Binary world-save serialization is untouched. ## Tests - DTO round-trip per spawner type; sparse-default omission; legacy `homeRange` read; export/import file round-trip. - `Import_MalformedFile_LeaksNoWorldItems` — proves a mid-array parse failure constructs zero world Items. - `AllSpawnFilesLoadTests` — every migrated spawn file deserializes and builds. - Duplicate-discriminator validation. - UOContent.Tests 485/485, Server.Tests 710/710, build clean. ## Follow-up (not in this PR) `Rectangle3DConverter.Write` (in `Projects/Server/`) omits `z1/z2` when `Start.Z == -128`, so a future server-side export of a `homeRange`-style spawner round-trips depth 256→255. Pre-existing and out of scope here (Server change); the migrated data reads correctly. Worth a separate small converter PR.
702 lines
17 KiB
JSON
702 lines
17 KiB
JSON
[
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "04207c29-fe38-4d43-a08d-8645a3a312e8",
|
|
"name": "Spawner (205)",
|
|
"location": [5441, 680, 20],
|
|
"map": "Felucca",
|
|
"count": 6,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "EarthElemental", "maxCount": 6, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "05844644-3d42-4021-bb28-8114d8f7f064",
|
|
"name": "Spawner (205)",
|
|
"location": [5420, 746, 5],
|
|
"map": "Felucca",
|
|
"count": 10,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 18,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "EarthElemental", "maxCount": 4, "probability": 100 },
|
|
{ "name": "Ettin", "maxCount": 6, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "0a6bf903-a9a3-4252-a4a0-652d89fb9381",
|
|
"name": "Spawner (205)",
|
|
"location": [5528, 902, 30],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 15,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Ogre", "maxCount": 3, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "11e8cbb5-45dd-4109-8606-54eb9bada453",
|
|
"name": "Spawner (205)",
|
|
"location": [5460, 526, 60],
|
|
"map": "Felucca",
|
|
"count": 5,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Lizardman", "maxCount": 5, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "1855dd2e-81b1-4326-bc97-f3e50884e09a",
|
|
"name": "Spawner (205)",
|
|
"location": [5391, 905, 30],
|
|
"map": "Felucca",
|
|
"count": 4,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 15,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Ettin", "maxCount": 4, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "1adcf3d5-536f-473d-af18-4ae23415ebba",
|
|
"name": "Spawner (205)",
|
|
"location": [5505, 747, 5],
|
|
"map": "Felucca",
|
|
"count": 5,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 18,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "EarthElemental", "maxCount": 3, "probability": 100 },
|
|
{ "name": "Ettin", "maxCount": 2, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "21b9e0f9-7627-4967-b7bf-5453be3e0266",
|
|
"name": "Spawner (205)",
|
|
"location": [5470, 860, 45],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Troll", "maxCount": 3, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "310b722e-fe5d-4692-97c7-956d15b76472",
|
|
"name": "Spawner (205)",
|
|
"location": [5482, 791, 60],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Troll", "maxCount": 3, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "38967322-06dc-48bc-bed1-6dd60661b1c0",
|
|
"name": "Spawner (205)",
|
|
"location": [5387, 697, 15],
|
|
"map": "Felucca",
|
|
"count": 5,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 5,
|
|
"walkingRange": 5,
|
|
"entries": [
|
|
{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 },
|
|
{ "name": "TreasureChestLevel3", "maxCount": 4, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "38fe4fe8-2f5d-441f-8f4b-8f679a955e96",
|
|
"name": "Spawner (205)",
|
|
"location": [5546, 888, 30],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 8,
|
|
"walkingRange": 8,
|
|
"entries": [
|
|
{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 },
|
|
{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "3d6315d9-3968-4b92-b71a-99a12bab509e",
|
|
"name": "Spawner (205)",
|
|
"location": [5590, 871, 45],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 20,
|
|
"walkingRange": 35,
|
|
"entries": [
|
|
{ "name": "Ogre", "maxCount": 3, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "441cff6e-d90b-4a74-bd0a-b10165fd6eb4",
|
|
"name": "Spawner (205)",
|
|
"location": [5426, 943, 20],
|
|
"map": "Felucca",
|
|
"count": 4,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 15,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Ettin", "maxCount": 4, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "4780e4d1-5a3a-4519-a8db-3cc6e77648c7",
|
|
"name": "Spawner (205)",
|
|
"location": [5398, 996, 5],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 15,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "AcidElemental", "maxCount": 3, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "4d00942a-33e2-4714-8d03-9d6a40778772",
|
|
"name": "Spawner (205)",
|
|
"location": [5390, 587, 45],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 5,
|
|
"walkingRange": 5,
|
|
"entries": [
|
|
{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 },
|
|
{ "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "51d69140-0708-491e-9dbd-9c5d58349172",
|
|
"name": "Spawner (205)",
|
|
"location": [5504, 939, 20],
|
|
"map": "Felucca",
|
|
"count": 4,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 15,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Ettin", "maxCount": 4, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "5c5b2844-5792-434b-9d9e-e5f607f32b6b",
|
|
"name": "Spawner (205)",
|
|
"location": [5439, 851, 45],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Troll", "maxCount": 3, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "6a01c304-d63d-4a59-8b00-d004329d1af7",
|
|
"name": "Spawner (205)",
|
|
"location": [5483, 711, 15],
|
|
"map": "Felucca",
|
|
"count": 4,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Ettin", "maxCount": 4, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "6b94a3e0-7977-459b-bc5f-6c13ce0062ff",
|
|
"name": "Spawner (205)",
|
|
"location": [5405, 576, 61],
|
|
"map": "Felucca",
|
|
"count": 5,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Lizardman", "maxCount": 5, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "70c7a94c-7535-432d-917a-640d0a6f47ff",
|
|
"name": "Spawner (205)",
|
|
"location": [5605, 799, 60],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 10,
|
|
"walkingRange": 10,
|
|
"entries": [
|
|
{ "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "74032d64-a50f-4f99-ad45-3d9ad9f1d718",
|
|
"name": "Spawner (205)",
|
|
"location": [5410, 611, 45],
|
|
"map": "Felucca",
|
|
"count": 5,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Lizardman", "maxCount": 5, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "74fc3f59-8ee2-4ee5-b246-ddf07c415ca5",
|
|
"name": "Spawner (205)",
|
|
"location": [5557, 828, 45],
|
|
"map": "Felucca",
|
|
"count": 1,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 7,
|
|
"walkingRange": 7,
|
|
"entries": [
|
|
{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "7573ab39-270e-45f0-99db-4304837ef7bb",
|
|
"name": "Spawner (205)",
|
|
"location": [5487, 902, 30],
|
|
"map": "Felucca",
|
|
"count": 4,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 15,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Ettin", "maxCount": 4, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "7c71f2a8-2d6b-40b8-8dca-9eca3aa54d47",
|
|
"name": "Spawner (205)",
|
|
"location": [5397, 995, 5],
|
|
"map": "Felucca",
|
|
"count": 1,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 10,
|
|
"walkingRange": 10,
|
|
"entries": [
|
|
{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "7ec31075-3cef-4d6b-a20e-2c55540052fc",
|
|
"name": "Spawner (205)",
|
|
"location": [5391, 660, 20],
|
|
"map": "Felucca",
|
|
"count": 4,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Ettin", "maxCount": 4, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "831675ca-f594-4e7e-bd6f-b0f15ed60bf0",
|
|
"name": "Spawner (205)",
|
|
"location": [5504, 597, 45],
|
|
"map": "Felucca",
|
|
"count": 5,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Lizardman", "maxCount": 5, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "8e65ea3f-2bca-43cd-b6e2-916b8625d69b",
|
|
"name": "Spawner (205)",
|
|
"location": [5398, 995, 5],
|
|
"map": "Felucca",
|
|
"count": 2,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 10,
|
|
"walkingRange": 10,
|
|
"entries": [
|
|
{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "93356ad6-8774-42cd-ab96-705c2d55e109",
|
|
"name": "Spawner (205)",
|
|
"location": [5604, 789, 60],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 15,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Ogre", "maxCount": 3, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "9dbd8c5c-40d7-4908-8fbd-86cd1f149be0",
|
|
"name": "Spawner (205)",
|
|
"location": [5503, 529, 60],
|
|
"map": "Felucca",
|
|
"count": 7,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Lizardman", "maxCount": 5, "probability": 100 },
|
|
{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 },
|
|
{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "9df386c1-05f8-4290-960b-8d0b6f0393ce",
|
|
"name": "Spawner (205)",
|
|
"location": [5593, 794, 60],
|
|
"map": "Felucca",
|
|
"count": 1,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 5,
|
|
"walkingRange": 5,
|
|
"entries": [
|
|
{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "9e9bcade-4455-4c85-b7f2-4d9dc6e6c6f4",
|
|
"name": "Spawner (205)",
|
|
"location": [5394, 525, 65],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 5,
|
|
"walkingRange": 5,
|
|
"entries": [
|
|
{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 },
|
|
{ "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "9f576fea-09aa-47fb-a3e4-a1b8369b650e",
|
|
"name": "Spawner (205)",
|
|
"location": [5386, 612, 45],
|
|
"map": "Felucca",
|
|
"count": 8,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Lizardman", "maxCount": 5, "probability": 100 },
|
|
{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 },
|
|
{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "b328679f-d212-49e8-8900-af2cd1f77b04",
|
|
"name": "Spawner (205)",
|
|
"location": [5564, 821, 45],
|
|
"map": "Felucca",
|
|
"count": 2,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 5,
|
|
"walkingRange": 5,
|
|
"entries": [
|
|
{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "b453194c-94e8-4d58-87cc-3870d6292247",
|
|
"name": "Spawner (205)",
|
|
"location": [5507, 656, 20],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 5,
|
|
"walkingRange": 5,
|
|
"entries": [
|
|
{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 },
|
|
{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "ba7fd9a9-bffc-4f04-8606-ae79b4bb830b",
|
|
"name": "Spawner (205)",
|
|
"location": [5411, 541, 60],
|
|
"map": "Felucca",
|
|
"count": 5,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Lizardman", "maxCount": 5, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "bb7fc3f7-d80d-44c5-8a63-743a970f5842",
|
|
"name": "Spawner (205)",
|
|
"location": [5385, 703, 15],
|
|
"map": "Felucca",
|
|
"count": 4,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 15,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Ettin", "maxCount": 4, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "c045d499-1fe2-4afa-be89-e8e5f3c6f4f8",
|
|
"name": "Spawner (205)",
|
|
"location": [5475, 748, 5],
|
|
"map": "Felucca",
|
|
"count": 5,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "EarthElemental", "maxCount": 3, "probability": 100 },
|
|
{ "name": "Ettin", "maxCount": 2, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "c739ce37-7e4b-4a3a-886b-4f45a049cde6",
|
|
"name": "Spawner (205)",
|
|
"location": [5558, 824, 45],
|
|
"map": "Felucca",
|
|
"count": 5,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "OgreLord", "maxCount": 5, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "c99bb2a1-1690-4656-95d7-d61b0c5640c8",
|
|
"name": "Spawner (205)",
|
|
"location": [5490, 676, 20],
|
|
"map": "Felucca",
|
|
"count": 4,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Ettin", "maxCount": 4, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "cb91b20f-4aee-45c7-b70b-de22c9adcdae",
|
|
"name": "Spawner (205)",
|
|
"location": [5471, 973, 15],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 18,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Cyclops", "maxCount": 3, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "d58f315e-8fcf-4282-a4e4-5aaed9c19cd5",
|
|
"name": "Spawner (205)",
|
|
"location": [5532, 787, 60],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 15,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Ogre", "maxCount": 3, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "f0922369-d73d-4c7e-a669-28812e0a0559",
|
|
"name": "Spawner (205)",
|
|
"location": [5435, 589, 47],
|
|
"map": "Felucca",
|
|
"count": 5,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Lizardman", "maxCount": 5, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "f8f62c01-5bf8-41dc-ac7a-37cc7a97134b",
|
|
"name": "Spawner (205)",
|
|
"location": [5426, 791, 60],
|
|
"map": "Felucca",
|
|
"count": 3,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Troll", "maxCount": 3, "probability": 100 }
|
|
]
|
|
},
|
|
{
|
|
"$type": "Spawner",
|
|
"guid": "fb3ee679-fc60-4abb-b6b0-a809f77ca53b",
|
|
"name": "Spawner (205)",
|
|
"location": [5464, 600, 45],
|
|
"map": "Felucca",
|
|
"count": 5,
|
|
"minDelay": "00:05:00",
|
|
"maxDelay": "00:10:00",
|
|
"team": 0,
|
|
"homeRange": 12,
|
|
"walkingRange": 25,
|
|
"entries": [
|
|
{ "name": "Lizardman", "maxCount": 5, "probability": 100 }
|
|
]
|
|
}
|
|
]
|