Skip to content

Instantly share code, notes, and snippets.

@VlLight
Created November 19, 2013 07:32
Show Gist options
  • Save VlLight/7541644 to your computer and use it in GitHub Desktop.
Save VlLight/7541644 to your computer and use it in GitHub Desktop.
This is patch is related to some NPC variables. 1. Script variables (those are done as NpcVariables class here). It is dynamic data, that changes during NPC "lifetime", reinitializes at NPC spawn, could contains several variable and related to certain NPC. We already have setScriptValue() and isScriptValue() methos but their paradigm is supporte…
Index: java/com/l2jserver/gameserver/datatables/NpcPersonalAIData.java
===================================================================
--- java/com/l2jserver/gameserver/datatables/NpcPersonalAIData.java (revision 0)
+++ java/com/l2jserver/gameserver/datatables/NpcPersonalAIData.java (working copy)
@@ -0,0 +1,132 @@
+/*
+ * 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.datatables;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.l2jserver.gameserver.model.L2Spawn;
+import com.l2jserver.gameserver.model.actor.L2Npc;
+import com.l2jserver.util.Rnd;
+
+/**
+ * This class holds parameter, specific to certain NPC's.
+ * It can be either general parameters overrided for certain NPC instance instead of template parameters(aggro range, for example), or some optional parameters, handled by datapack scripts.<br>
+ * @author GKR
+ */
+public class NpcPersonalAIData
+{
+ private final Map<String, Map<String, Integer>> _AIData;
+
+ /**
+ * Instantiates a new table.
+ */
+ protected NpcPersonalAIData()
+ {
+ _AIData = new HashMap<>();
+ }
+
+ /**
+ * Stores data for given spawn.
+ * @param spawnDat spawn to process
+ * @param data Map of AI values
+ */
+ public void storeData(L2Spawn spawnDat, Map<String, Integer> data)
+ {
+ if ((data != null) && !data.isEmpty())
+ {
+ // check for spawn name. Since spawn name is key for AI Data, generate random name, if spawn name isn't specified
+ if (spawnDat.getName() == null)
+ {
+ spawnDat.setName(Long.toString(Rnd.nextLong()));
+ }
+
+ _AIData.put(spawnDat.getName(), data);
+ }
+ }
+
+ /**
+ * @param spawnName spawn name to check
+ * @param paramName parameter to check
+ * @return value of given parameter for given spawn name
+ */
+ public int getAIValue(String spawnName, String paramName)
+ {
+ return hasAIValue(spawnName, paramName) ? _AIData.get(spawnName).get(paramName) : -1;
+ }
+
+ /**
+ * @param spawnName spawn name to check
+ * @param paramName parameter name to check
+ * @return {@code true} if parameter paramName is set for spawn spawnName, {@code false} otherwise
+ */
+ public boolean hasAIValue(String spawnName, String paramName)
+ {
+ return (spawnName != null) && _AIData.containsKey(spawnName) && _AIData.get(spawnName).containsKey(paramName);
+ }
+
+ /**
+ * Initializes npc parameters by specified values.
+ * @param npc NPC to process
+ * @param spawn link to NPC's spawn
+ * @param spawnName name of spawn
+ */
+ public void initializeNpcParameters(L2Npc npc, L2Spawn spawn, String spawnName)
+ {
+ if (_AIData.containsKey(spawnName))
+ {
+ Map<String, Integer> map = _AIData.get(spawnName);
+
+ try
+ {
+ for (String key : map.keySet())
+ {
+ switch (key)
+ {
+ case "disableRandomAnimation":
+ npc.setRandomAnimationEnabled((map.get(key) == 0));
+ break;
+ case "disableRandomWalk":
+ npc.setIsNoRndWalk((map.get(key) == 1));
+ spawn.setIsNoRndWalk((map.get(key) == 1));
+ break;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ // Do nothing
+ }
+ }
+ }
+
+ /**
+ * Gets the single instance of NpcTable.
+ * @return single instance of NpcTable
+ */
+ public static NpcPersonalAIData getInstance()
+ {
+ return SingletonHolder._instance;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final NpcPersonalAIData _instance = new NpcPersonalAIData();
+ }
+}
\ No newline at end of file
Index: java/com/l2jserver/gameserver/datatables/SpawnTable.java
===================================================================
--- java/com/l2jserver/gameserver/datatables/SpawnTable.java (revision 6279)
+++ java/com/l2jserver/gameserver/datatables/SpawnTable.java (working copy)
@@ -23,6 +23,7 @@
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
@@ -119,7 +120,14 @@
if (param.getNodeName().equalsIgnoreCase("spawn"))
{
String territoryName = null;
+ String spawnName = null;
+ Map<String, Integer> map = null;
+ // Check, if spawn name specified
+ if (attrs.getNamedItem("name") != null)
+ {
+ spawnName = parseString(attrs, "name");
+ }
// Check, if spawn territory specified and exists
if ((attrs.getNamedItem("zone") != null) && (ZoneManager.getInstance().getSpawnTerritory(attrs.getNamedItem("zone").getNodeValue()) != null))
{
@@ -129,7 +137,36 @@
for (Node npctag = param.getFirstChild(); npctag != null; npctag = npctag.getNextSibling())
{
attrs = npctag.getAttributes();
- if (npctag.getNodeName().equalsIgnoreCase("npc"))
+ // Check if there are any AI parameters
+ if (npctag.getNodeName().equalsIgnoreCase("AIData"))
+ {
+ attrs = npctag.getAttributes();
+ if (map == null)
+ {
+ map = new HashMap<>();
+ }
+ for (Node c = npctag.getFirstChild(); c != null; c = c.getNextSibling())
+ {
+ // Skip odd nodes
+ if (c.getNodeName().equals("#text"))
+ {
+ continue;
+ }
+ int val;
+ switch (c.getNodeName())
+ {
+ case "disableRandomAnimation":
+ case "disableRandomWalk":
+ val = Boolean.parseBoolean(c.getTextContent()) ? 1 : 0;
+ break;
+ default:
+ val = Integer.parseInt(c.getTextContent());
+ }
+ map.put(c.getNodeName(), val);
+ }
+ }
+ // Check for NPC spawns
+ else if (npctag.getNodeName().equalsIgnoreCase("npc"))
{
// mandatory
final int templateId = parseInt(attrs, "id");
@@ -161,6 +198,7 @@
spawnInfo.set("y", y);
spawnInfo.set("z", z);
spawnInfo.set("territoryName", territoryName);
+ spawnInfo.set("spawnName", spawnName);
// trying to read optional parameters
if (attrs.getNamedItem("heading") != null)
@@ -193,6 +231,7 @@
}
_xmlSpawnCount += addSpawn(spawnInfo);
+ _xmlSpawnCount += addSpawn(spawnInfo, map);
}
}
}
@@ -246,7 +285,13 @@
return npcSpawnCount;
}
- private int addSpawn(StatsSet spawnInfo)
+ /**
+ * Creates NPC spawn
+ * @param spawnInfo StatsSet of spawn parameters
+ * @param AIData Map of specific AI parameters for this spawn
+ * @return count NPC instances, spawned by this spawn
+ */
+ private int addSpawn(StatsSet spawnInfo, Map<String, Integer> AIData)
{
L2Spawn spawnDat;
int ret = 0;
@@ -261,11 +306,18 @@
spawnDat.setRespawnDelay(spawnInfo.getInt("respawnDelay", 0), spawnInfo.getInt("respawnRandom", 0));
spawnDat.setLocationId(spawnInfo.getInt("locId", 0));
String territoryName = spawnInfo.getString("territoryName", "");
+ String spawnName = spawnInfo.getString("spawnName", "");
spawnDat.setCustom(spawnInfo.getBoolean("isCustomSpawn", false));
+ if (!spawnName.isEmpty())
+ {
+ spawnDat.setName(spawnName);
+ }
if (!territoryName.isEmpty())
{
spawnDat.setSpawnTerritory(ZoneManager.getInstance().getSpawnTerritory(territoryName));
}
+ // Register AI Data for this spawn
+ NpcPersonalAIData.getInstance().storeData(spawnDat, AIData);
switch (spawnInfo.getInt("periodOfDay", 0))
{
case 0: // default
@@ -292,6 +344,16 @@
return ret;
}
+ /**
+ * Wrapper for {@link #addSpawn(StatsSet, Map<String, Integer>)}.
+ * @param spawnInfo StatsSet of spawn parameters
+ * @return count NPC instances, spawned by this spawn
+ */
+ private int addSpawn(StatsSet spawnInfo)
+ {
+ return addSpawn(spawnInfo, null);
+ }
+
public Map<Integer, Set<L2Spawn>> getSpawnTable()
{
return _spawnTable;
Index: java/com/l2jserver/gameserver/model/actor/L2Npc.java
===================================================================
--- java/com/l2jserver/gameserver/model/actor/L2Npc.java (revision 6279)
+++ java/com/l2jserver/gameserver/model/actor/L2Npc.java (working copy)
@@ -32,6 +32,7 @@
import com.l2jserver.gameserver.cache.HtmCache;
import com.l2jserver.gameserver.datatables.CategoryData;
import com.l2jserver.gameserver.datatables.ItemTable;
+import com.l2jserver.gameserver.datatables.NpcPersonalAIData;
import com.l2jserver.gameserver.enums.AIType;
import com.l2jserver.gameserver.enums.CategoryType;
import com.l2jserver.gameserver.enums.InstanceType;
@@ -73,6 +74,7 @@
import com.l2jserver.gameserver.model.quest.Quest;
import com.l2jserver.gameserver.model.skills.L2Skill;
import com.l2jserver.gameserver.model.skills.targets.L2TargetType;
+import com.l2jserver.gameserver.model.variables.NpcVariables;
import com.l2jserver.gameserver.model.zone.type.L2TownZone;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.AbstractNpcInfo;
@@ -573,11 +575,11 @@
}
/**
- * @return the Aggro Range of this L2NpcInstance contained in the L2NpcTemplate.
+ * @return the Aggro Range of this L2NpcInstance either contained in the L2NpcTemplate, or overriden by spawnlist AI value.
*/
public int getAggroRange()
{
- return _staticAIData.getAggroRange();
+ return hasAIValue("aggroRange") ? getAIValue("aggroRange") : _staticAIData.getAggroRange();
}
/**
@@ -1859,6 +1861,58 @@
}
/**
+ * @param paramName the parameter name to check
+ * @return given AI parameter value
+ */
+ public int getAIValue(final String paramName)
+ {
+ return hasAIValue(paramName) ? NpcPersonalAIData.getInstance().getAIValue(getSpawn().getName(), paramName) : -1;
+ }
+
+ /**
+ * @param paramName the parameter name to check
+ * @return {@code true} if given parameter is set for NPC, {@code false} otherwise
+ */
+ public boolean hasAIValue(final String paramName)
+ {
+ return (getSpawn() != null) && (getSpawn().getName() != null) && NpcPersonalAIData.getInstance().hasAIValue(getSpawn().getName(), paramName);
+ }
+
+ /**
+ * @param npc NPC to check
+ * @return {@code true} if both given NPC and this NPC is in the same spawn group, {@code false} otherwise
+ */
+ public boolean isInMySpawnGroup(L2Npc npc)
+ {
+ return ((getSpawn() != null) && (npc.getSpawn() != null) && (getSpawn().getName() != null) && (getSpawn().getName().equals(npc.getSpawn().getName())));
+ }
+
+ /**
+ * @return {@code true} if NPC currently located in own spawn point, {@code false} otherwise
+ */
+ public boolean staysInSpawnLoc()
+ {
+ return ((getSpawn() != null) && (getSpawn().getX(this) == getX()) && (getSpawn().getY(this) == getY()));
+ }
+
+ /**
+ * @return {@code true} if {@link NpcVariables} instance is attached to current player's scripts, {@code false} otherwise.
+ */
+ public boolean hasVariables()
+ {
+ return getScript(NpcVariables.class) != null;
+ }
+
+ /**
+ * @return {@link NpcVariables} instance containing parameters regarding player.
+ */
+ public NpcVariables getVariables()
+ {
+ final NpcVariables vars = getScript(NpcVariables.class);
+ return vars != null ? vars : addScript(new NpcVariables());
+ }
+
+ /**
* Send an "event" to all NPC's within given radius
* @param eventName - name of event
* @param radius - radius to send event
Index: java/com/l2jserver/gameserver/model/L2Spawn.java
===================================================================
--- java/com/l2jserver/gameserver/model/L2Spawn.java (revision 6279)
+++ java/com/l2jserver/gameserver/model/L2Spawn.java (working copy)
@@ -30,6 +30,7 @@
import com.l2jserver.Config;
import com.l2jserver.gameserver.GeoData;
import com.l2jserver.gameserver.ThreadPoolManager;
+import com.l2jserver.gameserver.datatables.NpcPersonalAIData;
import com.l2jserver.gameserver.datatables.TerritoryTable;
import com.l2jserver.gameserver.idfactory.IdFactory;
import com.l2jserver.gameserver.model.actor.L2Attackable;
@@ -38,6 +39,7 @@
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jserver.gameserver.model.interfaces.IIdentifiable;
import com.l2jserver.gameserver.model.interfaces.ILocational;
+import com.l2jserver.gameserver.model.interfaces.INamable;
import com.l2jserver.gameserver.model.interfaces.IPositionable;
import com.l2jserver.gameserver.model.zone.type.NpcSpawnTerritory;
import com.l2jserver.util.Rnd;
@@ -49,10 +51,12 @@
* The heading of the L2NpcInstance can be a random heading if not defined (value= -1) or an exact heading (ex : merchant...).
* @author Nightmare
*/
-public class L2Spawn implements IPositionable, IIdentifiable
+public class L2Spawn implements IPositionable, IIdentifiable, INamable
{
protected static final Logger _log = Logger.getLogger(L2Spawn.class.getName());
+ /** String identifier of this spawn */
+ private String _name;
/** The link on the L2NpcTemplate object containing generic and static properties of this spawn (ex : RewardExp, RewardSP, AggroRange...) */
private L2NpcTemplate _template;
/** The maximum number of L2NpcInstance that can manage this L2Spawn */
@@ -156,6 +160,24 @@
return _maximumCount;
}
+ /**
+ * @return the String Identifier of this spawn.
+ */
+ @Override
+ public String getName()
+ {
+ return _name;
+ }
+
+ /**
+ * Set the String Identifier of this spawn.
+ * @param name
+ */
+ public void setName(String name)
+ {
+ _name = name;
+ }
+
/**
* @return the Identifier of the location area where L2NpcInstance can be spwaned.
*/
@@ -531,6 +553,12 @@
return mob;
}
mob = (L2Npc) tmp;
+ // Check for certain AI data, overriden in spawnlist
+ if (_name != null)
+ {
+ NpcPersonalAIData.getInstance().initializeNpcParameters(mob, this, _name);
+ }
+
return initializeNpcInstance(mob);
}
catch (Exception e)
@@ -597,6 +625,11 @@
mob.setCurrentHpMp(mob.getMaxHp(), mob.getMaxMp());
// Set default value
mob.setScriptValue(0);
+ // Clear script variables
+ if (mob.hasVariables())
+ {
+ mob.getVariables().getSet().clear();
+ }
// Set is not random walk default value
mob.setIsNoRndWalk(isNoRndWalk());
Index: java/com/l2jserver/gameserver/model/variables/NpcVariables.java
===================================================================
--- java/com/l2jserver/gameserver/model/variables/NpcVariables.java (revision 0)
+++ java/com/l2jserver/gameserver/model/variables/NpcVariables.java (working copy)
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2004-2013 L2J DataPack
+ *
+ * This file is part of L2J DataPack.
+ *
+ * L2J DataPack 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 DataPack 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.variables;
+
+/**
+ * @author GKR
+ */
+public class NpcVariables extends AbstractVariables
+{
+ public NpcVariables()
+ {
+ }
+
+ @Override
+ public boolean restoreMe()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean storeMe()
+ {
+ return true;
+ }
+}
\ No newline at end of file
Index: dist/game/data/xsd/spawnlist.xsd
===================================================================
--- dist/game/data/xsd/spawnlist.xsd (revision 10037)
+++ dist/game/data/xsd/spawnlist.xsd (working copy)
@@ -6,6 +6,15 @@
<xs:element name="spawn" minOccurs="1" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="unbounded">
+ <xs:element name="AIData" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:sequence minOccurs="1" maxOccurs="1">
+ <xs:element type="xs:nonNegativeInteger" name="aggroRange" minOccurs="0" maxOccurs="1" />
+ <xs:element type="xs:boolean" name="disableRandomAnimation" minOccurs="0" maxOccurs="1" />
+ <xs:element type="xs:boolean" name="disableRandomWalk" minOccurs="0" maxOccurs="1" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
<xs:element name="npc" minOccurs="1" maxOccurs="unbounded">
<xs:complexType>
<xs:simpleContent>
@@ -31,7 +40,8 @@
</xs:complexType>
</xs:element>
</xs:sequence>
- <xs:attribute type="xs:string" name="zone" use="optional"/>
+ <xs:attribute type="xs:string" name="zone" use="optional" />
+ <xs:attribute type="xs:string" name="name" use="optional" />
</xs:complexType>
</xs:element>
</xs:sequence>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment