Level/exp on all equips + Player stats overhaul + Chair handler patch

Implemented a massive overhaul on Character.wz, adding in equipment level-related nodes in order to make equipments level/EXP info available for anyone to check.
Implemented a major overhaul on the player stat management throughout the source, properly encapsulating and concurrency protecting it's mechanics.
Reviewed several MoveLifeHandler aspects, some of them trying to prevent mobs from falling from footholds in certain circumstances.
Fixed MP Recovery instant killing players when they run out of HP to use.
Fixed some chairs arbitrarily disconnecting players upon HP/MP recovery.
Fixed Puppets sticking to maps in certain scenarios.
Cached data and added concurrency protection for EXP gain on equipments.
Reworked Chair Mastery skill, now with recovering amounts based on a player's base HP/MP pool.
Fixed several deadlock issues revolving rate coupons and character inventory.
Improved overall autopot handler performance.
Reworked door bosses (such as Crocell), now spawning each 3 hours instead of upon player's demand.
Fixed alliances not saving rank names on DB at creation time.
Fixed alliances retaining disbanded guild info on DB.
Added Mystic Door support for the Mushroom Castle area.
This commit is contained in:
ronancpl
2018-09-23 22:37:00 -03:00
parent c3e3c6dfbb
commit 5480035005
255 changed files with 9576 additions and 7850 deletions

View File

@@ -399,7 +399,7 @@ public class Server {
worlds.add(world);
Map<Integer, String> channelInfo = new HashMap<>();
long bootTime = System.currentTimeMillis();
long bootTime = getCurrentTime();
for (int j = 1; j <= Integer.parseInt(p.getProperty("channels" + i)); j++) {
int channelid = j;
Channel channel = new Channel(i, channelid, bootTime);

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -31,6 +31,7 @@ public enum MonitoredLockType {
CHARACTER_PET,
CHARACTER_PRT,
CHARACTER_EVT,
CHARACTER_STA,
CLIENT,
CLIENT_ENCODER,
CLIENT_LOGIN,

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -48,7 +48,7 @@ public class EmptyReadLock implements MonitoredReadLock {
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
s += "----------------------------";
s += "----------------------------\r\n\r\n";
return s;
}

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -48,7 +48,7 @@ public class EmptyReentrantLock implements MonitoredReentrantLock {
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
s += "----------------------------";
s += "----------------------------\r\n\r\n";
return s;
}

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -48,7 +48,7 @@ public class EmptyWriteLock implements MonitoredWriteLock {
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
s += "----------------------------";
s += "----------------------------\r\n\r\n";
return s;
}

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -214,6 +214,8 @@ public final class Channel {
closeChannelSchedules();
players = null;
MapleServerHandler handler = (MapleServerHandler) acceptor.getHandler();
handler.dispose();
acceptor.unbind();
finishedShutdown = true;
@@ -611,7 +613,7 @@ public final class Channel {
}
}, clockTime + 3000); // let the TIMES UP display for 3 seconds, then warp
dojoFinishTime[slot] = System.currentTimeMillis() + clockTime;
dojoFinishTime[slot] = Server.getInstance().getCurrentTime() + clockTime;
}
public void dismissDojoSchedule(int dojoMapId, MapleParty party) {

View File

@@ -298,7 +298,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
}
}
} else if (attack.skill == Marauder.ENERGY_DRAIN || attack.skill == ThunderBreaker.ENERGY_DRAIN || attack.skill == NightWalker.VAMPIRE || attack.skill == Assassin.DRAIN) {
player.addHP(Math.min(monster.getMaxHp(), Math.min((int) ((double) totDamage * (double) SkillFactory.getSkill(attack.skill).getEffect(player.getSkillLevel(SkillFactory.getSkill(attack.skill))).getX() / 100.0), player.getMaxHp() / 2)));
player.addHP(Math.min(monster.getMaxHp(), Math.min((int) ((double) totDamage * (double) SkillFactory.getSkill(attack.skill).getEffect(player.getSkillLevel(SkillFactory.getSkill(attack.skill))).getX() / 100.0), player.getCurrentMaxHp() / 2)));
} else if (attack.skill == Bandit.STEAL) {
Skill steal = SkillFactory.getSkill(Bandit.STEAL);
if (monster.getStolen().size() < 1) { // One steal per mob <3
@@ -399,8 +399,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
Skill skill;
if (player.getBuffedValue(MapleBuffStat.COMBO_DRAIN) != null) {
skill = SkillFactory.getSkill(21100005);
player.setHp(player.getHp() + ((totDamage * skill.getEffect(player.getSkillLevel(skill)).getX()) / 100), true);
player.updateSingleStat(MapleStat.HP, player.getHp());
player.addHP(((totDamage * skill.getEffect(player.getSkillLevel(skill)).getX()) / 100));
}
} else if (job == 412 || job == 422 || job == 1411) {
Skill type = SkillFactory.getSkill(player.getJob().getId() == 412 ? 4120005 : (player.getJob().getId() == 1411 ? 14110004 : 4220005));

View File

@@ -201,7 +201,7 @@ public final class AllianceOperationHandler extends AbstractMaplePacketHandler {
private void changeLeaderAllianceRank(MapleAlliance alliance, MapleCharacter newLeader) {
MapleGuildCharacter lmgc = alliance.getLeader();
MapleCharacter leader = Server.getInstance().getWorld(newLeader.getWorld()).getPlayerStorage().getCharacterById(lmgc.getId());
MapleCharacter leader = newLeader.getWorldServer().getPlayerStorage().getCharacterById(lmgc.getId());
leader.getMGC().setAllianceRank(2);
leader.saveGuildStatus();

View File

@@ -85,18 +85,17 @@ public final class ChangeMapHandler extends AbstractMaplePacketHandler {
executeStandardPath = chr.getEventInstance().revivePlayer(chr);
}
if (executeStandardPath) {
MapleMap to = chr.getMap();
MapleMap map = chr.getMap();
if (wheel && chr.haveItemWithId(5510000, false)) {
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, 5510000, 1, true, false);
chr.announce(MaplePacketCreator.showWheelsLeft(chr.getItemQuantity(5510000, false)));
chr.updateHp(50);
chr.changeMap(map, map.getRandomPlayerSpawnpoint());
} else {
chr.cancelAllBuffs(false);
to = chr.getWarpMap(chr.getMap().getReturnMapId());
chr.setStance(0);
chr.respawn(map.getReturnMapId());
}
chr.setHp(50);
chr.updatePartyMemberHP();
chr.changeMap(to, to.getRandomPlayerSpawnpoint());
}
} else if (targetid != -1) {
if(chr.isGM()) {

View File

@@ -132,14 +132,8 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler {
if (dmgIt.hasNext()) {
totDamageToOneMonster = dmgIt.next().get(0).intValue();
}
int remainingHP = chr.getHp() - totDamageToOneMonster * attack.getAttackEffect(chr, null).getX() / 100;
if (remainingHP > 1) {
chr.setHp(remainingHP);
} else {
chr.setHp(1);
}
chr.updateSingleStat(MapleStat.HP, chr.getHp());
chr.checkBerserk(chr.isHidden());
chr.safeAddHP(-1 * totDamageToOneMonster * attack.getAttackEffect(chr, null).getX() / 100);
}
if (attack.numAttacked > 0 && attack.skill == 1211002) {
boolean advcharge_prob = false;

View File

@@ -32,6 +32,7 @@ import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
public final class DamageSummonHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
int oid = slea.readInt();
slea.skip(1); // -1

View File

@@ -26,6 +26,7 @@ import client.MapleClient;
import client.autoban.AutobanFactory;
import client.autoban.AutobanManager;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import server.maps.MapleMapFactory;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.MaplePacketCreator;
@@ -37,11 +38,12 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler {
if(!chr.isLoggedinWorld()) return;
AutobanManager abm = chr.getAutobanManager();
int timestamp = slea.readInt();
abm.setTimestamp(0, timestamp, 3);
slea.skip(4);
int timestamp = (int) (Server.getInstance().getCurrentTime() - Server.uptime);
slea.skip(8);
short healHP = slea.readShort();
if (healHP != 0) {
abm.setTimestamp(8, timestamp, 3); // thanks Vcoc for pointing out d/c happening here
if ((abm.getLastSpam(0) + 1500) > timestamp) AutobanFactory.FAST_HP_HEALING.addPoint(abm, "Fast hp healing");
int abHeal = 120 + (int)(20 * MapleMapFactory.getMapRecoveryRate(chr.getMapId())); // Sleepywood sauna and showa spa...
@@ -49,14 +51,14 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler {
AutobanFactory.HIGH_HP_HEALING.autoban(chr, "Healing: " + healHP + "; Max is " + abHeal + ".");
return;
}
chr.addHP(healHP);
chr.addHP(healHP);
chr.getMap().broadcastMessage(chr, MaplePacketCreator.showHpHealed(chr.getId(), healHP), false);
chr.checkBerserk(chr.isHidden());
abm.spam(0, timestamp);
}
short healMP = slea.readShort();
if (healMP != 0 && healMP < 1000) {
abm.setTimestamp(9, timestamp, 3);
if ((abm.getLastSpam(1) + 1500) > timestamp) AutobanFactory.FAST_MP_HEALING.addPoint(abm, "Fast mp healing");
chr.addMP(healMP);
abm.spam(1, timestamp);

View File

@@ -24,7 +24,7 @@ package net.server.channel.handlers;
import client.MapleCharacter;
import client.MapleClient;
import java.awt.Point;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import server.life.MapleMonster;
import server.life.MapleMonsterInformationProvider;
@@ -47,27 +47,29 @@ import tools.data.input.SeekableLittleEndianAccessor;
* @author Ronan (HeavenMS)
*/
public final class MoveLifeHandler extends AbstractMovementPacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter player = c.getPlayer();
MapleMap map = player.getMap();
int objectid = slea.readInt();
short moveid = slea.readShort();
MapleMapObject mmo = map.getMapObject(objectid);
if (mmo == null || mmo.getType() != MapleMapObjectType.MONSTER) {
return;
}
MapleMonster monster = (MapleMonster) mmo;
List<LifeMovementFragment> res = null;
List<MapleCharacter> banishPlayers = new ArrayList<>();
List<MapleCharacter> banishPlayers = new LinkedList<>();
byte pNibbles = slea.readByte();
byte rawActivity = slea.readByte();
byte useSkillId = slea.readByte();
byte useSkillLevel = slea.readByte();
slea.readByte();
slea.readByte();
short pOption = slea.readShort();
slea.skip(8);
if (rawActivity >= 0) {
rawActivity = (byte) (rawActivity & 0xFF >> 1);
}
@@ -78,77 +80,65 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
byte attackId = (byte) (isAttack ? rawActivity - 12 : -1);
boolean nextMovementCouldBeSkill = (pNibbles & 0x0F) != 0;
boolean pUnk = (pNibbles & 0xF0) != 0;
int nextCastSkill = useSkillId;
int nextCastSkillLevel = useSkillLevel;
boolean currentController = (monster.getController() == player);
MobSkill toUse = null;
int rndSkill = -1;
int useSkillId = 0, useSkillLevel = 0;
if(monster.getNoSkills() > 0) {
if(nextMovementCouldBeSkill) {
rndSkill = Randomizer.nextInt(monster.getNoSkills());
if (isSkill) {
int noSkills = monster.getNoSkills();
if (noSkills > 0) {
int rndSkill = Randomizer.nextInt(noSkills);
Pair<Integer, Integer> skillToUse = monster.getSkills().get(rndSkill);
useSkillId = skillToUse.getLeft();
useSkillLevel = skillToUse.getRight();
toUse = MobSkillFactory.getMobSkill(useSkillId, useSkillLevel);
} else {
nextMovementCouldBeSkill = false;
}
} else {
nextMovementCouldBeSkill = false;
}
if(monster.applyAnimationIfRoaming((attackId - 13) / 2, rndSkill)) {
if (rndSkill > -1) {
Pair<Integer, Integer> skillToUse = monster.getSkills().get(rndSkill);
nextCastSkill = skillToUse.getLeft();
nextCastSkillLevel = skillToUse.getRight();
toUse = MobSkillFactory.getMobSkill(nextCastSkill, nextCastSkillLevel);
if (!isSkill && !isAttack) {
long curtime = currentServerTime();
if(curtime >= monster.getNextBasicSkillTime()) { // dont use the special attack too often, chase the player f3
//MobAttackInfo mobAttack = MobAttackInfoFactory.getMobAttackInfo(monster, attackId);
monster.setNextBasicSkillTime(curtime);
} else {
toUse = null; // paliative measure for suspicious mob movement
}
}
if (toUse != null) {
int percHpLeft = (int) (((float) monster.getHp() / monster.getMaxHp()) * 100);
if (nextCastSkill != toUse.getSkillId() || nextCastSkillLevel != toUse.getSkillLevel()) {
//toUse.resetAnticipatedSkill();
return;
} else if (toUse.getHP() < percHpLeft) {
toUse = null;
} else if (monster.canUseSkill(toUse)) {
int animationTime = MapleMonsterInformationProvider.getInstance().getMobSkillAnimationTime(monster.getId(), rndSkill);
if(animationTime > 0) {
toUse.applyDelayedEffect(player, monster, true, banishPlayers, animationTime);
} else {
toUse.applyEffect(player, monster, true, banishPlayers);
}
} else {
toUse = null;
}
}
int mobMp;
if (toUse != null && toUse.getHP() >= (int) (((float) monster.getHp() / monster.getMaxHp()) * 100) && monster.canUseSkill(toUse)) {
mobMp = monster.getMp();
int animationTime = MapleMonsterInformationProvider.getInstance().getMobSkillAnimationTime(toUse);
if(animationTime > 0) {
toUse.applyDelayedEffect(player, monster, true, banishPlayers, animationTime);
} else {
toUse.applyEffect(player, monster, true, banishPlayers);
}
} else {
if(rndSkill > -1) {
nextMovementCouldBeSkill = false;
int atkStatus = monster.canUseAttack((attackId - 13) / 2);
if (atkStatus < 1) {
if (!currentController) {
return;
}
mobMp = atkStatus < 0 ? 0 : monster.getMp();
} else {
mobMp = monster.getMp();
}
}
if(toUse == null) {
useSkillId = 0;
useSkillLevel = 0;
rawActivity = -1;
pOption = 0;
toUse = null;
}
slea.readByte();
slea.readInt(); // whatever
short start_x = slea.readShort(); // hmm.. startpos?
short start_y = slea.readShort(); // hmm...
Point startPos = new Point(start_x, start_y);
res = parseMovement(slea);
if (monster.getController() != player) {
Point startPos = new Point(start_x, start_y - 2);
List<LifeMovementFragment> res = parseMovement(slea);
if (!currentController) {
if (monster.isAttackedBy(player)) {
monster.switchController(player, true);
} else {
@@ -156,22 +146,23 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
}
} else if (rawActivity == -1 && monster.isControllerKnowsAboutAggro() && !monster.isMobile() && !monster.isFirstAttack()) {
monster.setControllerHasAggro(false);
monster.setControllerKnowsAboutAggro(false);
monster.setControllerKnowsAboutAggro(false);
}
boolean aggro = monster.isControllerHasAggro();
if (toUse != null) {
c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, monster.getMp(), aggro, toUse.getSkillId(), toUse.getSkillLevel()));
c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro, toUse.getSkillId(), toUse.getSkillLevel()));
} else {
c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, monster.getMp(), aggro));
c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro));
}
if (aggro) {
monster.setControllerKnowsAboutAggro(true);
}
if (res != null) {
map.broadcastMessage(player, MaplePacketCreator.moveMonster(objectid, nextMovementCouldBeSkill, rawActivity, useSkillId, useSkillLevel, pOption, startPos, res), monster.getPosition());
updatePosition(res, monster, -1);
updatePosition(res, monster, -2);
map.moveMonster(monster, monster.getPosition());
}
@@ -180,7 +171,7 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
}
}
public static boolean inRangeInclusive(Byte pVal, Integer pMin, Integer pMax) {
private static boolean inRangeInclusive(Byte pVal, Integer pMin, Integer pMax) {
return !(pVal < pMin) || (pVal > pMax);
}
}

View File

@@ -23,7 +23,6 @@ package net.server.channel.handlers;
import client.MapleClient;
import client.MapleCharacter;
import client.inventory.Equip;
import client.inventory.Item;
import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
@@ -31,7 +30,6 @@ import net.AbstractMaplePacketHandler;
import client.inventory.manipulator.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
import server.MapleStatEffect;
import tools.Pair;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import constants.ServerConstants;
@@ -47,14 +45,9 @@ public final class PetAutoPotHandler extends AbstractMaplePacketHandler {
Item toUse;
List<Item> toUseList;
boolean hasHpGain;
boolean hasMpGain;
short maxHp;
short maxMp;
short incHp;
short incMp;
int curHp;
int curMp;
boolean hasHpGain, hasMpGain;
int maxHp, maxMp, curHp, curMp;
double incHp, incMp;
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
@@ -72,60 +65,64 @@ public final class PetAutoPotHandler extends AbstractMaplePacketHandler {
MapleCharacter chr = c.getPlayer();
MapleInventory useInv = chr.getInventory(MapleInventoryType.USE);
int useCount = 0, qtyCount = 0;
MapleStatEffect stat = null;
useInv.lockInventory();
try {
toUse = useInv.getItem(slot);
if(toUse != null) {
if (toUse != null) {
if (toUse.getItemId() != itemId) {
c.announce(MaplePacketCreator.enableActions());
return;
}
toUseList = null;
// from now on, toUse becomes the "cursor" for the current pot being used
if(toUse.getQuantity() <= 0) {
if(!cursorOnNextAvailablePot(chr)) {
if (toUse.getQuantity() <= 0) {
if (!cursorOnNextAvailablePot(chr)) {
c.announce(MaplePacketCreator.enableActions());
return;
}
}
MapleStatEffect stat = MapleItemInformationProvider.getInstance().getItemEffect(toUse.getItemId());
stat = MapleItemInformationProvider.getInstance().getItemEffect(toUse.getItemId());
hasHpGain = stat.getHp() > 0 || stat.getHpRate() > 0.0;
hasMpGain = stat.getMp() > 0 || stat.getMpRate() > 0.0;
// contabilize the HP and MP gains from equipments on one's effective MaxHP/MaxMP
Pair<Short, Short> maxHpMp = calcEffectivePool(chr);
maxHp = maxHpMp.left;
maxMp = maxHpMp.right;
incHp = stat.getHp();
if(incHp <= 0 && hasHpGain) incHp = (short)(maxHp * stat.getHpRate());
incMp = stat.getMp();
if(incMp <= 0 && hasMpGain) incMp = (short)(maxMp * stat.getMpRate());
maxHp = chr.getCurrentMaxHp();
maxMp = chr.getCurrentMaxMp();
curHp = chr.getHp();
curMp = chr.getMp();
incHp = stat.getHp();
if(incHp <= 0 && hasHpGain) incHp = Math.ceil(maxHp * stat.getHpRate());
incMp = stat.getMp();
if(incMp <= 0 && hasMpGain) incMp = Math.ceil(maxMp * stat.getMpRate());
if (hasHpGain) {
qtyCount = (int) Math.ceil(((ServerConstants.PET_AUTOHP_RATIO * maxHp) - curHp) / incHp);
}
if (hasMpGain) {
qtyCount = Math.max(qtyCount, (int) Math.ceil(((ServerConstants.PET_AUTOMP_RATIO * maxMp) - curMp) / incMp));
}
//System.out.println("\n-------------------\n");
while(true) {
do {
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, (short) 1, false);
stat.applyTo(chr);
curHp += incHp;
curMp += incMp;
//System.out.println();
//System.out.println("hp: " + hasHpGain + " hpgain " + incHp + " player hp " + curHp + " maxhp " + maxHp);
//System.out.println("mp: " + hasMpGain + " mpgain " + incMp + " player mp " + curMp + " maxmp " + maxMp);
//System.out.println("redo? " + (shouldReusePot() && toUse.getQuantity() > 0));
} while(shouldReusePot() && toUse.getQuantity() > 0);
if(toUse.getQuantity() == 0 && shouldReusePot()) {
while (true) {
short qtyToUse = (short) Math.min(qtyCount, toUse.getQuantity());
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, qtyToUse, false);
curHp += (incHp * qtyToUse);
curMp += (incMp * qtyToUse);
useCount += qtyToUse;
qtyCount -= qtyToUse;
if(toUse.getQuantity() == 0 && qtyCount > 0) {
// depleted out the current slot, fetch for more
if(!cursorOnNextAvailablePot(chr)) {
@@ -139,6 +136,12 @@ public final class PetAutoPotHandler extends AbstractMaplePacketHandler {
} finally {
useInv.unlockInventory();
}
for (int i = 0; i < useCount; i++) {
stat.applyTo(chr);
}
chr.announce(MaplePacketCreator.enableActions());
}
private boolean cursorOnNextAvailablePot(MapleCharacter chr) {
@@ -159,13 +162,5 @@ public final class PetAutoPotHandler extends AbstractMaplePacketHandler {
}
return false;
}
private static Pair<Short, Short> calcEffectivePool(MapleCharacter chr) {
return new Pair<>((short) chr.getMaxHpEquipped(), (short) chr.getMaxMpEquipped());
}
private boolean shouldReusePot() {
return (hasHpGain && curHp < ServerConstants.PET_AUTOHP_RATIO * maxHp) || (hasMpGain && curMp < ServerConstants.PET_AUTOMP_RATIO * maxMp);
}
}

View File

@@ -50,6 +50,7 @@ import client.MapleCharacter;
import client.MapleClient;
import client.MapleDisease;
import client.MapleFamily;
import client.MapleKeyBinding;
import client.SkillFactory;
import client.inventory.Equip;
import client.inventory.Item;
@@ -185,10 +186,12 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
player.sendKeymap();
player.sendMacros();
if(player.getKeymap().get(91) != null)
player.announce(MaplePacketCreator.sendAutoHpPot(player.getKeymap().get(91).getAction()));
if(player.getKeymap().get(92) != null)
player.announce(MaplePacketCreator.sendAutoMpPot(player.getKeymap().get(92).getAction()));
// pot bindings being passed through other characters on the account detected thanks to Croosade dev team
MapleKeyBinding autohpPot = player.getKeymap().get(91);
player.announce(MaplePacketCreator.sendAutoHpPot(autohpPot != null ? autohpPot.getAction() : 0));
MapleKeyBinding autompPot = player.getKeymap().get(92);
player.announce(MaplePacketCreator.sendAutoMpPot(autompPot != null ? autompPot.getAction() : 0));
player.getMap().addPlayer(player);
player.visitMap(player.getMap());
@@ -263,8 +266,6 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
MapleInventory eqpInv = player.getInventory(MapleInventoryType.EQUIPPED);
eqpInv.lockInventory();
try {
player.resetEquippedHpMp();
for(Item it : eqpInv.list()) {
player.equippedItem((Equip) it);
}

View File

@@ -111,12 +111,10 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler {
} else if (skillid == Brawler.MP_RECOVERY) {// MP Recovery
Skill s = SkillFactory.getSkill(skillid);
MapleStatEffect ef = s.getEffect(chr.getSkillLevel(s));
int lose = chr.getMaxHp() / ef.getX();
chr.setHp(chr.getHp() - lose);
chr.updateSingleStat(MapleStat.HP, chr.getHp());
int gain = lose * (ef.getY() / 100);
chr.setMp(chr.getMp() + gain);
chr.updateSingleStat(MapleStat.MP, chr.getMp());
int lose = chr.safeAddHP(-1 * (chr.getCurrentMaxHp() / ef.getX()));
int gain = -lose * (ef.getY() / 100);
chr.addMP(gain);
} else if (skillid == SuperGM.HEAL_PLUS_DISPEL) {
slea.skip(11);
chr.getMap().broadcastMessage(chr, MaplePacketCreator.showBuffeffect(chr.getId(), skillid, chr.getSkillLevel(skillid)), false);

View File

@@ -245,11 +245,8 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
}
if (!chr.isHidden()) {
map.broadcastMessage(chr, MaplePacketCreator.damagePlayer(damagefrom, monsteridfrom, chr.getId(), damage, fake, direction, is_pgmr, pgmr, is_pg, oid, pos_x, pos_y), false);
chr.checkBerserk(true);
}
else {
} else {
map.broadcastGMMessage(chr, MaplePacketCreator.damagePlayer(damagefrom, monsteridfrom, chr.getId(), damage, fake, direction, is_pgmr, pgmr, is_pg, oid, pos_x, pos_y), false);
chr.checkBerserk(false);
}
if (GameConstants.isDojo(map.getId())) {
chr.setDojoEnergy(chr.getDojoEnergy() + ServerConstants.DOJO_ENERGY_DMG);

View File

@@ -97,12 +97,10 @@ public final class UseItemHandler extends AbstractMaplePacketHandler {
if(toUse.getItemId() != 2022153) {
ii.getItemEffect(toUse.getItemId()).applyTo(chr);
chr.checkBerserk(chr.isHidden());
} else {
MapleStatEffect mse = ii.getItemEffect(toUse.getItemId());
for(MapleCharacter player : chr.getMap().getCharacters()) {
mse.applyTo(player);
player.checkBerserk(player.isHidden());
}
}
}

View File

@@ -47,28 +47,28 @@ public final class UseMountFoodHandler extends AbstractMaplePacketHandler {
MapleMount mount = chr.getMount();
Item item = chr.getInventory(MapleInventoryType.USE).getItem(pos);
if (item != null && item.getItemId() == itemid && mount != null) {
float healedFactor;
c.lockClient();
try {
int curTiredness = mount.getTiredness();
int healedTiredness = Math.min(curTiredness, 30);
healedFactor = (float) healedTiredness / 30;
float healedFactor = (float) healedTiredness / 30;
mount.setTiredness(curTiredness - healedTiredness);
if (healedFactor > 0.0f) {
mount.setExp(mount.getExp() + (int) Math.ceil(healedFactor * (2 * mount.getLevel() + 6)));
int level = mount.getLevel();
boolean levelup = mount.getExp() >= ExpTable.getMountExpNeededForLevel(level) && level < 31;
if (levelup) {
mount.setLevel(level + 1);
}
chr.getMap().broadcastMessage(MaplePacketCreator.updateMount(chr.getId(), mount, levelup));
}
MapleInventoryManipulator.removeById(c, MapleInventoryType.USE, itemid, 1, true, false);
} finally {
c.unlockClient();
}
if (healedFactor > 0.0f) {
mount.setExp(mount.getExp() + (int) Math.ceil(healedFactor * (2 * mount.getLevel() + 6)));
int level = mount.getLevel();
boolean levelup = mount.getExp() >= ExpTable.getMountExpNeededForLevel(level) && level < 31;
if (levelup) {
mount.setLevel(level + 1);
}
chr.getMap().broadcastMessage(MaplePacketCreator.updateMount(chr.getId(), mount, levelup));
MapleInventoryManipulator.removeById(c, MapleInventoryType.USE, itemid, 1, true, false);
}
}
}
}

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -1,6 +1,6 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft 2016 - 2018 RonanLana
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as

View File

@@ -297,17 +297,52 @@ public class MapleAlliance {
}
}
public static void removeGuildFromAlliance(int allianceId, int guildId, int worldId) {
MapleAlliance alliance = Server.getInstance().getAlliance(allianceId);
Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.removeGuildFromAlliance(alliance, guildId, worldId), -1, -1);
Server.getInstance().removeGuildFromAlliance(alliance.getId(), guildId);
Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.getGuildAlliances(alliance, worldId), -1, -1);
Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.allianceNotice(alliance.getId(), alliance.getNotice()), -1, -1);
Server.getInstance().guildMessage(guildId, MaplePacketCreator.disbandAlliance(alliance.getId()));
alliance.dropMessage("[" + Server.getInstance().getGuild(guildId, worldId).getName() + "] guild has left the union.");
private static void removeGuildFromAllianceOnDb(int guildId) {
PreparedStatement ps = null;
Connection con = null;
try {
con = DatabaseConnection.getConnection();
ps = con.prepareStatement("DELETE FROM `allianceguilds` WHERE guildid = ?");
ps.setInt(1, guildId);
ps.executeUpdate();
ps.close();
con.close();
} catch (SQLException sqle) {
sqle.printStackTrace();
} finally {
try {
if (ps != null && !ps.isClosed()) {
ps.close();
}
if (con != null && !con.isClosed()) {
con.close();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
public static boolean removeGuildFromAlliance(int allianceId, int guildId, int worldId) {
Server srv = Server.getInstance();
MapleAlliance alliance = srv.getAlliance(allianceId);
if (alliance.getLeader().getGuildId() == guildId) {
return false;
}
srv.allianceMessage(alliance.getId(), MaplePacketCreator.removeGuildFromAlliance(alliance, guildId, worldId), -1, -1);
srv.removeGuildFromAlliance(alliance.getId(), guildId);
removeGuildFromAllianceOnDb(guildId);
srv.allianceMessage(alliance.getId(), MaplePacketCreator.getGuildAlliances(alliance, worldId), -1, -1);
srv.allianceMessage(alliance.getId(), MaplePacketCreator.allianceNotice(alliance.getId(), alliance.getNotice()), -1, -1);
srv.guildMessage(guildId, MaplePacketCreator.disbandAlliance(alliance.getId()));
alliance.dropMessage("[" + srv.getGuild(guildId, worldId).getName() + "] guild has left the union.");
return true;
}
public void updateAlliancePackets(MapleCharacter chr) {

View File

@@ -604,10 +604,9 @@ public class MapleGuild {
public void disbandGuild() {
if(allianceId > 0) {
MapleAlliance alliance = Server.getInstance().getAlliance(allianceId);
if(alliance.getLeader().getGuildId() != id) MapleAlliance.removeGuildFromAlliance(allianceId, id, world);
else MapleAlliance.disbandAlliance(allianceId);
if (!MapleAlliance.removeGuildFromAlliance(allianceId, id, world)) {
MapleAlliance.disbandAlliance(allianceId);
}
}
membersLock.lock();

View File

@@ -170,7 +170,7 @@ public class World {
runningPartyId.set(1);
runningMessengerId.set(1);
petUpdate = System.currentTimeMillis();
petUpdate = Server.getInstance().getCurrentTime();
mountUpdate = petUpdate;
for (int i = 0; i < 9; i++) {
@@ -1303,7 +1303,7 @@ public class World {
activePetsLock.lock();
try {
int initProc;
if(System.currentTimeMillis() - petUpdate > 55000) initProc = ServerConstants.PET_EXHAUST_COUNT - 2;
if(Server.getInstance().getCurrentTime() - petUpdate > 55000) initProc = ServerConstants.PET_EXHAUST_COUNT - 2;
else initProc = ServerConstants.PET_EXHAUST_COUNT - 1;
activePets.put(key, initProc);
@@ -1328,7 +1328,7 @@ public class World {
activePetsLock.lock();
try {
petUpdate = System.currentTimeMillis();
petUpdate = Server.getInstance().getCurrentTime();
deployedPets = Collections.unmodifiableMap(activePets);
} finally {
activePetsLock.unlock();
@@ -1362,7 +1362,7 @@ public class World {
activeMountsLock.lock();
try {
int initProc;
if(System.currentTimeMillis() - mountUpdate > 45000) initProc = ServerConstants.MOUNT_EXHAUST_COUNT - 2;
if(Server.getInstance().getCurrentTime() - mountUpdate > 45000) initProc = ServerConstants.MOUNT_EXHAUST_COUNT - 2;
else initProc = ServerConstants.MOUNT_EXHAUST_COUNT - 1;
activeMounts.put(key, initProc);
@@ -1386,7 +1386,7 @@ public class World {
Map<Integer, Integer> deployedMounts;
activeMountsLock.lock();
try {
mountUpdate = System.currentTimeMillis();
mountUpdate = Server.getInstance().getCurrentTime();
deployedMounts = Collections.unmodifiableMap(activeMounts);
} finally {
activeMountsLock.unlock();