Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f2b8dd013 | ||
|
|
8039852aa3 | ||
|
|
d3b567953d | ||
|
|
5064ee936a | ||
|
|
86da9b0b29 | ||
|
|
acac203e42 | ||
|
|
cdd1c8cb61 | ||
|
|
439753eb6d | ||
|
|
e30700de66 | ||
|
|
994d1723b6 | ||
|
|
2d40a89c55 | ||
|
|
802cc2b5f5 | ||
|
|
2ffca90d29 | ||
|
|
0245e7d1af | ||
|
|
94a08d86a0 | ||
|
|
bcc7bedbc9 | ||
|
|
a878a4f3f9 |
@@ -249,7 +249,6 @@ server:
|
|||||||
USE_ENFORCE_MERCHANT_SAVE: true #Forces automatic DB save on merchant owners, at every item movement on shop.
|
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_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_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_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_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.
|
USE_ERASE_UNTRADEABLE_DROP: true #Forces flagged untradeable items to disappear when dropped.
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ function action(mode, type, selection) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cm.getChar().getGender() == 1) {
|
if (cm.getChar().getGender() == 1) {
|
||||||
for (var i = 0; i < fface.length; i++) {
|
for (var i = 0; i < fface_v.length; i++) {
|
||||||
pushIfItemExists(facenew, fface[i] + cm.getChar().getFace()
|
pushIfItemExists(facenew, fface_v[i] + cm.getChar().getFace()
|
||||||
% 1000 - (cm.getChar().getFace()
|
% 1000 - (cm.getChar().getFace()
|
||||||
% 100));
|
% 100));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -552,16 +552,14 @@ public class AssignAPProcessor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hp = player.getMaxHp();
|
int hplose = -takeHp(player.getJob());
|
||||||
int level_ = player.getLevel();
|
if (player.getMaxHp() + hplose < getMinHp(player.getJob(), player.getLevel())) {
|
||||||
if (hp < level_ * 14 + 148) {
|
|
||||||
player.message("You don't have the minimum HP pool required to swap.");
|
player.message("You don't have the minimum HP pool required to swap.");
|
||||||
c.sendPacket(PacketCreator.enableActions());
|
c.sendPacket(PacketCreator.enableActions());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int curHp = player.getHp();
|
int curHp = player.getHp();
|
||||||
int hplose = -takeHp(player.getJob());
|
|
||||||
player.assignHP(hplose, -1);
|
player.assignHP(hplose, -1);
|
||||||
if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) {
|
if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) {
|
||||||
player.updateHp(Math.max(1, curHp + hplose));
|
player.updateHp(Math.max(1, curHp + hplose));
|
||||||
@@ -583,29 +581,14 @@ public class AssignAPProcessor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mp = player.getMaxMp();
|
int mplose = -takeMp(player.getJob());
|
||||||
int level = player.getLevel();
|
if (player.getMaxMp() + mplose < getMinMp(player.getJob(), player.getLevel())) {
|
||||||
Job job = player.getJob();
|
|
||||||
|
|
||||||
boolean canWash = true;
|
|
||||||
if (job.isA(Job.SPEARMAN) && mp < 4 * level + 156) {
|
|
||||||
canWash = false;
|
|
||||||
} else if ((job.isA(Job.FIGHTER) || job.isA(Job.ARAN1)) && mp < 4 * level + 56) {
|
|
||||||
canWash = false;
|
|
||||||
} else if (job.isA(Job.THIEF) && job.getId() % 100 > 0 && mp < level * 14 - 4) {
|
|
||||||
canWash = false;
|
|
||||||
} else if (mp < level * 14 + 148) {
|
|
||||||
canWash = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!canWash) {
|
|
||||||
player.message("You don't have the minimum MP pool required to swap.");
|
player.message("You don't have the minimum MP pool required to swap.");
|
||||||
c.sendPacket(PacketCreator.enableActions());
|
c.sendPacket(PacketCreator.enableActions());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int curMp = player.getMp();
|
int curMp = player.getMp();
|
||||||
int mplose = -takeMp(job);
|
|
||||||
player.assignMP(mplose, -1);
|
player.assignMP(mplose, -1);
|
||||||
if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) {
|
if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) {
|
||||||
player.updateMp(Math.max(0, curMp + mplose));
|
player.updateMp(Math.max(0, curMp + mplose));
|
||||||
@@ -896,4 +879,109 @@ public class AssignAPProcessor {
|
|||||||
return MaxMP;
|
return MaxMP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getMinHp(Job job, int level) {
|
||||||
|
int multiplier = 0;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
if (job == Job.WARRIOR ||
|
||||||
|
job.isA(Job.PAGE) ||
|
||||||
|
job.isA(Job.SPEARMAN) ||
|
||||||
|
job == Job.DAWNWARRIOR1 ||
|
||||||
|
job == Job.ARAN1) {
|
||||||
|
multiplier = 24; offset = 118;
|
||||||
|
|
||||||
|
} else if (job.isA(Job.FIGHTER) ||
|
||||||
|
job.isA(Job.DAWNWARRIOR2) ||
|
||||||
|
job.isA(Job.ARAN2)) {
|
||||||
|
multiplier = 24; offset = 418;
|
||||||
|
|
||||||
|
} else if (job.isA(Job.MAGICIAN) ||
|
||||||
|
job.isA(Job.BLAZEWIZARD1)) {
|
||||||
|
multiplier = 10; offset = 54;
|
||||||
|
|
||||||
|
} else if (job == Job.BOWMAN ||
|
||||||
|
job == Job.THIEF ||
|
||||||
|
job == Job.WINDARCHER1 ||
|
||||||
|
job == Job.NIGHTWALKER1) {
|
||||||
|
multiplier = 20; offset = 58;
|
||||||
|
|
||||||
|
} else if (job.isA(Job.HUNTER) ||
|
||||||
|
job.isA(Job.CROSSBOWMAN) ||
|
||||||
|
job.isA(Job.ASSASSIN) ||
|
||||||
|
job.isA(Job.BANDIT) ||
|
||||||
|
job.isA(Job.WINDARCHER2) ||
|
||||||
|
job.isA(Job.NIGHTWALKER2)) {
|
||||||
|
multiplier = 20; offset = 358;
|
||||||
|
|
||||||
|
} else if (job == Job.PIRATE ||
|
||||||
|
job == Job.THUNDERBREAKER1) {
|
||||||
|
multiplier = 22; offset = 38;
|
||||||
|
|
||||||
|
} else if (job.isA(Job.BRAWLER) ||
|
||||||
|
job.isA(Job.GUNSLINGER) ||
|
||||||
|
job.isA(Job.THUNDERBREAKER2)) {
|
||||||
|
multiplier = 22; offset = 338;
|
||||||
|
|
||||||
|
} else if (job == Job.BEGINNER ||
|
||||||
|
job == Job.NOBLESSE) {
|
||||||
|
multiplier = 12; offset = 38;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (multiplier * level) + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getMinMp(Job job, int level) {
|
||||||
|
int multiplier = 0;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
if (job == Job.WARRIOR ||
|
||||||
|
job.isA(Job.FIGHTER) ||
|
||||||
|
job.isA(Job.DAWNWARRIOR1) ||
|
||||||
|
job.isA(Job.ARAN1)) {
|
||||||
|
multiplier = 4; offset = 55;
|
||||||
|
|
||||||
|
} else if (job.isA(Job.PAGE) ||
|
||||||
|
job.isA(Job.SPEARMAN)) {
|
||||||
|
multiplier = 4; offset = 155;
|
||||||
|
|
||||||
|
} else if (job == Job.MAGICIAN ||
|
||||||
|
job == Job.BLAZEWIZARD1) {
|
||||||
|
multiplier = 22; offset = -1;
|
||||||
|
|
||||||
|
} else if (job.isA(Job.FP_WIZARD) ||
|
||||||
|
job.isA(Job.IL_WIZARD) ||
|
||||||
|
job.isA(Job.CLERIC) ||
|
||||||
|
job.isA(Job.BLAZEWIZARD2)) {
|
||||||
|
multiplier = 22; offset = 449;
|
||||||
|
|
||||||
|
} else if (job == Job.BOWMAN ||
|
||||||
|
job == Job.THIEF ||
|
||||||
|
job == Job.WINDARCHER1 ||
|
||||||
|
job == Job.NIGHTWALKER1) {
|
||||||
|
multiplier = 14; offset = -15;
|
||||||
|
|
||||||
|
} else if (job.isA(Job.HUNTER) ||
|
||||||
|
job.isA(Job.CROSSBOWMAN) ||
|
||||||
|
job.isA(Job.ASSASSIN) ||
|
||||||
|
job.isA(Job.BANDIT) ||
|
||||||
|
job.isA(Job.WINDARCHER2) ||
|
||||||
|
job.isA(Job.NIGHTWALKER2)) {
|
||||||
|
multiplier = 14; offset = 135;
|
||||||
|
|
||||||
|
} else if (job == Job.PIRATE ||
|
||||||
|
job == Job.THUNDERBREAKER1) {
|
||||||
|
multiplier = 18; offset = -55;
|
||||||
|
|
||||||
|
} else if (job.isA(Job.BRAWLER) ||
|
||||||
|
job.isA(Job.GUNSLINGER) ||
|
||||||
|
job.isA(Job.THUNDERBREAKER2)) {
|
||||||
|
multiplier = 18; offset = 95;
|
||||||
|
|
||||||
|
} else if (job == Job.BEGINNER ||
|
||||||
|
job == Job.NOBLESSE) {
|
||||||
|
multiplier = 10; offset = -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (multiplier * level) + offset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ public class ServerConfig {
|
|||||||
public boolean USE_ENFORCE_MERCHANT_SAVE;
|
public boolean USE_ENFORCE_MERCHANT_SAVE;
|
||||||
public boolean USE_ENFORCE_MDOOR_POSITION;
|
public boolean USE_ENFORCE_MDOOR_POSITION;
|
||||||
public boolean USE_SPAWN_CLEAN_MDOOR;
|
public boolean USE_SPAWN_CLEAN_MDOOR;
|
||||||
public boolean USE_SPAWN_LOOT_ON_ANIMATION;
|
|
||||||
public boolean USE_SPAWN_RELEVANT_LOOT;
|
public boolean USE_SPAWN_RELEVANT_LOOT;
|
||||||
public boolean USE_ERASE_PERMIT_ON_OPENSHOP;
|
public boolean USE_ERASE_PERMIT_ON_OPENSHOP;
|
||||||
public boolean USE_ERASE_UNTRADEABLE_DROP;
|
public boolean USE_ERASE_UNTRADEABLE_DROP;
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ import server.life.MonsterDropEntry;
|
|||||||
import server.life.MonsterInformationProvider;
|
import server.life.MonsterInformationProvider;
|
||||||
import server.maps.MapItem;
|
import server.maps.MapItem;
|
||||||
import server.maps.MapObject;
|
import server.maps.MapObject;
|
||||||
import server.maps.MapObjectType;
|
|
||||||
import server.maps.MapleMap;
|
import server.maps.MapleMap;
|
||||||
import tools.PacketCreator;
|
import tools.PacketCreator;
|
||||||
import tools.Randomizer;
|
import tools.Randomizer;
|
||||||
@@ -113,14 +112,18 @@ import static java.util.concurrent.TimeUnit.MINUTES;
|
|||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
|
||||||
public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
||||||
|
private static final int EXPLODED_MESO_SPREAD_DELAY = 100;
|
||||||
|
private static final int EXPLODED_MESO_MAX_DELAY = 1000;
|
||||||
|
|
||||||
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();
|
||||||
|
public List<Integer> explodedMesos;
|
||||||
|
public Short attackDelay;
|
||||||
|
|
||||||
public StatEffect getAttackEffect(Character chr, Skill theSkill) {
|
public StatEffect getAttackEffect(Character chr, Skill theSkill) {
|
||||||
Skill mySkill = theSkill;
|
Skill mySkill = theSkill;
|
||||||
@@ -146,6 +149,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)) {
|
||||||
@@ -207,50 +213,14 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
|||||||
return;
|
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;
|
int totDamage = 0;
|
||||||
|
|
||||||
if (attack.skill == ChiefBandit.MESO_EXPLOSION) {
|
if (attack.skill == ChiefBandit.MESO_EXPLOSION) {
|
||||||
int delay = 0;
|
removeExplodedMesos(map, attack);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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) {
|
if (monster != null) {
|
||||||
double distance = player.getPosition().distanceSq(monster.getPosition());
|
double distance = player.getPosition().distanceSq(monster.getPosition());
|
||||||
double distanceToDetect = 200000.0;
|
double distanceToDetect = 200000.0;
|
||||||
@@ -285,7 +255,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int totDamageToOneMonster = 0;
|
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 (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)) {
|
||||||
@@ -321,7 +291,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
|||||||
Skill pickpocket = SkillFactory.getSkill(ChiefBandit.PICKPOCKET);
|
Skill pickpocket = SkillFactory.getSkill(ChiefBandit.PICKPOCKET);
|
||||||
int picklv = (player.isGM()) ? pickpocket.getMaxLevel() : player.getSkillLevel(pickpocket);
|
int picklv = (player.isGM()) ? pickpocket.getMaxLevel() : player.getSkillLevel(pickpocket);
|
||||||
if (picklv > 0) {
|
if (picklv > 0) {
|
||||||
int delay = 0;
|
short delay = 0;
|
||||||
final int maxmeso = player.getBuffedValue(BuffStat.PICKPOCKET);
|
final int maxmeso = player.getBuffedValue(BuffStat.PICKPOCKET);
|
||||||
for (Integer eachd : onedList) {
|
for (Integer eachd : onedList) {
|
||||||
eachd += Integer.MAX_VALUE;
|
eachd += Integer.MAX_VALUE;
|
||||||
@@ -334,7 +304,9 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
|||||||
eachdf = eachd;
|
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;
|
delay += 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,7 +332,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
|||||||
List<MonsterDropEntry> toSteal = new ArrayList<>();
|
List<MonsterDropEntry> toSteal = new ArrayList<>();
|
||||||
toSteal.add(mi.retrieveDrop(monster.getId()).get(i));
|
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);
|
monster.addStolen(toSteal.get(0).itemId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -480,7 +452,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
|||||||
StatEffect mortal = mortalBlow.getEffect(skillLevel);
|
StatEffect mortal = mortalBlow.getEffect(skillLevel);
|
||||||
if (monster.getHp() <= (monster.getStats().getHp() * mortal.getX()) / 100) {
|
if (monster.getHp() <= (monster.getStats().getHp() * mortal.getX()) / 100) {
|
||||||
if (Randomizer.rand(1, 100) <= mortal.getY()) {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -546,7 +518,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
|||||||
map.broadcastMessage(PacketCreator.damageMonster(monster.getObjectId(), totDamageToOneMonster));
|
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) {
|
if (monster.isBuffed(MonsterStatus.WEAPON_REFLECT) && !attack.magic) {
|
||||||
for (MobSkillId msId : monster.getSkills()) {
|
for (MobSkillId msId : monster.getSkills()) {
|
||||||
@@ -573,7 +545,8 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
int animationTime;
|
||||||
|
|
||||||
if (fixedTime == 0) {
|
if (fixedTime == 0) {
|
||||||
@@ -600,7 +573,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;
|
||||||
@@ -623,41 +596,9 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
|||||||
ret.direction = p.readByte();
|
ret.direction = p.readByte();
|
||||||
ret.stance = p.readByte();
|
ret.stance = p.readByte();
|
||||||
if (ret.skill == ChiefBandit.MESO_EXPLOSION) {
|
if (ret.skill == ChiefBandit.MESO_EXPLOSION) {
|
||||||
if (ret.numAttackedAndDamage == 0) {
|
return parseMesoExplosion(p, ret);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ranged) {
|
if (ranged) {
|
||||||
p.readByte();
|
p.readByte();
|
||||||
ret.speed = p.readByte();
|
ret.speed = p.readByte();
|
||||||
@@ -814,10 +755,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 +884,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);
|
||||||
@@ -955,7 +898,67 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int rand(int l, int u) {
|
private AttackInfo parseMesoExplosion(InPacket p, AttackInfo attackInfo) {
|
||||||
return (int) ((Math.random() * (u - l + 1)) + l);
|
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) {
|
||||||
|
int index = 0;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
int delay = attack.attackDelay + (index++ % 5) * EXPLODED_MESO_SPREAD_DELAY;
|
||||||
|
delay = Math.min(delay, EXPLODED_MESO_MAX_DELAY);
|
||||||
|
map.pickItemDrop(PacketCreator.removeExplodedMesoFromMap(mapItem.getObjectId(), (short) delay), mapItem);
|
||||||
|
} finally {
|
||||||
|
mapItem.unlockItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ public final class AdminCommandHandler extends AbstractPacketHandler {
|
|||||||
for (int x = 0; x < amount; x++) {
|
for (int x = 0; x < amount; x++) {
|
||||||
Monster monster = (Monster) monsterx.get(x);
|
Monster monster = (Monster) monsterx.get(x);
|
||||||
if (monster.getId() == mobToKill) {
|
if (monster.getId() == mobToKill) {
|
||||||
c.getPlayer().getMap().killMonster(monster, c.getPlayer(), true);
|
c.getPlayer().getMap().killMonster(monster, c.getPlayer(), true, (short) 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,8 @@ public final class MesoDropHandler extends AbstractPacketHandler {
|
|||||||
if (player.attemptCatchFish(meso)) {
|
if (player.attemptCatchFish(meso)) {
|
||||||
player.getMap().disappearingMesoDrop(meso, player, player, player.getPosition());
|
player.getMap().disappearingMesoDrop(meso, player, player, player.getPosition());
|
||||||
} else {
|
} else {
|
||||||
player.getMap().spawnMesoDrop(meso, player.getPosition(), player, player, true, (byte) 2);
|
player.getMap().spawnMesoDrop(meso, player.getPosition(), player, player, true, (byte) 2,
|
||||||
|
(short) 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,21 +57,24 @@ public final class MobDamageMobHandler extends AbstractPacketHandler {
|
|||||||
Monster attacker = map.getMonsterByOid(from);
|
Monster attacker = map.getMonsterByOid(from);
|
||||||
Monster damaged = map.getMonsterByOid(to);
|
Monster damaged = map.getMonsterByOid(to);
|
||||||
|
|
||||||
if (attacker != null && damaged != null) {
|
if (attacker == null || damaged == null) {
|
||||||
int maxDmg = calcMaxDamage(attacker, damaged, magic); // thanks Darter (YungMoozi) for reporting unchecked dmg
|
return;
|
||||||
|
|
||||||
if (dmg > maxDmg) {
|
|
||||||
AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing hypnotize damage exploit."); // thanks Rien dev team
|
|
||||||
String attackerName = MonsterInformationProvider.getInstance().getMobNameFromId(attacker.getId());
|
|
||||||
String damagedName = MonsterInformationProvider.getInstance().getMobNameFromId(damaged.getId());
|
|
||||||
log.warn("Chr {} had hypnotized {} to attack {} with damage {} (max: {})", c.getPlayer().getName(),
|
|
||||||
attackerName, damagedName, dmg, maxDmg);
|
|
||||||
dmg = maxDmg;
|
|
||||||
}
|
|
||||||
|
|
||||||
map.damageMonster(chr, damaged, dmg);
|
|
||||||
map.broadcastMessage(chr, PacketCreator.damageMonster(to, dmg), false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int maxDmg = calcMaxDamage(attacker, damaged, magic); // thanks Darter (YungMoozi) for reporting unchecked dmg
|
||||||
|
|
||||||
|
if (dmg > maxDmg) {
|
||||||
|
AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing hypnotize damage exploit."); // thanks Rien dev team
|
||||||
|
String attackerName = MonsterInformationProvider.getInstance().getMobNameFromId(attacker.getId());
|
||||||
|
String damagedName = MonsterInformationProvider.getInstance().getMobNameFromId(damaged.getId());
|
||||||
|
log.warn("Chr {} had hypnotized {} to attack {} with damage {} (max: {})", c.getPlayer().getName(),
|
||||||
|
attackerName, damagedName, dmg, maxDmg);
|
||||||
|
dmg = maxDmg;
|
||||||
|
}
|
||||||
|
|
||||||
|
map.damageMonster(chr, damaged, dmg);
|
||||||
|
map.broadcastMessage(chr, PacketCreator.damageMonster(to, dmg), false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int calcMaxDamage(Monster attacker, Monster damaged, boolean magic) {
|
private static int calcMaxDamage(Monster attacker, Monster damaged, boolean magic) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -41,31 +41,14 @@ import server.life.MonsterInformationProvider;
|
|||||||
import server.maps.Summon;
|
import server.maps.Summon;
|
||||||
import tools.PacketCreator;
|
import tools.PacketCreator;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class SummonDamageHandler extends AbstractDealDamageHandler {
|
public final class SummonDamageHandler extends AbstractDealDamageHandler {
|
||||||
private static final Logger log = LoggerFactory.getLogger(SummonDamageHandler.class);
|
private static final Logger log = LoggerFactory.getLogger(SummonDamageHandler.class);
|
||||||
|
|
||||||
public final class SummonAttackEntry {
|
public record SummonAttackTarget(int monsterOid, int damage, short delay) {}
|
||||||
|
|
||||||
private final int monsterOid;
|
|
||||||
private final int damage;
|
|
||||||
|
|
||||||
public SummonAttackEntry(int monsterOid, int damage) {
|
|
||||||
this.monsterOid = monsterOid;
|
|
||||||
this.damage = damage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMonsterOid() {
|
|
||||||
return monsterOid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDamage() {
|
|
||||||
return damage;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handlePacket(InPacket p, Client c) {
|
public void handlePacket(InPacket p, Client c) {
|
||||||
@@ -86,17 +69,21 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler {
|
|||||||
Skill summonSkill = SkillFactory.getSkill(summon.getSkill());
|
Skill summonSkill = SkillFactory.getSkill(summon.getSkill());
|
||||||
StatEffect summonEffect = summonSkill.getEffect(summon.getSkillLevel());
|
StatEffect summonEffect = summonSkill.getEffect(summon.getSkillLevel());
|
||||||
p.skip(4);
|
p.skip(4);
|
||||||
List<SummonAttackEntry> allDamage = new ArrayList<>();
|
List<SummonAttackTarget> targets = new ArrayList<>();
|
||||||
byte direction = p.readByte();
|
byte direction = p.readByte();
|
||||||
int numAttacked = p.readByte();
|
int numAttacked = p.readByte();
|
||||||
p.skip(8); // I failed lol (mob x,y and summon x,y), Thanks Gerald
|
p.skip(8); // I failed lol (mob x,y and summon x,y), Thanks Gerald
|
||||||
for (int x = 0; x < numAttacked; x++) {
|
for (int x = 0; x < numAttacked; x++) {
|
||||||
int monsterOid = p.readInt(); // attacked oid
|
int monsterOid = p.readInt(); // attacked oid
|
||||||
p.skip(18);
|
p.skip(8);
|
||||||
|
Point curPos = p.readPos();
|
||||||
|
Point nextPos = p.readPos();
|
||||||
|
short delay = p.readShort();
|
||||||
int damage = p.readInt();
|
int damage = p.readInt();
|
||||||
allDamage.add(new SummonAttackEntry(monsterOid, damage));
|
targets.add(new SummonAttackTarget(monsterOid, damage, delay));
|
||||||
}
|
}
|
||||||
player.getMap().broadcastMessage(player, PacketCreator.summonAttack(player.getId(), summon.getObjectId(), direction, allDamage), summon.getPosition());
|
player.getMap().broadcastMessage(player, PacketCreator.summonAttack(player.getId(), summon.getObjectId(),
|
||||||
|
direction, targets), summon.getPosition());
|
||||||
|
|
||||||
if (player.getMap().isOwnershipRestricted(player)) {
|
if (player.getMap().isOwnershipRestricted(player)) {
|
||||||
return;
|
return;
|
||||||
@@ -104,25 +91,28 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler {
|
|||||||
|
|
||||||
boolean magic = summonEffect.getWatk() == 0;
|
boolean magic = summonEffect.getWatk() == 0;
|
||||||
int maxDmg = calcMaxDamage(summonEffect, player, magic); // thanks Darter (YungMoozi) for reporting unchecked max dmg
|
int maxDmg = calcMaxDamage(summonEffect, player, magic); // thanks Darter (YungMoozi) for reporting unchecked max dmg
|
||||||
for (SummonAttackEntry attackEntry : allDamage) {
|
for (SummonAttackTarget target : targets) {
|
||||||
int damage = attackEntry.getDamage();
|
int damage = target.damage();
|
||||||
Monster target = player.getMap().getMonsterByOid(attackEntry.getMonsterOid());
|
Monster mob = player.getMap().getMonsterByOid(target.monsterOid());
|
||||||
if (target != null) {
|
if (mob == null) {
|
||||||
if (damage > maxDmg) {
|
continue;
|
||||||
AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing summon damage exploit.");
|
|
||||||
final String mobName = MonsterInformationProvider.getInstance().getMobNameFromId(target.getId());
|
|
||||||
log.info("Possible exploit - chr {} used a summon of skillId {} to attack {} with damage {} (max: {})",
|
|
||||||
c.getPlayer().getName(), summon.getSkill(), mobName, damage, maxDmg);
|
|
||||||
damage = maxDmg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (damage > 0 && summonEffect.getMonsterStati().size() > 0) {
|
|
||||||
if (summonEffect.makeChanceResult()) {
|
|
||||||
target.applyStatus(player, new MonsterStatusEffect(summonEffect.getMonsterStati(), summonSkill, null, false), summonEffect.isPoison(), 4000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
player.getMap().damageMonster(player, target, damage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (damage > maxDmg) {
|
||||||
|
AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing summon damage exploit.");
|
||||||
|
final String mobName = MonsterInformationProvider.getInstance().getMobNameFromId(mob.getId());
|
||||||
|
log.info("Possible exploit - chr {} used a summon of skillId {} to attack {} with damage {} (max: {})",
|
||||||
|
c.getPlayer().getName(), summon.getSkill(), mobName, damage, maxDmg);
|
||||||
|
damage = maxDmg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (damage > 0 && summonEffect.getMonsterStati().size() > 0) {
|
||||||
|
if (summonEffect.makeChanceResult()) {
|
||||||
|
mob.applyStatus(player, new MonsterStatusEffect(summonEffect.getMonsterStati(), summonSkill, null, false), summonEffect.isPoison(), 4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
player.getMap().damageMonster(player, mob, damage, target.delay());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (summon.getSkill() == Outlaw.GAVIOTA) { // thanks Periwinks for noticing Gaviota not cancelling after grenade toss
|
if (summon.getSkill() == Outlaw.GAVIOTA) { // thanks Periwinks for noticing Gaviota not cancelling after grenade toss
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
|
|||||||
case ItemId.PHEROMONE_PERFUME:
|
case ItemId.PHEROMONE_PERFUME:
|
||||||
if (mob.getId() == MobId.TAMABLE_HOG) {
|
if (mob.getId() == MobId.TAMABLE_HOG) {
|
||||||
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
||||||
mob.getMap().killMonster(mob, null, false);
|
killMonster(mob);
|
||||||
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
||||||
InventoryManipulator.addById(c, ItemId.HOG, (short) 1, "", -1);
|
InventoryManipulator.addById(c, ItemId.HOG, (short) 1, "", -1);
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
|
|||||||
if ((abm.getLastSpam(10) + 1000) < currentServerTime()) {
|
if ((abm.getLastSpam(10) + 1000) < currentServerTime()) {
|
||||||
if (mob.getHp() < ((mob.getMaxHp() / 10) * 4)) {
|
if (mob.getHp() < ((mob.getMaxHp() / 10) * 4)) {
|
||||||
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
||||||
mob.getMap().killMonster(mob, null, false);
|
killMonster(mob);
|
||||||
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
||||||
InventoryManipulator.addById(c, ItemId.GHOST_SACK, (short) 1, "", -1);
|
InventoryManipulator.addById(c, ItemId.GHOST_SACK, (short) 1, "", -1);
|
||||||
} else {
|
} else {
|
||||||
@@ -90,7 +90,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
|
|||||||
if (chr.canHold(ItemId.ARPQ_SPIRIT_JEWEL, 1)) {
|
if (chr.canHold(ItemId.ARPQ_SPIRIT_JEWEL, 1)) {
|
||||||
if (Math.random() < 0.5) { // 50% chance
|
if (Math.random() < 0.5) { // 50% chance
|
||||||
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
||||||
mob.getMap().killMonster(mob, null, false);
|
killMonster(mob);
|
||||||
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
||||||
InventoryManipulator.addById(c, ItemId.ARPQ_SPIRIT_JEWEL, (short) 1, "", -1);
|
InventoryManipulator.addById(c, ItemId.ARPQ_SPIRIT_JEWEL, (short) 1, "", -1);
|
||||||
chr.updateAriantScore();
|
chr.updateAriantScore();
|
||||||
@@ -113,7 +113,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
|
|||||||
if (mob.getId() == MobId.LOST_RUDOLPH) {
|
if (mob.getId() == MobId.LOST_RUDOLPH) {
|
||||||
if (mob.getHp() < ((mob.getMaxHp() / 10) * 4)) {
|
if (mob.getHp() < ((mob.getMaxHp() / 10) * 4)) {
|
||||||
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
||||||
mob.getMap().killMonster(mob, null, false);
|
killMonster(mob);
|
||||||
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
||||||
InventoryManipulator.addById(c, ItemId.TAMED_RUDOLPH, (short) 1, "", -1);
|
InventoryManipulator.addById(c, ItemId.TAMED_RUDOLPH, (short) 1, "", -1);
|
||||||
} else {
|
} else {
|
||||||
@@ -126,7 +126,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
|
|||||||
if (mob.getId() == MobId.KING_SLIME_DOJO) {
|
if (mob.getId() == MobId.KING_SLIME_DOJO) {
|
||||||
if (mob.getHp() < ((mob.getMaxHp() / 10) * 3)) {
|
if (mob.getHp() < ((mob.getMaxHp() / 10) * 3)) {
|
||||||
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
||||||
mob.getMap().killMonster(mob, null, false);
|
killMonster(mob);
|
||||||
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
||||||
InventoryManipulator.addById(c, ItemId.MONSTER_MARBLE_1, (short) 1, "", -1);
|
InventoryManipulator.addById(c, ItemId.MONSTER_MARBLE_1, (short) 1, "", -1);
|
||||||
} else {
|
} else {
|
||||||
@@ -139,7 +139,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
|
|||||||
if (mob.getId() == MobId.FAUST_DOJO) {
|
if (mob.getId() == MobId.FAUST_DOJO) {
|
||||||
if (mob.getHp() < ((mob.getMaxHp() / 10) * 3)) {
|
if (mob.getHp() < ((mob.getMaxHp() / 10) * 3)) {
|
||||||
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
||||||
mob.getMap().killMonster(mob, null, false);
|
killMonster(mob);
|
||||||
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
||||||
InventoryManipulator.addById(c, ItemId.MONSTER_MARBLE_2, (short) 1, "", -1);
|
InventoryManipulator.addById(c, ItemId.MONSTER_MARBLE_2, (short) 1, "", -1);
|
||||||
} else {
|
} else {
|
||||||
@@ -152,7 +152,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
|
|||||||
if (mob.getId() == MobId.MUSHMOM_DOJO) {
|
if (mob.getId() == MobId.MUSHMOM_DOJO) {
|
||||||
if (mob.getHp() < ((mob.getMaxHp() / 10) * 3)) {
|
if (mob.getHp() < ((mob.getMaxHp() / 10) * 3)) {
|
||||||
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
||||||
mob.getMap().killMonster(mob, null, false);
|
killMonster(mob);
|
||||||
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
||||||
InventoryManipulator.addById(c, ItemId.MONSTER_MARBLE_3, (short) 1, "", -1);
|
InventoryManipulator.addById(c, ItemId.MONSTER_MARBLE_3, (short) 1, "", -1);
|
||||||
} else {
|
} else {
|
||||||
@@ -165,7 +165,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
|
|||||||
if (mob.getId() == MobId.POISON_FLOWER) {
|
if (mob.getId() == MobId.POISON_FLOWER) {
|
||||||
if (mob.getHp() < ((mob.getMaxHp() / 10) * 4)) {
|
if (mob.getHp() < ((mob.getMaxHp() / 10) * 4)) {
|
||||||
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
||||||
mob.getMap().killMonster(mob, null, false);
|
killMonster(mob);
|
||||||
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
||||||
InventoryManipulator.addById(c, ItemId.EPQ_MONSTER_MARBLE, (short) 1, "", -1);
|
InventoryManipulator.addById(c, ItemId.EPQ_MONSTER_MARBLE, (short) 1, "", -1);
|
||||||
} else {
|
} else {
|
||||||
@@ -179,7 +179,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
|
|||||||
if ((abm.getLastSpam(10) + 3000) < currentServerTime()) {
|
if ((abm.getLastSpam(10) + 3000) < currentServerTime()) {
|
||||||
abm.spam(10);
|
abm.spam(10);
|
||||||
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
||||||
mob.getMap().killMonster(mob, null, false);
|
killMonster(mob);
|
||||||
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
||||||
InventoryManipulator.addById(c, ItemId.FISH_NET_WITH_A_CATCH, (short) 1, "", -1);
|
InventoryManipulator.addById(c, ItemId.FISH_NET_WITH_A_CATCH, (short) 1, "", -1);
|
||||||
} else {
|
} else {
|
||||||
@@ -202,7 +202,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
|
|||||||
if (timeCatch != 0 && (abm.getLastSpam(10) + timeCatch) < currentServerTime()) {
|
if (timeCatch != 0 && (abm.getLastSpam(10) + timeCatch) < currentServerTime()) {
|
||||||
if (mobHp != 0 && mob.getHp() < ((mob.getMaxHp() / 100) * mobHp)) {
|
if (mobHp != 0 && mob.getHp() < ((mob.getMaxHp() / 100) * mobHp)) {
|
||||||
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
|
||||||
mob.getMap().killMonster(mob, null, false);
|
killMonster(mob);
|
||||||
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
|
||||||
InventoryManipulator.addById(c, itemGanho, (short) 1, "", -1);
|
InventoryManipulator.addById(c, itemGanho, (short) 1, "", -1);
|
||||||
} else if (mob.getId() != MobId.P_JUNIOR) {
|
} else if (mob.getId() != MobId.P_JUNIOR) {
|
||||||
@@ -220,4 +220,8 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
|
|||||||
// System.out.println("UseCatchItemHandler: \r\n" + slea.toString());
|
// System.out.println("UseCatchItemHandler: \r\n" + slea.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void killMonster(Monster mob) {
|
||||||
|
mob.getMap().killMonster(mob, null, false, (short) 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import server.TimerManager;
|
|||||||
import server.life.LifeFactory;
|
import server.life.LifeFactory;
|
||||||
import server.life.Monster;
|
import server.life.Monster;
|
||||||
import server.maps.MapMonitor;
|
import server.maps.MapMonitor;
|
||||||
|
import server.maps.MapleMap;
|
||||||
import server.maps.Reactor;
|
import server.maps.Reactor;
|
||||||
import server.maps.ReactorDropEntry;
|
import server.maps.ReactorDropEntry;
|
||||||
import server.partyquest.CarnivalFactory;
|
import server.partyquest.CarnivalFactory;
|
||||||
@@ -43,7 +44,6 @@ import java.awt.*;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Lerk
|
* @author Lerk
|
||||||
@@ -52,7 +52,6 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
public class ReactorActionManager extends AbstractPlayerInteraction {
|
public class ReactorActionManager extends AbstractPlayerInteraction {
|
||||||
private final Reactor reactor;
|
private final Reactor reactor;
|
||||||
private final Invocable iv;
|
private final Invocable iv;
|
||||||
private ScheduledFuture<?> sprayTask = null;
|
|
||||||
|
|
||||||
public ReactorActionManager(Client c, Reactor reactor, Invocable iv) {
|
public ReactorActionManager(Client c, Reactor reactor, Invocable iv) {
|
||||||
super(c);
|
super(c);
|
||||||
@@ -172,7 +171,8 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
|
|||||||
int range = maxMeso - minMeso;
|
int range = maxMeso - minMeso;
|
||||||
int displayDrop = (int) (Math.random() * range) + minMeso;
|
int displayDrop = (int) (Math.random() * range) + minMeso;
|
||||||
int mesoDrop = (displayDrop * c.getWorldServer().getMesoRate());
|
int mesoDrop = (displayDrop * c.getWorldServer().getMesoRate());
|
||||||
reactor.getMap().spawnMesoDrop(mesoDrop, reactor.getMap().calcDropPos(dropPos, reactor.getPosition()), reactor, c.getPlayer(), false, (byte) 2);
|
reactor.getMap().spawnMesoDrop(mesoDrop, reactor.getMap().calcDropPos(dropPos,
|
||||||
|
reactor.getPosition()), reactor, c.getPlayer(), false, (byte) 2, (short) 0);
|
||||||
} else {
|
} else {
|
||||||
Item drop;
|
Item drop;
|
||||||
|
|
||||||
@@ -182,31 +182,24 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
|
|||||||
drop = ii.randomizeStats((Equip) ii.getEquipById(d.itemId));
|
drop = ii.randomizeStats((Equip) ii.getEquipById(d.itemId));
|
||||||
}
|
}
|
||||||
|
|
||||||
reactor.getMap().dropFromReactor(getPlayer(), reactor, drop, dropPos, (short) d.questid);
|
reactor.getMap().dropFromReactor(getPlayer(), reactor, drop, dropPos, (short) d.questid, (short) 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final Reactor r = reactor;
|
|
||||||
final List<ReactorDropEntry> dropItems = items;
|
|
||||||
final int worldMesoRate = c.getWorldServer().getMesoRate();
|
final int worldMesoRate = c.getWorldServer().getMesoRate();
|
||||||
|
|
||||||
dropPos.x -= (12 * items.size());
|
dropPos.x -= (12 * items.size());
|
||||||
|
short delay = 0;
|
||||||
sprayTask = TimerManager.getInstance().register(() -> {
|
for (ReactorDropEntry d : items) {
|
||||||
if (dropItems.isEmpty()) {
|
|
||||||
sprayTask.cancel(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactorDropEntry d = dropItems.remove(0);
|
|
||||||
if (d.itemId == 0) {
|
if (d.itemId == 0) {
|
||||||
int range = maxMeso - minMeso;
|
int range = maxMeso - minMeso;
|
||||||
int displayDrop = (int) (Math.random() * range) + minMeso;
|
int displayDrop = (int) (Math.random() * range) + minMeso;
|
||||||
int mesoDrop = (displayDrop * worldMesoRate);
|
int mesoDrop = displayDrop * worldMesoRate;
|
||||||
r.getMap().spawnMesoDrop(mesoDrop, r.getMap().calcDropPos(dropPos, r.getPosition()), r, chr, false, (byte) 2);
|
MapleMap map = reactor.getMap();
|
||||||
|
map.spawnMesoDrop(mesoDrop, map.calcDropPos(dropPos, reactor.getPosition()), reactor, chr,
|
||||||
|
false, (byte) 2, delay);
|
||||||
} else {
|
} else {
|
||||||
Item drop;
|
final Item drop;
|
||||||
|
|
||||||
if (ItemConstants.getInventoryType(d.itemId) != InventoryType.EQUIP) {
|
if (ItemConstants.getInventoryType(d.itemId) != InventoryType.EQUIP) {
|
||||||
drop = new Item(d.itemId, (short) 0, (short) 1);
|
drop = new Item(d.itemId, (short) 0, (short) 1);
|
||||||
} else {
|
} else {
|
||||||
@@ -214,11 +207,12 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
|
|||||||
drop = ii.randomizeStats((Equip) ii.getEquipById(d.itemId));
|
drop = ii.randomizeStats((Equip) ii.getEquipById(d.itemId));
|
||||||
}
|
}
|
||||||
|
|
||||||
r.getMap().dropFromReactor(getPlayer(), r, drop, dropPos, (short) d.questid);
|
reactor.getMap().dropFromReactor(getPlayer(), reactor, drop, dropPos, (short) d.questid, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
dropPos.x += 25;
|
dropPos.x += 25;
|
||||||
}, 200);
|
delay += 200;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -825,12 +825,12 @@ public class Monster extends AbstractLoadedLife {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (htKilled) {
|
if (htKilled) {
|
||||||
reviveMap.killMonster(ht, killer, true);
|
reviveMap.killMonster(ht, killer, true, (short) 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = MobId.DEAD_HORNTAIL_MAX; i >= MobId.DEAD_HORNTAIL_MIN; i--) {
|
for (int i = MobId.DEAD_HORNTAIL_MAX; i >= MobId.DEAD_HORNTAIL_MIN; i--) {
|
||||||
reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true);
|
reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true, (short) 0);
|
||||||
}
|
}
|
||||||
} else if (controller != null) {
|
} else if (controller != null) {
|
||||||
mob.aggroSwitchController(controller, aggro);
|
mob.aggroSwitchController(controller, aggro);
|
||||||
|
|||||||
@@ -208,7 +208,8 @@ public class MapItem extends AbstractMapObject {
|
|||||||
if (chr.needQuestItem(questid, getItemId())) {
|
if (chr.needQuestItem(questid, getItemId())) {
|
||||||
this.lockItem();
|
this.lockItem();
|
||||||
try {
|
try {
|
||||||
client.sendPacket(PacketCreator.dropItemFromMapObject(chr, this, null, getPosition(), (byte) 2));
|
client.sendPacket(PacketCreator.dropItemFromMapObject(chr, this, null, getPosition(),
|
||||||
|
(byte) 2, (short) 0));
|
||||||
} finally {
|
} finally {
|
||||||
this.unlockItem();
|
this.unlockItem();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ import java.util.Calendar;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
@@ -121,7 +120,6 @@ public class MapleMap {
|
|||||||
private final Map<String, Integer> environment = new LinkedHashMap<>();
|
private final Map<String, Integer> environment = new LinkedHashMap<>();
|
||||||
private final Map<MapItem, Long> droppedItems = new LinkedHashMap<>();
|
private final Map<MapItem, Long> droppedItems = new LinkedHashMap<>();
|
||||||
private final LinkedList<WeakReference<MapObject>> registeredDrops = new LinkedList<>();
|
private final LinkedList<WeakReference<MapObject>> registeredDrops = new LinkedList<>();
|
||||||
private final Map<MobLootEntry, Long> mobLootEntries = new HashMap(20);
|
|
||||||
private final List<Runnable> statUpdateRunnables = new ArrayList(50);
|
private final List<Runnable> statUpdateRunnables = new ArrayList(50);
|
||||||
private final List<Rectangle> areas = new ArrayList<>();
|
private final List<Rectangle> areas = new ArrayList<>();
|
||||||
private FootholdTree footholds = null;
|
private FootholdTree footholds = null;
|
||||||
@@ -160,7 +158,6 @@ public class MapleMap {
|
|||||||
private MonsterAggroCoordinator aggroMonitor = null; // aggroMonitor activity in sync with itemMonitor
|
private MonsterAggroCoordinator aggroMonitor = null; // aggroMonitor activity in sync with itemMonitor
|
||||||
private ScheduledFuture<?> itemMonitor = null;
|
private ScheduledFuture<?> itemMonitor = null;
|
||||||
private ScheduledFuture<?> expireItemsTask = null;
|
private ScheduledFuture<?> expireItemsTask = null;
|
||||||
private ScheduledFuture<?> mobSpawnLootTask = null;
|
|
||||||
private ScheduledFuture<?> characterStatUpdateTask = null;
|
private ScheduledFuture<?> characterStatUpdateTask = null;
|
||||||
private short itemMonitorTimeout;
|
private short itemMonitorTimeout;
|
||||||
private Pair<Integer, String> timeMob = null;
|
private Pair<Integer, String> timeMob = null;
|
||||||
@@ -654,9 +651,10 @@ public class MapleMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte dropItemsFromMonsterOnMap(List<MonsterDropEntry> dropEntry, Point pos, byte d, int chRate, byte droptype, int mobpos, Character chr, Monster mob) {
|
private byte dropItemsFromMonsterOnMap(List<MonsterDropEntry> dropEntry, Point pos, byte index, int chRate,
|
||||||
|
byte droptype, int mobpos, Character chr, Monster mob, short delay) {
|
||||||
if (dropEntry.isEmpty()) {
|
if (dropEntry.isEmpty()) {
|
||||||
return d;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.shuffle(dropEntry);
|
Collections.shuffle(dropEntry);
|
||||||
@@ -670,9 +668,9 @@ public class MapleMap {
|
|||||||
|
|
||||||
if (Randomizer.nextInt(999999) < dropChance) {
|
if (Randomizer.nextInt(999999) < dropChance) {
|
||||||
if (droptype == 3) {
|
if (droptype == 3) {
|
||||||
pos.x = mobpos + ((d % 2 == 0) ? (40 * ((d + 1) / 2)) : -(40 * (d / 2)));
|
pos.x = mobpos + ((index % 2 == 0) ? (40 * ((index + 1) / 2)) : -(40 * (index / 2)));
|
||||||
} else {
|
} else {
|
||||||
pos.x = mobpos + ((d % 2 == 0) ? (25 * ((d + 1) / 2)) : -(25 * (d / 2)));
|
pos.x = mobpos + ((index % 2 == 0) ? (25 * ((index + 1) / 2)) : -(25 * (index / 2)));
|
||||||
}
|
}
|
||||||
if (de.itemId == 0) { // meso
|
if (de.itemId == 0) { // meso
|
||||||
int mesos = Randomizer.nextInt(de.Maximum - de.Minimum) + de.Minimum;
|
int mesos = Randomizer.nextInt(de.Maximum - de.Minimum) + de.Minimum;
|
||||||
@@ -686,7 +684,8 @@ public class MapleMap {
|
|||||||
mesos = Integer.MAX_VALUE;
|
mesos = Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
spawnMesoDrop(mesos, calcDropPos(pos, mob.getPosition()), mob, chr, false, droptype);
|
spawnMesoDrop(mesos, calcDropPos(pos, mob.getPosition()), mob, chr, false, droptype,
|
||||||
|
delay);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (ItemConstants.getInventoryType(de.itemId) == InventoryType.EQUIP) {
|
if (ItemConstants.getInventoryType(de.itemId) == InventoryType.EQUIP) {
|
||||||
@@ -694,16 +693,17 @@ public class MapleMap {
|
|||||||
} else {
|
} else {
|
||||||
idrop = new Item(de.itemId, (short) 0, (short) (de.Maximum != 1 ? Randomizer.nextInt(de.Maximum - de.Minimum) + de.Minimum : 1));
|
idrop = new Item(de.itemId, (short) 0, (short) (de.Maximum != 1 ? Randomizer.nextInt(de.Maximum - de.Minimum) + de.Minimum : 1));
|
||||||
}
|
}
|
||||||
spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid);
|
spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid, delay);
|
||||||
}
|
}
|
||||||
d++;
|
index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return d;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte dropGlobalItemsFromMonsterOnMap(List<MonsterGlobalDropEntry> globalEntry, Point pos, byte d, byte droptype, int mobpos, Character chr, Monster mob) {
|
private byte dropGlobalItemsFromMonsterOnMap(List<MonsterGlobalDropEntry> globalEntry, Point pos, byte d,
|
||||||
|
byte droptype, int mobpos, Character chr, Monster mob, short delay) {
|
||||||
Collections.shuffle(globalEntry);
|
Collections.shuffle(globalEntry);
|
||||||
|
|
||||||
Item idrop;
|
Item idrop;
|
||||||
@@ -722,7 +722,7 @@ public class MapleMap {
|
|||||||
} else {
|
} else {
|
||||||
idrop = new Item(de.itemId, (short) 0, (short) (de.Maximum != 1 ? Randomizer.nextInt(de.Maximum - de.Minimum) + de.Minimum : 1));
|
idrop = new Item(de.itemId, (short) 0, (short) (de.Maximum != 1 ? Randomizer.nextInt(de.Maximum - de.Minimum) + de.Minimum : 1));
|
||||||
}
|
}
|
||||||
spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid);
|
spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid, delay);
|
||||||
d++;
|
d++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -731,7 +731,7 @@ public class MapleMap {
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dropFromMonster(final Character chr, final Monster mob, final boolean useBaseRate) {
|
private void dropFromMonster(final Character chr, final Monster mob, final boolean useBaseRate, short delay) {
|
||||||
if (mob.dropsDisabled() || !dropsOn) {
|
if (mob.dropsDisabled() || !dropsOn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -739,7 +739,6 @@ public class MapleMap {
|
|||||||
final byte droptype = (byte) (mob.getStats().isExplosiveReward() ? 3 : mob.getStats().isFfaLoot() ? 2 : chr.getParty() != null ? 1 : 0);
|
final byte droptype = (byte) (mob.getStats().isExplosiveReward() ? 3 : mob.getStats().isFfaLoot() ? 2 : chr.getParty() != null ? 1 : 0);
|
||||||
final int mobpos = mob.getPosition().x;
|
final int mobpos = mob.getPosition().x;
|
||||||
int chRate = !mob.isBoss() ? chr.getDropRate() : chr.getBossDropRate();
|
int chRate = !mob.isBoss() ? chr.getDropRate() : chr.getBossDropRate();
|
||||||
byte d = 1;
|
|
||||||
Point pos = new Point(0, mob.getPosition().y);
|
Point pos = new Point(0, mob.getPosition().y);
|
||||||
|
|
||||||
MonsterStatusEffect stati = mob.getStati(MonsterStatus.SHOWDOWN);
|
MonsterStatusEffect stati = mob.getStati(MonsterStatus.SHOWDOWN);
|
||||||
@@ -765,10 +764,20 @@ public class MapleMap {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerMobItemDrops(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, chr, mob);
|
|
||||||
|
byte index = 1;
|
||||||
|
// Normal Drops
|
||||||
|
index = dropItemsFromMonsterOnMap(dropEntry, pos, index, chRate, droptype, mobpos, chr, mob, delay);
|
||||||
|
|
||||||
|
// Global Drops
|
||||||
|
index = dropGlobalItemsFromMonsterOnMap(globalEntry, pos, index, droptype, mobpos, chr, mob, delay);
|
||||||
|
|
||||||
|
// Quest Drops
|
||||||
|
index = dropItemsFromMonsterOnMap(visibleQuestEntry, pos, index, chRate, droptype, mobpos, chr, mob, delay);
|
||||||
|
dropItemsFromMonsterOnMap(otherQuestEntry, pos, index, chRate, droptype, mobpos, chr, mob, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dropItemsFromMonster(List<MonsterDropEntry> list, final Character chr, final Monster mob) {
|
public void dropItemsFromMonster(List<MonsterDropEntry> list, final Character chr, final Monster mob, short delay) {
|
||||||
if (mob.dropsDisabled() || !dropsOn) {
|
if (mob.dropsDisabled() || !dropsOn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -779,15 +788,17 @@ public class MapleMap {
|
|||||||
byte d = 1;
|
byte d = 1;
|
||||||
Point pos = new Point(0, mob.getPosition().y);
|
Point pos = new Point(0, mob.getPosition().y);
|
||||||
|
|
||||||
dropItemsFromMonsterOnMap(list, pos, d, chRate, droptype, mobpos, chr, mob);
|
dropItemsFromMonsterOnMap(list, pos, d, chRate, droptype, mobpos, chr, mob, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dropFromFriendlyMonster(final Character chr, final Monster mob) {
|
public void dropFromFriendlyMonster(final Character chr, final Monster mob) {
|
||||||
dropFromMonster(chr, mob, true);
|
dropFromMonster(chr, mob, true, (short) 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dropFromReactor(final Character chr, final Reactor reactor, Item drop, Point dropPos, short questid) {
|
public void dropFromReactor(final Character chr, final Reactor reactor, Item drop, Point dropPos, short questid,
|
||||||
spawnDrop(drop, this.calcDropPos(dropPos, reactor.getPosition()), reactor, chr, (byte) (chr.getParty() != null ? 1 : 0), questid);
|
short delay) {
|
||||||
|
spawnDrop(drop, this.calcDropPos(dropPos, reactor.getPosition()), reactor, chr,
|
||||||
|
(byte) (chr.getParty() != null ? 1 : 0), questid, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopItemMonitor() {
|
private void stopItemMonitor() {
|
||||||
@@ -797,11 +808,6 @@ public class MapleMap {
|
|||||||
expireItemsTask.cancel(false);
|
expireItemsTask.cancel(false);
|
||||||
expireItemsTask = null;
|
expireItemsTask = null;
|
||||||
|
|
||||||
if (YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) {
|
|
||||||
mobSpawnLootTask.cancel(false);
|
|
||||||
mobSpawnLootTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
characterStatUpdateTask.cancel(false);
|
characterStatUpdateTask.cancel(false);
|
||||||
characterStatUpdateTask = null;
|
characterStatUpdateTask = null;
|
||||||
}
|
}
|
||||||
@@ -858,17 +864,6 @@ public class MapleMap {
|
|||||||
|
|
||||||
expireItemsTask = TimerManager.getInstance().register(() -> makeDisappearExpiredItemDrops(), YamlConfig.config.server.ITEM_EXPIRE_CHECK, YamlConfig.config.server.ITEM_EXPIRE_CHECK);
|
expireItemsTask = TimerManager.getInstance().register(() -> makeDisappearExpiredItemDrops(), YamlConfig.config.server.ITEM_EXPIRE_CHECK, YamlConfig.config.server.ITEM_EXPIRE_CHECK);
|
||||||
|
|
||||||
if (YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) {
|
|
||||||
lootLock.lock();
|
|
||||||
try {
|
|
||||||
mobLootEntries.clear();
|
|
||||||
} finally {
|
|
||||||
lootLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
mobSpawnLootTask = TimerManager.getInstance().register(() -> spawnMobItemDrops(), 200, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
characterStatUpdateTask = TimerManager.getInstance().register(() -> runCharacterStatUpdate(), 200, 200);
|
characterStatUpdateTask = TimerManager.getInstance().register(() -> runCharacterStatUpdate(), 200, 200);
|
||||||
|
|
||||||
itemMonitorTimeout = 1;
|
itemMonitorTimeout = 1;
|
||||||
@@ -965,63 +960,6 @@ public class MapleMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerMobItemDrops(byte droptype, int mobpos, int chRate, Point pos, List<MonsterDropEntry> dropEntry, List<MonsterDropEntry> visibleQuestEntry, List<MonsterDropEntry> otherQuestEntry, List<MonsterGlobalDropEntry> globalEntry, Character chr, Monster mob) {
|
|
||||||
MobLootEntry mle = new MobLootEntry(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, chr, mob);
|
|
||||||
|
|
||||||
if (YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) {
|
|
||||||
int animationTime = mob.getAnimationTime("die1");
|
|
||||||
|
|
||||||
lootLock.lock();
|
|
||||||
try {
|
|
||||||
long timeNow = Server.getInstance().getCurrentTime();
|
|
||||||
mobLootEntries.put(mle, timeNow + ((long) (0.42 * animationTime)));
|
|
||||||
} finally {
|
|
||||||
lootLock.unlock();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mle.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void spawnMobItemDrops() {
|
|
||||||
Set<Entry<MobLootEntry, Long>> mleList;
|
|
||||||
|
|
||||||
lootLock.lock();
|
|
||||||
try {
|
|
||||||
mleList = new HashSet<>(mobLootEntries.entrySet());
|
|
||||||
} finally {
|
|
||||||
lootLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
long timeNow = Server.getInstance().getCurrentTime();
|
|
||||||
List<MobLootEntry> toRemove = new LinkedList<>();
|
|
||||||
for (Entry<MobLootEntry, Long> mlee : mleList) {
|
|
||||||
if (mlee.getValue() < timeNow) {
|
|
||||||
toRemove.add(mlee.getKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!toRemove.isEmpty()) {
|
|
||||||
List<MobLootEntry> toSpawnLoot = new LinkedList<>();
|
|
||||||
|
|
||||||
lootLock.lock();
|
|
||||||
try {
|
|
||||||
for (MobLootEntry mle : toRemove) {
|
|
||||||
Long mler = mobLootEntries.remove(mle);
|
|
||||||
if (mler != null) {
|
|
||||||
toSpawnLoot.add(mle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lootLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MobLootEntry mle : toSpawnLoot) {
|
|
||||||
mle.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<MapItem> getDroppedItems() {
|
private List<MapItem> getDroppedItems() {
|
||||||
objectRLock.lock();
|
objectRLock.lock();
|
||||||
try {
|
try {
|
||||||
@@ -1123,7 +1061,8 @@ public class MapleMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void spawnDrop(final Item idrop, final Point dropPos, final MapObject dropper, final Character chr, final byte droptype, final short questid) {
|
private void spawnDrop(final Item idrop, final Point dropPos, final MapObject dropper, final Character chr,
|
||||||
|
final byte droptype, final short questid, short delay) {
|
||||||
final MapItem mdrop = new MapItem(idrop, dropPos, dropper, chr, chr.getClient(), droptype, false, questid);
|
final MapItem mdrop = new MapItem(idrop, dropPos, dropper, chr, chr.getClient(), droptype, false, questid);
|
||||||
mdrop.setDropTime(Server.getInstance().getCurrentTime());
|
mdrop.setDropTime(Server.getInstance().getCurrentTime());
|
||||||
spawnAndAddRangedMapObject(mdrop, c -> {
|
spawnAndAddRangedMapObject(mdrop, c -> {
|
||||||
@@ -1132,7 +1071,8 @@ public class MapleMap {
|
|||||||
if (chr1.needQuestItem(questid, idrop.getItemId())) {
|
if (chr1.needQuestItem(questid, idrop.getItemId())) {
|
||||||
mdrop.lockItem();
|
mdrop.lockItem();
|
||||||
try {
|
try {
|
||||||
c.sendPacket(PacketCreator.dropItemFromMapObject(chr1, mdrop, dropper.getPosition(), dropPos, (byte) 1));
|
c.sendPacket(PacketCreator.dropItemFromMapObject(chr1, mdrop, dropper.getPosition(), dropPos,
|
||||||
|
(byte) 1, delay));
|
||||||
} finally {
|
} finally {
|
||||||
mdrop.unlockItem();
|
mdrop.unlockItem();
|
||||||
}
|
}
|
||||||
@@ -1143,7 +1083,8 @@ public class MapleMap {
|
|||||||
activateItemReactors(mdrop, chr.getClient());
|
activateItemReactors(mdrop, chr.getClient());
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void spawnMesoDrop(final int meso, final Point position, final MapObject dropper, final Character owner, final boolean playerDrop, final byte droptype) {
|
public final void spawnMesoDrop(final int meso, final Point position, final MapObject dropper,
|
||||||
|
final Character owner, final boolean playerDrop, final byte droptype, short delay) {
|
||||||
final Point droppos = calcDropPos(position, position);
|
final Point droppos = calcDropPos(position, position);
|
||||||
final MapItem mdrop = new MapItem(meso, droppos, dropper, owner, owner.getClient(), droptype, playerDrop);
|
final MapItem mdrop = new MapItem(meso, droppos, dropper, owner, owner.getClient(), droptype, playerDrop);
|
||||||
mdrop.setDropTime(Server.getInstance().getCurrentTime());
|
mdrop.setDropTime(Server.getInstance().getCurrentTime());
|
||||||
@@ -1151,7 +1092,8 @@ public class MapleMap {
|
|||||||
spawnAndAddRangedMapObject(mdrop, c -> {
|
spawnAndAddRangedMapObject(mdrop, c -> {
|
||||||
mdrop.lockItem();
|
mdrop.lockItem();
|
||||||
try {
|
try {
|
||||||
c.sendPacket(PacketCreator.dropItemFromMapObject(c.getPlayer(), mdrop, dropper.getPosition(), droppos, (byte) 1));
|
c.sendPacket(PacketCreator.dropItemFromMapObject(c.getPlayer(), mdrop, dropper.getPosition(), droppos,
|
||||||
|
(byte) 1, delay));
|
||||||
} finally {
|
} finally {
|
||||||
mdrop.unlockItem();
|
mdrop.unlockItem();
|
||||||
}
|
}
|
||||||
@@ -1166,7 +1108,7 @@ public class MapleMap {
|
|||||||
|
|
||||||
mdrop.lockItem();
|
mdrop.lockItem();
|
||||||
try {
|
try {
|
||||||
broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 3, mdrop.getPosition());
|
broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 3, (short) 0, mdrop.getPosition());
|
||||||
} finally {
|
} finally {
|
||||||
mdrop.unlockItem();
|
mdrop.unlockItem();
|
||||||
}
|
}
|
||||||
@@ -1178,7 +1120,7 @@ public class MapleMap {
|
|||||||
|
|
||||||
mdrop.lockItem();
|
mdrop.lockItem();
|
||||||
try {
|
try {
|
||||||
broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 3, mdrop.getPosition());
|
broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 3, (short) 0, mdrop.getPosition());
|
||||||
} finally {
|
} finally {
|
||||||
mdrop.unlockItem();
|
mdrop.unlockItem();
|
||||||
}
|
}
|
||||||
@@ -1326,7 +1268,11 @@ public class MapleMap {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean damageMonster(final Character chr, final Monster monster, final int damage) {
|
public boolean damageMonster(Character chr, Monster monster, int damage) {
|
||||||
|
return damageMonster(chr, monster, damage, (short) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean damageMonster(final Character chr, final Monster monster, final int damage, short delay) {
|
||||||
if (monster.getId() == MobId.ZAKUM_1) {
|
if (monster.getId() == MobId.ZAKUM_1) {
|
||||||
for (MapObject object : chr.getMap().getMapObjects()) {
|
for (MapObject object : chr.getMap().getMapObjects()) {
|
||||||
Monster mons = chr.getMap().getMonsterByOid(object.getObjectId());
|
Monster mons = chr.getMap().getMonsterByOid(object.getObjectId());
|
||||||
@@ -1337,22 +1283,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, delay);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void broadcastBalrogVictory(String leaderName) {
|
public void broadcastBalrogVictory(String leaderName) {
|
||||||
@@ -1391,11 +1338,12 @@ public class MapleMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void killMonster(final Monster monster, final Character chr, final boolean withDrops) {
|
public void killMonster(final Monster monster, final Character chr, final boolean withDrops, short dropDelay) {
|
||||||
killMonster(monster, chr, withDrops, 1);
|
killMonster(monster, chr, withDrops, 1, dropDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void killMonster(final Monster monster, final Character chr, final boolean withDrops, int animation) {
|
public void killMonster(final Monster monster, final Character chr, final boolean withDrops, int animation,
|
||||||
|
short dropDelay) {
|
||||||
if (monster == null) {
|
if (monster == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1406,12 +1354,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,78 +1373,78 @@ 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, dropDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
this.killMonster(mob, (Character) getPlayers().get(0), false);
|
this.killMonster(mob, (Character) getPlayers().get(0), false, (short) 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void killMonster(int mobId) {
|
public void killMonster(int mobId) {
|
||||||
@@ -1500,7 +1453,7 @@ public class MapleMap {
|
|||||||
|
|
||||||
for (Monster mob : mobList) {
|
for (Monster mob : mobList) {
|
||||||
if (mob.getId() == mobId) {
|
if (mob.getId() == mobId) {
|
||||||
this.killMonster(mob, chr, false);
|
this.killMonster(mob, chr, false, (short) 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1519,7 +1472,7 @@ public class MapleMap {
|
|||||||
chr = defaultChr;
|
chr = defaultChr;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.killMonster(mob, chr, true);
|
this.killMonster(mob, chr, true, (short) 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1549,7 +1502,7 @@ public class MapleMap {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
killMonster(monster, null, false, 1);
|
killMonster(monster, null, false, 1, (short) 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1559,7 +1512,7 @@ public class MapleMap {
|
|||||||
for (MapObject monstermo : getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapObjectType.MONSTER))) {
|
for (MapObject monstermo : getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapObjectType.MONSTER))) {
|
||||||
Monster monster = (Monster) monstermo;
|
Monster monster = (Monster) monstermo;
|
||||||
|
|
||||||
killMonster(monster, null, false, 1);
|
killMonster(monster, null, false, 1, (short) 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1906,7 +1859,7 @@ public class MapleMap {
|
|||||||
Runnable removeAfterAction;
|
Runnable removeAfterAction;
|
||||||
|
|
||||||
if (selfDestruction == null) {
|
if (selfDestruction == null) {
|
||||||
removeAfterAction = () -> killMonster(monster, null, false);
|
removeAfterAction = () -> killMonster(monster, null, false, (short) 0);
|
||||||
|
|
||||||
registerMapSchedule(removeAfterAction, SECONDS.toMillis(monster.getStats().removeAfter()));
|
registerMapSchedule(removeAfterAction, SECONDS.toMillis(monster.getStats().removeAfter()));
|
||||||
} else {
|
} else {
|
||||||
@@ -2154,11 +2107,13 @@ public class MapleMap {
|
|||||||
getWorldServer().registerTimedMapObject(expireKite, YamlConfig.config.server.KITE_EXPIRE_TIME);
|
getWorldServer().registerTimedMapObject(expireKite, YamlConfig.config.server.KITE_EXPIRE_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void spawnItemDrop(final MapObject dropper, final Character owner, final Item item, Point pos, final boolean ffaDrop, final boolean playerDrop) {
|
public final void spawnItemDrop(final MapObject dropper, final Character owner, final Item item, Point pos,
|
||||||
|
final boolean ffaDrop, final boolean playerDrop) {
|
||||||
spawnItemDrop(dropper, owner, item, pos, (byte) (ffaDrop ? 2 : 0), playerDrop);
|
spawnItemDrop(dropper, owner, item, pos, (byte) (ffaDrop ? 2 : 0), playerDrop);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void spawnItemDrop(final MapObject dropper, final Character owner, final Item item, Point pos, final byte dropType, final boolean playerDrop) {
|
public final void spawnItemDrop(final MapObject dropper, final Character owner, final Item item, Point pos,
|
||||||
|
final byte dropType, final boolean playerDrop) {
|
||||||
if (FieldLimit.DROP_LIMIT.check(this.getFieldLimit())) { // thanks Conrad for noticing some maps shouldn't have loots available
|
if (FieldLimit.DROP_LIMIT.check(this.getFieldLimit())) { // thanks Conrad for noticing some maps shouldn't have loots available
|
||||||
this.disappearingItemDrop(dropper, owner, item, pos);
|
this.disappearingItemDrop(dropper, owner, item, pos);
|
||||||
return;
|
return;
|
||||||
@@ -2171,7 +2126,8 @@ public class MapleMap {
|
|||||||
spawnAndAddRangedMapObject(mdrop, c -> {
|
spawnAndAddRangedMapObject(mdrop, c -> {
|
||||||
mdrop.lockItem();
|
mdrop.lockItem();
|
||||||
try {
|
try {
|
||||||
c.sendPacket(PacketCreator.dropItemFromMapObject(c.getPlayer(), mdrop, dropper.getPosition(), droppos, (byte) 1));
|
c.sendPacket(PacketCreator.dropItemFromMapObject(c.getPlayer(), mdrop, dropper.getPosition(), droppos,
|
||||||
|
(byte) 1, (short) 0));
|
||||||
} finally {
|
} finally {
|
||||||
mdrop.unlockItem();
|
mdrop.unlockItem();
|
||||||
}
|
}
|
||||||
@@ -2179,7 +2135,7 @@ public class MapleMap {
|
|||||||
|
|
||||||
mdrop.lockItem();
|
mdrop.lockItem();
|
||||||
try {
|
try {
|
||||||
broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 0);
|
broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 0, (short) 0);
|
||||||
} finally {
|
} finally {
|
||||||
mdrop.unlockItem();
|
mdrop.unlockItem();
|
||||||
}
|
}
|
||||||
@@ -2188,49 +2144,6 @@ public class MapleMap {
|
|||||||
activateItemReactors(mdrop, owner.getClient());
|
activateItemReactors(mdrop, owner.getClient());
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void spawnItemDropList(List<Integer> list, final MapObject dropper, final Character owner, Point pos) {
|
|
||||||
spawnItemDropList(list, 1, 1, dropper, owner, pos, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void spawnItemDropList(List<Integer> list, int minCopies, int maxCopies, final MapObject dropper, final Character owner, Point pos) {
|
|
||||||
spawnItemDropList(list, minCopies, maxCopies, dropper, owner, pos, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// spawns item instances of all defined item ids on a list
|
|
||||||
public final void spawnItemDropList(List<Integer> list, int minCopies, int maxCopies, final MapObject dropper, final Character owner, Point pos, final boolean ffaDrop, final boolean playerDrop) {
|
|
||||||
int copies = (maxCopies - minCopies) + 1;
|
|
||||||
if (copies < 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.shuffle(list);
|
|
||||||
|
|
||||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
|
||||||
Random rnd = new Random();
|
|
||||||
|
|
||||||
final Point dropPos = new Point(pos);
|
|
||||||
dropPos.x -= (12 * list.size());
|
|
||||||
|
|
||||||
for (Integer integer : list) {
|
|
||||||
if (integer == 0) {
|
|
||||||
spawnMesoDrop(owner != null ? 10 * owner.getMesoRate() : 10, calcDropPos(dropPos, pos), dropper, owner, playerDrop, (byte) (ffaDrop ? 2 : 0));
|
|
||||||
} else {
|
|
||||||
final Item drop;
|
|
||||||
int randomedId = integer;
|
|
||||||
|
|
||||||
if (ItemConstants.getInventoryType(randomedId) != InventoryType.EQUIP) {
|
|
||||||
drop = new Item(randomedId, (short) 0, (short) (rnd.nextInt(copies) + minCopies));
|
|
||||||
} else {
|
|
||||||
drop = ii.randomizeStats((Equip) ii.getEquipById(randomedId));
|
|
||||||
}
|
|
||||||
|
|
||||||
spawnItemDrop(dropper, owner, drop, calcDropPos(dropPos, pos), ffaDrop, playerDrop);
|
|
||||||
}
|
|
||||||
|
|
||||||
dropPos.x += 25;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerMapSchedule(Runnable r, long delay) {
|
private void registerMapSchedule(Runnable r, long delay) {
|
||||||
OverallService service = (OverallService) this.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
|
OverallService service = (OverallService) this.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
|
||||||
service.registerOverallAction(mapid, r, delay);
|
service.registerOverallAction(mapid, r, delay);
|
||||||
@@ -2861,20 +2774,23 @@ public class MapleMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, Point rangedFrom) {
|
private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, short delay,
|
||||||
broadcastItemDropMessage(mdrop, dropperPos, dropPos, mod, getRangedDistance(), rangedFrom);
|
Point rangedFrom) {
|
||||||
|
broadcastItemDropMessage(mdrop, dropperPos, dropPos, mod, delay, getRangedDistance(), rangedFrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod) {
|
private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, short delay) {
|
||||||
broadcastItemDropMessage(mdrop, dropperPos, dropPos, mod, Double.POSITIVE_INFINITY, null);
|
broadcastItemDropMessage(mdrop, dropperPos, dropPos, mod, delay, Double.POSITIVE_INFINITY, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, double rangeSq, Point rangedFrom) {
|
private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, short delay,
|
||||||
|
double rangeSq, Point rangedFrom) {
|
||||||
chrRLock.lock();
|
chrRLock.lock();
|
||||||
try {
|
try {
|
||||||
for (Character chr : characters) {
|
for (Character chr : characters) {
|
||||||
Packet packet = PacketCreator.dropItemFromMapObject(chr, mdrop, dropperPos, dropPos, mod);
|
Packet packet = PacketCreator.dropItemFromMapObject(chr, mdrop, dropperPos, dropPos, mod, delay);
|
||||||
|
|
||||||
|
// TODO: remove along with USE_MAXRANGE config
|
||||||
if (rangeSq < Double.POSITIVE_INFINITY) {
|
if (rangeSq < Double.POSITIVE_INFINITY) {
|
||||||
if (rangedFrom.distanceSq(chr.getPosition()) <= rangeSq) {
|
if (rangedFrom.distanceSq(chr.getPosition()) <= rangeSq) {
|
||||||
chr.sendPacket(packet);
|
chr.sendPacket(packet);
|
||||||
@@ -3399,12 +3315,14 @@ public class MapleMap {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: no reason to implement runnable - this is not intended to be submitted to another thread
|
||||||
private class MobLootEntry implements Runnable {
|
private class MobLootEntry implements Runnable {
|
||||||
|
|
||||||
private final byte droptype;
|
private final byte droptype;
|
||||||
private final int mobpos;
|
private final int mobpos;
|
||||||
private final int chRate;
|
private final int chRate;
|
||||||
private final Point pos;
|
private final Point pos;
|
||||||
|
private final short delay;
|
||||||
private final List<MonsterDropEntry> dropEntry;
|
private final List<MonsterDropEntry> dropEntry;
|
||||||
private final List<MonsterDropEntry> visibleQuestEntry;
|
private final List<MonsterDropEntry> visibleQuestEntry;
|
||||||
private final List<MonsterDropEntry> otherQuestEntry;
|
private final List<MonsterDropEntry> otherQuestEntry;
|
||||||
@@ -3412,11 +3330,15 @@ public class MapleMap {
|
|||||||
private final Character chr;
|
private final Character chr;
|
||||||
private final Monster mob;
|
private final Monster mob;
|
||||||
|
|
||||||
protected MobLootEntry(byte droptype, int mobpos, int chRate, Point pos, List<MonsterDropEntry> dropEntry, List<MonsterDropEntry> visibleQuestEntry, List<MonsterDropEntry> otherQuestEntry, List<MonsterGlobalDropEntry> globalEntry, Character chr, Monster mob) {
|
protected MobLootEntry(byte droptype, int mobpos, int chRate, Point pos, short delay,
|
||||||
|
List<MonsterDropEntry> dropEntry, List<MonsterDropEntry> visibleQuestEntry,
|
||||||
|
List<MonsterDropEntry> otherQuestEntry, List<MonsterGlobalDropEntry> globalEntry,
|
||||||
|
Character chr, Monster mob) {
|
||||||
this.droptype = droptype;
|
this.droptype = droptype;
|
||||||
this.mobpos = mobpos;
|
this.mobpos = mobpos;
|
||||||
this.chRate = chRate;
|
this.chRate = chRate;
|
||||||
this.pos = pos;
|
this.pos = pos;
|
||||||
|
this.delay = delay;
|
||||||
this.dropEntry = dropEntry;
|
this.dropEntry = dropEntry;
|
||||||
this.visibleQuestEntry = visibleQuestEntry;
|
this.visibleQuestEntry = visibleQuestEntry;
|
||||||
this.otherQuestEntry = otherQuestEntry;
|
this.otherQuestEntry = otherQuestEntry;
|
||||||
@@ -3430,14 +3352,14 @@ public class MapleMap {
|
|||||||
byte d = 1;
|
byte d = 1;
|
||||||
|
|
||||||
// Normal Drops
|
// Normal Drops
|
||||||
d = dropItemsFromMonsterOnMap(dropEntry, pos, d, chRate, droptype, mobpos, chr, mob);
|
d = dropItemsFromMonsterOnMap(dropEntry, pos, d, chRate, droptype, mobpos, chr, mob, delay);
|
||||||
|
|
||||||
// Global Drops
|
// Global Drops
|
||||||
d = dropGlobalItemsFromMonsterOnMap(globalEntry, pos, d, droptype, mobpos, chr, mob);
|
d = dropGlobalItemsFromMonsterOnMap(globalEntry, pos, d, droptype, mobpos, chr, mob, delay);
|
||||||
|
|
||||||
// Quest Drops
|
// Quest Drops
|
||||||
d = dropItemsFromMonsterOnMap(visibleQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob);
|
d = dropItemsFromMonsterOnMap(visibleQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob, delay);
|
||||||
dropItemsFromMonsterOnMap(otherQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob);
|
dropItemsFromMonsterOnMap(otherQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob, delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4457,11 +4379,6 @@ public class MapleMap {
|
|||||||
expireItemsTask = null;
|
expireItemsTask = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mobSpawnLootTask != null) {
|
|
||||||
mobSpawnLootTask.cancel(false);
|
|
||||||
mobSpawnLootTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (characterStatUpdateTask != null) {
|
if (characterStatUpdateTask != null) {
|
||||||
characterStatUpdateTask.cancel(false);
|
characterStatUpdateTask.cancel(false);
|
||||||
characterStatUpdateTask = null;
|
characterStatUpdateTask = null;
|
||||||
|
|||||||
@@ -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,8 +68,9 @@ 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.SummonAttackTarget;
|
||||||
import net.server.channel.handlers.WhisperHandler;
|
import net.server.channel.handlers.WhisperHandler;
|
||||||
import net.server.guild.Alliance;
|
import net.server.guild.Alliance;
|
||||||
import net.server.guild.Guild;
|
import net.server.guild.Guild;
|
||||||
@@ -1814,7 +1816,8 @@ public class PacketCreator {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Packet dropItemFromMapObject(Character player, MapItem drop, Point dropfrom, Point dropto, byte mod) {
|
public static Packet dropItemFromMapObject(Character player, MapItem drop, Point dropfrom, Point dropto, byte mod,
|
||||||
|
short delay) {
|
||||||
int dropType = drop.getDropType();
|
int dropType = drop.getDropType();
|
||||||
if (drop.hasClientsideOwnership(player) && dropType < 3) {
|
if (drop.hasClientsideOwnership(player) && dropType < 3) {
|
||||||
dropType = 2;
|
dropType = 2;
|
||||||
@@ -1832,7 +1835,7 @@ public class PacketCreator {
|
|||||||
|
|
||||||
if (mod != 2) {
|
if (mod != 2) {
|
||||||
p.writePos(dropfrom);
|
p.writePos(dropfrom);
|
||||||
p.writeShort(0);//Fh?
|
p.writeShort(delay);
|
||||||
}
|
}
|
||||||
if (drop.getMeso() == 0) {
|
if (drop.getMeso() == 0) {
|
||||||
addExpirationTime(p, drop.getItem().getExpiration());
|
addExpirationTime(p, drop.getItem().getExpiration());
|
||||||
@@ -2302,18 +2305,18 @@ public class PacketCreator {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Packet summonAttack(int cid, int summonOid, byte direction, List<SummonAttackEntry> allDamage) {
|
public static Packet summonAttack(int cid, int summonOid, byte direction, List<SummonAttackTarget> targets) {
|
||||||
OutPacket p = OutPacket.create(SendOpcode.SUMMON_ATTACK);
|
OutPacket p = OutPacket.create(SendOpcode.SUMMON_ATTACK);
|
||||||
//b2 00 29 f7 00 00 9a a3 04 00 c8 04 01 94 a3 04 00 06 ff 2b 00
|
//b2 00 29 f7 00 00 9a a3 04 00 c8 04 01 94 a3 04 00 06 ff 2b 00
|
||||||
p.writeInt(cid);
|
p.writeInt(cid);
|
||||||
p.writeInt(summonOid);
|
p.writeInt(summonOid);
|
||||||
p.writeByte(0); // char level
|
p.writeByte(0); // char level
|
||||||
p.writeByte(direction);
|
p.writeByte(direction);
|
||||||
p.writeByte(allDamage.size());
|
p.writeByte(targets.size());
|
||||||
for (SummonAttackEntry attackEntry : allDamage) {
|
for (SummonAttackTarget target : targets) {
|
||||||
p.writeInt(attackEntry.getMonsterOid()); // oid
|
p.writeInt(target.monsterOid()); // oid
|
||||||
p.writeByte(6); // who knows
|
p.writeByte(6); // who knows
|
||||||
p.writeInt(attackEntry.getDamage()); // damage
|
p.writeInt(target.damage()); // damage
|
||||||
}
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
@@ -2338,29 +2341,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 +2388,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2594,6 +2608,14 @@ public class PacketCreator {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Packet removeExplodedMesoFromMap(int mapObjectId, short delay) {
|
||||||
|
OutPacket p = OutPacket.create(SendOpcode.REMOVE_ITEM_FROM_MAP);
|
||||||
|
p.writeByte(4);
|
||||||
|
p.writeInt(mapObjectId);
|
||||||
|
p.writeShort(delay);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
public static Packet updateCharLook(Client target, Character chr) {
|
public static Packet updateCharLook(Client target, Character chr) {
|
||||||
OutPacket p = OutPacket.create(SendOpcode.UPDATE_CHAR_LOOK);
|
OutPacket p = OutPacket.create(SendOpcode.UPDATE_CHAR_LOOK);
|
||||||
p.writeInt(chr.getId());
|
p.writeInt(chr.getId());
|
||||||
|
|||||||
237
src/test/java/client/processor/stat/AssignAPProcessorTest.java
Normal file
237
src/test/java/client/processor/stat/AssignAPProcessorTest.java
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
package client.processor.stat;
|
||||||
|
|
||||||
|
import client.Job;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class AssignAPProcessorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getMinHp() {
|
||||||
|
int max_level = 200;
|
||||||
|
int cygnus_max_level = 120;
|
||||||
|
|
||||||
|
BiFunction<Job,Integer,Integer> f = AssignAPProcessor::getMinHp;
|
||||||
|
|
||||||
|
assertAll(
|
||||||
|
// Beginners
|
||||||
|
() -> assertEquals(2438, f.apply(Job.BEGINNER, max_level)),
|
||||||
|
() -> assertEquals(1478, f.apply(Job.NOBLESSE, cygnus_max_level)),
|
||||||
|
|
||||||
|
// Warrior (Explorer)
|
||||||
|
() -> assertEquals(4918, f.apply(Job.WARRIOR, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(5218, f.apply(Job.FIGHTER, max_level)),
|
||||||
|
() -> assertEquals(5218, f.apply(Job.CRUSADER, max_level)),
|
||||||
|
() -> assertEquals(5218, f.apply(Job.HERO, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(4918, f.apply(Job.PAGE, max_level)),
|
||||||
|
() -> assertEquals(4918, f.apply(Job.WHITEKNIGHT, max_level)),
|
||||||
|
() -> assertEquals(4918, f.apply(Job.PALADIN, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(4918, f.apply(Job.SPEARMAN, max_level)),
|
||||||
|
() -> assertEquals(4918, f.apply(Job.DRAGONKNIGHT, max_level)),
|
||||||
|
() -> assertEquals(4918, f.apply(Job.DARKKNIGHT, max_level)),
|
||||||
|
|
||||||
|
// Warrior (Cygnus)
|
||||||
|
() -> assertEquals(2998, f.apply(Job.DAWNWARRIOR1, cygnus_max_level)),
|
||||||
|
() -> assertEquals(3298, f.apply(Job.DAWNWARRIOR2, cygnus_max_level)),
|
||||||
|
() -> assertEquals(3298, f.apply(Job.DAWNWARRIOR3, cygnus_max_level)),
|
||||||
|
() -> assertEquals(3298, f.apply(Job.DAWNWARRIOR4, cygnus_max_level)),
|
||||||
|
|
||||||
|
// Warrior (Aran)
|
||||||
|
() -> assertEquals(4918, f.apply(Job.ARAN1, max_level)),
|
||||||
|
() -> assertEquals(5218, f.apply(Job.ARAN2, max_level)),
|
||||||
|
() -> assertEquals(5218, f.apply(Job.ARAN3, max_level)),
|
||||||
|
() -> assertEquals(5218, f.apply(Job.ARAN4, max_level)),
|
||||||
|
|
||||||
|
// Magician (Explorer)
|
||||||
|
() -> assertEquals(2054, f.apply(Job.MAGICIAN, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(2054, f.apply(Job.FP_WIZARD, max_level)),
|
||||||
|
() -> assertEquals(2054, f.apply(Job.FP_MAGE, max_level)),
|
||||||
|
() -> assertEquals(2054, f.apply(Job.FP_ARCHMAGE, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(2054, f.apply(Job.IL_WIZARD, max_level)),
|
||||||
|
() -> assertEquals(2054, f.apply(Job.IL_MAGE, max_level)),
|
||||||
|
() -> assertEquals(2054, f.apply(Job.IL_ARCHMAGE, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(2054, f.apply(Job.CLERIC, max_level)),
|
||||||
|
() -> assertEquals(2054, f.apply(Job.PRIEST, max_level)),
|
||||||
|
() -> assertEquals(2054, f.apply(Job.BISHOP, max_level)),
|
||||||
|
|
||||||
|
// Magician (Cygnus)
|
||||||
|
() -> assertEquals(1254, f.apply(Job.BLAZEWIZARD1, cygnus_max_level)),
|
||||||
|
() -> assertEquals(1254, f.apply(Job.BLAZEWIZARD2, cygnus_max_level)),
|
||||||
|
() -> assertEquals(1254, f.apply(Job.BLAZEWIZARD3, cygnus_max_level)),
|
||||||
|
() -> assertEquals(1254, f.apply(Job.BLAZEWIZARD4, cygnus_max_level)),
|
||||||
|
|
||||||
|
// Bowman (Explorer)
|
||||||
|
() -> assertEquals(4058, f.apply(Job.BOWMAN, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(4358, f.apply(Job.HUNTER, max_level)),
|
||||||
|
() -> assertEquals(4358, f.apply(Job.RANGER, max_level)),
|
||||||
|
() -> assertEquals(4358, f.apply(Job.BOWMASTER, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(4358, f.apply(Job.CROSSBOWMAN, max_level)),
|
||||||
|
() -> assertEquals(4358, f.apply(Job.SNIPER, max_level)),
|
||||||
|
() -> assertEquals(4358, f.apply(Job.MARKSMAN, max_level)),
|
||||||
|
|
||||||
|
// Bowman (Cygnus)
|
||||||
|
() -> assertEquals(2458, f.apply(Job.WINDARCHER1, cygnus_max_level)),
|
||||||
|
() -> assertEquals(2758, f.apply(Job.WINDARCHER2, cygnus_max_level)),
|
||||||
|
() -> assertEquals(2758, f.apply(Job.WINDARCHER3, cygnus_max_level)),
|
||||||
|
() -> assertEquals(2758, f.apply(Job.WINDARCHER4, cygnus_max_level)),
|
||||||
|
|
||||||
|
// Thief (Explorer)
|
||||||
|
() -> assertEquals(4058, f.apply(Job.THIEF, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(4358, f.apply(Job.ASSASSIN, max_level)),
|
||||||
|
() -> assertEquals(4358, f.apply(Job.HERMIT, max_level)),
|
||||||
|
() -> assertEquals(4358, f.apply(Job.NIGHTLORD, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(4358, f.apply(Job.BANDIT, max_level)),
|
||||||
|
() -> assertEquals(4358, f.apply(Job.CHIEFBANDIT, max_level)),
|
||||||
|
() -> assertEquals(4358, f.apply(Job.SHADOWER, max_level)),
|
||||||
|
|
||||||
|
// Thief (Cygnus)
|
||||||
|
() -> assertEquals(2458, f.apply(Job.NIGHTWALKER1, cygnus_max_level)),
|
||||||
|
() -> assertEquals(2758, f.apply(Job.NIGHTWALKER2, cygnus_max_level)),
|
||||||
|
() -> assertEquals(2758, f.apply(Job.NIGHTWALKER3, cygnus_max_level)),
|
||||||
|
() -> assertEquals(2758, f.apply(Job.NIGHTWALKER4, cygnus_max_level)),
|
||||||
|
|
||||||
|
// Pirate (Explorer)
|
||||||
|
() -> assertEquals(4438, f.apply(Job.PIRATE, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(4738, f.apply(Job.BRAWLER, max_level)),
|
||||||
|
() -> assertEquals(4738, f.apply(Job.MARAUDER, max_level)),
|
||||||
|
() -> assertEquals(4738, f.apply(Job.BUCCANEER, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(4738, f.apply(Job.GUNSLINGER, max_level)),
|
||||||
|
() -> assertEquals(4738, f.apply(Job.OUTLAW, max_level)),
|
||||||
|
() -> assertEquals(4738, f.apply(Job.CORSAIR, max_level)),
|
||||||
|
|
||||||
|
// Pirate (Cygnus)
|
||||||
|
() -> assertEquals(2678, f.apply(Job.THUNDERBREAKER1, cygnus_max_level)),
|
||||||
|
() -> assertEquals(2978, f.apply(Job.THUNDERBREAKER2, cygnus_max_level)),
|
||||||
|
() -> assertEquals(2978, f.apply(Job.THUNDERBREAKER3, cygnus_max_level)),
|
||||||
|
() -> assertEquals(2978, f.apply(Job.THUNDERBREAKER4, cygnus_max_level))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getMinMp() {
|
||||||
|
int max_level = 200;
|
||||||
|
int cygnus_max_level = 120;
|
||||||
|
|
||||||
|
BiFunction<Job,Integer,Integer> f = AssignAPProcessor::getMinMp;
|
||||||
|
|
||||||
|
assertAll(
|
||||||
|
// Beginners
|
||||||
|
() -> assertEquals(1995, f.apply(Job.BEGINNER, max_level)),
|
||||||
|
() -> assertEquals(1195, f.apply(Job.NOBLESSE, cygnus_max_level)),
|
||||||
|
|
||||||
|
// Warrior (Explorer)
|
||||||
|
() -> assertEquals(855, f.apply(Job.WARRIOR, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(855, f.apply(Job.FIGHTER, max_level)),
|
||||||
|
() -> assertEquals(855, f.apply(Job.CRUSADER, max_level)),
|
||||||
|
() -> assertEquals(855, f.apply(Job.HERO, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(955, f.apply(Job.PAGE, max_level)),
|
||||||
|
() -> assertEquals(955, f.apply(Job.WHITEKNIGHT, max_level)),
|
||||||
|
() -> assertEquals(955, f.apply(Job.PALADIN, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(955, f.apply(Job.SPEARMAN, max_level)),
|
||||||
|
() -> assertEquals(955, f.apply(Job.DRAGONKNIGHT, max_level)),
|
||||||
|
() -> assertEquals(955, f.apply(Job.DARKKNIGHT, max_level)),
|
||||||
|
|
||||||
|
// Warrior (Cygnus)
|
||||||
|
() -> assertEquals(535, f.apply(Job.DAWNWARRIOR1, cygnus_max_level)),
|
||||||
|
() -> assertEquals(535, f.apply(Job.DAWNWARRIOR2, cygnus_max_level)),
|
||||||
|
() -> assertEquals(535, f.apply(Job.DAWNWARRIOR3, cygnus_max_level)),
|
||||||
|
() -> assertEquals(535, f.apply(Job.DAWNWARRIOR4, cygnus_max_level)),
|
||||||
|
|
||||||
|
// Warrior (Aran)
|
||||||
|
() -> assertEquals(855, f.apply(Job.ARAN1, max_level)),
|
||||||
|
() -> assertEquals(855, f.apply(Job.ARAN2, max_level)),
|
||||||
|
() -> assertEquals(855, f.apply(Job.ARAN3, max_level)),
|
||||||
|
() -> assertEquals(855, f.apply(Job.ARAN4, max_level)),
|
||||||
|
|
||||||
|
// Magician (Explorer)
|
||||||
|
() -> assertEquals(4399, f.apply(Job.MAGICIAN, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(4849, f.apply(Job.FP_WIZARD, max_level)),
|
||||||
|
() -> assertEquals(4849, f.apply(Job.FP_MAGE, max_level)),
|
||||||
|
() -> assertEquals(4849, f.apply(Job.FP_ARCHMAGE, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(4849, f.apply(Job.IL_WIZARD, max_level)),
|
||||||
|
() -> assertEquals(4849, f.apply(Job.IL_MAGE, max_level)),
|
||||||
|
() -> assertEquals(4849, f.apply(Job.IL_ARCHMAGE, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(4849, f.apply(Job.CLERIC, max_level)),
|
||||||
|
() -> assertEquals(4849, f.apply(Job.PRIEST, max_level)),
|
||||||
|
() -> assertEquals(4849, f.apply(Job.BISHOP, max_level)),
|
||||||
|
|
||||||
|
// Magician (Cygnus)
|
||||||
|
() -> assertEquals(2639, f.apply(Job.BLAZEWIZARD1, cygnus_max_level)),
|
||||||
|
() -> assertEquals(3089, f.apply(Job.BLAZEWIZARD2, cygnus_max_level)),
|
||||||
|
() -> assertEquals(3089, f.apply(Job.BLAZEWIZARD3, cygnus_max_level)),
|
||||||
|
() -> assertEquals(3089, f.apply(Job.BLAZEWIZARD4, cygnus_max_level)),
|
||||||
|
|
||||||
|
// Bowman (Explorer)
|
||||||
|
() -> assertEquals(2785, f.apply(Job.BOWMAN, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(2935, f.apply(Job.HUNTER, max_level)),
|
||||||
|
() -> assertEquals(2935, f.apply(Job.RANGER, max_level)),
|
||||||
|
() -> assertEquals(2935, f.apply(Job.BOWMASTER, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(2935, f.apply(Job.CROSSBOWMAN, max_level)),
|
||||||
|
() -> assertEquals(2935, f.apply(Job.SNIPER, max_level)),
|
||||||
|
() -> assertEquals(2935, f.apply(Job.MARKSMAN, max_level)),
|
||||||
|
|
||||||
|
// Bowman (Cygnus)
|
||||||
|
() -> assertEquals(1665, f.apply(Job.WINDARCHER1, cygnus_max_level)),
|
||||||
|
() -> assertEquals(1815, f.apply(Job.WINDARCHER2, cygnus_max_level)),
|
||||||
|
() -> assertEquals(1815, f.apply(Job.WINDARCHER3, cygnus_max_level)),
|
||||||
|
() -> assertEquals(1815, f.apply(Job.WINDARCHER4, cygnus_max_level)),
|
||||||
|
|
||||||
|
// Thief (Explorer)
|
||||||
|
() -> assertEquals(2785, f.apply(Job.THIEF, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(2935, f.apply(Job.ASSASSIN, max_level)),
|
||||||
|
() -> assertEquals(2935, f.apply(Job.HERMIT, max_level)),
|
||||||
|
() -> assertEquals(2935, f.apply(Job.NIGHTLORD, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(2935, f.apply(Job.BANDIT, max_level)),
|
||||||
|
() -> assertEquals(2935, f.apply(Job.CHIEFBANDIT, max_level)),
|
||||||
|
() -> assertEquals(2935, f.apply(Job.SHADOWER, max_level)),
|
||||||
|
|
||||||
|
// Thief (Cygnus)
|
||||||
|
() -> assertEquals(1665, f.apply(Job.NIGHTWALKER1, cygnus_max_level)),
|
||||||
|
() -> assertEquals(1815, f.apply(Job.NIGHTWALKER2, cygnus_max_level)),
|
||||||
|
() -> assertEquals(1815, f.apply(Job.NIGHTWALKER3, cygnus_max_level)),
|
||||||
|
() -> assertEquals(1815, f.apply(Job.NIGHTWALKER4, cygnus_max_level)),
|
||||||
|
|
||||||
|
// Pirate (Explorer)
|
||||||
|
() -> assertEquals(3545, f.apply(Job.PIRATE, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(3695, f.apply(Job.BRAWLER, max_level)),
|
||||||
|
() -> assertEquals(3695, f.apply(Job.MARAUDER, max_level)),
|
||||||
|
() -> assertEquals(3695, f.apply(Job.BUCCANEER, max_level)),
|
||||||
|
|
||||||
|
() -> assertEquals(3695, f.apply(Job.GUNSLINGER, max_level)),
|
||||||
|
() -> assertEquals(3695, f.apply(Job.OUTLAW, max_level)),
|
||||||
|
() -> assertEquals(3695, f.apply(Job.CORSAIR, max_level)),
|
||||||
|
|
||||||
|
// Pirate (Cygnus)
|
||||||
|
() -> assertEquals(2105, f.apply(Job.THUNDERBREAKER1, cygnus_max_level)),
|
||||||
|
() -> assertEquals(2255, f.apply(Job.THUNDERBREAKER2, cygnus_max_level)),
|
||||||
|
() -> assertEquals(2255, f.apply(Job.THUNDERBREAKER3, cygnus_max_level)),
|
||||||
|
() -> assertEquals(2255, f.apply(Job.THUNDERBREAKER4, cygnus_max_level))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user