HikariCP config + MaxHP/MP & EXP overhaul + Venom fix
Overhauled the HikariCP connection, now it properly tries the best to hand out a DB connection. Fixed "commands" NPC crashing out players when entering the command multiple times in a short time. Rebalanced HP/MP gain on leveling up/AP resetting. Rebalanced HP/MP loss when AP resetting to use the same amount they would be earning on levelup. Fixed EXP distribution now computing overall monster maxHP plus heal instead of flat monter maxHP, that rendered on distributions of over 100% of the raw EXP. Added concurrency protection on MapleMonster. Fixed an issue on venom skills that would let an "1 dmg" appear to the client alongside the DOT.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#Sun, 22 Oct 2017 15:23:02 -0200
|
||||
#Fri, 27 Oct 2017 11:16:00 -0200
|
||||
|
||||
|
||||
C\:\\Nexon\\MapleSolaxia\\MapleSolaxiaV2=
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
dist/MapleSolaxia.jar
vendored
BIN
dist/MapleSolaxia.jar
vendored
Binary file not shown.
@@ -76,6 +76,7 @@ Server potentials:
|
||||
* Owl of Minerva.
|
||||
* Pet item ignore.
|
||||
* Autosaver (periodically saves on DB current state of every player in-game).
|
||||
* Fixed and randomized versions of HP/MP growth rate, regarding player job. Placeholder for HP/MP washing feature.
|
||||
|
||||
Admin/GM commands:
|
||||
* Server commands layered by GM levels.
|
||||
|
||||
@@ -602,4 +602,16 @@ Corrigido Map chair n
|
||||
Corrigido itens com ownership diferente sendo agrupados num mesmo slot, perdendo a referencia de dono.
|
||||
Implementado feature "Arrange Items" do MapleStorage. Ele faz os devidos agrupamentos de itens e organiza os itens do storage.
|
||||
Corrigido storage mesclando itens que deveriam ser únicos (que não poderiam haver mais de um num mesmo slot, ou no inventário do jogador).
|
||||
Corrigido bug onde colocar um pet equipado no Cash Inventory e voltar ao jogo causaria crash no jogador.
|
||||
Corrigido bug onde colocar um pet equipado no Cash Inventory e voltar ao jogo causaria crash no jogador.
|
||||
|
||||
23 - 24 Outubro 2017,
|
||||
Adicionado proteção contra acesso concorrente em módulos de MapleMonster.
|
||||
Corrigido bug com Venom fazendo aparecer "dano 1" no DOT.
|
||||
Corrigido sistema de EXP agora contabilizando devidamente HP curado pelo mob na distribuição do EXP.
|
||||
|
||||
25 Outubro 2017,
|
||||
Corrigido alguns problemas com as configs de inicialização do HikariCP.
|
||||
Corrigido bug com inicialização de NPC scripts podendo desconectar o jogador se instaciado múltiplas vezes.
|
||||
|
||||
26 Outubro 2017,
|
||||
Adicionado feature de randomização dos stats ganhos de HP e MP. Stat ganho de MP leva na contabilização INT do jogador.
|
||||
@@ -7,4 +7,4 @@ file.reference.slf4j-api-1.6.6.jar=C:\\Nexon\\MapleSolaxia\\MapleSolaxiaV2\\core
|
||||
file.reference.slf4j-jdk14-1.7.5.jar=C:\\Nexon\\MapleSolaxia\\MapleSolaxiaV2\\cores\\slf4j-jdk14-1.7.5.jar
|
||||
javac.debug=true
|
||||
javadoc.preview=true
|
||||
user.properties.file=C:\\Users\\RonanLana\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties
|
||||
user.properties.file=C:\\Users\\USER\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties
|
||||
|
||||
@@ -2,15 +2,6 @@
|
||||
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
||||
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="2"/>
|
||||
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
|
||||
<group>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/PlayerLoggedinHandler.java</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/command/Commands.java</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/CashOperationHandler.java</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/quest/requirements/QuestRequirement.java</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/net/server/channel/handlers/InventoryMergeHandler.java</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/MapleStorage.java</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/client/MapleCharacter.java</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/src/server/MapleStorageInventory.java</file>
|
||||
</group>
|
||||
<group/>
|
||||
</open-files>
|
||||
</project-private>
|
||||
|
||||
@@ -35,6 +35,11 @@ function action(mode, type, selection) {
|
||||
if (mode < 0)
|
||||
cm.dispose();
|
||||
else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == 1)
|
||||
status++;
|
||||
else
|
||||
|
||||
@@ -40,6 +40,7 @@ public class ServerConstants {
|
||||
public static final boolean USE_MTS = false;
|
||||
public static final boolean USE_FAMILY_SYSTEM = false;
|
||||
public static final boolean USE_DUEY = true;
|
||||
public static final boolean USE_RANDOMIZE_HPMP_GAIN = true; //Enables randomizing on MaxHP/MaxMP gains and INT accounting for the MaxMP gain.
|
||||
public static final boolean USE_STORAGE_ITEM_SORT = true; //Enables storage "Arrange Items" feature.
|
||||
public static final boolean USE_ITEM_SORT = true; //Enables inventory "Item Sort/Merge" feature.
|
||||
public static final boolean USE_ITEM_SORT_BY_NAME = false; //Item sorting based on name rather than id.
|
||||
@@ -49,7 +50,7 @@ public class ServerConstants {
|
||||
public static final boolean USE_AUTOSAVE = true; //Enables server autosaving feature (saves characters to DB each 1 hour).
|
||||
public static final boolean USE_SERVER_AUTOASSIGNER = true; //Server-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments.
|
||||
public static final boolean USE_REFRESH_RANK_MOVE = true;
|
||||
public static final boolean USE_ENFORCE_OWL_SUGGESTIONS = false;//Forces the Owl of Minerva to always display the defined item array on GameConstants.OWL_DATA instead of the featured by the players.
|
||||
public static final boolean USE_ENFORCE_OWL_SUGGESTIONS = false;//Forces the Owl of Minerva to always display the defined item array on GameConstants.OWL_DATA instead of those featured by the players.
|
||||
public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues)
|
||||
public static final boolean USE_ENFORCE_MDOOR_POSITION = true; //Forces mystic door to be spawned near spawnpoints. (since things bugs out other way, and this helps players to locate the door faster)
|
||||
public static final boolean USE_ERASE_PERMIT_ON_OPENSHOP = true;//Forces "shop permit" item to be consumed when player deploy his/her player shop.
|
||||
@@ -70,9 +71,11 @@ public class ServerConstants {
|
||||
|
||||
public static final int PARTY_EXPERIENCE_MOD = 1; //Change for event stuff.
|
||||
|
||||
public static final byte MAX_MONITORED_BUFFSTATS = 5; //Limits accounting for "dormant" buff effects, that should take place when stronger stat buffs expires.
|
||||
public static final int MAX_AP = 32767; //Max AP allotted on the auto-assigner.
|
||||
public static final int MAX_EVENT_LEVELS = 8; //Event has different levels of rewarding system.
|
||||
//Miscellaneous COnfiguration
|
||||
public static final byte MIN_UNDERLEVEL_FOR_EXP_GAIN = 5; //Characters are unable to get EXP from a mob if their level are under this threshold, only if "USE_UNDERLEVELED_EXP_BLOCK" is enabled.
|
||||
public static final byte MAX_MONITORED_BUFFSTATS = 5; //Limits accounting for "dormant" buff effects, that should take place when stronger stat buffs expires.
|
||||
public static final int MAX_AP = 32767; //Max AP allotted on the auto-assigner.
|
||||
public static final int MAX_EVENT_LEVELS = 8; //Event has different levels of rewarding system.
|
||||
public static final long BLOCK_NPC_RACE_CONDT = (long)(0.5 * 1000); //Time the player client must wait before reopening a conversation with an NPC.
|
||||
public static final long PET_LOOT_UPON_ATTACK = (long)(0.7 * 1000); //Time the pet must wait before trying to pick items up.
|
||||
|
||||
|
||||
@@ -582,19 +582,20 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
|
||||
ret.speed = lea.readByte();
|
||||
lea.skip(4);
|
||||
}
|
||||
int calcDmgMax = 0;
|
||||
|
||||
// Find the base damage to base futher calculations on.
|
||||
// Several skills have their own formula in this section.
|
||||
|
||||
// Find the base damage to base futher calculations on.
|
||||
// Several skills have their own formula in this section.
|
||||
int calcDmgMax = 0;
|
||||
|
||||
if(magic && ret.skill != 0) {
|
||||
calcDmgMax = (chr.getTotalMagic() * chr.getTotalMagic() / 1000 + chr.getTotalMagic()) / 30 + chr.getTotalInt() / 200;
|
||||
} else if(ret.skill == 4001344 || ret.skill == NightWalker.LUCKY_SEVEN || ret.skill == NightLord.TRIPLE_THROW) {
|
||||
calcDmgMax = (chr.getTotalLuk() * 5) * chr.getTotalWatk() / 100;
|
||||
} else if(ret.skill == DragonKnight.DRAGON_ROAR) {
|
||||
calcDmgMax = (chr.getTotalStr() * 4 + chr.getTotalDex()) * chr.getTotalWatk() / 100;
|
||||
} else if(ret.skill == NightLord.VENOMOUS_STAR || ret.skill == Shadower.VENOMOUS_STAB) {
|
||||
calcDmgMax = (int) (18.5 * (chr.getTotalStr() + chr.getTotalLuk()) + chr.getTotalDex() * 2) / 100 * chr.calculateMaxBaseDamage(chr.getTotalWatk());
|
||||
} else {
|
||||
} else if(ret.skill == NightLord.VENOMOUS_STAR || ret.skill == Shadower.VENOMOUS_STAB) {
|
||||
calcDmgMax = (int) (18.5 * (chr.getTotalStr() + chr.getTotalLuk()) + chr.getTotalDex() * 2) / 100 * chr.calculateMaxBaseDamage(chr.getTotalWatk());
|
||||
} else {
|
||||
calcDmgMax = chr.calculateMaxBaseDamage(chr.getTotalWatk());
|
||||
}
|
||||
|
||||
@@ -739,74 +740,74 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
|
||||
}
|
||||
|
||||
if(ret.skill != 0) {
|
||||
Skill skill = SkillFactory.getSkill(ret.skill);
|
||||
if(skill.getElement() != Element.NEUTRAL && chr.getBuffedValue(MapleBuffStat.ELEMENTAL_RESET) == null) {
|
||||
// The skill has an element effect, so we need to factor that in.
|
||||
if(monster != null) {
|
||||
ElementalEffectiveness eff = monster.getEffectiveness(skill.getElement());
|
||||
if(eff == ElementalEffectiveness.WEAK) {
|
||||
Skill skill = SkillFactory.getSkill(ret.skill);
|
||||
if(skill.getElement() != Element.NEUTRAL && chr.getBuffedValue(MapleBuffStat.ELEMENTAL_RESET) == null) {
|
||||
// The skill has an element effect, so we need to factor that in.
|
||||
if(monster != null) {
|
||||
ElementalEffectiveness eff = monster.getElementalEffectiveness(skill.getElement());
|
||||
if(eff == ElementalEffectiveness.WEAK) {
|
||||
calcDmgMax *= 1.5;
|
||||
} else if(eff == ElementalEffectiveness.STRONG) {
|
||||
//calcDmgMax *= 0.5;
|
||||
}
|
||||
} else {
|
||||
// Since we already know the skill has an elemental attribute, but we dont know if the monster is weak or not, lets
|
||||
// take the safe approach and just assume they are weak.
|
||||
calcDmgMax *= 1.5;
|
||||
} else if(eff == ElementalEffectiveness.STRONG) {
|
||||
//calcDmgMax *= 0.5;
|
||||
}
|
||||
} else {
|
||||
// Since we already know the skill has an elemental attribute, but we dont know if the monster is weak or not, lets
|
||||
// take the safe approach and just assume they are weak.
|
||||
calcDmgMax *= 1.5;
|
||||
}
|
||||
}
|
||||
if(ret.skill == FPWizard.POISON_BREATH || ret.skill == FPMage.POISON_MIST || ret.skill == FPArchMage.FIRE_DEMON || ret.skill == ILArchMage.ICE_DEMON) {
|
||||
if(monster != null) {
|
||||
// Turns out poison is completely server side, so I don't know why I added this. >.<
|
||||
//calcDmgMax = monster.getHp() / (70 - chr.getSkillLevel(skill));
|
||||
}
|
||||
} else if(ret.skill == Hermit.SHADOW_WEB) {
|
||||
if(monster != null) {
|
||||
calcDmgMax = monster.getHp() / (50 - chr.getSkillLevel(skill));
|
||||
}
|
||||
}
|
||||
if(ret.skill == FPWizard.POISON_BREATH || ret.skill == FPMage.POISON_MIST || ret.skill == FPArchMage.FIRE_DEMON || ret.skill == ILArchMage.ICE_DEMON) {
|
||||
if(monster != null) {
|
||||
// Turns out poison is completely server side, so I don't know why I added this. >.<
|
||||
//calcDmgMax = monster.getHp() / (70 - chr.getSkillLevel(skill));
|
||||
}
|
||||
} else if(ret.skill == Hermit.SHADOW_WEB) {
|
||||
if(monster != null) {
|
||||
calcDmgMax = monster.getHp() / (50 - chr.getSkillLevel(skill));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < ret.numDamage; j++) {
|
||||
int damage = lea.readInt();
|
||||
int hitDmgMax = calcDmgMax;
|
||||
if(ret.skill == Buccaneer.BARRAGE) {
|
||||
if(j > 3)
|
||||
hitDmgMax *= Math.pow(2, (j - 3));
|
||||
}
|
||||
if(shadowPartner) {
|
||||
// For shadow partner, the second half of the hits only do 50% damage. So calc that
|
||||
// in for the crit effects.
|
||||
if(j >= ret.numDamage / 2) {
|
||||
hitDmgMax *= 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
if(ret.skill == Marksman.SNIPE) {
|
||||
damage = 195000 + Randomizer.nextInt(5000);
|
||||
hitDmgMax = 200000;
|
||||
}
|
||||
|
||||
int maxWithCrit = hitDmgMax;
|
||||
if(canCrit) // They can crit, so up the max.
|
||||
maxWithCrit *= 2;
|
||||
|
||||
// Warn if the damage is over 1.5x what we calculated above.
|
||||
if(damage > maxWithCrit * 1.5) {
|
||||
AutobanFactory.DAMAGE_HACK.alert(chr, "DMG: " + damage + " MaxDMG: " + maxWithCrit + " SID: " + ret.skill + " MobID: " + (monster != null ? monster.getId() : "null") + " Map: " + chr.getMap().getMapName() + " (" + chr.getMapId() + ")");
|
||||
}
|
||||
|
||||
// Add a ab point if its over 5x what we calculated.
|
||||
if(damage > maxWithCrit * 5) {
|
||||
AutobanFactory.DAMAGE_HACK.addPoint(chr.getAutobanManager(), "DMG: " + damage + " MaxDMG: " + maxWithCrit + " SID: " + ret.skill + " MobID: " + (monster != null ? monster.getId() : "null") + " Map: " + chr.getMap().getMapName() + " (" + chr.getMapId() + ")");
|
||||
}
|
||||
|
||||
if (ret.skill == Marksman.SNIPE || (canCrit && damage > hitDmgMax)) {
|
||||
// If the skill is a crit, inverse the damage to make it show up on clients.
|
||||
damage = -Integer.MAX_VALUE + damage - 1;
|
||||
}
|
||||
|
||||
allDamageNumbers.add(damage);
|
||||
int damage = lea.readInt();
|
||||
int hitDmgMax = calcDmgMax;
|
||||
if(ret.skill == Buccaneer.BARRAGE) {
|
||||
if(j > 3)
|
||||
hitDmgMax *= Math.pow(2, (j - 3));
|
||||
}
|
||||
if(shadowPartner) {
|
||||
// For shadow partner, the second half of the hits only do 50% damage. So calc that
|
||||
// in for the crit effects.
|
||||
if(j >= ret.numDamage / 2) {
|
||||
hitDmgMax *= 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
if(ret.skill == Marksman.SNIPE) {
|
||||
damage = 195000 + Randomizer.nextInt(5000);
|
||||
hitDmgMax = 200000;
|
||||
}
|
||||
|
||||
int maxWithCrit = hitDmgMax;
|
||||
if(canCrit) // They can crit, so up the max.
|
||||
maxWithCrit *= 2;
|
||||
|
||||
// Warn if the damage is over 1.5x what we calculated above.
|
||||
if(damage > maxWithCrit * 1.5) {
|
||||
AutobanFactory.DAMAGE_HACK.alert(chr, "DMG: " + damage + " MaxDMG: " + maxWithCrit + " SID: " + ret.skill + " MobID: " + (monster != null ? monster.getId() : "null") + " Map: " + chr.getMap().getMapName() + " (" + chr.getMapId() + ")");
|
||||
}
|
||||
|
||||
// Add a ab point if its over 5x what we calculated.
|
||||
if(damage > maxWithCrit * 5) {
|
||||
AutobanFactory.DAMAGE_HACK.addPoint(chr.getAutobanManager(), "DMG: " + damage + " MaxDMG: " + maxWithCrit + " SID: " + ret.skill + " MobID: " + (monster != null ? monster.getId() : "null") + " Map: " + chr.getMap().getMapName() + " (" + chr.getMapId() + ")");
|
||||
}
|
||||
|
||||
if (ret.skill == Marksman.SNIPE || (canCrit && damage > hitDmgMax)) {
|
||||
// If the skill is a crit, inverse the damage to make it show up on clients.
|
||||
damage = -Integer.MAX_VALUE + damage - 1;
|
||||
}
|
||||
|
||||
allDamageNumbers.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) {
|
||||
lea.skip(4);
|
||||
|
||||
@@ -27,6 +27,7 @@ import client.MapleJob;
|
||||
import client.MapleStat;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import constants.ServerConstants;
|
||||
import constants.skills.BlazeWizard;
|
||||
import constants.skills.Brawler;
|
||||
import constants.skills.DawnWarrior;
|
||||
@@ -34,24 +35,26 @@ import constants.skills.Magician;
|
||||
import constants.skills.Warrior;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Randomizer;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
public final class DistributeAPHandler extends AbstractMaplePacketHandler {
|
||||
private static final int max = 32767;
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
slea.readInt();
|
||||
int num = slea.readInt();
|
||||
if (c.getPlayer().getRemainingAp() > 0) {
|
||||
if (addStat(c, num)) {
|
||||
if (addStat(c, num, false)) {
|
||||
c.getPlayer().setRemainingAp(c.getPlayer().getRemainingAp() - 1);
|
||||
c.getPlayer().updateSingleStat(MapleStat.AVAILABLEAP, c.getPlayer().getRemainingAp());
|
||||
}
|
||||
}
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
|
||||
static boolean addStat(MapleClient c, int apTo) {
|
||||
|
||||
public static boolean addStat(MapleClient c, int apTo, boolean usedAPReset) {
|
||||
switch (apTo) {
|
||||
case 64: // Str
|
||||
if (c.getPlayer().getStr() >= max) {
|
||||
@@ -90,74 +93,202 @@ public final class DistributeAPHandler extends AbstractMaplePacketHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int addHP(MapleClient c) {
|
||||
private static int addHP(MapleClient c) {
|
||||
MapleCharacter player = c.getPlayer();
|
||||
MapleJob job = player.getJob();
|
||||
int MaxHP = player.getMaxHp();
|
||||
if (player.getHpMpApUsed() > 9999 || MaxHP >= 30000) {
|
||||
return MaxHP;
|
||||
}
|
||||
if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
|
||||
|
||||
return MaxHP + calcHpChange(player, job, false);
|
||||
}
|
||||
|
||||
public static int calcHpChange(MapleCharacter player, MapleJob job, boolean usedAPReset) {
|
||||
int MaxHP = 0;
|
||||
|
||||
if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1)) {
|
||||
Skill increaseHP = SkillFactory.getSkill(job.isA(MapleJob.DAWNWARRIOR1) ? DawnWarrior.MAX_HP_INCREASE : Warrior.IMPROVED_MAXHP);
|
||||
int sLvl = player.getSkillLevel(increaseHP);
|
||||
|
||||
|
||||
if(sLvl > 0)
|
||||
MaxHP += increaseHP.getEffect(sLvl).getY();
|
||||
|
||||
MaxHP += 20;
|
||||
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if (usedAPReset) {
|
||||
MaxHP += 18;
|
||||
} else {
|
||||
MaxHP += Randomizer.rand(18, 22);
|
||||
}
|
||||
} else {
|
||||
MaxHP += 20;
|
||||
}
|
||||
} else if(job.isA(MapleJob.ARAN1)) {
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if (usedAPReset) {
|
||||
MaxHP += 26;
|
||||
} else {
|
||||
MaxHP += Randomizer.rand(26, 30);
|
||||
}
|
||||
} else {
|
||||
MaxHP += 28;
|
||||
}
|
||||
} else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
|
||||
MaxHP += 6;
|
||||
} else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1) || job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
|
||||
MaxHP += 16;
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if (usedAPReset) {
|
||||
MaxHP += 5;
|
||||
} else {
|
||||
MaxHP += Randomizer.rand(5, 9);
|
||||
}
|
||||
} else {
|
||||
MaxHP += 6;
|
||||
}
|
||||
} else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if (usedAPReset) {
|
||||
MaxHP += 14;
|
||||
} else {
|
||||
MaxHP += Randomizer.rand(14, 18);
|
||||
}
|
||||
} else {
|
||||
MaxHP += 16;
|
||||
}
|
||||
} else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if (usedAPReset) {
|
||||
MaxHP += 14;
|
||||
} else {
|
||||
MaxHP += Randomizer.rand(14, 18);
|
||||
}
|
||||
} else {
|
||||
MaxHP += 16;
|
||||
}
|
||||
} else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
|
||||
Skill increaseHP = SkillFactory.getSkill(Brawler.IMPROVE_MAX_HP);
|
||||
int sLvl = player.getSkillLevel(increaseHP);
|
||||
|
||||
|
||||
if(sLvl > 0)
|
||||
MaxHP += increaseHP.getEffect(sLvl).getY();
|
||||
|
||||
MaxHP += 18;
|
||||
} else {
|
||||
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if (usedAPReset) {
|
||||
MaxHP += 16;
|
||||
} else {
|
||||
MaxHP += Randomizer.rand(16, 20);
|
||||
}
|
||||
} else {
|
||||
MaxHP += 18;
|
||||
}
|
||||
} else if (usedAPReset) {
|
||||
MaxHP += 8;
|
||||
} else {
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
MaxHP += Randomizer.rand(8, 12);
|
||||
} else {
|
||||
MaxHP += 10;
|
||||
}
|
||||
}
|
||||
return MaxHP;
|
||||
}
|
||||
|
||||
static int addMP(MapleClient c) {
|
||||
private static int addMP(MapleClient c) {
|
||||
MapleCharacter player = c.getPlayer();
|
||||
int MaxMP = player.getMaxMp();
|
||||
MapleJob job = player.getJob();
|
||||
if (player.getHpMpApUsed() > 9999 || player.getMaxMp() >= 30000) {
|
||||
return MaxMP;
|
||||
}
|
||||
|
||||
return MaxMP + calcMpChange(player, job, false);
|
||||
}
|
||||
|
||||
public static int calcMpChange(MapleCharacter player, MapleJob job, boolean usedAPReset) {
|
||||
int MaxMP = 0;
|
||||
|
||||
if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
|
||||
MaxMP += 2;
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if(!usedAPReset) {
|
||||
MaxMP += (Randomizer.rand(2, 4) + (player.getInt() / 10));
|
||||
}
|
||||
else {
|
||||
MaxMP += (2 + (player.getInt() / 10));
|
||||
}
|
||||
} else {
|
||||
MaxMP += 3;
|
||||
}
|
||||
} else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
|
||||
Skill increaseMP = SkillFactory.getSkill(job.isA(MapleJob.BLAZEWIZARD1) ? BlazeWizard.INCREASING_MAX_MP : Magician.IMPROVED_MAX_MP_INCREASE);
|
||||
int sLvl = player.getSkillLevel(increaseMP);
|
||||
|
||||
|
||||
if(sLvl > 0)
|
||||
MaxMP += increaseMP.getEffect(sLvl).getY();
|
||||
|
||||
MaxMP += 18;
|
||||
} else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1) || job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
|
||||
MaxMP += 10;
|
||||
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if(!usedAPReset) {
|
||||
MaxMP += (Randomizer.rand(12, 16) + (player.getInt() / 20));
|
||||
}
|
||||
else {
|
||||
MaxMP += (12 + (player.getInt() / 20));
|
||||
}
|
||||
} else {
|
||||
MaxMP += 18;
|
||||
}
|
||||
} else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if(!usedAPReset) {
|
||||
MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10));
|
||||
}
|
||||
else {
|
||||
MaxMP += (6 + (player.getInt() / 10));
|
||||
}
|
||||
} else {
|
||||
MaxMP += 10;
|
||||
}
|
||||
} else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if(!usedAPReset) {
|
||||
MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10));
|
||||
}
|
||||
else {
|
||||
MaxMP += (6 + (player.getInt() / 10));
|
||||
}
|
||||
} else {
|
||||
MaxMP += 10;
|
||||
}
|
||||
} else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
|
||||
MaxMP += 14;
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if(!usedAPReset) {
|
||||
MaxMP += (Randomizer.rand(7, 9) + (player.getInt() / 10));
|
||||
}
|
||||
else {
|
||||
MaxMP += (7 + (player.getInt() / 10));
|
||||
}
|
||||
} else {
|
||||
MaxMP += 14;
|
||||
}
|
||||
} else {
|
||||
MaxMP += 6;
|
||||
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if(!usedAPReset) {
|
||||
MaxMP += (Randomizer.rand(4, 6) + (player.getInt() / 10));
|
||||
}
|
||||
else {
|
||||
MaxMP += (4 + (player.getInt() / 10));
|
||||
}
|
||||
} else {
|
||||
MaxMP += 6;
|
||||
}
|
||||
}
|
||||
return MaxMP;
|
||||
}
|
||||
|
||||
static void addHP(MapleCharacter player, int MaxHP) {
|
||||
private static void addHP(MapleCharacter player, int MaxHP) {
|
||||
MaxHP = Math.min(30000, MaxHP);
|
||||
player.setHpMpApUsed(player.getHpMpApUsed() + 1);
|
||||
player.setMaxHp(MaxHP);
|
||||
player.updateSingleStat(MapleStat.MAXHP, MaxHP);
|
||||
}
|
||||
|
||||
static void addMP(MapleCharacter player, int MaxMP) {
|
||||
private static void addMP(MapleCharacter player, int MaxMP) {
|
||||
MaxMP = Math.min(30000, MaxMP);
|
||||
player.setHpMpApUsed(player.getHpMpApUsed() + 1);
|
||||
player.setMaxMp(MaxMP);
|
||||
|
||||
@@ -39,18 +39,19 @@ public final class DistributeSPHandler extends AbstractMaplePacketHandler {
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
slea.readInt();
|
||||
int skillid = slea.readInt();
|
||||
if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) {
|
||||
c.getSession().write(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) {
|
||||
c.getSession().write(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
MapleCharacter player = c.getPlayer();
|
||||
int remainingSp = player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000));
|
||||
boolean isBeginnerSkill = false;
|
||||
if ((!GameConstants.isPQSkillMap(player.getMapId()) && GameConstants.isPqSkill(skillid)) || (!player.isGM() && GameConstants.isGMSkills(skillid)) || (!GameConstants.isInJobTree(skillid, player.getJob().getId()) && !player.isGM())) {
|
||||
AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp.");
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.\r\n");
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp.");
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.\r\n");
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
if (skillid % 10000000 > 999 && skillid % 10000000 < 1003) {
|
||||
int total = 0;
|
||||
@@ -63,7 +64,7 @@ public final class DistributeSPHandler extends AbstractMaplePacketHandler {
|
||||
Skill skill = SkillFactory.getSkill(skillid);
|
||||
int curLevel = player.getSkillLevel(skill);
|
||||
if ((remainingSp > 0 && curLevel + 1 <= (skill.isFourthJob() ? player.getMasterLevel(skill) : skill.getMaxLevel()))) {
|
||||
if (!isBeginnerSkill) {
|
||||
if (!isBeginnerSkill) {
|
||||
player.setRemainingSp(player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000)) - 1, GameConstants.getSkillBook(skillid/10000));
|
||||
}
|
||||
player.updateSingleStat(MapleStat.AVAILABLESP, player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000)));
|
||||
|
||||
@@ -63,7 +63,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
final MapleCharacter player = c.getPlayer();
|
||||
if (System.currentTimeMillis() - player.getLastUsedCashItem() < 3000) {
|
||||
player.dropMessage(1, "You have used a cash item recently. Wait a moment and try again.");
|
||||
player.dropMessage(1, "You have used a cash item recently. Wait a moment, then try again.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
@@ -83,6 +83,11 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
return;
|
||||
}
|
||||
if (itemType == 505) { // AP/SP reset
|
||||
if(!player.isAlive()) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemId > 5050000) {
|
||||
int SPTo = slea.readInt();
|
||||
int SPFrom = slea.readInt();
|
||||
@@ -101,80 +106,76 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
switch (APFrom) {
|
||||
case 64: // str
|
||||
if (player.getStr() < 5) {
|
||||
c.getPlayer().message("You don't have the minimum STR required to swap.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
player.addStat(1, -1);
|
||||
break;
|
||||
case 128: // dex
|
||||
if (player.getDex() < 5) {
|
||||
c.getPlayer().message("You don't have the minimum DEX required to swap.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
player.addStat(2, -1);
|
||||
break;
|
||||
case 256: // int
|
||||
if (player.getInt() < 5) {
|
||||
c.getPlayer().message("You don't have the minimum INT required to swap.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
player.addStat(3, -1);
|
||||
break;
|
||||
case 512: // luk
|
||||
if (player.getLuk() < 5) {
|
||||
c.getPlayer().message("You don't have the minimum LUK required to swap.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
player.addStat(4, -1);
|
||||
break;
|
||||
case 2048: // HP
|
||||
if (APTo != 8192) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
c.getPlayer().message("You can only swap HP ability points to MP.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
int hplose = 0;
|
||||
final int jobid = player.getJob().getId();
|
||||
if (jobid == 0 || jobid == 1000 || jobid == 2000 || jobid >= 1200 && jobid <= 1211) { // Beginner
|
||||
hplose -= 12;
|
||||
} else if (jobid >= 100 && jobid <= 132) { // Warrior
|
||||
Skill improvinghplose = SkillFactory.getSkill(1000001);
|
||||
int improvinghploseLevel = c.getPlayer().getSkillLevel(improvinghplose);
|
||||
hplose -= 24;
|
||||
if (improvinghploseLevel >= 1) {
|
||||
hplose -= improvinghplose.getEffect(improvinghploseLevel).getY();
|
||||
}
|
||||
} else if (jobid >= 200 && jobid <= 232) { // Magician
|
||||
hplose -= 10;
|
||||
} else if (jobid >= 500 && jobid <= 522) { // Pirate
|
||||
Skill improvinghplose = SkillFactory.getSkill(5100000);
|
||||
int improvinghploseLevel = c.getPlayer().getSkillLevel(improvinghplose);
|
||||
hplose -= 22;
|
||||
if (improvinghploseLevel > 0) {
|
||||
hplose -= improvinghplose.getEffect(improvinghploseLevel).getY();
|
||||
}
|
||||
} else if (jobid >= 1100 && jobid <= 1111) { // Soul Master
|
||||
Skill improvinghplose = SkillFactory.getSkill(11000000);
|
||||
int improvinghploseLevel = c.getPlayer().getSkillLevel(improvinghplose);
|
||||
hplose -= 27;
|
||||
if (improvinghploseLevel >= 1) {
|
||||
hplose -= improvinghplose.getEffect(improvinghploseLevel).getY();
|
||||
}
|
||||
} else if ((jobid >= 1300 && jobid <= 1311) || (jobid >= 1400 && jobid <= 1411)) { // Wind Breaker and Night Walker
|
||||
hplose -= 17;
|
||||
} else if (jobid >= 300 && jobid <= 322 || jobid >= 400 && jobid <= 422 || jobid >= 2000 && jobid <= 2112) { // Aran
|
||||
hplose -= 20;
|
||||
} else { // GameMaster
|
||||
hplose -= 20;
|
||||
|
||||
int hp = player.getHp();
|
||||
int level_ = player.getLevel();
|
||||
|
||||
boolean canWash_ = true;
|
||||
if (hp < level_ * 14 + 148) {
|
||||
canWash_ = false;
|
||||
}
|
||||
player.setHp(player.getHp() + hplose);
|
||||
player.setMaxHp(player.getMaxHp() + hplose);
|
||||
statupdate.add(new Pair<>(MapleStat.HP, player.getHp()));
|
||||
statupdate.add(new Pair<>(MapleStat.MAXHP, player.getMaxHp()));
|
||||
|
||||
if (!canWash_) {
|
||||
c.getPlayer().message("You don't have the minimum HP pool required to swap.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
int hplose = -DistributeAPHandler.calcHpChange(player, player.getJob(), true);
|
||||
int nextHp = Math.max(1, player.getHp() + hplose), nextMaxHp = Math.max(50, player.getMaxHp() + hplose);
|
||||
|
||||
player.setHp(nextHp);
|
||||
player.setMaxHp(nextMaxHp);
|
||||
statupdate.add(new Pair<>(MapleStat.HP, nextHp));
|
||||
statupdate.add(new Pair<>(MapleStat.MAXHP, nextMaxHp));
|
||||
|
||||
break;
|
||||
case 8192: // MP
|
||||
if (APTo != 2048) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
c.getPlayer().message("You can only swap MP ability points to HP.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
int mp = player.getMp();
|
||||
int level = player.getLevel();
|
||||
MapleJob job = player.getJob();
|
||||
|
||||
boolean canWash = true;
|
||||
if (job.isA(MapleJob.SPEARMAN) && mp < 4 * level + 156) {
|
||||
canWash = false;
|
||||
@@ -185,30 +186,26 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
} else if (mp < level * 14 + 148) {
|
||||
canWash = false;
|
||||
}
|
||||
if (canWash) {
|
||||
int minmp = 0;
|
||||
if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
|
||||
minmp += 4;
|
||||
} else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
|
||||
minmp += 36;
|
||||
} else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1) || job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
|
||||
minmp += 12;
|
||||
} else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
|
||||
minmp += 16;
|
||||
} else {
|
||||
minmp += 8;
|
||||
}
|
||||
player.setMp(player.getMp() - minmp);
|
||||
player.setMaxMp(player.getMaxMp() - minmp);
|
||||
statupdate.add(new Pair<>(MapleStat.MP, player.getMp()));
|
||||
statupdate.add(new Pair<>(MapleStat.MAXMP, player.getMaxMp()));
|
||||
break;
|
||||
|
||||
if (!canWash) {
|
||||
c.getPlayer().message("You don't have the minimum MP pool required to swap.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
int mplose = -DistributeAPHandler.calcMpChange(player, job, true);
|
||||
int nextMp = Math.max(0, player.getMp() + mplose), nextMaxMp = Math.max(5, player.getMaxMp() + mplose);
|
||||
|
||||
player.setHp(nextMp);
|
||||
player.setMaxHp(nextMaxMp);
|
||||
statupdate.add(new Pair<>(MapleStat.HP, nextMp));
|
||||
statupdate.add(new Pair<>(MapleStat.MAXHP, nextMaxMp));
|
||||
break;
|
||||
default:
|
||||
c.announce(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true, c.getPlayer()));
|
||||
return;
|
||||
}
|
||||
DistributeAPHandler.addStat(c, APTo);
|
||||
DistributeAPHandler.addStat(c, APTo, true);
|
||||
c.announce(MaplePacketCreator.updatePlayerStats(statupdate, true, c.getPlayer()));
|
||||
}
|
||||
remove(c, itemId);
|
||||
|
||||
@@ -249,6 +249,8 @@ public class AbstractPlayerInteraction {
|
||||
}
|
||||
|
||||
public void openNpc(int npcid, String script) {
|
||||
if(c.getCM() != null) return;
|
||||
|
||||
c.removeClickedNPC();
|
||||
NPCScriptManager.getInstance().dispose(c);
|
||||
NPCScriptManager.getInstance().start(c, npcid, script, null);
|
||||
|
||||
@@ -31,7 +31,6 @@ import client.status.MonsterStatus;
|
||||
import client.status.MonsterStatusEffect;
|
||||
import constants.ServerConstants;
|
||||
import constants.skills.FPMage;
|
||||
import constants.skills.Hermit;
|
||||
import constants.skills.ILMage;
|
||||
import constants.skills.NightLord;
|
||||
import constants.skills.NightWalker;
|
||||
@@ -51,6 +50,7 @@ import java.util.Map.Entry;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import net.server.world.MapleParty;
|
||||
import net.server.world.MaplePartyCharacter;
|
||||
@@ -66,12 +66,14 @@ import tools.Randomizer;
|
||||
public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
private ChangeableStats ostats = null; //unused, v83 WZs offers no support for changeable stats.
|
||||
private MapleMonsterStats stats;
|
||||
private int hp, mp;
|
||||
private AtomicInteger hp = new AtomicInteger(1);
|
||||
private AtomicLong maxHpPlusHeal = new AtomicLong(1);
|
||||
private int mp;
|
||||
private WeakReference<MapleCharacter> controller = new WeakReference<>(null);
|
||||
private boolean controllerHasAggro, controllerKnowsAboutAggro;
|
||||
private Collection<MonsterListener> listeners = new LinkedList<>();
|
||||
private EnumMap<MonsterStatus, MonsterStatusEffect> stati = new EnumMap<>(MonsterStatus.class);
|
||||
private ArrayList<MonsterStatus> alreadyBuffed = new ArrayList<MonsterStatus>();
|
||||
private ArrayList<MonsterStatus> alreadyBuffed = new ArrayList<>();
|
||||
private MapleMap map;
|
||||
private int VenomMultiplier = 0;
|
||||
private boolean fake = false;
|
||||
@@ -82,7 +84,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
private int team;
|
||||
private final HashMap<Integer, AtomicInteger> takenDamage = new HashMap<>();
|
||||
|
||||
private ReentrantLock externalLock = new ReentrantLock();
|
||||
private ReentrantLock monsterLock = new ReentrantLock();
|
||||
private ReentrantLock statiLock = new ReentrantLock();
|
||||
|
||||
public MapleMonster(int id, MapleMonsterStats stats) {
|
||||
super(id);
|
||||
@@ -95,18 +99,20 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
public void lockMonster() {
|
||||
monsterLock.lock();
|
||||
externalLock.lock();
|
||||
}
|
||||
|
||||
public void unlockMonster() {
|
||||
monsterLock.unlock();
|
||||
externalLock.unlock();
|
||||
}
|
||||
|
||||
private void initWithStats(MapleMonsterStats stats) {
|
||||
setStance(5);
|
||||
this.stats = stats;
|
||||
hp = stats.getHp();
|
||||
hp.set(stats.getHp());
|
||||
mp = stats.getMp();
|
||||
|
||||
maxHpPlusHeal.set(hp.get());
|
||||
}
|
||||
|
||||
public void disableDrops() {
|
||||
@@ -122,11 +128,11 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
public int getHp() {
|
||||
return hp;
|
||||
return hp.get();
|
||||
}
|
||||
|
||||
public void setHp(int hp) {
|
||||
this.hp = hp;
|
||||
this.hp.set(hp);
|
||||
}
|
||||
|
||||
public int getMaxHp() {
|
||||
@@ -209,12 +215,13 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
if (!isAlive()) {
|
||||
return;
|
||||
}
|
||||
int trueDamage = Math.min(hp, damage); // since magic happens otherwise B^)
|
||||
int curHp = hp.get();
|
||||
int trueDamage = Math.min(curHp, damage); // since magic happens otherwise B^)
|
||||
|
||||
if(ServerConstants.USE_DEBUG == true) from.dropMessage(5, "Hitted MOB " + this.getId() + ", OID " + this.getObjectId());
|
||||
dispatchMonsterDamaged(from, trueDamage);
|
||||
|
||||
hp -= trueDamage;
|
||||
hp.set(curHp - trueDamage);
|
||||
if (!takenDamage.containsKey(from.getId())) {
|
||||
takenDamage.put(from.getId(), new AtomicInteger(trueDamage));
|
||||
} else {
|
||||
@@ -225,7 +232,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
from.setPlayerAggro(this.hashCode());
|
||||
from.getMap().broadcastBossHpMessage(this, this.hashCode(), makeBossHPBarPacket(), getPosition());
|
||||
} else if (!isBoss()) {
|
||||
int remainingHP = (int) Math.max(1, hp * 100f / getMaxHp());
|
||||
int remainingHP = (int) Math.max(1, hp.get() * 100f / getMaxHp());
|
||||
byte[] packet = MaplePacketCreator.showMonsterHP(getObjectId(), remainingHP);
|
||||
if (from.getParty() != null) {
|
||||
for (MaplePartyCharacter mpc : from.getParty().getMembers()) {
|
||||
@@ -241,17 +248,24 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
public void heal(int hp, int mp) {
|
||||
int hpHealed = hp;
|
||||
int hp2Heal = getHp() + hp;
|
||||
int mp2Heal = getMp() + mp;
|
||||
if (hp2Heal >= getMaxHp()) {
|
||||
hp2Heal = getMaxHp();
|
||||
|
||||
int maxHp = getMaxHp();
|
||||
int maxMp = getMaxMp();
|
||||
if (hp2Heal >= maxHp) {
|
||||
hpHealed = hp2Heal - maxHp;
|
||||
hp2Heal = maxHp;
|
||||
}
|
||||
if (mp2Heal >= getMaxMp()) {
|
||||
mp2Heal = getMaxMp();
|
||||
if (mp2Heal >= maxMp) {
|
||||
mp2Heal = maxMp;
|
||||
}
|
||||
setHp(hp2Heal);
|
||||
setMp(mp2Heal);
|
||||
getMap().broadcastMessage(MaplePacketCreator.healMonster(getObjectId(), hp));
|
||||
|
||||
maxHpPlusHeal.addAndGet(hpHealed);
|
||||
}
|
||||
|
||||
public boolean isAttackedBy(MapleCharacter chr) {
|
||||
@@ -273,7 +287,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
int partyLevel = 0;
|
||||
int leechMinLevel = (ServerConstants.USE_UNDERLEVELED_EXP_BLOCK) ? getLevel() - 20 : 0; //NO EXP WILL BE GIVEN for those who are underleveled!
|
||||
int leechMinLevel = (ServerConstants.USE_UNDERLEVELED_EXP_BLOCK) ? getLevel() - ServerConstants.MIN_UNDERLEVEL_FOR_EXP_GAIN : 0; //NO EXP WILL BE GIVEN for those who are underleveled!
|
||||
|
||||
int leechCount = 0;
|
||||
for (MapleCharacter mc : members) {
|
||||
@@ -307,15 +321,15 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
return;
|
||||
}
|
||||
int exp = getExp();
|
||||
int totalHealth = getMaxHp();
|
||||
long totalHealth = maxHpPlusHeal.get();
|
||||
Map<Integer, Integer> expDist = new HashMap<>();
|
||||
Map<Integer, Integer> partyExp = new HashMap<>();
|
||||
|
||||
float exp8 = (0.8f * exp); // 80% of pool is split amongst all the damagers
|
||||
float exp2 = (0.2f * exp); // 20% of pool goes to the killer or his/her party
|
||||
float exp8perHp = (0.8f * exp) / totalHealth; // 80% of pool is split amongst all the damagers
|
||||
float exp2 = (0.2f * exp); // 20% of pool goes to the killer or his/her party
|
||||
|
||||
for (Entry<Integer, AtomicInteger> damage : takenDamage.entrySet()) {
|
||||
expDist.put(damage.getKey(), (int) (Math.min((exp8 * damage.getValue().get()) / totalHealth, Integer.MAX_VALUE)));
|
||||
expDist.put(damage.getKey(), (int) (Math.min((exp8perHp * damage.getValue().get()), Integer.MAX_VALUE)));
|
||||
}
|
||||
|
||||
Collection<MapleCharacter> chrs = map.getCharacters();
|
||||
@@ -333,7 +347,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
long pXP = (long)xp + (partyExp.containsKey(pID) ? partyExp.get(pID) : 0);
|
||||
partyExp.put(pID, (int)Math.min(pXP, Integer.MAX_VALUE));
|
||||
} else {
|
||||
if(!ServerConstants.USE_UNDERLEVELED_EXP_BLOCK || mc.getLevel() >= getLevel() - 20) {
|
||||
if(!ServerConstants.USE_UNDERLEVELED_EXP_BLOCK || mc.getLevel() >= getLevel() - ServerConstants.MIN_UNDERLEVEL_FOR_EXP_GAIN) {
|
||||
//NO EXP WILL BE GIVEN for those who are underleveled!
|
||||
giveExpToCharacter(mc, xp, isKiller, 1);
|
||||
} else {
|
||||
@@ -379,8 +393,14 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
personalExp *= 1.0 + (holySymbol.doubleValue() / 100.0);
|
||||
}
|
||||
}
|
||||
if (stati.containsKey(MonsterStatus.SHOWDOWN)) {
|
||||
personalExp *= (stati.get(MonsterStatus.SHOWDOWN).getStati().get(MonsterStatus.SHOWDOWN).doubleValue() / 100.0 + 1.0);
|
||||
|
||||
statiLock.lock();
|
||||
try {
|
||||
if (stati.containsKey(MonsterStatus.SHOWDOWN)) {
|
||||
personalExp *= (stati.get(MonsterStatus.SHOWDOWN).getStati().get(MonsterStatus.SHOWDOWN).doubleValue() / 100.0 + 1.0);
|
||||
}
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,9 +413,10 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
public MapleCharacter killBy(final MapleCharacter killer) {
|
||||
distributeExperience(killer != null ? killer.getId() : 0);
|
||||
|
||||
if (getController() != null) { // this can/should only happen when a hidden gm attacks the monster
|
||||
getController().getClient().announce(MaplePacketCreator.stopControllingMonster(this.getObjectId()));
|
||||
getController().stopControllingMonster(this);
|
||||
MapleCharacter controller = getController();
|
||||
if (controller != null) { // this can/should only happen when a hidden gm attacks the monster
|
||||
controller.getClient().announce(MaplePacketCreator.stopControllingMonster(this.getObjectId()));
|
||||
controller.stopControllingMonster(this);
|
||||
}
|
||||
|
||||
final List<Integer> toSpawn = this.getRevives(); // this doesn't work (?)
|
||||
@@ -427,24 +448,26 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (Integer mid : toSpawn) {
|
||||
final MapleMonster mob = MapleLifeFactory.getMonster(mid);
|
||||
mob.setPosition(getPosition());
|
||||
if (dropsDisabled()) {
|
||||
mob.disableDrops();
|
||||
}
|
||||
reviveMap.spawnMonster(mob);
|
||||
|
||||
if(mob.getId() >= 8810010 && mob.getId() <= 8810017 && reviveMap.isHorntailDefeated()) {
|
||||
for(int i = 8810018; i >= 8810010; i--)
|
||||
reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true);
|
||||
if(toSpawn.size() > 0) {
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (Integer mid : toSpawn) {
|
||||
final MapleMonster mob = MapleLifeFactory.getMonster(mid);
|
||||
mob.setPosition(getPosition());
|
||||
if (dropsDisabled()) {
|
||||
mob.disableDrops();
|
||||
}
|
||||
reviveMap.spawnMonster(mob);
|
||||
|
||||
if(mob.getId() >= 8810010 && mob.getId() <= 8810017 && reviveMap.isHorntailDefeated()) {
|
||||
for(int i = 8810018; i >= 8810010; i--)
|
||||
reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, getAnimationTime("die1"));
|
||||
}, getAnimationTime("die1"));
|
||||
}
|
||||
}
|
||||
else { // is this even necessary?
|
||||
System.out.println("[CRITICAL LOSS] toSpawn is null for " + this.getName());
|
||||
@@ -468,7 +491,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
public void dispatchMonsterDamaged(MapleCharacter from, int trueDmg) {
|
||||
private void dispatchMonsterDamaged(MapleCharacter from, int trueDmg) {
|
||||
for (MonsterListener listener : listeners.toArray(new MonsterListener[listeners.size()])) {
|
||||
listener.monsterDamaged(from, trueDmg);
|
||||
}
|
||||
@@ -488,15 +511,25 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
public boolean isAlive() {
|
||||
return this.hp > 0;
|
||||
return this.hp.get() > 0;
|
||||
}
|
||||
|
||||
public MapleCharacter getController() {
|
||||
return controller.get();
|
||||
monsterLock.lock();
|
||||
try {
|
||||
return controller.get();
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void setController(MapleCharacter controller) {
|
||||
this.controller = new WeakReference<>(controller);
|
||||
monsterLock.lock();
|
||||
try {
|
||||
this.controller = new WeakReference<>(controller);
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void switchController(MapleCharacter newController, boolean immediateAggro) {
|
||||
@@ -521,25 +554,45 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
public boolean isControllerHasAggro() {
|
||||
return fake ? false : controllerHasAggro;
|
||||
monsterLock.lock();
|
||||
try {
|
||||
return fake ? false : controllerHasAggro;
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void setControllerHasAggro(boolean controllerHasAggro) {
|
||||
if (fake) {
|
||||
return;
|
||||
monsterLock.lock();
|
||||
try {
|
||||
if (fake) {
|
||||
return;
|
||||
}
|
||||
this.controllerHasAggro = controllerHasAggro;
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
this.controllerHasAggro = controllerHasAggro;
|
||||
}
|
||||
|
||||
public boolean isControllerKnowsAboutAggro() {
|
||||
return fake ? false : controllerKnowsAboutAggro;
|
||||
monsterLock.lock();
|
||||
try {
|
||||
return fake ? false : controllerKnowsAboutAggro;
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void setControllerKnowsAboutAggro(boolean controllerKnowsAboutAggro) {
|
||||
if (fake) {
|
||||
return;
|
||||
monsterLock.lock();
|
||||
try {
|
||||
if (fake) {
|
||||
return;
|
||||
}
|
||||
this.controllerKnowsAboutAggro = controllerKnowsAboutAggro;
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
this.controllerKnowsAboutAggro = controllerKnowsAboutAggro;
|
||||
}
|
||||
|
||||
public byte[] makeBossHPBarPacket() {
|
||||
@@ -560,11 +613,17 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.spawnMonster(this, false));
|
||||
}
|
||||
if (stati.size() > 0) {
|
||||
for (final MonsterStatusEffect mse : this.stati.values()) {
|
||||
c.announce(MaplePacketCreator.applyMonsterStatus(getObjectId(), mse, null));
|
||||
statiLock.lock();
|
||||
try {
|
||||
if (stati.size() > 0) {
|
||||
for (final MonsterStatusEffect mse : this.stati.values()) {
|
||||
c.announce(MaplePacketCreator.applyMonsterStatus(getObjectId(), mse, null));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
|
||||
if (hasBossHPBar()) {
|
||||
if (this.getMap().countMonster(8810026) > 0 && this.getMap().getId() == 240060200) {
|
||||
this.getMap().killAllMonsters();
|
||||
@@ -588,19 +647,52 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
return stats.isMobile();
|
||||
}
|
||||
|
||||
public ElementalEffectiveness getEffectiveness(Element e) {
|
||||
if (stati.size() > 0 && stati.get(MonsterStatus.DOOM) != null) {
|
||||
return ElementalEffectiveness.NORMAL; // like blue snails
|
||||
public ElementalEffectiveness getElementalEffectiveness(Element e) {
|
||||
statiLock.lock();
|
||||
try {
|
||||
if (stati.get(MonsterStatus.DOOM) != null) {
|
||||
return ElementalEffectiveness.NORMAL; // like blue snails
|
||||
}
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
|
||||
monsterLock.lock();
|
||||
try {
|
||||
return stats.getEffectiveness(e);
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private ElementalEffectiveness getMonsterEffectiveness(Element e) {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
return stats.getEffectiveness(e);
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
return stats.getEffectiveness(e);
|
||||
}
|
||||
|
||||
private int broadcastStatusEffect(final MonsterStatusEffect status) {
|
||||
int animationTime = status.getSkill().getAnimationTime();
|
||||
byte[] packet = MaplePacketCreator.applyMonsterStatus(getObjectId(), status, null);
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
|
||||
MapleCharacter controller = getController();
|
||||
if (controller != null && !controller.isMapObjectVisible(this)) {
|
||||
controller.getClient().announce(packet);
|
||||
}
|
||||
|
||||
return animationTime;
|
||||
}
|
||||
|
||||
public boolean applyStatus(MapleCharacter from, final MonsterStatusEffect status, boolean poison, long duration) {
|
||||
return applyStatus(from, status, poison, duration, false);
|
||||
}
|
||||
|
||||
public boolean applyStatus(MapleCharacter from, final MonsterStatusEffect status, boolean poison, long duration, boolean venom) {
|
||||
switch (stats.getEffectiveness(status.getSkill().getElement())) {
|
||||
switch (getMonsterEffectiveness(status.getSkill().getElement())) {
|
||||
case IMMUNE:
|
||||
case STRONG:
|
||||
case NEUTRAL:
|
||||
@@ -609,27 +701,27 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
case WEAK:
|
||||
break;
|
||||
default: {
|
||||
System.out.println("Unknown elemental effectiveness: " + stats.getEffectiveness(status.getSkill().getElement()));
|
||||
System.out.println("Unknown elemental effectiveness: " + getMonsterEffectiveness(status.getSkill().getElement()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (status.getSkill().getId() == FPMage.ELEMENT_COMPOSITION) { // fp compo
|
||||
ElementalEffectiveness effectiveness = stats.getEffectiveness(Element.POISON);
|
||||
ElementalEffectiveness effectiveness = getMonsterEffectiveness(Element.POISON);
|
||||
if (effectiveness == ElementalEffectiveness.IMMUNE || effectiveness == ElementalEffectiveness.STRONG) {
|
||||
return false;
|
||||
}
|
||||
} else if (status.getSkill().getId() == ILMage.ELEMENT_COMPOSITION) { // il compo
|
||||
ElementalEffectiveness effectiveness = stats.getEffectiveness(Element.ICE);
|
||||
ElementalEffectiveness effectiveness = getMonsterEffectiveness(Element.ICE);
|
||||
if (effectiveness == ElementalEffectiveness.IMMUNE || effectiveness == ElementalEffectiveness.STRONG) {
|
||||
return false;
|
||||
}
|
||||
} else if (status.getSkill().getId() == NightLord.VENOMOUS_STAR || status.getSkill().getId() == Shadower.VENOMOUS_STAB || status.getSkill().getId() == NightWalker.VENOM) {// venom
|
||||
if (stats.getEffectiveness(Element.POISON) == ElementalEffectiveness.WEAK) {
|
||||
if (getMonsterEffectiveness(Element.POISON) == ElementalEffectiveness.WEAK) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (poison && getHp() <= 1) {
|
||||
if (poison && hp.get() <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -642,17 +734,24 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
for (MonsterStatus stat : statis.keySet()) {
|
||||
final MonsterStatusEffect oldEffect = stati.get(stat);
|
||||
if (oldEffect != null) {
|
||||
oldEffect.removeActiveStatus(stat);
|
||||
if (oldEffect.getStati().isEmpty()) {
|
||||
oldEffect.cancelTask();
|
||||
oldEffect.cancelDamageSchedule();
|
||||
if(statis.size() > 0) {
|
||||
statiLock.lock();
|
||||
try {
|
||||
for (MonsterStatus stat : statis.keySet()) {
|
||||
final MonsterStatusEffect oldEffect = stati.get(stat);
|
||||
if (oldEffect != null) {
|
||||
oldEffect.removeActiveStatus(stat);
|
||||
if (oldEffect.getStati().isEmpty()) {
|
||||
oldEffect.cancelTask();
|
||||
oldEffect.cancelDamageSchedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TimerManager timerManager = TimerManager.getInstance();
|
||||
final Runnable cancelTask = new Runnable() {
|
||||
|
||||
@@ -661,26 +760,38 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
if (isAlive()) {
|
||||
byte[] packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), status.getStati());
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
if (getController() != null && !getController().isMapObjectVisible(MapleMonster.this)) {
|
||||
getController().getClient().announce(packet);
|
||||
|
||||
MapleCharacter controller = getController();
|
||||
if (controller != null && !controller.isMapObjectVisible(MapleMonster.this)) {
|
||||
controller.getClient().announce(packet);
|
||||
}
|
||||
}
|
||||
for (MonsterStatus stat : status.getStati().keySet()) {
|
||||
stati.remove(stat);
|
||||
|
||||
statiLock.lock();
|
||||
try {
|
||||
for (MonsterStatus stat : status.getStati().keySet()) {
|
||||
stati.remove(stat);
|
||||
}
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
|
||||
setVenomMulti(0);
|
||||
status.cancelDamageSchedule();
|
||||
}
|
||||
};
|
||||
|
||||
int animationTime;
|
||||
if (poison) {
|
||||
int poisonLevel = from.getSkillLevel(status.getSkill());
|
||||
int poisonDamage = Math.min(Short.MAX_VALUE, (int) (getMaxHp() / (70.0 - poisonLevel) + 0.999));
|
||||
status.setValue(MonsterStatus.POISON, Integer.valueOf(poisonDamage));
|
||||
animationTime = broadcastStatusEffect(status);
|
||||
status.setDamageSchedule(timerManager.register(new DamageTask(poisonDamage, from, status, cancelTask, 0), 1000, 1000));
|
||||
} else if (venom) {
|
||||
if (from.getJob() == MapleJob.NIGHTLORD || from.getJob() == MapleJob.SHADOWER || from.getJob().isA(MapleJob.NIGHTWALKER3)) {
|
||||
int poisonLevel, matk, id = from.getJob().getId();
|
||||
int skill = (id == 412 ? NightLord.VENOMOUS_STAR : (id == 422 ? Shadower.VENOMOUS_STAB : NightWalker.VENOM));
|
||||
int poisonLevel, matk, jobid = from.getJob().getId();
|
||||
int skill = (jobid == 412 ? NightLord.VENOMOUS_STAR : (jobid == 422 ? Shadower.VENOMOUS_STAB : NightWalker.VENOM));
|
||||
poisonLevel = from.getSkillLevel(SkillFactory.getSkill(skill));
|
||||
if (poisonLevel <= 0) {
|
||||
return false;
|
||||
@@ -699,13 +810,19 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
poisonDamage = Math.min(Short.MAX_VALUE, poisonDamage);
|
||||
status.setValue(MonsterStatus.VENOMOUS_WEAPON, Integer.valueOf(poisonDamage));
|
||||
status.setValue(MonsterStatus.POISON, Integer.valueOf(poisonDamage));
|
||||
animationTime = broadcastStatusEffect(status);
|
||||
status.setDamageSchedule(timerManager.register(new DamageTask(poisonDamage, from, status, cancelTask, 0), 1000, 1000));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
} else if (status.getSkill().getId() == Hermit.SHADOW_WEB || status.getSkill().getId() == NightWalker.SHADOW_WEB) { //Shadow Web
|
||||
status.setDamageSchedule(timerManager.schedule(new DamageTask((int) (getMaxHp() / 50.0 + 0.999), from, status, cancelTask, 1), 3500));
|
||||
int webDamage = (int) (getMaxHp() / 50.0 + 0.999);
|
||||
status.setValue(MonsterStatus.SHADOW_WEB, Integer.valueOf(webDamage));
|
||||
animationTime = broadcastStatusEffect(status);
|
||||
status.setDamageSchedule(timerManager.schedule(new DamageTask(webDamage, from, status, cancelTask, 1), 3500));
|
||||
*/
|
||||
} else if (status.getSkill().getId() == 4121004 || status.getSkill().getId() == 4221004) { // Ninja Ambush
|
||||
final Skill skill = SkillFactory.getSkill(status.getSkill().getId());
|
||||
final byte level = from.getSkillLevel(skill);
|
||||
@@ -715,19 +832,23 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}*/
|
||||
|
||||
status.setValue(MonsterStatus.NINJA_AMBUSH, Integer.valueOf(damage));
|
||||
animationTime = broadcastStatusEffect(status);
|
||||
status.setDamageSchedule(timerManager.register(new DamageTask(damage, from, status, cancelTask, 2), 1000, 1000));
|
||||
} else {
|
||||
animationTime = broadcastStatusEffect(status);
|
||||
}
|
||||
for (MonsterStatus stat : status.getStati().keySet()) {
|
||||
stati.put(stat, status);
|
||||
alreadyBuffed.add(stat);
|
||||
|
||||
statiLock.lock();
|
||||
try {
|
||||
for (MonsterStatus stat : status.getStati().keySet()) {
|
||||
stati.put(stat, status);
|
||||
alreadyBuffed.add(stat);
|
||||
}
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
int animationTime = status.getSkill().getAnimationTime();
|
||||
byte[] packet = MaplePacketCreator.applyMonsterStatus(getObjectId(), status, null);
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
if (getController() != null && !getController().isMapObjectVisible(this)) {
|
||||
getController().getClient().announce(packet);
|
||||
}
|
||||
status.setCancelTask(timerManager.schedule(cancelTask, duration + animationTime));
|
||||
|
||||
status.setCancelTask(timerManager.schedule(cancelTask, duration + animationTime - 100));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -740,11 +861,19 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
if (isAlive()) {
|
||||
byte[] packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), stats);
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
if (getController() != null && !getController().isMapObjectVisible(MapleMonster.this)) {
|
||||
getController().getClient().announce(packet);
|
||||
|
||||
MapleCharacter controller = getController();
|
||||
if (controller != null && !controller.isMapObjectVisible(MapleMonster.this)) {
|
||||
controller.getClient().announce(packet);
|
||||
}
|
||||
for (final MonsterStatus stat : stats.keySet()) {
|
||||
stati.remove(stat);
|
||||
|
||||
statiLock.lock();
|
||||
try {
|
||||
for (final MonsterStatus stat : stats.keySet()) {
|
||||
stati.remove(stat);
|
||||
}
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -752,12 +881,20 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
final MonsterStatusEffect effect = new MonsterStatusEffect(stats, null, skill, true);
|
||||
byte[] packet = MaplePacketCreator.applyMonsterStatus(getObjectId(), effect, reflection);
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
for (MonsterStatus stat : stats.keySet()) {
|
||||
stati.put(stat, effect);
|
||||
alreadyBuffed.add(stat);
|
||||
|
||||
statiLock.lock();
|
||||
try {
|
||||
for (MonsterStatus stat : stats.keySet()) {
|
||||
stati.put(stat, effect);
|
||||
alreadyBuffed.add(stat);
|
||||
}
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
if (getController() != null && !getController().isMapObjectVisible(this)) {
|
||||
getController().getClient().announce(packet);
|
||||
|
||||
MapleCharacter controller = getController();
|
||||
if (controller != null && !controller.isMapObjectVisible(this)) {
|
||||
controller.getClient().announce(packet);
|
||||
}
|
||||
effect.setCancelTask(timerManager.schedule(cancelTask, duration));
|
||||
}
|
||||
@@ -765,29 +902,51 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
public void debuffMob(int skillid) {
|
||||
//skillid is not going to be used for now until I get warrior debuff working
|
||||
MonsterStatus[] stats = {MonsterStatus.WEAPON_ATTACK_UP, MonsterStatus.WEAPON_DEFENSE_UP, MonsterStatus.MAGIC_ATTACK_UP, MonsterStatus.MAGIC_DEFENSE_UP};
|
||||
for (int i = 0; i < stats.length; i++) {
|
||||
if (isBuffed(stats[i])) {
|
||||
final MonsterStatusEffect oldEffect = stati.get(stats[i]);
|
||||
byte[] packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), oldEffect.getStati());
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
if (getController() != null && !getController().isMapObjectVisible(MapleMonster.this)) {
|
||||
getController().getClient().announce(packet);
|
||||
statiLock.lock();
|
||||
try {
|
||||
for (int i = 0; i < stats.length; i++) {
|
||||
if (isBuffed(stats[i])) {
|
||||
final MonsterStatusEffect oldEffect = stati.get(stats[i]);
|
||||
byte[] packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), oldEffect.getStati());
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
|
||||
MapleCharacter controller = getController();
|
||||
if (controller != null && !controller.isMapObjectVisible(MapleMonster.this)) {
|
||||
controller.getClient().announce(packet);
|
||||
}
|
||||
stati.remove(stats[i]);
|
||||
}
|
||||
stati.remove(stats);
|
||||
}
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBuffed(MonsterStatus status) {
|
||||
return stati.containsKey(status);
|
||||
statiLock.lock();
|
||||
try {
|
||||
return stati.containsKey(status);
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void setFake(boolean fake) {
|
||||
this.fake = fake;
|
||||
monsterLock.lock();
|
||||
try {
|
||||
this.fake = fake;
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFake() {
|
||||
return fake;
|
||||
monsterLock.lock();
|
||||
try {
|
||||
return fake;
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public MapleMap getMap() {
|
||||
@@ -806,18 +965,30 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
if (toUse == null) {
|
||||
return false;
|
||||
}
|
||||
for (Pair<Integer, Integer> skill : usedSkills) {
|
||||
if (skill.getLeft() == toUse.getSkillId() && skill.getRight() == toUse.getSkillLevel()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (toUse.getLimit() > 0) {
|
||||
if (this.skillsUsed.containsKey(new Pair<>(toUse.getSkillId(), toUse.getSkillLevel()))) {
|
||||
int times = this.skillsUsed.get(new Pair<>(toUse.getSkillId(), toUse.getSkillLevel()));
|
||||
if (times >= toUse.getLimit()) {
|
||||
|
||||
monsterLock.lock();
|
||||
try {
|
||||
for (Pair<Integer, Integer> skill : usedSkills) {
|
||||
if (skill.getLeft() == toUse.getSkillId() && skill.getRight() == toUse.getSkillLevel()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
|
||||
if (toUse.getLimit() > 0) {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
if (this.skillsUsed.containsKey(new Pair<>(toUse.getSkillId(), toUse.getSkillLevel()))) {
|
||||
int times = this.skillsUsed.get(new Pair<>(toUse.getSkillId(), toUse.getSkillLevel()));
|
||||
if (times >= toUse.getLimit()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
if (toUse.getSkillId() == 200) {
|
||||
Collection<MapleMapObject> mmo = getMap().getMapObjects();
|
||||
@@ -835,36 +1006,47 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
public void usedSkill(final int skillId, final int level, long cooltime) {
|
||||
this.usedSkills.add(new Pair<>(skillId, level));
|
||||
if (this.skillsUsed.containsKey(new Pair<>(skillId, level))) {
|
||||
int times = this.skillsUsed.get(new Pair<>(skillId, level)) + 1;
|
||||
this.skillsUsed.remove(new Pair<>(skillId, level));
|
||||
this.skillsUsed.put(new Pair<>(skillId, level), times);
|
||||
} else {
|
||||
this.skillsUsed.put(new Pair<>(skillId, level), 1);
|
||||
monsterLock.lock();
|
||||
try {
|
||||
this.usedSkills.add(new Pair<>(skillId, level));
|
||||
if (this.skillsUsed.containsKey(new Pair<>(skillId, level))) {
|
||||
int times = this.skillsUsed.get(new Pair<>(skillId, level)) + 1;
|
||||
this.skillsUsed.remove(new Pair<>(skillId, level));
|
||||
this.skillsUsed.put(new Pair<>(skillId, level), times);
|
||||
} else {
|
||||
this.skillsUsed.put(new Pair<>(skillId, level), 1);
|
||||
}
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
|
||||
final MapleMonster mons = this;
|
||||
TimerManager tMan = TimerManager.getInstance();
|
||||
tMan.schedule(
|
||||
new Runnable() {
|
||||
new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mons.clearSkill(skillId, level);
|
||||
}
|
||||
}, cooltime);
|
||||
@Override
|
||||
public void run() {
|
||||
mons.clearSkill(skillId, level);
|
||||
}
|
||||
}, cooltime);
|
||||
}
|
||||
|
||||
public void clearSkill(int skillId, int level) {
|
||||
int index = -1;
|
||||
for (Pair<Integer, Integer> skill : usedSkills) {
|
||||
if (skill.getLeft() == skillId && skill.getRight() == level) {
|
||||
index = usedSkills.indexOf(skill);
|
||||
break;
|
||||
monsterLock.lock();
|
||||
try {
|
||||
int index = -1;
|
||||
for (Pair<Integer, Integer> skill : usedSkills) {
|
||||
if (skill.getLeft() == skillId && skill.getRight() == level) {
|
||||
index = usedSkills.indexOf(skill);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index != -1) {
|
||||
usedSkills.remove(index);
|
||||
if (index != -1) {
|
||||
usedSkills.remove(index);
|
||||
}
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -900,16 +1082,19 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
int curHp = hp.get();
|
||||
if(curHp <= 0) return;
|
||||
|
||||
int damage = dealDamage;
|
||||
if (damage >= hp) {
|
||||
damage = hp - 1;
|
||||
if (damage >= curHp) {
|
||||
damage = curHp - 1;
|
||||
if (type == 1 || type == 2) {
|
||||
map.broadcastMessage(MaplePacketCreator.damageMonster(getObjectId(), damage), getPosition());
|
||||
cancelTask.run();
|
||||
status.getCancelTask().cancel(false);
|
||||
}
|
||||
}
|
||||
if (hp > 1 && damage > 0) {
|
||||
if (curHp > 1 && damage > 0) {
|
||||
damage(chr, damage);
|
||||
if (type == 1) {
|
||||
map.broadcastMessage(MaplePacketCreator.damageMonster(getObjectId(), damage), getPosition());
|
||||
@@ -931,23 +1116,38 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
public void setTempEffectiveness(Element e, ElementalEffectiveness ee, long milli) {
|
||||
final Element fE = e;
|
||||
final ElementalEffectiveness fEE = stats.getEffectiveness(e);
|
||||
if (!stats.getEffectiveness(e).equals(ElementalEffectiveness.WEAK)) {
|
||||
stats.setEffectiveness(e, ee);
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
final Element fE = e;
|
||||
final ElementalEffectiveness fEE = stats.getEffectiveness(e);
|
||||
if (!fEE.equals(ElementalEffectiveness.WEAK)) {
|
||||
stats.setEffectiveness(e, ee);
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
stats.removeEffectiveness(fE);
|
||||
stats.setEffectiveness(fE, fEE);
|
||||
}
|
||||
}, milli);
|
||||
@Override
|
||||
public void run() {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
stats.removeEffectiveness(fE);
|
||||
stats.setEffectiveness(fE, fEE);
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
}, milli);
|
||||
}
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<MonsterStatus> alreadyBuffedStats() {
|
||||
return Collections.unmodifiableCollection(alreadyBuffed);
|
||||
statiLock.lock();
|
||||
try {
|
||||
return Collections.unmodifiableCollection(alreadyBuffed);
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public BanishInfo getBanish() {
|
||||
@@ -967,7 +1167,21 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
public Map<MonsterStatus, MonsterStatusEffect> getStati() {
|
||||
return stati;
|
||||
statiLock.lock();
|
||||
try {
|
||||
return Collections.unmodifiableMap(stati);
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public MonsterStatusEffect getStati(MonsterStatus ms) {
|
||||
statiLock.lock();
|
||||
try {
|
||||
return stati.get(ms);
|
||||
} finally {
|
||||
statiLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- one can always have fun trying these pieces of codes below in-game rofl ----
|
||||
@@ -985,7 +1199,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
|
||||
public final void setOverrideStats(final OverrideMonsterStats ostats) {
|
||||
this.ostats = new ChangeableStats(stats, ostats);
|
||||
this.hp = ostats.getHp();
|
||||
this.hp.set(ostats.getHp());
|
||||
this.mp = ostats.getMp();
|
||||
}
|
||||
|
||||
@@ -998,7 +1212,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
return;
|
||||
}
|
||||
this.ostats = new ChangeableStats(stats, newLevel, pqMob);
|
||||
this.hp = ostats.getHp();
|
||||
this.hp.set(ostats.getHp());
|
||||
this.mp = ostats.getMp();
|
||||
}
|
||||
|
||||
|
||||
@@ -515,9 +515,9 @@ public class MapleMap {
|
||||
byte d = 1;
|
||||
Point pos = new Point(0, mob.getPosition().y);
|
||||
|
||||
Map<MonsterStatus, MonsterStatusEffect> stati = mob.getStati();
|
||||
if (stati.containsKey(MonsterStatus.SHOWDOWN)) {
|
||||
chRate *= (stati.get(MonsterStatus.SHOWDOWN).getStati().get(MonsterStatus.SHOWDOWN).doubleValue() / 100.0 + 1.0);
|
||||
MonsterStatusEffect stati = mob.getStati(MonsterStatus.SHOWDOWN);
|
||||
if (stati != null) {
|
||||
chRate *= (stati.getStati().get(MonsterStatus.SHOWDOWN).doubleValue() / 100.0 + 1.0);
|
||||
}
|
||||
|
||||
final MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package tools;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
@@ -18,13 +20,51 @@ public class DatabaseConnection {
|
||||
|
||||
public static Connection getConnection() throws SQLException {
|
||||
if(ds != null) {
|
||||
return ds.getConnection();
|
||||
} else {
|
||||
return DriverManager.getConnection(ServerConstants.DB_URL, ServerConstants.DB_USER, ServerConstants.DB_PASS);
|
||||
try {
|
||||
return ds.getConnection();
|
||||
} catch (SQLException sqle) {}
|
||||
}
|
||||
|
||||
int denies = 0;
|
||||
while(true) { // There is no way it can pass with a null out of here
|
||||
try {
|
||||
return DriverManager.getConnection(ServerConstants.DB_URL, ServerConstants.DB_USER, ServerConstants.DB_PASS);
|
||||
} catch (SQLException sqle) {
|
||||
denies++;
|
||||
|
||||
if(denies == 3) {
|
||||
// Give up, return null :3
|
||||
FilePrinter.printError(FilePrinter.SQL_EXCEPTION, "SQL Driver refused to give a connection after " + denies + " tries.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNumberOfAccounts() {
|
||||
try {
|
||||
Connection con = DriverManager.getConnection(ServerConstants.DB_URL, ServerConstants.DB_USER, ServerConstants.DB_PASS);
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT count(*) FROM accounts")) {
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
rs.next();
|
||||
return rs.getInt(1);
|
||||
}
|
||||
} finally {
|
||||
con.close();
|
||||
}
|
||||
} catch(SQLException sqle) {
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
|
||||
public DatabaseConnection() {
|
||||
try {
|
||||
Class.forName("com.mysql.jdbc.Driver"); // touch the mysql driver
|
||||
} catch (ClassNotFoundException e) {
|
||||
System.out.println("[SEVERE] SQL Driver Not Found. Consider death by clams.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
ds = null;
|
||||
|
||||
if(ServerConstants.DB_EXPERIMENTAL_POOL) {
|
||||
@@ -36,22 +76,18 @@ public class DatabaseConnection {
|
||||
config.setUsername(ServerConstants.DB_USER);
|
||||
config.setPassword(ServerConstants.DB_PASS);
|
||||
|
||||
config.addDataSourceProperty("connectionTimeout", "30000");
|
||||
config.addDataSourceProperty("maximumPoolSize", "100");
|
||||
int poolSize = getNumberOfAccounts() * 10; // make sure pool size is comfortable for the worst case scenario
|
||||
if(poolSize < 100) poolSize = 100;
|
||||
else if(poolSize > 10000) poolSize = 10000;
|
||||
|
||||
config.addDataSourceProperty("cachePrepStmts", "true");
|
||||
config.addDataSourceProperty("prepStmtCacheSize", "250");
|
||||
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
|
||||
config.setConnectionTimeout(30 * 1000);
|
||||
config.setMaximumPoolSize(poolSize);
|
||||
|
||||
config.addDataSourceProperty("cachePrepStmts", true);
|
||||
config.addDataSourceProperty("prepStmtCacheSize", 250);
|
||||
config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
|
||||
|
||||
ds = new HikariDataSource(config);
|
||||
} else {
|
||||
try {
|
||||
Class.forName("com.mysql.jdbc.Driver"); // touch the mysql driver
|
||||
} catch (ClassNotFoundException e) {
|
||||
System.out.println("[SEVERE] SQL Driver Not Found. Consider death by clams.");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public class FilePrinter {
|
||||
ERROR38 = "error38.txt",
|
||||
PACKET_LOG = "log.txt",
|
||||
EXCEPTION = "exceptions.txt",
|
||||
SQL_EXCEPTION = "sqlexceptions.txt",
|
||||
PACKET_HANDLER = "PacketHandler/",
|
||||
PORTAL = "portals/",
|
||||
NPC = "npcs/",
|
||||
|
||||
Reference in New Issue
Block a user