Last active
December 19, 2015 21:09
-
-
Save VlLight/6018291 to your computer and use it in GitHub Desktop.
Zone spawn support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: java/com/l2jserver/gameserver/ai/L2AttackableAI.java | |
=================================================================== | |
--- java/com/l2jserver/gameserver/ai/L2AttackableAI.java (revision 6097) | |
+++ java/com/l2jserver/gameserver/ai/L2AttackableAI.java (working copy) | |
@@ -350,8 +350,25 @@ | |
{ | |
if (npc.getSpawn() != null) | |
{ | |
+ int x, y, z; | |
+ | |
+ // Territory based spawn | |
+ if ((npc.getSpawn().getX() == 0) && (npc.getSpawn().getY() == 0)) | |
+ { | |
+ x = npc.getSpawn().getX(npc); | |
+ y = npc.getSpawn().getY(npc); | |
+ z = npc.getSpawn().getZ(npc); | |
+ } | |
+ // Npc with fixed coords | |
+ else | |
+ { | |
+ x = npc.getSpawn().getX(); | |
+ y = npc.getSpawn().getY(); | |
+ z = npc.getSpawn().getZ(); | |
+ } | |
+ | |
final int range = Config.MAX_DRIFT_RANGE; | |
- if (!npc.isInsideRadius(npc.getSpawn().getX(), npc.getSpawn().getY(), npc.getSpawn().getZ(), range + range, true, false)) | |
+ if (!npc.isInsideRadius(x, y, z, range + range, true, false)) | |
{ | |
intention = AI_INTENTION_ACTIVE; | |
} | |
@@ -642,8 +659,8 @@ | |
} | |
} | |
- // If NPC with random coord in territory | |
- if ((npc.getSpawn().getX() == 0) && (npc.getSpawn().getY() == 0)) | |
+ // If NPC with random coord in territory - old method (for backward compartibility) | |
+ if ((npc.getSpawn().getX() == 0) && (npc.getSpawn().getY() == 0) && (npc.getSpawn().getSpawnTerritory() == null)) | |
{ | |
// Calculate a destination point in the spawn area | |
int p[] = TerritoryTable.getInstance().getRandomPoint(npc.getSpawn().getLocationId()); | |
@@ -670,22 +687,31 @@ | |
} | |
else | |
{ | |
- // If NPC with fixed coord | |
- x1 = npc.getSpawn().getX(); | |
- y1 = npc.getSpawn().getY(); | |
- z1 = npc.getSpawn().getZ(); | |
- | |
+ // New territory based spawn - obtain last spawn point | |
+ if (npc.getSpawn().getX() == 0 && npc.getSpawn().getY() == 0) | |
+ { | |
+ x1 = npc.getSpawn().getX(npc); | |
+ y1 = npc.getSpawn().getY(npc); | |
+ z1 = npc.getSpawn().getZ(npc); | |
+ } | |
+ else // If NPC with fixed coord | |
+ { | |
+ x1 = npc.getSpawn().getX(); | |
+ y1 = npc.getSpawn().getY(); | |
+ z1 = npc.getSpawn().getZ(); | |
+ } | |
+ | |
if (!npc.isInsideRadius(x1, y1, range, false)) | |
{ | |
npc.setisReturningToSpawnPoint(true); | |
} | |
else | |
{ | |
- x1 = Rnd.nextInt(range * 2); // x | |
- y1 = Rnd.get(x1, range * 2); // distance | |
- y1 = (int) Math.sqrt((y1 * y1) - (x1 * x1)); // y | |
- x1 += npc.getSpawn().getX() - range; | |
- y1 += npc.getSpawn().getY() - range; | |
+ int deltaX = Rnd.nextInt(range * 2); // x | |
+ int deltaY = Rnd.get(deltaX, range * 2); // distance | |
+ deltaY = (int) Math.sqrt(deltaY * deltaY - deltaX * deltaX); // y | |
+ x1 = deltaX + x1 - range; | |
+ y1 = deltaY + y1 - range; | |
z1 = npc.getZ(); | |
} | |
} | |
Index: java/com/l2jserver/gameserver/datatables/SpawnTable.java | |
=================================================================== | |
--- java/com/l2jserver/gameserver/datatables/SpawnTable.java (revision 6097) | |
+++ java/com/l2jserver/gameserver/datatables/SpawnTable.java (working copy) | |
@@ -33,16 +33,22 @@ | |
import com.l2jserver.Config; | |
import com.l2jserver.L2DatabaseFactory; | |
+import com.l2jserver.gameserver.engines.DocumentParser; | |
import com.l2jserver.gameserver.instancemanager.DayNightSpawnManager; | |
+import com.l2jserver.gameserver.instancemanager.ZoneManager; | |
import com.l2jserver.gameserver.model.L2Spawn; | |
+import com.l2jserver.gameserver.model.StatsSet; | |
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate; | |
import com.l2jserver.gameserver.model.interfaces.IL2Procedure; | |
+import org.w3c.dom.NamedNodeMap; | |
+import org.w3c.dom.Node; | |
+ | |
/** | |
* Spawn data retriever. | |
* @author Zoey76 | |
*/ | |
-public final class SpawnTable | |
+public final class SpawnTable extends DocumentParser | |
{ | |
private static final Logger _log = Logger.getLogger(SpawnTable.class.getName()); | |
// SQL | |
@@ -51,6 +57,8 @@ | |
private static final Map<Integer, Set<L2Spawn>> _spawnTable = new FastMap<Integer, Set<L2Spawn>>().shared(); | |
+ private int _xmlSpawnCount = 0; | |
+ | |
protected SpawnTable() | |
{ | |
load(); | |
@@ -59,6 +67,7 @@ | |
/** | |
* Wrapper to load all spawns. | |
*/ | |
+ @Override | |
public void load() | |
{ | |
if (!Config.ALT_DEV_NO_SPAWNS) | |
@@ -71,9 +80,127 @@ | |
fillSpawnTable(true); | |
_log.info(getClass().getSimpleName() + ": Loaded " + (_spawnTable.size() - spawnCount) + " custom npc spawns."); | |
} | |
+ | |
+ // Load XML list | |
+ parseDirectory("data/spawnlist"); | |
+ _log.info(getClass().getSimpleName() + ": Loaded " + _xmlSpawnCount + " npc spawns from XML."); | |
} | |
} | |
+ private boolean checkTemplate(int npcId) | |
+ { | |
+ L2NpcTemplate npcTemplate = NpcTable.getInstance().getTemplate(npcId); | |
+ if (npcTemplate == null) | |
+ { | |
+ _log.warning(getClass().getSimpleName() + ": Data missing in NPC table for ID: " + npcId + "."); | |
+ return false; | |
+ } | |
+ | |
+ if (npcTemplate.isType("L2SiegeGuard") || npcTemplate.isType("L2RaidBoss") || (!Config.ALLOW_CLASS_MASTERS && npcTemplate.isType("L2ClassMaster"))) | |
+ { | |
+ // Don't spawn | |
+ return false; | |
+ } | |
+ | |
+ return true; | |
+ } | |
+ | |
+ @Override | |
+ protected void parseDocument() | |
+ { | |
+ NamedNodeMap attrs; | |
+ for (Node list = getCurrentDocument().getFirstChild(); list != null; list = list.getNextSibling()) | |
+ { | |
+ if (list.getNodeName().equalsIgnoreCase("list")) | |
+ { | |
+ for (Node param = list.getFirstChild(); param != null; param = param.getNextSibling()) | |
+ { | |
+ attrs = param.getAttributes(); | |
+ if (param.getNodeName().equalsIgnoreCase("spawn")) | |
+ { | |
+ String territoryName = null; | |
+ | |
+ // Check, if spawn territory specified and exists | |
+ if (attrs.getNamedItem("zone") != null && ZoneManager.getInstance().getSpawnTerritory(attrs.getNamedItem("zone").getNodeValue()) != null) | |
+ { | |
+ territoryName = parseString(attrs, "zone"); | |
+ } | |
+ | |
+ for (Node npctag = param.getFirstChild(); npctag != null; npctag = npctag.getNextSibling()) | |
+ { | |
+ attrs = npctag.getAttributes(); | |
+ if (npctag.getNodeName().equalsIgnoreCase("npc")) | |
+ { | |
+ // mandatory | |
+ final int templateId = parseInt(attrs, "id"); | |
+ // coordinates are optional, if territory is specified; mandatory otherwise | |
+ int x = 0; | |
+ int y = 0; | |
+ int z = 0; | |
+ | |
+ try | |
+ { | |
+ x = parseInt(attrs, "x"); | |
+ y = parseInt(attrs, "y"); | |
+ z = parseInt(attrs, "z"); | |
+ } | |
+ catch (NullPointerException npe) | |
+ { | |
+ // x, y, z can be unspecified, if this spawn is territory based, do nothing | |
+ } | |
+ | |
+ if ((x == 0) && (y == 0) && (territoryName == null)) // Both coordinates and zone are unspecified | |
+ { | |
+ _log.warning("XML Spawnlist: Spawn could not be initialized, both coordinates and zone are unspecified for ID " + templateId); | |
+ continue; | |
+ } | |
+ | |
+ StatsSet spawnInfo = new StatsSet(); | |
+ spawnInfo.set("npcTemplateid", templateId); | |
+ spawnInfo.set("x", x); | |
+ spawnInfo.set("y", y); | |
+ spawnInfo.set("z", z); | |
+ spawnInfo.set("territoryName", territoryName); | |
+ | |
+ // trying to read optional parameters | |
+ if (attrs.getNamedItem("heading") != null) | |
+ { | |
+ spawnInfo.set("heading", parseInt(attrs, "heading")); | |
+ } | |
+ | |
+ if (attrs.getNamedItem("count") != null) | |
+ { | |
+ spawnInfo.set("count", parseInt(attrs, "count")); | |
+ } | |
+ | |
+ if (attrs.getNamedItem("respawnDelay") != null) | |
+ { | |
+ spawnInfo.set("respawnDelay", parseInt(attrs, "respawnDelay")); | |
+ } | |
+ | |
+ if (attrs.getNamedItem("respawnRandom") != null) | |
+ { | |
+ spawnInfo.set("respawnRandom", parseInt(attrs, "respawnRandom")); | |
+ } | |
+ | |
+ if (attrs.getNamedItem("periodOfDay") != null) | |
+ { | |
+ String period = attrs.getNamedItem("periodOfDay").getNodeValue(); | |
+ if (period.equalsIgnoreCase("day") || period.equalsIgnoreCase("night")) | |
+ { | |
+ spawnInfo.set("periodOfDay", period.equalsIgnoreCase("day") ? 1 : 2); | |
+ } | |
+ } | |
+ | |
+ _xmlSpawnCount += addSpawn(spawnInfo); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
/** | |
* Retrieves spawn data from database. | |
* @param isCustom if {@code true} the spawns are loaded as custom from custom spawn table | |
@@ -86,52 +213,29 @@ | |
Statement s = con.createStatement(); | |
ResultSet rs = s.executeQuery(isCustom ? SELECT_CUSTOM_SPAWNS : SELECT_SPAWNS)) | |
{ | |
- L2Spawn spawn; | |
- L2NpcTemplate npcTemplate; | |
- int npcId; | |
while (rs.next()) | |
{ | |
- npcId = rs.getInt("npc_templateid"); | |
- npcTemplate = NpcTable.getInstance().getTemplate(npcId); | |
- if (npcTemplate == null) | |
- { | |
- _log.warning(getClass().getSimpleName() + ": Data missing in NPC table for ID: " + npcId + "."); | |
- continue; | |
- } | |
+ StatsSet spawnInfo = new StatsSet(); | |
+ int npcId = rs.getInt("npc_templateid"); | |
- if (npcTemplate.isType("L2SiegeGuard") || npcTemplate.isType("L2RaidBoss") || (!Config.ALLOW_CLASS_MASTERS && npcTemplate.isType("L2ClassMaster"))) | |
+ // Check basic requirements first | |
+ if (!checkTemplate(npcId)) | |
{ | |
- // Don't spawn | |
continue; | |
} | |
- spawn = new L2Spawn(npcTemplate); | |
- spawn.setAmount(rs.getInt("count")); | |
- spawn.setX(rs.getInt("locx")); | |
- spawn.setY(rs.getInt("locy")); | |
- spawn.setZ(rs.getInt("locz")); | |
- spawn.setHeading(rs.getInt("heading")); | |
- spawn.setRespawnDelay(rs.getInt("respawn_delay"), rs.getInt("respawn_random")); | |
- spawn.setCustom(isCustom); | |
- int loc_id = rs.getInt("loc_id"); | |
- spawn.setLocationId(loc_id); | |
- | |
- switch (rs.getInt("periodOfDay")) | |
- { | |
- case 0: // default | |
- npcSpawnCount += spawn.init(); | |
- break; | |
- case 1: // Day | |
- DayNightSpawnManager.getInstance().addDayCreature(spawn); | |
- npcSpawnCount++; | |
- break; | |
- case 2: // Night | |
- DayNightSpawnManager.getInstance().addNightCreature(spawn); | |
- npcSpawnCount++; | |
- break; | |
- } | |
- | |
- addSpawn(spawn); | |
+ spawnInfo.set("npcTemplateid", npcId); | |
+ spawnInfo.set("count", rs.getInt("count")); | |
+ spawnInfo.set("x", rs.getInt("locx")); | |
+ spawnInfo.set("y", rs.getInt("locy")); | |
+ spawnInfo.set("z", rs.getInt("locz")); | |
+ spawnInfo.set("heading", rs.getInt("heading")); | |
+ spawnInfo.set("respawnDelay", rs.getInt("respawn_delay")); | |
+ spawnInfo.set("respawnRandom", rs.getInt("respawn_random")); | |
+ spawnInfo.set("locId", rs.getInt("loc_id")); | |
+ spawnInfo.set("periodOfDay", rs.getInt("periodOfDay")); | |
+ spawnInfo.set("isCustomSpawn", isCustom); | |
+ npcSpawnCount += addSpawn(spawnInfo); | |
} | |
} | |
catch (Exception e) | |
@@ -141,6 +245,52 @@ | |
return npcSpawnCount; | |
} | |
+ private int addSpawn(StatsSet spawnInfo) | |
+ { | |
+ L2Spawn spawnDat; | |
+ int ret = 0; | |
+ try | |
+ { | |
+ spawnDat = new L2Spawn(NpcTable.getInstance().getTemplate(spawnInfo.getInteger("npcTemplateid"))); | |
+ spawnDat.setAmount(spawnInfo.getInteger("count", 1)); | |
+ spawnDat.setX(spawnInfo.getInteger("x", 0)); | |
+ spawnDat.setY(spawnInfo.getInteger("y", 0)); | |
+ spawnDat.setZ(spawnInfo.getInteger("z", 0)); | |
+ spawnDat.setHeading(spawnInfo.getInteger("heading", -1)); | |
+ spawnDat.setRespawnDelay(spawnInfo.getInteger("respawnDelay", 0), spawnInfo.getInteger("respawnRandom", 0)); | |
+ spawnDat.setLocationId(spawnInfo.getInteger("locId", 0)); | |
+ String territoryName = spawnInfo.getString("territoryName", ""); | |
+ spawnDat.setCustom(spawnInfo.getBool("isCustomSpawn", false)); | |
+ if (!territoryName.isEmpty()) | |
+ { | |
+ spawnDat.setSpawnTerritory(ZoneManager.getInstance().getSpawnTerritory(territoryName)); | |
+ } | |
+ switch (spawnInfo.getInteger("periodOfDay", 0)) | |
+ { | |
+ case 0: // default | |
+ ret += spawnDat.init(); | |
+ break; | |
+ case 1: // Day | |
+ DayNightSpawnManager.getInstance().addDayCreature(spawnDat); | |
+ ret = 1; | |
+ break; | |
+ case 2: // Night | |
+ DayNightSpawnManager.getInstance().addNightCreature(spawnDat); | |
+ ret = 1; | |
+ break; | |
+ } | |
+ | |
+ addSpawn(spawnDat); | |
+ } | |
+ catch (Exception e) | |
+ { | |
+ // problem with initializing spawn, go to next one | |
+ _log.log(Level.WARNING, "Spawn could not be initialized: " + e.getMessage(), e); | |
+ } | |
+ | |
+ return ret; | |
+ } | |
+ | |
public Map<Integer, Set<L2Spawn>> getSpawnTable() | |
{ | |
return _spawnTable; | |
Index: java/com/l2jserver/gameserver/instancemanager/ZoneManager.java | |
=================================================================== | |
--- java/com/l2jserver/gameserver/instancemanager/ZoneManager.java (revision 6097) | |
+++ java/com/l2jserver/gameserver/instancemanager/ZoneManager.java (working copy) | |
@@ -37,6 +37,7 @@ | |
import com.l2jserver.gameserver.model.actor.L2Character; | |
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance; | |
import com.l2jserver.gameserver.model.zone.AbstractZoneSettings; | |
+import com.l2jserver.gameserver.model.zone.L2ZoneForm; | |
import com.l2jserver.gameserver.model.zone.L2ZoneRespawn; | |
import com.l2jserver.gameserver.model.zone.L2ZoneType; | |
import com.l2jserver.gameserver.model.zone.form.ZoneCuboid; | |
@@ -45,6 +46,7 @@ | |
import com.l2jserver.gameserver.model.zone.type.L2ArenaZone; | |
import com.l2jserver.gameserver.model.zone.type.L2OlympiadStadiumZone; | |
import com.l2jserver.gameserver.model.zone.type.L2RespawnZone; | |
+import com.l2jserver.gameserver.model.zone.type.NpcSpawnTerritory; | |
/** | |
* This class manages the zones | |
@@ -55,6 +57,7 @@ | |
private static final Map<String, AbstractZoneSettings> _settings = new HashMap<>(); | |
private final Map<Class<? extends L2ZoneType>, Map<Integer, ? extends L2ZoneType>> _classZones = new HashMap<>(); | |
+ private final Map<String, NpcSpawnTerritory> _spawnTerritories = new HashMap<String, NpcSpawnTerritory>(); | |
private int _lastDynamicId = 300000; | |
private List<L2ItemInstance> _debugItems; | |
@@ -143,6 +146,17 @@ | |
{ | |
attrs = d.getAttributes(); | |
+ attribute = attrs.getNamedItem("type"); | |
+ if (attribute != null) | |
+ { | |
+ zoneType = attribute.getNodeValue(); | |
+ } | |
+ else | |
+ { | |
+ _log.warning("ZoneData: Missing type for zone in file: " + getCurrentFile().getName()); | |
+ continue; | |
+ } | |
+ | |
attribute = attrs.getNamedItem("id"); | |
if (attribute != null) | |
{ | |
@@ -150,7 +164,7 @@ | |
} | |
else | |
{ | |
- zoneId = _lastDynamicId++; | |
+ zoneId = zoneType.equalsIgnoreCase("NpcSpawnTerritory") ? 0 : _lastDynamicId++; | |
} | |
attribute = attrs.getNamedItem("name"); | |
@@ -162,6 +176,21 @@ | |
{ | |
zoneName = null; | |
} | |
+ | |
+ // Check zone name for NpcSpawnTerritory. Must exist and to be unique | |
+ if (zoneType.equalsIgnoreCase("NpcSpawnTerritory")) | |
+ { | |
+ if (zoneName == null) | |
+ { | |
+ _log.warning("ZoneData: Missing name for NpcSpawnTerritory in file: " + getCurrentFile().getName() + ", skipping zone"); | |
+ continue; | |
+ } | |
+ else if (_spawnTerritories.containsKey(zoneName)) | |
+ { | |
+ _log.warning("ZoneData: Name " + zoneName + " already used for another zone, check file: " + getCurrentFile().getName() + ". Skipping zone"); | |
+ continue; | |
+ } | |
+ } | |
minZ = parseInt(attrs, "minZ"); | |
maxZ = parseInt(attrs, "maxZ"); | |
@@ -169,23 +198,8 @@ | |
zoneType = attrs.getNamedItem("type").getNodeValue(); | |
zoneShape = attrs.getNamedItem("shape").getNodeValue(); | |
- // Create the zone | |
- Class<?> newZone = null; | |
- Constructor<?> zoneConstructor = null; | |
- L2ZoneType temp; | |
- try | |
- { | |
- newZone = Class.forName("com.l2jserver.gameserver.model.zone.type.L2" + zoneType); | |
- zoneConstructor = newZone.getConstructor(int.class); | |
- temp = (L2ZoneType) zoneConstructor.newInstance(zoneId); | |
- } | |
- catch (Exception e) | |
- { | |
- _log.warning(getClass().getSimpleName() + ": ZoneData: No such zone type: " + zoneType + " in file: " + getCurrentFile().getName()); | |
- continue; | |
- } | |
- | |
// Get the zone shape from xml | |
+ L2ZoneForm zoneForm = null; | |
try | |
{ | |
coords = null; | |
@@ -221,7 +235,7 @@ | |
{ | |
if (coords.length == 2) | |
{ | |
- temp.setZone(new ZoneCuboid(coords[0][0], coords[1][0], coords[0][1], coords[1][1], minZ, maxZ)); | |
+ zoneForm = new ZoneCuboid(coords[0][0], coords[1][0], coords[0][1], coords[1][1], minZ, maxZ); | |
} | |
else | |
{ | |
@@ -241,7 +255,7 @@ | |
aX[i] = coords[i][0]; | |
aY[i] = coords[i][1]; | |
} | |
- temp.setZone(new ZoneNPoly(aX, aY, minZ, maxZ)); | |
+ zoneForm = new ZoneNPoly(aX, aY, minZ, maxZ); | |
} | |
else | |
{ | |
@@ -257,7 +271,7 @@ | |
final int zoneRad = Integer.parseInt(attrs.getNamedItem("rad").getNodeValue()); | |
if ((coords.length == 1) && (zoneRad > 0)) | |
{ | |
- temp.setZone(new ZoneCylinder(coords[0][0], coords[0][1], minZ, maxZ, zoneRad)); | |
+ zoneForm = new ZoneCylinder(coords[0][0], coords[0][1], minZ, maxZ, zoneRad); | |
} | |
else | |
{ | |
@@ -265,11 +279,40 @@ | |
continue; | |
} | |
} | |
+ else | |
+ { | |
+ _log.warning(getClass().getSimpleName() + ": ZoneData: Unknown shape: \"" + zoneShape + "\" for zone: " + zoneId + " in file: " + getCurrentFile().getName()); | |
+ continue; | |
+ } | |
} | |
catch (Exception e) | |
{ | |
_log.log(Level.WARNING, getClass().getSimpleName() + ": ZoneData: Failed to load zone " + zoneId + " coordinates: " + e.getMessage(), e); | |
} | |
+ | |
+ // No further parameters needed, if NpcSpawnTerritory is loading | |
+ if (zoneType.equalsIgnoreCase("NpcSpawnTerritory")) | |
+ { | |
+ _spawnTerritories.put(zoneName, new NpcSpawnTerritory(zoneName, zoneForm)); | |
+ continue; | |
+ } | |
+ | |
+ // Create the zone | |
+ Class<?> newZone = null; | |
+ Constructor<?> zoneConstructor = null; | |
+ L2ZoneType temp; | |
+ try | |
+ { | |
+ newZone = Class.forName("com.l2jserver.gameserver.model.zone.type.L2" + zoneType); | |
+ zoneConstructor = newZone.getConstructor(int.class); | |
+ temp = (L2ZoneType) zoneConstructor.newInstance(zoneId); | |
+ temp.setZone(zoneForm); | |
+ } | |
+ catch (Exception e) | |
+ { | |
+ _log.warning(getClass().getSimpleName() + ": ZoneData: No such zone type: " + zoneType + " in file: " + getCurrentFile().getName()); | |
+ continue; | |
+ } | |
// Check for additional parameters | |
for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling()) | |
@@ -346,9 +389,11 @@ | |
long started = System.currentTimeMillis(); | |
parseDirectory("data/zones"); | |
+ parseDirectory("data/zones/npcSpawnTerritories"); | |
started = System.currentTimeMillis() - started; | |
_log.info(getClass().getSimpleName() + ": Loaded " + _classZones.size() + " zone classes and " + getSize() + " zones in " + (started / 1000) + " seconds."); | |
+ _log.info(getClass().getSimpleName() + ": Loaded " + _spawnTerritories.size() + " NPC spawn territoriers."); | |
} | |
/** | |
@@ -552,8 +597,39 @@ | |
} | |
return null; | |
} | |
+ | |
+ /** | |
+ * Get spawm territory by name | |
+ * @param name name of territory to search | |
+ * @return link to zone form | |
+ * | |
+ */ | |
+ public NpcSpawnTerritory getSpawnTerritory(String name) | |
+ { | |
+ return _spawnTerritories.containsKey(name) ? _spawnTerritories.get(name) : null; | |
+ } | |
/** | |
+ * Returns all spawm territories from where the object is located | |
+ * | |
+ * @param object | |
+ * @return zones | |
+ */ | |
+ public List<NpcSpawnTerritory> getSpawnTerritories(L2Object object) | |
+ { | |
+ List<NpcSpawnTerritory> temp = new ArrayList<NpcSpawnTerritory>(); | |
+ for (NpcSpawnTerritory territory : _spawnTerritories.values()) | |
+ { | |
+ if (territory.isInsideZone(object.getX(), object.getY(), object.getZ())) | |
+ { | |
+ temp.add(territory); | |
+ } | |
+ } | |
+ | |
+ return temp; | |
+ } | |
+ | |
+ /** | |
* Gets the arena. | |
* @param character the character | |
* @return the arena | |
Index: java/com/l2jserver/gameserver/model/L2Spawn.java | |
=================================================================== | |
--- java/com/l2jserver/gameserver/model/L2Spawn.java (revision 6097) | |
+++ java/com/l2jserver/gameserver/model/L2Spawn.java (working copy) | |
@@ -36,6 +36,8 @@ | |
import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance; | |
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate; | |
import com.l2jserver.gameserver.model.interfaces.IPositionable; | |
+import com.l2jserver.gameserver.model.zone.type.NpcSpawnTerritory; | |
+import com.l2jserver.gameserver.util.L2TIntObjectHashMap; | |
import com.l2jserver.util.Rnd; | |
/** | |
@@ -61,6 +63,8 @@ | |
private int _locationId; | |
/** The Location of this NPC spawn. */ | |
private Location _location = new Location(0, 0, 0); | |
+ /** Link to NPC spawn territory */ | |
+ private NpcSpawnTerritory _spawnTerritory = null; | |
/** Minimum respawn delay */ | |
private int _respawnMinDelay; | |
/** Maximum respawn delay */ | |
@@ -74,6 +78,8 @@ | |
private boolean _customSpawn; | |
private L2Npc _lastSpawn; | |
private static List<SpawnListener> _spawnListeners = new FastList<>(); | |
+ private FastList<L2Npc> _spawnedNpcs = new FastList<L2Npc>(); | |
+ private L2TIntObjectHashMap<Location> _lastSpawnPoints; | |
private boolean _isNoRndWalk = false; // Is no random walk | |
/** The task launching the function doSpawn() */ | |
@@ -168,6 +174,19 @@ | |
{ | |
return _location.getX(); | |
} | |
+ | |
+ /** | |
+ * @return the X position of the last spawn point of given NPC. | |
+ */ | |
+ public int getX(L2Npc npc) | |
+ { | |
+ if ((_lastSpawnPoints == null) || (npc == null) || !_lastSpawnPoints.containsKey(npc.getObjectId())) | |
+ { | |
+ return 0; | |
+ } | |
+ | |
+ return _lastSpawnPoints.get(npc.getObjectId()).getX(); | |
+ } | |
/** | |
* Set the X position of the spawn point. | |
@@ -183,6 +202,19 @@ | |
{ | |
return _location.getY(); | |
} | |
+ | |
+ /** | |
+ * @return the Y position of the last spawn point of given NPC. | |
+ */ | |
+ public int getY(L2Npc npc) | |
+ { | |
+ if ((_lastSpawnPoints == null) || (npc == null) || !_lastSpawnPoints.containsKey(npc.getObjectId())) | |
+ { | |
+ return 0; | |
+ } | |
+ | |
+ return _lastSpawnPoints.get(npc.getObjectId()).getY(); | |
+ } | |
/** | |
* Set the Y position of the spawn point. | |
@@ -198,6 +230,19 @@ | |
{ | |
return _location.getZ(); | |
} | |
+ | |
+ /** | |
+ * @return the Z position of the last spawn point of given NPC. | |
+ */ | |
+ public int getZ(L2Npc npc) | |
+ { | |
+ if ((_lastSpawnPoints == null) || (npc == null) || !_lastSpawnPoints.containsKey(npc.getObjectId())) | |
+ { | |
+ return 0; | |
+ } | |
+ | |
+ return _lastSpawnPoints.get(npc.getObjectId()).getZ(); | |
+ } | |
/** | |
* Set the Z position of the spawn point. | |
@@ -327,7 +372,16 @@ | |
// Decrease the current number of L2NpcInstance of this L2Spawn | |
_currentCount--; | |
+ | |
+ // Remove this NPC from list of spawned | |
+ _spawnedNpcs.remove(oldNpc); | |
+ // Remove spawn point for old NPC | |
+ if (_lastSpawnPoints != null) | |
+ { | |
+ _lastSpawnPoints.remove(oldNpc.getObjectId()); | |
+ } | |
+ | |
// Check if respawn is possible to prevent multiple respawning caused by lag | |
if (_doRespawn && ((_scheduledCount + _currentCount) < _maximumCount)) | |
{ | |
@@ -467,9 +521,18 @@ | |
{ | |
int newlocx, newlocy, newlocz; | |
- // If Locx=0 and Locy=0, the L2NpcInstance must be spawned in an area defined by location | |
- if ((getX() == 0) && (getY() == 0)) | |
+ // If Locx and Locy are not defined, the L2NpcInstance must be spawned in an area defined by location or spawn territory | |
+ // New method | |
+ if (isTerritoryBased()) | |
{ | |
+ int[] p = _spawnTerritory.getRandomPoint(); | |
+ newlocx = p[0]; | |
+ newlocy = p[1]; | |
+ newlocz = p[2]; | |
+ } | |
+ // Old method (for backward compatibility) | |
+ else if ((getX() == 0) && (getY() == 0)) | |
+ { | |
if (getLocationId() == 0) | |
{ | |
return mob; | |
@@ -547,7 +610,11 @@ | |
L2Spawn.notifyNpcSpawned(mob); | |
- _lastSpawn = mob; | |
+ _spawnedNpcs.add(mob); | |
+ if (_lastSpawnPoints != null) | |
+ { | |
+ _lastSpawnPoints.put(mob.getObjectId(), new Location(newlocx, newlocy, newlocz)); | |
+ } | |
if (Config.DEBUG) | |
{ | |
@@ -628,10 +695,36 @@ | |
return _respawnMinDelay != _respawnMaxDelay; | |
} | |
+ public void setSpawnTerritory(NpcSpawnTerritory territory) | |
+ { | |
+ _spawnTerritory = territory; | |
+ _lastSpawnPoints = new L2TIntObjectHashMap<Location>(); | |
+ } | |
+ | |
+ public NpcSpawnTerritory getSpawnTerritory() | |
+ { | |
+ return _spawnTerritory; | |
+ } | |
+ | |
+ public boolean isTerritoryBased() | |
+ { | |
+ return (_spawnTerritory != null) && (_location.getX() == 0) && (_location.getY() == 0); | |
+ } | |
+ | |
public L2Npc getLastSpawn() | |
{ | |
- return _lastSpawn; | |
+ if (!_spawnedNpcs.isEmpty()) | |
+ { | |
+ return _spawnedNpcs.getLast(); | |
+ } | |
+ | |
+ return null; | |
} | |
+ | |
+ public final FastList<L2Npc> getSpawnedNpcs() | |
+ { | |
+ return _spawnedNpcs; | |
+ } | |
/** | |
* @param oldNpc | |
Index: java/com/l2jserver/gameserver/model/zone/type/NpcSpawnTerritory.java | |
=================================================================== | |
--- java/com/l2jserver/gameserver/model/zone/type/NpcSpawnTerritory.java (revision 0) | |
+++ java/com/l2jserver/gameserver/model/zone/type/NpcSpawnTerritory.java (working copy) | |
@@ -0,0 +1,60 @@ | |
+/* | |
+ * Copyright (C) 2004-2013 L2J Server | |
+ * | |
+ * This file is part of L2J Server. | |
+ * | |
+ * L2J Server 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. | |
+ * | |
+ * L2J Server 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/>. | |
+ */ | |
+package com.l2jserver.gameserver.model.zone.type; | |
+ | |
+import com.l2jserver.gameserver.model.zone.L2ZoneForm; | |
+ | |
+import java.util.List; | |
+ | |
+/** | |
+ * Just dummy zone, needs only to geometry calculations | |
+ * @author GKR | |
+ */ | |
+public class NpcSpawnTerritory | |
+{ | |
+ private String _name; | |
+ private L2ZoneForm _territory; | |
+ private List<L2ZoneForm> _bannedTerritories; // TODO: Implement it | |
+ | |
+ public NpcSpawnTerritory(String name, L2ZoneForm territory) | |
+ { | |
+ _name = name; | |
+ _territory = territory; | |
+ } | |
+ | |
+ public String getName() | |
+ { | |
+ return _name; | |
+ } | |
+ | |
+ public int[] getRandomPoint() | |
+ { | |
+ return _territory.getRandomPoint(); | |
+ } | |
+ | |
+ public boolean isInsideZone(int x, int y, int z) | |
+ { | |
+ return _territory.isInsideZone(x, y, z); | |
+ } | |
+ | |
+ public void visualizeZone(int z) | |
+ { | |
+ _territory.visualizeZone(z); | |
+ } | |
+} | |
\ No newline at end of file |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: dist/game/data/html/admin/npcinfo.htm | |
=================================================================== | |
--- dist/game/data/html/admin/npcinfo.htm (revision 9857) | |
+++ dist/game/data/html/admin/npcinfo.htm (working copy) | |
@@ -19,7 +19,9 @@ | |
<tr><td colspan="4"><table width=270 border=0><tr><td width=270><font color="LEVEL">Npc Info</font> : %name% (<font color="LEVEL">%id%</font>) [<font color=00FF00>%lvl%</font> lvl]</td></tr></table></td></tr> | |
<tr><td><table width=270 border=0 bgcolor=131210><tr><td width=100><font color="LEVEL">ObjectId</font></td><td align=right width=170>%objid%</td></tr></table></td></tr> | |
<tr><td><table width=270 border=0><tr><td width=100><font color="LEVEL">Type:</font></td><td align=right width=170><font color=999999>%class%</font></td></tr></table></td></tr> | |
-<tr><td><table width=270 border=0 bgcolor=131210><tr><td width=100><font color="LEVEL">Spawn</font></td><td align=right width=170>%spawn%</td></tr></table></td></tr> | |
+<tr><td><table width=270 border=0><tr><td width=100><font color="LEVEL">Territory:</font></td><td align=right width=170>%territory%</td></tr></table></td></tr> | |
+<tr><td><table width=270 border=0 bgcolor=131210><tr><td width=100><font color="LEVEL">Spawn type:</font></td><td align=right width=170>%spawntype%</td></tr></table></td></tr> | |
+<tr><td><table width=270 border=0><tr><td width=100><font color="LEVEL">Spawn loc:</font></td><td align=right width=170>%spawn%</td></tr></table></td></tr> | |
<tr><td><table width=270 border=0><tr><td width=100><font color="LEVEL">Location:</font></td><td align=right width=170>%loc%</td></tr></table></td></tr> | |
<tr><td><table width=270 border=0 bgcolor=131210><tr><td width=100><font color="LEVEL">Heading:</font></td><td align=right width=170>%heading%</td></tr></table></td></tr> | |
<tr><td><table width=270 border=0><tr><td width=100><font color="LEVEL">Collision Radius:</font></td><td align=right width=170>%collision_radius%</td></tr></table></td></tr> | |
Index: dist/game/data/scripts/handlers/actionhandlers/L2NpcActionShift.java | |
=================================================================== | |
--- dist/game/data/scripts/handlers/actionhandlers/L2NpcActionShift.java (revision 9857) | |
+++ dist/game/data/scripts/handlers/actionhandlers/L2NpcActionShift.java (working copy) | |
@@ -112,7 +112,17 @@ | |
if (((L2Npc) target).getSpawn() != null) | |
{ | |
- html.replace("%spawn%", ((L2Npc) target).getSpawn().getX() + " " + ((L2Npc) target).getSpawn().getY() + " " + ((L2Npc) target).getSpawn().getZ()); | |
+ html.replace("%territory%", ((L2Npc)target).getSpawn().getSpawnTerritory() == null ? "None" : ((L2Npc)target).getSpawn().getSpawnTerritory().getName()); | |
+ if (((L2Npc)target).getSpawn().isTerritoryBased()) | |
+ { | |
+ html.replace("%spawntype%", "Random"); | |
+ html.replace("%spawn%", ((L2Npc)target).getSpawn().getX((L2Npc)target) + " " + ((L2Npc)target).getSpawn().getY((L2Npc)target) + " " + ((L2Npc)target).getSpawn().getZ((L2Npc)target)); | |
+ } | |
+ else | |
+ { | |
+ html.replace("%spawntype%", "Fixed"); | |
+ html.replace("%spawn%", ((L2Npc)target).getSpawn().getX()+" "+((L2Npc)target).getSpawn().getY()+" "+((L2Npc)target).getSpawn().getZ()); | |
+ } | |
html.replace("%loc2d%", String.valueOf((int) Math.sqrt(((L2Character) target).getPlanDistanceSq(((L2Npc) target).getSpawn().getX(), ((L2Npc) target).getSpawn().getY())))); | |
html.replace("%loc3d%", String.valueOf((int) Math.sqrt(((L2Character) target).getDistanceSq(((L2Npc) target).getSpawn().getX(), ((L2Npc) target).getSpawn().getY(), ((L2Npc) target).getSpawn().getZ())))); | |
if (((L2Npc) target).getSpawn().getRespawnMinDelay() == 0) | |
@@ -130,6 +140,8 @@ | |
} | |
else | |
{ | |
+ html.replace("%territory%", "<font color=FF0000>--</font>"); | |
+ html.replace("%spawntype%", "<font color=FF0000>--</font>"); | |
html.replace("%spawn%", "<font color=FF0000>null</font>"); | |
html.replace("%loc2d%", "<font color=FF0000>--</font>"); | |
html.replace("%loc3d%", "<font color=FF0000>--</font>"); | |
Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminZone.java | |
=================================================================== | |
--- dist/game/data/scripts/handlers/admincommandhandlers/AdminZone.java (revision 9857) | |
+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminZone.java (working copy) | |
@@ -30,6 +30,7 @@ | |
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; | |
import com.l2jserver.gameserver.model.zone.L2ZoneType; | |
import com.l2jserver.gameserver.model.zone.ZoneId; | |
+import com.l2jserver.gameserver.model.zone.type.NpcSpawnTerritory; | |
import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; | |
import com.l2jserver.util.StringUtil; | |
@@ -89,6 +90,11 @@ | |
{ | |
zone.visualizeZone(activeChar.getZ()); | |
} | |
+ for (NpcSpawnTerritory territory : ZoneManager.getInstance().getSpawnTerritories(activeChar)) | |
+ { | |
+ territory.visualizeZone(activeChar.getZ()); | |
+ } | |
+ | |
showHtml(activeChar); | |
} | |
else | |
@@ -147,6 +153,10 @@ | |
StringUtil.append(zones, " "); | |
} | |
} | |
+ for (NpcSpawnTerritory territory : ZoneManager.getInstance().getSpawnTerritories(activeChar)) | |
+ { | |
+ StringUtil.append(zones, territory.getName() + "<br1>"); | |
+ } | |
adminReply.replace("%ZLIST%", zones.toString()); | |
activeChar.sendPacket(adminReply); | |
} | |
Index: dist/game/data/spawnlist/test.xml | |
=================================================================== | |
--- dist/game/data/spawnlist/test.xml (revision 0) | |
+++ dist/game/data/spawnlist/test.xml (working copy) | |
@@ -0,0 +1,6 @@ | |
+<?xml version="1.0" encoding="UTF-8"?> | |
+<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../xsd/spawnlist.xsd"> | |
+ <spawn zone="spawn_zone_test_01"> | |
+ <npc id="20225" count="3" respawnDelay="15" respawnRandom="5" /> | |
+ </spawn> | |
+</list> | |
Index: dist/game/data/xsd/spawnlist.xsd | |
=================================================================== | |
--- dist/game/data/xsd/spawnlist.xsd (revision 0) | |
+++ dist/game/data/xsd/spawnlist.xsd (working copy) | |
@@ -0,0 +1,40 @@ | |
+<?xml version="1.0" encoding="UTF-8"?> | |
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> | |
+ <xs:element name="list"> | |
+ <xs:complexType> | |
+ <xs:sequence maxOccurs="1" minOccurs="1"> | |
+ <xs:element name="spawn" minOccurs="1" maxOccurs="unbounded"> | |
+ <xs:complexType> | |
+ <xs:sequence minOccurs="1" maxOccurs="unbounded"> | |
+ <xs:element name="npc" minOccurs="1" maxOccurs="unbounded"> | |
+ <xs:complexType> | |
+ <xs:simpleContent> | |
+ <xs:extension base="xs:string"> | |
+ <xs:attribute name="id" type="xs:positiveInteger" use="required" /> | |
+ <xs:attribute name="x" type="xs:integer" use="optional" /> | |
+ <xs:attribute name="y" type="xs:integer" use="optional" /> | |
+ <xs:attribute name="z" type="xs:integer" use="optional" /> | |
+ <xs:attribute name="heading" type="xs:integer" use="optional" /> | |
+ <xs:attribute name="count" type="xs:positiveInteger" use="optional" /> | |
+ <xs:attribute name="respawnDelay" type="xs:nonNegativeInteger" use="optional" /> | |
+ <xs:attribute name="respawnRandom" type="xs:nonNegativeInteger" use="optional" /> | |
+ <xs:attribute name="periodOfDay" use="optional"> | |
+ <xs:simpleType> | |
+ <xs:restriction base="xs:token"> | |
+ <xs:enumeration value="day" /> | |
+ <xs:enumeration value="night" /> | |
+ </xs:restriction> | |
+ </xs:simpleType> | |
+ </xs:attribute> | |
+ </xs:extension> | |
+ </xs:simpleContent> | |
+ </xs:complexType> | |
+ </xs:element> | |
+ </xs:sequence> | |
+ <xs:attribute type="xs:string" name="zone" use="optional"/> | |
+ </xs:complexType> | |
+ </xs:element> | |
+ </xs:sequence> | |
+ </xs:complexType> | |
+ </xs:element> | |
+</xs:schema> | |
\ No newline at end of file | |
Index: dist/game/data/xsd/zones.xsd | |
=================================================================== | |
--- dist/game/data/xsd/zones.xsd (revision 9857) | |
+++ dist/game/data/xsd/zones.xsd (working copy) | |
@@ -132,6 +132,7 @@ | |
<xs:enumeration value="NoRestartZone" /> | |
<xs:enumeration value="NoStoreZone" /> | |
<xs:enumeration value="NoSummonFriendZone" /> | |
+ <xs:enumeration value="NpcSpawnTerritory" /> | |
<xs:enumeration value="OlympiadStadiumZone" /> | |
<xs:enumeration value="PeaceZone" /> | |
<xs:enumeration value="ResidenceHallTeleportZone" /> | |
Index: dist/game/data/zones/npcSpawnTerritories/npc_spawn_zone_test.xml | |
=================================================================== | |
--- dist/game/data/zones/npcSpawnTerritories/npc_spawn_zone_test.xml (revision 0) | |
+++ dist/game/data/zones/npcSpawnTerritories/npc_spawn_zone_test.xml (working copy) | |
@@ -0,0 +1,7 @@ | |
+<?xml version="1.0" encoding="UTF-8"?> | |
+<list enabled="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../xsd/zones.xsd"> | |
+ <zone name="spawn_zone_test_01" type="NpcSpawnTerritory" shape="Cuboid" minZ="-3775" maxZ="-3770"> | |
+ <node X="73441" Y="143187" /> | |
+ <node X="72596" Y="142364" /> | |
+ </zone> | |
+</list> | |
\ No newline at end of file |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment