Skip to content

Instantly share code, notes, and snippets.

@VlLight
Last active December 19, 2015 21:09
Show Gist options
  • Save VlLight/6018291 to your computer and use it in GitHub Desktop.
Save VlLight/6018291 to your computer and use it in GitHub Desktop.
Zone spawn support
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
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