Skip to content

Commit

Permalink
Get Updates from Cosmic (#14)
Browse files Browse the repository at this point in the history
* 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.

* Use delay from packets for drop timing

* Overload damageMonster for when no delay is needed

* Remove unnecessary "spawn loot on animation" feature

No longer needed since item drop timing is now dictated by the client
since two commits back.

* Fix drop delay from summon attack

* Fix drop delay for Meso Explosion

* Proper timing on removing exploded meso

No longer using scheduling on server side but rather
a delay value inherent to the "remove item from map" packet.

* Scheduler free timing for reactor drops

---------

Co-authored-by: P0nk <[email protected]>
Co-authored-by: Ponk <[email protected]>
  • Loading branch information
3 people authored Aug 23, 2024
1 parent ad711d5 commit 8fa61f9
Show file tree
Hide file tree
Showing 16 changed files with 409 additions and 466 deletions.
1 change: 0 additions & 1 deletion config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ server:
USE_ENFORCE_MERCHANT_SAVE: true #Forces automatic DB save on merchant owners, at every item movement on shop.
USE_ENFORCE_MDOOR_POSITION: false #Forces mystic door to be spawned near spawnpoints.
USE_SPAWN_CLEAN_MDOOR: false #Makes mystic doors to be spawned without deploy animation. This clears disconnecting issues that may happen when trying to cancel doors a couple seconds after deployment.
USE_SPAWN_LOOT_ON_ANIMATION: false #Makes loot appear some time after the mob has been killed (following the mob death animation, instead of instantly).
USE_SPAWN_RELEVANT_LOOT: true #Forces to only spawn loots that are collectable by the player or any of their party members.
USE_ERASE_PERMIT_ON_OPENSHOP: true #Forces "shop permit" item to be consumed when player deploy his/her player shop.
USE_ERASE_UNTRADEABLE_DROP: true #Forces flagged untradeable items to disappear when dropped.
Expand Down
1 change: 0 additions & 1 deletion src/main/java/config/ServerConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ public class ServerConfig {
public boolean USE_ENFORCE_MERCHANT_SAVE;
public boolean USE_ENFORCE_MDOOR_POSITION;
public boolean USE_SPAWN_CLEAN_MDOOR;
public boolean USE_SPAWN_LOOT_ON_ANIMATION;
public boolean USE_SPAWN_RELEVANT_LOOT;
public boolean USE_ERASE_PERMIT_ON_OPENSHOP;
public boolean USE_ERASE_UNTRADEABLE_DROP;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@
import server.life.MonsterInformationProvider;
import server.maps.MapItem;
import server.maps.MapObject;
import server.maps.MapObjectType;
import server.maps.MapleMap;
import tools.PacketCreator;
import tools.Randomizer;
Expand All @@ -117,10 +116,12 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
public static class AttackInfo {

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 int speed = 4;
public Point position = new Point();
public List<Integer> explodedMesos;
public Short attackDelay;

public StatEffect getAttackEffect(Character chr, Skill theSkill) {
Skill mySkill = theSkill;
Expand All @@ -146,6 +147,9 @@ public StatEffect getAttackEffect(Character chr, Skill theSkill) {
}
}

// TODO: add position
public record AttackTarget(short delay, List<Integer> damageLines) {}

protected void applyAttack(AttackInfo attack, final Character player, int attackCount) {
final MapleMap map = player.getMap();
if (map.isOwnershipRestricted(player)) {
Expand Down Expand Up @@ -207,50 +211,14 @@ protected void applyAttack(AttackInfo attack, final Character player, int attack
return;
}

//WTF IS THIS F3,1
/*if (attackCount != attack.numDamage && attack.skill != ChiefBandit.MESO_EXPLOSION && attack.skill != NightWalker.VAMPIRE && attack.skill != WindArcher.WIND_SHOT && attack.skill != Aran.COMBO_SMASH && attack.skill != Aran.COMBO_FENRIR && attack.skill != Aran.COMBO_TEMPEST && attack.skill != NightLord.NINJA_AMBUSH && attack.skill != Shadower.NINJA_AMBUSH) {
return;
}*/

int totDamage = 0;

if (attack.skill == ChiefBandit.MESO_EXPLOSION) {
int delay = 0;
for (Integer oned : attack.allDamage.keySet()) {
MapObject mapobject = map.getMapObject(oned);
if (mapobject != null && mapobject.getType() == MapObjectType.ITEM) {
final MapItem mapitem = (MapItem) mapobject;
if (mapitem.getMeso() == 0) { //Maybe it is possible some how?
return;
}

mapitem.lockItem();
try {
if (mapitem.isPickedUp()) {
return;
}
TimerManager.getInstance().schedule(() -> {
mapitem.lockItem();
try {
if (mapitem.isPickedUp()) {
return;
}
map.pickItemDrop(PacketCreator.removeItemFromMap(mapitem.getObjectId(), 4, 0), mapitem);
} finally {
mapitem.unlockItem();
}
}, delay);
delay += 100;
} finally {
mapitem.unlockItem();
}
} else if (mapobject != null && mapobject.getType() != MapObjectType.MONSTER) {
return;
}
}
removeExplodedMesos(map, attack);
}
for (Integer oned : attack.allDamage.keySet()) {
final Monster monster = map.getMonsterByOid(oned);

for (Map.Entry<Integer, AttackTarget> target : attack.targets.entrySet()) {
final Monster monster = map.getMonsterByOid(target.getKey());
if (monster != null) {
double distance = player.getPosition().distanceSq(monster.getPosition());
double distanceToDetect = 200000.0;
Expand Down Expand Up @@ -285,7 +253,7 @@ protected void applyAttack(AttackInfo attack, final Character player, int attack
}

int totDamageToOneMonster = 0;
List<Integer> onedList = attack.allDamage.get(oned);
List<Integer> onedList = target.getValue().damageLines();

if (attack.magic) { // thanks BHB, Alex (CanIGetaPR) for noticing no immunity status check here
if (monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) {
Expand Down Expand Up @@ -321,7 +289,7 @@ protected void applyAttack(AttackInfo attack, final Character player, int attack
Skill pickpocket = SkillFactory.getSkill(ChiefBandit.PICKPOCKET);
int picklv = (player.isGM()) ? pickpocket.getMaxLevel() : player.getSkillLevel(pickpocket);
if (picklv > 0) {
int delay = 0;
short delay = 0;
final int maxmeso = player.getBuffedValue(BuffStat.PICKPOCKET);
for (Integer eachd : onedList) {
eachd += Integer.MAX_VALUE;
Expand All @@ -334,7 +302,9 @@ protected void applyAttack(AttackInfo attack, final Character player, int attack
eachdf = eachd;
}

TimerManager.getInstance().schedule(() -> map.spawnMesoDrop(Math.min((int) Math.max(((double) eachdf / (double) 20000) * (double) maxmeso, 1), maxmeso), new Point((int) (monster.getPosition().getX() + Randomizer.nextInt(100) - 50), (int) (monster.getPosition().getY())), monster, player, true, (byte) 2), delay);
int meso = Math.min((int) Math.max(((double) eachdf / (double) 20000) * (double) maxmeso, 1), maxmeso);
Point position = new Point((int) (monster.getPosition().getX() + Randomizer.nextInt(100) - 50), (int) (monster.getPosition().getY()));
map.spawnMesoDrop(meso, position, monster, player, true, (byte) 2, delay);
delay += 100;
}
}
Expand All @@ -360,7 +330,7 @@ protected void applyAttack(AttackInfo attack, final Character player, int attack
List<MonsterDropEntry> toSteal = new ArrayList<>();
toSteal.add(mi.retrieveDrop(monster.getId()).get(i));

map.dropItemsFromMonster(toSteal, player, monster);
map.dropItemsFromMonster(toSteal, player, monster, target.getValue().delay());
monster.addStolen(toSteal.get(0).itemId);
}
}
Expand Down Expand Up @@ -480,7 +450,7 @@ protected void applyAttack(AttackInfo attack, final Character player, int attack
StatEffect mortal = mortalBlow.getEffect(skillLevel);
if (monster.getHp() <= (monster.getStats().getHp() * mortal.getX()) / 100) {
if (Randomizer.rand(1, 100) <= mortal.getY()) {
map.damageMonster(player, monster, Integer.MAX_VALUE); // thanks Conrad for noticing reduced EXP gain from skill kill
map.damageMonster(player, monster, Integer.MAX_VALUE, target.getValue().delay()); // thanks Conrad for noticing reduced EXP gain from skill kill
}
}
}
Expand Down Expand Up @@ -546,7 +516,7 @@ protected void applyAttack(AttackInfo attack, final Character player, int attack
map.broadcastMessage(PacketCreator.damageMonster(monster.getObjectId(), totDamageToOneMonster));
}

map.damageMonster(player, monster, totDamageToOneMonster);
map.damageMonster(player, monster, totDamageToOneMonster, target.getValue().delay());
}
if (monster.isBuffed(MonsterStatus.WEAPON_REFLECT) && !attack.magic) {
for (MobSkillId msId : monster.getSkills()) {
Expand All @@ -573,7 +543,8 @@ protected void applyAttack(AttackInfo attack, final Character player, int attack
}
}

private static void damageMonsterWithSkill(final Character attacker, final MapleMap map, final Monster monster, final int damage, int skillid, int fixedTime) {
private static void damageMonsterWithSkill(final Character attacker, final MapleMap map, final Monster monster,
final int damage, int skillid, int fixedTime) {
int animationTime;

if (fixedTime == 0) {
Expand All @@ -600,7 +571,7 @@ protected AttackInfo parseDamage(InPacket p, Character chr, boolean ranged, bool
ret.numAttackedAndDamage = p.readByte();
ret.numAttacked = (ret.numAttackedAndDamage >>> 4) & 0xF;
ret.numDamage = ret.numAttackedAndDamage & 0xF;
ret.allDamage = new HashMap<>();
ret.targets = new HashMap<>();
ret.skill = p.readInt();
ret.ranged = ranged;
ret.magic = magic;
Expand All @@ -623,41 +594,9 @@ protected AttackInfo parseDamage(InPacket p, Character chr, boolean ranged, bool
ret.direction = p.readByte();
ret.stance = p.readByte();
if (ret.skill == ChiefBandit.MESO_EXPLOSION) {
if (ret.numAttackedAndDamage == 0) {
p.skip(10);
int bullets = p.readByte();
for (int j = 0; j < bullets; j++) {
int mesoid = p.readInt();
p.skip(1);
ret.allDamage.put(mesoid, null);
}
return ret;
} else {
p.skip(6);
}
for (int i = 0; i < ret.numAttacked + 1; i++) {
int oid = p.readInt();
if (i < ret.numAttacked) {
p.skip(12);
int bullets = p.readByte();
List<Integer> allDamageNumbers = new ArrayList<>();
for (int j = 0; j < bullets; j++) {
int damage = p.readInt();
allDamageNumbers.add(damage);
}
ret.allDamage.put(oid, allDamageNumbers);
p.skip(4);
} else {
int bullets = p.readByte();
for (int j = 0; j < bullets; j++) {
int mesoid = p.readInt();
p.skip(1);
ret.allDamage.put(mesoid, null);
}
}
}
return ret;
return parseMesoExplosion(p, ret);
}

if (ranged) {
p.readByte();
ret.speed = p.readByte();
Expand Down Expand Up @@ -814,10 +753,12 @@ protected AttackInfo parseDamage(InPacket p, Character chr, boolean ranged, bool
}
for (int i = 0; i < ret.numAttacked; i++) {
int oid = p.readInt();
p.skip(14);
List<Integer> allDamageNumbers = new ArrayList<>();
Monster monster = chr.getMap().getMonsterByOid(oid);

p.skip(4);
Point curPos = p.readPos();
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) {
// Charge, so now we need to check elemental effectiveness
int sourceID = chr.getBuffSource(BuffStat.WK_CHARGE);
Expand Down Expand Up @@ -941,12 +882,12 @@ protected AttackInfo parseDamage(InPacket p, Character chr, boolean ranged, bool
}
}

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) {
p.skip(4);
}
ret.allDamage.put(oid, allDamageNumbers);
ret.targets.put(oid, new AttackTarget(delay, damageLines));
}
if (ret.skill == NightWalker.POISON_BOMB) { // Poison Bomb
p.skip(4);
Expand All @@ -955,7 +896,66 @@ protected AttackInfo parseDamage(InPacket p, Character chr, boolean ranged, bool
return ret;
}

private static int rand(int l, int u) {
return (int) ((Math.random() * (u - l + 1)) + l);
private AttackInfo parseMesoExplosion(InPacket p, AttackInfo attackInfo) {
p.skip(6);

Map<Integer, List<Integer>> targetDamage = new HashMap<>();
for (int i = 0; i < attackInfo.numAttacked; i++) {
int mobOid = p.readInt();
p.skip(4);
Point curPos = p.readPos();
Point nextPos = p.readPos();
int damageLines = p.readByte();
List<Integer> allDamageNumbers = new ArrayList<>();
for (int j = 0; j < damageLines; j++) {
int damage = p.readInt();
allDamageNumbers.add(damage);
}
p.skip(4);
targetDamage.put(mobOid, allDamageNumbers);
}

p.skip(4);

List<Integer> explodedMesos = new ArrayList<>();
int explodedMesoCount = p.readByte();
for (int j = 0; j < explodedMesoCount; j++) {
int mesoOid = p.readInt();
p.skip(1);
explodedMesos.add(mesoOid);
}
attackInfo.explodedMesos = explodedMesos;

final short attackDelay = p.readShort();
attackInfo.attackDelay = attackDelay;

Map<Integer, AttackTarget> targets = new HashMap<>();
targetDamage.forEach((id, damage) -> targets.put(id, new AttackTarget(attackDelay, damage)));
attackInfo.targets = targets;
return attackInfo;
}

private void removeExplodedMesos(MapleMap map, AttackInfo attack) {
short delay = attack.attackDelay;
for (Integer mesoId : attack.explodedMesos) {
MapObject mapobject = map.getMapObject(mesoId);
if (!(mapobject instanceof MapItem mapItem)) {
return;
}
if (mapItem.getMeso() == 0) {
return;
}

mapItem.lockItem();
try {
if (mapItem.isPickedUp()) {
return;
}
map.pickItemDrop(PacketCreator.removeExplodedMesoFromMap(mapItem.getObjectId(), delay), mapItem);
} finally {
mapItem.unlockItem();
}
delay += 100;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public void handlePacket(InPacket p, Client c) {
for (int x = 0; x < amount; x++) {
Monster monster = (Monster) monsterx.get(x);
if (monster.getId() == mobToKill) {
c.getPlayer().getMap().killMonster(monster, c.getPlayer(), true);
c.getPlayer().getMap().killMonster(monster, c.getPlayer(), true, (short) 0);
}
}
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ public final void handlePacket(InPacket p, Client c) {
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;
Integer comboBuff = chr.getBuffedValue(BuffStat.COMBO);
if (GameConstants.isFinisherSkill(attack.skill)) {
Expand Down Expand Up @@ -139,9 +141,9 @@ public final void handlePacket(InPacket p, Client c) {
}
if (attack.numAttacked > 0 && attack.skill == DragonKnight.SACRIFICE) {
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()) {
totDamageToOneMonster = dmgIt.next().get(0);
totDamageToOneMonster = dmgIt.next().damageLines().getFirst();
}

chr.safeAddHP(-1 * totDamageToOneMonster * attack.getAttackEffect(chr, null).getX() / 100);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ public final void handlePacket(InPacket p, Client c) {
}

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);
StatEffect effect = attack.getAttackEffect(chr, null);
Expand All @@ -84,8 +85,8 @@ public final void handlePacket(InPacket p, Client c) {
Skill eaterSkill = SkillFactory.getSkill((chr.getJob().getId() - (chr.getJob().getId() % 10)) * 10000);// MP Eater, works with right job
int eaterLevel = chr.getSkillLevel(eaterSkill);
if (eaterLevel > 0) {
for (Integer singleDamage : attack.allDamage.keySet()) {
eaterSkill.getEffect(eaterLevel).applyPassive(chr, chr.getMap().getMapObject(singleDamage), 0);
for (Integer oid : attack.targets.keySet()) {
eaterSkill.getEffect(eaterLevel).applyPassive(chr, chr.getMap().getMapObject(oid), 0);
}
}
}
Expand Down
Loading

0 comments on commit 8fa61f9

Please sign in to comment.