rendered paste bodyIndex: java/net/sf/l2j/gameserver/clientpackets/MoveBackwardToLocation.java
===================================================================
--- java/net/sf/l2j/gameserver/clientpackets/MoveBackwardToLocation.java (revision 2917)
+++ java/net/sf/l2j/gameserver/clientpackets/MoveBackwardToLocation.java (working copy)
@@ -108,7 +108,7 @@
{
activeChar.sendPacket(new ActionFailed());
}
- else if (activeChar.isAttackingNow() && activeChar.getActiveWeaponItem() != null && (activeChar.getActiveWeaponItem().getItemType() == L2WeaponType.BOW))
+ else if (activeChar.isAttackingNow() && activeChar.getActiveWeaponItem() != null && ((activeChar.getActiveWeaponItem().getItemType() == L2WeaponType.BOW) || (activeChar.getActiveWeaponItem().getItemType() == L2WeaponType.CROSSBOW)))
{
activeChar.sendPacket(new ActionFailed());
}
Index: java/net/sf/l2j/gameserver/network/SystemMessageId.java
===================================================================
--- java/net/sf/l2j/gameserver/network/SystemMessageId.java (revision 2920)
+++ java/net/sf/l2j/gameserver/network/SystemMessageId.java (working copy)
@@ -785,10 +785,22 @@
/**
* ID: 112<br>
- * Message: You may no longer adjust items in the trade because the trade has been confirmed.
+ * Message: You have run out of arrows.
*/
NOT_ENOUGH_ARROWS(112),
+ /**
+ * ID: 2224<br>
+ * Message: Crossbow is preparing to fire.
+ */
+ CROSSBOW_PREPARING_TO_FIRE(2224),
+
+ /**
+ * ID: 2226<br>
+ * Message: Not enough bolts.
+ */
+ NOT_ENOUGH_BOLTS(2226),
+
/**
* ID: 129<br>
* Message: Your inventory is full.
Index: java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java
===================================================================
--- java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java (revision 2917)
+++ java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java (working copy)
@@ -556,6 +556,7 @@
private long _requestExpireTime = 0;
private L2Request _request = new L2Request(this);
private L2ItemInstance _arrowItem;
+ private L2ItemInstance _boltItem;
// Used for protection after teleport
private long _protectEndTime = 0;
@@ -5314,6 +5315,66 @@
return _arrowItem != null;
}
+ /**
+ * Reduce the number of bolts owned by the L2PcInstance and send it Server->Client Packet InventoryUpdate or ItemList (to unequip if the last bolt was consummed).<BR><BR>
+ */
+ @Override
+ protected void reduceBoltCount()
+ {
+ L2ItemInstance bolts = getInventory().destroyItem("Consume", getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_LHAND), 1, this, null);
+
+ if (Config.DEBUG) _log.fine("bolt count:" + (bolts==null? 0 : bolts.getCount()));
+
+ if (bolts == null || bolts.getCount() == 0)
+ {
+ getInventory().unEquipItemInSlot(Inventory.PAPERDOLL_LHAND);
+ _boltItem = null;
+
+ if (Config.DEBUG) _log.fine("removed bolts count");
+ sendPacket(new ItemList(this,false));
+ }
+ else
+ {
+ if (!Config.FORCE_INVENTORY_UPDATE)
+ {
+ InventoryUpdate iu = new InventoryUpdate();
+ iu.addModifiedItem(bolts);
+ sendPacket(iu);
+ }
+ else sendPacket(new ItemList(this, false));
+ }
+ }
+
+ /**
+ * Equip bolts needed in left hand and send a Server->Client packet ItemList to the L2PcINstance then return True.<BR><BR>
+ */
+ @Override
+ protected boolean checkAndEquipBolts()
+ {
+ // Check if nothing is equiped in left hand
+ if (getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND) == null)
+ {
+ // Get the L2ItemInstance of the arrows needed for this bow
+ _boltItem = getInventory().findBoltForCrossBow(getActiveWeaponItem());
+
+ if (_boltItem != null)
+ {
+ // Equip arrows needed in left hand
+ getInventory().setPaperdollItem(Inventory.PAPERDOLL_LHAND, _boltItem);
+
+ // Send a Server->Client packet ItemList to this L2PcINstance to update left hand equipement
+ ItemList il = new ItemList(this, false);
+ sendPacket(il);
+ }
+ }
+ else
+ {
+ // Get the L2ItemInstance of arrows equiped in left hand
+ _boltItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
+ }
+
+ return _boltItem != null;
+ }
/**
* Disarm the player's weapon and shield.<BR><BR>
*/
Index: java/net/sf/l2j/gameserver/model/L2Character.java
===================================================================
--- java/net/sf/l2j/gameserver/model/L2Character.java (revision 2917)
+++ java/net/sf/l2j/gameserver/model/L2Character.java (working copy)
@@ -35,7 +35,6 @@
import net.sf.l2j.Config;
import net.sf.l2j.gameserver.GameTimeController;
import net.sf.l2j.gameserver.GeoData;
-import net.sf.l2j.gameserver.Olympiad;
import net.sf.l2j.gameserver.ThreadPoolManager;
import net.sf.l2j.gameserver.ai.CtrlEvent;
import net.sf.l2j.gameserver.ai.CtrlIntention;
@@ -77,15 +76,12 @@
import net.sf.l2j.gameserver.serverpackets.ChangeWaitType;
import net.sf.l2j.gameserver.serverpackets.CharInfo;
import net.sf.l2j.gameserver.serverpackets.EtcStatusUpdate;
-import net.sf.l2j.gameserver.serverpackets.MoveToLocation;
-import net.sf.l2j.gameserver.serverpackets.ExOlympiadSpelledInfo;
import net.sf.l2j.gameserver.serverpackets.L2GameServerPacket;
-import net.sf.l2j.gameserver.serverpackets.AbnormalStatusUpdate;
import net.sf.l2j.gameserver.serverpackets.MagicSkillCanceld;
import net.sf.l2j.gameserver.serverpackets.MagicSkillLaunched;
import net.sf.l2j.gameserver.serverpackets.MagicSkillUse;
+import net.sf.l2j.gameserver.serverpackets.MoveToLocation;
import net.sf.l2j.gameserver.serverpackets.NpcInfo;
-import net.sf.l2j.gameserver.serverpackets.PartySpelled;
import net.sf.l2j.gameserver.serverpackets.PetInfo;
import net.sf.l2j.gameserver.serverpackets.RelationChanged;
import net.sf.l2j.gameserver.serverpackets.Revive;
@@ -547,6 +543,7 @@
* <B><U> Actions</U> :</B><BR><BR>
* <li>Get the active weapon (always equiped in the right hand) </li><BR><BR>
* <li>If weapon is a bow, check for arrows, MP and bow re-use delay (if necessary, equip the L2PcInstance with arrows in left hand)</li>
+ * <li>If weapon is a crossbow, check for bolts and crossbow re-use delay (if necessary, equip the L2PcInstance with bolts in left hand)</li>
* <li>If weapon is a bow, consume MP and set the new period of bow non re-use </li><BR><BR>
* <li>Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack) </li>
* <li>Select the type of attack to start (Simple, Bow, Pole or Dual) and verify if SoulShot are charged then start calculation</li>
@@ -686,10 +683,52 @@
return;
}
}
- else if (this instanceof L2NpcInstance)
- {
- if (_disableBowAttackEndTime > GameTimeController.getGameTicks())
- return;
+ }
+ // Check for a crossbow
+ if ((weaponItem != null && weaponItem.getItemType() == L2WeaponType.CROSSBOW))
+ {
+ //Check for bolts
+ if (this instanceof L2PcInstance)
+ {
+ // Checking if target has moved to peace zone - only for player-crossbow attacks at the moment
+ // Other melee is checked in movement code and for offensive spells a check is done every time
+ if (target.isInsidePeaceZone((L2PcInstance)this))
+ {
+ getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
+ sendPacket(new ActionFailed());
+ return;
+ }
+
+ // Verify if the crossbow can be use
+ if (_disableCrossBowAttackEndTime <= GameTimeController.getGameTicks())
+ {
+ // Set the period of bow non re-use
+ _disableCrossBowAttackEndTime = 5 * GameTimeController.TICKS_PER_SECOND + GameTimeController.getGameTicks();
+ }
+ else
+ {
+ // Cancel the action because the crossbow can't be re-use at this moment
+ ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(CtrlEvent.EVT_READY_TO_ACT), 1000);
+
+ sendPacket(new ActionFailed());
+ return;
+ }
+
+ // Equip bolts needed in left hand and send a Server->Client packet ItemList to the L2PcINstance then return True
+ if (!checkAndEquipBolts())
+ {
+ // Cancel the action because the L2PcInstance have no arrow
+ getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
+
+ sendPacket(new ActionFailed());
+ sendPacket(new SystemMessage(SystemMessageId.NOT_ENOUGH_BOLTS));
+ return;
+ }
+ }
+ else if (this instanceof L2NpcInstance)
+ {
+ if (_disableCrossBowAttackEndTime > GameTimeController.getGameTicks())
+ return;
}
}
@@ -743,6 +782,8 @@
hitted = doAttackHitSimple(attack, target, timeToHit);
else if (weaponItem.getItemType() == L2WeaponType.BOW)
hitted = doAttackHitByBow(attack, target, timeAtk, reuse);
+ else if (weaponItem.getItemType() == L2WeaponType.CROSSBOW)
+ hitted = doAttackHitByCrossBow(attack, target, timeAtk, reuse);
else if (weaponItem.getItemType() == L2WeaponType.POLE)
hitted = doAttackHitByPole(attack, timeToHit);
else if (isUsingDualWeapon())
@@ -878,6 +919,78 @@
return !miss1;
}
+ /**
+ * Launch a CrossBow attack.<BR><BR>
+ *
+ * <B><U> Actions</U> :</B><BR><BR>
+ * <li>Calculate if hit is missed or not </li>
+ * <li>Consumme bolts </li>
+ * <li>If hit isn't missed, calculate if shield defense is efficient </li>
+ * <li>If hit isn't missed, calculate if hit is critical </li>
+ * <li>If hit isn't missed, calculate physical damages </li>
+ * <li>If the L2Character is a L2PcInstance, Send a Server->Client packet SetupGauge </li>
+ * <li>Create a new hit task with Medium priority</li>
+ * <li>Calculate and set the disable delay of the crossbow in function of the Attack Speed</li>
+ * <li>Add this hit to the Server-Client packet Attack </li><BR><BR>
+ *
+ * @param attack Server->Client packet Attack in which the hit will be added
+ * @param target The L2Character targeted
+ * @param sAtk The Attack Speed of the attacker
+ *
+ * @return True if the hit isn't missed
+ *
+ */
+ private boolean doAttackHitByCrossBow(Attack attack, L2Character target, int sAtk, int reuse)
+ {
+ int damage1 = 0;
+ boolean shld1 = false;
+ boolean crit1 = false;
+
+ // Calculate if hit is missed or not
+ boolean miss1 = Formulas.getInstance().calcHitMiss(this, target);
+
+ // Consumme arrows
+ reduceBoltCount();
+
+ _move = null;
+
+ // Check if hit isn't missed
+ if (!miss1)
+ {
+ // Calculate if shield defense is efficient
+ shld1 = Formulas.getInstance().calcShldUse(this, target);
+
+ // Calculate if hit is critical
+ crit1 = Formulas.getInstance().calcCrit(getStat().getCriticalHit(target, null));
+
+ // Calculate physical damages
+ damage1 = (int)Formulas.getInstance().calcPhysDam(this, target, null, shld1, crit1, false, attack.soulshot);
+ }
+
+ // Check if the L2Character is a L2PcInstance
+ if (this instanceof L2PcInstance)
+ {
+ // Send a system message
+ sendPacket(new SystemMessage(SystemMessageId.CROSSBOW_PREPARING_TO_FIRE));
+
+ // Send a Server->Client packet SetupGauge
+ SetupGauge sg = new SetupGauge(SetupGauge.RED, sAtk+reuse);
+ sendPacket(sg);
+ }
+
+ // Create a new hit task with Medium priority
+ ThreadPoolManager.getInstance().scheduleAi(new HitTask(target, damage1, crit1, miss1, attack.soulshot, shld1), sAtk);
+
+ // Calculate and set the disable delay of the bow in function of the Attack Speed
+ _disableCrossBowAttackEndTime = (sAtk+reuse)/GameTimeController.MILLIS_IN_TICK + GameTimeController.getGameTicks();
+
+ // Add this hit to the Server-Client packet Attack
+ attack.addHit(target, damage1, miss1, crit1, shld1);
+
+ // Return true if hit isn't missed
+ return !miss1;
+ }
+
/**
* Launch a Dual attack.<BR><BR>
*
@@ -3022,6 +3135,7 @@
private int _attackEndTime;
private int _attacking;
private int _disableBowAttackEndTime;
+ private int _disableCrossBowAttackEndTime;
/** Table of calculators containing all standard NPC calculator (ex : ACCURACY_COMBAT, EVASION_RATE */
@@ -4481,6 +4595,18 @@
return true;
}
+ /**
+ * Retun True if bolts are available.<BR><BR>
+ *
+ * <B><U> Overriden in </U> :</B><BR><BR>
+ * <li> L2PcInstance</li><BR><BR>
+ *
+ */
+ protected boolean checkAndEquipBolts()
+ {
+ return true;
+ }
+
/**
* Add Exp and Sp to the L2Character.<BR><BR>
*
@@ -4808,6 +4934,11 @@
// default is to do nothin
}
+ protected void reduceBoltCount()
+ {
+ // default is to do nothin
+ }
+
/**
* Manage Forced attack (shift + select target).<BR><BR>
*
Index: java/net/sf/l2j/gameserver/model/Inventory.java
===================================================================
--- java/net/sf/l2j/gameserver/model/Inventory.java (revision 2917)
+++ java/net/sf/l2j/gameserver/model/Inventory.java (working copy)
@@ -169,6 +169,33 @@
}
}
+ final class CrossBowListener implements PaperdollListener
+ {
+ public void notifyUnequiped(int slot, L2ItemInstance item)
+ {
+ if (slot != PAPERDOLL_LRHAND)
+ return;
+ if (Config.ASSERT) assert null == getPaperdollItem(PAPERDOLL_LRHAND);
+ if (item.getItemType() == L2WeaponType.CROSSBOW)
+ {
+ L2ItemInstance bolts = getPaperdollItem(PAPERDOLL_LHAND);
+ if (bolts != null)
+ setPaperdollItem(PAPERDOLL_LHAND, null);
+ }
+ }
+ public void notifyEquiped(int slot, L2ItemInstance item)
+ {
+ if (slot != PAPERDOLL_LRHAND)
+ return;
+ if (Config.ASSERT) assert item == getPaperdollItem(PAPERDOLL_LRHAND);
+ if (item.getItemType() == L2WeaponType.CROSSBOW)
+ {
+ L2ItemInstance bolts = findBoltForCrossBow(item.getItem());
+ if (bolts != null)
+ setPaperdollItem(PAPERDOLL_LHAND, bolts);
+ }
+ }
+ }
final class BowListener implements PaperdollListener
{
public void notifyUnequiped(int slot, L2ItemInstance item)
@@ -501,6 +528,7 @@
_paperdoll = new L2ItemInstance[25];
_paperdollListeners = new FastList<PaperdollListener>();
addPaperdollListener(new ArmorSetListener());
+ addPaperdollListener(new CrossBowListener());
addPaperdollListener(new BowListener());
addPaperdollListener(new ItemPassiveSkillsListener());
addPaperdollListener(new StatsListener());
@@ -1190,6 +1218,29 @@
return getItemByItemId(arrowsId);
}
+ /**
+ * Return the L2ItemInstance of the bolts needed for this crossbow.<BR><BR>
+ * @param crossbow : L2Item designating the crossbow
+ * @return L2ItemInstance pointing out bolts for crossbow
+ */
+ public L2ItemInstance findBoltForCrossBow(L2Item crossbow)
+ {
+ int boltsId = 0;
+
+ switch (crossbow.getCrystalType())
+ {
+ default:
+ case L2Item.CRYSTAL_NONE: boltsId = 9632; break; // Wooden Bolt
+ case L2Item.CRYSTAL_D: boltsId = 9633; break; // Bone Bolt
+ case L2Item.CRYSTAL_C: boltsId = 9634; break; // Steel Bolt
+ case L2Item.CRYSTAL_B: boltsId = 9635; break; // Silver Bolt
+ case L2Item.CRYSTAL_A: boltsId = 9636; break; // Mithril Bolt
+ case L2Item.CRYSTAL_S: boltsId = 9637; break; // Shining Bolt
+ }
+
+ // Get the L2ItemInstance corresponding to the item identifier and return it
+ return getItemByItemId(boltsId);
+ }
/**
* Get back items in inventory from database
*/
Index: java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEnchant.java
===================================================================
--- java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEnchant.java (revision 2917)
+++ java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEnchant.java (working copy)
@@ -157,7 +157,7 @@
itemInstance = parmorInstance;
} else
{
- // for bows and double handed weapons
+ // for bows/crossbows and double handed weapons
parmorInstance = player.getInventory().getPaperdollItem(Inventory.PAPERDOLL_LRHAND);
if (parmorInstance != null && parmorInstance.getLocationSlot() == Inventory.PAPERDOLL_LRHAND)
itemInstance = parmorInstance;