Convert Regions/Spawns to JSON (#138)

This commit is contained in:
Kamron Batman 2020-05-26 00:34:29 -07:00 committed by GitHub
parent b8316e9202
commit 390f30e706
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
117 changed files with 97306 additions and 4848 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<spawnDefinitions>
<spawnGroup name="Escortables">
<object type="EscortableMage" />
<object type="Noble" />
<object type="SeekerOfAdventure" />
</spawnGroup>
<spawnGroup name="TownAnimals">
<object type="Cat" />
<object type="Dog" />
</spawnGroup>
<spawnGroup name="Reagents">
<object type="BlackPearl" />
<object type="Bloodmoss" />
<object type="Garlic" />
<object type="Ginseng" />
<object type="MandrakeRoot" />
<object type="Nightshade" />
<object type="SulfurousAsh" />
<object type="SpidersSilk" />
</spawnGroup>
</spawnDefinitions>

View file

@ -0,0 +1,339 @@
[
{
"type": "Spawner",
"location": [117, 1740, 0],
"map": "TerMur",
"count": 17,
"entries": [{ "maxCount": 3, "name": "GiantBlackWidow", "probability": 100 }],
"homeRange": 9,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [122, 1805, 0],
"map": "TerMur",
"count": 15,
"entries": [{ "maxCount": 2, "name": "GiantBlackWidow", "probability": 100 }],
"homeRange": 18,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [126, 1692, 0],
"map": "TerMur",
"count": 12,
"entries": [{ "maxCount": 2, "name": "GiantBlackWidow", "probability": 100 }],
"homeRange": 15,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [175, 1861, 0],
"map": "TerMur",
"count": 8,
"entries": [{ "maxCount": 1, "name": "GiantBlackWidow", "probability": 100 }],
"homeRange": 20,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [267, 1433, 40],
"map": "TerMur",
"count": 6,
"entries": [
{ "maxCount": 1, "name": "Alligator", "probability": 100 },
{ "maxCount": 1, "name": "BullFrog", "probability": 100 },
{ "maxCount": 1, "name": "GiantToad", "probability": 100 },
{ "maxCount": 1, "name": "Corpser", "probability": 100 },
{ "maxCount": 2, "name": "AcidElemental", "probability": 100 }
],
"homeRange": 30,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [286, 1804, 80],
"map": "TerMur",
"count": 7,
"entries": [
{ "maxCount": 4, "name": "VampireBat", "probability": 100 },
{ "maxCount": 3, "name": "TropicalBird", "probability": 100 }
],
"homeRange": 30,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [319, 1502, 40],
"map": "TerMur",
"count": 6,
"entries": [
{ "maxCount": 1, "name": "Alligator", "probability": 100 },
{ "maxCount": 1, "name": "BullFrog", "probability": 100 },
{ "maxCount": 1, "name": "GiantToad", "probability": 100 },
{ "maxCount": 1, "name": "Corpser", "probability": 100 },
{ "maxCount": 2, "name": "AcidElemental", "probability": 100 }
],
"homeRange": 30,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [341, 1418, 40],
"map": "TerMur",
"count": 6,
"entries": [
{ "maxCount": 1, "name": "Alligator", "probability": 100 },
{ "maxCount": 1, "name": "BullFrog", "probability": 100 },
{ "maxCount": 1, "name": "GiantToad", "probability": 100 },
{ "maxCount": 1, "name": "Corpser", "probability": 100 },
{ "maxCount": 2, "name": "AcidElemental", "probability": 100 }
],
"homeRange": 25,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [347, 1911, 0],
"map": "TerMur",
"count": 10,
"entries": [
{ "maxCount": 6, "name": "TropicalBird", "probability": 100 },
{ "maxCount": 3, "name": "Corpser", "probability": 100 },
{ "maxCount": 4, "name": "Panther", "probability": 100 },
{ "maxCount": 4, "name": "ForestOstard", "probability": 100 }
],
"homeRange": 25,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [355, 1676, 40],
"map": "TerMur",
"count": 4,
"entries": [{ "maxCount": 4, "name": "SandVortex", "probability": 100 }],
"homeRange": 15,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [398, 1896, 0],
"map": "TerMur",
"count": 10,
"entries": [
{ "maxCount": 6, "name": "TropicalBird", "probability": 100 },
{ "maxCount": 3, "name": "Corpser", "probability": 100 },
{ "maxCount": 4, "name": "Panther", "probability": 100 },
{ "maxCount": 4, "name": "ForestOstard", "probability": 100 }
],
"homeRange": 25,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [424, 1404, 40],
"map": "TerMur",
"count": 19,
"entries": [
{ "maxCount": 5, "name": "Alligator", "probability": 100 },
{ "maxCount": 5, "name": "BullFrog", "probability": 100 },
{ "maxCount": 5, "name": "GiantToad", "probability": 100 },
{ "maxCount": 3, "name": "Corpser", "probability": 100 }
],
"homeRange": 65,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [454, 1895, 0],
"map": "TerMur",
"count": 10,
"entries": [
{ "maxCount": 6, "name": "TropicalBird", "probability": 100 },
{ "maxCount": 3, "name": "Corpser", "probability": 100 },
{ "maxCount": 4, "name": "Panther", "probability": 100 },
{ "maxCount": 4, "name": "ForestOstard", "probability": 100 }
],
"homeRange": 22,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [506, 1582, 40],
"map": "TerMur",
"count": 17,
"entries": [
{ "maxCount": 9, "name": "TropicalBird", "probability": 100 },
{ "maxCount": 5, "name": "VampireBat", "probability": 100 },
{ "maxCount": 3, "name": "Corpser", "probability": 100 }
],
"homeRange": 30,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [512, 1915, 0],
"map": "TerMur",
"count": 10,
"entries": [
{ "maxCount": 6, "name": "TropicalBird", "probability": 100 },
{ "maxCount": 3, "name": "Corpser", "probability": 100 },
{ "maxCount": 4, "name": "Panther", "probability": 100 },
{ "maxCount": 4, "name": "ForestOstard", "probability": 100 }
],
"homeRange": 13,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [586, 1712, 40],
"map": "TerMur",
"count": 3,
"entries": [{ "maxCount": 3, "name": "Corpser", "probability": 100 }],
"homeRange": 15,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [606, 1574, 40],
"map": "TerMur",
"count": 20,
"entries": [
{ "maxCount": 9, "name": "TropicalBird", "probability": 100 },
{ "maxCount": 5, "name": "VampireBat", "probability": 100 },
{ "maxCount": 3, "name": "Corpser", "probability": 100 },
{ "maxCount": 2, "name": "ForestOstard", "probability": 100 }
],
"homeRange": 30,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [607, 1647, 40],
"map": "TerMur",
"count": 17,
"entries": [
{ "maxCount": 5, "name": "Gorilla", "probability": 100 },
{ "maxCount": 4, "name": "ForestOstard", "probability": 100 },
{ "maxCount": 2, "name": "Corpser", "probability": 100 },
{ "maxCount": 5, "name": "TropicalBird", "probability": 100 }
],
"homeRange": 50,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [614, 1748, 48],
"map": "TerMur",
"count": 16,
"entries": [
{ "maxCount": 5, "name": "Gorilla", "probability": 100 },
{ "maxCount": 5, "name": "TropicalBird", "probability": 100 },
{ "maxCount": 2, "name": "Corpser", "probability": 100 },
{ "maxCount": 3, "name": "ForestOstard", "probability": 100 }
],
"homeRange": 50,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [720, 1860, 40],
"map": "TerMur",
"count": 3,
"entries": [{ "maxCount": 3, "name": "Wisp", "probability": 100 }],
"homeRange": 15,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [801, 2003, 80],
"map": "TerMur",
"count": 17,
"entries": [
{ "maxCount": 12, "name": "TropicalBird", "probability": 100 },
{ "maxCount": 5, "name": "Corpser", "probability": 100 }
],
"homeRange": 5,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [856, 2132, 40],
"map": "TerMur",
"count": 13,
"entries": [
{ "maxCount": 3, "name": "LavaSerpent", "probability": 100 },
{ "maxCount": 2, "name": "LavaSnake", "probability": 100 },
{ "maxCount": 4, "name": "FireElemental", "probability": 100 },
{ "maxCount": 4, "name": "LavaLizard", "probability": 100 }
],
"homeRange": 25,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [929, 2013, 50],
"map": "TerMur",
"count": 2,
"entries": [{ "maxCount": 2, "name": "AirElemental", "probability": 100 }],
"homeRange": 15,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [942, 2076, 55],
"map": "TerMur",
"count": 4,
"entries": [{ "maxCount": 4, "name": "AirElemental", "probability": 100 }],
"homeRange": 20,
"walkingRange": 12,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
}
]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,46 @@
[
{
"type": "Spawner",
"location": [6278, 1786, 0],
"map": "Trammel",
"count": 3,
"entries": [{ "maxCount": 3, "name": "WaterElemental", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [6292, 1711, 0],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Hydra", "probability": 100 }],
"homeRange": -5,
"walkingRange": 30,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [6303, 1688, 10],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Hydra", "probability": 100 }],
"homeRange": -5,
"walkingRange": 30,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [6313, 1723, 0],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Hydra", "probability": 100 }],
"homeRange": -5,
"walkingRange": 30,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
}
]

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,482 @@
[
{
"type": "RegionSpawner",
"location": [375, 1191, 0],
"map": "Felucca",
"region": "A Wheatfield in Yew 2",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [567, 1239, 0],
"map": "Felucca",
"region": "A Wheatfield in Yew 1",
"count": 5,
"entries": [{ "maxCount": 5, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [571, 1099, 0],
"map": "Felucca",
"region": "A Farm in Yew",
"count": 10,
"entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [675, 939, 0],
"map": "Felucca",
"region": "A Field of Sheep in Yew 1",
"count": 10,
"entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [675, 1179, 0],
"map": "Felucca",
"region": "A Field of Sheep in Yew 2",
"count": 10,
"entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [813, 2163, 0],
"map": "Felucca",
"region": "A Wheatfield in Skara Brae 1",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [823, 2254, 0],
"map": "Felucca",
"region": "A Carrot Field in Skara Brae",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableCarrot", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [823, 2264, 0],
"map": "Felucca",
"region": "An Onion Field in Skara Brae",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableOnion", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [823, 2264, 0],
"map": "Felucca",
"region": "A Cabbage Field in Skara Brae 1",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableCabbage", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [823, 2274, 0],
"map": "Felucca",
"region": "A Cabbage Field in Skara Brae 2",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableCabbage", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [823, 2355, 0],
"map": "Felucca",
"region": "A Cotton Field in Skara Brae",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableCotton", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [843, 2352, 0],
"map": "Felucca",
"region": "A Wheatfield in Skara Brae 2",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1120, 1624, 0],
"map": "Felucca",
"region": "A Wheatfield in Britain 4",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1135, 1791, 0],
"map": "Felucca",
"region": "A Wheatfield in Britain 1",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1152, 1576, 0],
"map": "Felucca",
"region": "A Wheatfield in Britain 5",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1183, 1683, 0],
"map": "Felucca",
"region": "A Cabbage Field in Britain 1",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableCabbage", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1199, 1683, 0],
"map": "Felucca",
"region": "A Turnip Field in Britain 1",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableTurnip", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1199, 1823, 0],
"map": "Felucca",
"region": "A Wheatfield in Britain 2",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1215, 1723, 0],
"map": "Felucca",
"region": "A Carrot Field in Britain 1",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableCarrot", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1216, 1604, 0],
"map": "Felucca",
"region": "A Turnip Field in Britain 2",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableTurnip", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1231, 1723, 0],
"map": "Felucca",
"region": "An Onion Field in Britain 1",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableOnion", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1231, 1887, 0],
"map": "Felucca",
"region": "A Wheatfield in Britain 3",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1232, 1604, 0],
"map": "Felucca",
"region": "A Carrot Field in Britain 2",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableCarrot", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [4567, 1475, 0],
"map": "Felucca",
"region": "A Cotton Field in Moonglow",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableCotton", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [375, 1191, 0],
"map": "Trammel",
"region": "A Wheatfield in Yew 2",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [567, 1239, 0],
"map": "Trammel",
"region": "A Wheatfield in Yew 1",
"count": 5,
"entries": [{ "maxCount": 5, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [571, 1099, 0],
"map": "Trammel",
"region": "A Farm in Yew",
"count": 10,
"entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [675, 939, 0],
"map": "Trammel",
"region": "A Field of Sheep in Yew 1",
"count": 10,
"entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [675, 1179, 0],
"map": "Trammel",
"region": "A Field of Sheep in Yew 2",
"count": 10,
"entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [813, 2163, 0],
"map": "Trammel",
"region": "A Wheatfield in Skara Brae 1",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [823, 2254, 0],
"map": "Trammel",
"region": "A Carrot Field in Skara Brae",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableCarrot", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [823, 2264, 0],
"map": "Trammel",
"region": "An Onion Field in Skara Brae",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableOnion", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [823, 2264, 0],
"map": "Trammel",
"region": "A Cabbage Field in Skara Brae 1",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableCabbage", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [823, 2274, 0],
"map": "Trammel",
"region": "A Cabbage Field in Skara Brae 2",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableCabbage", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [823, 2355, 0],
"map": "Trammel",
"region": "A Cotton Field in Skara Brae",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableCotton", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [843, 2352, 0],
"map": "Trammel",
"region": "A Wheatfield in Skara Brae 2",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1120, 1624, 0],
"map": "Trammel",
"region": "A Wheatfield in Britain 4",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1135, 1791, 0],
"map": "Trammel",
"region": "A Wheatfield in Britain 1",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1152, 1576, 0],
"map": "Trammel",
"region": "A Wheatfield in Britain 5",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1183, 1683, 0],
"map": "Trammel",
"region": "A Cabbage Field in Britain 1",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableCabbage", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1199, 1683, 0],
"map": "Trammel",
"region": "A Turnip Field in Britain 1",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableTurnip", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1199, 1823, 0],
"map": "Trammel",
"region": "A Wheatfield in Britain 2",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1215, 1723, 0],
"map": "Trammel",
"region": "A Carrot Field in Britain 1",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableCarrot", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1216, 1604, 0],
"map": "Trammel",
"region": "A Turnip Field in Britain 2",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableTurnip", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1231, 1723, 0],
"map": "Trammel",
"region": "An Onion Field in Britain 1",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableOnion", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1231, 1887, 0],
"map": "Trammel",
"region": "A Wheatfield in Britain 3",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [1232, 1604, 0],
"map": "Trammel",
"region": "A Carrot Field in Britain 2",
"count": 6,
"entries": [{ "maxCount": 6, "name": "FarmableCarrot", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
},
{
"type": "RegionSpawner",
"location": [4567, 1475, 0],
"map": "Trammel",
"region": "A Cotton Field in Moonglow",
"count": 8,
"entries": [{ "maxCount": 8, "name": "FarmableCotton", "probability": 100 }],
"minDelay": "00:00:10",
"maxDelay": "00:00:30"
}
]

View file

@ -0,0 +1,818 @@
[
{
"type": "Spawner",
"location": [5657, 1824, 1],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 8,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5657, 1989, 1],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackSolenWarrior", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5665, 1869, 10],
"map": "Felucca",
"count": 3,
"entries": [
{ "maxCount": 1, "name": "BlackSolenQueen", "probability": 100 },
{ "maxCount": 2, "name": "BlackSolenWorker", "probability": 100 }
],
"homeRange": 16,
"walkingRange": 14,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5672, 1844, 2],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 8,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5677, 2027, 3],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5684, 1985, 3],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackSolenWarrior", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5698, 1894, 1],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "BlackSolenWarrior", "probability": 100 }],
"homeRange": 10,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5699, 2025, 4],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5707, 1874, 0],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackSolenWarrior", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5708, 1800, 2],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5718, 1819, 3],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5721, 1993, 0],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackSolenWarrior", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5725, 1915, 5],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "BlackSolenWarrior", "probability": 100 }],
"homeRange": 8,
"walkingRange": 12,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5737, 1941, 3],
"map": "Felucca",
"count": 3,
"entries": [
{ "maxCount": 1, "name": "BlackSolenQueen", "probability": 100 },
{ "maxCount": 2, "name": "BlackSolenWorker", "probability": 100 }
],
"homeRange": 12,
"walkingRange": 12,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5770, 1956, 1],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "DreadSpider", "probability": 100 }],
"homeRange": 10,
"walkingRange": 20,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5775, 1897, 22],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackSolenMatriarch", "probability": 100 }],
"homeRange": 5,
"walkingRange": 20,
"maxDelay": "00:00:05",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5783, 2021, -14],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "AntLion", "probability": 100 }],
"homeRange": 7,
"walkingRange": 9,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5788, 1796, 1],
"map": "Felucca",
"count": 2,
"entries": [
{ "maxCount": 1, "name": "BlackSolenWarrior", "probability": 100 },
{ "maxCount": 1, "name": "BlackSolenWorker", "probability": 100 }
],
"homeRange": 10,
"walkingRange": 20,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5790, 1985, 5],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackAmbitiousSolenQueen", "probability": 100 }],
"homeRange": 5,
"walkingRange": 20,
"maxDelay": "00:00:05",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5793, 1855, 2],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "BlackSolenWarrior", "probability": 100 }],
"homeRange": 10,
"walkingRange": 12,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5813, 1945, 2],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 8,
"walkingRange": 12,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5819, 1973, 3],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 8,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5830, 1862, 3],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackSolenWarrior", "probability": 100 }],
"homeRange": 10,
"walkingRange": 25,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5831, 1910, 3],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Beetle", "probability": 100 }],
"homeRange": 8,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5832, 1803, 0],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 8,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5835, 1936, 0],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "BlackSolenWarrior", "probability": 100 }],
"homeRange": 8,
"walkingRange": 25,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5836, 1994, 0],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Beetle", "probability": 100 }],
"homeRange": 12,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5847, 2020, 10],
"map": "Felucca",
"count": 3,
"entries": [{ "maxCount": 3, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5860, 1959, 2],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackSolenWarrior", "probability": 100 }],
"homeRange": 8,
"walkingRange": 20,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5860, 1979, 4],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackSolenWarrior", "probability": 100 }],
"homeRange": 5,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5874, 1798, 0],
"map": "Felucca",
"count": 3,
"entries": [
{ "maxCount": 1, "name": "BlackSolenQueen", "probability": 100 },
{ "maxCount": 2, "name": "BlackSolenWorker", "probability": 100 }
],
"homeRange": 8,
"walkingRange": 8,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5885, 1920, -17],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "AntLion", "probability": 100 }],
"homeRange": 10,
"walkingRange": 12,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5887, 1840, -18],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "AntLion", "probability": 100 }],
"homeRange": 15,
"walkingRange": 25,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5914, 1991, 4],
"map": "Felucca",
"count": 3,
"entries": [{ "maxCount": 3, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 8,
"walkingRange": 25,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5919, 1833, 1],
"map": "Felucca",
"count": 2,
"entries": [{ "maxCount": 2, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 8,
"walkingRange": 8,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5920, 1814, 1],
"map": "Felucca",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BlackSolenWorker", "probability": 100 }],
"homeRange": 8,
"walkingRange": 8,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5657, 1824, 1],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 8,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5657, 1989, 1],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedSolenWarrior", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5665, 1869, 10],
"map": "Trammel",
"count": 3,
"entries": [
{ "maxCount": 1, "name": "RedSolenQueen", "probability": 100 },
{ "maxCount": 2, "name": "RedSolenWorker", "probability": 100 }
],
"homeRange": 16,
"walkingRange": 14,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5672, 1844, 2],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 8,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5677, 2027, 3],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5684, 1985, 3],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedSolenWarrior", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5698, 1894, 1],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "RedSolenWarrior", "probability": 100 }],
"homeRange": 10,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5699, 2025, 4],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5707, 1874, 0],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedSolenWarrior", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5708, 1800, 2],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5718, 1819, 3],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5721, 1993, 0],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedSolenWarrior", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5725, 1915, 5],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "RedSolenWarrior", "probability": 100 }],
"homeRange": 8,
"walkingRange": 12,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5737, 1941, 3],
"map": "Trammel",
"count": 3,
"entries": [
{ "maxCount": 1, "name": "RedSolenQueen", "probability": 100 },
{ "maxCount": 2, "name": "RedSolenWorker", "probability": 100 }
],
"homeRange": 12,
"walkingRange": 12,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5770, 1956, 1],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "DreadSpider", "probability": 100 }],
"homeRange": 10,
"walkingRange": 20,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5775, 1897, 22],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedSolenMatriarch", "probability": 100 }],
"homeRange": 5,
"walkingRange": 20,
"maxDelay": "00:00:05",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5783, 2021, -14],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "AntLion", "probability": 100 }],
"homeRange": 7,
"walkingRange": 9,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5788, 1796, 1],
"map": "Trammel",
"count": 2,
"entries": [
{ "maxCount": 1, "name": "RedSolenWarrior", "probability": 100 },
{ "maxCount": 1, "name": "RedSolenWorker", "probability": 100 }
],
"homeRange": 10,
"walkingRange": 20,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5790, 1985, 5],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedAmbitiousSolenQueen", "probability": 100 }],
"homeRange": 5,
"walkingRange": 20,
"maxDelay": "00:00:05",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5793, 1855, 2],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "RedSolenWarrior", "probability": 100 }],
"homeRange": 10,
"walkingRange": 12,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5813, 1945, 2],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 8,
"walkingRange": 12,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5819, 1973, 3],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 8,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5830, 1862, 3],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedSolenWarrior", "probability": 100 }],
"homeRange": 10,
"walkingRange": 25,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5831, 1910, 3],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Beetle", "probability": 100 }],
"homeRange": 8,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5832, 1803, 0],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 8,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5835, 1936, 0],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "RedSolenWarrior", "probability": 100 }],
"homeRange": 8,
"walkingRange": 25,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5836, 1994, 0],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Beetle", "probability": 100 }],
"homeRange": 12,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5847, 2020, 10],
"map": "Trammel",
"count": 3,
"entries": [{ "maxCount": 3, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 10,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5860, 1959, 2],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedSolenWarrior", "probability": 100 }],
"homeRange": 8,
"walkingRange": 20,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5860, 1979, 4],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedSolenWarrior", "probability": 100 }],
"homeRange": 5,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5874, 1798, 0],
"map": "Trammel",
"count": 3,
"entries": [
{ "maxCount": 1, "name": "RedSolenQueen", "probability": 100 },
{ "maxCount": 2, "name": "RedSolenWorker", "probability": 100 }
],
"homeRange": 8,
"walkingRange": 8,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5885, 1920, -17],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "AntLion", "probability": 100 }],
"homeRange": 10,
"walkingRange": 12,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5887, 1840, -18],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "AntLion", "probability": 100 }],
"homeRange": 15,
"walkingRange": 25,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5914, 1991, 4],
"map": "Trammel",
"count": 3,
"entries": [{ "maxCount": 3, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 8,
"walkingRange": 25,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5919, 1833, 1],
"map": "Trammel",
"count": 2,
"entries": [{ "maxCount": 2, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 8,
"walkingRange": 8,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [5920, 1814, 1],
"map": "Trammel",
"count": 1,
"entries": [{ "maxCount": 1, "name": "RedSolenWorker", "probability": 100 }],
"homeRange": 8,
"walkingRange": 8,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
}
]

View file

@ -0,0 +1,912 @@
[
{
"type": "Spawner",
"location": [35, 99, -5],
"map": "TerMur",
"count": 12,
"entries": [{ "maxCount": 2, "name": "SilverSerpent", "probability": 100 }],
"homeRange": 10,
"walkingRange": 15,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [35, 159, 0],
"map": "TerMur",
"count": 18,
"entries": [{ "maxCount": 5, "name": "SilverSerpent", "probability": 100 }],
"homeRange": 20,
"walkingRange": 15,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [344, 618, 26],
"map": "TerMur",
"count": 13,
"entries": [
{ "maxCount": 8, "name": "Pixie", "probability": 100 },
{ "maxCount": 5, "name": "Wisp", "probability": 100 }
],
"homeRange": 15,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [365, 2952, 40],
"map": "TerMur",
"count": 10,
"entries": [
{ "maxCount": 3, "name": "FireGargoyle", "probability": 100 },
{ "maxCount": 2, "name": "FireElemental", "probability": 100 }
],
"homeRange": 20,
"walkingRange": 25,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [365, 3138, 36],
"map": "TerMur",
"count": 3,
"entries": [{ "maxCount": 3, "name": "HeadlessOne", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [375, 2999, -2],
"map": "TerMur",
"count": 3,
"entries": [{ "maxCount": 3, "name": "FireGargoyle", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [386, 3062, 29],
"map": "TerMur",
"count": 9,
"entries": [
{ "maxCount": 2, "name": "Efreet", "probability": 100 },
{ "maxCount": 2, "name": "Phoenix", "probability": 100 },
{ "maxCount": 3, "name": "FireGargoyle", "probability": 100 }
],
"homeRange": 15,
"walkingRange": 25,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [399, 3128, 37],
"map": "TerMur",
"count": 7,
"entries": [{ "maxCount": 2, "name": "FireGargoyle", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:04:00",
"minDelay": "00:03:00"
},
{
"type": "Spawner",
"location": [416, 2939, 36],
"map": "TerMur",
"count": 7,
"entries": [
{ "maxCount": 2, "name": "FireElemental", "probability": 100 },
{ "maxCount": 3, "name": "LavaSnake", "probability": 100 }
],
"homeRange": 5,
"walkingRange": 20,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [418, 2982, 26],
"map": "TerMur",
"count": 7,
"entries": [
{ "maxCount": 2, "name": "LavaLizard", "probability": 100 },
{ "maxCount": 3, "name": "LavaSnake", "probability": 100 }
],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [432, 3071, 39],
"map": "TerMur",
"count": 4,
"entries": [
{ "maxCount": 2, "name": "FireElemental", "probability": 100 },
{ "maxCount": 2, "name": "LavaSnake", "probability": 100 }
],
"homeRange": 5,
"walkingRange": 20,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [452, 3133, 38],
"map": "TerMur",
"count": 16,
"entries": [
{ "maxCount": 4, "name": "LavaSerpent", "probability": 100 },
{ "maxCount": 4, "name": "LavaSnake", "probability": 100 },
{ "maxCount": 4, "name": "HeadlessOne", "probability": 100 }
],
"homeRange": 15,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [465, 2977, 34],
"map": "TerMur",
"count": 8,
"entries": [
{ "maxCount": 2, "name": "FireElemental", "probability": 100 },
{ "maxCount": 3, "name": "LavaLizard", "probability": 100 }
],
"homeRange": 5,
"walkingRange": 30,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [465, 3042, 19],
"map": "TerMur",
"count": 5,
"entries": [{ "maxCount": 3, "name": "Efreet", "probability": 100 }],
"homeRange": 5,
"walkingRange": 15,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [475, 3188, 37],
"map": "TerMur",
"count": 11,
"entries": [{ "maxCount": 3, "name": "HeadlessOne", "probability": 100 }],
"homeRange": 25,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [487, 3748, -32],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "WanderingHealer", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [511, 3042, 38],
"map": "TerMur",
"count": 12,
"entries": [
{ "maxCount": 4, "name": "LavaSnake", "probability": 100 },
{ "maxCount": 4, "name": "LavaSerpent", "probability": 100 }
],
"homeRange": 15,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [534, 3421, 37],
"map": "TerMur",
"count": 48,
"entries": [
{ "maxCount": 7, "name": "SilverSerpent", "probability": 100 },
{ "maxCount": 10, "name": "Horse", "probability": 100 },
{ "maxCount": 15, "name": "Bird", "probability": 100 }
],
"homeRange": 35,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [555, 3495, 38],
"map": "TerMur",
"count": 22,
"entries": [
{ "maxCount": 4, "name": "SilverSerpent", "probability": 100 },
{ "maxCount": 4, "name": "Snake", "probability": 100 },
{ "maxCount": 6, "name": "Bird", "probability": 100 }
],
"homeRange": 10,
"walkingRange": 20,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [581, 898, -73],
"map": "TerMur",
"count": 71,
"entries": [
{ "maxCount": 10, "name": "LavaLizard", "probability": 100 },
{ "maxCount": 10, "name": "LavaSnake", "probability": 100 },
{ "maxCount": 10, "name": "HellHound", "probability": 100 },
{ "maxCount": 10, "name": "LavaSerpent", "probability": 100 }
],
"homeRange": 25,
"walkingRange": 5,
"maxDelay": "00:02:00",
"minDelay": "00:02:00"
},
{
"type": "Spawner",
"location": [586, 3283, 37],
"map": "TerMur",
"count": 19,
"entries": [
{ "maxCount": 10, "name": "Bird", "probability": 100 },
{ "maxCount": 5, "name": "Horse", "probability": 100 }
],
"homeRange": 20,
"walkingRange": 20,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [649, 703, 13],
"map": "TerMur",
"count": 21,
"entries": [
{ "maxCount": 5, "name": "Snake", "probability": 100 },
{ "maxCount": 10, "name": "LavaSnake", "probability": 100 },
{ "maxCount": 4, "name": "GiantSerpent", "probability": 100 }
],
"homeRange": 20,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [657, 2906, -3],
"map": "TerMur",
"count": 9,
"entries": [{ "maxCount": 1, "name": "NetherBoltScroll", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [676, 831, -109],
"map": "TerMur",
"count": 81,
"entries": [
{ "maxCount": 10, "name": "PatchworkSkeleton", "probability": 100 },
{ "maxCount": 10, "name": "Skeleton", "probability": 100 },
{ "maxCount": 10, "name": "SkeletalKnight", "probability": 100 },
{ "maxCount": 10, "name": "BoneKnight", "probability": 100 },
{ "maxCount": 10, "name": "SkeletalMage", "probability": 100 },
{ "maxCount": 10, "name": "BoneMagi", "probability": 100 }
],
"homeRange": 25,
"walkingRange": 5,
"maxDelay": "00:05:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [689, 485, -15],
"map": "TerMur",
"count": 10,
"entries": [{ "maxCount": 10, "name": "HellHound", "probability": 100 }],
"homeRange": 25,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [698, 773, 28],
"map": "TerMur",
"count": 9,
"entries": [
{ "maxCount": 4, "name": "Snake", "probability": 100 },
{ "maxCount": 1, "name": "Coil", "probability": 100 },
{ "maxCount": 4, "name": "SilverSerpent", "probability": 100 }
],
"homeRange": 10,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [702, 3434, -20],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Tailor", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [713, 3438, -19],
"map": "TerMur",
"count": 6,
"entries": [{ "maxCount": 6, "name": "Barkeeper", "probability": 100 }],
"homeRange": 1,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [715, 3423, -20],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Healer", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [716, 3445, -19],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Farmer", "probability": 100 }],
"homeRange": 1,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [726, 3427, -20],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Weaponsmith", "probability": 100 }],
"homeRange": 1,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [728, 3445, -20],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Carpenter", "probability": 100 }],
"homeRange": 1,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [735, 687, -47],
"map": "TerMur",
"count": 20,
"entries": [
{ "maxCount": 6, "name": "GiantSerpent", "probability": 100 },
{ "maxCount": 8, "name": "Snake", "probability": 100 },
{ "maxCount": 2, "name": "SilverSerpent", "probability": 100 }
],
"homeRange": 25,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [764, 746, 23],
"map": "TerMur",
"count": 18,
"entries": [
{ "maxCount": 1, "name": "Hydra", "probability": 100 },
{ "maxCount": 3, "name": "GiantSerpent", "probability": 100 },
{ "maxCount": 5, "name": "Snake", "probability": 100 },
{ "maxCount": 5, "name": "SilverSerpent", "probability": 100 },
{ "maxCount": 4, "name": "LavaSnake", "probability": 100 }
],
"homeRange": 15,
"walkingRange": 5,
"maxDelay": "00:02:00",
"minDelay": "00:02:00"
},
{
"type": "Spawner",
"location": [771, 3475, -20],
"map": "TerMur",
"count": 3,
"entries": [{ "maxCount": 1, "name": "Alchemist", "probability": 100 }],
"homeRange": 2,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [772, 3486, -20],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Mage", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [775, 770, 23],
"map": "TerMur",
"count": 5,
"entries": [{ "maxCount": 4, "name": "GiantSerpent", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:03:00",
"minDelay": "00:03:00"
},
{
"type": "Spawner",
"location": [778, 732, 23],
"map": "TerMur",
"count": 5,
"entries": [{ "maxCount": 4, "name": "GiantSerpent", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:03:00",
"minDelay": "00:03:00"
},
{
"type": "Spawner",
"location": [778, 3430, -10],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Jeweler", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [784, 3490, -20],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Healer", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [791, 3448, -10],
"map": "TerMur",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Farmer", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [793, 3491, -20],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Provisioner", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [795, 759, 23],
"map": "TerMur",
"count": 4,
"entries": [
{ "maxCount": 1, "name": "Hydra", "probability": 100 },
{ "maxCount": 3, "name": "SilverSerpent", "probability": 100 }
],
"homeRange": 2,
"walkingRange": 5,
"maxDelay": "00:02:00",
"minDelay": "00:02:00"
},
{
"type": "Spawner",
"location": [795, 771, 22],
"map": "TerMur",
"count": 5,
"entries": [{ "maxCount": 4, "name": "GiantSerpent", "probability": 100 }],
"homeRange": 10,
"walkingRange": 5,
"maxDelay": "00:02:00",
"minDelay": "00:02:00"
},
{
"type": "Spawner",
"location": [805, 3398, 0],
"map": "TerMur",
"count": 2,
"entries": [{ "maxCount": 1, "name": "Tailor", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [806, 734, 22],
"map": "TerMur",
"count": 5,
"entries": [{ "maxCount": 4, "name": "GiantSerpent", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:03:00",
"minDelay": "00:03:00"
},
{
"type": "Spawner",
"location": [806, 3387, 0],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Tanner", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [806, 3491, -20],
"map": "TerMur",
"count": 2,
"entries": [
{ "maxCount": 1, "name": "Cook", "probability": 100 },
{ "maxCount": 1, "name": "Baker", "probability": 100 }
],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [808, 3228, 40],
"map": "TerMur",
"count": 35,
"entries": [
{ "maxCount": 5, "name": "Horse", "probability": 100 },
{ "maxCount": 10, "name": "Bird", "probability": 100 },
{ "maxCount": 10, "name": "Hind", "probability": 100 },
{ "maxCount": 5, "name": "Rabbit", "probability": 100 },
{ "maxCount": 5, "name": "TimberWolf", "probability": 100 }
],
"homeRange": 50,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [810, 3800, -42],
"map": "TerMur",
"count": 85,
"entries": [
{ "maxCount": 10, "name": "Horse", "probability": 100 },
{ "maxCount": 25, "name": "Mongbat", "probability": 100 }
],
"homeRange": 75,
"walkingRange": 75,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [815, 3420, 0],
"map": "TerMur",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Blacksmith", "probability": 100 }],
"homeRange": 2,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [816, 658, 22],
"map": "TerMur",
"count": 25,
"entries": [
{ "maxCount": 5, "name": "SilverSerpent", "probability": 100 },
{ "maxCount": 10, "name": "Snake", "probability": 100 }
],
"homeRange": 10,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [816, 3438, 0],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Tinker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [817, 3428, 0],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Carpenter", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [851, 3635, -43],
"map": "TerMur",
"count": 34,
"entries": [
{ "maxCount": 5, "name": "Bird", "probability": 100 },
{ "maxCount": 7, "name": "Horse", "probability": 100 },
{ "maxCount": 7, "name": "Hind", "probability": 100 },
{ "maxCount": 5, "name": "Rat", "probability": 100 },
{ "maxCount": 5, "name": "Snake", "probability": 100 },
{ "maxCount": 5, "name": "Rabbit", "probability": 100 }
],
"homeRange": 50,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [911, 3998, -40],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Butcher", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [974, 3881, -42],
"map": "TerMur",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Baker", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [997, 3904, -42],
"map": "TerMur",
"count": 30,
"entries": [
{ "maxCount": 5, "name": "Bird", "probability": 100 },
{ "maxCount": 5, "name": "Rabbit", "probability": 100 },
{ "maxCount": 5, "name": "Horse", "probability": 100 },
{ "maxCount": 5, "name": "Rat", "probability": 100 },
{ "maxCount": 5, "name": "Snake", "probability": 100 },
{ "maxCount": 5, "name": "GreaterMongbat", "probability": 100 }
],
"homeRange": 70,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [1012, 3881, -42],
"map": "TerMur",
"count": 2,
"entries": [
{ "maxCount": 1, "name": "Blacksmith", "probability": 100 },
{ "maxCount": 1, "name": "Armorer", "probability": 100 }
],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [1018, 3907, -42],
"map": "TerMur",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Healer", "probability": 100 }],
"homeRange": 5,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [1030, 2959, 61],
"map": "TerMur",
"count": 13,
"entries": [
{ "maxCount": 10, "name": "DesertOstard", "probability": 100 },
{ "maxCount": 10, "name": "SandVortex", "probability": 100 },
{ "maxCount": 10, "name": "Scorpion", "probability": 100 }
],
"homeRange": 30,
"walkingRange": 30,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [1035, 2993, 50],
"map": "TerMur",
"count": 10,
"entries": [
{ "maxCount": 7, "name": "DesertOstard", "probability": 100 },
{ "maxCount": 7, "name": "SandVortex", "probability": 100 },
{ "maxCount": 7, "name": "Scorpion", "probability": 100 }
],
"homeRange": 30,
"walkingRange": 30,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [1040, 3004, 50],
"map": "TerMur",
"count": 11,
"entries": [
{ "maxCount": 8, "name": "DesertOstard", "probability": 100 },
{ "maxCount": 8, "name": "SandVortex", "probability": 100 },
{ "maxCount": 8, "name": "Scorpion", "probability": 100 }
],
"homeRange": 30,
"walkingRange": 30,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [1055, 2987, 62],
"map": "TerMur",
"count": 15,
"entries": [
{ "maxCount": 12, "name": "DesertOstard", "probability": 100 },
{ "maxCount": 12, "name": "SandVortex", "probability": 100 },
{ "maxCount": 12, "name": "Scorpion", "probability": 100 }
],
"homeRange": 30,
"walkingRange": 30,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [1074, 3008, 63],
"map": "TerMur",
"count": 15,
"entries": [
{ "maxCount": 12, "name": "DesertOstard", "probability": 100 },
{ "maxCount": 12, "name": "SandVortex", "probability": 100 },
{ "maxCount": 12, "name": "Scorpion", "probability": 100 }
],
"homeRange": 30,
"walkingRange": 30,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [1089, 2979, 51],
"map": "TerMur",
"count": 11,
"entries": [
{ "maxCount": 8, "name": "DesertOstard", "probability": 100 },
{ "maxCount": 8, "name": "SandVortex", "probability": 100 },
{ "maxCount": 8, "name": "Scorpion", "probability": 100 }
],
"homeRange": 30,
"walkingRange": 30,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [1111, 3733, -42],
"map": "TerMur",
"count": 50,
"entries": [
{ "maxCount": 20, "name": "GiantBlackWidow", "probability": 100 },
{ "maxCount": 30, "name": "GiantSpider", "probability": 100 }
],
"homeRange": 25,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [1128, 3479, -42],
"map": "TerMur",
"count": 24,
"entries": [
{ "maxCount": 2, "name": "SilverSerpent", "probability": 100 },
{ "maxCount": 5, "name": "GreaterMongbat", "probability": 100 },
{ "maxCount": 5, "name": "Snake", "probability": 100 },
{ "maxCount": 5, "name": "Horse", "probability": 100 }
],
"homeRange": 15,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:03:00"
},
{
"type": "Spawner",
"location": [1152, 3640, -42],
"map": "TerMur",
"count": 9,
"entries": [{ "maxCount": 5, "name": "GreaterMongbat", "probability": 100 }],
"homeRange": 15,
"walkingRange": 5,
"maxDelay": "00:10:00",
"minDelay": "00:05:00"
},
{
"type": "Spawner",
"location": [1165, 3366, -42],
"map": "TerMur",
"count": 18,
"entries": [
{ "maxCount": 4, "name": "SilverSerpent", "probability": 100 },
{ "maxCount": 4, "name": "GreaterMongbat", "probability": 100 }
],
"homeRange": 15,
"walkingRange": 5,
"maxDelay": "00:04:00",
"minDelay": "00:03:00"
}
]

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,16 @@
[
{
"type": "Spawner",
"location": [541, 2406, 10],
"map": "TerMur",
"count": 14,
"entries": [
{ "maxCount": 7, "name": "FireElemental", "probability": 100 },
{ "maxCount": 7, "name": "LavaLizard", "probability": 100 }
],
"homeRange": 20,
"walkingRange": 5,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
}
]

View file

@ -0,0 +1,261 @@
[
{
"type": "Spawner",
"location": [2130, 1174, -46],
"map": "Ilshenar",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Gnaw", "probability": 100 }],
"homeRange": 5,
"walkingRange": 8,
"maxDelay": "00:05:00",
"minDelay": "00:02:00"
},
{
"type": "Spawner",
"location": [2131, 1173, -46],
"map": "Ilshenar",
"count": 5,
"entries": [{ "maxCount": 5, "name": "DireWolf", "probability": 100 }],
"homeRange": 8,
"walkingRange": 8,
"maxDelay": "00:01:00",
"minDelay": "00:00:10"
},
{
"type": "Spawner",
"location": [2137, 1216, -60],
"map": "Ilshenar",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Corpser", "probability": 100 }],
"homeRange": 4,
"walkingRange": 5,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2137, 1216, -60],
"map": "Ilshenar",
"count": 4,
"entries": [{ "maxCount": 4, "name": "GiantBlackWidow", "probability": 100 }],
"homeRange": 0,
"walkingRange": 5,
"maxDelay": "00:02:00",
"minDelay": "00:00:30"
},
{
"type": "Spawner",
"location": [2141, 1209, -60],
"map": "Ilshenar",
"count": 2,
"entries": [{ "maxCount": 2, "name": "WhippingVine", "probability": 100 }],
"homeRange": 18,
"walkingRange": 18,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2150, 1188, -46],
"map": "Ilshenar",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Changeling", "probability": 100 }],
"homeRange": 8,
"walkingRange": 20,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2158, 1217, -60],
"map": "Ilshenar",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Quagmire", "probability": 100 }],
"homeRange": 15,
"walkingRange": 25,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2158, 1224, -60],
"map": "Ilshenar",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Irk", "probability": 100 }],
"homeRange": 15,
"walkingRange": 25,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2160, 1178, -40],
"map": "Ilshenar",
"count": 5,
"entries": [{ "maxCount": 5, "name": "DireWolf", "probability": 100 }],
"homeRange": 10,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2166, 1235, -60],
"map": "Ilshenar",
"count": 2,
"entries": [
{ "maxCount": 1, "name": "Corpser", "probability": 100 },
{ "maxCount": 1, "name": "WhippingVine", "probability": 100 }
],
"homeRange": 12,
"walkingRange": 25,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2167, 1266, -60],
"map": "Ilshenar",
"count": 3,
"entries": [{ "maxCount": 3, "name": "Malefic", "probability": 100 }],
"homeRange": 0,
"walkingRange": 5,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [2170, 1247, -60],
"map": "Ilshenar",
"count": 4,
"entries": [
{ "maxCount": 2, "name": "Malefic", "probability": 100 },
{ "maxCount": 2, "name": "Virulent", "probability": 100 }
],
"homeRange": 0,
"walkingRange": 10,
"maxDelay": "00:02:00",
"minDelay": "00:01:00"
},
{
"type": "Spawner",
"location": [2173, 1213, -60],
"map": "Ilshenar",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Guile", "probability": 100 }],
"homeRange": 10,
"walkingRange": 30,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2174, 1203, -60],
"map": "Ilshenar",
"count": 1,
"entries": [{ "maxCount": 1, "name": "LadyLissith", "probability": 100 }],
"homeRange": 5,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2174, 1213, -60],
"map": "Ilshenar",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Spite", "probability": 100 }],
"homeRange": 10,
"walkingRange": 30,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2177, 1229, -60],
"map": "Ilshenar",
"count": 4,
"entries": [{ "maxCount": 4, "name": "Silk", "probability": 100 }],
"homeRange": 0,
"walkingRange": 5,
"maxDelay": "00:01:30",
"minDelay": "00:00:30"
},
{
"type": "Spawner",
"location": [2179, 1214, -60],
"map": "Ilshenar",
"count": 1,
"entries": [{ "maxCount": 1, "name": "LadySabrix", "probability": 100 }],
"homeRange": 7,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2193, 1170, -36],
"map": "Ilshenar",
"count": 4,
"entries": [{ "maxCount": 4, "name": "Changeling", "probability": 100 }],
"homeRange": 20,
"walkingRange": 25,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2200, 1192, -35],
"map": "Ilshenar",
"count": 3,
"entries": [{ "maxCount": 3, "name": "MLDryad", "probability": 100 }],
"homeRange": 15,
"walkingRange": 20,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2207, 1191, -36],
"map": "Ilshenar",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Satyr", "probability": 100 }],
"homeRange": 12,
"walkingRange": 20,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2210, 1192, -35],
"map": "Ilshenar",
"count": 5,
"entries": [{ "maxCount": 5, "name": "CuSidhe", "probability": 100 }],
"homeRange": 15,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2211, 1169, -37],
"map": "Ilshenar",
"count": 1,
"entries": [{ "maxCount": 1, "name": "MLDryad", "probability": 100 }],
"homeRange": 8,
"walkingRange": 12,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [2222, 1222, 9],
"map": "Ilshenar",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Swoop", "probability": 100 }],
"homeRange": 5,
"walkingRange": 8,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
}
]

View file

@ -0,0 +1,121 @@
[
{
"type": "Spawner",
"location": [1018, 1116, -42],
"map": "TerMur",
"count": 14,
"entries": [
{ "maxCount": 5, "name": "BoneMagi", "probability": 100 },
{ "maxCount": 4, "name": "Wraith", "probability": 100 },
{ "maxCount": 1, "name": "Spectre", "probability": 100 },
{ "maxCount": 4, "name": "Skeleton", "probability": 100 }
],
"homeRange": 8,
"walkingRange": 20,
"maxDelay": "00:01:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [1031, 1152, -65],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Kraken", "probability": 100 }],
"homeRange": 30,
"walkingRange": 30,
"maxDelay": "00:00:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [1076, 1117, -65],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "Kraken", "probability": 100 }],
"homeRange": 20,
"walkingRange": 35,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [1111, 1022, -48],
"map": "TerMur",
"count": 1,
"entries": [{ "maxCount": 1, "name": "BloodElemental", "probability": 100 }],
"homeRange": 8,
"walkingRange": 12,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [1111, 1173, -29],
"map": "TerMur",
"count": 8,
"entries": [{ "maxCount": 8, "name": "Slime", "probability": 100 }],
"homeRange": 2,
"walkingRange": 2,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [1113, 1164, -18],
"map": "TerMur",
"count": 2,
"entries": [{ "maxCount": 2, "name": "Slime", "probability": 100 }],
"homeRange": 1,
"walkingRange": 1,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [1197, 1186, -78],
"map": "TerMur",
"count": 3,
"entries": [{ "maxCount": 1, "name": "WaterElemental", "probability": 100 }],
"homeRange": 8,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [1217, 1075, -52],
"map": "TerMur",
"count": 2,
"entries": [{ "maxCount": 2, "name": "RottingCorpse", "probability": 100 }],
"homeRange": 15,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [1218, 1075, -52],
"map": "TerMur",
"count": 5,
"entries": [
{ "maxCount": 2, "name": "Shade", "probability": 100 },
{ "maxCount": 2, "name": "Spectre", "probability": 100 },
{ "maxCount": 1, "name": "Wraith", "probability": 100 }
],
"homeRange": 15,
"walkingRange": 15,
"maxDelay": "00:00:00",
"minDelay": "00:00:01"
},
{
"type": "Spawner",
"location": [1219, 1075, -52],
"map": "TerMur",
"count": 5,
"entries": [{ "maxCount": 5, "name": "Mummy", "probability": 100 }],
"homeRange": 15,
"walkingRange": 15,
"maxDelay": "00:03:00",
"minDelay": "00:00:01"
}
]

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,4 @@
using System; using System;
using Server.Items;
using Server.Misc; using Server.Misc;
namespace Server.Tests namespace Server.Tests

View file

@ -58,7 +58,8 @@ namespace Server
public static TypeCache GetTypeCache(Assembly asm) public static TypeCache GetTypeCache(Assembly asm)
{ {
if (asm == null) return m_NullCache ??= new TypeCache(null); if (asm == null)
return m_NullCache ??= new TypeCache(null);
if (m_TypeCaches.TryGetValue(asm, out var c)) if (m_TypeCaches.TryGetValue(asm, out var c))
return c; return c;
@ -68,6 +69,8 @@ namespace Server
public static Type FindFirstTypeForName(string name, bool ignoreCase = false, Func<Type, bool> predicate = null) public static Type FindFirstTypeForName(string name, bool ignoreCase = false, Func<Type, bool> predicate = null)
{ {
if (string.IsNullOrWhiteSpace(name)) return null;
var types = FindTypesByName(name, ignoreCase).ToList(); var types = FindTypesByName(name, ignoreCase).ToList();
if (types.Count == 0) if (types.Count == 0)
return null; return null;
@ -87,14 +90,19 @@ namespace Server
types[0]; types[0];
} }
public static IEnumerable<Type> FindTypesByName(string name, bool ignoreCase = false) public static List<Type> FindTypesByName(string name, bool ignoreCase = false)
{ {
var types = new List<Type>(); var types = new List<Type>();
if (ignoreCase) if (ignoreCase)
name = name.ToLower(); name = name.ToLower();
for (var i = 0; i < Assemblies.Length; i++) types.AddRange(GetTypeCache(Assemblies[i])[name]);
for (var i = 0; i < Assemblies.Length; i++)
types.AddRange(GetTypeCache(Assemblies[i])[name]);
if (types.Count == 0) if (types.Count == 0)
types.AddRange(GetTypeCache(Core.Assembly)[name]); types.AddRange(GetTypeCache(Core.Assembly)[name]);
return types; return types;
} }
@ -122,6 +130,7 @@ namespace Server
public TypeCache(Assembly asm) public TypeCache(Assembly asm)
{ {
m_Types = asm?.GetTypes() ?? Type.EmptyTypes; m_Types = asm?.GetTypes() ?? Type.EmptyTypes;
var nameMap = new Dictionary<string, HashSet<int>>(); var nameMap = new Dictionary<string, HashSet<int>>();
HashSet<int> refs; HashSet<int> refs;
Action<int, string> addToRefs = (index, key) => Action<int, string> addToRefs = (index, key) =>
@ -136,6 +145,7 @@ namespace Server
nameMap.Add(key, refs); nameMap.Add(key, refs);
} }
}; };
var aliasType = typeof(TypeAliasAttribute); var aliasType = typeof(TypeAliasAttribute);
for (var i = 0; i < m_Types.Length; i++) for (var i = 0; i < m_Types.Length; i++)
{ {

View file

@ -3,7 +3,6 @@
// See LICENSE file in the project root for full license information. // See LICENSE file in the project root for full license information.
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;

View file

@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace System.Buffers namespace System.Buffers

View file

@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading; using System.Threading;
namespace System.Buffers namespace System.Buffers

View file

@ -71,14 +71,18 @@ namespace Server
void OnStatsQuery(Mobile beholder, Mobile beheld); void OnStatsQuery(Mobile beholder, Mobile beheld);
} }
public interface ISpawner // TODO: Add SpawnMap and change Spawner.Map to use it
public interface ISpawner : IEntity
{ {
bool UnlinkOnTaming { get; } bool UnlinkOnTaming { get; }
Point3D HomeLocation { get; } Point3D HomeLocation { get; }
int HomeRange { get; } int HomeRange { get; }
Region Region { get; } Region Region { get; }
bool ReturnOnDeactivate { get; }
void Remove(ISpawnable spawn); void Remove(ISpawnable spawn);
Point3D GetSpawnPosition(ISpawnable spawned, Map map);
void Respawn();
} }
public interface ISpawnable : IEntity public interface ISpawnable : IEntity

View file

@ -0,0 +1,34 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: MapConverterFactory.cs *
* Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class MapConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Map);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new MapConverter();
}
}

View file

@ -0,0 +1,105 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Point2DConverter.cs *
* Created: 2020/04/12 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class Point2DConverter : JsonConverter<Point2D>
{
private Point2D DeserializeArray(ref Utf8JsonReader reader)
{
Span<int> data = stackalloc int[2];
var count = 0;
while (true)
{
reader.Read();
if (reader.TokenType == JsonTokenType.EndArray)
break;
if (reader.TokenType == JsonTokenType.Number)
{
if (count < 2)
data[count] = reader.GetInt32();
count++;
}
}
if (count > 2)
throw new JsonException("Point2D must be an array of x, y");
return new Point2D(data[0], data[1]);
}
private Point2D DeserializeObj(ref Utf8JsonReader reader)
{
Span<int> data = stackalloc int[2];
while (true)
{
reader.Read();
if (reader.TokenType == JsonTokenType.EndObject)
break;
if (reader.TokenType != JsonTokenType.PropertyName)
throw new JsonException("Invalid Json structure for Point2D object");
var key = reader.GetString();
var i = key switch
{
"x" => 0,
"y" => 1,
_ => throw new JsonException($"Invalid property {key} for Point2D")
};
reader.Read();
if (reader.TokenType != JsonTokenType.Number)
throw new JsonException($"Value for {key} must be a number");
data[i] = reader.GetInt32();
}
return new Point2D(data[0], data[1]);
}
public override Point2D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
reader.TokenType switch
{
JsonTokenType.StartArray => DeserializeArray(ref reader),
JsonTokenType.StartObject => DeserializeObj(ref reader),
_ => throw new JsonException("Invalid Json for Point3D")
};
public override void Write(Utf8JsonWriter writer, Point2D value, JsonSerializerOptions options)
{
writer.WriteStartArray();
writer.WriteNumberValue(value.X);
writer.WriteNumberValue(value.Y);
writer.WriteEndArray();
}
}
}

View file

@ -0,0 +1,34 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Point2DConverterFactory.cs *
* Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class Point2DConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Point2D) || typeToConvert == typeof(IPoint2D);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new Point2DConverter();
}
}

View file

@ -2,8 +2,8 @@
* ModernUO * * ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team * * Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com * * Email: hi@modernuo.com *
* File: Point3dConverter.cs * * File: Point3DConverter.cs *
* Created: 2020/04/12 - Updated: 2020/05/02 * * Created: 2020/04/12 - Updated: 2020/05/23 *
* * * *
* This program is free software: you can redistribute it and/or modify * * This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by * * it under the terms of the GNU General Public License as published by *
@ -25,14 +25,11 @@ using System.Text.Json.Serialization;
namespace Server.Json namespace Server.Json
{ {
public class Point3dConverter : JsonConverter<Point3D> public class Point3DConverter : JsonConverter<Point3D>
{ {
public override Point3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) private Point3D DeserializeArray(ref Utf8JsonReader reader)
{ {
if (reader.TokenType != JsonTokenType.StartArray) Span<int> data = stackalloc int[3];
throw new JsonException("Point3d must be an array of x, y, z");
var data = new int[3];
var count = 0; var count = 0;
while (true) while (true)
@ -50,12 +47,54 @@ namespace Server.Json
} }
} }
if (count < 2 || count > 3) if (count > 3)
throw new JsonException("Point3d must be an array of x, y, z"); throw new JsonException("Point3D must be an array of x, y, z");
return new Point3D(data[0], data[1], data[2]); return new Point3D(data[0], data[1], data[2]);
} }
private Point3D DeserializeObj(ref Utf8JsonReader reader)
{
Span<int> data = stackalloc int[3];
while (true)
{
reader.Read();
if (reader.TokenType == JsonTokenType.EndObject)
break;
if (reader.TokenType != JsonTokenType.PropertyName)
throw new JsonException("Invalid Json structure for Point3D object");
var key = reader.GetString();
var i = key switch
{
"x" => 0,
"y" => 1,
"z" => 2,
_ => throw new JsonException($"Invalid property {key} for Point3D")
};
reader.Read();
if (reader.TokenType != JsonTokenType.Number)
throw new JsonException($"Value for {key} must be a number");
data[i] = reader.GetInt32();
}
return new Point3D(data[0], data[1], data[2]);
}
public override Point3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
reader.TokenType switch
{
JsonTokenType.StartArray => DeserializeArray(ref reader),
JsonTokenType.StartObject => DeserializeObj(ref reader),
_ => throw new JsonException("Invalid Json for Point3D")
};
public override void Write(Utf8JsonWriter writer, Point3D value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, Point3D value, JsonSerializerOptions options)
{ {
writer.WriteStartArray(); writer.WriteStartArray();

View file

@ -0,0 +1,34 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Point3DConverterFactory.cs *
* Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class Point3DConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Point3D) || typeToConvert == typeof(IPoint3D);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new Point3DConverter();
}
}

View file

@ -0,0 +1,156 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Rectangle3DConverter.cs *
* Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class Rectangle3DConverter : JsonConverter<Rectangle3D>
{
private Rectangle3D DeserializeArray(ref Utf8JsonReader reader)
{
Span<int> data = stackalloc int[6];
var count = 0;
while (true)
{
reader.Read();
if (reader.TokenType == JsonTokenType.EndArray)
break;
if (reader.TokenType == JsonTokenType.Number)
{
if (count < 6)
data[count] = reader.GetInt32();
count++;
}
}
if (count > 6)
throw new JsonException("Rectangle3D must be an array of x, y, z, h, w, d");
return new Rectangle3D(data[0], data[1], data[2], data[3], data[4], data[5]);
}
private Rectangle3D DeserializeObj(ref Utf8JsonReader reader, JsonSerializerOptions options)
{
Span<int> data = stackalloc int[6];
// 0 - xyzwhd, 1 - x1y1z1x2y2z2, 2 - start/end
int objType = -1;
while (true)
{
reader.Read();
if (reader.TokenType == JsonTokenType.EndObject)
break;
if (reader.TokenType != JsonTokenType.PropertyName)
throw new JsonException("Invalid Json structure for Rectangle3D object");
var key = reader.GetString();
reader.Read();
if (key == "start" || key == "end")
{
if (objType > -1 && objType != 2)
throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both.");
objType = 2;
var point3D = reader.ToObject<Point3D>(options);
var offset = key == "end" ? 3 : 0;
data[0 + offset] = point3D.X;
data[1 + offset] = point3D.Y;
data[1 + offset] = point3D.Z;
continue;
}
var i = key switch
{
"x" => 0,
"y" => 1,
"z" => 2,
"w" => 3,
"width" => 3,
"h" => 4,
"height" => 4,
"d" => 5,
"depth" => 5,
"x1" => 10,
"y1" => 11,
"z1" => 12,
"x2" => 13,
"y2" => 14,
"z2" => 15,
_ => throw new JsonException($"Invalid property {key} for Rectangle3D")
};
if (i < 10)
{
if (objType > -1 && objType != 0)
throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both.");
objType = 0;
data[i] = reader.GetInt32();
continue;
}
if (objType > -1 && objType != 1)
throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both.");
objType = 1;
data[i - 10] = reader.GetInt32();
}
return objType == 0
? new Rectangle3D(data[0], data[1], data[2], data[3], data[4], data[5])
: new Rectangle3D(
new Point3D(data[0], data[1], data[2]),
new Point3D(data[3], data[4], data[5])
);
}
public override Rectangle3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
reader.TokenType switch
{
JsonTokenType.StartArray => DeserializeArray(ref reader),
JsonTokenType.StartObject => DeserializeObj(ref reader, options),
_ => throw new JsonException("Invalid Json for Point3D")
};
public override void Write(Utf8JsonWriter writer, Rectangle3D value, JsonSerializerOptions options)
{
writer.WriteStartArray();
writer.WriteNumberValue(value.Start.X);
writer.WriteNumberValue(value.Start.Y);
writer.WriteNumberValue(value.Start.Z);
writer.WriteNumberValue(value.Width);
writer.WriteNumberValue(value.Height);
writer.WriteNumberValue(value.Depth);
writer.WriteEndArray();
}
}
}

View file

@ -0,0 +1,35 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Rectangle3DConverterFactory.cs *
* Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class Rectangle3DConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Rectangle3D);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) =>
new Rectangle3DConverter();
}
}

View file

@ -0,0 +1,36 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: TimeSpanConverter.cs *
* Created: 2020/04/12 - Updated: 2020/05/02 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class TimeSpanConverter : JsonConverter<TimeSpan>
{
public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> TimeSpan.Parse(reader.GetString());
public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options)
=> writer.WriteStringValue(value.ToString());
}
}

View file

@ -0,0 +1,34 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: TimeSpanConverterFactory.cs *
* Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class TimeSpanConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(TimeSpan);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new TimeSpanConverter();
}
}

View file

@ -0,0 +1,57 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: DynamicJson.cs - Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class DynamicJson
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement> data { get; set; }
public bool GetProperty<T>(string key, JsonSerializerOptions options, out T t)
{
if (data.TryGetValue(key, out var el))
{
t = el.ToObject<T>(options);
return true;
}
t = default;
return false;
}
public bool GetEnumProperty<T>(string key, JsonSerializerOptions options, out T t) where T : struct, Enum
{
if (data.TryGetValue(key, out var el))
return Enum.TryParse(el.ToObject<string>(options), out t);
t = default;
return false;
}
}
}

View file

@ -18,6 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * * along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/ *************************************************************************/
using System;
using System.Buffers;
using System.IO; using System.IO;
using System.Text.Json; using System.Text.Json;
@ -46,5 +48,23 @@ namespace Server.Json
File.WriteAllText(filePath, JsonSerializer.Serialize(value, options ?? Options)); File.WriteAllText(filePath, JsonSerializer.Serialize(value, options ?? Options));
} }
public static T ToObject<T>(this ref Utf8JsonReader reader, JsonSerializerOptions options = null) =>
JsonSerializer.Deserialize<T>(ref reader, options);
public static T ToObject<T>(this JsonElement element, JsonSerializerOptions options = null)
{
var bufferWriter = new ArrayBufferWriter<byte>();
using (var writer = new Utf8JsonWriter(bufferWriter))
element.WriteTo(writer);
return JsonSerializer.Deserialize<T>(bufferWriter.WrittenSpan, options);
}
public static T ToObject<T>(this JsonDocument document, JsonSerializerOptions options = null)
{
if (document == null)
throw new ArgumentNullException(nameof(document));
return document.RootElement.ToObject<T>(options);
}
} }
} }

View file

@ -376,7 +376,7 @@ namespace Server
AssemblyHandler.Invoke("Configure"); AssemblyHandler.Invoke("Configure");
Region.Load(); RegionLoader.LoadRegions();
World.Load(); World.Load();
AssemblyHandler.Invoke("Initialize"); AssemblyHandler.Invoke("Initialize");

View file

@ -374,15 +374,9 @@ namespace Server
public int CompareTo(Map other) => other == null ? -1 : MapID.CompareTo(other.MapID); public int CompareTo(Map other) => other == null ? -1 : MapID.CompareTo(other.MapID);
public static string[] GetMapNames() public static string[] GetMapNames() => Maps.Where(m => m != null).Select(m => m.Name).ToArray();
{
return Maps.Where(m => m != null).Select(m => m.Name).ToArray();
}
public static Map[] GetMapValues() public static Map[] GetMapValues() => Maps.Where(m => m != null).ToArray();
{
return Maps.Where(m => m != null).ToArray();
}
public static Map Parse(string value) public static Map Parse(string value)
{ {

View file

@ -20,7 +20,6 @@
*************************************************************************/ *************************************************************************/
using System; using System;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View file

@ -20,11 +20,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.Text.Json;
using System.Xml; using Server.Json;
using Server.Network; using Server.Network;
using Server.Targeting; using Server.Targeting;
using Server.Utilities;
namespace Server namespace Server
{ {
@ -146,10 +145,10 @@ namespace Server
} }
} }
public Region(XmlElement xml, Map map, Region parent) public Region(DynamicJson json, JsonSerializerOptions options)
{ {
Map = map; Map = json.GetProperty("map", options, out Map map) ? map : null;
Parent = parent; Parent = json.GetProperty("parent", options, out string parent) ? Find(parent, Map) : null;
Dynamic = false; Dynamic = false;
if (Parent == null) if (Parent == null)
@ -163,31 +162,21 @@ namespace Server
m_Priority = Parent.Priority; m_Priority = Parent.Priority;
} }
ReadString(xml, "name", ref m_Name, false); m_Name = json.GetProperty("name", options, out string name) ? name : null;
if (parent == null) m_Priority = json.GetProperty("priority", options, out int priority) ? priority : 0;
ReadInt32(xml, "priority", ref m_Priority, false);
var minZ = MinZ; Area = json.GetProperty("rects", options, out List<Rectangle3D> rects) ?
var maxZ = MaxZ; rects.ToArray() : Array.Empty<Rectangle3D>();
var zrange = xml["zrange"];
ReadInt32(zrange, "min", ref minZ, false);
ReadInt32(zrange, "max", ref maxZ, false);
var area = new List<Rectangle3D>();
foreach (XmlElement xmlRect in xml.SelectNodes("rect"))
{
if (ReadRectangle3D(xmlRect, minZ, maxZ, out var rect))
area.Add(rect);
}
Area = area.ToArray();
if (Area.Length == 0) if (Area.Length == 0)
Console.WriteLine("Empty area for region '{0}'", this); Console.WriteLine("Empty area for region '{0}'", this);
if (!ReadPoint3D(xml["go"], map, ref m_GoLocation, false) && Area.Length > 0) if (json.GetProperty("go", options, out Point3D go))
{
m_GoLocation = go;
}
else if (Area.Length > 0)
{ {
var start = Area[0].Start; var start = Area[0].Start;
var end = Area[0].End; var end = Area[0].End;
@ -195,14 +184,10 @@ namespace Server
var x = start.X + (end.X - start.X) / 2; var x = start.X + (end.X - start.X) / 2;
var y = start.Y + (end.Y - start.Y) / 2; var y = start.Y + (end.Y - start.Y) / 2;
m_GoLocation = new Point3D(x, y, Map.GetAverageZ(x, y)); m_GoLocation = new Point3D(x, y, Map?.GetAverageZ(x, y) ?? start.Z + (end.Z - start.Z) / 2);
} }
var music = DefaultMusic; Music = json.GetEnumProperty("music", options, out MusicName music) ? music : DefaultMusic;
ReadEnum(xml["music"], "name", ref music, false);
Music = music;
} }
public static List<Region> Regions { get; } = new List<Region>(); public static List<Region> Regions { get; } = new List<Region>();
@ -267,6 +252,29 @@ namespace Server
return reg.ChildLevel - ChildLevel; return reg.ChildLevel - ChildLevel;
} }
// This is not optimized. Use sparingly
public static Region Find(string name, Map map, bool insensitive = false)
{
if (insensitive)
name = name.ToLower();
for (int i = 0; i < Regions.Count; i++)
{
var region = Regions[i];
if (region.Map != map)
continue;
string rName = region.Name;
if (insensitive)
rName = rName.ToLower();
if (rName == name)
return region;
}
return null;
}
public static Region Find(Point3D p, Map map) public static Region Find(Point3D p, Map map)
{ {
if (map == null) if (map == null)
@ -727,338 +735,5 @@ namespace Server
} }
} }
} }
internal static void Load()
{
if (!File.Exists("Data/Regions.xml"))
{
Console.WriteLine("Error: Data/Regions.xml does not exist");
return;
}
Console.Write("Regions: Loading...");
var doc = new XmlDocument();
doc.Load(Path.Combine(Core.BaseDirectory, "Data/Regions.xml"));
var root = doc["ServerRegions"];
if (root == null)
{
Console.WriteLine("Could not find root element 'ServerRegions' in Regions.xml");
return;
}
foreach (XmlElement facet in root.SelectNodes("Facet"))
{
Map map = null;
if (ReadMap(facet, "name", ref map))
{
if (map == Map.Internal)
Console.WriteLine("Invalid internal map in a facet element");
else
LoadRegions(facet, map, null);
}
}
Console.WriteLine("done");
}
private static void LoadRegions(XmlElement xml, Map map, Region parent)
{
foreach (XmlElement xmlReg in xml.SelectNodes("region"))
{
var type = DefaultRegionType;
ReadType(xmlReg, "type", ref type, false);
if (!typeof(Region).IsAssignableFrom(type))
{
Console.WriteLine("Invalid region type '{0}' in regions.xml", type.FullName);
continue;
}
Region region;
try
{
region = (Region)ActivatorUtil.CreateInstance(type, xmlReg, map, parent);
}
catch (Exception ex)
{
Console.WriteLine("Error during the creation of region type '{0}': {1}", type.FullName, ex);
continue;
}
region.Register();
LoadRegions(xmlReg, map, region);
}
}
protected static string GetAttribute(XmlElement xml, string attribute, bool mandatory)
{
if (xml == null)
{
if (mandatory)
Console.WriteLine("Missing element for attribute '{0}'", attribute);
return null;
}
if (xml.HasAttribute(attribute)) return xml.GetAttribute(attribute);
if (mandatory)
Console.WriteLine("Missing attribute '{0}' in element '{1}'", attribute, xml.Name);
return null;
}
public static bool ReadString(XmlElement xml, string attribute, ref string value) =>
ReadString(xml, attribute, ref value, true);
public static bool ReadString(XmlElement xml, string attribute, ref string value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
value = s;
return true;
}
public static bool ReadInt32(XmlElement xml, string attribute, ref int value) =>
ReadInt32(xml, attribute, ref value, true);
public static bool ReadInt32(XmlElement xml, string attribute, ref int value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
try
{
value = XmlConvert.ToInt32(s);
}
catch
{
Console.WriteLine("Could not parse integer attribute '{0}' in element '{1}'", attribute, xml.Name);
return false;
}
return true;
}
public static bool ReadBoolean(XmlElement xml, string attribute, ref bool value) =>
ReadBoolean(xml, attribute, ref value, true);
public static bool ReadBoolean(XmlElement xml, string attribute, ref bool value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
try
{
value = XmlConvert.ToBoolean(s);
}
catch
{
Console.WriteLine("Could not parse boolean attribute '{0}' in element '{1}'", attribute, xml.Name);
return false;
}
return true;
}
public static bool ReadDateTime(XmlElement xml, string attribute, ref DateTime value) =>
ReadDateTime(xml, attribute, ref value, true);
public static bool ReadDateTime(XmlElement xml, string attribute, ref DateTime value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
try
{
value = XmlConvert.ToDateTime(s, XmlDateTimeSerializationMode.Utc);
}
catch
{
Console.WriteLine("Could not parse DateTime attribute '{0}' in element '{1}'", attribute, xml.Name);
return false;
}
return true;
}
public static bool ReadTimeSpan(XmlElement xml, string attribute, ref TimeSpan value) =>
ReadTimeSpan(xml, attribute, ref value, true);
public static bool ReadTimeSpan(XmlElement xml, string attribute, ref TimeSpan value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
try
{
value = XmlConvert.ToTimeSpan(s);
}
catch
{
Console.WriteLine("Could not parse TimeSpan attribute '{0}' in element '{1}'", attribute, xml.Name);
return false;
}
return true;
}
public static bool ReadEnum<T>(XmlElement xml, string attribute, ref T value) where T : struct =>
ReadEnum(xml, attribute, ref value, true);
public static bool ReadEnum<T>(XmlElement xml, string attribute, ref T value, bool mandatory)
where T : struct // We can't limit the where clause to Enums only
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
var type = typeof(T);
if (type.IsEnum && Enum.TryParse(s, true, out T tempVal))
{
value = tempVal;
return true;
}
Console.WriteLine("Could not parse {0} enum attribute '{1}' in element '{2}'", type, attribute, xml.Name);
return false;
}
public static bool ReadMap(XmlElement xml, string attribute, ref Map value) => ReadMap(xml, attribute, ref value, true);
public static bool ReadMap(XmlElement xml, string attribute, ref Map value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
try
{
value = Map.Parse(s);
}
catch
{
Console.WriteLine("Could not parse Map attribute '{0}' in element '{1}'", attribute, xml.Name);
return false;
}
return true;
}
public static bool ReadType(XmlElement xml, string attribute, ref Type value) =>
ReadType(xml, attribute, ref value, true);
public static bool ReadType(XmlElement xml, string attribute, ref Type value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
Type type;
try
{
type = AssemblyHandler.FindFirstTypeForName(s);
}
catch
{
Console.WriteLine("Could not parse Type attribute '{0}' in element '{1}'", attribute, xml.Name);
return false;
}
if (type == null)
{
Console.WriteLine("Could not find Type '{0}'", s);
return false;
}
value = type;
return true;
}
public static bool ReadPoint3D(XmlElement xml, Map map, ref Point3D value) => ReadPoint3D(xml, map, ref value, true);
public static bool ReadPoint3D(XmlElement xml, Map map, ref Point3D value, bool mandatory)
{
int x = 0, y = 0, z = 0;
var xyOk = ReadInt32(xml, "x", ref x, mandatory) & ReadInt32(xml, "y", ref y, mandatory);
var zOk = ReadInt32(xml, "z", ref z, mandatory && map == null);
if (xyOk && (zOk || map != null))
{
if (!zOk)
z = map.GetAverageZ(x, y);
value = new Point3D(x, y, z);
return true;
}
return false;
}
public static bool ReadRectangle3D(XmlElement xml, int defaultMinZ, int defaultMaxZ, out Rectangle3D value) =>
ReadRectangle3D(xml, defaultMinZ, defaultMaxZ, out value, true);
public static bool ReadRectangle3D(XmlElement xml, int defaultMinZ, int defaultMaxZ, out Rectangle3D value,
bool mandatory)
{
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
var z1 = defaultMinZ;
var z2 = defaultMaxZ;
if (xml.HasAttribute("x"))
{
if (ReadInt32(xml, "x", ref x1, mandatory)
& ReadInt32(xml, "y", ref y1, mandatory)
& ReadInt32(xml, "width", ref x2, mandatory)
& ReadInt32(xml, "height", ref y2, mandatory))
{
x2 += x1;
y2 += y1;
}
else
{
value = new Rectangle3D(new Point3D(x1, y1, z1), new Point3D(x2, y2, z2));
return false;
}
}
else
{
if (!ReadInt32(xml, "x1", ref x1, mandatory)
| !ReadInt32(xml, "y1", ref y1, mandatory)
| !ReadInt32(xml, "x2", ref x2, mandatory)
| !ReadInt32(xml, "y2", ref y2, mandatory))
{
value = new Rectangle3D(new Point3D(x1, y1, z1), new Point3D(x2, y2, z2));
return false;
}
}
ReadInt32(xml, "zmin", ref z1, false);
ReadInt32(xml, "zmax", ref z2, false);
value = new Rectangle3D(new Point3D(x1, y1, z1), new Point3D(x2, y2, z2));
return true;
}
} }
} }

View file

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.Json;
using Server.Json;
using Server.Utilities;
namespace Server
{
public static class RegionLoader
{
public static void LoadRegions()
{
var path = Path.Join(Core.BaseDirectory, "Data/regions.json");
// Json Deserialization options for custom objects
JsonSerializerOptions options = new JsonSerializerOptions();
options.Converters.Add(new MapConverterFactory());
options.Converters.Add(new Point3DConverterFactory());
options.Converters.Add(new Rectangle3DConverterFactory());
List<string> failures = new List<string>();
int count = 0;
Console.Write("Regions: Loading...");
var stopwatch = Stopwatch.StartNew();
List<DynamicJson> regions = JsonConfig.Deserialize<List<DynamicJson>>(path);
foreach (var json in regions)
{
Type type = AssemblyHandler.FindFirstTypeForName(json.Type);
if (type == null || !typeof(Region).IsAssignableFrom(type))
{
failures.Add($"\tInvalid region type {json.Type}");
continue;
}
var region = ActivatorUtil.CreateInstance(type, json, options) as Region;
region?.Register();
count++;
}
stopwatch.Stop();
Console.ForegroundColor = failures.Count > 0 ? ConsoleColor.Yellow : ConsoleColor.Green;
Console.Write("done{0}. ", failures.Count > 0 ? " with failures" : "");
Console.ResetColor();
Console.WriteLine("({0} regions, {1} failures) ({2:F2} seconds)", count, failures.Count, stopwatch.Elapsed.TotalSeconds);
if (failures.Count > 0)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(string.Join("\n", failures));
Console.ResetColor();
}
}
}
}

View file

@ -75,10 +75,10 @@
<CodeAnalysisRuleSet>..\..\Rules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Rules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="3.1.3" /> <PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="3.1.4" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" Version="3.1.3" /> <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" Version="3.1.4" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.3" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.4" />
<PackageReference Include="System.IO.Pipelines" Version="4.7.1" /> <PackageReference Include="System.IO.Pipelines" Version="4.7.2" />
<PackageReference Include="ZLib.Bindings" Version="1.0.0" /> <PackageReference Include="ZLib.Bindings" Version="1.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Analyze'"> <ItemGroup Condition="'$(Configuration)'=='Analyze'">
@ -87,7 +87,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers"> <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers">
<Version>2.9.8</Version> <Version>3.0.0</Version>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View file

@ -19,7 +19,6 @@
***************************************************************************/ ***************************************************************************/
using System; using System;
using System.Buffers.Binary;
using System.IO; using System.IO;
using System.Text; using System.Text;

View file

@ -47,7 +47,7 @@ namespace Server.Utilities
// check all given args map to params. // check all given args map to params.
for (var i = 0; i < args.Length; i++) for (var i = 0; i < args.Length; i++)
// if a null reference is passed, but the type is not nullable // if a null reference is passed, but the type is not nullable
if ((args[i] == null && paramList[i].ParameterType.IsValueType) if (args[i] == null && paramList[i].ParameterType.IsValueType
// or if an arg is not null and is not assignable to the parameter type, skip. // or if an arg is not null and is not assignable to the parameter type, skip.
|| !(args[i] == null || paramList[i].ParameterType.IsAssignableFrom(args[i]))) || !(args[i] == null || paramList[i].ParameterType.IsAssignableFrom(args[i])))
return false; return false;
@ -65,7 +65,8 @@ namespace Server.Utilities
} }
catch (Exception e) catch (Exception e)
{ {
throw new TypeInitializationException(type.ToString(), e); Console.WriteLine(e);
throw;
} }
} }

View file

@ -24,7 +24,8 @@ namespace Server
{ {
public interface IRandomProvider public interface IRandomProvider
{ {
public uint Next(uint c); public ulong Next(ulong max);
public uint Next(uint max);
public bool NextBool(); public bool NextBool();
public void GetBytes(Span<byte> b); public void GetBytes(Span<byte> b);
public double NextDouble(); public double NextDouble();

View file

@ -31,6 +31,8 @@ namespace Server
public override void GetBytes(Span<byte> data) => m_Random.GetBytes(data); public override void GetBytes(Span<byte> data) => m_Random.GetBytes(data);
public ulong Next(ulong c) => throw new NotImplementedException();
public uint Next(uint c) => throw new NotImplementedException(); public uint Next(uint c) => throw new NotImplementedException();
public bool NextBool() => throw new NotImplementedException(); public bool NextBool() => throw new NotImplementedException();

View file

@ -219,7 +219,7 @@ namespace Server.Misc
state.Send(new CharacterListUpdate(acct)); state.Send(new CharacterListUpdate(acct));
} }
else if (m.AccessLevel == AccessLevel.Player && else if (m.AccessLevel == AccessLevel.Player &&
Region.Find(m.LogoutLocation, m.LogoutMap).IsPartOf<Jail>()) // Don't need to check current location, if netstate is null, they're logged out Region.Find(m.LogoutLocation, m.LogoutMap).IsPartOf<JailRegion>()) // Don't need to check current location, if netstate is null, they're logged out
{ {
state.Send(new DeleteResult(DeleteResultType.BadRequest)); state.Send(new DeleteResult(DeleteResultType.BadRequest));
state.Send(new CharacterListUpdate(acct)); state.Send(new CharacterListUpdate(acct));

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using Server.Items; using Server.Items;
@ -190,13 +191,9 @@ namespace Server.Commands
if (!IsConstructible(ctor, from.AccessLevel)) if (!IsConstructible(ctor, from.AccessLevel))
continue; continue;
int totalParams = 0;
// Handle optional constructors // Handle optional constructors
ParameterInfo[] paramList = ctor.GetParameters(); ParameterInfo[] paramList = ctor.GetParameters();
for (int j = 0; j < paramList.Length; j++) int totalParams = paramList.Count(t => !t.HasDefaultValue);
if (!paramList[j].HasDefaultValue)
totalParams += 1;
if (args.Length >= totalParams && args.Length <= paramList.Length) if (args.Length >= totalParams && args.Length <= paramList.Length)
{ {

View file

@ -4,8 +4,8 @@ using System.IO;
using System.Linq; using System.Linq;
using Server.Engines.Quests.Haven; using Server.Engines.Quests.Haven;
using Server.Engines.Quests.Necro; using Server.Engines.Quests.Necro;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles;
using Server.Utilities; using Server.Utilities;
namespace Server.Commands namespace Server.Commands

View file

@ -5,8 +5,8 @@ using System.IO;
using System.Linq; using System.Linq;
using Server.Engines.Quests.Haven; using Server.Engines.Quests.Haven;
using Server.Engines.Quests.Necro; using Server.Engines.Quests.Necro;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles;
using Server.Utilities; using Server.Utilities;
namespace Server.Commands namespace Server.Commands

View file

@ -11,7 +11,7 @@ namespace Server.Commands
{ {
public struct Location public struct Location
{ {
[JsonPropertyName("point"), JsonConverter(typeof(Point3dConverter))] [JsonPropertyName("point"), JsonConverter(typeof(Point3DConverter))]
public Point3D Pos { get; set; } public Point3D Pos { get; set; }
[JsonPropertyName("map"), JsonConverter(typeof(MapConverter))] [JsonPropertyName("map"), JsonConverter(typeof(MapConverter))]
public Map Map { get; set; } public Map Map { get; set; }

View file

@ -340,7 +340,7 @@ namespace Server.Commands
int v = -aCount.CompareTo(bCount); int v = -aCount.CompareTo(bCount);
return v != 0 ? v : x.Key.FullName.CompareTo(y.Key.FullName); return v != 0 ? v : x.Key.FullName?.CompareTo(y.Key.FullName) ?? -1;
} }
} }
@ -353,7 +353,7 @@ namespace Server.Commands
int v = -aCount.CompareTo(bCount); int v = -aCount.CompareTo(bCount);
return v != 0 ? v : x.Key.FullName.CompareTo(y.Key.FullName); return v != 0 ? v : x.Key.FullName?.CompareTo(y.Key.FullName) ?? 1;
} }
} }
} }

View file

@ -1,4 +1,3 @@
using System;
using Server.Accounting; using Server.Accounting;
using Server.Items; using Server.Items;
using Server.Network; using Server.Network;

View file

@ -1148,7 +1148,7 @@ namespace Server.Engines.ConPVP
if (!pm.CheckAlive()) if (!pm.CheckAlive())
{ {
} }
else if (pm.Region.IsPartOf<Jail>()) else if (pm.Region.IsPartOf<JailRegion>())
{ {
} }
else if (CheckCombat(pm)) else if (CheckCombat(pm))
@ -1942,7 +1942,7 @@ namespace Server.Engines.ConPVP
if (dp == null) if (dp == null)
return "a slot is empty"; return "a slot is empty";
if (dp.Mobile.Region.IsPartOf<Jail>()) if (dp.Mobile.Region.IsPartOf<JailRegion>())
return $"{dp.Mobile.Name} is in jail"; return $"{dp.Mobile.Name} is in jail";
if (Sigil.ExistsOn(dp.Mobile)) if (Sigil.ExistsOn(dp.Mobile))

View file

@ -595,7 +595,7 @@ namespace Server.Engines.ConPVP
Mobile check = part.Players[j]; Mobile check = part.Players[j];
if (check.Deleted || check.Map == null || check.Map == Map.Internal || !check.Alive || if (check.Deleted || check.Map == null || check.Map == Map.Internal || !check.Alive ||
Sigil.ExistsOn(check) || check.Region.IsPartOf<Jail>()) Sigil.ExistsOn(check) || check.Region.IsPartOf<JailRegion>())
{ {
bad = true; bad = true;
break; break;
@ -776,7 +776,7 @@ namespace Server.Engines.ConPVP
Mobile check = part.Players[j]; Mobile check = part.Players[j];
if (check.Deleted || check.Map == null || check.Map == Map.Internal || !check.Alive || if (check.Deleted || check.Map == null || check.Map == Map.Internal || !check.Alive ||
Sigil.ExistsOn(check) || check.Region.IsPartOf<Jail>()) Sigil.ExistsOn(check) || check.Region.IsPartOf<JailRegion>())
{ {
bad = true; bad = true;
break; break;

View file

@ -254,7 +254,7 @@ namespace Server.Engines.Help
{ {
from.Location = house.BanLocation; from.Location = house.BanLocation;
} }
else if (from.Region.IsPartOf<Jail>()) else if (from.Region.IsPartOf<JailRegion>())
{ {
from.SendLocalizedMessage(1114345, "", 0x35); // You'll need a better jailbreak plan than that! from.SendLocalizedMessage(1114345, "", 0x35); // You'll need a better jailbreak plan than that!
} }
@ -313,7 +313,7 @@ namespace Server.Engines.Help
{ {
if (IsYoung(from)) if (IsYoung(from))
{ {
if (from.Region.IsPartOf<Jail>()) if (from.Region.IsPartOf<JailRegion>())
from.SendLocalizedMessage(1114345, "", 0x35); // You'll need a better jailbreak plan than that! from.SendLocalizedMessage(1114345, "", 0x35); // You'll need a better jailbreak plan than that!
else if (from.Region.IsPartOf("Haven Island")) else if (from.Region.IsPartOf("Haven Island"))
from.SendLocalizedMessage(1041529); // You're already in Haven from.SendLocalizedMessage(1041529); // You're already in Haven

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Server.Configurations;
using Server.Misc; using Server.Misc;
using Server.Mobiles; using Server.Mobiles;
using Server.Network; using Server.Network;

View file

@ -1,6 +1,7 @@
using System; using System;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -1,5 +1,6 @@
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System; using System;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;
@ -275,4 +276,4 @@ namespace Server.Engines.MLQuests.Definitions
reader.ReadInt(); reader.ReadInt();
} }
} }
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -1,5 +1,6 @@
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System; using System;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System; using System;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System; using System;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -1,5 +1,6 @@
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;
@ -82,4 +83,4 @@ namespace Server.Engines.MLQuests.Definitions
int version = reader.ReadInt(); int version = reader.ReadInt();
} }
} }
} }

View file

@ -2,6 +2,7 @@ using System;
using Server.Engines.MLQuests.Items; using Server.Engines.MLQuests.Items;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;
@ -2518,4 +2519,4 @@ namespace Server.Engines.MLQuests.Definitions
int version = reader.ReadInt(); int version = reader.ReadInt();
} }
} }
} }

View file

@ -1,5 +1,6 @@
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System; using System;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System; using System;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System; using System;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -1,6 +1,7 @@
using System; using System;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;

View file

@ -4,6 +4,7 @@ using System.Linq;
using Server.Engines.MLQuests.Gumps; using Server.Engines.MLQuests.Gumps;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
using Server.Engines.Spawners;
using Server.Mobiles; using Server.Mobiles;
namespace Server.Engines.MLQuests namespace Server.Engines.MLQuests

View file

@ -162,7 +162,7 @@ namespace Server.Engines.Quests
public override void UseGate(Mobile m) public override void UseGate(Mobile m)
{ {
if (m.Region.IsPartOf<Jail>()) if (m.Region.IsPartOf<JailRegion>())
{ {
m.SendLocalizedMessage(1114345); // You'll need a better jailbreak plan than that! m.SendLocalizedMessage(1114345); // You'll need a better jailbreak plan than that!
} }

View file

@ -1,41 +0,0 @@
using System;
using System.Xml;
using Server.Mobiles;
using Server.Regions;
namespace Server.Engines.Quests
{
public class CancelQuestRegion : BaseRegion
{
private readonly Type m_Quest;
public CancelQuestRegion(XmlElement xml, Map map, Region parent) : base(xml, map, parent)
{
ReadType(xml["quest"], "type", ref m_Quest);
}
public Type Quest => m_Quest;
public override bool OnMoveInto(Mobile m, Direction d, Point3D newLocation, Point3D oldLocation)
{
if (!base.OnMoveInto(m, d, newLocation, oldLocation))
return false;
if (m.AccessLevel > AccessLevel.Player)
return true;
if (m_Quest == null)
return true;
if (m is PlayerMobile player && player.Quest != null && player.Quest.GetType() == m_Quest)
{
if (!player.HasGump<QuestCancelGump>())
player.Quest.BeginCancelQuest();
return false;
}
return true;
}
}
}

View file

@ -1,38 +0,0 @@
using System;
using System.Xml;
using Server.Mobiles;
using Server.Regions;
namespace Server.Engines.Quests
{
public class QuestCompleteObjectiveRegion : BaseRegion
{
private readonly Type m_Objective;
private readonly Type m_Quest;
public QuestCompleteObjectiveRegion(XmlElement xml, Map map, Region parent) : base(xml, map, parent)
{
XmlElement questEl = xml["quest"];
ReadType(questEl, "type", ref m_Quest);
ReadType(questEl, "complete", ref m_Objective);
}
public Type Quest => m_Quest;
public Type Objective => m_Objective;
public override void OnEnter(Mobile m)
{
base.OnEnter(m);
if (m_Quest != null && m_Objective != null)
if (m is PlayerMobile player && player.Quest != null && player.Quest.GetType() == m_Quest)
{
QuestObjective obj = player.Quest.FindObjective(m_Objective);
if (obj?.Completed == false)
obj.Complete();
}
}
}
}

View file

@ -1,55 +0,0 @@
using System;
using System.Xml;
using Server.Mobiles;
using Server.Regions;
namespace Server.Engines.Quests
{
public class QuestNoEntryRegion : BaseRegion
{
private readonly Type m_MaxObjective;
private readonly int m_Message;
private readonly Type m_MinObjective;
private readonly Type m_Quest;
public QuestNoEntryRegion(XmlElement xml, Map map, Region parent) : base(xml, map, parent)
{
XmlElement questEl = xml["quest"];
ReadType(questEl, "type", ref m_Quest);
ReadType(questEl, "min", ref m_MinObjective, false);
ReadType(questEl, "max", ref m_MaxObjective, false);
ReadInt32(questEl, "message", ref m_Message, false);
}
public Type Quest => m_Quest;
public Type MinObjective => m_MinObjective;
public Type MaxObjective => m_MaxObjective;
public int Message => m_Message;
public override bool OnMoveInto(Mobile m, Direction d, Point3D newLocation, Point3D oldLocation)
{
if (!base.OnMoveInto(m, d, newLocation, oldLocation))
return false;
if (m.AccessLevel > AccessLevel.Player)
return true;
if (m is BaseCreature bc && !bc.Controlled && !bc.Summoned)
return true;
if (m_Quest == null)
return true;
if (m is PlayerMobile player && player.Quest != null && player.Quest.GetType() == m_Quest
&& (m_MinObjective == null || player.Quest.FindObjective(m_MinObjective) != null)
&& (m_MaxObjective == null || player.Quest.FindObjective(m_MaxObjective) == null))
return true;
if (m_Message != 0)
m.SendLocalizedMessage(m_Message);
return false;
}
}
}

View file

@ -1,39 +0,0 @@
using System;
using System.Xml;
using Server.Mobiles;
using Server.Regions;
using Server.Utilities;
namespace Server.Engines.Quests
{
public class QuestOfferRegion : BaseRegion
{
private readonly Type m_Quest;
public QuestOfferRegion(XmlElement xml, Map map, Region parent) : base(xml, map, parent)
{
ReadType(xml["quest"], "type", ref m_Quest);
}
public Type Quest => m_Quest;
public override void OnEnter(Mobile m)
{
base.OnEnter(m);
if (m_Quest == null)
return;
if (m is PlayerMobile player && player.Quest == null && QuestSystem.CanOfferQuest(m, m_Quest))
try
{
QuestSystem qs = (QuestSystem)ActivatorUtil.CreateInstance(m_Quest, player);
qs.SendOffer();
}
catch (Exception ex)
{
Console.WriteLine("Error creating quest {0}: {1}", m_Quest, ex);
}
}
}
}

View file

@ -1,9 +0,0 @@
using System;
namespace Server.Mobiles
{
public class SpawnerType
{
public static Type GetType(string name) => AssemblyHandler.FindFirstTypeForName(name);
}
}

View file

@ -1,49 +1,47 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.Json;
using Server.Commands; using Server.Commands;
using Server.Items; using Server.Items;
using Server.Json;
using Server.Mobiles;
using Server.Utilities; using Server.Utilities;
using CPA = Server.CommandPropertyAttribute; using CPA = Server.CommandPropertyAttribute;
namespace Server.Mobiles namespace Server.Engines.Spawners
{ {
public class Spawner : Item, ISpawner public abstract class BaseSpawner : Item, ISpawner
{ {
private static WarnTimer m_WarnTimer; private static WarnTimer m_WarnTimer;
private int m_Count; private int m_Count;
private bool m_Group; private bool m_Group;
private int m_HomeRange; private int m_HomeRange;
private int m_Team;
private TimeSpan m_MaxDelay; private TimeSpan m_MaxDelay;
private TimeSpan m_MinDelay; private TimeSpan m_MinDelay;
private bool m_Running; private bool m_Running;
private int m_Team;
private InternalTimer m_Timer; private InternalTimer m_Timer;
private int m_WalkingRange = -1; private int m_WalkingRange = -1;
// [Constructible] public BaseSpawner() : this(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), 0, 4)
public Spawner(int amount, int minDelay, int maxDelay, int team, int homeRange, string spawnedNames) : base(0x1f13)
{ {
InitSpawn(amount, TimeSpan.FromMinutes(minDelay), TimeSpan.FromMinutes(maxDelay), team, homeRange);
AddEntry(spawnedNames, 100, amount, false);
} }
// [Constructible] public BaseSpawner(string spawnedName) : this(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), 0, 4, spawnedName)
public Spawner(string spawnedName) : base(0x1f13)
{ {
InitSpawn(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), 0, 4);
AddEntry(spawnedName, 100, 1, false);
} }
[Constructible(AccessLevel.Developer)] public BaseSpawner(int amount, int minDelay, int maxDelay, int team, int homeRange,
public Spawner() : base(0x1f13) params string[] spawnedNames) : this(amount, TimeSpan.FromMinutes(minDelay), TimeSpan.FromMinutes(maxDelay),
team, homeRange, spawnedNames)
{ {
InitSpawn(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), 0, 4);
} }
public Spawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, public BaseSpawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange,
params string[] spawnedNames) : base(0x1f13) params string[] spawnedNames) : base(0x1f13)
{ {
InitSpawn(amount, minDelay, maxDelay, team, homeRange); InitSpawn(amount, minDelay, maxDelay, team, homeRange);
@ -51,7 +49,25 @@ namespace Server.Mobiles
AddEntry(spawnedNames[i], 100, amount, false); AddEntry(spawnedNames[i], 100, amount, false);
} }
public Spawner(Serial serial) : base(serial) public BaseSpawner(DynamicJson json, JsonSerializerOptions options) : base(0x1f13)
{
json.GetProperty("count", options, out int amount);
json.GetProperty("minDelay", options, out TimeSpan minDelay);
json.GetProperty("maxDelay", options, out TimeSpan maxDelay);
json.GetProperty("team", options, out int team);
json.GetProperty("homeRange", options, out int homeRange);
json.GetProperty("walkingRange", options, out int walkingRange);
m_WalkingRange = walkingRange;
InitSpawn(amount, minDelay, maxDelay, team, homeRange);
json.GetProperty("entries", options, out List<SpawnerEntry> entries);
foreach (var entry in entries)
AddEntry(entry.SpawnedName, entry.SpawnedProbability, entry.SpawnedMaxCount, false);
}
public BaseSpawner(Serial serial) : base(serial)
{ {
} }
@ -64,6 +80,9 @@ namespace Server.Mobiles
public Dictionary<ISpawnable, SpawnerEntry> Spawned { get; private set; } public Dictionary<ISpawnable, SpawnerEntry> Spawned { get; private set; }
[CommandProperty(AccessLevel.Developer)]
public bool ReturnOnDeactivate { get; set; }
[CommandProperty(AccessLevel.Developer)] [CommandProperty(AccessLevel.Developer)]
public int Count public int Count
{ {
@ -72,9 +91,8 @@ namespace Server.Mobiles
{ {
m_Count = value; m_Count = value;
if (m_Timer != null) if (m_Timer != null && (!IsFull && !m_Timer.Running || IsFull && m_Timer.Running))
if ((!IsFull && !m_Timer.Running) || (IsFull && m_Timer.Running)) DoTimer();
DoTimer();
InvalidateProperties(); InvalidateProperties();
} }
@ -164,7 +182,7 @@ namespace Server.Mobiles
} }
} }
public Point3D HomeLocation => Location; public virtual Point3D HomeLocation => Location;
public bool UnlinkOnTaming => true; public bool UnlinkOnTaming => true;
[CommandProperty(AccessLevel.Developer)] [CommandProperty(AccessLevel.Developer)]
@ -199,7 +217,7 @@ namespace Server.Mobiles
public override void OnAfterDuped(Item newItem) public override void OnAfterDuped(Item newItem)
{ {
if (newItem is Spawner newSpawner) if (newItem is BaseSpawner newSpawner)
for (int i = 0; i < Entries.Count; i++) for (int i = 0; i < Entries.Count; i++)
newSpawner.AddEntry(Entries[i].SpawnedName, Entries[i].SpawnedProbability, Entries[i].SpawnedMaxCount, newSpawner.AddEntry(Entries[i].SpawnedName, Entries[i].SpawnedProbability, Entries[i].SpawnedMaxCount,
false); false);
@ -238,6 +256,10 @@ namespace Server.Mobiles
from.SendGump(new SpawnerGump(this)); from.SendGump(new SpawnerGump(this));
} }
public virtual void GetSpawnerProperties(ObjectPropertyList list)
{
}
public override void GetProperties(ObjectPropertyList list) public override void GetProperties(ObjectPropertyList list)
{ {
base.GetProperties(list); base.GetProperties(list);
@ -248,14 +270,16 @@ namespace Server.Mobiles
list.Add(1060656, m_Count.ToString()); // amount to make: ~1_val~ list.Add(1060656, m_Count.ToString()); // amount to make: ~1_val~
list.Add(1061169, m_HomeRange.ToString()); // range ~1_val~ list.Add(1061169, m_HomeRange.ToString()); // range ~1_val~
list.Add(1060658, "walking range\t{0}", m_WalkingRange); // ~1_val~: ~2_val~ list.Add(1050039, "walking range:\t{0}", m_WalkingRange); // ~1_NUMBER~ ~2_ITEMNAME~
list.Add(1060658, "group\t{0}", m_Group); // ~1_val~: ~2_val~ list.Add(1053099, "group:\t{0}", m_Group); // ~1_oretype~: ~2_armortype~
list.Add(1060659, "team\t{0}", m_Team); // ~1_val~: ~2_val~ list.Add(1060847, "team:\t{0}", m_Team); // ~1_val~ ~2_val~
list.Add(1060660, "speed\t{0} to {1}", m_MinDelay, m_MaxDelay); // ~1_val~: ~2_val~ list.Add(1063483, "delay:\t{0} to {1}", m_MinDelay, m_MaxDelay); // ~1_MATERIAL~: ~2_ITEMNAME~
for (int i = 0; i < 3 && i < Entries.Count; ++i) GetSpawnerProperties(list);
list.Add(1060661 + i, "{0}\t{1}", Entries[i].SpawnedName, CountSpawns(Entries[i]));
for (int i = 0; i < 6 && i < Entries.Count; ++i)
list.Add(1060658 + i, "\t{0}\t{1}", Entries[i].SpawnedName, CountSpawns(Entries[i]));
} }
else else
{ {
@ -300,6 +324,22 @@ namespace Server.Mobiles
Entries[i].Defrag(this); Entries[i].Defrag(this);
} }
public virtual bool OnDefragSpawn(ISpawnable spawned, bool remove)
{
if (!remove) // Override could have set it to true already
remove = spawned.Deleted || spawned.Spawner == null || spawned switch
{
Item item => item.RootParent is Mobile || item.IsLockedDown || item.IsSecure,
Mobile m => m is BaseCreature c && (c.Controlled || c.IsStabled),
_ => true
};
if (remove)
Spawned.Remove(spawned);
return remove;
}
public void OnTick() public void OnTick()
{ {
if (m_Group) if (m_Group)
@ -336,11 +376,7 @@ namespace Server.Mobiles
if (Entries.Count <= 0 || IsFull) if (Entries.Count <= 0 || IsFull)
return; return;
int probsum = 0; int probsum = Entries.Where(t => !t.IsFull).Sum(t => t.SpawnedProbability);
for (int i = 0; i < Entries.Count; i++)
if (!Entries[i].IsFull)
probsum += Entries[i].SpawnedProbability;
if (probsum <= 0) if (probsum <= 0)
return; return;
@ -443,7 +479,7 @@ namespace Server.Mobiles
// Defrag taken care of in Spawn(), beforehand // Defrag taken care of in Spawn(), beforehand
// Count check taken care of in Spawn(), beforehand // Count check taken care of in Spawn(), beforehand
Type type = SpawnerType.GetType(entry.SpawnedName); Type type = AssemblyHandler.FindFirstTypeForName(entry.SpawnedName);
if (type != null) if (type != null)
{ {
@ -453,10 +489,8 @@ namespace Server.Mobiles
string[] paramargs; string[] paramargs;
string[] propargs; string[] propargs;
if (string.IsNullOrEmpty(entry.Properties)) propargs = string.IsNullOrEmpty(entry.Properties) ?
propargs = Array.Empty<string>(); Array.Empty<string>() : CommandSystem.Split(entry.Properties.Trim());
else
propargs = CommandSystem.Split(entry.Properties.Trim());
string[,] props = FormatProperties(propargs); string[,] props = FormatProperties(propargs);
@ -468,10 +502,8 @@ namespace Server.Mobiles
return false; return false;
} }
if (string.IsNullOrEmpty(entry.Parameters)) paramargs = string.IsNullOrEmpty(entry.Parameters) ?
paramargs = Array.Empty<string>(); Array.Empty<string>() : entry.Parameters.Trim().Split(' ');
else
paramargs = entry.Parameters.Trim().Split(' ');
if (paramargs.Length == 0) if (paramargs.Length == 0)
{ {
@ -504,7 +536,6 @@ namespace Server.Mobiles
} }
for (int i = 0; i < realProps.Length; i++) for (int i = 0; i < realProps.Length; i++)
{
if (realProps[i] != null) if (realProps[i] != null)
{ {
object toSet = null; object toSet = null;
@ -523,7 +554,6 @@ namespace Server.Mobiles
return false; return false;
} }
} }
}
if (o is Mobile m) if (o is Mobile m)
{ {
@ -591,75 +621,7 @@ namespace Server.Mobiles
public virtual int GetWalkingRange() => m_WalkingRange; public virtual int GetWalkingRange() => m_WalkingRange;
public virtual Point3D GetSpawnPosition(ISpawnable spawned, Map map) public abstract Point3D GetSpawnPosition(ISpawnable spawned, Map map);
{
if (map == null || map == Map.Internal)
return Location;
bool waterMob, waterOnlyMob;
if (spawned is Mobile mob)
{
waterMob = mob.CanSwim;
waterOnlyMob = mob.CanSwim && mob.CantWalk;
}
else
{
waterMob = false;
waterOnlyMob = false;
}
// Try 10 times to find a Spawnable location.
for (int i = 0; i < 10; i++)
{
int x = Location.X + (Utility.Random(m_HomeRange * 2 + 1) - m_HomeRange);
int y = Location.Y + (Utility.Random(m_HomeRange * 2 + 1) - m_HomeRange);
int mapZ = map.GetAverageZ(x, y);
if (waterMob)
{
if (IsValidWater(map, x, y, Z))
return new Point3D(x, y, Z);
if (IsValidWater(map, x, y, mapZ))
return new Point3D(x, y, mapZ);
}
if (!waterOnlyMob)
{
if (map.CanSpawnMobile(x, y, Z))
return new Point3D(x, y, Z);
if (map.CanSpawnMobile(x, y, mapZ))
return new Point3D(x, y, mapZ);
}
}
return Location;
}
public static bool IsValidWater(Map map, int x, int y, int z)
{
if (!Region.Find(new Point3D(x, y, z), map).AllowSpawn() || !map.CanFit(x, y, z, 16, false, true, false))
return false;
LandTile landTile = map.Tiles.GetLandTile(x, y);
if (landTile.Z == z && (TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags & TileFlag.Wet) != 0)
return true;
StaticTile[] staticTiles = map.Tiles.GetStaticTiles(x, y, true);
for (int i = 0; i < staticTiles.Length; ++i)
{
StaticTile staticTile = staticTiles[i];
if (staticTile.Z == z &&
(TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Flags & TileFlag.Wet) != 0)
return true;
}
return false;
}
public virtual Map GetSpawnMap() => Map; public virtual Map GetSpawnMap() => Map;
@ -783,7 +745,9 @@ namespace Server.Mobiles
{ {
base.Serialize(writer); base.Serialize(writer);
writer.Write(7); // version writer.Write(8); // version
writer.Write(ReturnOnDeactivate);
writer.Write(Entries.Count); writer.Write(Entries.Count);
@ -820,6 +784,11 @@ namespace Server.Mobiles
switch (version) switch (version)
{ {
case 8:
{
ReturnOnDeactivate = reader.ReadBool();
goto case 7;
}
case 7: case 7:
{ {
int size = reader.ReadInt(); int size = reader.ReadInt();
@ -909,7 +878,7 @@ namespace Server.Mobiles
else else
Entries[i].SpawnedName = typeName; Entries[i].SpawnedName = typeName;
if (SpawnerType.GetType(typeName) == null) if (AssemblyHandler.FindFirstTypeForName(typeName) == null)
{ {
m_WarnTimer ??= new WarnTimer(); m_WarnTimer ??= new WarnTimer();
@ -928,7 +897,7 @@ namespace Server.Mobiles
e.Spawner = this; e.Spawner = this;
for (int j = 0; j < Entries.Count; j++) for (int j = 0; j < Entries.Count; j++)
if (SpawnerType.GetType(Entries[j].SpawnedName) == e.GetType()) if (AssemblyHandler.FindFirstTypeForName(Entries[j].SpawnedName) == e.GetType())
{ {
Entries[j].Spawned.Add(e); Entries[j].Spawned.Add(e);
Spawned.Add(e, Entries[j]); Spawned.Add(e, Entries[j]);
@ -947,30 +916,11 @@ namespace Server.Mobiles
m_WalkingRange = m_HomeRange; m_WalkingRange = m_HomeRange;
} }
public static string ConvertTypes(string type)
{
type = type.ToLower();
return type switch
{
"wheat" => "WheatSheaf",
"noxxiousmage" => "NoxiousMage",
"noxxiousarcher" => "NoxiousArcher",
"noxxiouswarrior" => "NoxiousWarrior",
"noxxiouswarlord" => "NoxiousWarlord",
"obsidian" => "obsidianstatue",
"adeepwaterelemental" => "deepwaterelemental",
"noxskeleton" => "poisonskeleton",
"earthcaller" => "earthsummoner",
"bonedemon" => "bonedaemon",
_ => type
};
}
private class InternalTimer : Timer private class InternalTimer : Timer
{ {
private readonly Spawner m_Spawner; private readonly BaseSpawner m_Spawner;
public InternalTimer(Spawner spawner, TimeSpan delay) : base(delay) public InternalTimer(BaseSpawner spawner, TimeSpan delay) : base(delay)
{ {
if (spawner.IsFull) if (spawner.IsFull)
Priority = TimerPriority.FiveSeconds; Priority = TimerPriority.FiveSeconds;
@ -1052,142 +1002,4 @@ namespace Server.Mobiles
InvalidProps = 0x004, InvalidProps = 0x004,
InvalidEntry = 0x008 InvalidEntry = 0x008
} }
public class SpawnerEntry
{
public SpawnerEntry(string name, int probability, int maxcount)
{
SpawnedName = name;
SpawnedProbability = probability;
SpawnedMaxCount = maxcount;
Spawned = new List<ISpawnable>();
}
public SpawnerEntry(Spawner parent, IGenericReader reader)
{
int version = reader.ReadInt();
SpawnedName = reader.ReadString();
SpawnedProbability = reader.ReadInt();
SpawnedMaxCount = reader.ReadInt();
Properties = reader.ReadString();
Parameters = reader.ReadString();
int count = reader.ReadInt();
Spawned = new List<ISpawnable>(count);
for (int i = 0; i < count; ++i)
// IEntity e = World.FindEntity( reader.ReadInt() );
if (reader.ReadEntity() is ISpawnable e)
{
e.Spawner = parent;
if (e is BaseCreature creature)
creature.RemoveIfUntamed = true;
Spawned.Add(e);
if (!parent.Spawned.ContainsKey(e))
parent.Spawned.Add(e, this);
}
}
public int SpawnedProbability { get; set; }
public int SpawnedMaxCount { get; set; }
public string SpawnedName { get; set; }
public string Properties { get; set; }
public string Parameters { get; set; }
public EntryFlags Valid { get; set; }
public List<ISpawnable> Spawned { get; }
public bool IsFull => Spawned.Count >= SpawnedMaxCount;
public void Serialize(IGenericWriter writer)
{
writer.Write(0); // version
writer.Write(SpawnedName);
writer.Write(SpawnedProbability);
writer.Write(SpawnedMaxCount);
writer.Write(Properties);
writer.Write(Parameters);
writer.Write(Spawned.Count);
for (int i = 0; i < Spawned.Count; ++i)
{
object o = Spawned[i];
if (o is Item item)
writer.Write(item);
else if (o is Mobile mobile)
writer.Write(mobile);
else
writer.Write(Serial.MinusOne);
}
}
public void Defrag(Spawner parent)
{
for (int i = 0; i < Spawned.Count; ++i)
{
ISpawnable e = Spawned[i];
bool remove = false;
if (e is Item item)
{
if (item.Deleted || item.RootParent is Mobile || item.IsLockedDown || item.IsSecure ||
item.Spawner == null)
remove = true;
}
else if (e is Mobile m)
{
if (m.Deleted)
{
remove = true;
}
else if (m is BaseCreature c)
{
if (c.Controlled || c.IsStabled)
remove = true;
/*
else if (c.Combatant == null && ( c.GetDistanceToSqrt( Location ) > (c.RangeHome * 4) ))
{
//m_Spawned[i].Delete();
m_Spawned.RemoveAt( i );
--i;
c.Delete();
remove = true;
}
*/
}
else if (m.Spawner == null)
{
remove = true;
}
}
else
{
remove = true;
}
if (remove)
{
Spawned.RemoveAt(i--);
if (parent.Spawned.ContainsKey(e))
parent.Spawned.Remove(e);
}
}
}
}
} }

View file

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using Server.Json;
using Server.Utilities;
namespace Server.Engines.Spawners
{
public static class GenerateSpawners
{
public static void Initialize()
{
CommandSystem.Register("GenerateSpawners", AccessLevel.Developer, GenerateSpawners_OnCommand);
}
private static void GenerateSpawners_OnCommand(CommandEventArgs e)
{
Mobile from = e.Mobile;
if (e.Arguments.Length == 0)
{
from.SendMessage("Usage: [GenerateSpawners <path|search pattern>");
return;
}
var di = new DirectoryInfo(Core.BaseDirectory);
var files = di.GetFiles(e.Arguments[0], SearchOption.AllDirectories);
if (files.Length == 0)
{
from.SendMessage("GenerateSpawners: No files found matching the pattern");
return;
}
JsonSerializerOptions options = new JsonSerializerOptions();
options.Converters.Add(new MapConverterFactory());
options.Converters.Add(new Point3DConverterFactory());
options.Converters.Add(new TimeSpanConverterFactory());
options.Converters.Add(new TextDefinitionConverterFactory());
for (int i = 0; i < files.Length; i++)
{
var file = files[i];
from.SendMessage("GenerateSpawners: Generating spawners for {0}...", file.Name);
List<DynamicJson> spawners = JsonConfig.Deserialize<List<DynamicJson>>(file.FullName);
ParseSpawnerList(from, spawners, options);
}
}
private static void ParseSpawnerList(Mobile from, List<DynamicJson> spawners, JsonSerializerOptions options)
{
Stopwatch watch = Stopwatch.StartNew();
List<string> failures = new List<string>();
int count = 0;
for (var i = 0; i < spawners.Count; i++)
{
var json = spawners[i];
Type type = AssemblyHandler.FindFirstTypeForName(json.Type);
if (type == null || !typeof(BaseSpawner).IsAssignableFrom(type))
{
string failure = $"GenerateSpawners: Invalid spawner type {json.Type ?? "(-null-)"} ({i})";
if (!failures.Contains(failure))
{
failures.Add(failure);
from.SendMessage(failure);
}
continue;
}
json.GetProperty("location", options, out Point3D location);
json.GetProperty("map", options, out Map map);
var eable = map.GetItemsInRange<BaseSpawner>(location, 0);
if (eable.Any(sp => sp.GetType() == type))
{
eable.Free();
continue;
}
eable.Free();
try
{
var spawner = ActivatorUtil.CreateInstance(type, json, options) as ISpawner;
spawner!.MoveToWorld(location, map);
spawner!.Respawn();
}
catch (Exception)
{
string failure = $"GenerateSpawners: Spawner {type} failed to construct";
if (!failures.Contains(failure))
{
failures.Add(failure);
from.SendMessage(failure);
}
continue;
}
count++;
}
watch.Stop();
from.SendMessage("GenerateSpawners: Generated {0} spawners ({1:F2} seconds, {2} failures)", count, watch.Elapsed.TotalSeconds, failures.Count);
}
}
}

View file

@ -1,27 +1,30 @@
using System; using System;
using System.Text.Json;
using Server.Json;
using Server.Mobiles;
namespace Server.Mobiles namespace Server.Engines.Spawners
{ {
public class ProximitySpawner : Spawner public class ProximitySpawner : Spawner
{ {
[Constructible] [Constructible(AccessLevel.Developer)]
public ProximitySpawner() public ProximitySpawner()
{ {
} }
[Constructible] [Constructible(AccessLevel.Developer)]
public ProximitySpawner(string spawnName) public ProximitySpawner(string spawnName)
: base(spawnName) : base(spawnName)
{ {
} }
[Constructible] [Constructible(AccessLevel.Developer)]
public ProximitySpawner(int amount, int minDelay, int maxDelay, int team, int homeRange, string spawnName) public ProximitySpawner(int amount, int minDelay, int maxDelay, int team, int homeRange, string spawnName)
: base(amount, minDelay, maxDelay, team, homeRange, spawnName) : base(amount, minDelay, maxDelay, team, homeRange, spawnName)
{ {
} }
[Constructible] [Constructible(AccessLevel.Developer)]
public ProximitySpawner(int amount, int minDelay, int maxDelay, int team, int homeRange, int triggerRange, public ProximitySpawner(int amount, int minDelay, int maxDelay, int team, int homeRange, int triggerRange,
string spawnMessage, bool instantFlag, string spawnName) string spawnMessage, bool instantFlag, string spawnName)
: base(amount, minDelay, maxDelay, team, homeRange, spawnName) : base(amount, minDelay, maxDelay, team, homeRange, spawnName)
@ -31,12 +34,14 @@ namespace Server.Mobiles
InstantFlag = instantFlag; InstantFlag = instantFlag;
} }
[Constructible(AccessLevel.Developer)]
public ProximitySpawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, public ProximitySpawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange,
params string[] spawnedNames) params string[] spawnedNames)
: base(amount, minDelay, maxDelay, team, homeRange, spawnedNames) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames)
{ {
} }
[Constructible(AccessLevel.Developer)]
public ProximitySpawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, int triggerRange, public ProximitySpawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, int triggerRange,
TextDefinition spawnMessage, bool instantFlag, params string[] spawnedNames) TextDefinition spawnMessage, bool instantFlag, params string[] spawnedNames)
: base(amount, minDelay, maxDelay, team, homeRange, spawnedNames) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames)
@ -46,6 +51,17 @@ namespace Server.Mobiles
InstantFlag = instantFlag; InstantFlag = instantFlag;
} }
public ProximitySpawner(DynamicJson json, JsonSerializerOptions options) : base(json, options)
{
json.GetProperty("triggerRange", options, out int triggerRange);
json.GetProperty("spawnMessage", options, out TextDefinition spawnMessage);
json.GetProperty("instant", options, out bool instant);
TriggerRange = triggerRange;
SpawnMessage = spawnMessage;
InstantFlag = instant;
}
public ProximitySpawner(Serial serial) public ProximitySpawner(Serial serial)
: base(serial) : base(serial)
{ {
@ -129,4 +145,4 @@ namespace Server.Mobiles
InstantFlag = reader.ReadBool(); InstantFlag = reader.ReadBool();
} }
} }
} }

View file

@ -0,0 +1,152 @@
using System;
using System.Text.Json;
using Server.Json;
using Server.Regions;
namespace Server.Engines.Spawners
{
public class RegionSpawner : Spawner
{
private BaseRegion m_SpawnRegion;
[CommandProperty(AccessLevel.Developer)]
public BaseRegion SpawnRegion
{
get => m_SpawnRegion;
set
{
m_SpawnRegion = value;
m_SpawnRegion?.InitRectangles();
InvalidateProperties();
}
}
[Constructible(AccessLevel.Developer)]
public RegionSpawner()
{
}
[Constructible(AccessLevel.Developer)]
public RegionSpawner(string spawnedName) : base(spawnedName)
{
}
[Constructible(AccessLevel.Developer)]
public RegionSpawner(int amount, int minDelay, int maxDelay, int team, int homeRange,
params string[] spawnedNames) : this(amount, TimeSpan.FromMinutes(minDelay), TimeSpan.FromMinutes(maxDelay),
team, homeRange, spawnedNames)
{
}
[Constructible(AccessLevel.Developer)]
public RegionSpawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange,
params string[] spawnedNames) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames)
{
}
public RegionSpawner(DynamicJson json, JsonSerializerOptions options) : base(json, options)
{
json.GetProperty("map", options, out Map map);
json.GetProperty("region", options, out string spawnRegion);
m_SpawnRegion = Region.Find(spawnRegion, map) as BaseRegion;
m_SpawnRegion?.InitRectangles();
}
public RegionSpawner(Serial serial) : base(serial)
{
}
public override void GetSpawnerProperties(ObjectPropertyList list)
{
base.GetSpawnerProperties(list);
if (Running && m_SpawnRegion != null) list.Add(1076228, "region:\t{0}", m_SpawnRegion.Name); // ~1_DUMMY~ ~2_DUMMY~
}
public override Point3D GetSpawnPosition(ISpawnable spawned, Map map)
{
if (m_SpawnRegion == null || map == null || map == Map.Internal || map != m_SpawnRegion.Map || m_SpawnRegion.TotalWeight <= 0)
return Location;
bool waterMob, waterOnlyMob;
if (spawned is Mobile mob)
{
waterMob = mob.CanSwim;
waterOnlyMob = mob.CanSwim && mob.CantWalk;
}
else
{
waterMob = false;
waterOnlyMob = false;
}
// Try 10 times to find a valid location.
for (int i = 0; i < 10; i++)
{
int rand = Utility.Random(m_SpawnRegion.TotalWeight);
int x = int.MinValue;
int y = int.MinValue;
for (int j = 0; j < m_SpawnRegion.RectangleWeights.Length; j++)
{
int curWeight = m_SpawnRegion.RectangleWeights[j];
if (rand < curWeight)
{
Rectangle3D rect = m_SpawnRegion.Rectangles[j];
x = rect.Start.X + rand % rect.Width;
y = rect.Start.Y + rand / rect.Width;
break;
}
rand -= curWeight;
}
int mapZ = map.GetAverageZ(x, y);
if (waterMob)
{
if (IsValidWater(map, x, y, Z))
return new Point3D(x, y, Z);
if (IsValidWater(map, x, y, mapZ))
return new Point3D(x, y, mapZ);
}
if (!waterOnlyMob)
{
if (map.CanSpawnMobile(x, y, Z))
return new Point3D(x, y, Z);
if (map.CanSpawnMobile(x, y, mapZ))
return new Point3D(x, y, mapZ);
}
}
return HomeLocation;
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
m_SpawnRegion = Region.Find(reader.ReadString(), Map) as BaseRegion;
m_SpawnRegion?.InitRectangles();
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
writer.Write(m_SpawnRegion?.Name);
}
}
}

View file

@ -0,0 +1,139 @@
using System;
using System.Text.Json;
using Server.Json;
namespace Server.Engines.Spawners
{
public class Spawner : BaseSpawner
{
public static bool IsValidWater(Map map, int x, int y, int z)
{
if (!Region.Find(new Point3D(x, y, z), map).AllowSpawn() || !map.CanFit(x, y, z, 16, false, true, false))
return false;
LandTile landTile = map.Tiles.GetLandTile(x, y);
if (landTile.Z == z && (TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags & TileFlag.Wet) != 0)
return true;
StaticTile[] staticTiles = map.Tiles.GetStaticTiles(x, y, true);
for (int i = 0; i < staticTiles.Length; ++i)
{
StaticTile staticTile = staticTiles[i];
if (staticTile.Z == z &&
(TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Flags & TileFlag.Wet) != 0)
return true;
}
return false;
}
[Constructible(AccessLevel.Developer)]
public Spawner()
{
}
[Constructible(AccessLevel.Developer)]
public Spawner(string spawnedName) : base(spawnedName)
{
}
[Constructible(AccessLevel.Developer)]
public Spawner(int amount, int minDelay, int maxDelay, int team, int homeRange,
params string[] spawnedNames) : this(amount, TimeSpan.FromMinutes(minDelay), TimeSpan.FromMinutes(maxDelay),
team, homeRange, spawnedNames)
{
}
[Constructible(AccessLevel.Developer)]
public Spawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange,
params string[] spawnedNames) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames)
{
}
public Spawner(DynamicJson json, JsonSerializerOptions options) : base(json, options)
{
}
public Spawner(Serial serial) : base(serial)
{
}
/*
public override bool OnDefragSpawn(ISpawnable spawned, bool remove)
{
// To despawn a mob that was lured 4x away from its spawner
// TODO: Move this to a config
if (spawned is BaseCreature c && c.Combatant == null && c.GetDistanceToSqrt( Location ) > c.RangeHome * 4)
{
c.Delete();
remove = true;
}
return base.OnDefragSpawn(entry, spawned, remove);
}
*/
public override Point3D GetSpawnPosition(ISpawnable spawned, Map map)
{
if (map == null || map == Map.Internal)
return Location;
bool waterMob, waterOnlyMob;
if (spawned is Mobile mob)
{
waterMob = mob.CanSwim;
waterOnlyMob = mob.CanSwim && mob.CantWalk;
}
else
{
waterMob = false;
waterOnlyMob = false;
}
// Try 10 times to find a valid location.
for (int i = 0; i < 10; i++)
{
int x = Location.X + (Utility.Random(HomeRange * 2 + 1) - HomeRange);
int y = Location.Y + (Utility.Random(HomeRange * 2 + 1) - HomeRange);
int mapZ = map.GetAverageZ(x, y);
if (waterMob)
{
if (IsValidWater(map, x, y, Z))
return new Point3D(x, y, Z);
if (IsValidWater(map, x, y, mapZ))
return new Point3D(x, y, mapZ);
}
if (!waterOnlyMob)
{
if (map.CanSpawnMobile(x, y, Z))
return new Point3D(x, y, Z);
if (map.CanSpawnMobile(x, y, mapZ))
return new Point3D(x, y, mapZ);
}
}
return HomeLocation;
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
}
}

View file

@ -0,0 +1,113 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Server.Mobiles;
namespace Server.Engines.Spawners
{
public class SpawnerEntry
{
public SpawnerEntry() => Spawned = new List<ISpawnable>();
public SpawnerEntry(string name, int probability, int maxcount) : this()
{
SpawnedName = name;
SpawnedProbability = probability;
SpawnedMaxCount = maxcount;
}
public SpawnerEntry(BaseSpawner parent, IGenericReader reader)
{
int version = reader.ReadInt();
SpawnedName = reader.ReadString();
SpawnedProbability = reader.ReadInt();
SpawnedMaxCount = reader.ReadInt();
Properties = reader.ReadString();
Parameters = reader.ReadString();
int count = reader.ReadInt();
Spawned = new List<ISpawnable>(count);
for (int i = 0; i < count; ++i)
// IEntity e = World.FindEntity( reader.ReadInt() );
if (reader.ReadEntity() is ISpawnable e)
{
e.Spawner = parent;
if (e is BaseCreature creature)
creature.RemoveIfUntamed = true;
Spawned.Add(e);
if (!parent.Spawned.ContainsKey(e))
parent.Spawned.Add(e, this);
}
}
[JsonPropertyName("probability")]
public int SpawnedProbability { get; set; }
[JsonPropertyName("maxCount")]
public int SpawnedMaxCount { get; set; }
[JsonPropertyName("name")]
public string SpawnedName { get; set; }
[JsonPropertyName("properties")]
public string Properties { get; set; }
[JsonPropertyName("parameters")]
public string Parameters { get; set; }
[JsonIgnore]
public EntryFlags Valid { get; set; }
[JsonIgnore]
public List<ISpawnable> Spawned { get; }
public bool IsFull => Spawned.Count >= SpawnedMaxCount;
public void Serialize(IGenericWriter writer)
{
writer.Write(0); // version
writer.Write(SpawnedName);
writer.Write(SpawnedProbability);
writer.Write(SpawnedMaxCount);
writer.Write(Properties);
writer.Write(Parameters);
writer.Write(Spawned.Count);
for (int i = 0; i < Spawned.Count; ++i)
{
object o = Spawned[i];
if (o is Item item)
writer.Write(item);
else if (o is Mobile mobile)
writer.Write(mobile);
else
writer.Write(Serial.MinusOne);
}
}
public void Defrag(BaseSpawner parent)
{
for (int i = 0; i < Spawned.Count; ++i)
{
ISpawnable spawned = Spawned[i];
if (parent.OnDefragSpawn(spawned, false))
{
Spawned.RemoveAt(i--);
parent.Spawned.Remove(spawned);
}
}
}
}
}

View file

@ -3,15 +3,15 @@ using System.Collections.Generic;
using Server.Gumps; using Server.Gumps;
using Server.Network; using Server.Network;
namespace Server.Mobiles namespace Server.Engines.Spawners
{ {
public class SpawnerGump : Gump public class SpawnerGump : Gump
{ {
private SpawnerEntry m_Entry; private SpawnerEntry m_Entry;
private int m_Page; private int m_Page;
private readonly Spawner m_Spawner; private readonly BaseSpawner m_Spawner;
public SpawnerGump(Spawner spawner, SpawnerEntry focusentry = null, int page = 0) : base(50, 50) public SpawnerGump(BaseSpawner spawner, SpawnerEntry focusentry = null, int page = 0) : base(50, 50)
{ {
m_Spawner = spawner; m_Spawner = spawner;
m_Entry = focusentry; m_Entry = focusentry;
@ -124,7 +124,7 @@ namespace Server.Mobiles
public int GetButtonID(int type, int index) => 1 + index * 10 + type; public int GetButtonID(int type, int index) => 1 + index * 10 + type;
public void CreateArray(RelayInfo info, Mobile from, Spawner spawner) public void CreateArray(RelayInfo info, Mobile from, BaseSpawner spawner)
{ {
int ocount = spawner.Entries.Count; int ocount = spawner.Entries.Count;
@ -148,47 +148,46 @@ namespace Server.Mobiles
if (str.Length > 0) if (str.Length > 0)
{ {
Type type = SpawnerType.GetType(str); Type type = AssemblyHandler.FindFirstTypeForName(str);
if (type != null) if (type == null)
{ {
SpawnerEntry entry; from.SendMessage("{0} is not a valid type name for entry #{1}.", str, i);
return;
}
if (entryindex < ocount) SpawnerEntry entry;
{
entry = spawner.Entries[entryindex];
entry.SpawnedName = str;
if (mte != null) if (entryindex < ocount)
entry.SpawnedMaxCount = Utility.ToInt32(mte.Text.Trim()); {
entry = spawner.Entries[entryindex];
entry.SpawnedName = str;
if (poste != null) if (mte != null)
entry.SpawnedProbability = Utility.ToInt32(poste.Text.Trim()); entry.SpawnedMaxCount = Utility.ToInt32(mte.Text.Trim());
}
else
{
int maxcount = 1;
int probcount = 100;
if (mte != null) if (poste != null)
maxcount = Utility.ToInt32(mte.Text.Trim()); entry.SpawnedProbability = Utility.ToInt32(poste.Text.Trim());
if (poste != null)
probcount = Utility.ToInt32(poste.Text.Trim());
entry = spawner.AddEntry(str, probcount, maxcount);
}
if (parmte != null)
entry.Parameters = parmte.Text.Trim();
if (propte != null)
entry.Properties = propte.Text.Trim();
} }
else else
{ {
from.SendMessage("{0} is not a valid type name for entry #{1}.", str, i); int maxcount = 1;
int probcount = 100;
if (mte != null)
maxcount = Utility.ToInt32(mte.Text.Trim());
if (poste != null)
probcount = Utility.ToInt32(poste.Text.Trim());
entry = spawner.AddEntry(str, probcount, maxcount);
} }
if (parmte != null)
entry.Parameters = parmte.Text.Trim();
if (propte != null)
entry.Properties = propte.Text.Trim();
} }
else if (entryindex < ocount && spawner.Entries[entryindex] != null) else if (entryindex < ocount && spawner.Entries[entryindex] != null)
{ {

View file

@ -120,7 +120,7 @@ namespace Server.Items
private void InternalCallback(Mobile from, object targeted) private void InternalCallback(Mobile from, object targeted)
{ {
if (Deleted || UsesRemaining <= 0 || !from.InRange(GetWorldLocation(), 3) || if (Deleted || UsesRemaining <= 0 || !from.InRange(GetWorldLocation(), 3) ||
!IsAccessibleTo(from)) !IsAccessibleTo(from))
return; return;

View file

@ -138,7 +138,7 @@ namespace Server.Items
public override void OnDoubleClick(Mobile from) public override void OnDoubleClick(Mobile from)
{ {
if (from.Region.IsPartOf<Jail>()) if (from.Region.IsPartOf<JailRegion>())
from.SendMessage("You may not do that in jail."); from.SendMessage("You may not do that in jail.");
else if (!IsChildOf(from.Backpack)) else if (!IsChildOf(from.Backpack))
MessageHelper.SendLocalizedMessageTo(this, from, 1062334, MessageHelper.SendLocalizedMessageTo(this, from, 1062334,
@ -218,7 +218,7 @@ namespace Server.Items
if (m_Bag.Deleted) if (m_Bag.Deleted)
return; return;
if (from.Region.IsPartOf<Jail>()) if (from.Region.IsPartOf<JailRegion>())
{ {
from.SendMessage("You may not do that in jail."); from.SendMessage("You may not do that in jail.");
} }

View file

@ -201,7 +201,7 @@ namespace Server.Items
0x22); // The Crystal Ball fills with a red mist. You appear to have let your bond to your pet deteriorate. 0x22); // The Crystal Ball fills with a red mist. You appear to have let your bond to your pet deteriorate.
} }
else if (from.Map == Map.Ilshenar || from.Region.IsPartOf<DungeonRegion>() || else if (from.Map == Map.Ilshenar || from.Region.IsPartOf<DungeonRegion>() ||
from.Region.IsPartOf<Jail>() || from.Region.IsPartOf<SafeZone>()) from.Region.IsPartOf<JailRegion>() || from.Region.IsPartOf<SafeZone>())
{ {
from.Send(new AsciiMessage(Serial, ItemID, MessageType.Regular, 0x22, 3, "", from.Send(new AsciiMessage(Serial, ItemID, MessageType.Regular, 0x22, 3, "",
"You cannot summon your pet to this location.")); "You cannot summon your pet to this location."));

View file

@ -255,13 +255,13 @@ namespace Server.Items
return false; return false;
} }
if (from.Region.IsPartOf<Jail>()) if (from.Region.IsPartOf<JailRegion>())
{ {
from.SendLocalizedMessage(1114345, "", 0x35); // You'll need a better jailbreak plan than that! from.SendLocalizedMessage(1114345, "", 0x35); // You'll need a better jailbreak plan than that!
return false; return false;
} }
if (boundRoot.Region.IsPartOf<Jail>()) if (boundRoot.Region.IsPartOf<JailRegion>())
{ {
from.SendLocalizedMessage(1019004); // You are not allowed to travel there. from.SendLocalizedMessage(1019004); // You are not allowed to travel there.
return false; return false;

View file

@ -0,0 +1,46 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: TextDefinitionConverter.cs *
* Created: 2020/05/25 - Updated: 2020/05/25 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class TextDefinitionConverter : JsonConverter<TextDefinition>
{
public override TextDefinition Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
reader.TokenType switch
{
JsonTokenType.String => new TextDefinition(reader.GetString()),
JsonTokenType.Number => new TextDefinition(reader.GetInt32()),
_ => throw new JsonException("TextDefinition value must be an integer or string")
};
public override void Write(Utf8JsonWriter writer, TextDefinition value, JsonSerializerOptions options)
{
if (value.Number > 0)
writer.WriteNumberValue(value.Number);
else
writer.WriteStringValue(value.String);
}
}
}

View file

@ -0,0 +1,35 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: TextDefinitionConverterFactory.cs *
* Created: 2020/05/25 - Updated: 2020/05/25 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class TextDefinitionConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(TextDefinition);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) =>
new TextDefinitionConverter();
}
}

View file

@ -206,7 +206,7 @@ namespace Server.Misc
public static void Gain(Mobile from, Skill skill) public static void Gain(Mobile from, Skill skill)
{ {
if (from.Region.IsPartOf<Jail>()) if (from.Region.IsPartOf<JailRegion>())
return; return;
if (from is BaseCreature creature && creature.IsDeadPet) if (from is BaseCreature creature && creature.IsDeadPet)

View file

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Server.Mobiles; using Server.Engines.Spawners;
namespace Server namespace Server
{ {

View file

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Server.Mobiles; using Server.Engines.Spawners;
// Version 0.8 // Version 0.8

View file

@ -4,11 +4,11 @@ using System.IO;
using Server.ContextMenus; using Server.ContextMenus;
using Server.Engines.Quests; using Server.Engines.Quests;
using Server.Engines.Quests.Necro; using Server.Engines.Quests.Necro;
using Server.Engines.Spawners;
using Server.Factions; using Server.Factions;
using Server.Gumps; using Server.Gumps;
using Server.Items; using Server.Items;
using Server.Network; using Server.Network;
using Server.Regions;
using Server.Spells; using Server.Spells;
using Server.Spells.Spellweaving; using Server.Spells.Spellweaving;
using Server.Targets; using Server.Targets;
@ -1461,10 +1461,12 @@ namespace Server.Mobiles
m_Mobile.OwnerAbandonTime = DateTime.MinValue; m_Mobile.OwnerAbandonTime = DateTime.MinValue;
m_Mobile.IsBonded = false; m_Mobile.IsBonded = false;
if (m_Mobile.Spawner is SpawnEntry se && se.HomeLocation != Point3D.Zero) var spawner = m_Mobile.Spawner;
if (spawner != null && spawner.HomeLocation != Point3D.Zero)
{ {
m_Mobile.Home = se.HomeLocation; m_Mobile.Home = spawner.HomeLocation;
m_Mobile.RangeHome = se.HomeRange; m_Mobile.RangeHome = spawner.HomeRange;
} }
if (m_Mobile.DeleteOnRelease || m_Mobile.IsDeadPet) if (m_Mobile.DeleteOnRelease || m_Mobile.IsDeadPet)
@ -1923,9 +1925,9 @@ namespace Server.Mobiles
if (m_Mobile.Home == Point3D.Zero) if (m_Mobile.Home == Point3D.Zero)
{ {
if (m_Mobile.Spawner is SpawnEntry entry) if (m_Mobile.Spawner is RegionSpawner rs)
{ {
Region region = entry.Region; Region region = rs.SpawnRegion;
if (m_Mobile.Region.AcceptsSpawnsFrom(region)) if (m_Mobile.Region.AcceptsSpawnsFrom(region))
{ {
@ -2375,13 +2377,15 @@ namespace Server.Mobiles
{ {
m_Timer.Stop(); m_Timer.Stop();
if (m_Mobile.Spawner is SpawnEntry se && se.ReturnOnDeactivate && !m_Mobile.Controlled) var spawner = m_Mobile.Spawner;
if (spawner?.ReturnOnDeactivate == true && !m_Mobile.Controlled)
{ {
if (se.HomeLocation == Point3D.Zero) if (spawner.HomeLocation == Point3D.Zero)
{ {
if (!m_Mobile.Region.AcceptsSpawnsFrom(se.Region)) Timer.DelayCall(TimeSpan.Zero, ReturnToHome); if (!m_Mobile.Region.AcceptsSpawnsFrom(spawner.Region)) Timer.DelayCall(TimeSpan.Zero, ReturnToHome);
} }
else if (!m_Mobile.InRange(se.HomeLocation, se.HomeRange)) else if (!m_Mobile.InRange(spawner.HomeLocation, spawner.HomeRange))
{ {
Timer.DelayCall(TimeSpan.Zero, ReturnToHome); Timer.DelayCall(TimeSpan.Zero, ReturnToHome);
} }
@ -2391,11 +2395,11 @@ namespace Server.Mobiles
private void ReturnToHome() private void ReturnToHome()
{ {
if (m_Mobile.Spawner is SpawnEntry se) if (m_Mobile.Spawner != null)
{ {
Point3D loc = se.RandomSpawnLocation(16, !m_Mobile.CantWalk, m_Mobile.CanSwim); Point3D loc = m_Mobile.Spawner.GetSpawnPosition(m_Mobile, m_Mobile.Spawner.Map);
if (loc != Point3D.Zero) m_Mobile.MoveToWorld(loc, se.Region.Map); if (loc != Point3D.Zero) m_Mobile.MoveToWorld(loc, m_Mobile.Spawner.Map);
} }
} }

View file

@ -9,6 +9,7 @@ using Server.Engines.PartySystem;
using Server.Engines.Quests; using Server.Engines.Quests;
using Server.Engines.Quests.Doom; using Server.Engines.Quests.Doom;
using Server.Engines.Quests.Haven; using Server.Engines.Quests.Haven;
using Server.Engines.Spawners;
using Server.Ethics; using Server.Ethics;
using Server.Factions; using Server.Factions;
using Server.Items; using Server.Items;
@ -2688,7 +2689,7 @@ namespace Server.Mobiles
} }
public override bool CanBeRenamedBy(Mobile from) => public override bool CanBeRenamedBy(Mobile from) =>
(Controlled && from == ControlMaster && !from.Region.IsPartOf<Jail>()) || (Controlled && from == ControlMaster && !from.Region.IsPartOf<JailRegion>()) ||
base.CanBeRenamedBy(from); base.CanBeRenamedBy(from);
public bool SetControlMaster(Mobile m) public bool SetControlMaster(Mobile m)
@ -2746,7 +2747,7 @@ namespace Server.Mobiles
{ {
base.OnRegionChange(Old, New); base.OnRegionChange(Old, New);
if (Controlled && Spawner is SpawnEntry se && !se.UnlinkOnTaming && New?.AcceptsSpawnsFrom(se.Region) != true) if (Controlled && Spawner?.UnlinkOnTaming == false && New?.AcceptsSpawnsFrom(Spawner.Region) != true)
{ {
Spawner.Remove(this); Spawner.Remove(this);
Spawner = null; Spawner = null;

View file

@ -4119,7 +4119,7 @@ namespace Server.Mobiles
public bool YoungDeathTeleport() public bool YoungDeathTeleport()
{ {
if (Region.IsPartOf<Jail>() if (Region.IsPartOf<JailRegion>()
|| Region.IsPartOf("Samurai start location") || Region.IsPartOf("Samurai start location")
|| Region.IsPartOf("Ninja start location") || Region.IsPartOf("Ninja start location")
|| Region.IsPartOf("Ninja cave")) || Region.IsPartOf("Ninja cave"))

View file

@ -1,35 +1,25 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml; using System.Text.Json;
using Server.Gumps; using Server.Gumps;
using Server.Items; using Server.Json;
using Server.Mobiles; using Server.Mobiles;
using Server.Spells;
namespace Server.Regions namespace Server.Regions
{ {
public enum SpawnZLevel
{
Lowest,
Highest,
Random
}
public class BaseRegion : Region public class BaseRegion : Region
{ {
private static readonly List<Rectangle3D> m_RectBuffer1 = new List<Rectangle3D>(); private static readonly List<Rectangle3D> m_RectBuffer1 = new List<Rectangle3D>();
private static readonly List<Rectangle3D> m_RectBuffer2 = new List<Rectangle3D>(); private static readonly List<Rectangle3D> m_RectBuffer2 = new List<Rectangle3D>();
private static readonly List<int> m_SpawnBuffer1 = new List<int>(); public bool ExcludeFromParentSpawns { get; set; }
private static readonly List<Item> m_SpawnBuffer2 = new List<Item>();
private bool m_ExcludeFromParentSpawns;
private Rectangle3D[] m_Rectangles; public Rectangle3D[] Rectangles { get; private set; }
private int[] m_RectangleWeights; public int[] RectangleWeights { get; private set; }
private string m_RuneName; private string m_RuneName;
public int TotalWeight { get; private set; }
private SpawnEntry[] m_Spawns;
private int m_TotalWeight;
public BaseRegion(string name, Map map, int priority, params Rectangle2D[] area) : base(name, map, priority, area) public BaseRegion(string name, Map map, int priority, params Rectangle2D[] area) : base(name, map, priority, area)
{ {
@ -47,63 +37,12 @@ namespace Server.Regions
{ {
} }
public BaseRegion(XmlElement xml, Map map, Region parent) : base(xml, map, parent) public BaseRegion(DynamicJson json, JsonSerializerOptions options) : base(json, options)
{ {
ReadString(xml["rune"], "name", ref m_RuneName, false); if (json.data.TryGetValue("rune", out var runeName))
m_RuneName = runeName.GetString();
bool logoutDelayActive = true; NoLogoutDelay = json.data.TryGetValue("logoutDelay", out var logoutDelay) && !logoutDelay.GetBoolean();
ReadBoolean(xml["logoutDelay"], "active", ref logoutDelayActive, false);
NoLogoutDelay = !logoutDelayActive;
XmlElement spawning = xml["spawning"];
if (spawning != null)
{
ReadBoolean(spawning, "excludeFromParent", ref m_ExcludeFromParentSpawns, false);
SpawnZLevel zLevel = SpawnZLevel.Lowest;
ReadEnum(spawning, "zLevel", ref zLevel, false);
SpawnZLevel = zLevel;
List<SpawnEntry> list = new List<SpawnEntry>();
foreach (XmlNode node in spawning.ChildNodes)
if (node is XmlElement el)
{
SpawnDefinition def = SpawnDefinition.GetSpawnDefinition(el);
if (def == null)
continue;
int id = 0;
if (!ReadInt32(el, "id", ref id, true))
continue;
int amount = 0;
if (!ReadInt32(el, "amount", ref amount, true))
continue;
TimeSpan minSpawnTime = SpawnEntry.DefaultMinSpawnTime;
ReadTimeSpan(el, "minSpawnTime", ref minSpawnTime, false);
TimeSpan maxSpawnTime = SpawnEntry.DefaultMaxSpawnTime;
ReadTimeSpan(el, "maxSpawnTime", ref maxSpawnTime, false);
Point3D home = Point3D.Zero;
int range = 0;
XmlElement homeEl = el["home"];
if (ReadPoint3D(homeEl, map, ref home, false))
ReadInt32(homeEl, "range", ref range, false);
Direction dir = SpawnEntry.InvalidDirection;
ReadEnum(el["direction"], "value", ref dir, false);
SpawnEntry entry = new SpawnEntry(id, this, home, range, dir, def, amount, minSpawnTime,
maxSpawnTime);
list.Add(entry);
}
if (list.Count > 0) m_Spawns = list.ToArray();
}
} }
public virtual bool YoungProtected => true; public virtual bool YoungProtected => true;
@ -121,39 +60,11 @@ namespace Server.Regions
public bool NoLogoutDelay { get; set; } public bool NoLogoutDelay { get; set; }
public SpawnEntry[] Spawns
{
get => m_Spawns;
set
{
if (m_Spawns != null)
for (int i = 0; i < m_Spawns.Length; i++)
m_Spawns[i].Delete();
m_Spawns = value;
}
}
public SpawnZLevel SpawnZLevel { get; set; }
public bool ExcludeFromParentSpawns
{
get => m_ExcludeFromParentSpawns;
set => m_ExcludeFromParentSpawns = value;
}
public static void Configure() public static void Configure()
{ {
DefaultRegionType = typeof(BaseRegion); DefaultRegionType = typeof(BaseRegion);
} }
public override void OnUnregister()
{
base.OnUnregister();
Spawns = null;
}
public static string GetRuneNameFor(Region region) public static string GetRuneNameFor(Region region)
{ {
while (region != null) while (region != null)
@ -169,61 +80,24 @@ namespace Server.Regions
return null; return null;
} }
public override TimeSpan GetLogoutDelay(Mobile m) public override TimeSpan GetLogoutDelay(Mobile m) =>
{ NoLogoutDelay && m.Aggressors.Count == 0 && m.Aggressed.Count == 0 && !m.Criminal
if (NoLogoutDelay) ? TimeSpan.Zero
if (m.Aggressors.Count == 0 && m.Aggressed.Count == 0 && !m.Criminal) : base.GetLogoutDelay(m);
return TimeSpan.Zero;
return base.GetLogoutDelay(m);
}
public static bool CanSpawn(Region region, params Type[] types)
{
while (region != null)
{
if (!region.AllowSpawn())
return false;
if (region is BaseRegion br)
{
if (br.Spawns != null)
for (int i = 0; i < br.Spawns.Length; i++)
{
SpawnEntry entry = br.Spawns[i];
if (entry.Definition.CanSpawn(types))
return true;
}
if (br.ExcludeFromParentSpawns)
return false;
}
region = region.Parent;
}
return false;
}
public override void OnEnter(Mobile m) public override void OnEnter(Mobile m)
{ {
if (m is PlayerMobile mobile && mobile.Young) if (m is PlayerMobile mobile && mobile.Young && !YoungProtected)
if (!YoungProtected) mobile.SendGump(new YoungDungeonWarning());
mobile.SendGump(new YoungDungeonWarning());
} }
public override bool AcceptsSpawnsFrom(Region region) public override bool AcceptsSpawnsFrom(Region region) =>
{ (region == this || !ExcludeFromParentSpawns) && base.AcceptsSpawnsFrom(region);
if (region == this || !m_ExcludeFromParentSpawns)
return base.AcceptsSpawnsFrom(region);
return false; // TODO: Clean this up
} public void InitRectangles()
private void InitRectangles()
{ {
if (m_Rectangles != null) if (Rectangles != null)
return; return;
// Test if area rectangles are overlapping, and in that case break them into smaller non overlapping rectangles // Test if area rectangles are overlapping, and in that case break them into smaller non overlapping rectangles
@ -270,277 +144,22 @@ namespace Server.Regions
m_RectBuffer2.Clear(); m_RectBuffer2.Clear();
} }
m_Rectangles = m_RectBuffer1.ToArray(); Rectangles = m_RectBuffer1.ToArray();
m_RectBuffer1.Clear(); m_RectBuffer1.Clear();
m_RectangleWeights = new int[m_Rectangles.Length]; RectangleWeights = new int[Rectangles.Length];
for (int i = 0; i < m_Rectangles.Length; i++) for (int i = 0; i < Rectangles.Length; i++)
{ {
Rectangle3D rect = m_Rectangles[i]; Rectangle3D rect = Rectangles[i];
int weight = rect.Width * rect.Height; int weight = rect.Width * rect.Height;
m_RectangleWeights[i] = weight; RectangleWeights[i] = weight;
m_TotalWeight += weight; TotalWeight += weight;
} }
} }
public Point3D RandomSpawnLocation(int spawnHeight, bool land, bool water, Point3D home, int range) public override string ToString() => Name ?? RuneName ?? GetType().Name;
{
Map map = Map;
if (map == Map.Internal) public virtual bool CheckTravel(Mobile m, Point3D newLocation, TravelCheckType travelType) => true;
return Point3D.Zero;
InitRectangles();
if (m_TotalWeight <= 0)
return Point3D.Zero;
for (int i = 0; i < 10; i++) // Try 10 times
{
int x, y, minZ, maxZ;
if (home == Point3D.Zero)
{
int rand = Utility.Random(m_TotalWeight);
x = int.MinValue;
y = int.MinValue;
minZ = int.MaxValue;
maxZ = int.MinValue;
for (int j = 0; j < m_RectangleWeights.Length; j++)
{
int curWeight = m_RectangleWeights[j];
if (rand < curWeight)
{
Rectangle3D rect = m_Rectangles[j];
x = rect.Start.X + rand % rect.Width;
y = rect.Start.Y + rand / rect.Width;
minZ = rect.Start.Z;
maxZ = rect.End.Z;
break;
}
rand -= curWeight;
}
}
else
{
x = Utility.RandomMinMax(home.X - range, home.X + range);
y = Utility.RandomMinMax(home.Y - range, home.Y + range);
minZ = int.MaxValue;
maxZ = int.MinValue;
for (int j = 0; j < Area.Length; j++)
{
Rectangle3D rect = Area[j];
if (x >= rect.Start.X && x < rect.End.X && y >= rect.Start.Y && y < rect.End.Y)
{
minZ = rect.Start.Z;
maxZ = rect.End.Z;
break;
}
}
if (minZ == int.MaxValue)
continue;
}
if (x < 0 || y < 0 || x >= map.Width || y >= map.Height)
continue;
LandTile lt = map.Tiles.GetLandTile(x, y);
int ltLowZ = 0, ltAvgZ = 0, ltTopZ = 0;
map.GetAverageZ(x, y, ref ltLowZ, ref ltAvgZ, ref ltTopZ);
TileFlag ltFlags = TileData.LandTable[lt.ID & TileData.MaxLandValue].Flags;
bool ltImpassable = (ltFlags & TileFlag.Impassable) != 0;
if (!lt.Ignored && ltAvgZ >= minZ && ltAvgZ < maxZ)
if ((ltFlags & TileFlag.Wet) != 0)
{
if (water)
m_SpawnBuffer1.Add(ltAvgZ);
}
else if (land && !ltImpassable)
{
m_SpawnBuffer1.Add(ltAvgZ);
}
StaticTile[] staticTiles = map.Tiles.GetStaticTiles(x, y, true);
for (int j = 0; j < staticTiles.Length; j++)
{
StaticTile tile = staticTiles[j];
ItemData id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
int tileZ = tile.Z + id.CalcHeight;
if (tileZ >= minZ && tileZ < maxZ)
if ((id.Flags & TileFlag.Wet) != 0)
{
if (water)
m_SpawnBuffer1.Add(tileZ);
}
else if (land && id.Surface && !id.Impassable)
{
m_SpawnBuffer1.Add(tileZ);
}
}
Sector sector = map.GetSector(x, y);
for (int j = 0; j < sector.Items.Count; j++)
{
Item item = sector.Items[j];
if (!(item is BaseMulti) && item.ItemID <= TileData.MaxItemValue && item.AtWorldPoint(x, y))
{
m_SpawnBuffer2.Add(item);
if (!item.Movable)
{
ItemData id = item.ItemData;
int itemZ = item.Z + id.CalcHeight;
if (itemZ >= minZ && itemZ < maxZ)
if ((id.Flags & TileFlag.Wet) != 0)
{
if (water)
m_SpawnBuffer1.Add(itemZ);
}
else if (land && id.Surface && !id.Impassable)
{
m_SpawnBuffer1.Add(itemZ);
}
}
}
}
if (m_SpawnBuffer1.Count == 0)
{
m_SpawnBuffer1.Clear();
m_SpawnBuffer2.Clear();
continue;
}
int z;
switch (SpawnZLevel)
{
case SpawnZLevel.Lowest:
{
z = int.MaxValue;
for (int j = 0; j < m_SpawnBuffer1.Count; j++)
{
int l = m_SpawnBuffer1[j];
if (l < z)
z = l;
}
break;
}
case SpawnZLevel.Highest:
{
z = int.MinValue;
for (int j = 0; j < m_SpawnBuffer1.Count; j++)
{
int l = m_SpawnBuffer1[j];
if (l > z)
z = l;
}
break;
}
default: // SpawnZLevel.Random
{
int index = Utility.Random(m_SpawnBuffer1.Count);
z = m_SpawnBuffer1[index];
break;
}
}
m_SpawnBuffer1.Clear();
if (!Find(new Point3D(x, y, z), map).AcceptsSpawnsFrom(this))
{
m_SpawnBuffer2.Clear();
continue;
}
int top = z + spawnHeight;
bool ok = true;
for (int j = 0; j < m_SpawnBuffer2.Count; j++)
{
Item item = m_SpawnBuffer2[j];
ItemData id = item.ItemData;
if ((id.Surface || id.Impassable) && item.Z + id.CalcHeight > z && item.Z < top)
{
ok = false;
break;
}
}
m_SpawnBuffer2.Clear();
if (!ok)
continue;
if (ltImpassable && ltAvgZ > z && ltLowZ < top)
continue;
for (int j = 0; j < staticTiles.Length; j++)
{
StaticTile tile = staticTiles[j];
ItemData id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
if ((id.Surface || id.Impassable) && tile.Z + id.CalcHeight > z && tile.Z < top)
{
ok = false;
break;
}
}
if (!ok)
continue;
for (int j = 0; j < sector.Mobiles.Count; j++)
{
Mobile m = sector.Mobiles[j];
if (m.X == x && m.Y == y && (m.AccessLevel == AccessLevel.Player || !m.Hidden))
if (m.Z + 16 > z && m.Z < top)
{
ok = false;
break;
}
}
if (ok)
return new Point3D(x, y, z);
}
return Point3D.Zero;
}
public override string ToString()
{
if (Name != null)
return Name;
if (RuneName != null)
return RuneName;
return GetType().Name;
}
} }
} }

View file

@ -1,29 +1,22 @@
using System.Xml; using System.Text.Json;
using Server.Json;
namespace Server.Regions namespace Server.Regions
{ {
public class DungeonRegion : BaseRegion public class DungeonRegion : BaseRegion
{ {
private Point3D m_EntranceLocation; public DungeonRegion(DynamicJson json, JsonSerializerOptions options) : base(json, options)
public DungeonRegion(XmlElement xml, Map map, Region parent) : base(xml, map, parent)
{ {
XmlElement entrEl = xml["entrance"]; if (json.GetProperty("map", options, out Map map))
EntranceMap = map;
Map entrMap = map; if (json.GetProperty("entrance", options, out Point3D entrance))
ReadMap(entrEl, "map", ref entrMap, false); EntranceLocation = entrance;
if (ReadPoint3D(entrEl, entrMap, ref m_EntranceLocation, false))
EntranceMap = entrMap;
} }
public override bool YoungProtected => false; public override bool YoungProtected => false;
public Point3D EntranceLocation public Point3D EntranceLocation { get; set; }
{
get => m_EntranceLocation;
set => m_EntranceLocation = value;
}
public Map EntranceMap { get; set; } public Map EntranceMap { get; set; }
@ -34,12 +27,6 @@ namespace Server.Regions
global = LightCycle.DungeonLevel; global = LightCycle.DungeonLevel;
} }
public override bool CanUseStuckMenu(Mobile m) public override bool CanUseStuckMenu(Mobile m) => Map != Map.Felucca && base.CanUseStuckMenu(m);
{
if (Map == Map.Felucca)
return false;
return base.CanUseStuckMenu(m);
}
} }
} }

View file

@ -1,34 +0,0 @@
using System.Xml;
using Server.Spells.Chivalry;
using Server.Spells.Fourth;
using Server.Spells.Seventh;
using Server.Spells.Sixth;
namespace Server.Regions
{
public class GreenAcres : BaseRegion
{
public GreenAcres(XmlElement xml, Map map, Region parent) : base(xml, map, parent)
{
}
public override bool AllowHousing(Mobile from, Point3D p)
{
if (from.AccessLevel == AccessLevel.Player)
return false;
return base.AllowHousing(from, p);
}
public override bool OnBeginSpellCast(Mobile m, ISpell s)
{
if ((s is GateTravelSpell || s is RecallSpell || s is MarkSpell || s is SacredJourneySpell) &&
m.AccessLevel == AccessLevel.Player)
{
m.SendMessage("You cannot cast that spell here.");
return false;
}
return base.OnBeginSpellCast(m, s);
}
}
}

View file

@ -0,0 +1,31 @@
using System.Text.Json;
using Server.Json;
using Server.Spells;
using Server.Spells.Sixth;
namespace Server.Regions
{
public class GreenAcresRegion : BaseRegion
{
public GreenAcresRegion(DynamicJson json, JsonSerializerOptions options) : base(json, options)
{
}
public override bool AllowHousing(Mobile from, Point3D p) =>
from.AccessLevel != AccessLevel.Player && base.AllowHousing(from, p);
public override bool CheckTravel(Mobile m, Point3D newLocation, TravelCheckType travelType) =>
m.AccessLevel != AccessLevel.Player;
public override bool OnBeginSpellCast(Mobile m, ISpell s)
{
if (m.AccessLevel == AccessLevel.Player && s is MarkSpell)
{
m.SendLocalizedMessage(501802); // Thy spell doth not appear to work...
return false;
}
return base.OnBeginSpellCast(m, s);
}
}
}

Some files were not shown because too many files have changed in this diff Show more