Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ### Eclipse Workspace Patch 1.0
- #P Lisvus_GameServer
- Index: java/net/sf/l2j/gameserver/script/faenor/FaenorInterface.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/script/faenor/FaenorInterface.java (revision 355)
- +++ java/net/sf/l2j/gameserver/script/faenor/FaenorInterface.java (working copy)
- @@ -26,6 +26,7 @@
- import net.sf.l2j.gameserver.Announcements;
- import net.sf.l2j.gameserver.EventDroplist;
- import net.sf.l2j.gameserver.model.L2DropData;
- +import net.sf.l2j.gameserver.model.L2DropCategory;
- import net.sf.l2j.gameserver.model.L2PetData;
- import net.sf.l2j.gameserver.script.DateRange;
- import net.sf.l2j.gameserver.script.EngineInterface;
- @@ -87,7 +88,7 @@
- drop.setChance(chance);
- drop.setQuestID(questID);
- drop.addStates(states);
- - npc.addDropData(drop);
- + addDrop(npc, drop, false);
- }
- /**
- @@ -108,11 +109,52 @@
- drop.setItemId(itemID);
- drop.setMinDrop(min);
- drop.setMaxDrop(max);
- - drop.setSweep(sweep);
- drop.setChance(chance);
- - npc.addDropData(drop);
- +
- + addDrop(npc, drop, sweep);
- }
- +
- + /**
- + * Adds a new drop to an NPC. If the drop is sweep, it adds it to the NPC's Sweep category
- + * If the drop is non-sweep, it creates a new category for this drop.
- + *
- + * @param npc
- + * @param drop
- + * @param sweep
- + */
- + public void addDrop(L2NpcTemplate npc, L2DropData drop, boolean sweep)
- + {
- + if(sweep)
- + addDrop(npc, drop,-1);
- + else
- + {
- + int maxCategory = -1;
- + for(L2DropCategory cat:npc.getDropData())
- + {
- + if(maxCategory<cat.getCategoryType())
- + maxCategory = cat.getCategoryType();
- + }
- + maxCategory++;
- + npc.addDropData(drop, maxCategory);
- + }
- +
- + }
- +
- + /**
- + * Adds a new drop to an NPC, in the specified category. If the category does not exist,
- + * it is created.
- + *
- + * @param npc
- + * @param drop
- + * @param sweep
- + */
- + public void addDrop(L2NpcTemplate npc, L2DropData drop, int category)
- + {
- + npc.addDropData(drop, category);
- + }
- +
- +
- /**
- * @return Returns the _questDrops.
- */
- @@ -124,7 +166,8 @@
- return null;
- }
- List<L2DropData> questDrops = new FastList<L2DropData>();
- - for (L2DropData drop : npc.getDropData())
- + for (L2DropCategory cat:npc.getDropData())
- + for (L2DropData drop : cat.getAllDrops() )
- {
- if (drop.getQuestID() != null)
- {
- Index: java/net/sf/l2j/gameserver/model/L2DropData.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/model/L2DropData.java (revision 355)
- +++ java/net/sf/l2j/gameserver/model/L2DropData.java (working copy)
- @@ -35,7 +35,6 @@
- private int _itemId;
- private int _mindrop;
- private int _maxdrop;
- - private boolean _sweep;
- private int _chance;
- private String _questID = null;
- private String[] _stateID = null;
- @@ -77,15 +76,6 @@
- }
- /**
- - * Returns if item dropped come from sweep
- - * @return boolean
- - */
- - public boolean isSweep()
- - {
- - return _sweep;
- - }
- -
- - /**
- * Returns the chance of having a drop
- * @return int
- */
- @@ -113,15 +103,6 @@
- }
- /**
- - * Sets if the item come from sweep
- - * @param sweep
- - */
- - public void setSweep(boolean sweep)
- - {
- - _sweep = sweep;
- - }
- -
- - /**
- * Sets the chance of having the item for a drop
- * @param chance : int designating the chance
- */
- Index: java/net/sf/l2j/gameserver/templates/L2NpcTemplate.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/templates/L2NpcTemplate.java (revision 355)
- +++ java/net/sf/l2j/gameserver/templates/L2NpcTemplate.java (working copy)
- @@ -24,6 +24,7 @@
- import javolution.util.FastList;
- import javolution.util.FastMap;
- +import net.sf.l2j.gameserver.model.L2DropCategory;
- import net.sf.l2j.gameserver.model.L2DropData;
- import net.sf.l2j.gameserver.model.L2MinionData;
- import net.sf.l2j.gameserver.model.L2Skill;
- @@ -32,309 +33,315 @@
- import net.sf.l2j.gameserver.skills.Stats;
- /**
- - * This cl contains all generic data of a L2Spawn object.<BR><BR>
- - *
- - * <B><U> Data</U> :</B><BR><BR>
- - * <li>npcId, type, name, sex</li>
- - * <li>rewardExp, rewardSp</li>
- - * <li>aggroRange, factionId, factionRange</li>
- - * <li>rhand, lhand, armor</li>
- - * <li>isUndead</li>
- - * <li>_drops</li>
- - * <li>_minions</li>
- - * <li>_teachInfo</li>
- - * <li>_skills</li>
- - * <li>_questsStart</li><BR><BR>
- - *
- + * This cl contains all generic data of a L2Spawn object.<BR>
- + * <BR>
- + * <B><U> Data</U> :</B><BR>
- + * <BR>
- + * <li>npcId, type, name, sex</li> <li>rewardExp, rewardSp</li> <li>aggroRange, factionId, factionRange</li> <li>rhand, lhand, armor</li> <li>isUndead</li> <li>_drops</li> <li>_minions</li> <li>_teachInfo</li> <li>_skills</li> <li>_questsStart</li><BR>
- + * <BR>
- * @version $Revision: 1.1.2.4 $ $Date: 2005/04/02 15:57:51 $
- */
- public final class L2NpcTemplate extends L2CharTemplate
- {
- - protected static Logger _log = Logger.getLogger(Quest.class.getName());
- -
- - public final int npcId;
- - public final int idTemplate;
- - public final String type;
- - public final String name;
- - public final boolean serverSideName;
- - public final String title;
- - public final boolean serverSideTitle;
- - public final String sex;
- - public final int level;
- - public final int rewardExp;
- - public final int rewardSp;
- - public final int aggroRange;
- - public final int rhand;
- - public final int lhand;
- - public final int armor;
- - public final String factionId;
- - public final int factionRange;
- - public final int absorb_level;
- -
- + protected static Logger _log = Logger.getLogger(Quest.class.getName());
- +
- + public final int npcId;
- + public final int idTemplate;
- + public final String type;
- + public final String name;
- + public final boolean serverSideName;
- + public final String title;
- + public final boolean serverSideTitle;
- + public final String sex;
- + public final int level;
- + public final int rewardExp;
- + public final int rewardSp;
- + public final int aggroRange;
- + public final int rhand;
- + public final int lhand;
- + public final int armor;
- + public final String factionId;
- + public final int factionRange;
- + public final int absorb_level;
- +
- private final StatsSet npcStatsSet;
- -
- - /** fixed skills*/
- - public int race;
- -
- - /** The table containing all Item that can be dropped by L2NpcInstance using this L2NpcTemplate*/
- - private final List<L2DropData> _drops = new FastList<L2DropData>(0);
- - private final List<L2DropData> _fulldrops = new FastList<L2DropData>(0);
- - private final List<L2DropData> _miscdrops = new FastList<L2DropData>(0);
- -
- - /** The table containing all Minions that must be spawn with the L2NpcInstance using this L2NpcTemplate*/
- - private final List<L2MinionData> _minions = new FastList<L2MinionData>(0);
- -
- - private List<ClassId> _teachInfo;
- +
- + /** fixed skills */
- + public int race;
- +
- + /** The table containing all Item that can be dropped by L2NpcInstance using this L2NpcTemplate */
- + private final FastList<L2DropCategory> _categories = new FastList<L2DropCategory>();
- +
- + /** The table containing all Minions that must be spawn with the L2NpcInstance using this L2NpcTemplate */
- + private final List<L2MinionData> _minions = new FastList<L2MinionData>(0);
- +
- + private List<ClassId> _teachInfo;
- private Map<Integer, L2Skill> _skills;
- private Map<Stats, Double> _vulnerabilities;
- // contains a list of quests for each event type (questStart, questAttack, questKill, etc)
- - private Map<Quest.QuestEventType, Quest[]> _questEvents;
- -
- + private Map<Quest.QuestEventType, Quest[]> _questEvents;
- +
- /**
- - * Constructor of L2Character.<BR><BR>
- - *
- + * Constructor of L2Character.<BR>
- + * <BR>
- * @param set The StatsSet object to transfert data to the method
- - *
- */
- public L2NpcTemplate(StatsSet set)
- {
- super(set);
- - npcId = set.getInteger("npcId");
- - idTemplate = set.getInteger("idTemplate");
- - type = set.getString("type");
- - name = set.getString("name");
- - serverSideName = set.getBool("serverSideName");
- - title = set.getString("title");
- - serverSideTitle = set.getBool("serverSideTitle");
- - sex = set.getString("sex");
- - level = set.getInteger("level");
- + npcId = set.getInteger("npcId");
- + idTemplate = set.getInteger("idTemplate");
- + type = set.getString("type");
- + name = set.getString("name");
- + serverSideName = set.getBool("serverSideName");
- + title = set.getString("title");
- + serverSideTitle = set.getBool("serverSideTitle");
- + sex = set.getString("sex");
- + level = set.getInteger("level");
- rewardExp = set.getInteger("rewardExp");
- - rewardSp = set.getInteger("rewardSp");
- - aggroRange= set.getInteger("aggroRange");
- - rhand = set.getInteger("rhand");
- - lhand = set.getInteger("lhand");
- - armor = set.getInteger("armor");
- - String f = set.getString("factionId", null);
- + rewardSp = set.getInteger("rewardSp");
- + aggroRange = set.getInteger("aggroRange");
- + rhand = set.getInteger("rhand");
- + lhand = set.getInteger("lhand");
- + armor = set.getInteger("armor");
- + String f = set.getString("factionId", null);
- if (f == null)
- + {
- factionId = null;
- + }
- else
- + {
- factionId = f.intern();
- - factionRange = set.getInteger("factionRange");
- - absorb_level = set.getInteger("absorb_level", 0);
- - //String r = set.getString("race", null);
- - //if (r == null)
- - // race = null;
- - //else
- - // race = r.intern();
- + }
- + factionRange = set.getInteger("factionRange");
- + absorb_level = set.getInteger("absorb_level", 0);
- + // String r = set.getString("race", null);
- + // if (r == null)
- + // race = null;
- + // else
- + // race = r.intern();
- race = 0;
- npcStatsSet = set;
- _teachInfo = null;
- }
- -
- - public void addTeachInfo(ClassId classId)
- +
- + public void addTeachInfo(ClassId classId)
- {
- if (_teachInfo == null)
- + {
- _teachInfo = new FastList<ClassId>();
- + }
- _teachInfo.add(classId);
- }
- -
- +
- public ClassId[] getTeachInfo()
- {
- if (_teachInfo == null)
- + {
- return null;
- + }
- return _teachInfo.toArray(new ClassId[_teachInfo.size()]);
- }
- -
- +
- public boolean canTeach(ClassId classId)
- {
- if (_teachInfo == null)
- + {
- return false;
- -
- - // If the player is on a third class, fetch the class teacher
- - // information for its parent class.
- - if (classId.getId() >= 88)
- - return _teachInfo.contains(classId.getParent());
- -
- + }
- +
- + // If the player is on a third class, fetch the class teacher
- + // information for its parent class.
- + if (classId.getId() >= 88)
- + {
- + return _teachInfo.contains(classId.getParent());
- + }
- +
- return _teachInfo.contains(classId);
- }
- -
- - // for all spoil, plus adena, sealstone, quest, Raidboss, and all other drops that
- - // should NOT follow the rule of 1 drop per category per kill
- - public void addDropData(L2DropData drop)
- +
- + // add a drop to a given category. If the category does not exist, create it.
- + public void addDropData(L2DropData drop, int categoryType)
- {
- - if (drop.isQuestDrop()) {
- -// if (_questDrops == null)
- -// _questDrops = new FastList<L2DropData>(0);
- -// _questDrops.add(drop);
- - } else {
- - _drops.add(drop);
- - }
- + if (drop.isQuestDrop())
- + {
- + // if (_questDrops == null)
- + // _questDrops = new FastList<L2DropData>(0);
- + // _questDrops.add(drop);
- + }
- + else
- + {
- + // if the category doesn't already exist, create it first
- + synchronized (_categories)
- + {
- + boolean catExists = false;
- + for (L2DropCategory cat : _categories)
- + {
- + // if the category exists, add the drop to this category.
- + if (cat.getCategoryType() == categoryType)
- + {
- + cat.addDropData(drop);
- + catExists = true;
- + break;
- + }
- + }
- + // if the category doesn't exit, create it and add the drop
- + if (!catExists)
- + {
- + L2DropCategory cat = new L2DropCategory(categoryType);
- + cat.addDropData(drop);
- + _categories.add(cat);
- + }
- + }
- + }
- }
- -
- - // for the first category: Full drops and parts (like fabrics, patterns, edges, etc)
- - // only ONE of all items in this catgory should be dropped per kill .
- - public void addFullDropData(L2DropData drop)
- +
- + public void addRaidData(L2MinionData minion)
- {
- - if (drop.isQuestDrop()) {
- -// should never happen here
- - } else {
- - _fulldrops.add(drop);
- - }
- + _minions.add(minion);
- }
- -
- - // for the second category: mats, potions, scrolls, and other miscellaneous items.
- - // basically, for everything not included in the other two categories.
- - // only ONE of all items in this catgory should be dropped per kill .
- - public void addMiscDropData(L2DropData drop)
- +
- + public void addSkill(L2Skill skill)
- {
- - if (drop.isQuestDrop()) {
- -// should never happen here
- - } else {
- - _miscdrops.add(drop);
- - }
- + if (_skills == null)
- + {
- + _skills = new FastMap<Integer, L2Skill>();
- + }
- + _skills.put(skill.getId(), skill);
- }
- -
- - public void addRaidData(L2MinionData minion)
- - {
- - _minions.add(minion);
- - }
- -
- - public void addSkill(L2Skill skill)
- +
- + public void addVulnerability(Stats id, double vuln)
- {
- - if (_skills == null)
- - _skills = new FastMap<Integer, L2Skill>();
- - _skills.put(skill.getId(), skill);
- + if (_vulnerabilities == null)
- + {
- + _vulnerabilities = new FastMap<Stats, Double>();
- + }
- + _vulnerabilities.put(id, new Double(vuln));
- }
- -
- - public void addVulnerability(Stats id, double vuln)
- +
- + public double getVulnerability(Stats id)
- {
- - if (_vulnerabilities == null)
- - _vulnerabilities = new FastMap<Stats, Double>();
- - _vulnerabilities.put(id, new Double(vuln));
- + if ((_vulnerabilities == null) || (_vulnerabilities.get(id) == null))
- + {
- + return 1;
- + }
- + return _vulnerabilities.get(id);
- }
- -
- - public double getVulnerability(Stats id)
- +
- + public double removeVulnerability(Stats id)
- {
- - if (_vulnerabilities == null || _vulnerabilities.get(id) == null)
- - return 1;
- - return _vulnerabilities.get(id);
- + return _vulnerabilities.remove(id);
- }
- -
- - public double removeVulnerability(Stats id)
- +
- + public FastList<L2DropCategory> getDropData()
- {
- - return _vulnerabilities.remove(id);
- + return _categories;
- }
- -
- +
- /**
- - * Return the list of all possible UNCATEGORIZED drops of this L2NpcTemplate.<BR><BR>
- + * Return the list of all possible item drops of this L2NpcTemplate.<BR>
- + * (ie full drops and part drops, mats, miscellaneous & UNCATEGORIZED)<BR>
- + * <BR>
- */
- - public List<L2DropData> getDropData()
- + public List<L2DropData> getAllDropData()
- {
- - return _drops;
- + List<L2DropData> lst = new FastList<L2DropData>();
- + for (L2DropCategory tmp : _categories)
- + {
- + lst.addAll(tmp.getAllDrops());
- + }
- + return lst;
- }
- -
- +
- /**
- - * Return the list of all possible full drops and part drops of this L2NpcTemplate.<BR><BR>
- + * Empty all possible drops of this L2NpcTemplate.<BR>
- + * <BR>
- */
- - public List<L2DropData> getFullDropData()
- + public synchronized void clearAllDropData()
- {
- - return _fulldrops;
- + while (_categories.size() > 0)
- + {
- + _categories.getFirst().clearAllDrops();
- + _categories.removeFirst();
- + }
- + _categories.clear();
- }
- -
- +
- /**
- - * Return the list of all possible mat & miscellaneous item drops of this L2NpcTemplate.<BR><BR>
- + * Return the list of all Minions that must be spawn with the L2NpcInstance using this L2NpcTemplate.<BR>
- + * <BR>
- */
- - public List<L2DropData> getMiscDropData()
- - {
- - return _miscdrops;
- - }
- -
- - /**
- - * Return the list of all possible item drops of this L2NpcTemplate.<BR>
- - * (ie full drops and part drops, mats, miscellaneous & UNCATEGORIZED)<BR><BR>
- - */
- - public List<L2DropData> getAllDropData()
- - {
- - List<L2DropData> lst = new FastList<L2DropData>();
- - lst.addAll(_drops);
- - lst.addAll(_fulldrops);
- - lst.addAll(_miscdrops);
- - return lst;
- - }
- -
- - /**
- - * Empty all possible drops of this L2NpcTemplate.<BR><BR>
- - */
- - public void clearAllDropData()
- - {
- - _drops.clear();
- - _fulldrops.clear();
- - _miscdrops.clear();
- - }
- -
- - /**
- - * Return the list of all Minions that must be spawn with the L2NpcInstance using this L2NpcTemplate.<BR><BR>
- - */
- public List<L2MinionData> getMinionData()
- {
- - return _minions;
- + return _minions;
- }
- -
- - public Map<Integer, L2Skill> getSkills()
- +
- + public Map<Integer, L2Skill> getSkills()
- {
- - return _skills;
- + return _skills;
- }
- -
- +
- public void addQuestEvent(Quest.QuestEventType EventType, Quest q)
- - {
- - if (_questEvents == null)
- - _questEvents = new FastMap<Quest.QuestEventType, Quest[]>();
- -
- - if (_questEvents.get(EventType) == null)
- - _questEvents.put(EventType, new Quest[]{q});
- - else
- - {
- - Quest[] _quests = _questEvents.get(EventType);
- - int len = _quests.length;
- -
- - // if only one registration per npc is allowed for this event type
- - // then only register this NPC if not already registered for the specified event.
- - // if a quest allows multiple registrations, then register regardless of count
- - if (EventType.isMultipleRegistrationAllowed() || (len < 1))
- - {
- - Quest[] tmp = new Quest[len+1];
- - for (int i=0; i < len; i++)
- - {
- - if (_quests[i].getName().equals(q.getName()))
- - {
- - _quests[i] = q;
- - return;
- - }
- - tmp[i] = _quests[i];
- - }
- - tmp[len] = q;
- - _questEvents.put(EventType, tmp);
- - }
- - else
- - _log.warning("Quest event not allowed in multiple quests. Skipped addition of Event Type \""+EventType+"\" for NPC \""+this.name +"\" and quest \""+q.getName()+"\".");
- - }
- + {
- + if (_questEvents == null)
- + {
- + _questEvents = new FastMap<Quest.QuestEventType, Quest[]>();
- + }
- +
- + if (_questEvents.get(EventType) == null)
- + {
- + _questEvents.put(EventType, new Quest[]
- + {
- + q
- + });
- + }
- + else
- + {
- + Quest[] _quests = _questEvents.get(EventType);
- + int len = _quests.length;
- +
- + // if only one registration per npc is allowed for this event type
- + // then only register this NPC if not already registered for the specified event.
- + // if a quest allows multiple registrations, then register regardless of count
- + if (EventType.isMultipleRegistrationAllowed() || (len < 1))
- + {
- + Quest[] tmp = new Quest[len + 1];
- + for (int i = 0; i < len; i++)
- + {
- + if (_quests[i].getName().equals(q.getName()))
- + {
- + _quests[i] = q;
- + return;
- + }
- + tmp[i] = _quests[i];
- + }
- + tmp[len] = q;
- + _questEvents.put(EventType, tmp);
- + }
- + else
- + {
- + _log.warning("Quest event not allowed in multiple quests. Skipped addition of Event Type \"" + EventType + "\" for NPC \"" + this.name + "\" and quest \"" + q.getName() + "\".");
- + }
- + }
- }
- -
- - public Quest[] getEventQuests(Quest.QuestEventType EventType)
- - {
- - if (_questEvents == null)
- - return null;
- -
- - return _questEvents.get(EventType);
- - }
- -
- +
- + public Quest[] getEventQuests(Quest.QuestEventType EventType)
- + {
- + if (_questEvents == null)
- + {
- + return null;
- + }
- +
- + return _questEvents.get(EventType);
- + }
- +
- public StatsSet getStatsSet()
- {
- - return npcStatsSet;
- + return npcStatsSet;
- }
- -
- +
- public void setRace(int newrace)
- {
- - race = newrace;
- + race = newrace;
- }
- }
- \ No newline at end of file
- Index: java/net/sf/l2j/gameserver/datatables/NpcTable.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/datatables/NpcTable.java (revision 355)
- +++ java/net/sf/l2j/gameserver/datatables/NpcTable.java (working copy)
- @@ -28,6 +28,7 @@
- import javolution.util.FastMap;
- import net.sf.l2j.Config;
- import net.sf.l2j.L2DatabaseFactory;
- +import net.sf.l2j.gameserver.model.L2DropCategory;
- import net.sf.l2j.gameserver.model.L2DropData;
- import net.sf.l2j.gameserver.model.L2MinionData;
- import net.sf.l2j.gameserver.model.L2Skill;
- @@ -135,7 +136,7 @@
- try
- {
- - PreparedStatement statement2 = con.prepareStatement("SELECT " + L2DatabaseFactory.getInstance().safetyString(new String[] {"mobId", "itemId", "min", "max", "sweep", "chance"}) + ", IFNULL(drop_category,1) AS drop_category FROM droplist LEFT JOIN etcitem ON itemId = item_id ORDER BY mobId, chance DESC");
- + PreparedStatement statement2 = con.prepareStatement("SELECT " + L2DatabaseFactory.getInstance().safetyString(new String[] {"mobId", "itemId", "min", "max", "category", "chance"}) + " FROM droplist ORDER BY mobId, chance DESC");
- ResultSet dropData = statement2.executeQuery();
- L2DropData dropDat = null;
- L2NpcTemplate npcDat = null;
- @@ -154,37 +155,11 @@
- dropDat.setItemId(dropData.getInt("itemId"));
- dropDat.setMinDrop(dropData.getInt("min"));
- dropDat.setMaxDrop(dropData.getInt("max"));
- - dropDat.setSweep(dropData.getInt("sweep") == 1);
- dropDat.setChance(dropData.getInt("chance"));
- - int category = dropData.getInt("drop_category");
- + int category = dropData.getInt("category");
- - // uncategorized drops: marked as uncategorized, or are sweep, or this is a raidboss
- - // uncategorized allows many drops to be given from the same list
- - // the following mobs are grandbosses. Grandbosses are L2Monster but should be treated like RBs.
- - // 12001 Queen Ant
- - // 12169 Orfen
- - // 12211 Antharas
- - // 12372 Baium
- - // 12374 Zaken
- - // 12899 Valakas
- - if ((category == 0)
- - || (dropDat.isSweep())
- - || (npcDat.type.compareToIgnoreCase("L2RaidBoss") == 0)
- - || (npcDat.type.compareToIgnoreCase("L2Boss") == 0)
- - )
- - {
- - npcDat.addDropData(dropDat);
- - }
- - // categorized as full drops and parts for armor/weapon/jewel
- - else
- - {
- - if (category == 1)
- - npcDat.addFullDropData(dropDat);
- - // other items (miscellaneous like scrolls, potions, mats, etc).
- - else
- - npcDat.addMiscDropData(dropDat);
- - }
- + npcDat.addDropData(dropDat, category);
- }
- dropData.close();
- @@ -350,10 +325,10 @@
- if (old.getSkills() != null)
- skills.putAll(old.getSkills());
- - List<L2DropData> drops = new FastList<L2DropData>();
- + FastList<L2DropCategory> categories = new FastList<L2DropCategory>();
- if (old.getDropData() != null)
- - drops.addAll(old.getDropData());
- + categories.addAll(old.getDropData());
- ClassId[] classIds = null;
- @@ -380,9 +355,6 @@
- for (L2Skill skill : skills.values())
- created.addSkill(skill);
- - for (L2DropData drop : drops)
- - created.addDropData(drop);
- -
- if (classIds != null)
- for (ClassId classId : classIds)
- created.addTeachInfo(classId);
- Index: java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditNpc.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditNpc.java (revision 355)
- +++ java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditNpc.java (working copy)
- @@ -35,6 +35,7 @@
- import net.sf.l2j.gameserver.datatables.NpcTable;
- import net.sf.l2j.gameserver.handler.IAdminCommandHandler;
- import net.sf.l2j.gameserver.model.L2DropData;
- +import net.sf.l2j.gameserver.model.L2DropCategory;
- import net.sf.l2j.gameserver.model.L2ItemInstance;
- import net.sf.l2j.gameserver.model.L2Object;
- import net.sf.l2j.gameserver.model.L2TradeList;
- @@ -839,13 +840,14 @@
- replyMSG.append("<table>");
- replyMSG.append("<tr><td>npc_id itemId</td><td>item[id]</td><td>type</td><td>del</td></tr>");
- - for(L2DropData drop : npcData.getAllDropData())
- - {
- - replyMSG.append("<tr><td><a action=\"bypass -h admin_edit_drop " + npcData.npcId + " " + drop.getItemId() + "\">"
- - + npcData.npcId + " " + drop.getItemId() + "</a></td>" +
- - "<td>" + ItemTable.getInstance().getTemplate(drop.getItemId()).getName() + "[" + drop.getItemId() + "]" + "</td><td>" + (drop.isQuestDrop()?"Q":(drop.isSweep()?"S":"D")) + "</td><td>" +
- - "<a action=\"bypass -h admin_del_drop " + npcData.npcId + " " + drop.getItemId() + "\">del</a></td></tr>");
- - }
- + for(L2DropCategory cat:npcData.getDropData())
- + for(L2DropData drop : cat.getAllDrops())
- + {
- + replyMSG.append("<tr><td><a action=\"bypass -h admin_edit_drop " + npcData.npcId + " " + drop.getItemId() + "\">"
- + + npcData.npcId + " " + drop.getItemId() + "</a></td>" +
- + "<td>" + ItemTable.getInstance().getTemplate(drop.getItemId()).getName() + "[" + drop.getItemId() + "]" + "</td><td>" + (drop.isQuestDrop()?"Q":(cat.isSweep()?"S":"D")) + "</td><td>" +
- + "<a action=\"bypass -h admin_del_drop " + npcData.npcId + " " + drop.getItemId() + "\">del</a></td></tr>");
- + }
- replyMSG.append("</table>");
- replyMSG.append("<center>");
- @@ -1102,28 +1104,10 @@
- dropData.setItemId(dropDataList.getInt("itemId"));
- dropData.setMinDrop(dropDataList.getInt("min"));
- dropData.setMaxDrop(dropDataList.getInt("max"));
- - dropData.setSweep(dropDataList.getInt("sweep") == 1);
- dropData.setChance(dropDataList.getInt("chance"));
- - int category = dropDataList.getInt("drop_category");
- -
- - if ((category == 0)
- - || (dropData.isSweep())
- - || (npcData.type.compareToIgnoreCase("L2RaidBoss") == 0)
- - || (npcData.type.compareToIgnoreCase("L2Boss") == 0)
- - )
- - {
- - npcData.addDropData(dropData);
- - }
- - // categorized as full drops and parts for armor/weapon/jewel
- - else
- - {
- - if (category == 1)
- - npcData.addFullDropData(dropData);
- - // other items (miscellaneous like scrolls, potions, mats, etc).
- - else
- - npcData.addMiscDropData(dropData);
- - }
- + int category = dropDataList.getInt("category");
- + npcData.addDropData(dropData, category);
- }
- dropDataList.close();
- statement.close();
- Index: java/net/sf/l2j/gameserver/model/L2DropCategory.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/model/L2DropCategory.java (revision 0)
- +++ java/net/sf/l2j/gameserver/model/L2DropCategory.java (revision 0)
- @@ -0,0 +1,170 @@
- +/*
- + * 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 net.sf.l2j.gameserver.model;
- +
- +import javolution.util.FastList;
- +import net.sf.l2j.Config;
- +import net.sf.l2j.gameserver.lib.Rnd;
- +
- +/**
- + * @author Fulminus
- + */
- +public class L2DropCategory
- +{
- + private final FastList<L2DropData> _drops;
- + private int _categoryChance; // a sum of chances for calculating if an item will be dropped from this category
- + private int _categoryBalancedChance; // sum for balancing drop selection inside categories in high rate servers
- + private final int _categoryType;
- +
- + public L2DropCategory(int categoryType)
- + {
- + _categoryType = categoryType;
- + _drops = new FastList<>(0);
- + _categoryChance = 0;
- + _categoryBalancedChance = 0;
- + }
- +
- + public void addDropData(L2DropData drop)
- + {
- + if (drop.isQuestDrop())
- + {
- + // if (_questDrops == null)
- + // _questDrops = new FastList<>(0);
- + // _questDrops.add(drop);
- + }
- + else
- + {
- +
- + _drops.add(drop);
- + _categoryChance += drop.getChance();
- + // for drop selection inside a category: max 100 % chance for getting an item, scaling all values to that.
- + _categoryBalancedChance += Math.min((drop.getChance() * (Config.RATE_DROP_ITEMS)), L2DropData.MAX_CHANCE);
- +
- + }
- + }
- +
- + public FastList<L2DropData> getAllDrops()
- + {
- + return _drops;
- + }
- +
- + public void clearAllDrops()
- + {
- + _drops.clear();
- + }
- +
- + public boolean isSweep()
- + {
- + return (getCategoryType() == -1);
- + }
- +
- + // this returns the chance for the category to be visited in order to check if
- + // drops might come from it. Category -1 (spoil) must always be visited
- + // (but may return 0 or many drops)
- + public int getCategoryChance()
- + {
- + if (getCategoryType() >= 0)
- + {
- + return _categoryChance;
- + }
- + return L2DropData.MAX_CHANCE;
- + }
- +
- + public int getCategoryBalancedChance()
- + {
- + if (getCategoryType() >= 0)
- + {
- + return _categoryBalancedChance;
- + }
- + return L2DropData.MAX_CHANCE;
- + }
- +
- + public int getCategoryType()
- + {
- + return _categoryType;
- + }
- +
- + /**
- + * Useful for seeded conditions... the category will attempt to drop only among items that are allowed to be dropped when a mob is seeded.<br>
- + * Previously, this only included Adena. According to sh1ny, seals tones are also acceptable drops.<br>
- + * If no acceptable drops are in the category, nothing will be dropped.<br>
- + * therwise, it will check for the item's chance to drop and either drop it or drop nothing.
- + * @return acceptable drop when mob is seeded, if it exists. Null otherwise.
- + */
- + public synchronized L2DropData dropSeedAllowedDropsOnly()
- + {
- + FastList<L2DropData> drops = new FastList<>();
- + int subCatChance = 0;
- + for (L2DropData drop : getAllDrops())
- + {
- + if ((drop.getItemId() == PcInventory.ADENA_ID))
- + {
- + drops.add(drop);
- + subCatChance += drop.getChance();
- + }
- + }
- +
- + // among the results choose one.
- + int randomIndex = Rnd.get(subCatChance);
- + int sum = 0;
- + for (L2DropData drop : drops)
- + {
- + sum += drop.getChance();
- +
- + if (sum > randomIndex) // drop this item and exit the function
- + {
- + drops.clear();
- + drops = null;
- + return drop;
- + }
- + }
- + // since it is still within category, only drop one of the acceptable drops from the results.
- + return null;
- + }
- +
- + /**
- + * One of the drops in this category is to be dropped now.<br>
- + * to see which one will be dropped, weight all items' chances such that their sum of chances equals MAX_CHANCE.<br>
- + * Since the individual drops have their base chance, we also ought to use the base category chance for the weight.<br>
- + * So weight = MAX_CHANCE/basecategoryDropChance.<br>
- + * Then get a single random number within this range. The first item (in order of the list) whose contribution to the sum makes the sum greater than the random number, will be dropped.<br>
- + * Edited: How _categoryBalancedChance works in high rate servers:<br>
- + * Let's say item1 has a drop chance (when considered alone, without category) of 1 % * RATE_DROP_ITEMS and item2 has 20 % * RATE_DROP_ITEMS, and the server's RATE_DROP_ITEMS is for example 50x.<br>
- + * Without this balancer, the relative chance inside the category to select item1 to be dropped would be 1/26 and item2 25/26, no matter what rates are used.<br>
- + * In high rate servers people usually consider the 1 % individual drop chance should become higher than this relative chance (1/26) inside the category, since having the both items for example in their own categories would result in having a drop chance for item1 50 % and item2 1000 %.<br>
- + * _categoryBalancedChance limits the individual chances to 100 % max, making the chance for item1 to be selected from this category 50/(50+100) = 1/3 and item2 100/150 = 2/3.<br>
- + * This change doesn't affect calculation when drop_chance * RATE_DROP_ITEMS < 100%, meaning there are no big changes for low rate servers and no changes at all for 1x servers.
- + * @return selected drop from category, or null if nothing is dropped.
- + */
- + public synchronized L2DropData dropOne()
- + {
- + int randomIndex = Rnd.get(getCategoryBalancedChance());
- + int sum = 0;
- + for (L2DropData drop : getAllDrops())
- + {
- + sum += Math.min((drop.getChance() * (Config.RATE_DROP_ITEMS)), L2DropData.MAX_CHANCE);
- +
- + if (sum >= randomIndex)
- + {
- + return drop;
- + }
- + }
- + return null;
- + }
- +}
- Index: java/net/sf/l2j/gameserver/model/L2Attackable.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/model/L2Attackable.java (revision 355)
- +++ java/net/sf/l2j/gameserver/model/L2Attackable.java (working copy)
- @@ -58,2059 +58,2434 @@
- import net.sf.l2j.gameserver.util.Util;
- /**
- - * This class manages all NPC that can be attacked.<BR><BR>
- - *
- - * L2Attackable :<BR><BR>
- - * <li>L2ArtefactInstance</li>
- - * <li>L2FriendlyMobInstance</li>
- - * <li>L2MonsterInstance</li>
- - * <li>L2SiegeGuardInstance </li>
- - *
- + * This class manages all NPC that can be attacked.<BR>
- + * <BR>
- + * L2Attackable :<BR>
- + * <BR>
- + * <li>L2ArtefactInstance</li> <li>L2FriendlyMobInstance</li> <li>L2MonsterInstance</li> <li>L2SiegeGuardInstance</li>
- * @version $Revision: 1.24.2.3.2.16 $ $Date: 2005/04/11 19:11:21 $
- */
- public class L2Attackable extends L2NpcInstance
- {
- - //protected static Logger _log = Logger.getLogger(L2Attackable.class.getName());
- -
- - /**
- - * This class contains all AggroInfo of the L2Attackable against the attacker L2Character.<BR><BR>
- - *
- - * <B><U> Data</U> :</B><BR><BR>
- - * <li>attacker : The attaker L2Character concerned by this AggroInfo of this L2Attackable </li>
- - * <li>hate : Hate level of this L2Attackable against the attaker L2Character (hate = damage) </li>
- - * <li>damage : Number of damages that the attaker L2Character gave to this L2Attackable </li><BR><BR>
- - *
- - */
- - public final class AggroInfo
- - {
- - /** The attaker L2Character concerned by this AggroInfo of this L2Attackable */
- - L2Character attacker;
- -
- - /** Hate level of this L2Attackable against the attaker L2Character (hate = damage) */
- - int hate;
- -
- - /** Number of damages that the attaker L2Character gave to this L2Attackable */
- - int damage;
- -
- -
- - /**
- - * Constructor of AggroInfo.<BR><BR>
- - */
- - AggroInfo(L2Character pAttacker)
- - {
- - attacker = pAttacker;
- - }
- -
- - /**
- - * Verify is object is equal to this AggroInfo.<BR><BR>
- - */
- - public boolean equals(Object obj)
- - {
- - if (this == obj)
- - return true;
- -
- - if (obj instanceof AggroInfo)
- - return (((AggroInfo)obj).attacker == attacker);
- -
- - return false;
- - }
- -
- - /**
- - * Return the Identifier of the attaker L2Character.<BR><BR>
- - */
- - public int hashCode()
- - {
- - return attacker.getObjectId();
- - }
- -
- - }
- -
- -
- - /**
- - * This class contains all RewardInfo of the L2Attackable against the any attacker L2Character, based on amount of damage done.<BR><BR>
- - *
- - * <B><U> Data</U> :</B><BR><BR>
- - * <li>attacker : The attaker L2Character concerned by this RewardInfo of this L2Attackable </li>
- - * <li>dmg : Total amount of damage done by the attacker to this L2Attackable (summon + own) </li>
- - *
- - */
- - protected final class RewardInfo
- - {
- - protected L2Character attacker;
- - protected int dmg = 0;
- -
- - public RewardInfo(L2Character pAttacker, int pDmg)
- - {
- - attacker = pAttacker;
- - dmg = pDmg;
- - }
- -
- - public void addDamage(int pDmg)
- - {
- - dmg += pDmg;
- - }
- -
- - public boolean equals(Object obj)
- - {
- - if (this == obj)
- - return true;
- -
- - if (obj instanceof RewardInfo)
- - return (((RewardInfo)obj).attacker == attacker);
- -
- - return false;
- - }
- -
- - public int hashCode()
- - {
- - return attacker.getObjectId();
- - }
- - }
- -
- - /**
- - * This class contains all AbsorberInfo of the L2Attackable against the absorber L2Character.<BR><BR>
- - *
- - * <B><U> Data</U> :</B><BR><BR>
- - * <li>absorber : The attaker L2Character concerned by this AbsorberInfo of this L2Attackable </li>
- - *
- - */
- - public final class AbsorberInfo
- - {
- - /** The attaker L2Character concerned by this AbsorberInfo of this L2Attackable */
- - L2PcInstance absorber;
- - int crystalId;
- - double absorbedHP;
- -
- - /**
- - * Constructor of AbsorberInfo.<BR><BR>
- - */
- - AbsorberInfo(L2PcInstance attacker, int pCrystalId, double pAbsorbedHP)
- - {
- - absorber = attacker;
- - crystalId = pCrystalId;
- - absorbedHP = pAbsorbedHP;
- - }
- -
- - /**
- - * Verify is object is equal to this AbsorberInfo.<BR><BR>
- - */
- - public boolean equals(Object obj)
- - {
- - if (this == obj)
- - return true;
- -
- - if (obj instanceof AbsorberInfo)
- - return (((AbsorberInfo)obj).absorber == absorber);
- -
- - return false;
- - }
- -
- - /**
- - * Return the Identifier of the absorber L2Character.<BR><BR>
- - */
- - public int hashCode()
- - {
- - return absorber.getObjectId();
- - }
- - }
- -
- - /**
- - * This class is used to create item reward lists instead of creating item instances.<BR><BR>
- - */
- - public final class RewardItem
- - {
- - protected int _itemId;
- - protected int _count;
- -
- - public RewardItem(int itemId, int count)
- - {
- - _itemId = itemId;
- - _count = count;
- - }
- -
- - public int getItemId() { return _itemId;}
- - public int getCount() { return _count;}
- - }
- -
- - private FastMap<L2Character, AggroInfo> _aggroList = new FastMap<L2Character, AggroInfo>().setShared(true);
- -
- - /** Use this to Remove Object from this Map
- - * This Should be Synchronized While Iterating over This Map - if u cant iterating and removing object at once
- - */
- - public final FastMap<L2Character, AggroInfo> getAggroList()
- - {
- - return _aggroList;
- - }
- -
- - private boolean _isReturningToSpawnPoint = false;
- -
- - public final boolean isReturningToSpawnPoint()
- - {
- - return _isReturningToSpawnPoint;
- - }
- -
- - public final void setIsReturningToSpawnPoint(boolean value)
- - {
- - _isReturningToSpawnPoint = value;
- - }
- -
- - /** Table containing all Items that a Dwarf can Sweep on this L2Attackable */
- - private RewardItem[] _sweepItems;
- -
- - /** crops */
- - private RewardItem[] _harvestItems;
- - private boolean _seeded;
- - private int _seedType = 0;
- - private L2PcInstance _seeder = null;
- -
- - /** True if an over-hit enabled skill has successfully landed on the L2Attackable */
- - private boolean _overhit;
- -
- - /** Stores the extra (over-hit) damage done to the L2Attackable when the attacker uses an over-hit enabled skill */
- - private double _overhitDamage;
- -
- - /** Stores the attacker who used the over-hit enabled skill on the L2Attackable */
- - private L2Character _overhitAttacker;
- -
- - /** True if a Soul Crystal was successfuly used on the L2Attackable */
- - private boolean _absorbed;
- -
- - /** The table containing all L2PcInstance that successfuly absorbed the soul of this L2Attackable */
- - private FastMap<L2PcInstance, AbsorberInfo> _absorbersList = new FastMap<L2PcInstance, AbsorberInfo>().setShared(true);
- -
- - /** Have this L2Attackable to drop somethink on DIE? **/
- - private boolean _haveToDrop;
- -
- - /** Have this L2Attackable to reward Exp and SP on Die? **/
- - private boolean _mustGiveExpSp;
- -
- - /**
- - * Constructor of L2Attackable (use L2Character and L2NpcInstance constructor).<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Call the L2Character constructor to set the _template of the L2Attackable (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR) </li>
- - * <li>Set the name of the L2Attackable</li>
- - * <li>Create a RandomAnimation Task that will be launched after the calculated delay if the server allow it </li><BR><BR>
- - *
- - * @param objectId Identifier of the object to initialized
- - * @param L2NpcTemplate Template to apply to the NPC
- - */
- - public L2Attackable(int objectId, L2NpcTemplate template)
- - {
- - super(objectId, template);
- - getKnownList(); // init knownlist
- - _haveToDrop = true;
- - _mustGiveExpSp = true;
- - }
- -
- - public AttackableKnownList getKnownList()
- - {
- - if (super.getKnownList() == null || !(super.getKnownList() instanceof AttackableKnownList))
- - setKnownList(new AttackableKnownList(this));
- - return (AttackableKnownList)super.getKnownList();
- - }
- -
- - /**
- - * Return the L2Character AI of the L2Attackable and if its null create a new one.<BR><BR>
- - */
- - public L2CharacterAI getAI()
- - {
- - if (_ai == null)
- - {
- - synchronized(this)
- - {
- - if (_ai == null)
- - _ai = new L2AttackableAI(new AIAccessor());
- - }
- - }
- - return _ai;
- - }
- -
- - // get condition to hate, actually isAggressive() is checked
- - // by monster and karma by guards in motheds that overwrite this one.
- - /**
- - * Not used.<BR><BR>
- - *
- - * @deprecated
- - *
- - */
- - @Deprecated
- - public boolean getCondition2(L2Character target)
- - {
- - if (target instanceof L2FolkInstance || target instanceof L2DoorInstance)
- - return false;
- -
- - if (target.isAlikeDead()
- - || !isInsideRadius(target, getAggroRange(), false, false)
- - || Math.abs(getZ()-target.getZ()) > 100
- -)
- - return false;
- -
- - return !target.isInvul();
- - }
- -
- - /**
- - * Reduce the current HP of the L2Attackable.<BR><BR>
- - *
- - * @param damage The HP decrease value
- - * @param attacker The L2Character who attacks
- - *
- - */
- - public void reduceCurrentHp(double damage, L2Character attacker)
- - {
- - reduceCurrentHp(damage, attacker, true);
- - }
- -
- - /**
- - * Reduce the current HP of the L2Attackable, update its _aggroList and launch the doDie Task if necessary.<BR><BR>
- - *
- - * @param i The HP decrease value
- - * @param attacker The L2Character who attacks
- - * @param awake The awake state (If True : stop sleeping)
- - *
- - */
- - public void reduceCurrentHp(double damage, L2Character attacker, boolean awake)
- - {
- -
- - if (isEventMob)
- - return;
- -
- -
- - // Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList
- - if (attacker != null)
- - {
- - addDamage(attacker, (int)damage);
- - addBufferHate();
- - }
- -
- - // If this L2Attackable is a L2MonsterInstance and it has spawned minions, call its minions to battle
- - if (this instanceof L2MonsterInstance)
- - {
- - L2MonsterInstance master = (L2MonsterInstance) this;
- - if (this instanceof L2MinionInstance)
- - {
- - master = ((L2MinionInstance)this).getLeader();
- - if (!master.isInCombat()&&!master.isDead())
- - {
- - master.addDamage(attacker, 1);
- - master.addBufferHate();
- - }
- - }
- - if (master.hasMinions())
- - master.callMinionsToAssist(attacker);
- - }
- -
- - // Reduce the current HP of the L2Attackable and launch the doDie Task if necessary
- - super.reduceCurrentHp(damage, attacker, awake);
- - }
- -
- - public synchronized void setHaveToDrop(boolean value)
- - {
- - _haveToDrop = value;
- - }
- -
- - public synchronized boolean getHaveToDrop()
- - {
- - return _haveToDrop;
- - }
- -
- - public synchronized void setMustRewardExpSp(boolean value)
- - {
- - _mustGiveExpSp = value;
- - }
- -
- - public synchronized boolean getMustRewardExpSP()
- - {
- - return _mustGiveExpSp;
- - }
- -
- - /**
- - * Kill the L2Attackable (the corpse disappeared after 7 seconds), distribute rewards (EXP, SP, Drops...) and notify Quest Engine.<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members </li>
- - * <li>Notify the Quest Engine of the L2Attackable death if necessary</li>
- - * <li>Kill the L2NpcInstance (the corpse disappeared after 7 seconds) </li><BR><BR>
- - *
- - * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T GIVE rewards to L2PetInstance</B></FONT><BR><BR>
- - *
- - * @param killer The L2Character that has killed the L2Attackable
- - *
- - */
- - public boolean doDie(L2Character killer)
- - {
- - // Kill the L2NpcInstance (the corpse disappeared after 7 seconds)
- - if (!super.doDie(killer))
- - return false;
- -
- - // Enhance soul crystals of the attacker if this L2Attackable had its soul absorbed
- - try
- - {
- -
- -
- - levelSoulCrystals(killer);
- -
- - }
- - catch (Exception e) { _log.log(Level.SEVERE, "", e); }
- -
- - // Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members
- -
- - if (getHaveToDrop() || getMustRewardExpSP())
- - {
- - try
- - {
- - calculateRewards(killer);
- - }
- - catch (Exception e)
- - {
- - _log.log(Level.SEVERE, "", e);
- - }
- - }
- -
- - // Notify the Quest Engine of the L2Attackable death if necessary
- - try
- - {
- - L2PcInstance player = null;
- - if (killer instanceof L2PcInstance)
- - player = (L2PcInstance)killer;
- - else if (killer instanceof L2Summon)
- - player = ((L2Summon)killer).getOwner();
- -
- - if (player != null)
- - {
- - List<QuestState> questList = new FastList<QuestState>();
- -
- - if (player.getParty() != null)
- - {
- - Map<String, List<QuestState>> tempMap = new FastMap<String, List<QuestState>>();
- -
- - for (L2PcInstance pl : player.getParty().getPartyMembers())
- - {
- - if (pl.getQuestsForKills(this) == null)
- - continue;
- -
- - for (QuestState qs : pl.getQuestsForKills(this))
- - {
- - if (qs.getState().isParty())
- - {
- - if (!qs.isCompleted() && !pl.isDead() && Util.checkIfInRange(1400, this, pl, true))
- - {
- - if (this instanceof L2RaidBossInstance)
- - questList.add(qs);
- - else
- - {
- - if (tempMap.get(qs.getQuest().getName()) != null)
- - tempMap.get(qs.getQuest().getName()).add(qs);
- - else
- - {
- - List<QuestState> tempList = new FastList<QuestState>();
- - tempList.add(qs);
- - tempMap.put(qs.getQuest().getName(), tempList);
- - }
- - }
- - }
- - }
- - else if (pl == player)
- - questList.add(qs);
- - }
- - }
- -
- - for (List<QuestState> list : tempMap.values())
- - questList.add((QuestState)list.toArray()[Rnd.nextInt(list.size())]);
- - }
- - else
- - {
- - if (player.getQuestsForKills(this) != null)
- - {
- - for (QuestState qs : player.getQuestsForKills(this))
- - questList.add(qs);
- - }
- - }
- -
- - for (QuestState qs : questList)
- - qs.getQuest().notifyKill(this, qs);
- - }
- - }
- - catch (Exception e)
- - {
- -
- - _log.log(Level.SEVERE, "", e);
- - }
- - return true;
- - }
- -
- - /**
- - * Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members.<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Get the L2PcInstance owner of the L2SummonInstance (if necessary) and L2Party in progress </li>
- - * <li>Calculate the Experience and SP rewards in function of the level difference</li>
- - * <li>Add Exp and SP rewards to L2PcInstance (including Summon penalty) and to Party members in the known area of the last attacker </li><BR><BR>
- - *
- - * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T GIVE rewards to L2PetInstance</B></FONT><BR><BR>
- - *
- - * @param lastAttacker The L2Character that has killed the L2Attackable
- - *
- - */
- - private void calculateRewards(L2Character lastAttacker)
- - {
- - if (getAggroList().isEmpty()) return;
- -
- - if (getMustRewardExpSP())
- - {
- -
- -
- - // Creates an empty list of rewards
- - FastMap<L2Character, RewardInfo> rewards = new FastMap<L2Character, RewardInfo>().setShared(true);
- - int rewardCount = 0;
- -
- -
- - int damage;
- - L2Character attacker;
- - L2Character ddealer;
- - RewardInfo reward;
- -
- - // While Iterating over This Map Removing Object is Not Allowed
- - synchronized (getAggroList())
- - {
- - // Go through the _aggroList of the L2Attackable
- - for (AggroInfo info : getAggroList().values())
- - {
- - if (info == null) continue;
- -
- - // Get the L2Character corresponding to this attacker
- - attacker = info.attacker;
- -
- -
- - // Get damages done by this attacker
- - damage = info.damage;
- -
- - // Prevent unwanted behavior
- - if (damage > 1)
- - {
- - if (attacker instanceof L2SummonInstance)
- - ddealer = ((L2SummonInstance)attacker).getOwner();
- - else
- - ddealer = info.attacker;
- -
- - if (!Util.checkIfInRange(1600, this, ddealer, true))
- - continue;
- -
- -
- - // Calculate real damages (Summoners should get own damage plus summon's damage)
- - reward = rewards.get(ddealer);
- -
- -
- - if (reward == null)
- - {
- - reward = new RewardInfo(ddealer, damage);
- - rewardCount++;
- - }
- - else
- - {
- - reward.addDamage(damage);
- - }
- -
- - rewards.put(ddealer, reward);
- - }
- -
- - }
- -
- - }
- -
- - if (!rewards.isEmpty())
- - {
- -
- - L2Party attackerParty;
- -
- - int exp;
- - int levelDiff;
- - int partyDmg;
- - float partyMul;
- - float penalty;
- -
- - RewardInfo reward2;
- - int sp;
- - int[] tmp;
- -
- - for (FastMap.Entry<L2Character, RewardInfo> entry = rewards.head(), end = rewards.tail(); (entry = entry.getNext()) != end;)
- - {
- - if (entry == null) continue;
- -
- - reward = entry.getValue();
- - if(reward == null) continue;
- -
- - // Penalty applied to the attacker's XP
- - penalty = 0;
- -
- - // Attacker to be rewarded
- - attacker = reward.attacker;
- -
- - // Total amount of damage done
- - damage = reward.dmg;
- -
- - // If the attacker is a Pet, get the party of the owner
- - if (attacker instanceof L2PetInstance)
- - attackerParty = ((L2PetInstance)attacker).getParty();
- - else if (attacker instanceof L2PcInstance)
- - attackerParty = ((L2PcInstance)attacker).getParty();
- - else
- - return;
- -
- - // If this attacker is a L2PcInstance with a summoned L2SummonInstance, get Exp Penalty applied for the current summoned L2SummonInstance
- - if (attacker instanceof L2PcInstance && ((L2PcInstance)attacker).getPet() instanceof L2SummonInstance)
- -
- - penalty = ((L2SummonInstance)((L2PcInstance)attacker).getPet()).getExpPenalty();
- -
- -
- - // We must avoid "over damage", if any
- - if (damage > getMaxHp()) damage = getMaxHp();
- -
- - // If there's NO party in progress
- - if (attackerParty == null)
- - {
- - // Calculate Exp and SP rewards
- - if (attacker.getKnownList().knowsObject(this))
- - {
- - // Calculate the difference of level between this attacker (L2PcInstance or L2SummonInstance owner) and the L2Attackable
- - // mob = 24, atk = 10, diff = -14 (full xp)
- - // mob = 24, atk = 28, diff = 4 (some xp)
- - // mob = 24, atk = 50, diff = 26 (no xp)
- - levelDiff = attacker.getLevel() - getLevel();
- -
- - tmp = calculateExpAndSp(levelDiff, damage);
- - exp = tmp[0];
- - exp *= 1 - penalty;
- - sp = tmp[1];
- -
- - // Check for an over-hit enabled strike
- - if (attacker instanceof L2PcInstance)
- - {
- - L2PcInstance player = (L2PcInstance)attacker;
- - if (isOverhit() && attacker == getOverhitAttacker())
- - {
- - player.sendPacket(new SystemMessage(SystemMessage.OVER_HIT));
- - exp += calculateOverhitExp(exp);
- - }
- - }
- -
- - // Distribute the Exp and SP between the L2PcInstance and its L2Summon
- - if (!attacker.isDead())
- - attacker.addExpAndSp((int)Math.round(attacker.calcStat(Stats.EXPSP_RATE, exp, null, null)), (int)attacker.calcStat(Stats.EXPSP_RATE, sp, null, null));
- - }
- - }
- - else
- - {
- - //share with party members
- - partyDmg = 0;
- - partyMul = 1.f;
- -
- - // Get all L2Character (including L2Summon) that can be rewarded in the party
- - List<L2Character> rewardedMembers = new FastList<L2Character>();
- -
- - // Go through all L2PcInstance in the party
- - for (L2PcInstance pl : attackerParty.getPartyMembers())
- - {
- - if (pl == null || pl.isDead()) continue;
- -
- - // Get the RewardInfo of this L2PcInstance from L2Attackable rewards
- - reward2 = rewards.get(pl);
- -
- - // If the L2PcInstance is in the L2Attackable rewards add its damages to party damages
- - if (reward2 != null)
- - {
- - if (Util.checkIfInRange(1600, this, pl, true))
- - {
- - // Add L2PcInstance damages to party damages
- - partyDmg += reward2.dmg;
- -
- -
- - rewardedMembers.add(pl);
- - }
- -
- -
- - // Remove the L2PcInstance from the L2Attackable rewards
- - rewards.remove(pl);
- - }
- - else
- - {
- - // Add L2PcInstance of the party (that have attacked or not) to members that can be rewarded if it's not dead
- - // and in range of the monster.
- - if (Util.checkIfInRange(1600, this, pl, true))
- - rewardedMembers.add(pl);
- - }
- -
- -
- -
- - L2PlayableInstance summon = pl.getPet();
- - if (summon != null && summon instanceof L2PetInstance)
- - {
- - reward2 = rewards.get(summon);
- - if (reward2 != null)
- - {
- -
- - if (Util.checkIfInRange(1600, this, summon, true))
- - {
- - partyDmg += reward2.dmg;
- - rewardedMembers.add(summon);
- - }
- -
- - // Remove the summon from the L2Attackable rewards
- - rewards.remove(summon);
- - }
- - }
- - }
- -
- - // If the party didn't killed this L2Attackable alone
- - if (partyDmg < getMaxHp()) partyMul = ((float)partyDmg / (float)getMaxHp());
- -
- - // Avoid "over damage"
- - if (partyDmg > getMaxHp()) partyDmg = getMaxHp();
- -
- - // Calculate the level difference between Party and L2Attackable
- - levelDiff = attackerParty.getLevel() - getLevel();
- -
- - // Calculate Exp and SP rewards
- - tmp = calculateExpAndSp(levelDiff, partyDmg);
- - exp = tmp[0];
- - sp = tmp[1];
- -
- - exp *= partyMul;
- - sp *= partyMul;
- -
- - // Check for an over-hit enabled strike
- - // (When in party, the over-hit exp bonus is given to the whole party and splitted proportionally through the party members)
- - if (attacker instanceof L2PcInstance)
- - {
- - L2PcInstance player = (L2PcInstance)attacker;
- - if (isOverhit() && attacker == getOverhitAttacker())
- - {
- -
- -
- - player.sendPacket(new SystemMessage(SystemMessage.OVER_HIT));
- - exp += calculateOverhitExp(exp);
- - }
- - }
- -
- - // Distribute Experience and SP rewards to L2PcInstance Party members in the known area of the last attacker
- - if (partyDmg > 0) attackerParty.distributeXpAndSp(exp, sp, rewardedMembers, lastAttacker);
- - }
- - }
- - }
- -
- - rewards = null;
- - }
- - // Manage Base, Quests and Sweep drops of the L2Attackable
- - if (getHaveToDrop())
- - doItemDrop(lastAttacker);
- - // Manage drop of Special Events created by GM for a defined period
- - doEventDrop(lastAttacker);
- - }
- -
- - /**
- - * Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList.<BR><BR>
- - *
- - * @param attacker The L2Character that gave damages to this L2Attackable
- - * @param damage The number of damages given by the attacker L2Character
- - *
- - */
- - public void addDamage(L2Character attacker, int damage)
- - {
- - addDamageHate(attacker, damage, damage);
- - }
- -
- - public void addBufferHate()
- - {
- - L2Character target = getMostHated();
- -
- - if (target == null)
- - return;
- -
- - for (L2PcInstance actor : getKnownList().getKnownPlayers().values())
- - {
- - if (actor.isCastingNow() && target.getLastBuffer() == actor)
- - {
- - int actorLevel = actor.getLevel();
- - double divisor = 0;
- - L2Skill skill = actor.getLastSkillCast();
- -
- - if (actorLevel < 10)
- - divisor = 15;
- - else if (actorLevel > 9 && actorLevel < 20)
- - divisor = 11.5;
- - else if (actorLevel > 19 && actorLevel < 30)
- - divisor = 8.5;
- - else if (actorLevel > 29 && actorLevel < 40)
- - divisor = 6;
- - else if (actorLevel > 39 && actorLevel < 50)
- - divisor = 4;
- - else if (actorLevel > 49 && actorLevel < 60)
- - divisor = 2.5;
- - else if (actorLevel > 59 && actorLevel < 70)
- - divisor = 1.5;
- - else if (actorLevel > 69)
- - divisor = 1;
- -
- -
- - if (skill != null && (skill.getSkillType() == SkillType.HEAL || skill.getSkillType() == SkillType.HEAL_PERCENT || skill.getSkillType() == SkillType.MANAHEAL || skill.getSkillType() == SkillType.BALANCE_LIFE))
- - {
- - L2Object[] targetList = skill.getTargetList(actor,true);
- -
- - if(targetList != null && targetList.length != 0 && (targetList[0] instanceof L2PcInstance || targetList[0] instanceof L2SummonInstance || targetList[0] instanceof L2PetInstance))
- - {
- - if ((getMaxHp()/5) < target.getLastHealAmount())
- - target.setLastHealAmount((getMaxHp()/5));
- -
- - addDamageHate(actor, 0, (int)(target.getLastHealAmount()/divisor));
- -
- - if (Config.DEBUG)
- - System.out.print("Mob detect heal.\n");
- - }
- - }
- -
- - if (skill != null && (skill.getSkillType() == SkillType.BUFF || skill.getSkillType() == SkillType.HOT))
- - {
- - L2Object[] targetList = skill.getTargetList(actor,true);
- -
- - if(targetList != null && targetList.length != 0 && (targetList[0] instanceof L2PcInstance || targetList[0] instanceof L2SummonInstance || targetList[0] instanceof L2PetInstance))
- - {
- - addDamageHate(actor, 0, (int)((skill.getLevel()*getStat().getMpConsume(skill))/divisor));
- -
- - if (Config.DEBUG)
- - System.out.print("Mob detect buff.\n");
- - }
- - }
- -
- - if (((L2PcInstance)target).isInParty() && skill != null && skill.getTargetType() == L2Skill.SkillTargetType.TARGET_PARTY)
- - {
- - List<L2PcInstance> members = ((L2PcInstance)target).getParty().getPartyMembers();
- -
- - for (L2PcInstance member : members)
- - {
- - if (member == actor)
- - {
- - if ((getMaxHp()/3) < (int)(((getHating(target)-getHating(actor))+800)/divisor))
- - addDamageHate(actor, 0, (getMaxHp()/3));
- - else
- - addDamageHate(actor, 0, (int)(((getHating(target)-getHating(actor))+800)/divisor));
- -
- - if (Config.DEBUG)
- - System.out.print("Mob detect party cast.\n");
- - }
- - }
- - }
- - }
- - }
- - }
- -
- - /**
- - * Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList.<BR><BR>
- - *
- - * @param attacker The L2Character that gave damages to this L2Attackable
- - * @param damage The number of damages given by the attacker L2Character
- - * @param aggro The hate (=damage) given by the attacker L2Character
- - *
- - */
- - public void addDamageHate(L2Character attacker, int damage, int aggro)
- - {
- - if (attacker == null) return;
- -
- - // Get the AggroInfo of the attacker L2Character from the _aggroList of the L2Attackable
- - AggroInfo ai = getAggroList().get(attacker);
- - if (ai == null)
- -
- - {
- -
- - ai = new AggroInfo(attacker);
- - ai.damage = 0;
- - ai.hate = 0;
- -
- -
- -
- - getAggroList().put(attacker, ai);
- - }
- -
- -
- - // Add new damage and aggro (=damage) to the AggroInfo object
- - ai.damage += damage;
- - ai.hate += aggro;
- -
- - // Set the intention to the L2Attackable to AI_INTENTION_ACTIVE
- - if (aggro > 0 && getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE) getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
- -
- - // Notify the L2Attackable AI with EVT_ATTACKED
- - if (damage > 0) getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, attacker);
- -
- - try
- - {
- - L2PcInstance player = null;
- - if (attacker instanceof L2PcInstance)
- - player = (L2PcInstance)attacker;
- - else if (attacker instanceof L2Summon)
- - player = ((L2Summon)attacker).getOwner();
- -
- - if (player != null)
- - {
- -
- - if (player.getQuestsForAttacks(this) != null)
- - {
- - for (QuestState st : player.getQuestsForAttacks(this))
- - st.getQuest().notifyAttack(this, st);
- - }
- - }
- - }
- - catch (Exception e) { _log.log(Level.SEVERE, "", e); }
- - }
- -
- - /**
- - * Return the most hated L2Character of the L2Attackable _aggroList.<BR><BR>
- - */
- - public L2Character getMostHated()
- - {
- - if (getAggroList().isEmpty() || isAlikeDead()) return null;
- -
- - L2Character mostHated = null;
- - int maxHate = 0;
- -
- - // While Iterating over This Map Removing Object is Not Allowed
- -
- - synchronized (getAggroList())
- - {
- - // Go through the aggroList of the L2Attackable
- - for (AggroInfo ai : getAggroList().values())
- - {
- - if (ai == null) continue;
- - if (ai.attacker.isAlikeDead() || !getKnownList().knowsObject(ai.attacker) ||!ai.attacker.isVisible())
- - ai.hate = 0;
- -
- -
- - if (ai.hate > maxHate)
- - {
- - mostHated = ai.attacker;
- - maxHate = ai.hate;
- - }
- - }
- - }
- -
- - return mostHated;
- - }
- -
- - /**
- - * Return the hate level of the L2Attackable against this L2Character contained in _aggroList.<BR><BR>
- - *
- - * @param target The L2Character whose hate level must be returned
- - *
- - */
- - public int getHating(L2Character target)
- - {
- - if (getAggroList().isEmpty()) return 0;
- -
- - AggroInfo ai = getAggroList().get(target);
- - if (ai == null) return 0;
- -
- - if (ai.attacker instanceof L2PcInstance && (((L2PcInstance)ai.attacker).getInvisible() == 1 || ai.attacker.isInvul()))
- - {
- - //Remove Object Should Use This Method and Can be Blocked While Iterating
- - getAggroList().remove(target);
- - return 0;
- - }
- - if (!ai.attacker.isVisible())
- - {
- - getAggroList().remove(target);
- - return 0;
- - }
- - if (ai.attacker.isAlikeDead())
- - {
- - ai.hate = 0;
- - return 0;
- - }
- -
- - return ai.hate;
- - }
- -
- - /**
- - * Calculates quantity of items for specific drop acording to current situation <br>
- - *
- - * @param drop The L2DropData count is being calculated for
- - * @param lastAttacker The L2PcInstance that has killed the L2Attackable
- - * @param deepBlueDrop Factor to divide the drop chance
- - * @param levelModifier level modifier in %'s (will be subtracted from drop chance)
- - */
- - private RewardItem calculateRewardItem(L2PcInstance lastAttacker, L2DropData drop, int levelModifier)
- - {
- - // Get default drop chance
- - float dropChance = drop.getChance();
- -
- - float dropItemsRate;
- - if (this instanceof L2BossInstance || this instanceof L2RaidBossInstance)
- - dropItemsRate = Config.RATE_BOSS_DROP_ITEMS;
- - else
- - dropItemsRate = Config.RATE_DROP_ITEMS;
- -
- - int deepBlueDrop = 1;
- - if (Config.DEEPBLUE_DROP_RULES)
- - {
- - if (levelModifier > 0)
- - {
- - // We should multiply by the server's drop rate, so we always get a low chance of drop for deep blue mobs.
- - // NOTE: This is valid only for adena drops! Others drops will still obey server's rate
- - deepBlueDrop = 3;
- - if (drop.getItemId() == 57) deepBlueDrop *= (int)dropItemsRate;
- - }
- - }
- -
- - if(deepBlueDrop == 0) //avoid div by 0
- - deepBlueDrop = 1;
- - // Check if we should apply our maths so deep blue mobs will not drop that easy
- - if (Config.DEEPBLUE_DROP_RULES) dropChance = ((drop.getChance() - ((drop.getChance() * levelModifier)/100)) / deepBlueDrop);
- -
- - // Applies Drop rates
- - if (drop.getItemId() == 57) dropChance *= Config.RATE_DROP_ADENA;
- - else if (drop.isSweep()) dropChance *= Config.RATE_DROP_SPOIL;
- - else dropChance *= dropItemsRate;
- -
- - // Round drop chance
- - dropChance = Math.round(dropChance);
- -
- - // Set our limits for chance of drop
- - if (dropChance < 1) dropChance = 1;
- -// if (drop.getItemId() == 57 && dropChance > L2DropData.MAX_CHANCE) dropChance = L2DropData.MAX_CHANCE; // If item is adena, dont drop multiple time
- -
- - // Get min and max Item quantity that can be dropped in one time
- - int minCount = drop.getMinDrop();
- - int maxCount = drop.getMaxDrop();
- - int itemCount = 0;
- -
- - // Count and chance adjustment for high rate servers
- - if (dropChance > L2DropData.MAX_CHANCE && !Config.PRECISE_DROP_CALCULATION)
- - {
- - int multiplicator = (int)dropChance / L2DropData.MAX_CHANCE;
- - if (minCount < maxCount) itemCount += Rnd.get(minCount * multiplicator, maxCount * multiplicator);
- - else if (minCount == maxCount) itemCount += minCount * multiplicator;
- - else itemCount += multiplicator;
- -
- - dropChance = dropChance % L2DropData.MAX_CHANCE;
- - }
- -
- - // Check if the Item must be dropped
- - int random = Rnd.get(L2DropData.MAX_CHANCE);
- - while (random < dropChance)
- - {
- - // Get the item quantity dropped
- - if (minCount < maxCount) itemCount += Rnd.get(minCount, maxCount);
- - else if (minCount == maxCount) itemCount += minCount;
- - else itemCount++;
- -
- - // Prepare for next iteration if dropChance > L2DropData.MAX_CHANCE
- - dropChance -= L2DropData.MAX_CHANCE;
- - }
- -
- - if (itemCount > 0) return new RewardItem(drop.getItemId(), itemCount);
- - else if (itemCount == 0 && Config.DEBUG) _log.fine("Roll produced 0 items to drop...");
- -
- - return null;
- - }
- -
- - /**
- - * Calculates quantity of items for specific drop CATEGORY according to current situation <br>
- - * Only a max of ONE item from a category is allowed to be dropped.
- - *
- - * @param drop The L2DropData count is being calculated for
- - * @param lastAttacker The L2PcInstance that has killed the L2Attackable
- - * @param deepBlueDrop Factor to divide the drop chance
- - * @param levelModifier level modifier in %'s (will be subtracted from drop chance)
- - */
- - private RewardItem calculateCategorizedRewardItem(L2PcInstance lastAttacker, List<L2DropData> categoryDrops, int levelModifier)
- - {
- - if ((categoryDrops == null) || (categoryDrops.size() == 0))
- - return null;
- -
- - // Get default drop chance for the category (that's the sum of chances for all items in the category)
- - // keep track of the base category chance as it'll be used later, if an item is drop from the category.
- - // for everything else, use the total "categoryDropChance"
- - int basecategoryDropChance = 0;
- - int categoryDropChance = 0;
- - for (L2DropData drop : categoryDrops)
- - basecategoryDropChance += drop.getChance();
- - categoryDropChance = basecategoryDropChance;
- -
- - int deepBlueDrop = 1;
- - if (Config.DEEPBLUE_DROP_RULES)
- - {
- - if (levelModifier > 0)
- - {
- - // We should multiply by the server's drop rate, so we always get a low chance of drop for deep blue mobs.
- - // NOTE: This is valid only for adena drops! Others drops will still obey server's rate
- - deepBlueDrop = 3;
- - }
- - }
- -
- - if(deepBlueDrop == 0) //avoid div by 0
- - deepBlueDrop = 1;
- - // Check if we should apply our maths so deep blue mobs will not drop that easy
- - if (Config.DEEPBLUE_DROP_RULES) categoryDropChance = ((categoryDropChance - ((categoryDropChance * levelModifier)/100)) / deepBlueDrop);
- -
- - // Applies Drop rates
- - if (this instanceof L2BossInstance || this instanceof L2RaidBossInstance)
- - categoryDropChance *= Config.RATE_BOSS_DROP_ITEMS;
- - else
- - categoryDropChance *= Config.RATE_DROP_ITEMS;
- -
- - // Round drop chance
- - categoryDropChance = Math.round(categoryDropChance);
- -
- - // Set our limits for chance of drop
- - if (categoryDropChance < 1) categoryDropChance = 1;
- -
- - // Check if an Item from this category must be dropped
- - if (Rnd.get(L2DropData.MAX_CHANCE) < categoryDropChance)
- - {
- - // ONE of the drops in this category is to be dropped now.
- - // to see which one will be dropped, weight all items' chances such that
- - // their sum of chances equals MAX_CHANCE.
- - // since the individual drops have their base chance, we also ought to use the
- - // base category chance for the weight. So weight = MAX_CHANCE/basecategoryDropChance.
- - // Then get a single random number within this range. The first item
- - // (in order of the list) whose contribution to the sum makes the
- - // sum greater than the random number, will be dropped.
- - int randomIndex = Rnd.get(L2DropData.MAX_CHANCE);
- - double sum = 0.0;
- - double weight = ((double)(L2DropData.MAX_CHANCE))/basecategoryDropChance;
- - for (L2DropData drop : categoryDrops)
- - {
- - sum = sum + (drop.getChance()*weight);
- -
- - if(Config.DEBUG)
- - _log.info("sum so far: "+sum);
- -
- - if (sum >= randomIndex) // drop this item and exit the function
- - {
- - // Get min and max Item quantity that can be dropped in one time
- - int min = drop.getMinDrop();
- - int max = drop.getMaxDrop();
- -
- - // Get the item quantity dropped
- - int itemCount = 0;
- - if (min < max)
- - itemCount += Rnd.get(min, max);
- - else if (min == max)
- - itemCount += min;
- - else
- - itemCount++;
- -
- - if (itemCount > 0)
- - return new RewardItem(drop.getItemId(), itemCount);
- - else if (itemCount == 0 && Config.DEBUG) _log.fine("Roll produced 0 items to drop...");
- - }
- - }
- - }
- - return null;
- - }
- -
- - /**
- - * Calculates the level modifier for drop<br>
- - *
- - * @param lastAttacker The L2PcInstance that has killed the L2Attackable
- - */
- - private int calculateLevelModifierForDrop(L2PcInstance lastAttacker)
- - {
- - if (Config.DEEPBLUE_DROP_RULES)
- - {
- - int highestLevel = lastAttacker.getLevel();
- -
- - // Check to prevent very high level player to nearly kill mob and let low level player do the last hit.
- - if (getAttackByList() != null && !getAttackByList().isEmpty())
- - {
- - for (L2Character atkChar: getAttackByList())
- - if (atkChar != null && atkChar.getLevel() > highestLevel) highestLevel = atkChar.getLevel();
- - }
- -
- - // According to official data (Prima), deep blue mobs are 9 or more levels below players
- - if (highestLevel - 9 >= getLevel()) return ((highestLevel - (getLevel() + 8)) * 9);
- - }
- -
- - return 0;
- - }
- -
- - public void doItemDrop(L2Character lastAttacker)
- - {
- - doItemDrop(getTemplate(),lastAttacker);
- - }
- -
- - /**
- - * Manage Base, Quests and Special Events drops of L2Attackable (called by calculateRewards).<BR><BR>
- - *
- - * <B><U> Concept</U> :</B><BR><BR>
- - * During a Special Event all L2Attackable can drop extra Items.
- - * Those extra Items are defined in the table <B>allNpcDateDrops</B> of the EventDroplist.
- - * Each Special Event has a start and end date to stop to drop extra Items automaticaly. <BR><BR>
- - *
- - * <B><U> Actions</U> : </B><BR><BR>
- - * <li>Manage drop of Special Events created by GM for a defined period </li>
- - * <li>Get all possible drops of this L2Attackable from L2NpcTemplate and add it Quest drops</li>
- - * <li>For each possible drops (base + quests), calculate which one must be dropped (random) </li>
- - * <li>Get each Item quantity dropped (random) </li>
- - * <li>Create this or these L2ItemInstance corresponding to each Item Identifier dropped</li>
- - * <li>If the autoLoot mode is actif and if the L2Character that has killed the L2Attackable is a L2PcInstance, give this or these Item(s) to the L2PcInstance that has killed the L2Attackable</li>
- - * <li>If the autoLoot mode isn't actif or if the L2Character that has killed the L2Attackable is not a L2PcInstance, add this or these Item(s) in the world as a visible object at the position where mob was last</li><BR><BR>
- - *
- - * @param lastAttacker The L2Character that has killed the L2Attackable
- - */
- - public void doItemDrop(L2NpcTemplate npcTemplate, L2Character lastAttacker)
- - {
- - L2PcInstance player = null;
- - if (lastAttacker instanceof L2PcInstance) player = (L2PcInstance)lastAttacker;
- - else if (lastAttacker instanceof L2Summon) player = ((L2Summon)lastAttacker).getOwner();
- -
- - if (player == null) return; // Don't drop anything if the last attacker or ownere isn't L2PcInstance
- -
- - int levelModifier = calculateLevelModifierForDrop(player); // level modifier in %'s (will be subtracted from drop chance)
- -
- - // Create a table containing all possible drops of this L2Attackable from L2NpcTemplate
- - List<L2DropData> drops = new FastList<L2DropData>();
- - drops.addAll(npcTemplate.getDropData());
- - if (Config.DEBUG) _log.finer("this npc has "+drops.size()+" drops defined");
- -
- - // Add Quest drops to the table containing all possible drops of this L2Attackable
- - player.fillQuestDrops(this, drops);
- -
- - List<RewardItem> sweepList = new FastList<RewardItem>();
- - // Go Throw all possible drops (base + quests) of this L2Attackable
- - // This includes all spoil, plus adena, sealstone, quest, Raidboss, and all other drops that
- - // should NOT follow the rule of 1 drop per category per kill
- - for (L2DropData drop : drops)
- - {
- - if (drop == null) continue;
- -
- - RewardItem item = calculateRewardItem(player, drop, levelModifier);
- - if (item == null) continue;
- -
- - if (drop.isSweep())
- - {
- - if (!isSpoil()) continue; // L2Attackable was not spoiled, skip sweep drop
- - if (Config.DEBUG) _log.fine("Item id to spoil: " + item.getItemId() + " amount: " + item.getCount());
- - sweepList.add(item);
- - }
- - else
- - {
- - if (isSeeded() && item.getItemId() != 57) continue; // L2Attackable was seeded, don't ropd other than adena
- - if (Config.DEBUG) _log.fine("Item id to drop: " + item.getItemId() + " amount: " + item.getCount());
- -
- - // Check if the autoLoot mode is active
- - if (Config.AUTO_LOOT)
- - player.doAutoLoot(this, item); // Give this or these Item(s) to the L2PcInstance that has killed the L2Attackable
- - else
- - dropItem(player, item); // drop the item on the ground
- -
- - // Broadcast message if RaidBoss was defeated
- - if (this instanceof L2RaidBossInstance)
- - {
- - SystemMessage sm;
- - sm = new SystemMessage(SystemMessage.S1_DIED_DROPPED_S3_S2);
- - sm.addString(getName());
- - sm.addItemName(item.getItemId());
- - sm.addNumber(item.getCount());
- - broadcastPacket(sm);
- - }
- - }
- - }
- -
- - if (!isSeeded())
- - {
- - RewardItem item = calculateCategorizedRewardItem(player, npcTemplate.getFullDropData(), levelModifier);
- - if (item != null)
- - {
- - if (Config.DEBUG) _log.fine("Item id to drop: " + item.getItemId() + " amount: " + item.getCount());
- -
- - // Check if the autoLoot mode is active
- - if (Config.AUTO_LOOT)
- - player.doAutoLoot(this, item); // Give this or these Item(s) to the L2PcInstance that has killed the L2Attackable
- - else
- - dropItem(player, item); // drop the item on the ground
- - }
- -
- - item = calculateCategorizedRewardItem(player, npcTemplate.getMiscDropData(), levelModifier);
- - if (item != null)
- - {
- - if (Config.DEBUG) _log.fine("Item id to drop: " + item.getItemId() + " amount: " + item.getCount());
- -
- - // Check if the autoLoot mode is active
- - if (Config.AUTO_LOOT)
- - player.doAutoLoot(this, item); // Give this or these Item(s) to the L2PcInstance that has killed the L2Attackable
- - else
- - dropItem(player, item); // drop the item on the ground
- - }
- - }
- -
- -
- - // Set the table _sweepItems of this L2Attackable
- - if (!sweepList.isEmpty())
- - _sweepItems = sweepList.toArray(new RewardItem[sweepList.size()]);
- - }
- -
- - /**
- - * Manage Special Events drops created by GM for a defined period.<BR><BR>
- - *
- - * <B><U> Concept</U> :</B><BR><BR>
- - * During a Special Event all L2Attackable can drop extra Items.
- - * Those extra Items are defined in the table <B>allNpcDateDrops</B> of the EventDroplist.
- - * Each Special Event has a start and end date to stop to drop extra Items automaticaly. <BR><BR>
- - *
- - * <B><U> Actions</U> : <I>If an extra drop must be generated</I></B><BR><BR>
- - * <li>Get an Item Identifier (random) from the DateDrop Item table of this Event </li>
- - * <li>Get the Item quantity dropped (random) </li>
- - * <li>Create this or these L2ItemInstance corresponding to this Item Identifier</li>
- - * <li>If the autoLoot mode is actif and if the L2Character that has killed the L2Attackable is a L2PcInstance, give this or these Item(s) to the L2PcInstance that has killed the L2Attackable</li>
- - * <li>If the autoLoot mode isn't actif or if the L2Character that has killed the L2Attackable is not a L2PcInstance, add this or these Item(s) in the world as a visible object at the position where mob was last</li><BR><BR>
- - *
- - * @param lastAttacker The L2Character that has killed the L2Attackable
- - */
- - public void doEventDrop(L2Character lastAttacker)
- - {
- - L2PcInstance player = null;
- - if (lastAttacker instanceof L2PcInstance)
- - player = (L2PcInstance)lastAttacker;
- - else if (lastAttacker instanceof L2Summon)
- - player = ((L2Summon)lastAttacker).getOwner();
- -
- - if (player == null) return; // Don't drop anything if the last attacker or ownere isn't L2PcInstance
- -
- - if (player.getLevel() - getLevel() > 9) return;
- -
- - // Go through DateDrop of EventDroplist allNpcDateDrops within the date range
- - for (DateDrop drop : EventDroplist.getInstance().getAllDrops())
- - {
- - if (Rnd.get(L2DropData.MAX_CHANCE) < drop.chance)
- - {
- - RewardItem item = new RewardItem(drop.items[Rnd.get(drop.items.length)], Rnd.get(drop.min, drop.max));
- - if (Config.AUTO_LOOT)
- - player.doAutoLoot(this, item); // Give this or these Item(s) to the L2PcInstance that has killed the L2Attackable
- - else
- - dropItem(player, item); // drop the item on the ground
- - }
- - }
- - }
- -
- - /**
- - * Drop reward item.<BR><BR>
- - */
- - public L2ItemInstance dropItem(L2PcInstance lastAttacker, RewardItem item)
- - {
- - int randDropLim = 70;
- -
- - L2ItemInstance ditem = null;
- - for (int i = 0; i < item.getCount(); i++)
- - {
- - // Randomize drop position
- - int newX = getX() + Rnd.get(randDropLim * 2 + 1) - randDropLim;
- - int newY = getY() + Rnd.get(randDropLim * 2 + 1) - randDropLim;
- - int newZ = Math.max(getZ(), lastAttacker.getZ()) + 20; // TODO: temp hack, do something nicer when we have geodatas
- -
- - // Init the dropped L2ItemInstance and add it in the world as a visible object at the position where mob was last
- - ditem = ItemTable.getInstance().createItem("Loot", item.getItemId(), item.getCount(), lastAttacker, this);
- - ditem.dropMe(this, newX, newY, newZ);
- -
- - // Add drop to auto destroy item task
- - if (!Config.LIST_PROTECTED_ITEMS.contains(item.getItemId()))
- - {
- - if (Config.AUTODESTROY_ITEM_AFTER > 0)
- - ItemsAutoDestroy.getInstance().addItem(ditem);
- - }
- - ditem.setProtected(false);
- - // If stackable, end loop as entire count is included in 1 instance of item
- - if (ditem.isStackable() || !Config.MULTIPLE_ITEM_DROP) break;
- - }
- - return ditem;
- - }
- -
- - public L2ItemInstance dropItem(L2PcInstance lastAttacker, int itemId, int itemCount)
- - {
- - return dropItem(lastAttacker, new RewardItem(itemId, itemCount));
- - }
- -
- - /**
- - * Return the active weapon of this L2Attackable (= null).<BR><BR>
- - */
- - public L2ItemInstance getActiveWeapon()
- - {
- - return null;
- - }
- -
- - /**
- - * Return True if the _aggroList of this L2Attackable is Empty.<BR><BR>
- - */
- - public boolean noTarget()
- - {
- - return getAggroList().isEmpty();
- - }
- -
- - /**
- - * Return True if the _aggroList of this L2Attackable contains the L2Character.<BR><BR>
- - *
- - * @param player The L2Character searched in the _aggroList of the L2Attackable
- - *
- - */
- - public boolean containsTarget(L2Character player)
- - {
- - return getAggroList().containsKey(player);
- - }
- -
- - /**
- - * Clear the _aggroList of the L2Attackable.<BR><BR>
- - */
- - public void clearAggroList()
- - {
- - getAggroList().clear();
- -
- - _overhit = false;
- - _overhitDamage = 0;
- - _overhitAttacker = null;
- - }
- -
- - /**
- - * Clears _aggroList hate of the L2Character without removing from the list.<BR><BR>
- - */
- - public void clearHating(L2Character target)
- - {
- - if (getAggroList().isEmpty()) return;
- - AggroInfo ai = getAggroList().get(target);
- - if (ai == null) return;
- - ai.hate = 0;
- - }
- -
- - /**
- - * Return True if a Dwarf use Sweep on the L2Attackable and if item can be spoiled.<BR><BR>
- - */
- - public boolean isSweepActive()
- - {
- - return _sweepItems != null;
- - }
- -
- - /**
- - * Return table containing all L2ItemInstance that can be spoiled.<BR><BR>
- - */
- - public synchronized RewardItem[] takeSweep()
- - {
- - RewardItem[] sweep = _sweepItems;
- -
- - _sweepItems = null;
- -
- - return sweep;
- - }
- -
- - /**
- - * Return table containing all L2ItemInstance that can be harvested.<BR><BR>
- - */
- - public synchronized RewardItem[] takeHarvest()
- - {
- - RewardItem[] harvest = _harvestItems;
- - _harvestItems = null;
- - return harvest;
- - }
- -
- - /**
- - * Set the over-hit flag on the L2Attackable.<BR><BR>
- - *
- - * @param status The status of the over-hit flag
- - *
- - */
- - public void overhitEnabled(boolean status)
- - {
- - _overhit = status;
- - }
- -
- - /**
- - * Set the over-hit values like the attacker who did the strike and the ammount of damage done by the skill.<BR><BR>
- - *
- - * @param attacker The L2Character who hit on the L2Attackable using the over-hit enabled skill
- - * @param damage The ammount of damage done by the over-hit enabled skill on the L2Attackable
- - *
- - */
- - public void setOverhitValues(L2Character attacker, double damage)
- - {
- - // Calculate the over-hit damage
- - // Ex: mob had 10 HP left, over-hit skill did 50 damage total, over-hit damage is 40
- - double overhitDmg = ((getCurrentHp() - damage) * (-1));
- - if (overhitDmg < 0)
- - {
- - // we didn't killed the mob with the over-hit strike. (it wasn't really an over-hit strike)
- - // let's just clear all the over-hit related values
- - overhitEnabled(false);
- - _overhitDamage = 0;
- - _overhitAttacker = null;
- - return;
- - }
- - overhitEnabled(true);
- - _overhitDamage = overhitDmg;
- - _overhitAttacker = attacker;
- - }
- -
- - /**
- - * Return the L2Character who hit on the L2Attackable using an over-hit enabled skill.<BR><BR>
- - *
- - * @return L2Character attacker
- - */
- - public L2Character getOverhitAttacker()
- - {
- - return _overhitAttacker;
- - }
- -
- - /**
- - * Return the ammount of damage done on the L2Attackable using an over-hit enabled skill.<BR><BR>
- - *
- - * @return double damage
- - */
- - public double getOverhitDamage()
- - {
- - return _overhitDamage;
- - }
- -
- - /**
- - * Return True if the L2Attackable was hit by an over-hit enabled skill.<BR><BR>
- - */
- - public boolean isOverhit()
- - {
- - return _overhit;
- - }
- -
- - /**
- - * Activate the absorbed soul condition on the L2Attackable.<BR><BR>
- - */
- - public void absorbSoul()
- - {
- - _absorbed = true;
- - }
- -
- - /**
- - * Return True if the L2Attackable had his soul absorbed.<BR><BR>
- - */
- - public boolean isAbsorbed()
- - {
- - return _absorbed;
- - }
- -
- - /**
- - * Adds an attacker that successfully absorbed the soul of this L2Attackable into the _absorbersList.<BR><BR>
- - *
- - * params: attacker - a valid L2PcInstance
- - * condition - an integer indicating the event when mob dies. This should be:
- - * = 0 - "the crystal scatters";
- - * = 1 - "the crystal failed to absorb. nothing happens";
- - * = 2 - "the crystal resonates because you got more than 1 crystal on you";
- - * = 3 - "the crystal cannot absorb the soul because the mob level is too low";
- - * = 4 - "the crystal successfuly absorbed the soul";
- - */
- - public void addAbsorber(L2PcInstance attacker, int crystalId)
- - {
- - // This just works for targets like L2MonsterInstance
- - if (!(this instanceof L2MonsterInstance))
- - return;
- -
- - // The attacker must not be null
- - if (attacker == null)
- - return;
- -
- - // This L2Attackable must be of one type in the _absorbingMOBS_levelXX tables.
- - // OBS: This is done so to avoid triggering the absorbed conditions for mobs that can't be absorbed.
- - if (getAbsorbLevel() == 0)
- - return;
- -
- - // If we have no _absorbersList initiated, do it
- - AbsorberInfo ai = _absorbersList.get(attacker);
- -
- -
- - // If the L2Character attacker isn't already in the _absorbersList of this L2Attackable, add it
- - if (ai == null)
- - {
- - ai = new AbsorberInfo(attacker, crystalId, getCurrentHp());
- - _absorbersList.put(attacker, ai);
- - }
- - else
- - {
- - ai.absorber = attacker;
- - ai.crystalId = crystalId;
- - ai.absorbedHP = getCurrentHp();
- - }
- -
- - // Set this L2Attackable as absorbed
- - absorbSoul();
- - }
- -
- - /**
- - * Calculate the leveling chance of Soul Crystals based on the attacker that killed this L2Attackable
- - *
- - * @param attacker The player that last killed this L2Attackable
- - * $ Rewrite 06.12.06 - Yesod
- - */
- - private void levelSoulCrystals(L2Character attacker)
- - {
- - // Only L2PcInstance can absorb a soul
- - if (!(attacker instanceof L2PcInstance))
- - {
- - resetAbsorbList(); return;
- - }
- -
- - int maxAbsorbLevel = getAbsorbLevel();
- - int minAbsorbLevel = 0;
- -
- - // If this is not a valid L2Attackable, clears the _absorbersList and just return
- - if (maxAbsorbLevel == 0)
- - {
- - resetAbsorbList(); return;
- - }
- - // All boss mobs with maxAbsorbLevel 13 have minAbsorbLevel of 12 else 10
- - if (maxAbsorbLevel > 10)
- - minAbsorbLevel = maxAbsorbLevel > 12 ? 12 : 10;
- -
- - //Init some useful vars
- - boolean isSuccess = true;
- - boolean doLevelup = true;
- - boolean isBossMob = maxAbsorbLevel > 10 ? true : false;
- -
- - L2PcInstance killer = (L2PcInstance)attacker;
- -
- - // If this mob is a boss, then skip some checkings
- - if (!isBossMob)
- - {
- - // Fail if this L2Attackable isn't absorbed or there's no one in its _absorbersList
- - if (!isAbsorbed() /*|| _absorbersList == null*/)
- - {
- - resetAbsorbList();
- - return;
- - }
- -
- - // Fail if the killer isn't in the _absorbersList of this L2Attackable and mob is not boss
- - AbsorberInfo ai = _absorbersList.get(killer);
- - if (ai == null || ai.absorber.getObjectId() != killer.getObjectId())
- - isSuccess = false;
- -
- - // Check if the soul crystal was used when HP of this L2Attackable wasn't higher than half of it
- - if (ai != null && ai.absorbedHP > (getMaxHp()/2))
- - isSuccess = false;
- -
- - if (!isSuccess) {
- - resetAbsorbList();
- - return;
- - }
- - }
- -
- - // ********
- - String[] crystalNFO = null;
- - String crystalNME = "";
- -
- - int dice = Rnd.get(100);
- - int crystalQTY = 0;
- - int crystalLVL = 0;
- - int crystalOLD = 0;
- - int crystalNEW = 0;
- -
- - // ********
- - // Now we have four choices:
- - // 1- The Monster level is too low for the crystal. Nothing happens.
- - // 2- Everything is correct, but it failed. Nothing happens. (57.5%)
- - // 3- Everything is correct, but it failed. The crystal scatters. A sound event is played. (10%)
- - // 4- Everything is correct, the crystal level up. A sound event is played. (32.5%)
- -
- - List<L2PcInstance> players = new FastList<L2PcInstance>();
- -
- - if (isBossMob && killer.isInParty())
- - players = killer.getParty().getPartyMembers();
- - else
- - players.add(killer);
- -
- - for (L2PcInstance player : players)
- - {
- - if (player == null)
- - continue;
- -
- - QuestState qs = player.getQuestState("350_EnhanceYourWeapon");
- - if (qs == null || !qs.isStarted())
- - continue;
- -
- -
- -
- - crystalQTY = 0;
- -
- - L2ItemInstance[] inv = player.getInventory().getItems();
- - for (L2ItemInstance item : inv)
- - {
- - int itemId = item.getItemId();
- - for (int id : SoulCrystal.SoulCrystalTable)
- - {
- - // Find any of the 39 possible crystals.
- - if (id == itemId)
- - {
- - crystalQTY++;
- - // Keep count but make sure the player has no more than 1 crystal
- - if (crystalQTY > 1)
- - {
- - isSuccess = false; break;
- - }
- -
- - // Validate if the crystal has already leveled
- - if(id != SoulCrystal.RED_NEW_CRYSTAL
- - && id != SoulCrystal.GRN_NEW_CYRSTAL
- - && id != SoulCrystal.BLU_NEW_CRYSTAL)
- - {
- - try
- - {
- - if (item.getItem().getName().contains("Grade"))
- - {
- - // Split the name of the crystal into 'name' & 'level'
- - crystalNFO = item.getItem().getName().trim().replace(" Grade ", "-").split("-");
- - // Set Level to 13
- - crystalLVL = 13;
- - // Get Name
- - crystalNME = crystalNFO[0].toLowerCase();
- - }
- - else
- - {
- - // Split the name of the crystal into 'name' & 'level'
- - crystalNFO = item.getItem().getName().trim().replace(" Stage ", "").split("-");
- - // Get Level
- - crystalLVL = Integer.parseInt(crystalNFO[1].trim());
- - // Get Name
- - crystalNME = crystalNFO[0].toLowerCase();
- - }
- - // Allocate current and levelup ids' for higher level crystals
- - if(crystalLVL > 9)
- - {
- - for(int i = 0; i < SoulCrystal.HighSoulConvert.length; i++)
- - // Get the next stage above 10 using array.
- - if(id == SoulCrystal.HighSoulConvert[i][0])
- - {
- - crystalNEW = SoulCrystal.HighSoulConvert[i][1]; break;
- - }
- - }
- - else
- - crystalNEW = id+1;
- - }
- - catch (NumberFormatException nfe)
- - {
- - _log.log(Level.WARNING, "An attempt to identify a soul crystal failed, " +
- - "verify the names have not changed in etcitem " +
- - "table.", nfe);
- -
- - player.sendMessage("There has been an error handling your soul crystal." +
- - " Please notify your server admin.");
- -
- - isSuccess = false;
- - break;
- - }
- - catch (Exception e)
- - {
- - e.printStackTrace();
- - isSuccess = false;
- - break;
- - }
- - }
- - else
- - {
- - crystalNME = item.getItem().getName().toLowerCase().trim();
- - crystalNEW = id+1;
- - }
- -
- - // Done
- - crystalOLD = id;
- - break;
- - }
- - }
- - if (!isSuccess) break;
- - }
- -
- - // If the crystal level is way too high for this mob, say that we can't increase it
- - if ((crystalLVL < minAbsorbLevel) || (crystalLVL >= maxAbsorbLevel))
- - doLevelup = false;
- -
- - // The player doesn't have any crystals with him get to the next player.
- - if (crystalQTY < 1 || crystalQTY > 1 || !isSuccess || !doLevelup)
- - {
- - // Too many crystals in inventory.
- - if (crystalQTY > 1)
- - {
- - player.sendPacket(new SystemMessage(SystemMessage.SOUL_CRYSTAL_ABSORBING_FAILED_RESONATION));
- - }
- - // The soul crystal stage of the player is way too high
- - else if (!doLevelup && crystalQTY > 0)
- - player.sendPacket(new SystemMessage(SystemMessage.SOUL_CRYSTAL_ABSORBING_REFUSED));
- -
- - crystalQTY = 0;
- - continue;
- - }
- -
- - // Ember and Anakazel(78) are not 100% success rate and each individual
- - // member of the party has a failure rate on leveling.
- - if(isBossMob && (getNpcId() == 10319 || getNpcId() == 10338))
- - doLevelup = false;
- -
- - // If succeeds or it is a boss mob, level up the crystal.
- - if ((isBossMob && doLevelup) || (dice <= SoulCrystal.LEVEL_CHANCE))
- - {
- - // Give staged crystal
- - exchangeCrystal(player, crystalOLD, crystalNEW, false);
- - }
- - // If true and not a boss mob, break the crystal.
- - else if (!isBossMob && dice >= (100.0 - SoulCrystal.BREAK_CHANCE))
- - {
- - // Remove current crystal an give a broken open.
- - if (crystalNME.startsWith("red"))
- - exchangeCrystal(player, crystalOLD, SoulCrystal.RED_BROKEN_CRYSTAL, true);
- -
- - else if (crystalNME.startsWith("gre"))
- - exchangeCrystal(player, crystalOLD, SoulCrystal.GRN_BROKEN_CYRSTAL, true);
- -
- - else if (crystalNME.startsWith("blu"))
- - exchangeCrystal(player, crystalOLD, SoulCrystal.BLU_BROKEN_CRYSTAL, true);
- -
- - resetAbsorbList();
- - }
- - else
- - player.sendPacket(new SystemMessage(SystemMessage.SOUL_CRYSTAL_ABSORBING_FAILED));
- - }
- - }
- -
- - private void exchangeCrystal(L2PcInstance player, int takeid, int giveid, boolean broke)
- - {
- - L2ItemInstance Item = player.getInventory().destroyItemByItemId("SoulCrystal", takeid, 1, player, this);
- - if (Item != null)
- - {
- - // Prepare inventory update packet
- - InventoryUpdate playerIU = new InventoryUpdate();
- - playerIU.addRemovedItem(Item);
- -
- - // Add new crystal to the killer's inventory
- - Item = player.getInventory().addItem("SoulCrystal", giveid, 1, player, this);
- - playerIU.addItem(Item);
- -
- - // Send a sound event and text message to the player
- - if(broke)
- - {
- - player.sendPacket(new SystemMessage(SystemMessage.SOUL_CRYSTAL_BROKE));
- - }
- - else
- - player.sendPacket(new SystemMessage(SystemMessage.SOUL_CRYSTAL_ABSORBING_SUCCEEDED));
- -
- - // Send system message
- - SystemMessage sms = new SystemMessage(SystemMessage.EARNED_ITEM);
- - sms.addItemName(giveid);
- - player.sendPacket(sms);
- -
- - // Send inventory update packet
- - player.sendPacket(playerIU);
- - }
- - }
- -
- - private void resetAbsorbList()
- - {
- - _absorbed = false;
- - _absorbersList.clear();
- - }
- -
- - /**
- - * Calculate the Experience and SP to distribute to attacker (L2PcInstance, L2SummonInstance or L2Party) of the L2Attackable.<BR><BR>
- - *
- - * @param diff The difference of level between attacker (L2PcInstance, L2SummonInstance or L2Party) and the L2Attackable
- - * @param damage The damages given by the attacker (L2PcInstance, L2SummonInstance or L2Party)
- - *
- - */
- - private int[] calculateExpAndSp(int diff, int damage)
- - {
- - long xp;
- - long sp;
- -
- - if(diff < -5) diff = -5; // makes possible to use ALT_GAME_EXPONENT configuration
- - xp = (long)getExpReward() * damage / getMaxHp();
- - if (Config.ALT_GAME_EXPONENT_XP != 0) xp *= Math.pow(2., -diff / Config.ALT_GAME_EXPONENT_XP);
- -
- - sp = (long)getSpReward() * damage / getMaxHp();
- - if (Config.ALT_GAME_EXPONENT_SP != 0) sp *= Math.pow(2., -diff / Config.ALT_GAME_EXPONENT_SP);
- -
- - if (Config.ALT_GAME_EXPONENT_XP == 0 && Config.ALT_GAME_EXPONENT_SP == 0)
- - {
- - // deep blue mob, is more than 8 levels below attacker lvl
- - if (diff > 8)
- - {
- - xp = 0;
- - sp = 0;
- - }
- - // green or light blue mob, is 6 to 8 levels below attacker lvl
- - else if (diff > 5)
- - {
- - xp -= diff * xp / 10;
- - sp -= diff * sp / 10;
- - }
- -
- - if (xp <= 0)
- - {
- - xp = 0;
- - sp = 0;
- - }
- - else if (sp <= 0)
- - {
- - sp = 0;
- - }
- - }
- -
- - int[] tmp = { (int)xp, (int)sp };
- -
- - return tmp;
- - }
- -
- - public int calculateOverhitExp(int normalExp)
- - {
- - // Get the percentage based on the total of extra (over-hit) damage done relative to the total (maximum) ammount of HP on the L2Attackable
- - double overhitPercentage = ((getOverhitDamage() * 100) / getMaxHp());
- -
- - // Over-hit damage percentages are limited to 25% max
- - if (overhitPercentage > 25)
- - overhitPercentage = 25;
- -
- - // Get the overhit exp bonus according to the above over-hit damage percentage
- - // (1/1 basis - 13% of over-hit damage, 13% of extra exp is given, and so on...)
- - double overhitExp = ((overhitPercentage / 100) * normalExp);
- -
- - // Return the rounded ammount of exp points to be added to the player's normal exp reward
- - int bonusOverhit = (int)Math.round(overhitExp);
- - return bonusOverhit;
- - }
- -
- - /**
- - * Return True.<BR><BR>
- - */
- - public boolean isAttackable()
- - {
- - return true;
- - }
- -
- - public void onSpawn()
- - {
- - super.onSpawn();
- - // Clear mob spoil,seed
- - setSpoil(false);
- -
- - // Clear all aggro char from list
- - clearAggroList();
- - // Clear Harvester Reward List
- - _harvestItems = null;
- - // Clear mod Seeded stat
- - setSeeded(false);
- - // Clear overhit value
- - overhitEnabled(false);
- -
- - _sweepItems = null;
- - resetAbsorbList();
- -
- - setWalking();
- -
- - // check the region where this mob is, do not activate the AI if region is inactive.
- - if (!isInActiveRegion())
- - if (this instanceof L2SiegeGuardInstance)
- - ((L2SiegeGuardAI) getAI()).stopAITask();
- - else
- - ((L2AttackableAI) getAI()).stopAITask();
- - }
- -
- - public void setSeeded()
- - {
- - if (_seedType != 0 && _seeder != null)
- - setSeeded(_seedType, _seeder.getLevel());
- - }
- -
- - public void setSeeded(int id, L2PcInstance seeder)
- - {
- - if (!_seeded)
- - {
- - _seedType = id;
- - _seeder = seeder;
- - }
- - }
- -
- - public void setSeeded(int id, int seederLvl)
- - {
- - _seeded = true;
- - _seedType = id;
- - int count = 1;
- -
- - Map<Integer, L2Skill> skills = getTemplate().getSkills();
- - if (skills != null)
- - {
- - for (int skillId : skills.keySet())
- - {
- - switch (skillId)
- - {
- - case 4303: //Strong type x2
- - count *= 2;
- - break;
- - case 4304: //Strong type x3
- - count *= 3;
- - break;
- - case 4305: //Strong type x4
- - count *= 4;
- - break;
- - case 4306: //Strong type x5
- - count *= 5;
- - break;
- - case 4307: //Strong type x6
- - count *= 6;
- - break;
- - case 4308: //Strong type x7
- - count *= 7;
- - break;
- - case 4309: //Strong type x8
- - count *= 8;
- - break;
- - case 4310: //Strong type x9
- - count *= 9;
- - break;
- - }
- - }
- - }
- -
- - int diff = (getLevel() - (L2Manor.getInstance().getSeedLevel(_seedType) - 5));
- -
- - // hi-lvl mobs bonus
- - if (diff > 0)
- -
- - count += diff;
- -
- -
- - FastList<RewardItem> harvested = new FastList<RewardItem>();
- -
- - harvested.add(new RewardItem(L2Manor.getInstance().getCropType(_seedType), count * Config.RATE_DROP_MANOR));
- -
- - _harvestItems = harvested.toArray(new RewardItem[harvested.size()]);
- - }
- -
- - public void setSeeded(boolean seeded)
- - {
- - _seeded = seeded;
- - }
- -
- - public L2PcInstance getSeeder()
- - {
- - return _seeder;
- - }
- -
- - public int getSeedType()
- - {
- - return _seedType;
- - }
- -
- - public boolean isSeeded()
- - {
- - return _seeded;
- - }
- -
- - private int getAbsorbLevel()
- - {
- - return getTemplate().absorb_level;
- - }
- -
- - /**
- - * Check if the server allows Random Animation.<BR><BR>
- - */
- - public boolean hasRandomAnimation()
- - {
- - return ((Config.MAX_MONSTER_ANIMATION > 0) && !(this instanceof L2BossInstance));
- - }
- + // protected static Logger _log = Logger.getLogger(L2Attackable.class.getName());
- +
- + /**
- + * This class contains all AggroInfo of the L2Attackable against the attacker L2Character.<BR>
- + * <BR>
- + * <B><U> Data</U> :</B><BR>
- + * <BR>
- + * <li>attacker : The attaker L2Character concerned by this AggroInfo of this L2Attackable</li> <li>hate : Hate level of this L2Attackable against the attaker L2Character (hate = damage)</li> <li>damage : Number of damages that the attaker L2Character gave to this L2Attackable</li><BR>
- + * <BR>
- + */
- + public final class AggroInfo
- + {
- + /** The attaker L2Character concerned by this AggroInfo of this L2Attackable */
- + L2Character attacker;
- +
- + /** Hate level of this L2Attackable against the attaker L2Character (hate = damage) */
- + int hate;
- +
- + /** Number of damages that the attaker L2Character gave to this L2Attackable */
- + int damage;
- +
- + /**
- + * Constructor of AggroInfo.<BR>
- + * <BR>
- + */
- + AggroInfo(L2Character pAttacker)
- + {
- + attacker = pAttacker;
- + }
- +
- + /**
- + * Verify is object is equal to this AggroInfo.<BR>
- + * <BR>
- + */
- + @Override
- + public boolean equals(Object obj)
- + {
- + if (this == obj)
- + {
- + return true;
- + }
- +
- + if (obj instanceof AggroInfo)
- + {
- + return (((AggroInfo) obj).attacker == attacker);
- + }
- +
- + return false;
- + }
- +
- + /**
- + * Return the Identifier of the attaker L2Character.<BR>
- + * <BR>
- + */
- + @Override
- + public int hashCode()
- + {
- + return attacker.getObjectId();
- + }
- +
- + }
- +
- + /**
- + * This class contains all RewardInfo of the L2Attackable against the any attacker L2Character, based on amount of damage done.<BR>
- + * <BR>
- + * <B><U> Data</U> :</B><BR>
- + * <BR>
- + * <li>attacker : The attaker L2Character concerned by this RewardInfo of this L2Attackable</li> <li>dmg : Total amount of damage done by the attacker to this L2Attackable (summon + own)</li>
- + */
- + protected final class RewardInfo
- + {
- + protected L2Character attacker;
- + protected int dmg = 0;
- +
- + public RewardInfo(L2Character pAttacker, int pDmg)
- + {
- + attacker = pAttacker;
- + dmg = pDmg;
- + }
- +
- + public void addDamage(int pDmg)
- + {
- + dmg += pDmg;
- + }
- +
- + @Override
- + public boolean equals(Object obj)
- + {
- + if (this == obj)
- + {
- + return true;
- + }
- +
- + if (obj instanceof RewardInfo)
- + {
- + return (((RewardInfo) obj).attacker == attacker);
- + }
- +
- + return false;
- + }
- +
- + @Override
- + public int hashCode()
- + {
- + return attacker.getObjectId();
- + }
- + }
- +
- + /**
- + * This class contains all AbsorberInfo of the L2Attackable against the absorber L2Character.<BR>
- + * <BR>
- + * <B><U> Data</U> :</B><BR>
- + * <BR>
- + * <li>absorber : The attaker L2Character concerned by this AbsorberInfo of this L2Attackable</li>
- + */
- + public final class AbsorberInfo
- + {
- + /** The attaker L2Character concerned by this AbsorberInfo of this L2Attackable */
- + L2PcInstance absorber;
- + int crystalId;
- + double absorbedHP;
- +
- + /**
- + * Constructor of AbsorberInfo.<BR>
- + * <BR>
- + */
- + AbsorberInfo(L2PcInstance attacker, int pCrystalId, double pAbsorbedHP)
- + {
- + absorber = attacker;
- + crystalId = pCrystalId;
- + absorbedHP = pAbsorbedHP;
- + }
- +
- + /**
- + * Verify is object is equal to this AbsorberInfo.<BR>
- + * <BR>
- + */
- + @Override
- + public boolean equals(Object obj)
- + {
- + if (this == obj)
- + {
- + return true;
- + }
- +
- + if (obj instanceof AbsorberInfo)
- + {
- + return (((AbsorberInfo) obj).absorber == absorber);
- + }
- +
- + return false;
- + }
- +
- + /**
- + * Return the Identifier of the absorber L2Character.<BR>
- + * <BR>
- + */
- + @Override
- + public int hashCode()
- + {
- + return absorber.getObjectId();
- + }
- + }
- +
- + /**
- + * This class is used to create item reward lists instead of creating item instances.<BR>
- + * <BR>
- + */
- + public final class RewardItem
- + {
- + protected int _itemId;
- + protected int _count;
- +
- + public RewardItem(int itemId, int count)
- + {
- + _itemId = itemId;
- + _count = count;
- + }
- +
- + public int getItemId()
- + {
- + return _itemId;
- + }
- +
- + public int getCount()
- + {
- + return _count;
- + }
- + }
- +
- + private final FastMap<L2Character, AggroInfo> _aggroList = new FastMap<L2Character, AggroInfo>().setShared(true);
- +
- + /**
- + * Use this to Remove Object from this Map This Should be Synchronized While Iterating over This Map - if u cant iterating and removing object at once
- + */
- + public final FastMap<L2Character, AggroInfo> getAggroList()
- + {
- + return _aggroList;
- + }
- +
- + private boolean _isReturningToSpawnPoint = false;
- +
- + public final boolean isReturningToSpawnPoint()
- + {
- + return _isReturningToSpawnPoint;
- + }
- +
- + public final void setIsReturningToSpawnPoint(boolean value)
- + {
- + _isReturningToSpawnPoint = value;
- + }
- +
- + /** Table containing all Items that a Dwarf can Sweep on this L2Attackable */
- + private RewardItem[] _sweepItems;
- +
- + /** crops */
- + private RewardItem[] _harvestItems;
- + private boolean _seeded;
- + private int _seedType = 0;
- + private L2PcInstance _seeder = null;
- +
- + /** True if an over-hit enabled skill has successfully landed on the L2Attackable */
- + private boolean _overhit;
- +
- + /** Stores the extra (over-hit) damage done to the L2Attackable when the attacker uses an over-hit enabled skill */
- + private double _overhitDamage;
- +
- + /** Stores the attacker who used the over-hit enabled skill on the L2Attackable */
- + private L2Character _overhitAttacker;
- +
- + /** True if a Soul Crystal was successfuly used on the L2Attackable */
- + private boolean _absorbed;
- +
- + /** The table containing all L2PcInstance that successfuly absorbed the soul of this L2Attackable */
- + private final FastMap<L2PcInstance, AbsorberInfo> _absorbersList = new FastMap<L2PcInstance, AbsorberInfo>().setShared(true);
- +
- + /** Have this L2Attackable to drop somethink on DIE? **/
- + private boolean _haveToDrop;
- +
- + /** Have this L2Attackable to reward Exp and SP on Die? **/
- + private boolean _mustGiveExpSp;
- +
- + /**
- + * Constructor of L2Attackable (use L2Character and L2NpcInstance constructor).<BR>
- + * <BR>
- + * <B><U> Actions</U> :</B><BR>
- + * <BR>
- + * <li>Call the L2Character constructor to set the _template of the L2Attackable (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR)</li> <li>Set the name of the L2Attackable</li> <li>Create a RandomAnimation Task that will be launched after the calculated delay if
- + * the server allow it</li><BR>
- + * <BR>
- + * @param objectId Identifier of the object to initialized
- + * @param L2NpcTemplate Template to apply to the NPC
- + */
- + public L2Attackable(int objectId, L2NpcTemplate template)
- + {
- + super(objectId, template);
- + getKnownList(); // init knownlist
- + _haveToDrop = true;
- + _mustGiveExpSp = true;
- + }
- +
- + @Override
- + public AttackableKnownList getKnownList()
- + {
- + if ((super.getKnownList() == null) || !(super.getKnownList() instanceof AttackableKnownList))
- + {
- + setKnownList(new AttackableKnownList(this));
- + }
- + return (AttackableKnownList) super.getKnownList();
- + }
- +
- + /**
- + * Return the L2Character AI of the L2Attackable and if its null create a new one.<BR>
- + * <BR>
- + */
- + @Override
- + public L2CharacterAI getAI()
- + {
- + if (_ai == null)
- + {
- + synchronized (this)
- + {
- + if (_ai == null)
- + {
- + _ai = new L2AttackableAI(new AIAccessor());
- + }
- + }
- + }
- + return _ai;
- + }
- +
- + // get condition to hate, actually isAggressive() is checked
- + // by monster and karma by guards in motheds that overwrite this one.
- + /**
- + * Not used.<BR>
- + * <BR>
- + * @deprecated
- + */
- + @Deprecated
- + public boolean getCondition2(L2Character target)
- + {
- + if ((target instanceof L2FolkInstance) || (target instanceof L2DoorInstance))
- + {
- + return false;
- + }
- +
- + if (target.isAlikeDead() || !isInsideRadius(target, getAggroRange(), false, false) || (Math.abs(getZ() - target.getZ()) > 100))
- + {
- + return false;
- + }
- +
- + return !target.isInvul();
- + }
- +
- + /**
- + * Reduce the current HP of the L2Attackable.<BR>
- + * <BR>
- + * @param damage The HP decrease value
- + * @param attacker The L2Character who attacks
- + */
- + @Override
- + public void reduceCurrentHp(double damage, L2Character attacker)
- + {
- + reduceCurrentHp(damage, attacker, true);
- + }
- +
- + /**
- + * Reduce the current HP of the L2Attackable, update its _aggroList and launch the doDie Task if necessary.<BR>
- + * <BR>
- + * @param i The HP decrease value
- + * @param attacker The L2Character who attacks
- + * @param awake The awake state (If True : stop sleeping)
- + */
- + @Override
- + public void reduceCurrentHp(double damage, L2Character attacker, boolean awake)
- + {
- +
- + if (isEventMob)
- + {
- + return;
- + }
- +
- + // Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList
- + if (attacker != null)
- + {
- + addDamage(attacker, (int) damage);
- + addBufferHate();
- + }
- +
- + // If this L2Attackable is a L2MonsterInstance and it has spawned minions, call its minions to battle
- + if (this instanceof L2MonsterInstance)
- + {
- + L2MonsterInstance master = (L2MonsterInstance) this;
- + if (this instanceof L2MinionInstance)
- + {
- + master = ((L2MinionInstance) this).getLeader();
- + if (!master.isInCombat() && !master.isDead())
- + {
- + master.addDamage(attacker, 1);
- + master.addBufferHate();
- + }
- + }
- + if (master.hasMinions())
- + {
- + master.callMinionsToAssist(attacker);
- + }
- + }
- +
- + // Reduce the current HP of the L2Attackable and launch the doDie Task if necessary
- + super.reduceCurrentHp(damage, attacker, awake);
- + }
- +
- + public synchronized void setHaveToDrop(boolean value)
- + {
- + _haveToDrop = value;
- + }
- +
- + public synchronized boolean getHaveToDrop()
- + {
- + return _haveToDrop;
- + }
- +
- + public synchronized void setMustRewardExpSp(boolean value)
- + {
- + _mustGiveExpSp = value;
- + }
- +
- + public synchronized boolean getMustRewardExpSP()
- + {
- + return _mustGiveExpSp;
- + }
- +
- + /**
- + * Kill the L2Attackable (the corpse disappeared after 7 seconds), distribute rewards (EXP, SP, Drops...) and notify Quest Engine.<BR>
- + * <BR>
- + * <B><U> Actions</U> :</B><BR>
- + * <BR>
- + * <li>Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members</li> <li>Notify the Quest Engine of the L2Attackable death if necessary</li> <li>Kill the L2NpcInstance (the corpse disappeared after 7 seconds)</li><BR>
- + * <BR>
- + * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T GIVE rewards to L2PetInstance</B></FONT><BR>
- + * <BR>
- + * @param killer The L2Character that has killed the L2Attackable
- + */
- + @Override
- + public boolean doDie(L2Character killer)
- + {
- + // Kill the L2NpcInstance (the corpse disappeared after 7 seconds)
- + if (!super.doDie(killer))
- + {
- + return false;
- + }
- +
- + // Enhance soul crystals of the attacker if this L2Attackable had its soul absorbed
- + try
- + {
- +
- + levelSoulCrystals(killer);
- +
- + }
- + catch (Exception e)
- + {
- + _log.log(Level.SEVERE, "", e);
- + }
- +
- + // Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members
- +
- + if (getHaveToDrop() || getMustRewardExpSP())
- + {
- + try
- + {
- + calculateRewards(killer);
- + }
- + catch (Exception e)
- + {
- + _log.log(Level.SEVERE, "", e);
- + }
- + }
- +
- + // Notify the Quest Engine of the L2Attackable death if necessary
- + try
- + {
- + L2PcInstance player = null;
- + if (killer instanceof L2PcInstance)
- + {
- + player = (L2PcInstance) killer;
- + }
- + else if (killer instanceof L2Summon)
- + {
- + player = ((L2Summon) killer).getOwner();
- + }
- +
- + if (player != null)
- + {
- + List<QuestState> questList = new FastList<QuestState>();
- +
- + if (player.getParty() != null)
- + {
- + Map<String, List<QuestState>> tempMap = new FastMap<String, List<QuestState>>();
- +
- + for (L2PcInstance pl : player.getParty().getPartyMembers())
- + {
- + if (pl.getQuestsForKills(this) == null)
- + {
- + continue;
- + }
- +
- + for (QuestState qs : pl.getQuestsForKills(this))
- + {
- + if (qs.getState().isParty())
- + {
- + if (!qs.isCompleted() && !pl.isDead() && Util.checkIfInRange(1400, this, pl, true))
- + {
- + if (this instanceof L2RaidBossInstance)
- + {
- + questList.add(qs);
- + }
- + else
- + {
- + if (tempMap.get(qs.getQuest().getName()) != null)
- + {
- + tempMap.get(qs.getQuest().getName()).add(qs);
- + }
- + else
- + {
- + List<QuestState> tempList = new FastList<QuestState>();
- + tempList.add(qs);
- + tempMap.put(qs.getQuest().getName(), tempList);
- + }
- + }
- + }
- + }
- + else if (pl == player)
- + {
- + questList.add(qs);
- + }
- + }
- + }
- +
- + for (List<QuestState> list : tempMap.values())
- + {
- + questList.add((QuestState) list.toArray()[Rnd.nextInt(list.size())]);
- + }
- + }
- + else
- + {
- + if (player.getQuestsForKills(this) != null)
- + {
- + for (QuestState qs : player.getQuestsForKills(this))
- + {
- + questList.add(qs);
- + }
- + }
- + }
- +
- + for (QuestState qs : questList)
- + {
- + qs.getQuest().notifyKill(this, qs);
- + }
- + }
- + }
- + catch (Exception e)
- + {
- +
- + _log.log(Level.SEVERE, "", e);
- + }
- + return true;
- + }
- +
- + /**
- + * Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members.<BR>
- + * <BR>
- + * <B><U> Actions</U> :</B><BR>
- + * <BR>
- + * <li>Get the L2PcInstance owner of the L2SummonInstance (if necessary) and L2Party in progress</li> <li>Calculate the Experience and SP rewards in function of the level difference</li> <li>Add Exp and SP rewards to L2PcInstance (including Summon penalty) and to Party members in the known area
- + * of the last attacker</li><BR>
- + * <BR>
- + * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T GIVE rewards to L2PetInstance</B></FONT><BR>
- + * <BR>
- + * @param lastAttacker The L2Character that has killed the L2Attackable
- + */
- + private void calculateRewards(L2Character lastAttacker)
- + {
- + if (getAggroList().isEmpty())
- + {
- + return;
- + }
- +
- + if (getMustRewardExpSP())
- + {
- +
- + // Creates an empty list of rewards
- + FastMap<L2Character, RewardInfo> rewards = new FastMap<L2Character, RewardInfo>().setShared(true);
- + int rewardCount = 0;
- +
- + int damage;
- + L2Character attacker;
- + L2Character ddealer;
- + RewardInfo reward;
- +
- + // While Iterating over This Map Removing Object is Not Allowed
- + synchronized (getAggroList())
- + {
- + // Go through the _aggroList of the L2Attackable
- + for (AggroInfo info : getAggroList().values())
- + {
- + if (info == null)
- + {
- + continue;
- + }
- +
- + // Get the L2Character corresponding to this attacker
- + attacker = info.attacker;
- +
- + // Get damages done by this attacker
- + damage = info.damage;
- +
- + // Prevent unwanted behavior
- + if (damage > 1)
- + {
- + if (attacker instanceof L2SummonInstance)
- + {
- + ddealer = ((L2SummonInstance) attacker).getOwner();
- + }
- + else
- + {
- + ddealer = info.attacker;
- + }
- +
- + if (!Util.checkIfInRange(1600, this, ddealer, true))
- + {
- + continue;
- + }
- +
- + // Calculate real damages (Summoners should get own damage plus summon's damage)
- + reward = rewards.get(ddealer);
- +
- + if (reward == null)
- + {
- + reward = new RewardInfo(ddealer, damage);
- + rewardCount++;
- + }
- + else
- + {
- + reward.addDamage(damage);
- + }
- +
- + rewards.put(ddealer, reward);
- + }
- +
- + }
- +
- + }
- +
- + if (!rewards.isEmpty())
- + {
- +
- + L2Party attackerParty;
- +
- + int exp;
- + int levelDiff;
- + int partyDmg;
- + float partyMul;
- + float penalty;
- +
- + RewardInfo reward2;
- + int sp;
- + int[] tmp;
- +
- + for (FastMap.Entry<L2Character, RewardInfo> entry = rewards.head(), end = rewards.tail(); (entry = entry.getNext()) != end;)
- + {
- + if (entry == null)
- + {
- + continue;
- + }
- +
- + reward = entry.getValue();
- + if (reward == null)
- + {
- + continue;
- + }
- +
- + // Penalty applied to the attacker's XP
- + penalty = 0;
- +
- + // Attacker to be rewarded
- + attacker = reward.attacker;
- +
- + // Total amount of damage done
- + damage = reward.dmg;
- +
- + // If the attacker is a Pet, get the party of the owner
- + if (attacker instanceof L2PetInstance)
- + {
- + attackerParty = ((L2PetInstance) attacker).getParty();
- + }
- + else if (attacker instanceof L2PcInstance)
- + {
- + attackerParty = ((L2PcInstance) attacker).getParty();
- + }
- + else
- + {
- + return;
- + }
- +
- + // If this attacker is a L2PcInstance with a summoned L2SummonInstance, get Exp Penalty applied for the current summoned L2SummonInstance
- + if ((attacker instanceof L2PcInstance) && (((L2PcInstance) attacker).getPet() instanceof L2SummonInstance))
- + {
- + penalty = ((L2SummonInstance) ((L2PcInstance) attacker).getPet()).getExpPenalty();
- + }
- +
- + // We must avoid "over damage", if any
- + if (damage > getMaxHp())
- + {
- + damage = getMaxHp();
- + }
- +
- + // If there's NO party in progress
- + if (attackerParty == null)
- + {
- + // Calculate Exp and SP rewards
- + if (attacker.getKnownList().knowsObject(this))
- + {
- + // Calculate the difference of level between this attacker (L2PcInstance or L2SummonInstance owner) and the L2Attackable
- + // mob = 24, atk = 10, diff = -14 (full xp)
- + // mob = 24, atk = 28, diff = 4 (some xp)
- + // mob = 24, atk = 50, diff = 26 (no xp)
- + levelDiff = attacker.getLevel() - getLevel();
- +
- + tmp = calculateExpAndSp(levelDiff, damage);
- + exp = tmp[0];
- + exp *= 1 - penalty;
- + sp = tmp[1];
- +
- + // Check for an over-hit enabled strike
- + if (attacker instanceof L2PcInstance)
- + {
- + L2PcInstance player = (L2PcInstance) attacker;
- + if (isOverhit() && (attacker == getOverhitAttacker()))
- + {
- + player.sendPacket(new SystemMessage(SystemMessage.OVER_HIT));
- + exp += calculateOverhitExp(exp);
- + }
- + }
- +
- + // Distribute the Exp and SP between the L2PcInstance and its L2Summon
- + if (!attacker.isDead())
- + {
- + attacker.addExpAndSp((int) Math.round(attacker.calcStat(Stats.EXPSP_RATE, exp, null, null)), (int) attacker.calcStat(Stats.EXPSP_RATE, sp, null, null));
- + }
- + }
- + }
- + else
- + {
- + // share with party members
- + partyDmg = 0;
- + partyMul = 1.f;
- +
- + // Get all L2Character (including L2Summon) that can be rewarded in the party
- + List<L2Character> rewardedMembers = new FastList<L2Character>();
- +
- + // Go through all L2PcInstance in the party
- + for (L2PcInstance pl : attackerParty.getPartyMembers())
- + {
- + if ((pl == null) || pl.isDead())
- + {
- + continue;
- + }
- +
- + // Get the RewardInfo of this L2PcInstance from L2Attackable rewards
- + reward2 = rewards.get(pl);
- +
- + // If the L2PcInstance is in the L2Attackable rewards add its damages to party damages
- + if (reward2 != null)
- + {
- + if (Util.checkIfInRange(1600, this, pl, true))
- + {
- + // Add L2PcInstance damages to party damages
- + partyDmg += reward2.dmg;
- +
- + rewardedMembers.add(pl);
- + }
- +
- + // Remove the L2PcInstance from the L2Attackable rewards
- + rewards.remove(pl);
- + }
- + else
- + {
- + // Add L2PcInstance of the party (that have attacked or not) to members that can be rewarded if it's not dead
- + // and in range of the monster.
- + if (Util.checkIfInRange(1600, this, pl, true))
- + {
- + rewardedMembers.add(pl);
- + }
- + }
- +
- + L2PlayableInstance summon = pl.getPet();
- + if ((summon != null) && (summon instanceof L2PetInstance))
- + {
- + reward2 = rewards.get(summon);
- + if (reward2 != null)
- + {
- +
- + if (Util.checkIfInRange(1600, this, summon, true))
- + {
- + partyDmg += reward2.dmg;
- + rewardedMembers.add(summon);
- + }
- +
- + // Remove the summon from the L2Attackable rewards
- + rewards.remove(summon);
- + }
- + }
- + }
- +
- + // If the party didn't killed this L2Attackable alone
- + if (partyDmg < getMaxHp())
- + {
- + partyMul = ((float) partyDmg / (float) getMaxHp());
- + }
- +
- + // Avoid "over damage"
- + if (partyDmg > getMaxHp())
- + {
- + partyDmg = getMaxHp();
- + }
- +
- + // Calculate the level difference between Party and L2Attackable
- + levelDiff = attackerParty.getLevel() - getLevel();
- +
- + // Calculate Exp and SP rewards
- + tmp = calculateExpAndSp(levelDiff, partyDmg);
- + exp = tmp[0];
- + sp = tmp[1];
- +
- + exp *= partyMul;
- + sp *= partyMul;
- +
- + // Check for an over-hit enabled strike
- + // (When in party, the over-hit exp bonus is given to the whole party and splitted proportionally through the party members)
- + if (attacker instanceof L2PcInstance)
- + {
- + L2PcInstance player = (L2PcInstance) attacker;
- + if (isOverhit() && (attacker == getOverhitAttacker()))
- + {
- +
- + player.sendPacket(new SystemMessage(SystemMessage.OVER_HIT));
- + exp += calculateOverhitExp(exp);
- + }
- + }
- +
- + // Distribute Experience and SP rewards to L2PcInstance Party members in the known area of the last attacker
- + if (partyDmg > 0)
- + {
- + attackerParty.distributeXpAndSp(exp, sp, rewardedMembers, lastAttacker);
- + }
- + }
- + }
- + }
- +
- + rewards = null;
- + }
- + // Manage Base, Quests and Sweep drops of the L2Attackable
- + if (getHaveToDrop())
- + {
- + doItemDrop(lastAttacker);
- + }
- + // Manage drop of Special Events created by GM for a defined period
- + doEventDrop(lastAttacker);
- + }
- +
- + /**
- + * Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList.<BR>
- + * <BR>
- + * @param attacker The L2Character that gave damages to this L2Attackable
- + * @param damage The number of damages given by the attacker L2Character
- + */
- + public void addDamage(L2Character attacker, int damage)
- + {
- + addDamageHate(attacker, damage, damage);
- + }
- +
- + public void addBufferHate()
- + {
- + L2Character target = getMostHated();
- +
- + if (target == null)
- + {
- + return;
- + }
- +
- + for (L2PcInstance actor : getKnownList().getKnownPlayers().values())
- + {
- + if (actor.isCastingNow() && (target.getLastBuffer() == actor))
- + {
- + int actorLevel = actor.getLevel();
- + double divisor = 0;
- + L2Skill skill = actor.getLastSkillCast();
- +
- + if (actorLevel < 10)
- + {
- + divisor = 15;
- + }
- + else if ((actorLevel > 9) && (actorLevel < 20))
- + {
- + divisor = 11.5;
- + }
- + else if ((actorLevel > 19) && (actorLevel < 30))
- + {
- + divisor = 8.5;
- + }
- + else if ((actorLevel > 29) && (actorLevel < 40))
- + {
- + divisor = 6;
- + }
- + else if ((actorLevel > 39) && (actorLevel < 50))
- + {
- + divisor = 4;
- + }
- + else if ((actorLevel > 49) && (actorLevel < 60))
- + {
- + divisor = 2.5;
- + }
- + else if ((actorLevel > 59) && (actorLevel < 70))
- + {
- + divisor = 1.5;
- + }
- + else if (actorLevel > 69)
- + {
- + divisor = 1;
- + }
- +
- + if ((skill != null) && ((skill.getSkillType() == SkillType.HEAL) || (skill.getSkillType() == SkillType.HEAL_PERCENT) || (skill.getSkillType() == SkillType.MANAHEAL) || (skill.getSkillType() == SkillType.BALANCE_LIFE)))
- + {
- + L2Object[] targetList = skill.getTargetList(actor, true);
- +
- + if ((targetList != null) && (targetList.length != 0) && ((targetList[0] instanceof L2PcInstance) || (targetList[0] instanceof L2SummonInstance) || (targetList[0] instanceof L2PetInstance)))
- + {
- + if ((getMaxHp() / 5) < target.getLastHealAmount())
- + {
- + target.setLastHealAmount((getMaxHp() / 5));
- + }
- +
- + addDamageHate(actor, 0, (int) (target.getLastHealAmount() / divisor));
- +
- + if (Config.DEBUG)
- + {
- + System.out.print("Mob detect heal.\n");
- + }
- + }
- + }
- +
- + if ((skill != null) && ((skill.getSkillType() == SkillType.BUFF) || (skill.getSkillType() == SkillType.HOT)))
- + {
- + L2Object[] targetList = skill.getTargetList(actor, true);
- +
- + if ((targetList != null) && (targetList.length != 0) && ((targetList[0] instanceof L2PcInstance) || (targetList[0] instanceof L2SummonInstance) || (targetList[0] instanceof L2PetInstance)))
- + {
- + addDamageHate(actor, 0, (int) ((skill.getLevel() * getStat().getMpConsume(skill)) / divisor));
- +
- + if (Config.DEBUG)
- + {
- + System.out.print("Mob detect buff.\n");
- + }
- + }
- + }
- +
- + if (((L2PcInstance) target).isInParty() && (skill != null) && (skill.getTargetType() == L2Skill.SkillTargetType.TARGET_PARTY))
- + {
- + List<L2PcInstance> members = ((L2PcInstance) target).getParty().getPartyMembers();
- +
- + for (L2PcInstance member : members)
- + {
- + if (member == actor)
- + {
- + if ((getMaxHp() / 3) < (int) (((getHating(target) - getHating(actor)) + 800) / divisor))
- + {
- + addDamageHate(actor, 0, (getMaxHp() / 3));
- + }
- + else
- + {
- + addDamageHate(actor, 0, (int) (((getHating(target) - getHating(actor)) + 800) / divisor));
- + }
- +
- + if (Config.DEBUG)
- + {
- + System.out.print("Mob detect party cast.\n");
- + }
- + }
- + }
- + }
- + }
- + }
- + }
- +
- + /**
- + * Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList.<BR>
- + * <BR>
- + * @param attacker The L2Character that gave damages to this L2Attackable
- + * @param damage The number of damages given by the attacker L2Character
- + * @param aggro The hate (=damage) given by the attacker L2Character
- + */
- + public void addDamageHate(L2Character attacker, int damage, int aggro)
- + {
- + if (attacker == null)
- + {
- + return;
- + }
- +
- + // Get the AggroInfo of the attacker L2Character from the _aggroList of the L2Attackable
- + AggroInfo ai = getAggroList().get(attacker);
- + if (ai == null)
- +
- + {
- +
- + ai = new AggroInfo(attacker);
- + ai.damage = 0;
- + ai.hate = 0;
- +
- + getAggroList().put(attacker, ai);
- + }
- +
- + // Add new damage and aggro (=damage) to the AggroInfo object
- + ai.damage += damage;
- + ai.hate += aggro;
- +
- + // Set the intention to the L2Attackable to AI_INTENTION_ACTIVE
- + if ((aggro > 0) && (getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE))
- + {
- + getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
- + }
- +
- + // Notify the L2Attackable AI with EVT_ATTACKED
- + if (damage > 0)
- + {
- + getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, attacker);
- + }
- +
- + try
- + {
- + L2PcInstance player = null;
- + if (attacker instanceof L2PcInstance)
- + {
- + player = (L2PcInstance) attacker;
- + }
- + else if (attacker instanceof L2Summon)
- + {
- + player = ((L2Summon) attacker).getOwner();
- + }
- +
- + if (player != null)
- + {
- +
- + if (player.getQuestsForAttacks(this) != null)
- + {
- + for (QuestState st : player.getQuestsForAttacks(this))
- + {
- + st.getQuest().notifyAttack(this, st);
- + }
- + }
- + }
- + }
- + catch (Exception e)
- + {
- + _log.log(Level.SEVERE, "", e);
- + }
- + }
- +
- + /**
- + * Return the most hated L2Character of the L2Attackable _aggroList.<BR>
- + * <BR>
- + */
- + public L2Character getMostHated()
- + {
- + if (getAggroList().isEmpty() || isAlikeDead())
- + {
- + return null;
- + }
- +
- + L2Character mostHated = null;
- + int maxHate = 0;
- +
- + // While Iterating over This Map Removing Object is Not Allowed
- +
- + synchronized (getAggroList())
- + {
- + // Go through the aggroList of the L2Attackable
- + for (AggroInfo ai : getAggroList().values())
- + {
- + if (ai == null)
- + {
- + continue;
- + }
- + if (ai.attacker.isAlikeDead() || !getKnownList().knowsObject(ai.attacker) || !ai.attacker.isVisible())
- + {
- + ai.hate = 0;
- + }
- +
- + if (ai.hate > maxHate)
- + {
- + mostHated = ai.attacker;
- + maxHate = ai.hate;
- + }
- + }
- + }
- +
- + return mostHated;
- + }
- +
- + /**
- + * Return the hate level of the L2Attackable against this L2Character contained in _aggroList.<BR>
- + * <BR>
- + * @param target The L2Character whose hate level must be returned
- + */
- + public int getHating(L2Character target)
- + {
- + if (getAggroList().isEmpty())
- + {
- + return 0;
- + }
- +
- + AggroInfo ai = getAggroList().get(target);
- + if (ai == null)
- + {
- + return 0;
- + }
- +
- + if ((ai.attacker instanceof L2PcInstance) && ((((L2PcInstance) ai.attacker).getInvisible() == 1) || ai.attacker.isInvul()))
- + {
- + // Remove Object Should Use This Method and Can be Blocked While Iterating
- + getAggroList().remove(target);
- + return 0;
- + }
- + if (!ai.attacker.isVisible())
- + {
- + getAggroList().remove(target);
- + return 0;
- + }
- + if (ai.attacker.isAlikeDead())
- + {
- + ai.hate = 0;
- + return 0;
- + }
- +
- + return ai.hate;
- + }
- +
- + /**
- + * Calculates quantity of items for specific drop acording to current situation <br>
- + * @param drop The L2DropData count is being calculated for
- + * @param lastAttacker The L2PcInstance that has killed the L2Attackable
- + * @param deepBlueDrop Factor to divide the drop chance
- + * @param levelModifier level modifier in %'s (will be subtracted from drop chance)
- + */
- + private RewardItem calculateRewardItem(L2PcInstance lastAttacker, L2DropData drop, int levelModifier, boolean isSweep)
- + {
- + // Get default drop chance
- + float dropChance = drop.getChance();
- +
- + float dropItemsRate;
- + if ((this instanceof L2BossInstance) || (this instanceof L2RaidBossInstance))
- + {
- + dropItemsRate = Config.RATE_BOSS_DROP_ITEMS;
- + }
- + else
- + {
- + dropItemsRate = Config.RATE_DROP_ITEMS;
- + }
- +
- + int deepBlueDrop = 1;
- + if (Config.DEEPBLUE_DROP_RULES)
- + {
- + if (levelModifier > 0)
- + {
- + // We should multiply by the server's drop rate, so we always get a low chance of drop for deep blue mobs.
- + // NOTE: This is valid only for adena drops! Others drops will still obey server's rate
- + deepBlueDrop = 3;
- + if (drop.getItemId() == 57)
- + {
- + deepBlueDrop *= (int) dropItemsRate;
- + }
- + }
- + }
- +
- + if (deepBlueDrop == 0)
- + {
- + deepBlueDrop = 1;
- + }
- + // Check if we should apply our maths so deep blue mobs will not drop that easy
- + if (Config.DEEPBLUE_DROP_RULES)
- + {
- + dropChance = ((drop.getChance() - ((drop.getChance() * levelModifier) / 100)) / deepBlueDrop);
- + }
- +
- + // Applies Drop rates
- + if (drop.getItemId() == 57)
- + {
- + dropChance *= Config.RATE_DROP_ADENA;
- + }
- + else if (isSweep)
- + {
- + dropChance *= Config.RATE_DROP_SPOIL;
- + }
- + else
- + {
- + dropChance *= dropItemsRate;
- + }
- +
- + // Round drop chance
- + dropChance = Math.round(dropChance);
- +
- + // Set our limits for chance of drop
- + if (dropChance < 1)
- + {
- + dropChance = 1;
- + // if (drop.getItemId() == 57 && dropChance > L2DropData.MAX_CHANCE) dropChance = L2DropData.MAX_CHANCE; // If item is adena, dont drop multiple time
- + }
- +
- + // Get min and max Item quantity that can be dropped in one time
- + int minCount = drop.getMinDrop();
- + int maxCount = drop.getMaxDrop();
- + int itemCount = 0;
- +
- + // Count and chance adjustment for high rate servers
- + if ((dropChance > L2DropData.MAX_CHANCE) && !Config.PRECISE_DROP_CALCULATION)
- + {
- + int multiplier = (int) dropChance / L2DropData.MAX_CHANCE;
- + if (minCount < maxCount)
- + {
- + itemCount += Rnd.get(minCount * multiplier, maxCount * multiplier);
- + }
- + else if (minCount == maxCount)
- + {
- + itemCount += minCount * multiplier;
- + }
- + else
- + {
- + itemCount += multiplier;
- + }
- +
- + dropChance = dropChance % L2DropData.MAX_CHANCE;
- + }
- +
- + // Check if the Item must be dropped
- + int random = Rnd.get(L2DropData.MAX_CHANCE);
- + while (random < dropChance)
- + {
- + // Get the item quantity dropped
- + if (minCount < maxCount)
- + {
- + itemCount += Rnd.get(minCount, maxCount);
- + }
- + else if (minCount == maxCount)
- + {
- + itemCount += minCount;
- + }
- + else
- + {
- + itemCount++;
- + }
- +
- + // Prepare for next iteration if dropChance > L2DropData.MAX_CHANCE
- + dropChance -= L2DropData.MAX_CHANCE;
- + }
- +
- + if (itemCount > 0)
- + {
- + return new RewardItem(drop.getItemId(), itemCount);
- + }
- + else if ((itemCount == 0) && Config.DEBUG)
- + {
- + _log.fine("Roll produced 0 items to drop...");
- + }
- +
- + return null;
- + }
- +
- + /**
- + * Calculates quantity of items for specific drop CATEGORY according to current situation <br>
- + * Only a max of ONE item from a category is allowed to be dropped.
- + * @param drop The L2DropData count is being calculated for
- + * @param lastAttacker The L2PcInstance that has killed the L2Attackable
- + * @param deepBlueDrop Factor to divide the drop chance
- + * @param levelModifier level modifier in %'s (will be subtracted from drop chance)
- + */
- + private RewardItem calculateCategorizedRewardItem(L2PcInstance lastAttacker, L2DropCategory categoryDrops, int levelModifier)
- + {
- + if (categoryDrops == null)
- + {
- + return null;
- + }
- +
- + // Get default drop chance for the category (that's the sum of chances for all items in the category)
- + // keep track of the base category chance as it'll be used later, if an item is drop from the category.
- + // for everything else, use the total "categoryDropChance"
- + int basecategoryDropChance = categoryDrops.getCategoryChance();
- + int categoryDropChance = basecategoryDropChance;
- + int deepBlueDrop = 1;
- + if (Config.DEEPBLUE_DROP_RULES)
- + {
- + if (levelModifier > 0)
- + {
- + // We should multiply by the server's drop rate, so we always get a low chance of drop for deep blue mobs.
- + // NOTE: This is valid only for adena drops! Others drops will still obey server's rate
- + deepBlueDrop = 3;
- + }
- + }
- +
- + if (deepBlueDrop == 0)
- + {
- + deepBlueDrop = 1;
- + }
- + // Check if we should apply our maths so deep blue mobs will not drop that easy
- + if (Config.DEEPBLUE_DROP_RULES)
- + {
- + categoryDropChance = ((categoryDropChance - ((categoryDropChance * levelModifier) / 100)) / deepBlueDrop);
- + }
- + // Applies Drop rates
- + categoryDropChance *= Config.RATE_DROP_ITEMS;
- + // Round drop chance
- + categoryDropChance = Math.round(categoryDropChance);
- + // Set our limits for chance of drop
- + if (categoryDropChance < 1)
- + {
- + categoryDropChance = 1;
- + }
- + // Check if an Item from this category must be dropped
- + if (Rnd.get(L2DropData.MAX_CHANCE) < categoryDropChance)
- + {
- + L2DropData drop = categoryDrops.dropOne();
- + if (drop == null)
- + {
- + return null;
- + }
- +
- + // Now decide the quantity to drop based on the rates and penalties. To get this value
- + // simply divide the modified categoryDropChance by the base category chance. This
- + // results in a chance that will dictate the drops amounts: for each amount over 100
- + // that it is, it will give another chance to add to the min/max quantities.
- + //
- + // For example, If the final chance is 120%, then the item should drop between
- + // its min and max one time, and then have 20% chance to drop again. If the final
- + // chance is 330%, it will similarly give 3 times the min and max, and have a 30%
- + // chance to give a 4th time.
- + // At least 1 item will be dropped for sure. So the chance will be adjusted to 100%
- + // if smaller.
- +
- + int dropChance = drop.getChance();
- + if (drop.getItemId() == 57)
- + {
- + dropChance *= Config.RATE_DROP_ADENA;
- + }
- + else
- + {
- + dropChance *= Config.RATE_DROP_ITEMS;
- + }
- + dropChance = Math.round(dropChance);
- + if (dropChance < L2DropData.MAX_CHANCE)
- + {
- + dropChance = L2DropData.MAX_CHANCE;
- + }
- +
- + // Get min and max Item quantity that can be dropped in one time
- + int min = drop.getMinDrop();
- + int max = drop.getMaxDrop();
- + // Get the item quantity dropped
- + int itemCount = 0;
- +
- + // Count and chance adjustment for high rate servers
- + if ((dropChance > L2DropData.MAX_CHANCE) && !Config.PRECISE_DROP_CALCULATION)
- + {
- + int multiplier = dropChance / L2DropData.MAX_CHANCE;
- + if (min < max)
- + {
- + itemCount += Rnd.get(min * multiplier, max * multiplier);
- + }
- + else if (min == max)
- + {
- + itemCount += min * multiplier;
- + }
- + else
- + {
- + itemCount += multiplier;
- + }
- + dropChance = dropChance % L2DropData.MAX_CHANCE;
- + }
- + // Check if the Item must be dropped
- + int random = Rnd.get(L2DropData.MAX_CHANCE);
- + while (random < dropChance)
- + {
- + // Get the item quantity dropped
- + if (min < max)
- + {
- + itemCount += Rnd.get(min, max);
- + }
- + else if (min == max)
- + {
- + itemCount += min;
- + }
- + else
- + {
- + itemCount++;
- + }
- +
- + // Prepare for next iteration if dropChance > L2DropData.MAX_CHANCE
- + dropChance -= L2DropData.MAX_CHANCE;
- + }
- +
- + if (itemCount > 0)
- + {
- + return new RewardItem(drop.getItemId(), itemCount);
- + }
- + else if ((itemCount == 0) && Config.DEBUG)
- + {
- + _log.fine("Roll produced 0 items to drop...");
- + }
- + }
- + return null;
- +
- + /*
- + * // Applies Drop rates if (drop.getItemId() == 57) dropChance *= Config.RATE_DROP_ADENA; else if (isSweep) dropChance *= Config.RATE_DROP_SPOIL; else dropChance *= Config.RATE_DROP_ITEMS; // Round drop chance dropChance = Math.round(dropChance); // Set our limits for chance of drop if
- + * (dropChance < 1) dropChance = 1; // if (drop.getItemId() == 57 && dropChance > L2DropData.MAX_CHANCE) dropChance = L2DropData.MAX_CHANCE; // If item is adena, dont drop multiple time // Get min and max Item quantity that can be dropped in one time int minCount = drop.getMinDrop(); int
- + * maxCount = drop.getMaxDrop(); int itemCount = 0; if (itemCount > 0) return new RewardItem(drop.getItemId(), itemCount); else if (itemCount == 0 && Config.DEBUG) _log.fine("Roll produced 0 items to drop..."); return null;
- + */
- + }
- +
- + /**
- + * Calculates the level modifier for drop<br>
- + * @param lastAttacker The L2PcInstance that has killed the L2Attackable
- + */
- + private int calculateLevelModifierForDrop(L2PcInstance lastAttacker)
- + {
- + if (Config.DEEPBLUE_DROP_RULES)
- + {
- + int highestLevel = lastAttacker.getLevel();
- +
- + // Check to prevent very high level player to nearly kill mob and let low level player do the last hit.
- + if ((getAttackByList() != null) && !getAttackByList().isEmpty())
- + {
- + for (L2Character atkChar : getAttackByList())
- + {
- + if ((atkChar != null) && (atkChar.getLevel() > highestLevel))
- + {
- + highestLevel = atkChar.getLevel();
- + }
- + }
- + }
- +
- + // According to official data (Prima), deep blue mobs are 9 or more levels below players
- + if ((highestLevel - 9) >= getLevel())
- + {
- + return ((highestLevel - (getLevel() + 8)) * 9);
- + }
- + }
- +
- + return 0;
- + }
- +
- + public void doItemDrop(L2Character lastAttacker)
- + {
- + doItemDrop(getTemplate(), lastAttacker);
- + }
- +
- + /**
- + * Manage Base, Quests and Special Events drops of L2Attackable (called by calculateRewards).<BR>
- + * <BR>
- + * <B><U> Concept</U> :</B><BR>
- + * <BR>
- + * During a Special Event all L2Attackable can drop extra Items. Those extra Items are defined in the table <B>allNpcDateDrops</B> of the EventDroplist. Each Special Event has a start and end date to stop to drop extra Items automaticaly. <BR>
- + * <BR>
- + * <B><U> Actions</U> : </B><BR>
- + * <BR>
- + * <li>Manage drop of Special Events created by GM for a defined period</li> <li>Get all possible drops of this L2Attackable from L2NpcTemplate and add it Quest drops</li> <li>For each possible drops (base + quests), calculate which one must be dropped (random)</li> <li>Get each Item quantity
- + * dropped (random)</li> <li>Create this or these L2ItemInstance corresponding to each Item Identifier dropped</li> <li>If the autoLoot mode is actif and if the L2Character that has killed the L2Attackable is a L2PcInstance, give this or these Item(s) to the L2PcInstance that has killed the
- + * L2Attackable</li> <li>If the autoLoot mode isn't actif or if the L2Character that has killed the L2Attackable is not a L2PcInstance, add this or these Item(s) in the world as a visible object at the position where mob was last</li><BR>
- + * <BR>
- + * @param lastAttacker The L2Character that has killed the L2Attackable
- + */
- + public void doItemDrop(L2NpcTemplate npcTemplate, L2Character lastAttacker)
- + {
- + L2PcInstance player = null;
- + if (lastAttacker instanceof L2PcInstance)
- + {
- + player = (L2PcInstance) lastAttacker;
- + }
- + else if (lastAttacker instanceof L2Summon)
- + {
- + player = ((L2Summon) lastAttacker).getOwner();
- + }
- +
- + if (player == null)
- + {
- + return; // Don't drop anything if the last attacker or ownere isn't L2PcInstance
- + }
- +
- + int levelModifier = calculateLevelModifierForDrop(player); // level modifier in %'s (will be subtracted from drop chance)
- +
- + // Add Quest drops to the table containing all possible drops of this L2Attackable
- + FastList<L2DropData> questDrops = new FastList<L2DropData>();
- + player.fillQuestDrops(this, questDrops);
- +
- + // First, throw possible quest drops of this L2Attackable
- + for (L2DropData drop : questDrops)
- + {
- + if (drop == null)
- + {
- + continue;
- + }
- +
- + RewardItem item = calculateRewardItem(player, drop, levelModifier, false);
- + if (item == null)
- + {
- + continue;
- + }
- +
- + // Check if the autoLoot mode is active
- + if (Config.AUTO_LOOT)
- + {
- + player.doAutoLoot(this, item); // Give this or these Item(s) to the L2PcInstance that has killed the L2Attackable
- + }
- + else
- + {
- + dropItem(player, item); // drop the item on the ground
- + }
- + }
- +
- + // now throw all categorized drops and handle spoil.
- + for (L2DropCategory cat : npcTemplate.getDropData())
- + {
- + RewardItem item = null;
- + if (cat.isSweep())
- + {
- + // according to sh1ny, seeded mobs CAN be spoiled and swept.
- + if (isSpoil()/* && !isSeeded() */)
- + {
- + FastList<RewardItem> sweepList = new FastList<RewardItem>();
- +
- + for (L2DropData drop : cat.getAllDrops())
- + {
- + item = calculateRewardItem(player, drop, levelModifier, true);
- + if (item == null)
- + {
- + continue;
- + }
- +
- + if (Config.DEBUG)
- + {
- + _log.fine("Item id to spoil: " + item.getItemId() + " amount: " + item.getCount());
- + }
- + sweepList.add(item);
- + }
- +
- + // Set the table _sweepItems of this L2Attackable
- + if (sweepList.size() > 0)
- + {
- + _sweepItems = sweepList.toArray(new RewardItem[sweepList.size()]);
- + }
- + }
- + }
- + else
- + {
- + if (isSeeded())
- + {
- + L2DropData drop = cat.dropSeedAllowedDropsOnly();
- + if (drop == null)
- + {
- + continue;
- + }
- +
- + item = calculateRewardItem(player, drop, levelModifier, false);
- + }
- + else
- + {
- + item = calculateCategorizedRewardItem(player, cat, levelModifier);
- + }
- + if (item != null)
- + {
- + if (Config.DEBUG)
- + {
- + _log.fine("Item id to drop: " + item.getItemId() + " amount: " + item.getCount());
- + }
- +
- + // Check if the autoLoot mode is active
- + if (Config.AUTO_LOOT)
- + {
- + player.doAutoLoot(this, item); // Give this or these Item(s) to the L2PcInstance that has killed the L2Attackable
- + }
- + else
- + {
- + dropItem(player, item); // drop the item on the ground
- + }
- +
- + // Broadcast message if RaidBoss was defeated
- + if (this instanceof L2RaidBossInstance)
- + {
- + SystemMessage sm;
- + sm = new SystemMessage(SystemMessage.S1_DIED_DROPPED_S3_S2);
- + sm.addString(getName());
- + sm.addItemName(item.getItemId());
- + sm.addNumber(item.getCount());
- + broadcastPacket(sm);
- + }
- + }
- + }
- + }
- +
- + }
- +
- + /**
- + * Manage Special Events drops created by GM for a defined period.<BR>
- + * <BR>
- + * <B><U> Concept</U> :</B><BR>
- + * <BR>
- + * During a Special Event all L2Attackable can drop extra Items. Those extra Items are defined in the table <B>allNpcDateDrops</B> of the EventDroplist. Each Special Event has a start and end date to stop to drop extra Items automaticaly. <BR>
- + * <BR>
- + * <B><U> Actions</U> : <I>If an extra drop must be generated</I></B><BR>
- + * <BR>
- + * <li>Get an Item Identifier (random) from the DateDrop Item table of this Event</li> <li>Get the Item quantity dropped (random)</li> <li>Create this or these L2ItemInstance corresponding to this Item Identifier</li> <li>If the autoLoot mode is actif and if the L2Character that has killed the
- + * L2Attackable is a L2PcInstance, give this or these Item(s) to the L2PcInstance that has killed the L2Attackable</li> <li>If the autoLoot mode isn't actif or if the L2Character that has killed the L2Attackable is not a L2PcInstance, add this or these Item(s) in the world as a visible object at
- + * the position where mob was last</li><BR>
- + * <BR>
- + * @param lastAttacker The L2Character that has killed the L2Attackable
- + */
- + public void doEventDrop(L2Character lastAttacker)
- + {
- + L2PcInstance player = null;
- + if (lastAttacker instanceof L2PcInstance)
- + {
- + player = (L2PcInstance) lastAttacker;
- + }
- + else if (lastAttacker instanceof L2Summon)
- + {
- + player = ((L2Summon) lastAttacker).getOwner();
- + }
- +
- + if (player == null)
- + {
- + return; // Don't drop anything if the last attacker or ownere isn't L2PcInstance
- + }
- +
- + if ((player.getLevel() - getLevel()) > 9)
- + {
- + return;
- + }
- +
- + // Go through DateDrop of EventDroplist allNpcDateDrops within the date range
- + for (DateDrop drop : EventDroplist.getInstance().getAllDrops())
- + {
- + if (Rnd.get(L2DropData.MAX_CHANCE) < drop.chance)
- + {
- + RewardItem item = new RewardItem(drop.items[Rnd.get(drop.items.length)], Rnd.get(drop.min, drop.max));
- + if (Config.AUTO_LOOT)
- + {
- + player.doAutoLoot(this, item); // Give this or these Item(s) to the L2PcInstance that has killed the L2Attackable
- + }
- + else
- + {
- + dropItem(player, item); // drop the item on the ground
- + }
- + }
- + }
- + }
- +
- + /**
- + * Drop reward item.<BR>
- + * <BR>
- + */
- + public L2ItemInstance dropItem(L2PcInstance lastAttacker, RewardItem item)
- + {
- + int randDropLim = 70;
- +
- + L2ItemInstance ditem = null;
- + for (int i = 0; i < item.getCount(); i++)
- + {
- + // Randomize drop position
- + int newX = (getX() + Rnd.get((randDropLim * 2) + 1)) - randDropLim;
- + int newY = (getY() + Rnd.get((randDropLim * 2) + 1)) - randDropLim;
- + int newZ = Math.max(getZ(), lastAttacker.getZ()) + 20; // TODO: temp hack, do something nicer when we have geodatas
- +
- + // Init the dropped L2ItemInstance and add it in the world as a visible object at the position where mob was last
- + ditem = ItemTable.getInstance().createItem("Loot", item.getItemId(), item.getCount(), lastAttacker, this);
- + ditem.dropMe(this, newX, newY, newZ);
- +
- + // Add drop to auto destroy item task
- + if (!Config.LIST_PROTECTED_ITEMS.contains(item.getItemId()))
- + {
- + if (Config.AUTODESTROY_ITEM_AFTER > 0)
- + {
- + ItemsAutoDestroy.getInstance().addItem(ditem);
- + }
- + }
- + ditem.setProtected(false);
- + // If stackable, end loop as entire count is included in 1 instance of item
- + if (ditem.isStackable() || !Config.MULTIPLE_ITEM_DROP)
- + {
- + break;
- + }
- + }
- + return ditem;
- + }
- +
- + public L2ItemInstance dropItem(L2PcInstance lastAttacker, int itemId, int itemCount)
- + {
- + return dropItem(lastAttacker, new RewardItem(itemId, itemCount));
- + }
- +
- + /**
- + * Return the active weapon of this L2Attackable (= null).<BR>
- + * <BR>
- + */
- + public L2ItemInstance getActiveWeapon()
- + {
- + return null;
- + }
- +
- + /**
- + * Return True if the _aggroList of this L2Attackable is Empty.<BR>
- + * <BR>
- + */
- + public boolean noTarget()
- + {
- + return getAggroList().isEmpty();
- + }
- +
- + /**
- + * Return True if the _aggroList of this L2Attackable contains the L2Character.<BR>
- + * <BR>
- + * @param player The L2Character searched in the _aggroList of the L2Attackable
- + */
- + public boolean containsTarget(L2Character player)
- + {
- + return getAggroList().containsKey(player);
- + }
- +
- + /**
- + * Clear the _aggroList of the L2Attackable.<BR>
- + * <BR>
- + */
- + public void clearAggroList()
- + {
- + getAggroList().clear();
- +
- + _overhit = false;
- + _overhitDamage = 0;
- + _overhitAttacker = null;
- + }
- +
- + /**
- + * Clears _aggroList hate of the L2Character without removing from the list.<BR>
- + * <BR>
- + */
- + public void clearHating(L2Character target)
- + {
- + if (getAggroList().isEmpty())
- + {
- + return;
- + }
- + AggroInfo ai = getAggroList().get(target);
- + if (ai == null)
- + {
- + return;
- + }
- + ai.hate = 0;
- + }
- +
- + /**
- + * Return True if a Dwarf use Sweep on the L2Attackable and if item can be spoiled.<BR>
- + * <BR>
- + */
- + public boolean isSweepActive()
- + {
- + return _sweepItems != null;
- + }
- +
- + /**
- + * Return table containing all L2ItemInstance that can be spoiled.<BR>
- + * <BR>
- + */
- + public synchronized RewardItem[] takeSweep()
- + {
- + RewardItem[] sweep = _sweepItems;
- +
- + _sweepItems = null;
- +
- + return sweep;
- + }
- +
- + /**
- + * Return table containing all L2ItemInstance that can be harvested.<BR>
- + * <BR>
- + */
- + public synchronized RewardItem[] takeHarvest()
- + {
- + RewardItem[] harvest = _harvestItems;
- + _harvestItems = null;
- + return harvest;
- + }
- +
- + /**
- + * Set the over-hit flag on the L2Attackable.<BR>
- + * <BR>
- + * @param status The status of the over-hit flag
- + */
- + public void overhitEnabled(boolean status)
- + {
- + _overhit = status;
- + }
- +
- + /**
- + * Set the over-hit values like the attacker who did the strike and the ammount of damage done by the skill.<BR>
- + * <BR>
- + * @param attacker The L2Character who hit on the L2Attackable using the over-hit enabled skill
- + * @param damage The ammount of damage done by the over-hit enabled skill on the L2Attackable
- + */
- + public void setOverhitValues(L2Character attacker, double damage)
- + {
- + // Calculate the over-hit damage
- + // Ex: mob had 10 HP left, over-hit skill did 50 damage total, over-hit damage is 40
- + double overhitDmg = ((getCurrentHp() - damage) * (-1));
- + if (overhitDmg < 0)
- + {
- + // we didn't killed the mob with the over-hit strike. (it wasn't really an over-hit strike)
- + // let's just clear all the over-hit related values
- + overhitEnabled(false);
- + _overhitDamage = 0;
- + _overhitAttacker = null;
- + return;
- + }
- + overhitEnabled(true);
- + _overhitDamage = overhitDmg;
- + _overhitAttacker = attacker;
- + }
- +
- + /**
- + * Return the L2Character who hit on the L2Attackable using an over-hit enabled skill.<BR>
- + * <BR>
- + * @return L2Character attacker
- + */
- + public L2Character getOverhitAttacker()
- + {
- + return _overhitAttacker;
- + }
- +
- + /**
- + * Return the ammount of damage done on the L2Attackable using an over-hit enabled skill.<BR>
- + * <BR>
- + * @return double damage
- + */
- + public double getOverhitDamage()
- + {
- + return _overhitDamage;
- + }
- +
- + /**
- + * Return True if the L2Attackable was hit by an over-hit enabled skill.<BR>
- + * <BR>
- + */
- + public boolean isOverhit()
- + {
- + return _overhit;
- + }
- +
- + /**
- + * Activate the absorbed soul condition on the L2Attackable.<BR>
- + * <BR>
- + */
- + public void absorbSoul()
- + {
- + _absorbed = true;
- + }
- +
- + /**
- + * Return True if the L2Attackable had his soul absorbed.<BR>
- + * <BR>
- + */
- + public boolean isAbsorbed()
- + {
- + return _absorbed;
- + }
- +
- + /**
- + * Adds an attacker that successfully absorbed the soul of this L2Attackable into the _absorbersList.<BR>
- + * <BR>
- + * params: attacker - a valid L2PcInstance condition - an integer indicating the event when mob dies. This should be: = 0 - "the crystal scatters"; = 1 - "the crystal failed to absorb. nothing happens"; = 2 - "the crystal resonates because you got more than 1 crystal on you"; = 3 -
- + * "the crystal cannot absorb the soul because the mob level is too low"; = 4 - "the crystal successfuly absorbed the soul";
- + */
- + public void addAbsorber(L2PcInstance attacker, int crystalId)
- + {
- + // This just works for targets like L2MonsterInstance
- + if (!(this instanceof L2MonsterInstance))
- + {
- + return;
- + }
- +
- + // The attacker must not be null
- + if (attacker == null)
- + {
- + return;
- + }
- +
- + // This L2Attackable must be of one type in the _absorbingMOBS_levelXX tables.
- + // OBS: This is done so to avoid triggering the absorbed conditions for mobs that can't be absorbed.
- + if (getAbsorbLevel() == 0)
- + {
- + return;
- + }
- +
- + // If we have no _absorbersList initiated, do it
- + AbsorberInfo ai = _absorbersList.get(attacker);
- +
- + // If the L2Character attacker isn't already in the _absorbersList of this L2Attackable, add it
- + if (ai == null)
- + {
- + ai = new AbsorberInfo(attacker, crystalId, getCurrentHp());
- + _absorbersList.put(attacker, ai);
- + }
- + else
- + {
- + ai.absorber = attacker;
- + ai.crystalId = crystalId;
- + ai.absorbedHP = getCurrentHp();
- + }
- +
- + // Set this L2Attackable as absorbed
- + absorbSoul();
- + }
- +
- + /**
- + * Calculate the leveling chance of Soul Crystals based on the attacker that killed this L2Attackable
- + * @param attacker The player that last killed this L2Attackable $ Rewrite 06.12.06 - Yesod
- + */
- + private void levelSoulCrystals(L2Character attacker)
- + {
- + // Only L2PcInstance can absorb a soul
- + if (!(attacker instanceof L2PcInstance))
- + {
- + resetAbsorbList();
- + return;
- + }
- +
- + int maxAbsorbLevel = getAbsorbLevel();
- + int minAbsorbLevel = 0;
- +
- + // If this is not a valid L2Attackable, clears the _absorbersList and just return
- + if (maxAbsorbLevel == 0)
- + {
- + resetAbsorbList();
- + return;
- + }
- + // All boss mobs with maxAbsorbLevel 13 have minAbsorbLevel of 12 else 10
- + if (maxAbsorbLevel > 10)
- + {
- + minAbsorbLevel = maxAbsorbLevel > 12 ? 12 : 10;
- + }
- +
- + // Init some useful vars
- + boolean isSuccess = true;
- + boolean doLevelup = true;
- + boolean isBossMob = maxAbsorbLevel > 10 ? true : false;
- +
- + L2PcInstance killer = (L2PcInstance) attacker;
- +
- + // If this mob is a boss, then skip some checkings
- + if (!isBossMob)
- + {
- + // Fail if this L2Attackable isn't absorbed or there's no one in its _absorbersList
- + if (!isAbsorbed() /* || _absorbersList == null */)
- + {
- + resetAbsorbList();
- + return;
- + }
- +
- + // Fail if the killer isn't in the _absorbersList of this L2Attackable and mob is not boss
- + AbsorberInfo ai = _absorbersList.get(killer);
- + if ((ai == null) || (ai.absorber.getObjectId() != killer.getObjectId()))
- + {
- + isSuccess = false;
- + }
- +
- + // Check if the soul crystal was used when HP of this L2Attackable wasn't higher than half of it
- + if ((ai != null) && (ai.absorbedHP > (getMaxHp() / 2)))
- + {
- + isSuccess = false;
- + }
- +
- + if (!isSuccess)
- + {
- + resetAbsorbList();
- + return;
- + }
- + }
- +
- + // ********
- + String[] crystalNFO = null;
- + String crystalNME = "";
- +
- + int dice = Rnd.get(100);
- + int crystalQTY = 0;
- + int crystalLVL = 0;
- + int crystalOLD = 0;
- + int crystalNEW = 0;
- +
- + // ********
- + // Now we have four choices:
- + // 1- The Monster level is too low for the crystal. Nothing happens.
- + // 2- Everything is correct, but it failed. Nothing happens. (57.5%)
- + // 3- Everything is correct, but it failed. The crystal scatters. A sound event is played. (10%)
- + // 4- Everything is correct, the crystal level up. A sound event is played. (32.5%)
- +
- + List<L2PcInstance> players = new FastList<L2PcInstance>();
- +
- + if (isBossMob && killer.isInParty())
- + {
- + players = killer.getParty().getPartyMembers();
- + }
- + else
- + {
- + players.add(killer);
- + }
- +
- + for (L2PcInstance player : players)
- + {
- + if (player == null)
- + {
- + continue;
- + }
- +
- + QuestState qs = player.getQuestState("350_EnhanceYourWeapon");
- + if ((qs == null) || !qs.isStarted())
- + {
- + continue;
- + }
- +
- + crystalQTY = 0;
- +
- + L2ItemInstance[] inv = player.getInventory().getItems();
- + for (L2ItemInstance item : inv)
- + {
- + int itemId = item.getItemId();
- + for (int id : SoulCrystal.SoulCrystalTable)
- + {
- + // Find any of the 39 possible crystals.
- + if (id == itemId)
- + {
- + crystalQTY++;
- + // Keep count but make sure the player has no more than 1 crystal
- + if (crystalQTY > 1)
- + {
- + isSuccess = false;
- + break;
- + }
- +
- + // Validate if the crystal has already leveled
- + if ((id != SoulCrystal.RED_NEW_CRYSTAL) && (id != SoulCrystal.GRN_NEW_CYRSTAL) && (id != SoulCrystal.BLU_NEW_CRYSTAL))
- + {
- + try
- + {
- + if (item.getItem().getName().contains("Grade"))
- + {
- + // Split the name of the crystal into 'name' & 'level'
- + crystalNFO = item.getItem().getName().trim().replace(" Grade ", "-").split("-");
- + // Set Level to 13
- + crystalLVL = 13;
- + // Get Name
- + crystalNME = crystalNFO[0].toLowerCase();
- + }
- + else
- + {
- + // Split the name of the crystal into 'name' & 'level'
- + crystalNFO = item.getItem().getName().trim().replace(" Stage ", "").split("-");
- + // Get Level
- + crystalLVL = Integer.parseInt(crystalNFO[1].trim());
- + // Get Name
- + crystalNME = crystalNFO[0].toLowerCase();
- + }
- + // Allocate current and levelup ids' for higher level crystals
- + if (crystalLVL > 9)
- + {
- + for (int[] element : SoulCrystal.HighSoulConvert)
- + {
- + // Get the next stage above 10 using array.
- + if (id == element[0])
- + {
- + crystalNEW = element[1];
- + break;
- + }
- + }
- + }
- + else
- + {
- + crystalNEW = id + 1;
- + }
- + }
- + catch (NumberFormatException nfe)
- + {
- + _log.log(Level.WARNING, "An attempt to identify a soul crystal failed, " + "verify the names have not changed in etcitem " + "table.", nfe);
- +
- + player.sendMessage("There has been an error handling your soul crystal." + " Please notify your server admin.");
- +
- + isSuccess = false;
- + break;
- + }
- + catch (Exception e)
- + {
- + e.printStackTrace();
- + isSuccess = false;
- + break;
- + }
- + }
- + else
- + {
- + crystalNME = item.getItem().getName().toLowerCase().trim();
- + crystalNEW = id + 1;
- + }
- +
- + // Done
- + crystalOLD = id;
- + break;
- + }
- + }
- + if (!isSuccess)
- + {
- + break;
- + }
- + }
- +
- + // If the crystal level is way too high for this mob, say that we can't increase it
- + if ((crystalLVL < minAbsorbLevel) || (crystalLVL >= maxAbsorbLevel))
- + {
- + doLevelup = false;
- + }
- +
- + // The player doesn't have any crystals with him get to the next player.
- + if ((crystalQTY < 1) || (crystalQTY > 1) || !isSuccess || !doLevelup)
- + {
- + // Too many crystals in inventory.
- + if (crystalQTY > 1)
- + {
- + player.sendPacket(new SystemMessage(SystemMessage.SOUL_CRYSTAL_ABSORBING_FAILED_RESONATION));
- + }
- + // The soul crystal stage of the player is way too high
- + else if (!doLevelup && (crystalQTY > 0))
- + {
- + player.sendPacket(new SystemMessage(SystemMessage.SOUL_CRYSTAL_ABSORBING_REFUSED));
- + }
- +
- + crystalQTY = 0;
- + continue;
- + }
- +
- + // Ember and Anakazel(78) are not 100% success rate and each individual
- + // member of the party has a failure rate on leveling.
- + if (isBossMob && ((getNpcId() == 10319) || (getNpcId() == 10338)))
- + {
- + doLevelup = false;
- + }
- +
- + // If succeeds or it is a boss mob, level up the crystal.
- + if ((isBossMob && doLevelup) || (dice <= SoulCrystal.LEVEL_CHANCE))
- + {
- + // Give staged crystal
- + exchangeCrystal(player, crystalOLD, crystalNEW, false);
- + }
- + // If true and not a boss mob, break the crystal.
- + else if (!isBossMob && (dice >= (100.0 - SoulCrystal.BREAK_CHANCE)))
- + {
- + // Remove current crystal an give a broken open.
- + if (crystalNME.startsWith("red"))
- + {
- + exchangeCrystal(player, crystalOLD, SoulCrystal.RED_BROKEN_CRYSTAL, true);
- + }
- + else if (crystalNME.startsWith("gre"))
- + {
- + exchangeCrystal(player, crystalOLD, SoulCrystal.GRN_BROKEN_CYRSTAL, true);
- + }
- + else if (crystalNME.startsWith("blu"))
- + {
- + exchangeCrystal(player, crystalOLD, SoulCrystal.BLU_BROKEN_CRYSTAL, true);
- + }
- +
- + resetAbsorbList();
- + }
- + else
- + {
- + player.sendPacket(new SystemMessage(SystemMessage.SOUL_CRYSTAL_ABSORBING_FAILED));
- + }
- + }
- + }
- +
- + private void exchangeCrystal(L2PcInstance player, int takeid, int giveid, boolean broke)
- + {
- + L2ItemInstance Item = player.getInventory().destroyItemByItemId("SoulCrystal", takeid, 1, player, this);
- + if (Item != null)
- + {
- + // Prepare inventory update packet
- + InventoryUpdate playerIU = new InventoryUpdate();
- + playerIU.addRemovedItem(Item);
- +
- + // Add new crystal to the killer's inventory
- + Item = player.getInventory().addItem("SoulCrystal", giveid, 1, player, this);
- + playerIU.addItem(Item);
- +
- + // Send a sound event and text message to the player
- + if (broke)
- + {
- + player.sendPacket(new SystemMessage(SystemMessage.SOUL_CRYSTAL_BROKE));
- + }
- + else
- + {
- + player.sendPacket(new SystemMessage(SystemMessage.SOUL_CRYSTAL_ABSORBING_SUCCEEDED));
- + }
- +
- + // Send system message
- + SystemMessage sms = new SystemMessage(SystemMessage.EARNED_ITEM);
- + sms.addItemName(giveid);
- + player.sendPacket(sms);
- +
- + // Send inventory update packet
- + player.sendPacket(playerIU);
- + }
- + }
- +
- + private void resetAbsorbList()
- + {
- + _absorbed = false;
- + _absorbersList.clear();
- + }
- +
- + /**
- + * Calculate the Experience and SP to distribute to attacker (L2PcInstance, L2SummonInstance or L2Party) of the L2Attackable.<BR>
- + * <BR>
- + * @param diff The difference of level between attacker (L2PcInstance, L2SummonInstance or L2Party) and the L2Attackable
- + * @param damage The damages given by the attacker (L2PcInstance, L2SummonInstance or L2Party)
- + */
- + private int[] calculateExpAndSp(int diff, int damage)
- + {
- + long xp;
- + long sp;
- +
- + if (diff < -5)
- + {
- + diff = -5; // makes possible to use ALT_GAME_EXPONENT configuration
- + }
- + xp = ((long) getExpReward() * damage) / getMaxHp();
- + if (Config.ALT_GAME_EXPONENT_XP != 0)
- + {
- + xp *= Math.pow(2., -diff / Config.ALT_GAME_EXPONENT_XP);
- + }
- +
- + sp = ((long) getSpReward() * damage) / getMaxHp();
- + if (Config.ALT_GAME_EXPONENT_SP != 0)
- + {
- + sp *= Math.pow(2., -diff / Config.ALT_GAME_EXPONENT_SP);
- + }
- +
- + if ((Config.ALT_GAME_EXPONENT_XP == 0) && (Config.ALT_GAME_EXPONENT_SP == 0))
- + {
- + // deep blue mob, is more than 8 levels below attacker lvl
- + if (diff > 8)
- + {
- + xp = 0;
- + sp = 0;
- + }
- + // green or light blue mob, is 6 to 8 levels below attacker lvl
- + else if (diff > 5)
- + {
- + xp -= (diff * xp) / 10;
- + sp -= (diff * sp) / 10;
- + }
- +
- + if (xp <= 0)
- + {
- + xp = 0;
- + sp = 0;
- + }
- + else if (sp <= 0)
- + {
- + sp = 0;
- + }
- + }
- +
- + int[] tmp =
- + {
- + (int) xp,
- + (int) sp
- + };
- +
- + return tmp;
- + }
- +
- + public int calculateOverhitExp(int normalExp)
- + {
- + // Get the percentage based on the total of extra (over-hit) damage done relative to the total (maximum) ammount of HP on the L2Attackable
- + double overhitPercentage = ((getOverhitDamage() * 100) / getMaxHp());
- +
- + // Over-hit damage percentages are limited to 25% max
- + if (overhitPercentage > 25)
- + {
- + overhitPercentage = 25;
- + }
- +
- + // Get the overhit exp bonus according to the above over-hit damage percentage
- + // (1/1 basis - 13% of over-hit damage, 13% of extra exp is given, and so on...)
- + double overhitExp = ((overhitPercentage / 100) * normalExp);
- +
- + // Return the rounded ammount of exp points to be added to the player's normal exp reward
- + int bonusOverhit = (int) Math.round(overhitExp);
- + return bonusOverhit;
- + }
- +
- + /**
- + * Return True.<BR>
- + * <BR>
- + */
- + @Override
- + public boolean isAttackable()
- + {
- + return true;
- + }
- +
- + @Override
- + public void onSpawn()
- + {
- + super.onSpawn();
- + // Clear mob spoil,seed
- + setSpoil(false);
- +
- + // Clear all aggro char from list
- + clearAggroList();
- + // Clear Harvester Reward List
- + _harvestItems = null;
- + // Clear mod Seeded stat
- + setSeeded(false);
- + // Clear overhit value
- + overhitEnabled(false);
- +
- + _sweepItems = null;
- + resetAbsorbList();
- +
- + setWalking();
- +
- + // check the region where this mob is, do not activate the AI if region is inactive.
- + if (!isInActiveRegion())
- + {
- + if (this instanceof L2SiegeGuardInstance)
- + {
- + ((L2SiegeGuardAI) getAI()).stopAITask();
- + }
- + else
- + {
- + ((L2AttackableAI) getAI()).stopAITask();
- + }
- + }
- + }
- +
- + public void setSeeded()
- + {
- + if ((_seedType != 0) && (_seeder != null))
- + {
- + setSeeded(_seedType, _seeder.getLevel());
- + }
- + }
- +
- + public void setSeeded(int id, L2PcInstance seeder)
- + {
- + if (!_seeded)
- + {
- + _seedType = id;
- + _seeder = seeder;
- + }
- + }
- +
- + public void setSeeded(int id, int seederLvl)
- + {
- + _seeded = true;
- + _seedType = id;
- + int count = 1;
- +
- + Map<Integer, L2Skill> skills = getTemplate().getSkills();
- + if (skills != null)
- + {
- + for (int skillId : skills.keySet())
- + {
- + switch (skillId)
- + {
- + case 4303: // Strong type x2
- + count *= 2;
- + break;
- + case 4304: // Strong type x3
- + count *= 3;
- + break;
- + case 4305: // Strong type x4
- + count *= 4;
- + break;
- + case 4306: // Strong type x5
- + count *= 5;
- + break;
- + case 4307: // Strong type x6
- + count *= 6;
- + break;
- + case 4308: // Strong type x7
- + count *= 7;
- + break;
- + case 4309: // Strong type x8
- + count *= 8;
- + break;
- + case 4310: // Strong type x9
- + count *= 9;
- + break;
- + }
- + }
- + }
- +
- + int diff = (getLevel() - (L2Manor.getInstance().getSeedLevel(_seedType) - 5));
- +
- + // hi-lvl mobs bonus
- + if (diff > 0)
- + {
- + count += diff;
- + }
- +
- + FastList<RewardItem> harvested = new FastList<RewardItem>();
- +
- + harvested.add(new RewardItem(L2Manor.getInstance().getCropType(_seedType), count * Config.RATE_DROP_MANOR));
- +
- + _harvestItems = harvested.toArray(new RewardItem[harvested.size()]);
- + }
- +
- + public void setSeeded(boolean seeded)
- + {
- + _seeded = seeded;
- + }
- +
- + public L2PcInstance getSeeder()
- + {
- + return _seeder;
- + }
- +
- + public int getSeedType()
- + {
- + return _seedType;
- + }
- +
- + public boolean isSeeded()
- + {
- + return _seeded;
- + }
- +
- + private int getAbsorbLevel()
- + {
- + return getTemplate().absorb_level;
- + }
- +
- + /**
- + * Check if the server allows Random Animation.<BR>
- + * <BR>
- + */
- + @Override
- + public boolean hasRandomAnimation()
- + {
- + return ((Config.MAX_MONSTER_ANIMATION > 0) && !(this instanceof L2BossInstance));
- + }
- }
- \ No newline at end of file
- Index: java/net/sf/l2j/gameserver/model/actor/instance/L2NpcInstance.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/model/actor/instance/L2NpcInstance.java (revision 355)
- +++ java/net/sf/l2j/gameserver/model/actor/instance/L2NpcInstance.java (working copy)
- @@ -25,7 +25,6 @@
- import javolution.lang.TextBuilder;
- import javolution.util.FastList;
- -
- import net.sf.l2j.Config;
- import net.sf.l2j.gameserver.ClientThread;
- import net.sf.l2j.gameserver.Olympiad;
- @@ -49,6 +48,7 @@
- import net.sf.l2j.gameserver.model.L2Attackable;
- import net.sf.l2j.gameserver.model.L2Character;
- import net.sf.l2j.gameserver.model.L2Clan;
- +import net.sf.l2j.gameserver.model.L2DropCategory;
- import net.sf.l2j.gameserver.model.L2DropData;
- import net.sf.l2j.gameserver.model.L2ItemInstance;
- import net.sf.l2j.gameserver.model.L2Multisell;
- @@ -88,1411 +88,1558 @@
- import net.sf.l2j.gameserver.templates.L2Weapon;
- /**
- - * This class represents a Non-Player-Character in the world. It can be a monster or a friendly character.
- - * It also uses a template to fetch some static values. The templates are hardcoded in the client, so we can rely on them.<BR><BR>
- - *
- - * L2Character :<BR><BR>
- - * <li>L2Attackable</li>
- - * <li>L2BoxInstance</li>
- - * <li>L2FolkInstance</li>
- - *
- + * This class represents a Non-Player-Character in the world. It can be a monster or a friendly character. It also uses a template to fetch some static values. The templates are hardcoded in the client, so we can rely on them.<BR>
- + * <BR>
- + * L2Character :<BR>
- + * <BR>
- + * <li>L2Attackable</li> <li>L2BoxInstance</li> <li>L2FolkInstance</li>
- * @version $Revision: 1.32.2.7.2.24 $ $Date: 2005/04/11 10:06:09 $
- */
- public class L2NpcInstance extends L2Character
- {
- - //private static Logger _log = Logger.getLogger(L2NpcInstance.class.getName());
- -
- - /** The interaction distance of the L2NpcInstance(is used as offset in MovetoLocation method) */
- - public static final int INTERACTION_DISTANCE = 150;
- -
- - /** The L2Spawn object that manage this L2NpcInstance */
- - private L2Spawn _spawn;
- -
- - /** The flag to specify if this L2NpcInstance is busy */
- - private boolean _IsBusy = false;
- -
- - /** The busy message for this L2NpcInstance */
- - private String _BusyMessage = "";
- -
- - /** True if endDecayTask has already been called */
- - volatile boolean _isDecayed = false;
- -
- - /** True if a Dwarf has used Spoil on this L2NpcInstance */
- - private boolean _IsSpoil = false;
- -
- -
- -
- - /** The castle index in the array of L2Castle this L2NpcInstance belongs to */
- - private int _castleIndex = -2;
- -
- -
- - public boolean isEventMob = false;
- -
- -
- - private boolean _isInTown = false;
- -
- - private int _isSpoiledBy = 0;
- -
- - protected RandomAnimationTask _rAniTask = null;
- -
- - private int _currentCollisionHeight;
- - private int _currentCollisionRadius;
- -
- - /** Task launching the function onRandomAnimation() */
- - protected class RandomAnimationTask implements Runnable
- - {
- - public void run()
- - {
- - try
- - {
- - if (this != _rAniTask)
- - return;
- -
- - if (L2NpcInstance.this instanceof L2Attackable)
- - {
- - if (getAI().getIntention() != AI_INTENTION_ACTIVE)
- - return;
- - }
- -
- - else
- - {
- - if (!isInActiveRegion())
- - return;
- - }
- -
- - if (!(isDead() || isStunned() || isSleeping() || isParalyzed()))
- - onRandomAnimation();
- -
- - startRandomAnimationTimer();
- - }
- - catch (Throwable t) {}
- - }
- - }
- -
- -
- - /**
- - * Send a packet SocialAction to all L2PcInstance in the _KnownPlayers of the L2NpcInstance and create a new RandomAnimation Task.<BR><BR>
- - */
- - public void onRandomAnimation()
- - {
- - // Send a packet SocialAction to all L2PcInstance in the _KnownPlayers of the L2NpcInstance
- - SocialAction sa = new SocialAction(getObjectId(), Rnd.get(1, 3));
- - broadcastPacket(sa);
- - }
- -
- - /**
- - * Create a RandomAnimation Task that will be launched after the calculated delay.<BR><BR>
- - */
- - public void startRandomAnimationTimer()
- - {
- - if (!hasRandomAnimation())
- - return;
- -
- - int minWait = this instanceof L2Attackable ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION;
- - int maxWait = this instanceof L2Attackable ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION;
- -
- - // Calculate the delay before the next animation
- - int interval = Rnd.get(minWait, maxWait) * 1000;
- -
- - // Create a RandomAnimation Task that will be launched after the calculated delay
- - _rAniTask = new RandomAnimationTask();
- - ThreadPoolManager.getInstance().scheduleGeneral(_rAniTask, interval);
- - }
- -
- - /**
- - * Check if the server allows Random Animation.<BR><BR>
- - */
- - public boolean hasRandomAnimation()
- - {
- - return (Config.MAX_NPC_ANIMATION > 0);
- - }
- -
- - /**
- - * Constructor of L2NpcInstance (use L2Character constructor).<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Call the L2Character constructor to set the _template of the L2Character (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR) </li>
- - * <li>Set the name of the L2Character</li>
- - * <li>Create a RandomAnimation Task that will be launched after the calculated delay if the server allow it </li><BR><BR>
- - *
- - * @param objectId Identifier of the object to initialized
- - * @param template The L2NpcTemplate to apply to the NPC
- - *
- - */
- - public L2NpcInstance(int objectId, L2NpcTemplate template)
- - {
- - // Call the L2Character constructor to set the _template of the L2Character, copy skills from template to object
- - // and link _calculators to NPC_STD_CALCULATOR
- - super(objectId, template);
- - getKnownList();
- - getStat();
- - getStatus();
- -
- - super.initCharStatusUpdateValues(); // init status update values
- -
- - _currentCollisionHeight = getTemplate().collisionHeight;
- - _currentCollisionRadius = getTemplate().collisionRadius;
- -
- - if (template == null)
- - {
- - _log.severe("No template for Npc. Please check your datapack is setup correctly.");
- - return;
- - }
- -
- - // Set the name of the L2Character
- - setName(template.name);
- - }
- -
- - public NpcKnownList getKnownList()
- - {
- - if (super.getKnownList() == null || !(super.getKnownList() instanceof NpcKnownList))
- - setKnownList(new NpcKnownList(this));
- - return (NpcKnownList)super.getKnownList();
- - }
- -
- - public NpcStat getStat()
- - {
- - if (super.getStat() == null || !(super.getStat() instanceof NpcStat))
- - setStat(new NpcStat(this));
- - return (NpcStat)super.getStat();
- - }
- -
- - public NpcStatus getStatus()
- - {
- - if (super.getStatus() == null || !(super.getStatus() instanceof NpcStatus))
- - setStatus(new NpcStatus(this));
- - return (NpcStatus)super.getStatus();
- - }
- -
- -
- - /** Return the L2NpcTemplate of the L2NpcInstance. */
- - public final L2NpcTemplate getTemplate()
- - {
- - return (L2NpcTemplate)super.getTemplate();
- - }
- -
- - /**
- - * Return the generic Identifier of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
- - */
- - public int getNpcId()
- - {
- - return getTemplate().npcId;
- - }
- -
- - public boolean isAttackable()
- - {
- - return true;
- - }
- -
- - /**
- - * Return the faction Identifier of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
- - *
- - * <B><U> Concept</U> :</B><BR><BR>
- - * If a NPC belows to a Faction, other NPC of the faction inside the Faction range will help it if it's attacked<BR><BR>
- - *
- - */
- - public final String getFactionId()
- - {
- - return getTemplate().factionId;
- - }
- -
- - /**
- - * Return the Level of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
- - */
- - public final int getLevel()
- - {
- - return getTemplate().level;
- - }
- -
- - /**
- - * Return True if the L2NpcInstance is agressive (ex : L2MonsterInstance in function of aggroRange).<BR><BR>
- - */
- - public boolean isAggressive()
- - {
- - return false;
- - }
- -
- - /**
- - * Return the Aggro Range of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
- - */
- - public int getAggroRange()
- - {
- - return getTemplate().aggroRange;
- - }
- -
- - /**
- - * Return the Faction Range of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
- - */
- - public int getFactionRange()
- - {
- - return getTemplate().factionRange;
- - }
- -
- - /**
- - * Return True if this L2NpcInstance is undead in function of the L2NpcTemplate.<BR><BR>
- - */
- - public boolean isUndead()
- - {
- - return getTemplate().isUndead;
- - }
- -
- - /**
- - * Send a packet NpcInfo with state of abnormal effect to all L2PcInstance in the _KnownPlayers of the L2NpcInstance.<BR><BR>
- - */
- - public void updateAbnormalEffect()
- - {
- -
- -
- - // Send a Server->Client packet NpcInfo with state of abnormal effect to all L2PcInstance in the _KnownPlayers of the L2NpcInstance
- - for (L2PcInstance player : getKnownList().getKnownPlayers().values())
- - {
- - if (player != null)
- - {
- - if (getRunSpeed() == 0)
- - player.sendPacket(new ServerObjectInfo(this, player));
- - else
- - player.sendPacket(new NpcInfo(this, player));
- - }
- - }
- - }
- -
- - /**
- - * Return the distance under which the object must be add to _knownObject in function of the object type.<BR><BR>
- - *
- - * <B><U> Values </U> :</B><BR><BR>
- - * <li> object is a L2FolkInstance : 0 (don't remember it) </li>
- - * <li> object is a L2Character : 0 (don't remember it) </li>
- - * <li> object is a L2PlayableInstance : 1500 </li>
- - * <li> others : 500 </li><BR><BR>
- - *
- - * <B><U> Overriden in </U> :</B><BR><BR>
- - * <li> L2Attackable</li><BR><BR>
- - *
- - * @param object The Object to add to _knownObject
- - *
- - */
- - public int getDistanceToWatchObject(L2Object object)
- - {
- - if (object instanceof L2FestivalGuideInstance)
- - return 10000;
- -
- - if (object instanceof L2FolkInstance || !(object instanceof L2Character))
- - return 0;
- -
- - if (object instanceof L2PlayableInstance)
- - return 1500;
- -
- - return 500;
- - }
- -
- - /**
- - * Return the distance after which the object must be remove from _knownObject in function of the object type.<BR><BR>
- - *
- - * <B><U> Values </U> :</B><BR><BR>
- - * <li> object is not a L2Character : 0 (don't remember it) </li>
- - * <li> object is a L2FolkInstance : 0 (don't remember it)</li>
- - * <li> object is a L2PlayableInstance : 3000 </li>
- - * <li> others : 1000 </li><BR><BR>
- - *
- - * <B><U> Overriden in </U> :</B><BR><BR>
- - * <li> L2Attackable</li><BR><BR>
- - *
- - * @param object The Object to remove from _knownObject
- - *
- - */
- - public int getDistanceToForgetObject(L2Object object)
- - {
- - return 2*getDistanceToWatchObject(object);
- - }
- -
- - /**
- - * Return False.<BR><BR>
- - *
- - * <B><U> Overriden in </U> :</B><BR><BR>
- - * <li> L2MonsterInstance : Check if the attacker is not another L2MonsterInstance</li>
- - * <li> L2PcInstance</li><BR><BR>
- - */
- - public boolean isAutoAttackable(@SuppressWarnings("unused") L2Character attacker)
- - {
- - return false;
- - }
- -
- - /**
- - * Return the Identifier of the item in the left hand of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
- - */
- - public int getLeftHandItem()
- - {
- - return getTemplate().lhand;
- - }
- -
- - /**
- - * Return the Identifier of the item in the right hand of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
- - */
- - public int getRightHandItem()
- - {
- - return getTemplate().rhand;
- - }
- -
- - /**
- - * Return True if this L2NpcInstance has drops that can be sweeped.<BR><BR>
- - */
- - public boolean isSpoil()
- - {
- - return _IsSpoil;
- - }
- -
- - /**
- - * Set the spoil state of this L2NpcInstance.<BR><BR>
- - */
- - public void setSpoil(boolean isSpoil)
- - {
- - _IsSpoil = isSpoil;
- - }
- -
- - public final int getIsSpoiledBy()
- - {
- - return _isSpoiledBy;
- - }
- -
- - public final void setIsSpoiledBy(int value)
- - {
- - _isSpoiledBy = value;
- - }
- -
- - /**
- - * Return the busy status of this L2NpcInstance.<BR><BR>
- - */
- - public final boolean isBusy()
- - {
- - return _IsBusy;
- - }
- -
- - /**
- - * Set the busy status of this L2NpcInstance.<BR><BR>
- - */
- - public void setBusy(boolean isBusy)
- - {
- - _IsBusy = isBusy;
- - }
- -
- - /**
- - * Return the busy message of this L2NpcInstance.<BR><BR>
- - */
- - public final String getBusyMessage()
- - {
- - return _BusyMessage;
- - }
- -
- - /**
- - * Set the busy message of this L2NpcInstance.<BR><BR>
- - */
- - public void setBusyMessage(String message)
- - {
- - _BusyMessage = message;
- - }
- -
- - protected boolean canTarget(L2PcInstance player)
- - {
- - if (player.isConfused())
- - {
- - player.sendPacket(new ActionFailed());
- - return false;
- - }
- - // TODO: More checks...
- -
- - return true;
- - }
- -
- - public boolean canInteract(L2PcInstance player)
- - {
- - // TODO: NPC busy check etc...
- -
- - if (player.isCastingNow() || player.isSitting())
- - return false;
- -
- - if (player.isDead() || player.isFakeDeath())
- - return false;
- -
- - if (player.getPrivateStoreType() != 0)
- - return false;
- -
- -
- - if (!isInsideRadius(player, INTERACTION_DISTANCE, false, false))
- - return false;
- -
- - return true;
- - }
- -
- - /**
- - * Manage actions when a player click on the L2NpcInstance.<BR><BR>
- - *
- - * <B><U> Actions on first click on the L2NpcInstance (Select it)</U> :</B><BR><BR>
- - * <li>Set the L2NpcInstance as target of the L2PcInstance player (if necessary)</li>
- - * <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li>
- - * <li>If L2NpcInstance is autoAttackable, send a Server->Client packet StatusUpdate to the L2PcInstance in order to update L2NpcInstance HP bar </li>
- - * <li>Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client </li><BR><BR>
- - *
- - * <B><U> Actions on second click on the L2NpcInstance (Attack it/Intercat with it)</U> :</B><BR><BR>
- - * <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li>
- - * <li>If L2NpcInstance is autoAttackable, notify the L2PcInstance AI with AI_INTENTION_ATTACK (after a height verification)</li>
- - * <li>If L2NpcInstance is NOT autoAttackable, notify the L2PcInstance AI with AI_INTENTION_INTERACT (after a distance verification) and show message</li><BR><BR>
- - *
- - * <FONT COLOR=#FF0000><B> <U>Caution</U> : Each group of Server->Client packet must be terminated by a ActionFailed packet in order to avoid
- - * that client wait an other packet</B></FONT><BR><BR>
- - *
- - * <B><U> Example of use </U> :</B><BR><BR>
- - * <li> Client packet : Action, AttackRequest</li><BR><BR>
- - *
- - * <B><U> Overriden in </U> :</B><BR><BR>
- - * <li> L2ArtefactInstance : Manage only fisrt click to select Artefact</li><BR><BR>
- - * <li> L2GuardInstance : </li><BR><BR>
- - *
- - * @param player The L2PcInstance that start an action on the L2NpcInstance
- - *
- - */
- - public void onAction(L2PcInstance player)
- - {
- - if (!canTarget(player))
- - return;
- -
- - // Check if the L2PcInstance already target the L2NpcInstance
- - if (this != player.getTarget())
- - {
- - if (Config.DEBUG) _log.fine("new target selected:"+getObjectId());
- -
- - // Set the target of the L2PcInstance player
- - player.setTarget(this);
- -
- - // Check if the player is attackable (without a forced attack)
- - if (isAutoAttackable(player))
- - {
- - // Send a Server->Client packet MyTargetSelected to the L2PcInstance player
- - // The player.getLevel() - getLevel() permit to display the correct color in the select window
- - MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel());
- - player.sendPacket(my);
- -
- - // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar
- - StatusUpdate su = new StatusUpdate(getObjectId());
- - su.addAttribute(StatusUpdate.CUR_HP, (int)getCurrentHp());
- - su.addAttribute(StatusUpdate.MAX_HP, getMaxHp());
- - player.sendPacket(su);
- - }
- - else
- - {
- - // Send a Server->Client packet MyTargetSelected to the L2PcInstance player
- - MyTargetSelected my = new MyTargetSelected(getObjectId(), 0);
- - player.sendPacket(my);
- - }
- -
- - // Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client
- - player.sendPacket(new ValidateLocation(this));
- - }
- - else
- - {
- - player.sendPacket(new ValidateLocation(this));
- - // Check if the player is attackable (without a forced attack) and isn't dead
- - if (isAutoAttackable(player) && !isAlikeDead())
- - {
- - // Check the height difference
- - if (Math.abs(player.getZ() - getZ()) < 400) // this max heigth difference might need some tweaking
- - {
- - // Set the L2PcInstance Intention to AI_INTENTION_ATTACK
- - player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);
- -
- - }
- - else
- - {
- - // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
- - player.sendPacket(new ActionFailed());
- - }
- - }
- - else if (!isAutoAttackable(player))
- - {
- - // Calculate the distance between the L2PcInstance and the L2NpcInstance
- - if (!canInteract(player))
- - {
- - // Notify the L2PcInstance AI with AI_INTENTION_INTERACT
- - player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this);
- - }
- - else
- - {
- - // Send a Server->Client packet SocialAction to the all L2PcInstance on the _knownPlayer of the L2NpcInstance
- - // to display a social action of the L2NpcInstance on their client
- - SocialAction sa = new SocialAction(getObjectId(), Rnd.get(8));
- - broadcastPacket(sa);
- -
- - // Open a chat window on client with the text of the L2NpcInstance
- - if (isEventMob)
- -
- - L2Event.showEventHtml(player, String.valueOf(getObjectId()));
- -
- - else
- - {
- - Quest[] qlsa = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
- - if ((qlsa != null) && qlsa.length > 0)
- - player.setLastQuestNpcObject(getObjectId());
- -
- - Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.NPC_FIRST_TALK);
- - if ((qlst != null) && qlst.length == 1)
- - qlst[0].notifyFirstTalk(this, player);
- - else
- -
- - showChatWindow(player, 0);
- - }
- -
- - }
- - }
- - else
- - player.sendPacket(new ActionFailed());
- - }
- - }
- -
- - /**
- - * Manage and Display the GM console to modify the L2NpcInstance (GM only).<BR><BR>
- - *
- - * <B><U> Actions (If the L2PcInstance is a GM only)</U> :</B><BR><BR>
- - * <li>Set the L2NpcInstance as target of the L2PcInstance player (if necessary)</li>
- - * <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li>
- - * <li>If L2NpcInstance is autoAttackable, send a Server->Client packet StatusUpdate to the L2PcInstance in order to update L2NpcInstance HP bar </li>
- - * <li>Send a Server->Client NpcHtmlMessage() containing the GM console about this L2NpcInstance </li><BR><BR>
- - *
- - * <FONT COLOR=#FF0000><B> <U>Caution</U> : Each group of Server->Client packet must be terminated by a ActionFailed packet in order to avoid
- - * that client wait an other packet</B></FONT><BR><BR>
- - *
- - * <B><U> Example of use </U> :</B><BR><BR>
- - * <li> Client packet : Action</li><BR><BR>
- - *
- - * @param client The thread that manage the player that pessed Shift and click on the L2NpcInstance
- - *
- - */
- - public void onActionShift(ClientThread client)
- - {
- - // Get the L2PcInstance corresponding to the thread
- - L2PcInstance player = client.getActiveChar();
- -
- - if (player == null)
- - return;
- -
- - // Check if the L2PcInstance is a GM
- - if (player.getAccessLevel() >= Config.GM_ACCESSLEVEL)
- - {
- - // Set the target of the L2PcInstance player
- - player.setTarget(this);
- -
- - // Send a Server->Client packet MyTargetSelected to the L2PcInstance player
- - // The player.getLevel() - getLevel() permit to display the correct color in the select window
- - MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel());
- - player.sendPacket(my);
- -
- - // Check if the player is attackable (without a forced attack)
- - if (isAutoAttackable(player))
- - {
- - // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar
- - StatusUpdate su = new StatusUpdate(getObjectId());
- - su.addAttribute(StatusUpdate.CUR_HP, (int)getCurrentHp() );
- - su.addAttribute(StatusUpdate.MAX_HP, getMaxHp() );
- - player.sendPacket(su);
- - }
- -
- - // Send a Server->Client NpcHtmlMessage() containing the GM console about this L2NpcInstance
- - NpcHtmlMessage html = new NpcHtmlMessage(0);
- - TextBuilder html1 = new TextBuilder("<html><body><center><font color=\"LEVEL\">NPC Information</font></center>");
- - String className = getClass().getName().substring(43);
- -
- - html1.append("<br>");
- -
- - html1.append("Instance Type: " + className + "<br1>Faction: " + getFactionId() + "<br1>Location ID: " + (getSpawn() != null ? getSpawn().getLocation() : 0) + "<br1>");
- -
- - if (this instanceof L2ControllableMobInstance)
- - html1.append("Mob Group: " + MobGroupTable.getInstance().getGroupForMob((L2ControllableMobInstance)this).getGroupId() + "<br>");
- - else
- - html1.append("Respawn Time: " + (getSpawn()!=null ? (getSpawn().getRespawnDelay() / 1000)+" Seconds<br>" : "? Seconds<br>"));
- -
- - html1.append("<table border=\"0\" width=\"100%\">");
- - html1.append("<tr><td>Object ID</td><td>"+getObjectId()+"</td><td>NPC ID</td><td>"+getTemplate().npcId+"</td></tr>");
- -
- - html1.append("<tr><td>Castle</td><td>"+(getCastle() != null ? getCastle().getCastleId() : 0)+"</td><td>Coords</td><td>"+getX()+","+getY()+","+getZ()+"</td></tr>");
- -
- - html1.append("<tr><td>Level</td><td>"+getLevel()+"</td><td>Aggro</td><td>"+((this instanceof L2Attackable)? ((L2Attackable)this).getAggroRange() : 0)+"</td></tr>");
- - html1.append("</table><br>");
- -
- - html1.append("<font color=\"LEVEL\">Combat</font>");
- - html1.append("<table border=\"0\" width=\"100%\">");
- - html1.append("<tr><td>Current HP</td><td>"+getCurrentHp()+"</td><td>Current MP</td><td>"+getCurrentMp()+"</td></tr>");
- - html1.append("<tr><td>Max.HP</td><td>"+(int)(getMaxHp()/getStat().calcStat(Stats.MAX_HP , 1, this, null))+"*"+getStat().calcStat(Stats.MAX_HP , 1, this, null)+"</td><td>Max.MP</td><td>"+getMaxMp()+"</td></tr>");
- - html1.append("<tr><td>P.Atk.</td><td>"+getPAtk(null)+"</td><td>M.Atk.</td><td>"+getMAtk(null,null)+"</td></tr>");
- - html1.append("<tr><td>P.Def.</td><td>"+getPDef(null)+"</td><td>M.Def.</td><td>"+getMDef(null,null)+"</td></tr>");
- - html1.append("<tr><td>Accuracy</td><td>"+getAccuracy()+"</td><td>Evasion</td><td>"+getEvasionRate(null)+"</td></tr>");
- - html1.append("<tr><td>Critical</td><td>"+getCriticalHit(null,null)+"</td><td>Speed</td><td>"+getRunSpeed()+"</td></tr>");
- - html1.append("<tr><td>Atk.Speed</td><td>"+getPAtkSpd()+"</td><td>Cast.Speed</td><td>"+getMAtkSpd()+"</td></tr>");
- - html1.append("</table><br>");
- -
- - html1.append("<font color=\"LEVEL\">Basic Stats</font>");
- - html1.append("<table border=\"0\" width=\"100%\">");
- - html1.append("<tr><td>STR</td><td>"+getSTR()+"</td><td>DEX</td><td>"+getDEX()+"</td><td>CON</td><td>"+getCON()+"</td></tr>");
- - html1.append("<tr><td>INT</td><td>"+getINT()+"</td><td>WIT</td><td>"+getWIT()+"</td><td>MEN</td><td>"+getMEN()+"</td></tr>");
- - html1.append("</table>");
- -
- - html1.append("<br><center><table><tr><td><button value=\"Edit NPC\" action=\"bypass -h admin_edit_npc " + getTemplate().npcId + "\" width=100 height=15 back=\"sek.cbui94\" fore=\"sek.cbui92\"><br1></td>");
- - html1.append("<td><button value=\"Kill\" action=\"bypass -h admin_kill\" width=40 height=15 back=\"sek.cbui94\" fore=\"sek.cbui92\"></td><br1></tr>");
- - html1.append("<tr><td><button value=\"Show DropList\" action=\"bypass -h admin_show_droplist " + getTemplate().npcId + "\" width=100 height=15 back=\"sek.cbui94\" fore=\"sek.cbui92\"></td></tr>");
- - html1.append("<td><button value=\"Delete\" action=\"bypass -h admin_delete\" width=40 height=15 back=\"sek.cbui94\" fore=\"sek.cbui92\"></td></tr>");
- - html1.append("</table></center><br>");
- - html1.append("</body></html>");
- -
- - html.setHtml(html1.toString());
- - player.sendPacket(html);
- - }
- - else if(Config.ALT_GAME_VIEWNPC)
- - {
- - // Set the target of the L2PcInstance player
- - player.setTarget(this);
- -
- - // Send a Server->Client packet MyTargetSelected to the L2PcInstance player
- - // The player.getLevel() - getLevel() permit to display the correct color in the select window
- - MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel());
- - player.sendPacket(my);
- -
- - // Check if the player is attackable (without a forced attack)
- - if (isAutoAttackable(player))
- - {
- - // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar
- - StatusUpdate su = new StatusUpdate(getObjectId());
- - su.addAttribute(StatusUpdate.CUR_HP, (int)getCurrentHp());
- - su.addAttribute(StatusUpdate.MAX_HP, getMaxHp() );
- - player.sendPacket(su);
- - }
- -
- - NpcHtmlMessage html = new NpcHtmlMessage(0);
- - TextBuilder html1 = new TextBuilder("<html><body>");
- -
- - html1.append("<br><center><font color=\"LEVEL\">[Combat Stats]</font></center>");
- - html1.append("<table border=0 width=\"100%\">");
- - html1.append("<tr><td>Max.HP</td><td>"+(int)(getMaxHp()/getStat().calcStat(Stats.MAX_HP , 1, this, null))+"*"+(int) getStat().calcStat(Stats.MAX_HP , 1, this, null)+"</td><td>Max.MP</td><td>"+getMaxMp()+"</td></tr>");
- - html1.append("<tr><td>P.Atk.</td><td>"+getPAtk(null)+"</td><td>M.Atk.</td><td>"+getMAtk(null,null)+"</td></tr>");
- - html1.append("<tr><td>P.Def.</td><td>"+getPDef(null)+"</td><td>M.Def.</td><td>"+getMDef(null,null)+"</td></tr>");
- - html1.append("<tr><td>Accuracy</td><td>"+getAccuracy()+"</td><td>Evasion</td><td>"+getEvasionRate(null)+"</td></tr>");
- - html1.append("<tr><td>Critical</td><td>"+getCriticalHit(null,null)+"</td><td>Speed</td><td>"+getRunSpeed()+"</td></tr>");
- - html1.append("<tr><td>Atk.Speed</td><td>"+getPAtkSpd()+"</td><td>Cast.Speed</td><td>"+getMAtkSpd()+"</td></tr>");
- - html1.append("<tr><td>Race</td><td>"+getTemplate().race+"</td><td></td><td></td></tr>");
- - html1.append("</table>");
- -
- - html1.append("<br><center><font color=\"LEVEL\">[Basic Stats]</font></center>");
- - html1.append("<table border=0 width=\"100%\">");
- - html1.append("<tr><td>STR</td><td>"+getSTR()+"</td><td>DEX</td><td>"+getDEX()+"</td><td>CON</td><td>"+getCON()+"</td></tr>");
- - html1.append("<tr><td>INT</td><td>"+getINT()+"</td><td>WIT</td><td>"+getWIT()+"</td><td>MEN</td><td>"+getMEN()+"</td></tr>");
- - html1.append("</table>");
- -
- - html1.append("<br><center><font color=\"LEVEL\">[Drop Info]</font></center>");
- - html1.append("Rates legend: <font color=\"ff0000\">50%+</font> <font color=\"00ff00\">30%+</font> <font color=\"0000ff\">less than 30%</font>");
- - html1.append("<table border=0 width=\"100%\">");
- -
- -
- - if (getTemplate().getDropData() != null)
- - {
- - for (L2DropData drop : getTemplate().getDropData())
- - {
- - String name = ItemTable.getInstance().getTemplate(drop.getItemId()).getName();
- -
- - if (drop.getChance() >= 600000)
- - html1.append("<tr><td><font color=\"ff0000\">" + name + "</font></td><td>" + (drop.isQuestDrop()?"Quest":(drop.isSweep()?"Sweep":"Drop")) + "</td></tr>");
- - else if(drop.getChance() >= 300000)
- - html1.append("<tr><td><font color=\"00ff00\">" + name + "</font></td><td>" + (drop.isQuestDrop()?"Quest":(drop.isSweep()?"Sweep":"Drop")) + "</td></tr>");
- - else
- - html1.append("<tr><td><font color=\"0000ff\">" + name + "</font></td><td>" + (drop.isQuestDrop()?"Quest":(drop.isSweep()?"Sweep":"Drop")) + "</td></tr>");
- - }
- - }
- -
- - html1.append("</table>");
- - html1.append("</body></html>");
- -
- - html.setHtml(html1.toString());
- - player.sendPacket(html);
- - }
- -
- - // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
- - player.sendPacket(new ActionFailed());
- - }
- -
- - /** Return the L2Castle this L2NpcInstance belongs to. */
- - public final Castle getCastle()
- - {
- -
- -
- - if (_castleIndex < 0)
- - {
- -
- - L2TownZone town = TownManager.getInstance().getTown(getX(), getY(), getZ());
- - if (town != null)
- - _castleIndex = CastleManager.getInstance().getCastleIndex(town.getTaxById());
- -
- -
- -
- - if (_castleIndex < 0)
- - _castleIndex = CastleManager.getInstance().findNearestCastleIndex(this);
- - else
- -
- - _isInTown = true; // Npc was spawned in town
- -
- - }
- -
- - if (_castleIndex < 0)
- - return null;
- -
- -
- - return CastleManager.getInstance().getCastles().get(_castleIndex);
- -
- - }
- -
- -
- - public boolean getIsInCastleTown()
- - {
- - if (_castleIndex < 0)
- - getCastle();
- -
- - return _isInTown;
- - }
- -
- - /**
- - * Open a quest or chat window on client with the text of the L2NpcInstance in function of the command.<BR><BR>
- - *
- - * <B><U> Example of use </U> :</B><BR><BR>
- - * <li> Client packet : RequestBypassToServer</li><BR><BR>
- - *
- - * @param command The command string received from client
- - *
- - */
- - public void onBypassFeedback(L2PcInstance player, String command)
- - {
- -
- - if (isBusy() && getBusyMessage().length() > 0)
- - {
- - player.sendPacket(new ActionFailed());
- - NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- - html.setFile("data/html/npcbusy.htm");
- - html.replace("%busymessage%", getBusyMessage());
- - html.replace("%npcname%", getName());
- - html.replace("%playername%", player.getName());
- - player.sendPacket(html);
- - }
- -
- - else if (command.equalsIgnoreCase("TerritoryStatus"))
- - {
- - NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- - html.setFile("data/html/territorystatus.htm");
- - html.replace("%objectId%", String.valueOf(getObjectId()));
- - html.replace("%npcname%", getName());
- -
- -
- - if (getIsInCastleTown())
- - {
- - html.replace("%castlename%", getCastle().getName());
- - html.replace("%taxpercent%", "" + getCastle().getTaxPercent());
- -
- -
- - if (getCastle().getOwnerId() > 0)
- - {
- - L2Clan clan = ClanTable.getInstance().getClan(getCastle().getOwnerId());
- - html.replace("%clanname%", clan.getName());
- - html.replace("%clanleadername%", clan.getLeaderName());
- - }
- - else
- - {
- - html.replace("%clanname%", "NPC");
- - html.replace("%clanleadername%", "NPC");
- - }
- - }
- - else
- - {
- - html.replace("%castlename%", "Open");
- - html.replace("%taxpercent%", "0");
- -
- -
- - html.replace("%clanname%", "No");
- - html.replace("%clanleadername%", "None");
- - }
- -
- -
- - player.sendPacket(html);
- - }
- - else if (command.startsWith("Quest"))
- - {
- - String quest = "";
- - try
- - {
- - quest = command.substring(5).trim();
- - }
- - catch (IndexOutOfBoundsException ioobe) {}
- -
- - if (quest.length() == 0)
- - showQuestWindow(player);
- - else
- - showQuestWindow(player, quest);
- - }
- - else if (command.startsWith("Chat"))
- - {
- - int val = 0;
- - try
- - {
- - val = Integer.parseInt(command.substring(5));
- - }
- - catch (IndexOutOfBoundsException ioobe)
- -{
- -}
- - catch (NumberFormatException nfe) {}
- -
- - showChatWindow(player, val);
- - }
- - else if (command.startsWith("Link"))
- - {
- - String path = command.substring(5).trim();
- - if (path.indexOf("..") != -1)
- - return;
- -
- - String filename = "data/html/"+path;
- - NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- - html.setFile(filename);
- - html.replace("%objectId%", String.valueOf(getObjectId()));
- - player.sendPacket(html);
- - }
- - else if (command.startsWith("NobleTeleport"))
- - {
- - if (!player.isNoble())
- - {
- - String filename = "data/html/teleporter/nobleteleporter-no.htm";
- - NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- - html.setFile(filename);
- - html.replace("%objectId%", String.valueOf(getObjectId()));
- - html.replace("%npcname%", getName());
- - player.sendPacket(html);
- - return;
- - }
- -
- - int val = 0;
- - try
- - {
- - val = Integer.parseInt(command.substring(14));
- - }
- - catch (IndexOutOfBoundsException ioobe) {
- -}
- - catch (NumberFormatException nfe) {}
- -
- - showChatWindow(player, val);
- - }
- - else if (command.startsWith("Loto"))
- - {
- - int val = 0;
- - try
- - {
- - val = Integer.parseInt(command.substring(5));
- - }
- - catch (IndexOutOfBoundsException ioobe) {
- -}
- - catch (NumberFormatException nfe) {}
- -
- - if (val == 0)
- - {
- - // new loto ticket
- - for (int i=0;i<5;i++)
- - player.setLoto(i,0);
- - }
- - showLotoWindow(player, val);
- - }
- - else if (command.startsWith("CPRecovery"))
- - {
- - makeCPRecovery(player);
- - }
- - else if (command.startsWith("SupportMagic"))
- - {
- - makeSupportMagic(player);
- - }
- - else if (command.startsWith("multisell"))
- - {
- - L2Multisell.getInstance().createMultiSell(Integer.parseInt(command.substring(9).trim()), player, false, this);
- - }
- - else if (command.startsWith("exc_multisell"))
- - {
- - L2Multisell.getInstance().createMultiSell(Integer.parseInt(command.substring(13).trim()), player, true, this);
- - }
- - else if (command.startsWith("npcfind_byid"))
- - {
- - try
- - {
- - L2Spawn spawn = SpawnTable.getInstance().getTemplate(Integer.parseInt(command.substring(12).trim()));
- -
- -
- - if (spawn!=null)
- - player.sendPacket(new RadarControl(0,1,spawn.getLocx(),spawn.getLocy(),spawn.getLocz()));
- - }
- - catch (NumberFormatException nfe)
- - {
- - player.sendMessage("Wrong command parameters");
- - }
- - }
- -
- - }
- -
- - /**
- - * Return null (regular NPCs don't have weapons instancies).<BR><BR>
- - */
- - public L2ItemInstance getActiveWeaponInstance()
- - {
- - // regular NPCs dont have weapons instancies
- - return null;
- - }
- -
- - /**
- - * Return the weapon item equiped in the right hand of the L2NpcInstance or null.<BR><BR>
- - */
- - public L2Weapon getActiveWeaponItem()
- - {
- - // Get the weapon identifier equiped in the right hand of the L2NpcInstance
- - int weaponId = getTemplate().rhand;
- -
- - if (weaponId < 1)
- - return null;
- -
- - // Get the weapon item equiped in the right hand of the L2NpcInstance
- - L2Item item = ItemTable.getInstance().getTemplate(getTemplate().rhand);
- -
- - if (!(item instanceof L2Weapon))
- - return null;
- -
- - return (L2Weapon)item;
- - }
- -
- - /**
- - * Return null (regular NPCs don't have weapons instancies).<BR><BR>
- - */
- - public L2ItemInstance getSecondaryWeaponInstance()
- - {
- - // regular NPCs dont have weapons instancies
- - return null;
- - }
- -
- - /**
- - * Return the weapon item equiped in the left hand of the L2NpcInstance or null.<BR><BR>
- - */
- - public L2Weapon getSecondaryWeaponItem()
- - {
- - // Get the weapon identifier equiped in the right hand of the L2NpcInstance
- - int weaponId = getTemplate().lhand;
- -
- - if (weaponId < 1)
- - return null;
- -
- - // Get the weapon item equiped in the right hand of the L2NpcInstance
- - L2Item item = ItemTable.getInstance().getTemplate(getTemplate().lhand);
- -
- - if (!(item instanceof L2Weapon))
- - return null;
- -
- - return (L2Weapon)item;
- - }
- -
- - /**
- - * Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance.<BR><BR>
- - *
- - * @param player The L2PcInstance who talks with the L2NpcInstance
- - * @param content The text of the L2NpcMessage
- - *
- - */
- - public void insertObjectIdAndShowChatWindow(L2PcInstance player, String content)
- - {
- - // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
- - content = content.replaceAll("%objectId%", String.valueOf(getObjectId()));
- - NpcHtmlMessage npcReply = new NpcHtmlMessage(getObjectId());
- - npcReply.setHtml(content);
- - player.sendPacket(npcReply);
- - }
- -
- - /**
- - * Return the pathfile of the selected HTML file in function of the npcId and of the page number.<BR><BR>
- - *
- - * <B><U> Format of the pathfile </U> :</B><BR><BR>
- - * <li> if the file exists on the server (page number = 0) : <B>data/html/default/12006.htm</B> (npcId-page number)</li>
- - * <li> if the file exists on the server (page number > 0) : <B>data/html/default/12006-1.htm</B> (npcId-page number)</li>
- - * <li> if the file doesn't exist on the server : <B>data/html/npcdefault.htm</B> (message : "I have nothing to say to you")</li><BR><BR>
- - *
- - * <B><U> Overriden in </U> :</B><BR><BR>
- - * <li> L2GuardInstance : Set the pathfile to data/html/guard/12006-1.htm (npcId-page number)</li><BR><BR>
- - *
- - * @param npcId The Identifier of the L2NpcInstance whose text must be display
- - * @param val The number of the page to display
- - *
- - */
- - public String getHtmlPath(int npcId, int val)
- - {
- - String pom = "";
- -
- - if (val == 0)
- - pom = "" + npcId;
- - else
- - pom = npcId + "-" + val;
- -
- - String temp = "data/html/default/" + pom + ".htm";
- -
- - if (!Config.LAZY_CACHE)
- - {
- - // If not running lazy cache the file must be in the cache or it doesnt exist
- - if (HtmCache.getInstance().contains(temp))
- - return temp;
- - }
- - else
- - {
- - if (HtmCache.getInstance().isLoadable(temp))
- - return temp;
- - }
- -
- - // If the file is not found, the standard message "I have nothing to say to you" is returned
- - return "data/html/npcdefault.htm";
- - }
- -
- - public void showBuyWindow(L2PcInstance player, int val)
- - {
- - double taxRate = 0;
- - if (getIsInCastleTown())
- - taxRate = getCastle().getTaxRate();
- -
- - player.tempInventoryDisable();
- -
- - if (Config.DEBUG) _log.fine("Showing buylist");
- -
- - L2TradeList list = TradeController.getInstance().getBuyList(val);
- -
- - if (list != null && list.getNpcId().equals(String.valueOf(getNpcId())))
- -
- - player.sendPacket(new BuyList(list, player.getAdena(), taxRate));
- -
- - else
- - {
- - _log.warning("possible client hacker: " + player.getName()
- - + " attempting to buy from GM shop! < Ban him!");
- - _log.warning("buylist id:" + val);
- - }
- -
- - player.sendPacket(new ActionFailed());
- - }
- -
- - /**
- - * Open a choose quest window on client with all quests available of the L2NpcInstance.<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance </li><BR><BR>
- - *
- - * @param player The L2PcInstance that talk with the L2NpcInstance
- - * @param quests The table containing quests of the L2NpcInstance
- - *
- - */
- - public void showQuestChooseWindow(L2PcInstance player, Quest[] quests)
- - {
- - TextBuilder sb = new TextBuilder();
- -
- - sb.append("<html><body><title>Talk about:</title><br>");
- -
- - for (Quest q : quests)
- - {
- - sb.append("<a action=\"bypass -h npc_").append(getObjectId())
- - .append("_Quest ").append(q.getName()).append("\">")
- - .append(q.getDescr()).append("</a><br>");
- - }
- -
- - sb.append("</body></html>");
- -
- - // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
- - insertObjectIdAndShowChatWindow(player, sb.toString());
- - }
- -
- - /**
- - * Open a quest window on client with the text of the L2NpcInstance.<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Get the text of the quest state in the folder data/jscript/quests/questId/stateId.htm </li>
- - * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance </li>
- - * <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet </li><BR><BR>
- - *
- - * @param player The L2PcInstance that talk with the L2NpcInstance
- - * @param questId The Identifier of the quest to display the message
- - *
- - */
- - public void showQuestWindow(L2PcInstance player, String questId)
- - {
- - String content;
- -
- -
- - // Get the state of the selected quest
- - QuestState qs = player.getQuestState(questId);
- -
- - if (qs != null)
- - {
- - // If the quest is already started, no need to show a window
- - if (!qs.getQuest().notifyTalk(this, qs))
- - return;
- - }
- - else
- - {
- - Quest q = QuestManager.getInstance().getQuest(questId);
- - if (q != null)
- - {
- - if (q.getQuestIntId() >= 1 && q.getQuestIntId() < 1000)
- - {
- - Quest[] questList = player.getAllActiveQuests();
- - if (questList.length >= 15) // if too many ongoing quests, don't show window and send message
- - {
- - player.sendPacket(new SystemMessage(401));
- - return;
- - }
- -
- - if (player.getWeightPenalty() >= 3 || player.getInventoryLimit() * 0.8 <= player.getInventory().getSize())
- - {
- - player.sendPacket(new SystemMessage(1118));
- - return;
- - }
- - }
- -
- - // check for start point
- - Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
- -
- - if (qlst != null && qlst.length > 0)
- - {
- - for (int i=0; i < qlst.length; i++)
- - {
- - if (qlst[i] == q)
- - {
- - qs = q.newQuestState(player);
- - //disabled by mr. because quest dialog only show on second click.
- - //if(qs.getState().getName().equalsIgnoreCase("completed"))
- - //{
- - if (!qs.getQuest().notifyTalk(this, qs))
- - return; // no need to show a window
- - //}
- - break;
- - }
- - }
- - }
- - }
- - }
- -
- - if (qs == null)
- - {
- - // no quests found
- - content = "<html><body>I have no tasks for you right now.</body></html>";
- - }
- - else
- - {
- - questId = qs.getQuest().getName();
- - String stateId = qs.getStateId();
- - String path = "data/jscript/quests/"+questId+"/"+stateId+".htm";
- - content = HtmCache.getInstance().getHtm(path); //TODO path for quests html
- -
- - if (Config.DEBUG)
- - {
- - if (content != null)
- - {
- - _log.fine("Showing quest window for quest "+questId+" html path: " + path);
- - }
- - else
- - {
- - _log.fine("File not exists for quest "+questId+" html path: " + path);
- - }
- - }
- - }
- -
- - // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
- - if (content != null)
- - insertObjectIdAndShowChatWindow(player, content);
- -
- - // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
- - player.sendPacket(new ActionFailed());
- - }
- -
- - /**
- - * Collect awaiting quests/start points and display a QuestChooseWindow (if several available) or QuestWindow.<BR><BR>
- - *
- - * @param player The L2PcInstance that talk with the L2NpcInstance
- - *
- - */
- - public void showQuestWindow(L2PcInstance player)
- - {
- - // collect awaiting quests and start points
- - List<Quest> options = new FastList<Quest>();
- -
- - QuestState[] awaits = player.getQuestsForTalk(getTemplate().npcId);
- - Quest[] starts = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
- -
- - // Quests are limited between 1 and 999 because those are the quests that are supported by the client.
- - if (awaits != null)
- - {
- - for (QuestState x : awaits)
- - {
- - if (!options.contains(x))
- - if((x.getQuest().getQuestIntId()>0) && (x.getQuest().getQuestIntId()<1000))
- - options.add(x.getQuest());
- - }
- - }
- -
- - if (starts != null)
- - {
- - for (Quest x : starts)
- - {
- - if (!options.contains(x))
- - if((x.getQuestIntId()>0) && (x.getQuestIntId()<1000))
- - options.add(x);
- - }
- - }
- -
- - // Display a QuestChooseWindow (if several quests are available) or QuestWindow
- - if (options.size() > 1)
- - {
- - showQuestChooseWindow(player, options.toArray(new Quest[options.size()]));
- - }
- - else if (options.size() == 1)
- - {
- - showQuestWindow(player, options.get(0).getName());
- - }
- - else
- - {
- - showQuestWindow(player, "");
- - }
- - }
- -
- - /**
- - * Open a Loto window on client with the text of the L2NpcInstance.<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Get the text of the selected HTML file in function of the npcId and of the page number </li>
- - * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance </li>
- - * <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet </li><BR>
- - *
- - * @param player The L2PcInstance that talk with the L2NpcInstance
- - * @param val The number of the page of the L2NpcInstance to display
- - *
- - */
- - // 0 - first buy lottery ticket window
- - // 1-20 - buttons
- - // 21 - second buy lottery ticket window
- - // 22 - selected ticket with 5 numbers
- - // 23 - current lottery jackpot
- - // 24 - Previous winning numbers/Prize claim
- - // >24 - check lottery ticket by item object id
- - public void showLotoWindow(L2PcInstance player, int val)
- - {
- - int npcId = getTemplate().npcId;
- - String filename;
- - SystemMessage sm;
- - NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- -
- - if (val == 0) // 0 - first buy lottery ticket window
- - {
- - filename = (getHtmlPath(npcId, 1));
- - html.setFile(filename);
- - }
- - else if (val >= 1 && val <= 21) // 1-20 - buttons, 21 - second buy lottery ticket window
- - {
- - if (!Lottery.getInstance().isStarted())
- - {
- - //tickets can't be sold
- - player.sendPacket(new SystemMessage(930));
- - return;
- - }
- - if (!Lottery.getInstance().isSellableTickets())
- - {
- - //tickets can't be sold
- - player.sendPacket(new SystemMessage(784));
- - return;
- - }
- -
- + // private static Logger _log = Logger.getLogger(L2NpcInstance.class.getName());
- +
- + /** The interaction distance of the L2NpcInstance(is used as offset in MovetoLocation method) */
- + public static final int INTERACTION_DISTANCE = 150;
- +
- + /** The L2Spawn object that manage this L2NpcInstance */
- + private L2Spawn _spawn;
- +
- + /** The flag to specify if this L2NpcInstance is busy */
- + private boolean _IsBusy = false;
- +
- + /** The busy message for this L2NpcInstance */
- + private String _BusyMessage = "";
- +
- + /** True if endDecayTask has already been called */
- + volatile boolean _isDecayed = false;
- +
- + /** True if a Dwarf has used Spoil on this L2NpcInstance */
- + private boolean _IsSpoil = false;
- +
- + /** The castle index in the array of L2Castle this L2NpcInstance belongs to */
- + private int _castleIndex = -2;
- +
- + public boolean isEventMob = false;
- +
- + private boolean _isInTown = false;
- +
- + private int _isSpoiledBy = 0;
- +
- + protected RandomAnimationTask _rAniTask = null;
- +
- + private int _currentCollisionHeight;
- + private int _currentCollisionRadius;
- +
- + /** Task launching the function onRandomAnimation() */
- + protected class RandomAnimationTask implements Runnable
- + {
- + @Override
- + public void run()
- + {
- + try
- + {
- + if (this != _rAniTask)
- + {
- + return;
- + }
- +
- + if (L2NpcInstance.this instanceof L2Attackable)
- + {
- + if (getAI().getIntention() != AI_INTENTION_ACTIVE)
- + {
- + return;
- + }
- + }
- +
- + else
- + {
- + if (!isInActiveRegion())
- + {
- + return;
- + }
- + }
- +
- + if (!(isDead() || isStunned() || isSleeping() || isParalyzed()))
- + {
- + onRandomAnimation();
- + }
- +
- + startRandomAnimationTimer();
- + }
- + catch (Throwable t)
- + {
- + }
- + }
- + }
- +
- + /**
- + * Send a packet SocialAction to all L2PcInstance in the _KnownPlayers of the L2NpcInstance and create a new RandomAnimation Task.<BR>
- + * <BR>
- + */
- + public void onRandomAnimation()
- + {
- + // Send a packet SocialAction to all L2PcInstance in the _KnownPlayers of the L2NpcInstance
- + SocialAction sa = new SocialAction(getObjectId(), Rnd.get(1, 3));
- + broadcastPacket(sa);
- + }
- +
- + /**
- + * Create a RandomAnimation Task that will be launched after the calculated delay.<BR>
- + * <BR>
- + */
- + public void startRandomAnimationTimer()
- + {
- + if (!hasRandomAnimation())
- + {
- + return;
- + }
- +
- + int minWait = this instanceof L2Attackable ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION;
- + int maxWait = this instanceof L2Attackable ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION;
- +
- + // Calculate the delay before the next animation
- + int interval = Rnd.get(minWait, maxWait) * 1000;
- +
- + // Create a RandomAnimation Task that will be launched after the calculated delay
- + _rAniTask = new RandomAnimationTask();
- + ThreadPoolManager.getInstance().scheduleGeneral(_rAniTask, interval);
- + }
- +
- + /**
- + * Check if the server allows Random Animation.<BR>
- + * <BR>
- + */
- + public boolean hasRandomAnimation()
- + {
- + return (Config.MAX_NPC_ANIMATION > 0);
- + }
- +
- + /**
- + * Constructor of L2NpcInstance (use L2Character constructor).<BR>
- + * <BR>
- + * <B><U> Actions</U> :</B><BR>
- + * <BR>
- + * <li>Call the L2Character constructor to set the _template of the L2Character (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR)</li> <li>Set the name of the L2Character</li> <li>Create a RandomAnimation Task that will be launched after the calculated delay if
- + * the server allow it</li><BR>
- + * <BR>
- + * @param objectId Identifier of the object to initialized
- + * @param template The L2NpcTemplate to apply to the NPC
- + */
- + public L2NpcInstance(int objectId, L2NpcTemplate template)
- + {
- + // Call the L2Character constructor to set the _template of the L2Character, copy skills from template to object
- + // and link _calculators to NPC_STD_CALCULATOR
- + super(objectId, template);
- + getKnownList();
- + getStat();
- + getStatus();
- +
- + super.initCharStatusUpdateValues(); // init status update values
- +
- + _currentCollisionHeight = getTemplate().collisionHeight;
- + _currentCollisionRadius = getTemplate().collisionRadius;
- +
- + if (template == null)
- + {
- + _log.severe("No template for Npc. Please check your datapack is setup correctly.");
- + return;
- + }
- +
- + // Set the name of the L2Character
- + setName(template.name);
- + }
- +
- + @Override
- + public NpcKnownList getKnownList()
- + {
- + if ((super.getKnownList() == null) || !(super.getKnownList() instanceof NpcKnownList))
- + {
- + setKnownList(new NpcKnownList(this));
- + }
- + return (NpcKnownList) super.getKnownList();
- + }
- +
- + @Override
- + public NpcStat getStat()
- + {
- + if ((super.getStat() == null) || !(super.getStat() instanceof NpcStat))
- + {
- + setStat(new NpcStat(this));
- + }
- + return (NpcStat) super.getStat();
- + }
- +
- + @Override
- + public NpcStatus getStatus()
- + {
- + if ((super.getStatus() == null) || !(super.getStatus() instanceof NpcStatus))
- + {
- + setStatus(new NpcStatus(this));
- + }
- + return (NpcStatus) super.getStatus();
- + }
- +
- + /** Return the L2NpcTemplate of the L2NpcInstance. */
- + @Override
- + public final L2NpcTemplate getTemplate()
- + {
- + return (L2NpcTemplate) super.getTemplate();
- + }
- +
- + /**
- + * Return the generic Identifier of this L2NpcInstance contained in the L2NpcTemplate.<BR>
- + * <BR>
- + */
- + public int getNpcId()
- + {
- + return getTemplate().npcId;
- + }
- +
- + @Override
- + public boolean isAttackable()
- + {
- + return true;
- + }
- +
- + /**
- + * Return the faction Identifier of this L2NpcInstance contained in the L2NpcTemplate.<BR>
- + * <BR>
- + * <B><U> Concept</U> :</B><BR>
- + * <BR>
- + * If a NPC belows to a Faction, other NPC of the faction inside the Faction range will help it if it's attacked<BR>
- + * <BR>
- + */
- + public final String getFactionId()
- + {
- + return getTemplate().factionId;
- + }
- +
- + /**
- + * Return the Level of this L2NpcInstance contained in the L2NpcTemplate.<BR>
- + * <BR>
- + */
- + @Override
- + public final int getLevel()
- + {
- + return getTemplate().level;
- + }
- +
- + /**
- + * Return True if the L2NpcInstance is agressive (ex : L2MonsterInstance in function of aggroRange).<BR>
- + * <BR>
- + */
- + public boolean isAggressive()
- + {
- + return false;
- + }
- +
- + /**
- + * Return the Aggro Range of this L2NpcInstance contained in the L2NpcTemplate.<BR>
- + * <BR>
- + */
- + public int getAggroRange()
- + {
- + return getTemplate().aggroRange;
- + }
- +
- + /**
- + * Return the Faction Range of this L2NpcInstance contained in the L2NpcTemplate.<BR>
- + * <BR>
- + */
- + public int getFactionRange()
- + {
- + return getTemplate().factionRange;
- + }
- +
- + /**
- + * Return True if this L2NpcInstance is undead in function of the L2NpcTemplate.<BR>
- + * <BR>
- + */
- + @Override
- + public boolean isUndead()
- + {
- + return getTemplate().isUndead;
- + }
- +
- + /**
- + * Send a packet NpcInfo with state of abnormal effect to all L2PcInstance in the _KnownPlayers of the L2NpcInstance.<BR>
- + * <BR>
- + */
- + @Override
- + public void updateAbnormalEffect()
- + {
- +
- + // Send a Server->Client packet NpcInfo with state of abnormal effect to all L2PcInstance in the _KnownPlayers of the L2NpcInstance
- + for (L2PcInstance player : getKnownList().getKnownPlayers().values())
- + {
- + if (player != null)
- + {
- + if (getRunSpeed() == 0)
- + {
- + player.sendPacket(new ServerObjectInfo(this, player));
- + }
- + else
- + {
- + player.sendPacket(new NpcInfo(this, player));
- + }
- + }
- + }
- + }
- +
- + /**
- + * Return the distance under which the object must be add to _knownObject in function of the object type.<BR>
- + * <BR>
- + * <B><U> Values </U> :</B><BR>
- + * <BR>
- + * <li>object is a L2FolkInstance : 0 (don't remember it)</li> <li>object is a L2Character : 0 (don't remember it)</li> <li>object is a L2PlayableInstance : 1500</li> <li>others : 500</li><BR>
- + * <BR>
- + * <B><U> Overriden in </U> :</B><BR>
- + * <BR>
- + * <li>L2Attackable</li><BR>
- + * <BR>
- + * @param object The Object to add to _knownObject
- + */
- + public int getDistanceToWatchObject(L2Object object)
- + {
- + if (object instanceof L2FestivalGuideInstance)
- + {
- + return 10000;
- + }
- +
- + if ((object instanceof L2FolkInstance) || !(object instanceof L2Character))
- + {
- + return 0;
- + }
- +
- + if (object instanceof L2PlayableInstance)
- + {
- + return 1500;
- + }
- +
- + return 500;
- + }
- +
- + /**
- + * Return the distance after which the object must be remove from _knownObject in function of the object type.<BR>
- + * <BR>
- + * <B><U> Values </U> :</B><BR>
- + * <BR>
- + * <li>object is not a L2Character : 0 (don't remember it)</li> <li>object is a L2FolkInstance : 0 (don't remember it)</li> <li>object is a L2PlayableInstance : 3000</li> <li>others : 1000</li><BR>
- + * <BR>
- + * <B><U> Overriden in </U> :</B><BR>
- + * <BR>
- + * <li>L2Attackable</li><BR>
- + * <BR>
- + * @param object The Object to remove from _knownObject
- + */
- + public int getDistanceToForgetObject(L2Object object)
- + {
- + return 2 * getDistanceToWatchObject(object);
- + }
- +
- + /**
- + * Return False.<BR>
- + * <BR>
- + * <B><U> Overriden in </U> :</B><BR>
- + * <BR>
- + * <li>L2MonsterInstance : Check if the attacker is not another L2MonsterInstance</li> <li>L2PcInstance</li><BR>
- + * <BR>
- + */
- + @Override
- + public boolean isAutoAttackable(@SuppressWarnings("unused") L2Character attacker)
- + {
- + return false;
- + }
- +
- + /**
- + * Return the Identifier of the item in the left hand of this L2NpcInstance contained in the L2NpcTemplate.<BR>
- + * <BR>
- + */
- + public int getLeftHandItem()
- + {
- + return getTemplate().lhand;
- + }
- +
- + /**
- + * Return the Identifier of the item in the right hand of this L2NpcInstance contained in the L2NpcTemplate.<BR>
- + * <BR>
- + */
- + public int getRightHandItem()
- + {
- + return getTemplate().rhand;
- + }
- +
- + /**
- + * Return True if this L2NpcInstance has drops that can be sweeped.<BR>
- + * <BR>
- + */
- + public boolean isSpoil()
- + {
- + return _IsSpoil;
- + }
- +
- + /**
- + * Set the spoil state of this L2NpcInstance.<BR>
- + * <BR>
- + */
- + public void setSpoil(boolean isSpoil)
- + {
- + _IsSpoil = isSpoil;
- + }
- +
- + public final int getIsSpoiledBy()
- + {
- + return _isSpoiledBy;
- + }
- +
- + public final void setIsSpoiledBy(int value)
- + {
- + _isSpoiledBy = value;
- + }
- +
- + /**
- + * Return the busy status of this L2NpcInstance.<BR>
- + * <BR>
- + */
- + public final boolean isBusy()
- + {
- + return _IsBusy;
- + }
- +
- + /**
- + * Set the busy status of this L2NpcInstance.<BR>
- + * <BR>
- + */
- + public void setBusy(boolean isBusy)
- + {
- + _IsBusy = isBusy;
- + }
- +
- + /**
- + * Return the busy message of this L2NpcInstance.<BR>
- + * <BR>
- + */
- + public final String getBusyMessage()
- + {
- + return _BusyMessage;
- + }
- +
- + /**
- + * Set the busy message of this L2NpcInstance.<BR>
- + * <BR>
- + */
- + public void setBusyMessage(String message)
- + {
- + _BusyMessage = message;
- + }
- +
- + protected boolean canTarget(L2PcInstance player)
- + {
- + if (player.isConfused())
- + {
- + player.sendPacket(new ActionFailed());
- + return false;
- + }
- + // TODO: More checks...
- +
- + return true;
- + }
- +
- + public boolean canInteract(L2PcInstance player)
- + {
- + // TODO: NPC busy check etc...
- +
- + if (player.isCastingNow() || player.isSitting())
- + {
- + return false;
- + }
- +
- + if (player.isDead() || player.isFakeDeath())
- + {
- + return false;
- + }
- +
- + if (player.getPrivateStoreType() != 0)
- + {
- + return false;
- + }
- +
- + if (!isInsideRadius(player, INTERACTION_DISTANCE, false, false))
- + {
- + return false;
- + }
- +
- + return true;
- + }
- +
- + /**
- + * Manage actions when a player click on the L2NpcInstance.<BR>
- + * <BR>
- + * <B><U> Actions on first click on the L2NpcInstance (Select it)</U> :</B><BR>
- + * <BR>
- + * <li>Set the L2NpcInstance as target of the L2PcInstance player (if necessary)</li> <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li> <li>If L2NpcInstance is autoAttackable, send a Server->Client packet StatusUpdate to the
- + * L2PcInstance in order to update L2NpcInstance HP bar</li> <li>Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client</li><BR>
- + * <BR>
- + * <B><U> Actions on second click on the L2NpcInstance (Attack it/Intercat with it)</U> :</B><BR>
- + * <BR>
- + * <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li> <li>If L2NpcInstance is autoAttackable, notify the L2PcInstance AI with AI_INTENTION_ATTACK (after a height verification)</li> <li>If L2NpcInstance is NOT autoAttackable, notify the
- + * L2PcInstance AI with AI_INTENTION_INTERACT (after a distance verification) and show message</li><BR>
- + * <BR>
- + * <FONT COLOR=#FF0000><B> <U>Caution</U> : Each group of Server->Client packet must be terminated by a ActionFailed packet in order to avoid that client wait an other packet</B></FONT><BR>
- + * <BR>
- + * <B><U> Example of use </U> :</B><BR>
- + * <BR>
- + * <li>Client packet : Action, AttackRequest</li><BR>
- + * <BR>
- + * <B><U> Overriden in </U> :</B><BR>
- + * <BR>
- + * <li>L2ArtefactInstance : Manage only fisrt click to select Artefact</li><BR>
- + * <BR>
- + * <li>L2GuardInstance :</li><BR>
- + * <BR>
- + * @param player The L2PcInstance that start an action on the L2NpcInstance
- + */
- + @Override
- + public void onAction(L2PcInstance player)
- + {
- + if (!canTarget(player))
- + {
- + return;
- + }
- +
- + // Check if the L2PcInstance already target the L2NpcInstance
- + if (this != player.getTarget())
- + {
- + if (Config.DEBUG)
- + {
- + _log.fine("new target selected:" + getObjectId());
- + }
- +
- + // Set the target of the L2PcInstance player
- + player.setTarget(this);
- +
- + // Check if the player is attackable (without a forced attack)
- + if (isAutoAttackable(player))
- + {
- + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player
- + // The player.getLevel() - getLevel() permit to display the correct color in the select window
- + MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel());
- + player.sendPacket(my);
- +
- + // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar
- + StatusUpdate su = new StatusUpdate(getObjectId());
- + su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp());
- + su.addAttribute(StatusUpdate.MAX_HP, getMaxHp());
- + player.sendPacket(su);
- + }
- + else
- + {
- + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player
- + MyTargetSelected my = new MyTargetSelected(getObjectId(), 0);
- + player.sendPacket(my);
- + }
- +
- + // Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client
- + player.sendPacket(new ValidateLocation(this));
- + }
- + else
- + {
- + player.sendPacket(new ValidateLocation(this));
- + // Check if the player is attackable (without a forced attack) and isn't dead
- + if (isAutoAttackable(player) && !isAlikeDead())
- + {
- + // Check the height difference
- + if (Math.abs(player.getZ() - getZ()) < 400) // this max heigth difference might need some tweaking
- + {
- + // Set the L2PcInstance Intention to AI_INTENTION_ATTACK
- + player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);
- +
- + }
- + else
- + {
- + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
- + player.sendPacket(new ActionFailed());
- + }
- + }
- + else if (!isAutoAttackable(player))
- + {
- + // Calculate the distance between the L2PcInstance and the L2NpcInstance
- + if (!canInteract(player))
- + {
- + // Notify the L2PcInstance AI with AI_INTENTION_INTERACT
- + player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this);
- + }
- + else
- + {
- + // Send a Server->Client packet SocialAction to the all L2PcInstance on the _knownPlayer of the L2NpcInstance
- + // to display a social action of the L2NpcInstance on their client
- + SocialAction sa = new SocialAction(getObjectId(), Rnd.get(8));
- + broadcastPacket(sa);
- +
- + // Open a chat window on client with the text of the L2NpcInstance
- + if (isEventMob)
- + {
- + L2Event.showEventHtml(player, String.valueOf(getObjectId()));
- + }
- + else
- + {
- + Quest[] qlsa = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
- + if ((qlsa != null) && (qlsa.length > 0))
- + {
- + player.setLastQuestNpcObject(getObjectId());
- + }
- +
- + Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.NPC_FIRST_TALK);
- + if ((qlst != null) && (qlst.length == 1))
- + {
- + qlst[0].notifyFirstTalk(this, player);
- + }
- + else
- + {
- + showChatWindow(player, 0);
- + }
- + }
- +
- + }
- + }
- + else
- + {
- + player.sendPacket(new ActionFailed());
- + }
- + }
- + }
- +
- + /**
- + * Manage and Display the GM console to modify the L2NpcInstance (GM only).<BR>
- + * <BR>
- + * <B><U> Actions (If the L2PcInstance is a GM only)</U> :</B><BR>
- + * <BR>
- + * <li>Set the L2NpcInstance as target of the L2PcInstance player (if necessary)</li> <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li> <li>If L2NpcInstance is autoAttackable, send a Server->Client packet StatusUpdate to the
- + * L2PcInstance in order to update L2NpcInstance HP bar</li> <li>Send a Server->Client NpcHtmlMessage() containing the GM console about this L2NpcInstance</li><BR>
- + * <BR>
- + * <FONT COLOR=#FF0000><B> <U>Caution</U> : Each group of Server->Client packet must be terminated by a ActionFailed packet in order to avoid that client wait an other packet</B></FONT><BR>
- + * <BR>
- + * <B><U> Example of use </U> :</B><BR>
- + * <BR>
- + * <li>Client packet : Action</li><BR>
- + * <BR>
- + * @param client The thread that manage the player that pessed Shift and click on the L2NpcInstance
- + */
- + @Override
- + public void onActionShift(ClientThread client)
- + {
- + // Get the L2PcInstance corresponding to the thread
- + L2PcInstance player = client.getActiveChar();
- +
- + if (player == null)
- + {
- + return;
- + }
- +
- + // Check if the L2PcInstance is a GM
- + if (player.getAccessLevel() >= Config.GM_ACCESSLEVEL)
- + {
- + // Set the target of the L2PcInstance player
- + player.setTarget(this);
- +
- + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player
- + // The player.getLevel() - getLevel() permit to display the correct color in the select window
- + MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel());
- + player.sendPacket(my);
- +
- + // Check if the player is attackable (without a forced attack)
- + if (isAutoAttackable(player))
- + {
- + // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar
- + StatusUpdate su = new StatusUpdate(getObjectId());
- + su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp());
- + su.addAttribute(StatusUpdate.MAX_HP, getMaxHp());
- + player.sendPacket(su);
- + }
- +
- + // Send a Server->Client NpcHtmlMessage() containing the GM console about this L2NpcInstance
- + NpcHtmlMessage html = new NpcHtmlMessage(0);
- + TextBuilder html1 = new TextBuilder("<html><body><center><font color=\"LEVEL\">NPC Information</font></center>");
- + String className = getClass().getName().substring(43);
- +
- + html1.append("<br>");
- +
- + html1.append("Instance Type: " + className + "<br1>Faction: " + getFactionId() + "<br1>Location ID: " + (getSpawn() != null ? getSpawn().getLocation() : 0) + "<br1>");
- +
- + if (this instanceof L2ControllableMobInstance)
- + {
- + html1.append("Mob Group: " + MobGroupTable.getInstance().getGroupForMob((L2ControllableMobInstance) this).getGroupId() + "<br>");
- + }
- + else
- + {
- + html1.append("Respawn Time: " + (getSpawn() != null ? (getSpawn().getRespawnDelay() / 1000) + " Seconds<br>" : "? Seconds<br>"));
- + }
- +
- + html1.append("<table border=\"0\" width=\"100%\">");
- + html1.append("<tr><td>Object ID</td><td>" + getObjectId() + "</td><td>NPC ID</td><td>" + getTemplate().npcId + "</td></tr>");
- +
- + html1.append("<tr><td>Castle</td><td>" + (getCastle() != null ? getCastle().getCastleId() : 0) + "</td><td>Coords</td><td>" + getX() + "," + getY() + "," + getZ() + "</td></tr>");
- +
- + html1.append("<tr><td>Level</td><td>" + getLevel() + "</td><td>Aggro</td><td>" + ((this instanceof L2Attackable) ? ((L2Attackable) this).getAggroRange() : 0) + "</td></tr>");
- + html1.append("</table><br>");
- +
- + html1.append("<font color=\"LEVEL\">Combat</font>");
- + html1.append("<table border=\"0\" width=\"100%\">");
- + html1.append("<tr><td>Current HP</td><td>" + getCurrentHp() + "</td><td>Current MP</td><td>" + getCurrentMp() + "</td></tr>");
- + html1.append("<tr><td>Max.HP</td><td>" + (int) (getMaxHp() / getStat().calcStat(Stats.MAX_HP, 1, this, null)) + "*" + getStat().calcStat(Stats.MAX_HP, 1, this, null) + "</td><td>Max.MP</td><td>" + getMaxMp() + "</td></tr>");
- + html1.append("<tr><td>P.Atk.</td><td>" + getPAtk(null) + "</td><td>M.Atk.</td><td>" + getMAtk(null, null) + "</td></tr>");
- + html1.append("<tr><td>P.Def.</td><td>" + getPDef(null) + "</td><td>M.Def.</td><td>" + getMDef(null, null) + "</td></tr>");
- + html1.append("<tr><td>Accuracy</td><td>" + getAccuracy() + "</td><td>Evasion</td><td>" + getEvasionRate(null) + "</td></tr>");
- + html1.append("<tr><td>Critical</td><td>" + getCriticalHit(null, null) + "</td><td>Speed</td><td>" + getRunSpeed() + "</td></tr>");
- + html1.append("<tr><td>Atk.Speed</td><td>" + getPAtkSpd() + "</td><td>Cast.Speed</td><td>" + getMAtkSpd() + "</td></tr>");
- + html1.append("</table><br>");
- +
- + html1.append("<font color=\"LEVEL\">Basic Stats</font>");
- + html1.append("<table border=\"0\" width=\"100%\">");
- + html1.append("<tr><td>STR</td><td>" + getSTR() + "</td><td>DEX</td><td>" + getDEX() + "</td><td>CON</td><td>" + getCON() + "</td></tr>");
- + html1.append("<tr><td>INT</td><td>" + getINT() + "</td><td>WIT</td><td>" + getWIT() + "</td><td>MEN</td><td>" + getMEN() + "</td></tr>");
- + html1.append("</table>");
- +
- + html1.append("<br><center><table><tr><td><button value=\"Edit NPC\" action=\"bypass -h admin_edit_npc " + getTemplate().npcId + "\" width=100 height=15 back=\"sek.cbui94\" fore=\"sek.cbui92\"><br1></td>");
- + html1.append("<td><button value=\"Kill\" action=\"bypass -h admin_kill\" width=40 height=15 back=\"sek.cbui94\" fore=\"sek.cbui92\"></td><br1></tr>");
- + html1.append("<tr><td><button value=\"Show DropList\" action=\"bypass -h admin_show_droplist " + getTemplate().npcId + "\" width=100 height=15 back=\"sek.cbui94\" fore=\"sek.cbui92\"></td></tr>");
- + html1.append("<td><button value=\"Delete\" action=\"bypass -h admin_delete\" width=40 height=15 back=\"sek.cbui94\" fore=\"sek.cbui92\"></td></tr>");
- + html1.append("</table></center><br>");
- + html1.append("</body></html>");
- +
- + html.setHtml(html1.toString());
- + player.sendPacket(html);
- + }
- + else if (Config.ALT_GAME_VIEWNPC)
- + {
- + // Set the target of the L2PcInstance player
- + player.setTarget(this);
- +
- + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player
- + // The player.getLevel() - getLevel() permit to display the correct color in the select window
- + MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel());
- + player.sendPacket(my);
- +
- + // Check if the player is attackable (without a forced attack)
- + if (isAutoAttackable(player))
- + {
- + // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar
- + StatusUpdate su = new StatusUpdate(getObjectId());
- + su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp());
- + su.addAttribute(StatusUpdate.MAX_HP, getMaxHp());
- + player.sendPacket(su);
- + }
- +
- + NpcHtmlMessage html = new NpcHtmlMessage(0);
- + TextBuilder html1 = new TextBuilder("<html><body>");
- +
- + html1.append("<br><center><font color=\"LEVEL\">[Combat Stats]</font></center>");
- + html1.append("<table border=0 width=\"100%\">");
- + html1.append("<tr><td>Max.HP</td><td>" + (int) (getMaxHp() / getStat().calcStat(Stats.MAX_HP, 1, this, null)) + "*" + (int) getStat().calcStat(Stats.MAX_HP, 1, this, null) + "</td><td>Max.MP</td><td>" + getMaxMp() + "</td></tr>");
- + html1.append("<tr><td>P.Atk.</td><td>" + getPAtk(null) + "</td><td>M.Atk.</td><td>" + getMAtk(null, null) + "</td></tr>");
- + html1.append("<tr><td>P.Def.</td><td>" + getPDef(null) + "</td><td>M.Def.</td><td>" + getMDef(null, null) + "</td></tr>");
- + html1.append("<tr><td>Accuracy</td><td>" + getAccuracy() + "</td><td>Evasion</td><td>" + getEvasionRate(null) + "</td></tr>");
- + html1.append("<tr><td>Critical</td><td>" + getCriticalHit(null, null) + "</td><td>Speed</td><td>" + getRunSpeed() + "</td></tr>");
- + html1.append("<tr><td>Atk.Speed</td><td>" + getPAtkSpd() + "</td><td>Cast.Speed</td><td>" + getMAtkSpd() + "</td></tr>");
- + html1.append("<tr><td>Race</td><td>" + getTemplate().race + "</td><td></td><td></td></tr>");
- + html1.append("</table>");
- +
- + html1.append("<br><center><font color=\"LEVEL\">[Basic Stats]</font></center>");
- + html1.append("<table border=0 width=\"100%\">");
- + html1.append("<tr><td>STR</td><td>" + getSTR() + "</td><td>DEX</td><td>" + getDEX() + "</td><td>CON</td><td>" + getCON() + "</td></tr>");
- + html1.append("<tr><td>INT</td><td>" + getINT() + "</td><td>WIT</td><td>" + getWIT() + "</td><td>MEN</td><td>" + getMEN() + "</td></tr>");
- + html1.append("</table>");
- +
- + html1.append("<br><center><font color=\"LEVEL\">[Drop Info]</font></center>");
- + html1.append("Rates legend: <font color=\"ff0000\">50%+</font> <font color=\"00ff00\">30%+</font> <font color=\"0000ff\">less than 30%</font>");
- + html1.append("<table border=0 width=\"100%\">");
- +
- + for (L2DropCategory cat : getTemplate().getDropData())
- + {
- + for (L2DropData drop : cat.getAllDrops())
- + {
- + String name = ItemTable.getInstance().getTemplate(drop.getItemId()).getName();
- +
- + if (drop.getChance() >= 600000)
- + {
- + html1.append("<tr><td><font color=\"ff0000\">" + name + "</font></td><td>" + (drop.isQuestDrop() ? "Quest" : (cat.isSweep() ? "Sweep" : "Drop")) + "</td></tr>");
- + }
- + else if (drop.getChance() >= 300000)
- + {
- + html1.append("<tr><td><font color=\"00ff00\">" + name + "</font></td><td>" + (drop.isQuestDrop() ? "Quest" : (cat.isSweep() ? "Sweep" : "Drop")) + "</td></tr>");
- + }
- + else
- + {
- + html1.append("<tr><td><font color=\"0000ff\">" + name + "</font></td><td>" + (drop.isQuestDrop() ? "Quest" : (cat.isSweep() ? "Sweep" : "Drop")) + "</td></tr>");
- + }
- + }
- + }
- +
- + html1.append("</table>");
- + html1.append("</body></html>");
- +
- + html.setHtml(html1.toString());
- + player.sendPacket(html);
- + }
- +
- + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
- + player.sendPacket(new ActionFailed());
- + }
- +
- + /** Return the L2Castle this L2NpcInstance belongs to. */
- + public final Castle getCastle()
- + {
- +
- + if (_castleIndex < 0)
- + {
- +
- + L2TownZone town = TownManager.getInstance().getTown(getX(), getY(), getZ());
- + if (town != null)
- + {
- + _castleIndex = CastleManager.getInstance().getCastleIndex(town.getTaxById());
- + }
- +
- + if (_castleIndex < 0)
- + {
- + _castleIndex = CastleManager.getInstance().findNearestCastleIndex(this);
- + }
- + else
- + {
- + _isInTown = true; // Npc was spawned in town
- + }
- +
- + }
- +
- + if (_castleIndex < 0)
- + {
- + return null;
- + }
- +
- + return CastleManager.getInstance().getCastles().get(_castleIndex);
- +
- + }
- +
- + public boolean getIsInCastleTown()
- + {
- + if (_castleIndex < 0)
- + {
- + getCastle();
- + }
- +
- + return _isInTown;
- + }
- +
- + /**
- + * Open a quest or chat window on client with the text of the L2NpcInstance in function of the command.<BR>
- + * <BR>
- + * <B><U> Example of use </U> :</B><BR>
- + * <BR>
- + * <li>Client packet : RequestBypassToServer</li><BR>
- + * <BR>
- + * @param command The command string received from client
- + */
- + public void onBypassFeedback(L2PcInstance player, String command)
- + {
- +
- + if (isBusy() && (getBusyMessage().length() > 0))
- + {
- + player.sendPacket(new ActionFailed());
- + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- + html.setFile("data/html/npcbusy.htm");
- + html.replace("%busymessage%", getBusyMessage());
- + html.replace("%npcname%", getName());
- + html.replace("%playername%", player.getName());
- + player.sendPacket(html);
- + }
- +
- + else if (command.equalsIgnoreCase("TerritoryStatus"))
- + {
- + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- + html.setFile("data/html/territorystatus.htm");
- + html.replace("%objectId%", String.valueOf(getObjectId()));
- + html.replace("%npcname%", getName());
- +
- + if (getIsInCastleTown())
- + {
- + html.replace("%castlename%", getCastle().getName());
- + html.replace("%taxpercent%", "" + getCastle().getTaxPercent());
- +
- + if (getCastle().getOwnerId() > 0)
- + {
- + L2Clan clan = ClanTable.getInstance().getClan(getCastle().getOwnerId());
- + html.replace("%clanname%", clan.getName());
- + html.replace("%clanleadername%", clan.getLeaderName());
- + }
- + else
- + {
- + html.replace("%clanname%", "NPC");
- + html.replace("%clanleadername%", "NPC");
- + }
- + }
- + else
- + {
- + html.replace("%castlename%", "Open");
- + html.replace("%taxpercent%", "0");
- +
- + html.replace("%clanname%", "No");
- + html.replace("%clanleadername%", "None");
- + }
- +
- + player.sendPacket(html);
- + }
- + else if (command.startsWith("Quest"))
- + {
- + String quest = "";
- + try
- + {
- + quest = command.substring(5).trim();
- + }
- + catch (IndexOutOfBoundsException ioobe)
- + {
- + }
- +
- + if (quest.length() == 0)
- + {
- + showQuestWindow(player);
- + }
- + else
- + {
- + showQuestWindow(player, quest);
- + }
- + }
- + else if (command.startsWith("Chat"))
- + {
- + int val = 0;
- + try
- + {
- + val = Integer.parseInt(command.substring(5));
- + }
- + catch (IndexOutOfBoundsException ioobe)
- + {
- + }
- + catch (NumberFormatException nfe)
- + {
- + }
- +
- + showChatWindow(player, val);
- + }
- + else if (command.startsWith("Link"))
- + {
- + String path = command.substring(5).trim();
- + if (path.indexOf("..") != -1)
- + {
- + return;
- + }
- +
- + String filename = "data/html/" + path;
- + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- + html.setFile(filename);
- + html.replace("%objectId%", String.valueOf(getObjectId()));
- + player.sendPacket(html);
- + }
- + else if (command.startsWith("NobleTeleport"))
- + {
- + if (!player.isNoble())
- + {
- + String filename = "data/html/teleporter/nobleteleporter-no.htm";
- + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- + html.setFile(filename);
- + html.replace("%objectId%", String.valueOf(getObjectId()));
- + html.replace("%npcname%", getName());
- + player.sendPacket(html);
- + return;
- + }
- +
- + int val = 0;
- + try
- + {
- + val = Integer.parseInt(command.substring(14));
- + }
- + catch (IndexOutOfBoundsException ioobe)
- + {
- + }
- + catch (NumberFormatException nfe)
- + {
- + }
- +
- + showChatWindow(player, val);
- + }
- + else if (command.startsWith("Loto"))
- + {
- + int val = 0;
- + try
- + {
- + val = Integer.parseInt(command.substring(5));
- + }
- + catch (IndexOutOfBoundsException ioobe)
- + {
- + }
- + catch (NumberFormatException nfe)
- + {
- + }
- +
- + if (val == 0)
- + {
- + // new loto ticket
- + for (int i = 0; i < 5; i++)
- + {
- + player.setLoto(i, 0);
- + }
- + }
- + showLotoWindow(player, val);
- + }
- + else if (command.startsWith("CPRecovery"))
- + {
- + makeCPRecovery(player);
- + }
- + else if (command.startsWith("SupportMagic"))
- + {
- + makeSupportMagic(player);
- + }
- + else if (command.startsWith("multisell"))
- + {
- + L2Multisell.getInstance().createMultiSell(Integer.parseInt(command.substring(9).trim()), player, false, this);
- + }
- + else if (command.startsWith("exc_multisell"))
- + {
- + L2Multisell.getInstance().createMultiSell(Integer.parseInt(command.substring(13).trim()), player, true, this);
- + }
- + else if (command.startsWith("npcfind_byid"))
- + {
- + try
- + {
- + L2Spawn spawn = SpawnTable.getInstance().getTemplate(Integer.parseInt(command.substring(12).trim()));
- +
- + if (spawn != null)
- + {
- + player.sendPacket(new RadarControl(0, 1, spawn.getLocx(), spawn.getLocy(), spawn.getLocz()));
- + }
- + }
- + catch (NumberFormatException nfe)
- + {
- + player.sendMessage("Wrong command parameters");
- + }
- + }
- +
- + }
- +
- + /**
- + * Return null (regular NPCs don't have weapons instancies).<BR>
- + * <BR>
- + */
- + @Override
- + public L2ItemInstance getActiveWeaponInstance()
- + {
- + // regular NPCs dont have weapons instancies
- + return null;
- + }
- +
- + /**
- + * Return the weapon item equiped in the right hand of the L2NpcInstance or null.<BR>
- + * <BR>
- + */
- + @Override
- + public L2Weapon getActiveWeaponItem()
- + {
- + // Get the weapon identifier equiped in the right hand of the L2NpcInstance
- + int weaponId = getTemplate().rhand;
- +
- + if (weaponId < 1)
- + {
- + return null;
- + }
- +
- + // Get the weapon item equiped in the right hand of the L2NpcInstance
- + L2Item item = ItemTable.getInstance().getTemplate(getTemplate().rhand);
- +
- + if (!(item instanceof L2Weapon))
- + {
- + return null;
- + }
- +
- + return (L2Weapon) item;
- + }
- +
- + /**
- + * Return null (regular NPCs don't have weapons instancies).<BR>
- + * <BR>
- + */
- + @Override
- + public L2ItemInstance getSecondaryWeaponInstance()
- + {
- + // regular NPCs dont have weapons instancies
- + return null;
- + }
- +
- + /**
- + * Return the weapon item equiped in the left hand of the L2NpcInstance or null.<BR>
- + * <BR>
- + */
- + @Override
- + public L2Weapon getSecondaryWeaponItem()
- + {
- + // Get the weapon identifier equiped in the right hand of the L2NpcInstance
- + int weaponId = getTemplate().lhand;
- +
- + if (weaponId < 1)
- + {
- + return null;
- + }
- +
- + // Get the weapon item equiped in the right hand of the L2NpcInstance
- + L2Item item = ItemTable.getInstance().getTemplate(getTemplate().lhand);
- +
- + if (!(item instanceof L2Weapon))
- + {
- + return null;
- + }
- +
- + return (L2Weapon) item;
- + }
- +
- + /**
- + * Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance.<BR>
- + * <BR>
- + * @param player The L2PcInstance who talks with the L2NpcInstance
- + * @param content The text of the L2NpcMessage
- + */
- + public void insertObjectIdAndShowChatWindow(L2PcInstance player, String content)
- + {
- + // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
- + content = content.replaceAll("%objectId%", String.valueOf(getObjectId()));
- + NpcHtmlMessage npcReply = new NpcHtmlMessage(getObjectId());
- + npcReply.setHtml(content);
- + player.sendPacket(npcReply);
- + }
- +
- + /**
- + * Return the pathfile of the selected HTML file in function of the npcId and of the page number.<BR>
- + * <BR>
- + * <B><U> Format of the pathfile </U> :</B><BR>
- + * <BR>
- + * <li>if the file exists on the server (page number = 0) : <B>data/html/default/12006.htm</B> (npcId-page number)</li> <li>if the file exists on the server (page number > 0) : <B>data/html/default/12006-1.htm</B> (npcId-page number)</li> <li>if the file doesn't exist on the server :
- + * <B>data/html/npcdefault.htm</B> (message : "I have nothing to say to you")</li><BR>
- + * <BR>
- + * <B><U> Overriden in </U> :</B><BR>
- + * <BR>
- + * <li>L2GuardInstance : Set the pathfile to data/html/guard/12006-1.htm (npcId-page number)</li><BR>
- + * <BR>
- + * @param npcId The Identifier of the L2NpcInstance whose text must be display
- + * @param val The number of the page to display
- + */
- + public String getHtmlPath(int npcId, int val)
- + {
- + String pom = "";
- +
- + if (val == 0)
- + {
- + pom = "" + npcId;
- + }
- + else
- + {
- + pom = npcId + "-" + val;
- + }
- +
- + String temp = "data/html/default/" + pom + ".htm";
- +
- + if (!Config.LAZY_CACHE)
- + {
- + // If not running lazy cache the file must be in the cache or it doesnt exist
- + if (HtmCache.getInstance().contains(temp))
- + {
- + return temp;
- + }
- + }
- + else
- + {
- + if (HtmCache.getInstance().isLoadable(temp))
- + {
- + return temp;
- + }
- + }
- +
- + // If the file is not found, the standard message "I have nothing to say to you" is returned
- + return "data/html/npcdefault.htm";
- + }
- +
- + public void showBuyWindow(L2PcInstance player, int val)
- + {
- + double taxRate = 0;
- + if (getIsInCastleTown())
- + {
- + taxRate = getCastle().getTaxRate();
- + }
- +
- + player.tempInventoryDisable();
- +
- + if (Config.DEBUG)
- + {
- + _log.fine("Showing buylist");
- + }
- +
- + L2TradeList list = TradeController.getInstance().getBuyList(val);
- +
- + if ((list != null) && list.getNpcId().equals(String.valueOf(getNpcId())))
- + {
- + player.sendPacket(new BuyList(list, player.getAdena(), taxRate));
- + }
- + else
- + {
- + _log.warning("possible client hacker: " + player.getName() + " attempting to buy from GM shop! < Ban him!");
- + _log.warning("buylist id:" + val);
- + }
- +
- + player.sendPacket(new ActionFailed());
- + }
- +
- + /**
- + * Open a choose quest window on client with all quests available of the L2NpcInstance.<BR>
- + * <BR>
- + * <B><U> Actions</U> :</B><BR>
- + * <BR>
- + * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li><BR>
- + * <BR>
- + * @param player The L2PcInstance that talk with the L2NpcInstance
- + * @param quests The table containing quests of the L2NpcInstance
- + */
- + public void showQuestChooseWindow(L2PcInstance player, Quest[] quests)
- + {
- + TextBuilder sb = new TextBuilder();
- +
- + sb.append("<html><body><title>Talk about:</title><br>");
- +
- + for (Quest q : quests)
- + {
- + sb.append("<a action=\"bypass -h npc_").append(getObjectId()).append("_Quest ").append(q.getName()).append("\">").append(q.getDescr()).append("</a><br>");
- + }
- +
- + sb.append("</body></html>");
- +
- + // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
- + insertObjectIdAndShowChatWindow(player, sb.toString());
- + }
- +
- + /**
- + * Open a quest window on client with the text of the L2NpcInstance.<BR>
- + * <BR>
- + * <B><U> Actions</U> :</B><BR>
- + * <BR>
- + * <li>Get the text of the quest state in the folder data/jscript/quests/questId/stateId.htm</li> <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li> <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the
- + * client wait another packet</li><BR>
- + * <BR>
- + * @param player The L2PcInstance that talk with the L2NpcInstance
- + * @param questId The Identifier of the quest to display the message
- + */
- + public void showQuestWindow(L2PcInstance player, String questId)
- + {
- + String content;
- +
- + // Get the state of the selected quest
- + QuestState qs = player.getQuestState(questId);
- +
- + if (qs != null)
- + {
- + // If the quest is already started, no need to show a window
- + if (!qs.getQuest().notifyTalk(this, qs))
- + {
- + return;
- + }
- + }
- + else
- + {
- + Quest q = QuestManager.getInstance().getQuest(questId);
- + if (q != null)
- + {
- + if ((q.getQuestIntId() >= 1) && (q.getQuestIntId() < 1000))
- + {
- + Quest[] questList = player.getAllActiveQuests();
- + if (questList.length >= 15) // if too many ongoing quests, don't show window and send message
- + {
- + player.sendPacket(new SystemMessage(401));
- + return;
- + }
- +
- + if ((player.getWeightPenalty() >= 3) || ((player.getInventoryLimit() * 0.8) <= player.getInventory().getSize()))
- + {
- + player.sendPacket(new SystemMessage(1118));
- + return;
- + }
- + }
- +
- + // check for start point
- + Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
- +
- + if ((qlst != null) && (qlst.length > 0))
- + {
- + for (Quest element : qlst)
- + {
- + if (element == q)
- + {
- + qs = q.newQuestState(player);
- + // disabled by mr. because quest dialog only show on second click.
- + // if(qs.getState().getName().equalsIgnoreCase("completed"))
- + // {
- + if (!qs.getQuest().notifyTalk(this, qs))
- + {
- + return; // no need to show a window
- + }
- + // }
- + break;
- + }
- + }
- + }
- + }
- + }
- +
- + if (qs == null)
- + {
- + // no quests found
- + content = "<html><body>I have no tasks for you right now.</body></html>";
- + }
- + else
- + {
- + questId = qs.getQuest().getName();
- + String stateId = qs.getStateId();
- + String path = "data/jscript/quests/" + questId + "/" + stateId + ".htm";
- + content = HtmCache.getInstance().getHtm(path); // TODO path for quests html
- +
- + if (Config.DEBUG)
- + {
- + if (content != null)
- + {
- + _log.fine("Showing quest window for quest " + questId + " html path: " + path);
- + }
- + else
- + {
- + _log.fine("File not exists for quest " + questId + " html path: " + path);
- + }
- + }
- + }
- +
- + // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
- + if (content != null)
- + {
- + insertObjectIdAndShowChatWindow(player, content);
- + }
- +
- + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
- + player.sendPacket(new ActionFailed());
- + }
- +
- + /**
- + * Collect awaiting quests/start points and display a QuestChooseWindow (if several available) or QuestWindow.<BR>
- + * <BR>
- + * @param player The L2PcInstance that talk with the L2NpcInstance
- + */
- + public void showQuestWindow(L2PcInstance player)
- + {
- + // collect awaiting quests and start points
- + List<Quest> options = new FastList<Quest>();
- +
- + QuestState[] awaits = player.getQuestsForTalk(getTemplate().npcId);
- + Quest[] starts = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
- +
- + // Quests are limited between 1 and 999 because those are the quests that are supported by the client.
- + if (awaits != null)
- + {
- + for (QuestState x : awaits)
- + {
- + if (!options.contains(x))
- + {
- + if ((x.getQuest().getQuestIntId() > 0) && (x.getQuest().getQuestIntId() < 1000))
- + {
- + options.add(x.getQuest());
- + }
- + }
- + }
- + }
- +
- + if (starts != null)
- + {
- + for (Quest x : starts)
- + {
- + if (!options.contains(x))
- + {
- + if ((x.getQuestIntId() > 0) && (x.getQuestIntId() < 1000))
- + {
- + options.add(x);
- + }
- + }
- + }
- + }
- +
- + // Display a QuestChooseWindow (if several quests are available) or QuestWindow
- + if (options.size() > 1)
- + {
- + showQuestChooseWindow(player, options.toArray(new Quest[options.size()]));
- + }
- + else if (options.size() == 1)
- + {
- + showQuestWindow(player, options.get(0).getName());
- + }
- + else
- + {
- + showQuestWindow(player, "");
- + }
- + }
- +
- + /**
- + * Open a Loto window on client with the text of the L2NpcInstance.<BR>
- + * <BR>
- + * <B><U> Actions</U> :</B><BR>
- + * <BR>
- + * <li>Get the text of the selected HTML file in function of the npcId and of the page number</li> <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li> <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the
- + * client wait another packet</li><BR>
- + * @param player The L2PcInstance that talk with the L2NpcInstance
- + * @param val The number of the page of the L2NpcInstance to display
- + */
- + // 0 - first buy lottery ticket window
- + // 1-20 - buttons
- + // 21 - second buy lottery ticket window
- + // 22 - selected ticket with 5 numbers
- + // 23 - current lottery jackpot
- + // 24 - Previous winning numbers/Prize claim
- + // >24 - check lottery ticket by item object id
- + public void showLotoWindow(L2PcInstance player, int val)
- + {
- + int npcId = getTemplate().npcId;
- + String filename;
- + SystemMessage sm;
- + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- +
- + if (val == 0) // 0 - first buy lottery ticket window
- + {
- + filename = (getHtmlPath(npcId, 1));
- + html.setFile(filename);
- + }
- + else if ((val >= 1) && (val <= 21)) // 1-20 - buttons, 21 - second buy lottery ticket window
- + {
- + if (!Lottery.getInstance().isStarted())
- + {
- + // tickets can't be sold
- + player.sendPacket(new SystemMessage(930));
- + return;
- + }
- + if (!Lottery.getInstance().isSellableTickets())
- + {
- + // tickets can't be sold
- + player.sendPacket(new SystemMessage(784));
- + return;
- + }
- +
- filename = (getHtmlPath(npcId, 5));
- html.setFile(filename);
- int count = 0;
- int found = 0;
- // counting buttons and unsetting button if found
- - for (int i = 0; i < 5; i++)
- - {
- - if (player.getLoto(i) == val)
- - {
- - //unsetting button
- - player.setLoto(i, 0);
- - found = 1;
- - }
- - else if (player.getLoto(i) > 0)
- - {
- - count++;
- - }
- - }
- -
- - //if not rearched limit 5 and not unseted value
- - if (count < 5 && found == 0 && val <= 20)
- - for (int i = 0; i < 5; i++)
- - if (player.getLoto(i) == 0)
- - {
- - player.setLoto(i, val);
- - break;
- - }
- -
- - //setting pusshed buttons
- - count = 0;
- - for (int i = 0; i < 5; i++)
- - if (player.getLoto(i) > 0)
- - {
- - count++;
- - String button = String.valueOf(player.getLoto(i));
- - if (player.getLoto(i) < 10) button = "0" + button;
- - String search = "fore=\"L2UI.lottoNum" + button + "\" back=\"L2UI.lottoNum" + button + "a_check\"";
- - String replace = "fore=\"L2UI.lottoNum" + button + "a_check\" back=\"L2UI.lottoNum" + button + "\"";
- - html.replace(search, replace);
- - }
- -
- - if (count == 5)
- - {
- - String search = "0\">Return";
- - String replace = "22\">The winner selected the numbers above.";
- - html.replace(search, replace);
- - }
- - }
- - else if (val == 22) //22 - selected ticket with 5 numbers
- - {
- - if (!Lottery.getInstance().isStarted())
- - {
- - //tickets can't be sold
- - player.sendPacket(new SystemMessage(930));
- - return;
- - }
- - if (!Lottery.getInstance().isSellableTickets())
- - {
- - //tickets can't be sold
- - player.sendPacket(new SystemMessage(784));
- - return;
- - }
- -
- - int price = Config.ALT_LOTTERY_TICKET_PRICE;
- - int lotonumber = Lottery.getInstance().getId();
- - int enchant = 0;
- - int type2 = 0;
- -
- - for (int i = 0; i < 5; i++)
- - {
- - if (player.getLoto(i) == 0) return;
- -
- - if (player.getLoto(i) < 17) enchant += Math.pow(2, player.getLoto(i) - 1);
- - else type2 += Math.pow(2, player.getLoto(i) - 17);
- - }
- - if (player.getAdena() < price)
- - {
- - sm = new SystemMessage(SystemMessage.YOU_NOT_ENOUGH_ADENA);
- - player.sendPacket(sm);
- - return;
- - }
- - if (!player.reduceAdena("Loto", price, this, true)) return;
- - Lottery.getInstance().increasePrize(price);
- -
- - sm = new SystemMessage(SystemMessage.ACQUIRED);
- - sm.addNumber(lotonumber);
- - sm.addItemName(4442);
- - player.sendPacket(sm);
- -
- - L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), 4442);
- + for (int i = 0; i < 5; i++)
- + {
- + if (player.getLoto(i) == val)
- + {
- + // unsetting button
- + player.setLoto(i, 0);
- + found = 1;
- + }
- + else if (player.getLoto(i) > 0)
- + {
- + count++;
- + }
- + }
- +
- + // if not rearched limit 5 and not unseted value
- + if ((count < 5) && (found == 0) && (val <= 20))
- + {
- + for (int i = 0; i < 5; i++)
- + {
- + if (player.getLoto(i) == 0)
- + {
- + player.setLoto(i, val);
- + break;
- + }
- + }
- + }
- +
- + // setting pusshed buttons
- + count = 0;
- + for (int i = 0; i < 5; i++)
- + {
- + if (player.getLoto(i) > 0)
- + {
- + count++;
- + String button = String.valueOf(player.getLoto(i));
- + if (player.getLoto(i) < 10)
- + {
- + button = "0" + button;
- + }
- + String search = "fore=\"L2UI.lottoNum" + button + "\" back=\"L2UI.lottoNum" + button + "a_check\"";
- + String replace = "fore=\"L2UI.lottoNum" + button + "a_check\" back=\"L2UI.lottoNum" + button + "\"";
- + html.replace(search, replace);
- + }
- + }
- +
- + if (count == 5)
- + {
- + String search = "0\">Return";
- + String replace = "22\">The winner selected the numbers above.";
- + html.replace(search, replace);
- + }
- + }
- + else if (val == 22) // 22 - selected ticket with 5 numbers
- + {
- + if (!Lottery.getInstance().isStarted())
- + {
- + // tickets can't be sold
- + player.sendPacket(new SystemMessage(930));
- + return;
- + }
- + if (!Lottery.getInstance().isSellableTickets())
- + {
- + // tickets can't be sold
- + player.sendPacket(new SystemMessage(784));
- + return;
- + }
- +
- + int price = Config.ALT_LOTTERY_TICKET_PRICE;
- + int lotonumber = Lottery.getInstance().getId();
- + int enchant = 0;
- + int type2 = 0;
- +
- + for (int i = 0; i < 5; i++)
- + {
- + if (player.getLoto(i) == 0)
- + {
- + return;
- + }
- +
- + if (player.getLoto(i) < 17)
- + {
- + enchant += Math.pow(2, player.getLoto(i) - 1);
- + }
- + else
- + {
- + type2 += Math.pow(2, player.getLoto(i) - 17);
- + }
- + }
- + if (player.getAdena() < price)
- + {
- + sm = new SystemMessage(SystemMessage.YOU_NOT_ENOUGH_ADENA);
- + player.sendPacket(sm);
- + return;
- + }
- + if (!player.reduceAdena("Loto", price, this, true))
- + {
- + return;
- + }
- + Lottery.getInstance().increasePrize(price);
- +
- + sm = new SystemMessage(SystemMessage.ACQUIRED);
- + sm.addNumber(lotonumber);
- + sm.addItemName(4442);
- + player.sendPacket(sm);
- +
- + L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), 4442);
- item.setCount(1);
- item.setCustomType1(lotonumber);
- item.setEnchantLevel(enchant);
- @@ -1504,720 +1651,812 @@
- L2ItemInstance adenaupdate = player.getInventory().getItemByItemId(57);
- iu.addModifiedItem(adenaupdate);
- player.sendPacket(iu);
- -
- - filename = (getHtmlPath(npcId, 3));
- - html.setFile(filename);
- - }
- - else if (val == 23) //23 - current lottery jackpot
- - {
- - filename = (getHtmlPath(npcId, 3));
- - html.setFile(filename);
- - }
- - else if (val == 24) // 24 - Previous winning numbers/Prize claim
- - {
- - filename = (getHtmlPath(npcId, 4));
- - html.setFile(filename);
- -
- - int lotonumber = Lottery.getInstance().getId();
- - String message = "";
- - for (L2ItemInstance item : player.getInventory().getItems())
- - {
- - if (item == null) continue;
- - if (item.getItemId() == 4442 && item.getCustomType1() < lotonumber)
- - {
- - message = message + "<a action=\"bypass -h npc_%objectId%_Loto "
- - + item.getObjectId() + "\">" + item.getCustomType1() + " Event Number ";
- - int[] numbers = Lottery.getInstance().decodeNumbers(item.getEnchantLevel(),
- - item.getCustomType2());
- - for (int i = 0; i < 5; i++)
- - {
- - message += numbers[i] + " ";
- - }
- - int[] check = Lottery.getInstance().checkTicket(item);
- - if (check[0] > 0)
- - {
- - switch (check[0])
- - {
- - case 1:
- - message += "- 1st Prize";
- - break;
- - case 2:
- - message += "- 2nd Prize";
- - break;
- - case 3:
- - message += "- 3th Prize";
- - break;
- - case 4:
- - message += "- 4th Prize";
- - break;
- - }
- - message += " " + check[1] + "a.";
- - }
- - message += "</a><br>";
- - }
- - }
- - if (message == "")
- - {
- - message += "There is no winning lottery ticket...<br>";
- - }
- - html.replace("%result%", message);
- - }
- - else if (val > 24) // >24 - check lottery ticket by item object id
- - {
- - int lotonumber = Lottery.getInstance().getId();
- - L2ItemInstance item = player.getInventory().getItemByObjectId(val);
- - if (item == null || item.getItemId() != 4442 || item.getCustomType1() >= lotonumber) return;
- - int[] check = Lottery.getInstance().checkTicket(item);
- -
- - sm = new SystemMessage(SystemMessage.DISSAPEARED_ITEM);
- - sm.addItemName(4442);
- - player.sendPacket(sm);
- -
- - int adena = check[1];
- - if (adena > 0)
- - player.addAdena("Loto", adena, this, true);
- - player.destroyItem("Loto", item, this, false);
- - return;
- - }
- - html.replace("%objectId%", String.valueOf(getObjectId()));
- - html.replace("%race%", "" + Lottery.getInstance().getId());
- - html.replace("%adena%", "" + Lottery.getInstance().getPrize());
- - html.replace("%ticket_price%", "" + Config.ALT_LOTTERY_TICKET_PRICE);
- - html.replace("%prize5%", "" + (Config.ALT_LOTTERY_5_NUMBER_RATE * 100));
- - html.replace("%prize4%", "" + (Config.ALT_LOTTERY_4_NUMBER_RATE * 100));
- - html.replace("%prize3%", "" + (Config.ALT_LOTTERY_3_NUMBER_RATE * 100));
- - html.replace("%prize2%", "" + Config.ALT_LOTTERY_2_AND_1_NUMBER_PRIZE);
- - html.replace("%enddate%", "" + DateFormat.getDateInstance().format(Lottery.getInstance().getEndDate()));
- - player.sendPacket(html);
- -
- - // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
- - player.sendPacket(new ActionFailed());
- - }
- -
- - public void makeCPRecovery(L2PcInstance player)
- - {
- - if (getNpcId() != 8225 && getNpcId() != 8226)
- - return;
- -
- - int neededmoney = 100;
- -
- - if (!player.reduceAdena("RestoreCP", neededmoney, player.getLastFolkNPC(), true))
- - return;
- -
- - L2Skill skill = SkillTable.getInstance().getInfo(4380, 1);
- - if (skill != null)
- - {
- - setTarget(player);
- - doCast(skill);
- - }
- - player.sendPacket(new ActionFailed());
- - }
- -
- -
- - /**
- - * Add Newbie helper buffs to L2Player according to its level.<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Get the range level in wich player must be to obtain buff </li>
- - * <li>If player level is out of range, display a message and return </li>
- - * <li>According to player level cast buff </li><BR><BR>
- - *
- - * <FONT COLOR=#FF0000><B> Newbie Helper Buff list is define in sql table helper_buff_list</B></FONT><BR><BR>
- - *
- - * @param player The L2PcInstance that talk with the L2NpcInstance
- - *
- - */
- - public void makeSupportMagic(L2PcInstance player)
- - {
- - int player_level = player.getLevel();
- - int lowestLevel;
- - int higestLevel;
- - L2Skill skill;
- -
- -
- - // Select the player
- - setTarget(player);
- -
- -
- - // Calculate the min and max level between wich the player must be to obtain buff
- - if(player.isMageClass())
- - {
- - lowestLevel = HelperBuffTable.getInstance().getMagicClassLowestLevel();
- - higestLevel = HelperBuffTable.getInstance().getMagicClassHighestLevel();
- - }
- - else
- - {
- - lowestLevel = HelperBuffTable.getInstance().getPhysicClassLowestLevel();
- - higestLevel = HelperBuffTable.getInstance().getPhysicClassHighestLevel();
- - }
- -
- -
- - // If the player is too high level, display a message and return
- - if (player_level > higestLevel)
- - {
- - String content = "<html><body>Newbie Guide:<br>Only a <font color=\"LEVEL\">novice character of level "+ higestLevel +" or less</font> can receive my support magic.<br>Your novice character is the first one that you created and raised in this world.</body></html>";
- - insertObjectIdAndShowChatWindow(player, content);
- - return;
- - }
- -
- -
- - // If the player is too low level, display a message and return
- - if (player_level < lowestLevel)
- - {
- - String content = "<html><body>Come back here when you have reached level "+ lowestLevel +". I will give you support magic then.</body></html>";
- - insertObjectIdAndShowChatWindow(player, content);
- - return;
- - }
- -
- -
- - // Go through the Helper Buff list define in sql table helper_buff_list and cast skill
- - for (L2HelperBuff helperBuffItem : HelperBuffTable.getInstance().getHelperBuffTable())
- - {
- - if( helperBuffItem.isMagicClassBuff() == player.isMageClass())
- - {
- - if(player_level>=helperBuffItem.getLowerLevel() && player_level<=helperBuffItem.getUpperLevel())
- - {
- - skill = SkillTable.getInstance().getInfo(helperBuffItem.getSkillID(),helperBuffItem.getSkillLevel());
- - if (skill.getSkillType() == SkillType.SUMMON)
- - player.doCast(skill);
- - else
- - doCast(skill);
- - }
- - }
- - }
- -
- -
- - }
- -
- - public void showChatWindow(L2PcInstance player)
- - {
- - showChatWindow(player, 0);
- - }
- -
- - /**
- - * Open a chat window on client with the text of the L2NpcInstance.<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Get the text of the selected HTML file in function of the npcId and of the page number </li>
- - * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance </li>
- - * <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet </li><BR>
- - *
- - * @param player The L2PcInstance that talk with the L2NpcInstance
- - * @param val The number of the page of the L2NpcInstance to display
- - *
- - */
- - public void showChatWindow(L2PcInstance player, int val)
- - {
- - if (getTemplate().type == "L2Auctioneer" && val==0)
- - return;
- - int npcId = getTemplate().npcId;
- -
- - /* For use with Seven Signs implementation */
- - String filename = SevenSigns.SEVEN_SIGNS_HTML_PATH;
- - int sealAvariceOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_AVARICE);
- - int sealGnosisOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_GNOSIS);
- - int playerCabal = SevenSigns.getInstance().getPlayerCabal(player);
- - boolean isSealValidationPeriod = SevenSigns.getInstance().isSealValidationPeriod();
- - int compWinner = SevenSigns.getInstance().getCabalHighestScore();
- -
- - switch (npcId) {
- - case 8078:
- - case 8079:
- - case 8080:
- - case 8081:
- - case 8082: // Dawn Priests
- - case 8083:
- - case 8084:
- - case 8168:
- - case 8692:
- - case 8694:
- - switch (playerCabal)
- - {
- - case SevenSigns.CABAL_DAWN:
- - if (isSealValidationPeriod)
- - if (compWinner == SevenSigns.CABAL_DAWN)
- - if (compWinner != sealGnosisOwner)
- - filename += "dawn_priest_2c.htm";
- - else
- - filename += "dawn_priest_2a.htm";
- - else
- - filename += "dawn_priest_2b.htm";
- - else
- - filename += "dawn_priest_1b.htm";
- - break;
- - case SevenSigns.CABAL_DUSK:
- - if (isSealValidationPeriod)
- - filename += "dawn_priest_3b.htm";
- - else
- - filename += "dawn_priest_3a.htm";
- - break;
- - default:
- - if (isSealValidationPeriod)
- - if (compWinner == SevenSigns.CABAL_DAWN)
- - filename += "dawn_priest_4.htm";
- - else
- - filename += "dawn_priest_2b.htm";
- - else
- - filename += "dawn_priest_1a.htm";
- - break;
- - }
- - break;
- - case 8085:
- - case 8086:
- - case 8087:
- - case 8088: // Dusk Priest
- - case 8089:
- - case 8090:
- - case 8091:
- - case 8169:
- - case 8693:
- - case 8695:
- - switch (playerCabal)
- - {
- - case SevenSigns.CABAL_DUSK:
- - if (isSealValidationPeriod)
- - if (compWinner == SevenSigns.CABAL_DUSK)
- - if (compWinner != sealGnosisOwner)
- - filename += "dusk_priest_2c.htm";
- - else
- - filename += "dusk_priest_2a.htm";
- - else
- - filename += "dusk_priest_2b.htm";
- - else
- - filename += "dusk_priest_1b.htm";
- - break;
- - case SevenSigns.CABAL_DAWN:
- - if (isSealValidationPeriod)
- - filename += "dusk_priest_3b.htm";
- - else
- - filename += "dusk_priest_3a.htm";
- - break;
- - default:
- - if (isSealValidationPeriod)
- - if (compWinner == SevenSigns.CABAL_DUSK)
- - filename += "dusk_priest_4.htm";
- - else
- - filename += "dusk_priest_2b.htm";
- - else
- - filename += "dusk_priest_1a.htm";
- - break;
- - }
- - break;
- - case 8095: //
- - case 8096: //
- - case 8097: //
- - case 8098: // Enter Necropolises
- - case 8099: //
- - case 8100: //
- - case 8101: //
- - case 8102: //
- - if (isSealValidationPeriod)
- - {
- - if (playerCabal != compWinner || sealAvariceOwner != compWinner)
- - {
- - switch (compWinner)
- - {
- - case SevenSigns.CABAL_DAWN:
- - player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DAWN));
- - filename += "necro_no.htm";
- - break;
- - case SevenSigns.CABAL_DUSK:
- - player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DUSK));
- - filename += "necro_no.htm";
- - break;
- - case SevenSigns.CABAL_NULL:
- - filename = (getHtmlPath(npcId, val)); // do the default!
- - break;
- - }
- - }
- - else
- - filename = (getHtmlPath(npcId, val)); // do the default!
- - }
- - else
- - {
- - if (playerCabal == SevenSigns.CABAL_NULL)
- - filename += "necro_no.htm";
- - else
- - filename = (getHtmlPath(npcId, val)); // do the default!
- - }
- - break;
- - case 8114: //
- - case 8115: //
- - case 8116: // Enter Catacombs
- - case 8117: //
- - case 8118: //
- - case 8119: //
- - if (isSealValidationPeriod)
- - {
- - if (playerCabal != compWinner || sealGnosisOwner != compWinner)
- - {
- - switch (compWinner)
- - {
- - case SevenSigns.CABAL_DAWN:
- - player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DAWN));
- - filename += "cata_no.htm";
- - break;
- - case SevenSigns.CABAL_DUSK:
- - player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DUSK));
- - filename += "cata_no.htm";
- - break;
- - case SevenSigns.CABAL_NULL:
- - filename = (getHtmlPath(npcId, val)); // do the default!
- - break;
- - }
- - }
- - else
- - filename = (getHtmlPath(npcId, val)); // do the default!
- - }
- - else
- - {
- - if (playerCabal == SevenSigns.CABAL_NULL)
- - filename += "cata_no.htm";
- - else
- - filename = (getHtmlPath(npcId, val)); // do the default!
- - }
- - break;
- - case 8111: // Gatekeeper Spirit (Disciples)
- - if (playerCabal == sealAvariceOwner && playerCabal == compWinner)
- - {
- - switch (sealAvariceOwner)
- - {
- - case SevenSigns.CABAL_DAWN:
- - filename += "spirit_dawn.htm";
- - break;
- - case SevenSigns.CABAL_DUSK:
- - filename += "spirit_dusk.htm";
- - break;
- - case SevenSigns.CABAL_NULL:
- - filename += "spirit_null.htm";
- - break;
- - }
- - }
- - else
- - {
- - filename += "spirit_null.htm";
- - }
- - break;
- - case 8112: // Gatekeeper Spirit (Disciples)
- - filename += "spirit_exit.htm";
- - break;
- - case 8127: //
- - case 8128: //
- - case 8129: // Dawn Festival Guides
- - case 8130: //
- - case 8131: //
- - filename += "festival/dawn_guide.htm";
- - break;
- - case 8137: //
- - case 8138: //
- - case 8139: // Dusk Festival Guides
- - case 8140: //
- - case 8141: //
- - filename += "festival/dusk_guide.htm";
- - break;
- - case 8092: // Black Marketeer of Mammon
- - filename += "blkmrkt_1.htm";
- - break;
- - case 8113: // Merchant of Mammon
- - switch (compWinner)
- - {
- - case SevenSigns.CABAL_DAWN:
- - if (playerCabal != compWinner || playerCabal != sealAvariceOwner)
- - {
- - player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DAWN));
- - player.sendPacket(new ActionFailed());
- - return;
- - }
- - break;
- - case SevenSigns.CABAL_DUSK:
- - if (playerCabal != compWinner || playerCabal != sealAvariceOwner)
- - {
- - player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DUSK));
- - player.sendPacket(new ActionFailed());
- - return;
- - }
- - break;
- - }
- - filename += "mammmerch_1.htm";
- - break;
- - case 8126: // Blacksmith of Mammon
- - switch (compWinner)
- - {
- - case SevenSigns.CABAL_DAWN:
- - if (playerCabal != compWinner || playerCabal != sealGnosisOwner)
- - {
- - player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DAWN));
- - player.sendPacket(new ActionFailed());
- - return;
- - }
- - break;
- - case SevenSigns.CABAL_DUSK:
- - if (playerCabal != compWinner || playerCabal != sealGnosisOwner)
- - {
- - player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DUSK));
- - player.sendPacket(new ActionFailed());
- - return;
- - }
- - break;
- - }
- - filename += "mammblack_1.htm";
- - break;
- - case 8132:
- - case 8133:
- - case 8134:
- - case 8135:
- - case 8136: // Festival Witches
- - case 8142:
- - case 8143:
- - case 8144:
- - case 8145:
- - case 8146:
- - filename += "festival/festival_witch.htm";
- - break;
- - case 8688:
- - if (player.isNoble())
- - filename = Olympiad.OLYMPIAD_HTML_FILE + "noble_main.htm";
- - else
- - filename = (getHtmlPath(npcId, val));
- - break;
- - case 8690:
- - case 8769:
- - case 8770:
- - case 8771:
- - case 8772:
- - if (player.isHero())
- - filename = Olympiad.OLYMPIAD_HTML_FILE + "hero_main.htm";
- - else
- - filename = (getHtmlPath(npcId, val));
- - break;
- - default:
- - if ((npcId >= 8093 && npcId <= 8094) ||
- - (npcId >= 8172 && npcId <= 8201) ||
- - (npcId >= 8239 && npcId <= 8254))
- - return;
- - // Get the text of the selected HTML file in function of the npcId and of the page number
- - filename = (getHtmlPath(npcId, val));
- - break;
- - }
- -
- - // Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance
- - NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- - html.setFile(filename);
- -
- - //String word = "npc-"+npcId+(val>0 ? "-"+val : "" )+"-dialog-append";
- -
- - if (this instanceof L2MerchantInstance)
- - if (Config.LIST_PET_RENT_NPC.contains(npcId))
- - html.replace("_Quest", "_RentPet\">Rent Pet</a><br><a action=\"bypass -h npc_%objectId%_Quest");
- -
- - html.replace("%objectId%", String.valueOf(getObjectId()));
- - html.replace("%festivalMins%", SevenSignsFestival.getInstance().getTimeToNextFestivalStr());
- - player.sendPacket(html);
- -
- - // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
- - player.sendPacket( new ActionFailed());
- - }
- -
- - /**
- - * Open a chat window on client with the text specified by the given file name and path,<BR>
- - * relative to the datapack root.
- - * <BR><BR>
- - * Added by Tempy
- - * @param player The L2PcInstance that talk with the L2NpcInstance
- - * @param filename The filename that contains the text to send
- - *
- - */
- - public void showChatWindow(L2PcInstance player, String filename)
- - {
- - // Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance
- - NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- - html.setFile(filename);
- - html.replace("%objectId%",String.valueOf(getObjectId()));
- - player.sendPacket(html);
- -
- - // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
- - player.sendPacket( new ActionFailed() );
- - }
- -
- - /**
- - * Return the Exp Reward of this L2NpcInstance contained in the L2NpcTemplate (modified by RATE_XP).<BR><BR>
- - */
- - public int getExpReward()
- - {
- - double rateXp = getStat().calcStat(Stats.MAX_HP , 1, this, null);
- - return (int)(getTemplate().rewardExp * rateXp * Config.RATE_XP);
- - }
- -
- - /**
- - * Return the SP Reward of this L2NpcInstance contained in the L2NpcTemplate (modified by RATE_SP).<BR><BR>
- - */
- - public int getSpReward()
- - {
- - double rateSp = getStat().calcStat(Stats.MAX_HP , 1, this, null);
- - return (int)(getTemplate().rewardSp * rateSp * Config.RATE_SP);
- - }
- -
- - /**
- - * Kill the L2NpcInstance (the corpse disappeared after 7 seconds).<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Create a DecayTask to remove the corpse of the L2NpcInstance after 7 seconds </li>
- - * <li>Set target to null and cancel Attack or Cast </li>
- - * <li>Stop movement </li>
- - * <li>Stop HP/MP/CP Regeneration task </li>
- - * <li>Stop all active skills effects in progress on the L2Character </li>
- - * <li>Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform </li>
- - * <li>Notify L2Character AI </li><BR><BR>
- - *
- - * <B><U> Overriden in </U> :</B><BR><BR>
- - * <li> L2Attackable </li><BR><BR>
- - *
- - * @param killer The L2Character who killed it
- - *
- - */
- - public boolean doDie(L2Character killer)
- - {
- - if (!super.doDie(killer))
- - return false;
- -
- - _currentCollisionHeight = getTemplate().collisionHeight;
- - _currentCollisionRadius = getTemplate().collisionRadius;
- - DecayTaskManager.getInstance().addDecayTask(this);
- - return true;
- - }
- -
- - /**
- - * Set the spawn of the L2NpcInstance.<BR><BR>
- - *
- - * @param spawn The L2Spawn that manage the L2NpcInstance
- - *
- - */
- - public void setSpawn(L2Spawn spawn)
- - {
- - _spawn = spawn;
- - }
- -
- - @Override
- - public void onSpawn()
- - {
- - super.onSpawn();
- - }
- -
- - /**
- - * Remove the L2NpcInstance from the world and update its spawn object (for a complete removal use the deleteMe method).<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Remove the L2NpcInstance from the world when the decay task is launched </li>
- - * <li>Decrease its spawn counter </li>
- - * <li>Manage Siege task (killFlag, killCT) </li><BR><BR>
- - *
- - * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T REMOVE the object from _allObjects of L2World </B></FONT><BR>
- - * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packets to players</B></FONT><BR><BR>
- - *
- - */
- - public void onDecay()
- - {
- - if (isDecayed()) return;
- - setDecayed(true);
- -
- - // Manage Life Control Tower
- - if (this instanceof L2ControlTowerInstance)
- - ((L2ControlTowerInstance)this).onDeath();
- -
- - // Remove the L2NpcInstance from the world when the decay task is launched
- - super.onDecay();
- -
- - // Decrease its spawn counter
- - if (_spawn != null)
- - _spawn.decreaseCount(this);
- - }
- -
- - /**
- - * Remove PROPERLY the L2NpcInstance from the world.<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Remove the L2NpcInstance from the world and update its spawn object </li>
- - * <li>Remove all L2Object from _knownObjects and _knownPlayer of the L2NpcInstance then cancel Attak or Cast and notify AI </li>
- - * <li>Remove L2Object object from _allObjects of L2World </li><BR><BR>
- - *
- - * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packets to players</B></FONT><BR><BR>
- - *
- - */
- - public void deleteMe()
- - {
- - L2WorldRegion oldRegion = getWorldRegion();
- -
- - try
- - {
- - decayMe();
- - }
- - catch (Throwable t)
- - {
- - _log.severe("deletedMe(): " + t);
- - }
- -
- -
- - if (oldRegion != null)
- - oldRegion.removeFromZones(this);
- -
- - // Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then cancel Attak or Cast and notify AI
- - try { getKnownList().removeAllKnownObjects(); } catch (Throwable t) {_log.severe("deletedMe(): " + t); }
- -
- - // Remove L2Object object from _allObjects of L2World
- - L2World.getInstance().removeObject(this);
- - }
- -
- - /**
- - * Return the L2Spawn object that manage this L2NpcInstance.<BR><BR>
- - */
- - public L2Spawn getSpawn()
- - {
- - return _spawn;
- - }
- -
- - public String toString()
- - {
- - return getTemplate().name;
- - }
- -
- - public boolean isDecayed()
- - {
- - return _isDecayed;
- - }
- -
- - public void setDecayed(boolean decayed)
- - {
- - _isDecayed = decayed;
- - }
- -
- - public void endDecayTask()
- - {
- - if (!isDecayed())
- - {
- -
- - DecayTaskManager.getInstance().cancelDecayTask(this);
- - onDecay();
- - }
- - }
- -
- -
- - public void setCollisionHeight(int height)
- - {
- - _currentCollisionHeight = height;
- - }
- -
- - public void setCollisionRadius(int radius)
- - {
- - _currentCollisionRadius = radius;
- - }
- -
- - public int getCollisionHeight()
- - {
- - return _currentCollisionHeight;
- - }
- -
- - public int getCollisionRadius()
- - {
- - return _currentCollisionRadius;
- - }
- +
- + filename = (getHtmlPath(npcId, 3));
- + html.setFile(filename);
- + }
- + else if (val == 23) // 23 - current lottery jackpot
- + {
- + filename = (getHtmlPath(npcId, 3));
- + html.setFile(filename);
- + }
- + else if (val == 24) // 24 - Previous winning numbers/Prize claim
- + {
- + filename = (getHtmlPath(npcId, 4));
- + html.setFile(filename);
- +
- + int lotonumber = Lottery.getInstance().getId();
- + String message = "";
- + for (L2ItemInstance item : player.getInventory().getItems())
- + {
- + if (item == null)
- + {
- + continue;
- + }
- + if ((item.getItemId() == 4442) && (item.getCustomType1() < lotonumber))
- + {
- + message = message + "<a action=\"bypass -h npc_%objectId%_Loto " + item.getObjectId() + "\">" + item.getCustomType1() + " Event Number ";
- + int[] numbers = Lottery.getInstance().decodeNumbers(item.getEnchantLevel(), item.getCustomType2());
- + for (int i = 0; i < 5; i++)
- + {
- + message += numbers[i] + " ";
- + }
- + int[] check = Lottery.getInstance().checkTicket(item);
- + if (check[0] > 0)
- + {
- + switch (check[0])
- + {
- + case 1:
- + message += "- 1st Prize";
- + break;
- + case 2:
- + message += "- 2nd Prize";
- + break;
- + case 3:
- + message += "- 3th Prize";
- + break;
- + case 4:
- + message += "- 4th Prize";
- + break;
- + }
- + message += " " + check[1] + "a.";
- + }
- + message += "</a><br>";
- + }
- + }
- + if (message == "")
- + {
- + message += "There is no winning lottery ticket...<br>";
- + }
- + html.replace("%result%", message);
- + }
- + else if (val > 24) // >24 - check lottery ticket by item object id
- + {
- + int lotonumber = Lottery.getInstance().getId();
- + L2ItemInstance item = player.getInventory().getItemByObjectId(val);
- + if ((item == null) || (item.getItemId() != 4442) || (item.getCustomType1() >= lotonumber))
- + {
- + return;
- + }
- + int[] check = Lottery.getInstance().checkTicket(item);
- +
- + sm = new SystemMessage(SystemMessage.DISSAPEARED_ITEM);
- + sm.addItemName(4442);
- + player.sendPacket(sm);
- +
- + int adena = check[1];
- + if (adena > 0)
- + {
- + player.addAdena("Loto", adena, this, true);
- + }
- + player.destroyItem("Loto", item, this, false);
- + return;
- + }
- + html.replace("%objectId%", String.valueOf(getObjectId()));
- + html.replace("%race%", "" + Lottery.getInstance().getId());
- + html.replace("%adena%", "" + Lottery.getInstance().getPrize());
- + html.replace("%ticket_price%", "" + Config.ALT_LOTTERY_TICKET_PRICE);
- + html.replace("%prize5%", "" + (Config.ALT_LOTTERY_5_NUMBER_RATE * 100));
- + html.replace("%prize4%", "" + (Config.ALT_LOTTERY_4_NUMBER_RATE * 100));
- + html.replace("%prize3%", "" + (Config.ALT_LOTTERY_3_NUMBER_RATE * 100));
- + html.replace("%prize2%", "" + Config.ALT_LOTTERY_2_AND_1_NUMBER_PRIZE);
- + html.replace("%enddate%", "" + DateFormat.getDateInstance().format(Lottery.getInstance().getEndDate()));
- + player.sendPacket(html);
- +
- + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
- + player.sendPacket(new ActionFailed());
- + }
- +
- + public void makeCPRecovery(L2PcInstance player)
- + {
- + if ((getNpcId() != 8225) && (getNpcId() != 8226))
- + {
- + return;
- + }
- +
- + int neededmoney = 100;
- +
- + if (!player.reduceAdena("RestoreCP", neededmoney, player.getLastFolkNPC(), true))
- + {
- + return;
- + }
- +
- + L2Skill skill = SkillTable.getInstance().getInfo(4380, 1);
- + if (skill != null)
- + {
- + setTarget(player);
- + doCast(skill);
- + }
- + player.sendPacket(new ActionFailed());
- + }
- +
- + /**
- + * Add Newbie helper buffs to L2Player according to its level.<BR>
- + * <BR>
- + * <B><U> Actions</U> :</B><BR>
- + * <BR>
- + * <li>Get the range level in wich player must be to obtain buff</li> <li>If player level is out of range, display a message and return</li> <li>According to player level cast buff</li><BR>
- + * <BR>
- + * <FONT COLOR=#FF0000><B> Newbie Helper Buff list is define in sql table helper_buff_list</B></FONT><BR>
- + * <BR>
- + * @param player The L2PcInstance that talk with the L2NpcInstance
- + */
- + public void makeSupportMagic(L2PcInstance player)
- + {
- + int player_level = player.getLevel();
- + int lowestLevel;
- + int higestLevel;
- + L2Skill skill;
- +
- + // Select the player
- + setTarget(player);
- +
- + // Calculate the min and max level between wich the player must be to obtain buff
- + if (player.isMageClass())
- + {
- + lowestLevel = HelperBuffTable.getInstance().getMagicClassLowestLevel();
- + higestLevel = HelperBuffTable.getInstance().getMagicClassHighestLevel();
- + }
- + else
- + {
- + lowestLevel = HelperBuffTable.getInstance().getPhysicClassLowestLevel();
- + higestLevel = HelperBuffTable.getInstance().getPhysicClassHighestLevel();
- + }
- +
- + // If the player is too high level, display a message and return
- + if (player_level > higestLevel)
- + {
- + String content = "<html><body>Newbie Guide:<br>Only a <font color=\"LEVEL\">novice character of level " + higestLevel + " or less</font> can receive my support magic.<br>Your novice character is the first one that you created and raised in this world.</body></html>";
- + insertObjectIdAndShowChatWindow(player, content);
- + return;
- + }
- +
- + // If the player is too low level, display a message and return
- + if (player_level < lowestLevel)
- + {
- + String content = "<html><body>Come back here when you have reached level " + lowestLevel + ". I will give you support magic then.</body></html>";
- + insertObjectIdAndShowChatWindow(player, content);
- + return;
- + }
- +
- + // Go through the Helper Buff list define in sql table helper_buff_list and cast skill
- + for (L2HelperBuff helperBuffItem : HelperBuffTable.getInstance().getHelperBuffTable())
- + {
- + if (helperBuffItem.isMagicClassBuff() == player.isMageClass())
- + {
- + if ((player_level >= helperBuffItem.getLowerLevel()) && (player_level <= helperBuffItem.getUpperLevel()))
- + {
- + skill = SkillTable.getInstance().getInfo(helperBuffItem.getSkillID(), helperBuffItem.getSkillLevel());
- + if (skill.getSkillType() == SkillType.SUMMON)
- + {
- + player.doCast(skill);
- + }
- + else
- + {
- + doCast(skill);
- + }
- + }
- + }
- + }
- +
- + }
- +
- + public void showChatWindow(L2PcInstance player)
- + {
- + showChatWindow(player, 0);
- + }
- +
- + /**
- + * Open a chat window on client with the text of the L2NpcInstance.<BR>
- + * <BR>
- + * <B><U> Actions</U> :</B><BR>
- + * <BR>
- + * <li>Get the text of the selected HTML file in function of the npcId and of the page number</li> <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li> <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the
- + * client wait another packet</li><BR>
- + * @param player The L2PcInstance that talk with the L2NpcInstance
- + * @param val The number of the page of the L2NpcInstance to display
- + */
- + public void showChatWindow(L2PcInstance player, int val)
- + {
- + if ((getTemplate().type == "L2Auctioneer") && (val == 0))
- + {
- + return;
- + }
- + int npcId = getTemplate().npcId;
- +
- + /* For use with Seven Signs implementation */
- + String filename = SevenSigns.SEVEN_SIGNS_HTML_PATH;
- + int sealAvariceOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_AVARICE);
- + int sealGnosisOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_GNOSIS);
- + int playerCabal = SevenSigns.getInstance().getPlayerCabal(player);
- + boolean isSealValidationPeriod = SevenSigns.getInstance().isSealValidationPeriod();
- + int compWinner = SevenSigns.getInstance().getCabalHighestScore();
- +
- + switch (npcId)
- + {
- + case 8078:
- + case 8079:
- + case 8080:
- + case 8081:
- + case 8082: // Dawn Priests
- + case 8083:
- + case 8084:
- + case 8168:
- + case 8692:
- + case 8694:
- + switch (playerCabal)
- + {
- + case SevenSigns.CABAL_DAWN:
- + if (isSealValidationPeriod)
- + {
- + if (compWinner == SevenSigns.CABAL_DAWN)
- + {
- + if (compWinner != sealGnosisOwner)
- + {
- + filename += "dawn_priest_2c.htm";
- + }
- + else
- + {
- + filename += "dawn_priest_2a.htm";
- + }
- + }
- + else
- + {
- + filename += "dawn_priest_2b.htm";
- + }
- + }
- + else
- + {
- + filename += "dawn_priest_1b.htm";
- + }
- + break;
- + case SevenSigns.CABAL_DUSK:
- + if (isSealValidationPeriod)
- + {
- + filename += "dawn_priest_3b.htm";
- + }
- + else
- + {
- + filename += "dawn_priest_3a.htm";
- + }
- + break;
- + default:
- + if (isSealValidationPeriod)
- + {
- + if (compWinner == SevenSigns.CABAL_DAWN)
- + {
- + filename += "dawn_priest_4.htm";
- + }
- + else
- + {
- + filename += "dawn_priest_2b.htm";
- + }
- + }
- + else
- + {
- + filename += "dawn_priest_1a.htm";
- + }
- + break;
- + }
- + break;
- + case 8085:
- + case 8086:
- + case 8087:
- + case 8088: // Dusk Priest
- + case 8089:
- + case 8090:
- + case 8091:
- + case 8169:
- + case 8693:
- + case 8695:
- + switch (playerCabal)
- + {
- + case SevenSigns.CABAL_DUSK:
- + if (isSealValidationPeriod)
- + {
- + if (compWinner == SevenSigns.CABAL_DUSK)
- + {
- + if (compWinner != sealGnosisOwner)
- + {
- + filename += "dusk_priest_2c.htm";
- + }
- + else
- + {
- + filename += "dusk_priest_2a.htm";
- + }
- + }
- + else
- + {
- + filename += "dusk_priest_2b.htm";
- + }
- + }
- + else
- + {
- + filename += "dusk_priest_1b.htm";
- + }
- + break;
- + case SevenSigns.CABAL_DAWN:
- + if (isSealValidationPeriod)
- + {
- + filename += "dusk_priest_3b.htm";
- + }
- + else
- + {
- + filename += "dusk_priest_3a.htm";
- + }
- + break;
- + default:
- + if (isSealValidationPeriod)
- + {
- + if (compWinner == SevenSigns.CABAL_DUSK)
- + {
- + filename += "dusk_priest_4.htm";
- + }
- + else
- + {
- + filename += "dusk_priest_2b.htm";
- + }
- + }
- + else
- + {
- + filename += "dusk_priest_1a.htm";
- + }
- + break;
- + }
- + break;
- + case 8095: //
- + case 8096: //
- + case 8097: //
- + case 8098: // Enter Necropolises
- + case 8099: //
- + case 8100: //
- + case 8101: //
- + case 8102: //
- + if (isSealValidationPeriod)
- + {
- + if ((playerCabal != compWinner) || (sealAvariceOwner != compWinner))
- + {
- + switch (compWinner)
- + {
- + case SevenSigns.CABAL_DAWN:
- + player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DAWN));
- + filename += "necro_no.htm";
- + break;
- + case SevenSigns.CABAL_DUSK:
- + player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DUSK));
- + filename += "necro_no.htm";
- + break;
- + case SevenSigns.CABAL_NULL:
- + filename = (getHtmlPath(npcId, val)); // do the default!
- + break;
- + }
- + }
- + else
- + {
- + filename = (getHtmlPath(npcId, val)); // do the default!
- + }
- + }
- + else
- + {
- + if (playerCabal == SevenSigns.CABAL_NULL)
- + {
- + filename += "necro_no.htm";
- + }
- + else
- + {
- + filename = (getHtmlPath(npcId, val)); // do the default!
- + }
- + }
- + break;
- + case 8114: //
- + case 8115: //
- + case 8116: // Enter Catacombs
- + case 8117: //
- + case 8118: //
- + case 8119: //
- + if (isSealValidationPeriod)
- + {
- + if ((playerCabal != compWinner) || (sealGnosisOwner != compWinner))
- + {
- + switch (compWinner)
- + {
- + case SevenSigns.CABAL_DAWN:
- + player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DAWN));
- + filename += "cata_no.htm";
- + break;
- + case SevenSigns.CABAL_DUSK:
- + player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DUSK));
- + filename += "cata_no.htm";
- + break;
- + case SevenSigns.CABAL_NULL:
- + filename = (getHtmlPath(npcId, val)); // do the default!
- + break;
- + }
- + }
- + else
- + {
- + filename = (getHtmlPath(npcId, val)); // do the default!
- + }
- + }
- + else
- + {
- + if (playerCabal == SevenSigns.CABAL_NULL)
- + {
- + filename += "cata_no.htm";
- + }
- + else
- + {
- + filename = (getHtmlPath(npcId, val)); // do the default!
- + }
- + }
- + break;
- + case 8111: // Gatekeeper Spirit (Disciples)
- + if ((playerCabal == sealAvariceOwner) && (playerCabal == compWinner))
- + {
- + switch (sealAvariceOwner)
- + {
- + case SevenSigns.CABAL_DAWN:
- + filename += "spirit_dawn.htm";
- + break;
- + case SevenSigns.CABAL_DUSK:
- + filename += "spirit_dusk.htm";
- + break;
- + case SevenSigns.CABAL_NULL:
- + filename += "spirit_null.htm";
- + break;
- + }
- + }
- + else
- + {
- + filename += "spirit_null.htm";
- + }
- + break;
- + case 8112: // Gatekeeper Spirit (Disciples)
- + filename += "spirit_exit.htm";
- + break;
- + case 8127: //
- + case 8128: //
- + case 8129: // Dawn Festival Guides
- + case 8130: //
- + case 8131: //
- + filename += "festival/dawn_guide.htm";
- + break;
- + case 8137: //
- + case 8138: //
- + case 8139: // Dusk Festival Guides
- + case 8140: //
- + case 8141: //
- + filename += "festival/dusk_guide.htm";
- + break;
- + case 8092: // Black Marketeer of Mammon
- + filename += "blkmrkt_1.htm";
- + break;
- + case 8113: // Merchant of Mammon
- + switch (compWinner)
- + {
- + case SevenSigns.CABAL_DAWN:
- + if ((playerCabal != compWinner) || (playerCabal != sealAvariceOwner))
- + {
- + player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DAWN));
- + player.sendPacket(new ActionFailed());
- + return;
- + }
- + break;
- + case SevenSigns.CABAL_DUSK:
- + if ((playerCabal != compWinner) || (playerCabal != sealAvariceOwner))
- + {
- + player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DUSK));
- + player.sendPacket(new ActionFailed());
- + return;
- + }
- + break;
- + }
- + filename += "mammmerch_1.htm";
- + break;
- + case 8126: // Blacksmith of Mammon
- + switch (compWinner)
- + {
- + case SevenSigns.CABAL_DAWN:
- + if ((playerCabal != compWinner) || (playerCabal != sealGnosisOwner))
- + {
- + player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DAWN));
- + player.sendPacket(new ActionFailed());
- + return;
- + }
- + break;
- + case SevenSigns.CABAL_DUSK:
- + if ((playerCabal != compWinner) || (playerCabal != sealGnosisOwner))
- + {
- + player.sendPacket(new SystemMessage(SystemMessage.CAN_BE_USED_BY_DUSK));
- + player.sendPacket(new ActionFailed());
- + return;
- + }
- + break;
- + }
- + filename += "mammblack_1.htm";
- + break;
- + case 8132:
- + case 8133:
- + case 8134:
- + case 8135:
- + case 8136: // Festival Witches
- + case 8142:
- + case 8143:
- + case 8144:
- + case 8145:
- + case 8146:
- + filename += "festival/festival_witch.htm";
- + break;
- + case 8688:
- + if (player.isNoble())
- + {
- + filename = Olympiad.OLYMPIAD_HTML_FILE + "noble_main.htm";
- + }
- + else
- + {
- + filename = (getHtmlPath(npcId, val));
- + }
- + break;
- + case 8690:
- + case 8769:
- + case 8770:
- + case 8771:
- + case 8772:
- + if (player.isHero())
- + {
- + filename = Olympiad.OLYMPIAD_HTML_FILE + "hero_main.htm";
- + }
- + else
- + {
- + filename = (getHtmlPath(npcId, val));
- + }
- + break;
- + default:
- + if (((npcId >= 8093) && (npcId <= 8094)) || ((npcId >= 8172) && (npcId <= 8201)) || ((npcId >= 8239) && (npcId <= 8254)))
- + {
- + return;
- + }
- + // Get the text of the selected HTML file in function of the npcId and of the page number
- + filename = (getHtmlPath(npcId, val));
- + break;
- + }
- +
- + // Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance
- + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- + html.setFile(filename);
- +
- + // String word = "npc-"+npcId+(val>0 ? "-"+val : "" )+"-dialog-append";
- +
- + if (this instanceof L2MerchantInstance)
- + {
- + if (Config.LIST_PET_RENT_NPC.contains(npcId))
- + {
- + html.replace("_Quest", "_RentPet\">Rent Pet</a><br><a action=\"bypass -h npc_%objectId%_Quest");
- + }
- + }
- +
- + html.replace("%objectId%", String.valueOf(getObjectId()));
- + html.replace("%festivalMins%", SevenSignsFestival.getInstance().getTimeToNextFestivalStr());
- + player.sendPacket(html);
- +
- + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
- + player.sendPacket(new ActionFailed());
- + }
- +
- + /**
- + * Open a chat window on client with the text specified by the given file name and path,<BR>
- + * relative to the datapack root. <BR>
- + * <BR>
- + * Added by Tempy
- + * @param player The L2PcInstance that talk with the L2NpcInstance
- + * @param filename The filename that contains the text to send
- + */
- + public void showChatWindow(L2PcInstance player, String filename)
- + {
- + // Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance
- + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
- + html.setFile(filename);
- + html.replace("%objectId%", String.valueOf(getObjectId()));
- + player.sendPacket(html);
- +
- + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
- + player.sendPacket(new ActionFailed());
- + }
- +
- + /**
- + * Return the Exp Reward of this L2NpcInstance contained in the L2NpcTemplate (modified by RATE_XP).<BR>
- + * <BR>
- + */
- + public int getExpReward()
- + {
- + double rateXp = getStat().calcStat(Stats.MAX_HP, 1, this, null);
- + return (int) (getTemplate().rewardExp * rateXp * Config.RATE_XP);
- + }
- +
- + /**
- + * Return the SP Reward of this L2NpcInstance contained in the L2NpcTemplate (modified by RATE_SP).<BR>
- + * <BR>
- + */
- + public int getSpReward()
- + {
- + double rateSp = getStat().calcStat(Stats.MAX_HP, 1, this, null);
- + return (int) (getTemplate().rewardSp * rateSp * Config.RATE_SP);
- + }
- +
- + /**
- + * Kill the L2NpcInstance (the corpse disappeared after 7 seconds).<BR>
- + * <BR>
- + * <B><U> Actions</U> :</B><BR>
- + * <BR>
- + * <li>Create a DecayTask to remove the corpse of the L2NpcInstance after 7 seconds</li> <li>Set target to null and cancel Attack or Cast</li> <li>Stop movement</li> <li>Stop HP/MP/CP Regeneration task</li> <li>Stop all active skills effects in progress on the L2Character</li> <li>Send the
- + * Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform</li> <li>Notify L2Character AI</li><BR>
- + * <BR>
- + * <B><U> Overriden in </U> :</B><BR>
- + * <BR>
- + * <li>L2Attackable</li><BR>
- + * <BR>
- + * @param killer The L2Character who killed it
- + */
- + @Override
- + public boolean doDie(L2Character killer)
- + {
- + if (!super.doDie(killer))
- + {
- + return false;
- + }
- +
- + _currentCollisionHeight = getTemplate().collisionHeight;
- + _currentCollisionRadius = getTemplate().collisionRadius;
- + DecayTaskManager.getInstance().addDecayTask(this);
- + return true;
- + }
- +
- + /**
- + * Set the spawn of the L2NpcInstance.<BR>
- + * <BR>
- + * @param spawn The L2Spawn that manage the L2NpcInstance
- + */
- + public void setSpawn(L2Spawn spawn)
- + {
- + _spawn = spawn;
- + }
- +
- + @Override
- + public void onSpawn()
- + {
- + super.onSpawn();
- + }
- +
- + /**
- + * Remove the L2NpcInstance from the world and update its spawn object (for a complete removal use the deleteMe method).<BR>
- + * <BR>
- + * <B><U> Actions</U> :</B><BR>
- + * <BR>
- + * <li>Remove the L2NpcInstance from the world when the decay task is launched</li> <li>Decrease its spawn counter</li> <li>Manage Siege task (killFlag, killCT)</li><BR>
- + * <BR>
- + * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T REMOVE the object from _allObjects of L2World </B></FONT><BR>
- + * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packets to players</B></FONT><BR>
- + * <BR>
- + */
- + @Override
- + public void onDecay()
- + {
- + if (isDecayed())
- + {
- + return;
- + }
- + setDecayed(true);
- +
- + // Manage Life Control Tower
- + if (this instanceof L2ControlTowerInstance)
- + {
- + ((L2ControlTowerInstance) this).onDeath();
- + }
- +
- + // Remove the L2NpcInstance from the world when the decay task is launched
- + super.onDecay();
- +
- + // Decrease its spawn counter
- + if (_spawn != null)
- + {
- + _spawn.decreaseCount(this);
- + }
- + }
- +
- + /**
- + * Remove PROPERLY the L2NpcInstance from the world.<BR>
- + * <BR>
- + * <B><U> Actions</U> :</B><BR>
- + * <BR>
- + * <li>Remove the L2NpcInstance from the world and update its spawn object</li> <li>Remove all L2Object from _knownObjects and _knownPlayer of the L2NpcInstance then cancel Attak or Cast and notify AI</li> <li>Remove L2Object object from _allObjects of L2World</li><BR>
- + * <BR>
- + * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packets to players</B></FONT><BR>
- + * <BR>
- + */
- + public void deleteMe()
- + {
- + L2WorldRegion oldRegion = getWorldRegion();
- +
- + try
- + {
- + decayMe();
- + }
- + catch (Throwable t)
- + {
- + _log.severe("deletedMe(): " + t);
- + }
- +
- + if (oldRegion != null)
- + {
- + oldRegion.removeFromZones(this);
- + }
- +
- + // Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then cancel Attak or Cast and notify AI
- + try
- + {
- + getKnownList().removeAllKnownObjects();
- + }
- + catch (Throwable t)
- + {
- + _log.severe("deletedMe(): " + t);
- + }
- +
- + // Remove L2Object object from _allObjects of L2World
- + L2World.getInstance().removeObject(this);
- + }
- +
- + /**
- + * Return the L2Spawn object that manage this L2NpcInstance.<BR>
- + * <BR>
- + */
- + public L2Spawn getSpawn()
- + {
- + return _spawn;
- + }
- +
- + @Override
- + public String toString()
- + {
- + return getTemplate().name;
- + }
- +
- + public boolean isDecayed()
- + {
- + return _isDecayed;
- + }
- +
- + public void setDecayed(boolean decayed)
- + {
- + _isDecayed = decayed;
- + }
- +
- + public void endDecayTask()
- + {
- + if (!isDecayed())
- + {
- +
- + DecayTaskManager.getInstance().cancelDecayTask(this);
- + onDecay();
- + }
- + }
- +
- + public void setCollisionHeight(int height)
- + {
- + _currentCollisionHeight = height;
- + }
- +
- + public void setCollisionRadius(int radius)
- + {
- + _currentCollisionRadius = radius;
- + }
- +
- + public int getCollisionHeight()
- + {
- + return _currentCollisionHeight;
- + }
- +
- + public int getCollisionRadius()
- + {
- + return _currentCollisionRadius;
- + }
- }
- \ No newline at end of file
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement