Missing Pirate questlines + Find & MoveLife update + Party-Map patch
Implemented missing scripts for Aerial Strike, Hypnotize and Time Leap skill questlines. Fixed an deadlock issue within player's inventory and status. Fixed skill spam detection disconnecting players when legitimatelly spamming Flash Jump or Heal. Fixed party members map location not being properly updated after changing maps. Rehauled MoveLife handler. Improved packet read of attack and skill elements, properly reflecting incoming actions to other player on the map. Reviewed an issue with equipments MaxHP/MaxMP getting unsynced with player's MaxHP/MaxMP, resulting in differences within server-client view of a player's pool. Fixed mobs not being spawned by dojo bosses after enough spawn actions were instanced. Implemented proper Mob.wz linked mob data support. Fixed /find not identifying a player's channel properly. Fixed Echo of Hero not affecting other player in the map. Fixed commands warp and goto not taking player off events/expeditions properly. Fixed storage expansion not being registered on DB when buying the cash item. Fixed party leadership being reassigned after changing channels. Implemented suggested level range limit between party members on the EXP share system. Players whose level range from the attacker exceeds the estipulated threshold will not receive any EXP from the action. Happy Thanksgiving Day everyone!!!
This commit is contained in:
@@ -645,8 +645,8 @@ public class MapleItemInformationProvider {
|
||||
return Math.pow(1.0 - prop, dices);
|
||||
}
|
||||
|
||||
public static boolean rollSuccessChance(double prop) {
|
||||
return Math.random() >= testYourLuck(prop / 100.0, ServerConstants.SCROLL_CHANCE_RATE);
|
||||
public static boolean rollSuccessChance(double propPercent) {
|
||||
return Math.random() >= testYourLuck(propPercent / 100.0, ServerConstants.SCROLL_CHANCE_RATE);
|
||||
}
|
||||
|
||||
private static short getMaximumShortMaxIfOverflow(int value1, int value2) {
|
||||
|
||||
@@ -717,6 +717,18 @@ public class MapleStatEffect {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean applyEchoOfHero(MapleCharacter applyfrom) {
|
||||
Map<Integer, MapleCharacter> mapPlayers = applyfrom.getMap().getMapPlayers();
|
||||
mapPlayers.remove(applyfrom.getId());
|
||||
|
||||
boolean hwResult = applyTo(applyfrom);
|
||||
for (MapleCharacter chr : mapPlayers.values()) { // Echo of Hero not buffing players in the map detected thanks to Masterrulax
|
||||
applyTo(applyfrom, chr, false, null, false, 1);
|
||||
}
|
||||
|
||||
return hwResult;
|
||||
}
|
||||
|
||||
public boolean applyTo(MapleCharacter chr) {
|
||||
return applyTo(chr, chr, true, null, false, 1);
|
||||
}
|
||||
@@ -886,11 +898,11 @@ public class MapleStatEffect {
|
||||
|
||||
applyto.cancelBuffStats(MapleBuffStat.SOULARROW); // cancel door buff
|
||||
}
|
||||
} else if (isMist()) {
|
||||
} else if (isMist()) {
|
||||
Rectangle bounds = calculateBoundingBox(sourceid == NightWalker.POISON_BOMB ? pos : applyfrom.getPosition(), applyfrom.isFacingLeft());
|
||||
MapleMist mist = new MapleMist(bounds, applyfrom, this);
|
||||
applyfrom.getMap().spawnMist(mist, getDuration(), mist.isPoisonMist(), false, mist.isRecoveryMist());
|
||||
} else if(isTimeLeap()) {
|
||||
} else if (isTimeLeap()) {
|
||||
applyto.removeAllCooldownsExcept(Buccaneer.TIME_LEAP, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ import tools.Pair;
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
* @author Ronan (concurrency safety & check available slots)
|
||||
* @author Ronan - concurrency safety & check available slots
|
||||
*/
|
||||
public class MapleTrade {
|
||||
private MapleTrade partner = null;
|
||||
|
||||
@@ -54,130 +54,179 @@ public class MapleLifeFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private static class MobAttackInfoHolder {
|
||||
protected int attackPos;
|
||||
protected int mpCon;
|
||||
protected int coolTime;
|
||||
protected int animationTime;
|
||||
|
||||
protected MobAttackInfoHolder(int attackPos, int mpCon, int coolTime, int animationTime) {
|
||||
this.attackPos = attackPos;
|
||||
this.mpCon = mpCon;
|
||||
this.coolTime = coolTime;
|
||||
this.animationTime = animationTime;
|
||||
}
|
||||
}
|
||||
|
||||
private static void setMonsterAttackInfo(int mid, List<MobAttackInfoHolder> attackInfos) {
|
||||
if (!attackInfos.isEmpty()) {
|
||||
MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance();
|
||||
|
||||
for (MobAttackInfoHolder attackInfo : attackInfos) {
|
||||
mi.setMobAttackInfo(mid, attackInfo.attackPos, attackInfo.mpCon, attackInfo.coolTime);
|
||||
mi.setMobAttackAnimationTime(mid, attackInfo.attackPos, attackInfo.animationTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Pair<MapleMonsterStats, List<MobAttackInfoHolder>> getMonsterStats(int mid) {
|
||||
MapleData monsterData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(mid) + ".img", '0', 11));
|
||||
if (monsterData == null) {
|
||||
return null;
|
||||
}
|
||||
MapleData monsterInfoData = monsterData.getChildByPath("info");
|
||||
|
||||
List<MobAttackInfoHolder> attackInfos = new LinkedList<>();
|
||||
MapleMonsterStats stats;
|
||||
|
||||
int linkMid = MapleDataTool.getIntConvert("link", monsterInfoData, 0);
|
||||
if (linkMid == 0) {
|
||||
stats = new MapleMonsterStats();
|
||||
} else {
|
||||
Pair<MapleMonsterStats, List<MobAttackInfoHolder>> linkStats = getMonsterStats(linkMid);
|
||||
if (linkStats == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
stats = linkStats.getLeft();
|
||||
attackInfos.addAll(linkStats.getRight());
|
||||
}
|
||||
|
||||
stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData));
|
||||
stats.setFriendly(MapleDataTool.getIntConvert("damagedByMob", monsterInfoData, stats.isFriendly() ? 1 : 0) == 1);
|
||||
stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData));
|
||||
stats.setPDDamage(MapleDataTool.getIntConvert("PDDamage", monsterInfoData));
|
||||
stats.setMADamage(MapleDataTool.getIntConvert("MADamage", monsterInfoData));
|
||||
stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData));
|
||||
stats.setMp(MapleDataTool.getIntConvert("maxMP", monsterInfoData, stats.getMp()));
|
||||
stats.setExp(MapleDataTool.getIntConvert("exp", monsterInfoData, stats.getExp()));
|
||||
stats.setLevel(MapleDataTool.getIntConvert("level", monsterInfoData));
|
||||
stats.setRemoveAfter(MapleDataTool.getIntConvert("removeAfter", monsterInfoData, stats.removeAfter()));
|
||||
stats.setBoss(MapleDataTool.getIntConvert("boss", monsterInfoData, stats.isBoss() ? 1 : 0) > 0);
|
||||
stats.setExplosiveReward(MapleDataTool.getIntConvert("explosiveReward", monsterInfoData, stats.isExplosiveReward() ? 1 : 0) > 0);
|
||||
stats.setFfaLoot(MapleDataTool.getIntConvert("publicReward", monsterInfoData, stats.isFfaLoot() ? 1 : 0) > 0);
|
||||
stats.setUndead(MapleDataTool.getIntConvert("undead", monsterInfoData, stats.isUndead() ? 1 : 0) > 0);
|
||||
stats.setName(MapleDataTool.getString(mid + "/name", mobStringData, "MISSINGNO"));
|
||||
stats.setBuffToGive(MapleDataTool.getIntConvert("buff", monsterInfoData, stats.getBuffToGive()));
|
||||
stats.setCP(MapleDataTool.getIntConvert("getCP", monsterInfoData, stats.getCP()));
|
||||
stats.setRemoveOnMiss(MapleDataTool.getIntConvert("removeOnMiss", monsterInfoData, stats.removeOnMiss() ? 1 : 0) > 0);
|
||||
|
||||
MapleData special = monsterInfoData.getChildByPath("coolDamage");
|
||||
if (special != null) {
|
||||
int coolDmg = MapleDataTool.getIntConvert("coolDamage", monsterInfoData);
|
||||
int coolProb = MapleDataTool.getIntConvert("coolDamageProb", monsterInfoData, 0);
|
||||
stats.setCool(new Pair<>(coolDmg, coolProb));
|
||||
}
|
||||
special = monsterInfoData.getChildByPath("loseItem");
|
||||
if (special != null) {
|
||||
for (MapleData liData : special.getChildren()) {
|
||||
stats.addLoseItem(new loseItem(MapleDataTool.getInt(liData.getChildByPath("id")), (byte) MapleDataTool.getInt(liData.getChildByPath("prop")), (byte) MapleDataTool.getInt(liData.getChildByPath("x"))));
|
||||
}
|
||||
}
|
||||
special = monsterInfoData.getChildByPath("selfDestruction");
|
||||
if (special != null) {
|
||||
stats.setSelfDestruction(new selfDestruction((byte) MapleDataTool.getInt(special.getChildByPath("action")), MapleDataTool.getIntConvert("removeAfter", special, -1), MapleDataTool.getIntConvert("hp", special, -1)));
|
||||
}
|
||||
MapleData firstAttackData = monsterInfoData.getChildByPath("firstAttack");
|
||||
int firstAttack = 0;
|
||||
if (firstAttackData != null) {
|
||||
if (firstAttackData.getType() == MapleDataType.FLOAT) {
|
||||
firstAttack = Math.round(MapleDataTool.getFloat(firstAttackData));
|
||||
} else {
|
||||
firstAttack = MapleDataTool.getInt(firstAttackData);
|
||||
}
|
||||
}
|
||||
stats.setFirstAttack(firstAttack > 0);
|
||||
stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, stats.getDropPeriod() / 10000) * 10000);
|
||||
|
||||
stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0));
|
||||
stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0));
|
||||
|
||||
for (MapleData idata : monsterData) {
|
||||
if (!idata.getName().equals("info")) {
|
||||
int delay = 0;
|
||||
for (MapleData pic : idata.getChildren()) {
|
||||
delay += MapleDataTool.getIntConvert("delay", pic, 0);
|
||||
}
|
||||
stats.setAnimationTime(idata.getName(), delay);
|
||||
}
|
||||
}
|
||||
MapleData reviveInfo = monsterInfoData.getChildByPath("revive");
|
||||
if (reviveInfo != null) {
|
||||
List<Integer> revives = new LinkedList<>();
|
||||
for (MapleData data_ : reviveInfo) {
|
||||
revives.add(MapleDataTool.getInt(data_));
|
||||
}
|
||||
stats.setRevives(revives);
|
||||
}
|
||||
decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, ""));
|
||||
|
||||
MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance();
|
||||
MapleData monsterSkillInfoData = monsterInfoData.getChildByPath("skill");
|
||||
if (monsterSkillInfoData != null) {
|
||||
int i = 0;
|
||||
List<Pair<Integer, Integer>> skills = new ArrayList<>();
|
||||
while (monsterSkillInfoData.getChildByPath(Integer.toString(i)) != null) {
|
||||
int skillId = MapleDataTool.getInt(i + "/skill", monsterSkillInfoData, 0);
|
||||
int skillLv = MapleDataTool.getInt(i + "/level", monsterSkillInfoData, 0);
|
||||
skills.add(new Pair<>(skillId, skillLv));
|
||||
|
||||
MapleData monsterSkillData = monsterData.getChildByPath("skill" + (i + 1));
|
||||
if (monsterSkillData != null) {
|
||||
int animationTime = 0;
|
||||
for (MapleData effectEntry : monsterSkillData.getChildren()) {
|
||||
animationTime += MapleDataTool.getIntConvert("delay", effectEntry, 0);
|
||||
}
|
||||
|
||||
MobSkill skill = MobSkillFactory.getMobSkill(skillId, skillLv);
|
||||
mi.setMobSkillAnimationTime(skill, animationTime);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
stats.setSkills(skills);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
MapleData monsterAttackData;
|
||||
while ((monsterAttackData = monsterData.getChildByPath("attack" + (i + 1))) != null) {
|
||||
int animationTime = 0;
|
||||
for (MapleData effectEntry : monsterAttackData.getChildren()) {
|
||||
animationTime += MapleDataTool.getIntConvert("delay", effectEntry, 0);
|
||||
}
|
||||
|
||||
int mpCon = MapleDataTool.getIntConvert("info/conMP", monsterAttackData, 0);
|
||||
int coolTime = MapleDataTool.getIntConvert("info/attackAfter", monsterAttackData, 0);
|
||||
attackInfos.add(new MobAttackInfoHolder(i, mpCon, coolTime, animationTime));
|
||||
i++;
|
||||
}
|
||||
|
||||
MapleData banishData = monsterInfoData.getChildByPath("ban");
|
||||
if (banishData != null) {
|
||||
stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp")));
|
||||
}
|
||||
|
||||
return new Pair<>(stats, attackInfos);
|
||||
}
|
||||
|
||||
public static MapleMonster getMonster(int mid) {
|
||||
try {
|
||||
MapleMonsterStats stats = monsterStats.get(Integer.valueOf(mid));
|
||||
if (stats == null) {
|
||||
MapleData monsterData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(mid) + ".img", '0', 11));
|
||||
if (monsterData == null) {
|
||||
return null;
|
||||
}
|
||||
MapleData monsterInfoData = monsterData.getChildByPath("info");
|
||||
stats = new MapleMonsterStats();
|
||||
stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData));
|
||||
stats.setFriendly(MapleDataTool.getIntConvert("damagedByMob", monsterInfoData, 0) == 1);
|
||||
stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData));
|
||||
stats.setPDDamage(MapleDataTool.getIntConvert("PDDamage", monsterInfoData));
|
||||
stats.setMADamage(MapleDataTool.getIntConvert("MADamage", monsterInfoData));
|
||||
stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData));
|
||||
stats.setMp(MapleDataTool.getIntConvert("maxMP", monsterInfoData, 0));
|
||||
stats.setExp(MapleDataTool.getIntConvert("exp", monsterInfoData, 0));
|
||||
stats.setLevel(MapleDataTool.getIntConvert("level", monsterInfoData));
|
||||
stats.setRemoveAfter(MapleDataTool.getIntConvert("removeAfter", monsterInfoData, 0));
|
||||
stats.setBoss(MapleDataTool.getIntConvert("boss", monsterInfoData, 0) > 0);
|
||||
stats.setExplosiveReward(MapleDataTool.getIntConvert("explosiveReward", monsterInfoData, 0) > 0);
|
||||
stats.setFfaLoot(MapleDataTool.getIntConvert("publicReward", monsterInfoData, 0) > 0);
|
||||
stats.setUndead(MapleDataTool.getIntConvert("undead", monsterInfoData, 0) > 0);
|
||||
stats.setName(MapleDataTool.getString(mid + "/name", mobStringData, "MISSINGNO"));
|
||||
stats.setBuffToGive(MapleDataTool.getIntConvert("buff", monsterInfoData, -1));
|
||||
stats.setCP(MapleDataTool.getIntConvert("getCP", monsterInfoData, 0));
|
||||
stats.setRemoveOnMiss(MapleDataTool.getIntConvert("removeOnMiss", monsterInfoData, 0) > 0);
|
||||
|
||||
MapleData special = monsterInfoData.getChildByPath("coolDamage");
|
||||
if (special != null) {
|
||||
int coolDmg = MapleDataTool.getIntConvert("coolDamage", monsterInfoData);
|
||||
int coolProb = MapleDataTool.getIntConvert("coolDamageProb", monsterInfoData, 0);
|
||||
stats.setCool(new Pair<>(coolDmg, coolProb));
|
||||
}
|
||||
special = monsterInfoData.getChildByPath("loseItem");
|
||||
if (special != null) {
|
||||
for (MapleData liData : special.getChildren()) {
|
||||
stats.addLoseItem(new loseItem(MapleDataTool.getInt(liData.getChildByPath("id")), (byte) MapleDataTool.getInt(liData.getChildByPath("prop")), (byte) MapleDataTool.getInt(liData.getChildByPath("x"))));
|
||||
}
|
||||
}
|
||||
special = monsterInfoData.getChildByPath("selfDestruction");
|
||||
if (special != null) {
|
||||
stats.setSelfDestruction(new selfDestruction((byte) MapleDataTool.getInt(special.getChildByPath("action")), MapleDataTool.getIntConvert("removeAfter", special, -1), MapleDataTool.getIntConvert("hp", special, -1)));
|
||||
}
|
||||
MapleData firstAttackData = monsterInfoData.getChildByPath("firstAttack");
|
||||
int firstAttack = 0;
|
||||
if (firstAttackData != null) {
|
||||
if (firstAttackData.getType() == MapleDataType.FLOAT) {
|
||||
firstAttack = Math.round(MapleDataTool.getFloat(firstAttackData));
|
||||
} else {
|
||||
firstAttack = MapleDataTool.getInt(firstAttackData);
|
||||
}
|
||||
}
|
||||
stats.setFirstAttack(firstAttack > 0);
|
||||
stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, 0) * 10000);
|
||||
|
||||
stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0));
|
||||
stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0));
|
||||
|
||||
for (MapleData idata : monsterData) {
|
||||
if (!idata.getName().equals("info")) {
|
||||
int delay = 0;
|
||||
for (MapleData pic : idata.getChildren()) {
|
||||
delay += MapleDataTool.getIntConvert("delay", pic, 0);
|
||||
}
|
||||
stats.setAnimationTime(idata.getName(), delay);
|
||||
}
|
||||
}
|
||||
MapleData reviveInfo = monsterInfoData.getChildByPath("revive");
|
||||
if (reviveInfo != null) {
|
||||
List<Integer> revives = new LinkedList<>();
|
||||
for (MapleData data_ : reviveInfo) {
|
||||
revives.add(MapleDataTool.getInt(data_));
|
||||
}
|
||||
stats.setRevives(revives);
|
||||
}
|
||||
decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, ""));
|
||||
Pair<MapleMonsterStats, List<MobAttackInfoHolder>> mobStats = getMonsterStats(mid);
|
||||
stats = mobStats.getLeft();
|
||||
setMonsterAttackInfo(mid, mobStats.getRight());
|
||||
|
||||
MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance();
|
||||
MapleData monsterSkillInfoData = monsterInfoData.getChildByPath("skill");
|
||||
if (monsterSkillInfoData != null) {
|
||||
int i = 0;
|
||||
List<Pair<Integer, Integer>> skills = new ArrayList<>();
|
||||
while (monsterSkillInfoData.getChildByPath(Integer.toString(i)) != null) {
|
||||
int skillId = MapleDataTool.getInt(i + "/skill", monsterSkillInfoData, 0);
|
||||
int skillLv = MapleDataTool.getInt(i + "/level", monsterSkillInfoData, 0);
|
||||
skills.add(new Pair<>(skillId, skillLv));
|
||||
|
||||
MapleData monsterSkillData = monsterData.getChildByPath("skill" + (i + 1));
|
||||
if (monsterSkillData != null) {
|
||||
int animationTime = 0;
|
||||
for (MapleData effectEntry : monsterSkillData.getChildren()) {
|
||||
animationTime += MapleDataTool.getIntConvert("delay", effectEntry, 0);
|
||||
}
|
||||
|
||||
MobSkill skill = MobSkillFactory.getMobSkill(skillId, skillLv);
|
||||
mi.setMobSkillAnimationTime(skill, animationTime);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
stats.setSkills(skills);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
MapleData monsterAttackData;
|
||||
while ((monsterAttackData = monsterData.getChildByPath("attack" + (i + 1))) != null) {
|
||||
int animationTime = 0;
|
||||
for (MapleData effectEntry : monsterAttackData.getChildren()) {
|
||||
animationTime += MapleDataTool.getIntConvert("delay", effectEntry, 0);
|
||||
}
|
||||
|
||||
int mpCon = MapleDataTool.getIntConvert("info/conMP", monsterAttackData, 0);
|
||||
int coolTime = MapleDataTool.getIntConvert("info/attackAfter", monsterAttackData, 0);
|
||||
mi.setMobAttackInfo(mid, i, mpCon, coolTime);
|
||||
mi.setMobAttackAnimationTime(mid, i, animationTime);
|
||||
i++;
|
||||
}
|
||||
|
||||
MapleData banishData = monsterInfoData.getChildByPath("ban");
|
||||
if (banishData != null) {
|
||||
stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp")));
|
||||
}
|
||||
monsterStats.put(Integer.valueOf(mid), stats);
|
||||
}
|
||||
MapleMonster ret = new MapleMonster(mid, stats);
|
||||
|
||||
@@ -402,7 +402,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
return takenDamage.containsKey(chr.getId());
|
||||
}
|
||||
|
||||
private void distributeExperienceToParty(int pid, float exp, int killer, Set<MapleCharacter> underleveled, int minThresholdLevel) {
|
||||
private void distributeExperienceToParty(int pid, float exp, int killer, int killerLevel, Set<MapleCharacter> underleveled, int minThresholdLevel) {
|
||||
List<MapleCharacter> members = new LinkedList<>();
|
||||
MapleCharacter pchar = getMap().getAnyCharacterFromParty(pid);
|
||||
if(pchar != null) {
|
||||
@@ -415,13 +415,17 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
|
||||
members.add(chr);
|
||||
}
|
||||
|
||||
int partyLevel = 0;
|
||||
int leechCount = 0;
|
||||
|
||||
List<MapleCharacter> expSharers = new LinkedList<>();
|
||||
int expSharersLevel = 0;
|
||||
for (MapleCharacter mc : members) {
|
||||
if (mc.getLevel() >= minThresholdLevel) { //NO EXP WILL BE GIVEN for those who are underleveled!
|
||||
partyLevel += mc.getLevel();
|
||||
leechCount++;
|
||||
if (Math.abs(killerLevel - mc.getLevel()) < ServerConstants.MIN_UNDERLEVEL_TO_EXP_LEECH) {
|
||||
// thanks Thora for pointing out leech level limitation
|
||||
|
||||
expSharersLevel += mc.getLevel();
|
||||
expSharers.add(mc);
|
||||
}
|
||||
} else {
|
||||
underleveled.add(mc);
|
||||
}
|
||||
@@ -429,18 +433,15 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
|
||||
final int mostDamageCid = getHighestDamagerId();
|
||||
|
||||
for (MapleCharacter mc : members) {
|
||||
for (MapleCharacter mc : expSharers) {
|
||||
int id = mc.getId();
|
||||
int level = mc.getLevel();
|
||||
if (level >= minThresholdLevel) {
|
||||
boolean isKiller = killer == id;
|
||||
boolean mostDamage = mostDamageCid == id;
|
||||
float xp = ((0.80f * exp * level) / partyLevel);
|
||||
if (mostDamage) {
|
||||
xp += (0.20f * exp);
|
||||
}
|
||||
giveExpToCharacter(mc, xp, isKiller, leechCount);
|
||||
boolean isKiller = killer == id;
|
||||
boolean mostDamage = mostDamageCid == id;
|
||||
float xp = ((0.80f * exp * mc.getLevel()) / expSharersLevel);
|
||||
if (mostDamage) {
|
||||
xp += (0.20f * exp);
|
||||
}
|
||||
giveExpToCharacter(mc, xp, isKiller, expSharers.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,7 +465,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
EventInstanceManager eim = getMap().getEventInstance();
|
||||
int minThresholdLevel = calcThresholdLevel(eim != null);
|
||||
int minThresholdLevel = calcThresholdLevel(eim != null), killerLevel = Integer.MAX_VALUE;
|
||||
int exp = getExp();
|
||||
long totalHealth = maxHpPlusHeal.get();
|
||||
Map<Integer, Float> expDist = new HashMap<>();
|
||||
@@ -482,15 +483,14 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
for (MapleCharacter mc : mapChrs) {
|
||||
Float mcExp = expDist.remove(mc.getId());
|
||||
if (mcExp != null) {
|
||||
float xp = mcExp;
|
||||
boolean isKiller = (mc.getId() == killerId);
|
||||
if (isKiller) {
|
||||
if (eim != null) {
|
||||
eim.monsterKilled(mc, this);
|
||||
}
|
||||
}
|
||||
|
||||
float xp = mcExp;
|
||||
if (isKiller) {
|
||||
|
||||
killerLevel = mc.getLevel();
|
||||
xp += exp2;
|
||||
}
|
||||
|
||||
@@ -529,7 +529,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
for (Entry<Integer, Float> party : partyExp.entrySet()) {
|
||||
distributeExperienceToParty(party.getKey(), party.getValue(), killerId, underleveled, minThresholdLevel);
|
||||
distributeExperienceToParty(party.getKey(), party.getValue(), killerId, killerLevel, underleveled, minThresholdLevel);
|
||||
}
|
||||
|
||||
for(MapleCharacter mc : underleveled) {
|
||||
@@ -573,10 +573,12 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
private void removeController() {
|
||||
private Pair<MapleCharacter, Boolean> removeController() {
|
||||
this.lockMonster();
|
||||
try {
|
||||
MapleCharacter chrController = getController();
|
||||
boolean hadAggro = isControllerHasAggro();
|
||||
|
||||
if (chrController != null) { // this can/should only happen when a hidden gm attacks the monster
|
||||
chrController.announce(MaplePacketCreator.stopControllingMonster(this.getObjectId()));
|
||||
chrController.stopControllingMonster(this);
|
||||
@@ -585,6 +587,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
setController(null);
|
||||
setControllerHasAggro(false);
|
||||
setControllerKnowsAboutAggro(false);
|
||||
|
||||
return new Pair<>(chrController, hadAggro);
|
||||
} finally {
|
||||
this.unlockMonster();
|
||||
}
|
||||
@@ -593,9 +597,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
public MapleCharacter killBy(final MapleCharacter killer) {
|
||||
distributeExperience(killer != null ? killer.getId() : 0);
|
||||
|
||||
removeController();
|
||||
|
||||
final List<Integer> toSpawn = this.getRevives(); // this doesn't work (?)
|
||||
final Pair<MapleCharacter, Boolean> lastController = removeController();
|
||||
final List<Integer> toSpawn = this.getRevives();
|
||||
if (toSpawn != null) {
|
||||
final MapleMap reviveMap = map;
|
||||
if (toSpawn.contains(9300216) && reviveMap.getId() > 925000000 && reviveMap.getId() < 926000000) {
|
||||
@@ -630,6 +633,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MapleCharacter controller = lastController.getLeft();
|
||||
boolean aggro = lastController.getRight();
|
||||
|
||||
for (Integer mid : toSpawn) {
|
||||
final MapleMonster mob = MapleLifeFactory.getMonster(mid);
|
||||
mob.setPosition(getPosition());
|
||||
@@ -641,7 +647,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
reviveMap.spawnMonster(mob);
|
||||
|
||||
if(mob.getId() >= 8810010 && mob.getId() <= 8810017 && reviveMap.isHorntailDefeated()) {
|
||||
if (mob.getId() >= 8810010 && mob.getId() <= 8810017 && reviveMap.isHorntailDefeated()) {
|
||||
boolean htKilled = false;
|
||||
MapleMonster ht = reviveMap.getMonsterById(8810018);
|
||||
|
||||
@@ -663,6 +669,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
for(int i = 8810017; i >= 8810010; i--) {
|
||||
reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true);
|
||||
}
|
||||
} else if (controller != null) {
|
||||
mob.switchController(controller, aggro);
|
||||
}
|
||||
|
||||
if(eim != null) {
|
||||
@@ -1176,10 +1184,10 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
public void refreshMobPosition() {
|
||||
updateMobPosition(getPosition());
|
||||
resetMobPosition(getPosition());
|
||||
}
|
||||
|
||||
public void updateMobPosition(Point newPoint) {
|
||||
public void resetMobPosition(Point newPoint) {
|
||||
removeController();
|
||||
setPosition(newPoint);
|
||||
map.broadcastMessage(MaplePacketCreator.moveMonster(this.getObjectId(), false, -1, 0, 0, 0, this.getPosition(), this.getIdleMovement()));
|
||||
@@ -1276,11 +1284,25 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
return stats.hasSkill(skillId, level);
|
||||
}
|
||||
|
||||
public int getSkillPos(int skillId, int level) {
|
||||
int pos = 0;
|
||||
for (Pair<Integer, Integer> ms : this.getSkills()) {
|
||||
if (ms.getLeft() == skillId && ms.getRight() == level) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean canUseSkill(MobSkill toUse) {
|
||||
if (toUse == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int useLimit = toUse.getLimit();
|
||||
if (toUse.getSkillId() == 200) {
|
||||
int i = 0;
|
||||
for (MapleMapObject mo : getMap().getMapObjects()) {
|
||||
@@ -1291,13 +1313,16 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
if (i > 100) {
|
||||
return false;
|
||||
}
|
||||
if (map.isDojoMap()) { // spawns in dojo should be unlimited
|
||||
useLimit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (toUse.getLimit() > 0) {
|
||||
if (useLimit > 0) {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
Integer times = this.skillsUsed.get(new Pair<>(toUse.getSkillId(), toUse.getSkillLevel()));
|
||||
if (times != null && times >= toUse.getLimit()) {
|
||||
if (times != null && times >= useLimit) {
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
@@ -1307,20 +1332,24 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
|
||||
monsterLock.lock();
|
||||
try {
|
||||
/*
|
||||
for (Pair<Integer, Integer> skill : usedSkills) {
|
||||
if (skill.getLeft() == toUse.getSkillId() && skill.getRight() == toUse.getSkillLevel()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
int mpCon = toUse.getMpCon();
|
||||
if (mp < mpCon) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
if (!this.applyAnimationIfRoaming(-1, toUse)) {
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
this.usedSkill(toUse);
|
||||
} finally {
|
||||
@@ -1380,16 +1409,18 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
public int canUseAttack(int attackPos) {
|
||||
public int canUseAttack(int attackPos, boolean isSkill) {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
/*
|
||||
if (usedAttacks.contains(attackPos)) {
|
||||
return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
Pair<Integer, Integer> attackInfo = MapleMonsterInformationProvider.getInstance().getMobAttackInfo(this.getId(), attackPos);
|
||||
if (attackInfo == null) {
|
||||
return usedAttacks.isEmpty() ? 0 : -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mpCon = attackInfo.getLeft();
|
||||
@@ -1397,9 +1428,11 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
if (!this.applyAnimationIfRoaming(attackPos, null)) {
|
||||
return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
usedAttack(attackPos, mpCon, attackInfo.getRight());
|
||||
return 1;
|
||||
|
||||
@@ -37,7 +37,7 @@ import tools.Pair;
|
||||
*/
|
||||
public class MapleMonsterStats {
|
||||
private boolean changeable;
|
||||
private int exp, hp, mp, level, PADamage, PDDamage, MADamage, MDDamage, dropPeriod, cp, buffToGive, removeAfter;
|
||||
private int exp, hp, mp, level, PADamage, PDDamage, MADamage, MDDamage, dropPeriod, cp, buffToGive = -1, removeAfter;
|
||||
private boolean boss, undead, ffaLoot, isExplosiveReward, firstAttack, removeOnMiss;
|
||||
private String name;
|
||||
private Map<String, Integer> animationTimes = new HashMap<String, Integer>();
|
||||
@@ -151,7 +151,7 @@ public class MapleMonsterStats {
|
||||
this.undead = undead;
|
||||
}
|
||||
|
||||
public boolean getUndead() {
|
||||
public boolean isUndead() {
|
||||
return undead;
|
||||
}
|
||||
|
||||
@@ -193,8 +193,12 @@ public class MapleMonsterStats {
|
||||
}
|
||||
|
||||
public void setSkills(List<Pair<Integer, Integer>> skills) {
|
||||
for (Pair<Integer, Integer> skill : skills) {
|
||||
this.skills.add(skill);
|
||||
for (int i = this.skills.size(); i < skills.size(); i++) {
|
||||
this.skills.add(null);
|
||||
}
|
||||
|
||||
for (int i = 0; i < skills.size(); i++) {
|
||||
this.skills.set(i, skills.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ import net.server.audit.locks.MonitoredLockType;
|
||||
/**
|
||||
*
|
||||
* @author XoticStory
|
||||
* @author Ronan (concurrency protection)
|
||||
* @author Ronan - concurrency protection
|
||||
*/
|
||||
public class MapleHiredMerchant extends AbstractMapleMapObject {
|
||||
private int ownerId, itemId, mesos = 0;
|
||||
|
||||
@@ -1539,7 +1539,40 @@ public class MapleMap {
|
||||
mr.setPosition(points.remove(points.size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private MapleCharacter getNextControllerCandidate() {
|
||||
int mincontrolled = Integer.MAX_VALUE;
|
||||
MapleCharacter newController = null;
|
||||
|
||||
int mincontrolleddead = Integer.MAX_VALUE;
|
||||
MapleCharacter newControllerDead = null;
|
||||
|
||||
chrRLock.lock();
|
||||
try {
|
||||
for (MapleCharacter chr : characters) {
|
||||
if (!chr.isHidden()) {
|
||||
int ctrlMonsSize = chr.getControlledMonsters().size();
|
||||
|
||||
if (chr.isAlive()) {
|
||||
if (ctrlMonsSize < mincontrolled) {
|
||||
mincontrolled = ctrlMonsSize;
|
||||
newController = chr;
|
||||
}
|
||||
} else {
|
||||
if (ctrlMonsSize < mincontrolleddead) {
|
||||
mincontrolleddead = ctrlMonsSize;
|
||||
newControllerDead = chr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
chrRLock.unlock();
|
||||
}
|
||||
|
||||
return newController != null ? newController : newControllerDead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automagically finds a new controller for the given monster from the chars
|
||||
* on the map...
|
||||
@@ -1553,29 +1586,28 @@ public class MapleMap {
|
||||
return;
|
||||
}
|
||||
|
||||
MapleCharacter newController;
|
||||
MapleCharacter chrController = monster.getController();
|
||||
if (chrController != null) {
|
||||
if (chrController.getMap() != this) {
|
||||
chrController.stopControllingMonster(monster);
|
||||
newController = getNextControllerCandidate();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
int mincontrolled = -1;
|
||||
MapleCharacter newController = null;
|
||||
chrRLock.lock();
|
||||
try {
|
||||
for (MapleCharacter chr : characters) {
|
||||
int ctrlMonsSize = chr.getControlledMonsters().size();
|
||||
|
||||
if (!chr.isHidden() && (ctrlMonsSize < mincontrolled || mincontrolled == -1)) {
|
||||
mincontrolled = ctrlMonsSize;
|
||||
newController = chr;
|
||||
if (chrController.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
newController = getNextControllerCandidate();
|
||||
if (newController == null || !newController.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
chrController.stopControllingMonster(monster);
|
||||
}
|
||||
} finally {
|
||||
chrRLock.unlock();
|
||||
} else {
|
||||
newController = getNextControllerCandidate();
|
||||
}
|
||||
|
||||
if (newController != null) { // was a new controller found? (if not no one is on the map)
|
||||
if (monster.isFirstAttack()) {
|
||||
newController.controlMonster(monster, true);
|
||||
@@ -2364,11 +2396,8 @@ public class MapleMap {
|
||||
chr.cancelEffectFromBuffStat(MapleBuffStat.MONSTER_RIDING);
|
||||
chr.cancelBuffStats(MapleBuffStat.MONSTER_RIDING);
|
||||
}
|
||||
if (mapid == 923010000) { // Kenta's Mount Quest
|
||||
if(getMonsterById(9300102) == null) {
|
||||
spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(9300102), new Point(77, 426));
|
||||
}
|
||||
} else if (mapid == 200090060) { // To Rien
|
||||
|
||||
if (mapid == 200090060) { // To Rien
|
||||
int travelTime = getWorldServer().getTransportationTime(1 * 60 * 1000);
|
||||
chr.announce(MaplePacketCreator.getClock(travelTime / 1000));
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
|
||||
@@ -46,7 +46,7 @@ import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
* @author Ronan (concurrency protection)
|
||||
* @author Ronan - concurrency protection
|
||||
*/
|
||||
public class MaplePlayerShop extends AbstractMapleMapObject {
|
||||
private AtomicBoolean open = new AtomicBoolean(false);
|
||||
|
||||
@@ -30,7 +30,7 @@ import tools.MaplePacketCreator;
|
||||
/*
|
||||
* MapleTVEffect
|
||||
* @author MrXotic
|
||||
* @author Ronan (made MapleTV mechanics synchronous)
|
||||
* @author Ronan - made MapleTV mechanics synchronous
|
||||
*/
|
||||
public class MapleTVEffect {
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ import tools.StringUtil;
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
* @author Ronan (support for medal quests)
|
||||
* @author Ronan - support for medal quests
|
||||
*/
|
||||
public class MapleQuest {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user