Add attack delay to AttackInfo

Meant to be used in item drop packet for timing handled by the client
rather than scheduling item drop packets on the server.
This commit is contained in:
P0nk
2024-08-14 07:45:06 +02:00
parent 8f2c2dc08f
commit 2ffca90d29
6 changed files with 163 additions and 128 deletions
@@ -117,7 +117,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
public static class AttackInfo { public static class AttackInfo {
public int numAttacked, numDamage, numAttackedAndDamage, skill, skilllevel, stance, direction, rangedirection, charge, display; public int numAttacked, numDamage, numAttackedAndDamage, skill, skilllevel, stance, direction, rangedirection, charge, display;
public Map<Integer, List<Integer>> allDamage; public Map<Integer, AttackTarget> targets;
public boolean ranged, magic; public boolean ranged, magic;
public int speed = 4; public int speed = 4;
public Point position = new Point(); public Point position = new Point();
@@ -146,6 +146,9 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
} }
} }
// TODO: add position
public record AttackTarget(short delay, List<Integer> damageLines) {}
protected void applyAttack(AttackInfo attack, final Character player, int attackCount) { protected void applyAttack(AttackInfo attack, final Character player, int attackCount) {
final MapleMap map = player.getMap(); final MapleMap map = player.getMap();
if (map.isOwnershipRestricted(player)) { if (map.isOwnershipRestricted(player)) {
@@ -216,7 +219,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
if (attack.skill == ChiefBandit.MESO_EXPLOSION) { if (attack.skill == ChiefBandit.MESO_EXPLOSION) {
int delay = 0; int delay = 0;
for (Integer oned : attack.allDamage.keySet()) { for (Integer oned : attack.targets.keySet()) {
MapObject mapobject = map.getMapObject(oned); MapObject mapobject = map.getMapObject(oned);
if (mapobject != null && mapobject.getType() == MapObjectType.ITEM) { if (mapobject != null && mapobject.getType() == MapObjectType.ITEM) {
final MapItem mapitem = (MapItem) mapobject; final MapItem mapitem = (MapItem) mapobject;
@@ -249,7 +252,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
} }
} }
} }
for (Integer oned : attack.allDamage.keySet()) { for (Integer oned : attack.targets.keySet()) {
final Monster monster = map.getMonsterByOid(oned); final Monster monster = map.getMonsterByOid(oned);
if (monster != null) { if (monster != null) {
double distance = player.getPosition().distanceSq(monster.getPosition()); double distance = player.getPosition().distanceSq(monster.getPosition());
@@ -285,7 +288,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
} }
int totDamageToOneMonster = 0; int totDamageToOneMonster = 0;
List<Integer> onedList = attack.allDamage.get(oned); List<Integer> onedList = attack.targets.get(oned).damageLines();
if (attack.magic) { // thanks BHB, Alex (CanIGetaPR) for noticing no immunity status check here if (attack.magic) { // thanks BHB, Alex (CanIGetaPR) for noticing no immunity status check here
if (monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) { if (monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) {
@@ -600,7 +603,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
ret.numAttackedAndDamage = p.readByte(); ret.numAttackedAndDamage = p.readByte();
ret.numAttacked = (ret.numAttackedAndDamage >>> 4) & 0xF; ret.numAttacked = (ret.numAttackedAndDamage >>> 4) & 0xF;
ret.numDamage = ret.numAttackedAndDamage & 0xF; ret.numDamage = ret.numAttackedAndDamage & 0xF;
ret.allDamage = new HashMap<>(); ret.targets = new HashMap<>();
ret.skill = p.readInt(); ret.skill = p.readInt();
ret.ranged = ranged; ret.ranged = ranged;
ret.magic = magic; ret.magic = magic;
@@ -629,7 +632,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
for (int j = 0; j < bullets; j++) { for (int j = 0; j < bullets; j++) {
int mesoid = p.readInt(); int mesoid = p.readInt();
p.skip(1); p.skip(1);
ret.allDamage.put(mesoid, null); ret.targets.put(mesoid, null);
} }
return ret; return ret;
} else { } else {
@@ -638,6 +641,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
for (int i = 0; i < ret.numAttacked + 1; i++) { for (int i = 0; i < ret.numAttacked + 1; i++) {
int oid = p.readInt(); int oid = p.readInt();
if (i < ret.numAttacked) { if (i < ret.numAttacked) {
// TODO: read delay in from these skipped bytes
p.skip(12); p.skip(12);
int bullets = p.readByte(); int bullets = p.readByte();
List<Integer> allDamageNumbers = new ArrayList<>(); List<Integer> allDamageNumbers = new ArrayList<>();
@@ -645,14 +649,14 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
int damage = p.readInt(); int damage = p.readInt();
allDamageNumbers.add(damage); allDamageNumbers.add(damage);
} }
ret.allDamage.put(oid, allDamageNumbers); ret.targets.put(oid, new AttackTarget((short) 0, allDamageNumbers));
p.skip(4); p.skip(4);
} else { } else {
int bullets = p.readByte(); int bullets = p.readByte();
for (int j = 0; j < bullets; j++) { for (int j = 0; j < bullets; j++) {
int mesoid = p.readInt(); int mesoid = p.readInt();
p.skip(1); p.skip(1);
ret.allDamage.put(mesoid, null); ret.targets.put(mesoid, null);
} }
} }
} }
@@ -814,10 +818,12 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
} }
for (int i = 0; i < ret.numAttacked; i++) { for (int i = 0; i < ret.numAttacked; i++) {
int oid = p.readInt(); int oid = p.readInt();
p.skip(14); p.skip(4);
List<Integer> allDamageNumbers = new ArrayList<>(); Point curPos = p.readPos();
Monster monster = chr.getMap().getMonsterByOid(oid); Point nextPos = p.readPos();
short delay = p.readShort();
List<Integer> damageLines = new ArrayList<>();
final Monster monster = chr.getMap().getMonsterByOid(oid);
if (chr.getBuffEffect(BuffStat.WK_CHARGE) != null) { if (chr.getBuffEffect(BuffStat.WK_CHARGE) != null) {
// Charge, so now we need to check elemental effectiveness // Charge, so now we need to check elemental effectiveness
int sourceID = chr.getBuffSource(BuffStat.WK_CHARGE); int sourceID = chr.getBuffSource(BuffStat.WK_CHARGE);
@@ -941,12 +947,12 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
} }
} }
allDamageNumbers.add(damage); damageLines.add(damage);
} }
if (ret.skill != Corsair.RAPID_FIRE || ret.skill != Aran.HIDDEN_FULL_DOUBLE || ret.skill != Aran.HIDDEN_FULL_TRIPLE || ret.skill != Aran.HIDDEN_OVER_DOUBLE || ret.skill != Aran.HIDDEN_OVER_TRIPLE) { if (ret.skill != Corsair.RAPID_FIRE || ret.skill != Aran.HIDDEN_FULL_DOUBLE || ret.skill != Aran.HIDDEN_FULL_TRIPLE || ret.skill != Aran.HIDDEN_OVER_DOUBLE || ret.skill != Aran.HIDDEN_OVER_TRIPLE) {
p.skip(4); p.skip(4);
} }
ret.allDamage.put(oid, allDamageNumbers); ret.targets.put(oid, new AttackTarget(delay, damageLines));
} }
if (ret.skill == NightWalker.POISON_BOMB) { // Poison Bomb if (ret.skill == NightWalker.POISON_BOMB) { // Poison Bomb
p.skip(4); p.skip(4);
@@ -954,8 +960,4 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
} }
return ret; return ret;
} }
private static int rand(int l, int u) {
return (int) ((Math.random() * (u - l + 1)) + l);
}
} }
@@ -78,7 +78,9 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler {
c.sendPacket(PacketCreator.getEnergy("energy", chr.getDojoEnergy())); c.sendPacket(PacketCreator.getEnergy("energy", chr.getDojoEnergy()));
} }
chr.getMap().broadcastMessage(chr, PacketCreator.closeRangeAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, attack.allDamage, attack.speed, attack.direction, attack.display), false, true); chr.getMap().broadcastMessage(chr, PacketCreator.closeRangeAttack(chr, attack.skill, attack.skilllevel,
attack.stance, attack.numAttackedAndDamage, attack.targets, attack.speed, attack.direction,
attack.display), false, true);
int numFinisherOrbs = 0; int numFinisherOrbs = 0;
Integer comboBuff = chr.getBuffedValue(BuffStat.COMBO); Integer comboBuff = chr.getBuffedValue(BuffStat.COMBO);
if (GameConstants.isFinisherSkill(attack.skill)) { if (GameConstants.isFinisherSkill(attack.skill)) {
@@ -139,9 +141,9 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler {
} }
if (attack.numAttacked > 0 && attack.skill == DragonKnight.SACRIFICE) { if (attack.numAttacked > 0 && attack.skill == DragonKnight.SACRIFICE) {
int totDamageToOneMonster = 0; // sacrifice attacks only 1 mob with 1 attack int totDamageToOneMonster = 0; // sacrifice attacks only 1 mob with 1 attack
final Iterator<List<Integer>> dmgIt = attack.allDamage.values().iterator(); final Iterator<AttackTarget> dmgIt = attack.targets.values().iterator();
if (dmgIt.hasNext()) { if (dmgIt.hasNext()) {
totDamageToOneMonster = dmgIt.next().get(0); totDamageToOneMonster = dmgIt.next().damageLines().getFirst();
} }
chr.safeAddHP(-1 * totDamageToOneMonster * attack.getAttackEffect(chr, null).getX() / 100); chr.safeAddHP(-1 * totDamageToOneMonster * attack.getAttackEffect(chr, null).getX() / 100);
@@ -66,7 +66,8 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler {
} }
int charge = (attack.skill == Evan.FIRE_BREATH || attack.skill == Evan.ICE_BREATH || attack.skill == FPArchMage.BIG_BANG || attack.skill == ILArchMage.BIG_BANG || attack.skill == Bishop.BIG_BANG) ? attack.charge : -1; int charge = (attack.skill == Evan.FIRE_BREATH || attack.skill == Evan.ICE_BREATH || attack.skill == FPArchMage.BIG_BANG || attack.skill == ILArchMage.BIG_BANG || attack.skill == Bishop.BIG_BANG) ? attack.charge : -1;
Packet packet = PacketCreator.magicAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, attack.allDamage, charge, attack.speed, attack.direction, attack.display); Packet packet = PacketCreator.magicAttack(chr, attack.skill, attack.skilllevel, attack.stance,
attack.numAttackedAndDamage, attack.targets, charge, attack.speed, attack.direction, attack.display);
chr.getMap().broadcastMessage(chr, packet, false, true); chr.getMap().broadcastMessage(chr, packet, false, true);
StatEffect effect = attack.getAttackEffect(chr, null); StatEffect effect = attack.getAttackEffect(chr, null);
@@ -84,8 +85,8 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler {
Skill eaterSkill = SkillFactory.getSkill((chr.getJob().getId() - (chr.getJob().getId() % 10)) * 10000);// MP Eater, works with right job Skill eaterSkill = SkillFactory.getSkill((chr.getJob().getId() - (chr.getJob().getId() % 10)) * 10000);// MP Eater, works with right job
int eaterLevel = chr.getSkillLevel(eaterSkill); int eaterLevel = chr.getSkillLevel(eaterSkill);
if (eaterLevel > 0) { if (eaterLevel > 0) {
for (Integer singleDamage : attack.allDamage.keySet()) { for (Integer oid : attack.targets.keySet()) {
eaterSkill.getEffect(eaterLevel).applyPassive(chr, chr.getMap().getMapObject(singleDamage), 0); eaterSkill.getEffect(eaterLevel).applyPassive(chr, chr.getMap().getMapObject(oid), 0);
} }
} }
} }
@@ -83,17 +83,23 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
} }
if (attack.skill == Buccaneer.ENERGY_ORB || attack.skill == ThunderBreaker.SPARK || attack.skill == Shadower.TAUNT || attack.skill == NightLord.TAUNT) { if (attack.skill == Buccaneer.ENERGY_ORB || attack.skill == ThunderBreaker.SPARK || attack.skill == Shadower.TAUNT || attack.skill == NightLord.TAUNT) {
chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false); chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel,
attack.stance, attack.numAttackedAndDamage, 0, attack.targets, attack.speed,
attack.direction, attack.display), false);
applyAttack(attack, chr, 1); applyAttack(attack, chr, 1);
} else if (attack.skill == ThunderBreaker.SHARK_WAVE && chr.getSkillLevel(ThunderBreaker.SHARK_WAVE) > 0) { } else if (attack.skill == ThunderBreaker.SHARK_WAVE && chr.getSkillLevel(ThunderBreaker.SHARK_WAVE) > 0) {
chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false); chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel,
attack.stance, attack.numAttackedAndDamage, 0, attack.targets, attack.speed,
attack.direction, attack.display), false);
applyAttack(attack, chr, 1); applyAttack(attack, chr, 1);
for (int i = 0; i < attack.numAttacked; i++) { for (int i = 0; i < attack.numAttacked; i++) {
chr.handleEnergyChargeGain(); chr.handleEnergyChargeGain();
} }
} else if (attack.skill == Aran.COMBO_SMASH || attack.skill == Aran.COMBO_FENRIR || attack.skill == Aran.COMBO_TEMPEST) { } else if (attack.skill == Aran.COMBO_SMASH || attack.skill == Aran.COMBO_FENRIR || attack.skill == Aran.COMBO_TEMPEST) {
chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false); chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel,
attack.stance, attack.numAttackedAndDamage, 0, attack.targets, attack.speed,
attack.direction, attack.display), false);
if (attack.skill == Aran.COMBO_SMASH && chr.getCombo() >= 30) { if (attack.skill == Aran.COMBO_SMASH && chr.getCombo() >= 30) {
chr.setCombo((short) 0); chr.setCombo((short) 0);
applyAttack(attack, chr, 1); applyAttack(attack, chr, 1);
@@ -213,10 +219,14 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
case 3221001: // Pierce case 3221001: // Pierce
case 5221004: // Rapid Fire case 5221004: // Rapid Fire
case 13111002: // KoC Hurricane case 13111002: // KoC Hurricane
packet = PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.rangedirection, attack.numAttackedAndDamage, visProjectile, attack.allDamage, attack.speed, attack.direction, attack.display); packet = PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.rangedirection,
attack.numAttackedAndDamage, visProjectile, attack.targets, attack.speed,
attack.direction, attack.display);
break; break;
default: default:
packet = PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, visProjectile, attack.allDamage, attack.speed, attack.direction, attack.display); packet = PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance,
attack.numAttackedAndDamage, visProjectile, attack.targets, attack.speed,
attack.direction, attack.display);
break; break;
} }
chr.getMap().broadcastMessage(chr, packet, false, true); chr.getMap().broadcastMessage(chr, packet, false, true);
+90 -84
View File
@@ -1337,22 +1337,23 @@ public class MapleMap {
} }
} }
} }
if (monster.isAlive()) { if (!monster.isAlive()) {
boolean killed = monster.damage(chr, damage, false); return false;
selfDestruction selfDestr = monster.getStats().selfDestruction();
if (selfDestr != null && selfDestr.getHp() > -1) {// should work ;p
if (monster.getHp() <= selfDestr.getHp()) {
killMonster(monster, chr, true, selfDestr.getAction());
return true;
}
}
if (killed) {
killMonster(monster, chr, true);
}
return true;
} }
return false;
boolean killed = monster.damage(chr, damage, false);
selfDestruction selfDestr = monster.getStats().selfDestruction();
if (selfDestr != null && selfDestr.getHp() > -1) {// should work ;p
if (monster.getHp() <= selfDestr.getHp()) {
killMonster(monster, chr, true, selfDestr.getAction());
return true;
}
}
if (killed) {
killMonster(monster, chr, true);
}
return true;
} }
public void broadcastBalrogVictory(String leaderName) { public void broadcastBalrogVictory(String leaderName) {
@@ -1406,12 +1407,17 @@ public class MapleMap {
broadcastMessage(PacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition()); broadcastMessage(PacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition());
monster.aggroSwitchController(null, false); monster.aggroSwitchController(null, false);
} }
} else { return;
if (removeKilledMonsterObject(monster)) { }
try {
if (monster.getStats().getLevel() >= chr.getLevel() + 30 && !chr.isGM()) { if (!removeKilledMonsterObject(monster)) {
AutobanFactory.GENERAL.alert(chr, " for killing a " + monster.getName() + " which is over 30 levels higher."); return;
} }
try {
if (monster.getStats().getLevel() >= chr.getLevel() + 30 && !chr.isGM()) {
AutobanFactory.GENERAL.alert(chr, " for killing a " + monster.getName() + " which is over 30 levels higher.");
}
/*if (chr.getQuest(Quest.getInstance(29400)).getStatus().equals(QuestStatus.Status.STARTED)) { /*if (chr.getQuest(Quest.getInstance(29400)).getStatus().equals(QuestStatus.Status.STARTED)) {
if (chr.getLevel() >= 120 && monster.getStats().getLevel() >= 120) { if (chr.getLevel() >= 120 && monster.getStats().getLevel() >= 120) {
@@ -1420,74 +1426,74 @@ public class MapleMap {
} }
}*/ }*/
if (monster.getCP() > 0 && chr.getMap().isCPQMap()) { if (monster.getCP() > 0 && chr.getMap().isCPQMap()) {
chr.gainCP(monster.getCP()); chr.gainCP(monster.getCP());
} }
int buff = monster.getBuffToGive(); int buff = monster.getBuffToGive();
if (buff > -1) { if (buff > -1) {
ItemInformationProvider mii = ItemInformationProvider.getInstance(); ItemInformationProvider mii = ItemInformationProvider.getInstance();
for (MapObject mmo : this.getPlayers()) { for (MapObject mmo : this.getPlayers()) {
Character character = (Character) mmo; Character character = (Character) mmo;
if (character.isAlive()) { if (character.isAlive()) {
StatEffect statEffect = mii.getItemEffect(buff); StatEffect statEffect = mii.getItemEffect(buff);
character.sendPacket(PacketCreator.showOwnBuffEffect(buff, 1)); character.sendPacket(PacketCreator.showOwnBuffEffect(buff, 1));
broadcastMessage(character, PacketCreator.showBuffEffect(character.getId(), buff, 1), false); broadcastMessage(character, PacketCreator.showBuffEffect(character.getId(), buff, 1), false);
statEffect.applyTo(character); statEffect.applyTo(character);
}
}
} }
if (MobId.isZakumArm(monster.getId())) {
boolean makeZakReal = true;
Collection<MapObject> objects = getMapObjects();
for (MapObject object : objects) {
Monster mons = getMonsterByOid(object.getObjectId());
if (mons != null) {
if (MobId.isZakumArm(mons.getId())) {
makeZakReal = false;
break;
}
}
}
if (makeZakReal) {
MapleMap map = chr.getMap();
for (MapObject object : objects) {
Monster mons = map.getMonsterByOid(object.getObjectId());
if (mons != null) {
if (mons.getId() == MobId.ZAKUM_1) {
makeMonsterReal(mons);
break;
}
}
}
}
}
Character dropOwner = monster.killBy(chr);
if (withDrops && !monster.dropsDisabled()) {
if (dropOwner == null) {
dropOwner = chr;
}
dropFromMonster(dropOwner, monster, false);
}
if (monster.hasBossHPBar()) {
for (Character mc : this.getAllPlayers()) {
if (mc.getTargetHpBarHash() == monster.hashCode()) {
mc.resetPlayerAggro();
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally { // thanks resinate for pointing out a memory leak possibly from an exception thrown
monster.dispatchMonsterKilled(true);
broadcastMessage(PacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition());
} }
} }
if (MobId.isZakumArm(monster.getId())) {
boolean makeZakReal = true;
Collection<MapObject> objects = getMapObjects();
for (MapObject object : objects) {
Monster mons = getMonsterByOid(object.getObjectId());
if (mons != null) {
if (MobId.isZakumArm(mons.getId())) {
makeZakReal = false;
break;
}
}
}
if (makeZakReal) {
MapleMap map = chr.getMap();
for (MapObject object : objects) {
Monster mons = map.getMonsterByOid(object.getObjectId());
if (mons != null) {
if (mons.getId() == MobId.ZAKUM_1) {
makeMonsterReal(mons);
break;
}
}
}
}
}
Character dropOwner = monster.killBy(chr);
if (withDrops && !monster.dropsDisabled()) {
if (dropOwner == null) {
dropOwner = chr;
}
dropFromMonster(dropOwner, monster, false);
}
if (monster.hasBossHPBar()) {
for (Character mc : this.getAllPlayers()) {
if (mc.getTargetHpBarHash() == monster.hashCode()) {
mc.resetPlayerAggro();
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally { // thanks resinate for pointing out a memory leak possibly from an exception thrown
monster.dispatchMonsterKilled(true);
broadcastMessage(PacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition());
} }
} }
public void killFriendlies(Monster mob) { public void killFriendlies(Monster mob) {
+29 -15
View File
@@ -56,6 +56,7 @@ import constants.id.MapId;
import constants.id.NpcId; import constants.id.NpcId;
import constants.inventory.ItemConstants; import constants.inventory.ItemConstants;
import constants.skills.Buccaneer; import constants.skills.Buccaneer;
import constants.skills.ChiefBandit;
import constants.skills.Corsair; import constants.skills.Corsair;
import constants.skills.ThunderBreaker; import constants.skills.ThunderBreaker;
import net.encryption.InitializationVector; import net.encryption.InitializationVector;
@@ -67,6 +68,7 @@ import net.packet.Packet;
import net.server.PlayerCoolDownValueHolder; import net.server.PlayerCoolDownValueHolder;
import net.server.Server; import net.server.Server;
import net.server.channel.Channel; import net.server.channel.Channel;
import net.server.channel.handlers.AbstractDealDamageHandler.AttackTarget;
import net.server.channel.handlers.PlayerInteractionHandler; import net.server.channel.handlers.PlayerInteractionHandler;
import net.server.channel.handlers.SummonDamageHandler.SummonAttackEntry; import net.server.channel.handlers.SummonDamageHandler.SummonAttackEntry;
import net.server.channel.handlers.WhisperHandler; import net.server.channel.handlers.WhisperHandler;
@@ -2338,29 +2340,40 @@ public class PacketCreator {
} }
*/ */
public static Packet closeRangeAttack(Character chr, int skill, int skilllevel, int stance, int numAttackedAndDamage, Map<Integer, List<Integer>> damage, int speed, int direction, int display) { public static Packet closeRangeAttack(Character chr, int skill, int skilllevel, int stance,
int numAttackedAndDamage, Map<Integer, AttackTarget> targets, int speed,
int direction, int display) {
final OutPacket p = OutPacket.create(SendOpcode.CLOSE_RANGE_ATTACK); final OutPacket p = OutPacket.create(SendOpcode.CLOSE_RANGE_ATTACK);
addAttackBody(p, chr, skill, skilllevel, stance, numAttackedAndDamage, 0, damage, speed, direction, display); addAttackBody(p, chr, skill, skilllevel, stance, numAttackedAndDamage, 0, targets, speed, direction,
display);
return p; return p;
} }
public static Packet rangedAttack(Character chr, int skill, int skilllevel, int stance, int numAttackedAndDamage, int projectile, Map<Integer, List<Integer>> damage, int speed, int direction, int display) { public static Packet rangedAttack(Character chr, int skill, int skilllevel, int stance, int numAttackedAndDamage,
int projectile, Map<Integer, AttackTarget> targets, int speed, int direction,
int display) {
final OutPacket p = OutPacket.create(SendOpcode.RANGED_ATTACK); final OutPacket p = OutPacket.create(SendOpcode.RANGED_ATTACK);
addAttackBody(p, chr, skill, skilllevel, stance, numAttackedAndDamage, projectile, damage, speed, direction, display); addAttackBody(p, chr, skill, skilllevel, stance, numAttackedAndDamage, projectile, targets, speed, direction,
display);
p.writeInt(0); p.writeInt(0);
return p; return p;
} }
public static Packet magicAttack(Character chr, int skill, int skilllevel, int stance, int numAttackedAndDamage, Map<Integer, List<Integer>> damage, int charge, int speed, int direction, int display) { public static Packet magicAttack(Character chr, int skill, int skilllevel, int stance, int numAttackedAndDamage,
Map<Integer, AttackTarget> targets, int charge, int speed, int direction,
int display) {
final OutPacket p = OutPacket.create(SendOpcode.MAGIC_ATTACK); final OutPacket p = OutPacket.create(SendOpcode.MAGIC_ATTACK);
addAttackBody(p, chr, skill, skilllevel, stance, numAttackedAndDamage, 0, damage, speed, direction, display); addAttackBody(p, chr, skill, skilllevel, stance, numAttackedAndDamage, 0, targets, speed, direction,
display);
if (charge != -1) { if (charge != -1) {
p.writeInt(charge); p.writeInt(charge);
} }
return p; return p;
} }
private static void addAttackBody(OutPacket p, Character chr, int skill, int skilllevel, int stance, int numAttackedAndDamage, int projectile, Map<Integer, List<Integer>> damage, int speed, int direction, int display) { private static void addAttackBody(OutPacket p, Character chr, int skill, int skilllevel, int stance,
int numAttackedAndDamage, int projectile, Map<Integer, AttackTarget> targets,
int speed, int direction, int display) {
p.writeInt(chr.getId()); p.writeInt(chr.getId());
p.writeByte(numAttackedAndDamage); p.writeByte(numAttackedAndDamage);
p.writeByte(0x5B);//? p.writeByte(0x5B);//?
@@ -2374,16 +2387,16 @@ public class PacketCreator {
p.writeByte(speed); p.writeByte(speed);
p.writeByte(0x0A); p.writeByte(0x0A);
p.writeInt(projectile); p.writeInt(projectile);
for (Integer oned : damage.keySet()) { for (Map.Entry<Integer, AttackTarget> target : targets.entrySet()) {
List<Integer> onedList = damage.get(oned); AttackTarget value = target.getValue();
if (onedList != null) { if (value != null) {
p.writeInt(oned); p.writeInt(target.getKey());
p.writeByte(0x0); p.writeByte(0x0);
if (skill == 4211006) { if (skill == ChiefBandit.MESO_EXPLOSION) {
p.writeByte(onedList.size()); p.writeByte(value.damageLines().size());
} }
for (Integer eachd : onedList) { for (Integer damageLine : value.damageLines()) {
p.writeInt(eachd); p.writeInt(damageLine);
} }
} }
} }
@@ -2581,6 +2594,7 @@ public class PacketCreator {
* @param slot * @param slot
* @return * @return
*/ */
// TODO: look for a "delay" in case animation = 4 (explode). Doesn't make sense for it to be server-sided.
public static Packet removeItemFromMap(int objId, int animation, int chrId, boolean pet, int slot) { public static Packet removeItemFromMap(int objId, int animation, int chrId, boolean pet, int slot) {
OutPacket p = OutPacket.create(SendOpcode.REMOVE_ITEM_FROM_MAP); OutPacket p = OutPacket.create(SendOpcode.REMOVE_ITEM_FROM_MAP);
p.writeByte(animation); // expire p.writeByte(animation); // expire