Boss Daily Entry + Maker enable actions + Duey Quick Delivery
Fixed Legendary Spirit UI getting stuck when trying to apply scrolls in equipments without upgrade slots available. Revised map leasing. Players no longer lose ownership when changing maps. Players are allowed to lease one map each time. Revised expeditions warping players out as soon as the leader leaves the event or the number of players inside gets to be less than the minimum required to enter. Fixed start/complete quest commands not acting properly for some quests. Refactored quest loadouts unnecessarily reloading MapleData. Implemented support for daily boss limit entry, usable on expeditions. Revised potential exploit cases within chair, face expression, quest action, summon damage and mob damage mob handlers. Adjusted displayed date in Duey. Value displayed now should be consistent with the expected expiration time. Refactored damage for friendly mobs getting handled inside packet structure. Implemented support for Quick Delivery from Duey. Fixed Horntail specifically not dropping loots after recent updates. Refactored commands system. All commands are instanced at boot time instead of at every call. Fixed usage of Maker skill not sending MAKER_RESULT packet to players. This automatically reenables the actions button (such as create) in Maker UI. Adjusted minidungeons, now using time limits specified in their respective recipes. Reviewed the "timeLimit" property utilized by maps, which was poised to work on 2 different concepts altogether. Fixed Gaga space event, should be functional now. Added RPS minigame, resources implemented by Arnah. Fixed damage taken from mob auto-destruction not working properly.
This commit is contained in:
@@ -163,6 +163,7 @@ public final class PacketProcessor {
|
||||
registerHandler(RecvOpcode.CANCEL_BUFF, new CancelBuffHandler());
|
||||
registerHandler(RecvOpcode.CANCEL_ITEM_EFFECT, new CancelItemEffectHandler());
|
||||
registerHandler(RecvOpcode.PLAYER_INTERACTION, new PlayerInteractionHandler());
|
||||
registerHandler(RecvOpcode.RPS_ACTION, new RPSActionHandler());
|
||||
registerHandler(RecvOpcode.DISTRIBUTE_AP, new DistributeAPHandler());
|
||||
registerHandler(RecvOpcode.DISTRIBUTE_SP, new DistributeSPHandler());
|
||||
registerHandler(RecvOpcode.CHANGE_KEYMAP, new KeymapChangeHandler());
|
||||
|
||||
@@ -239,6 +239,7 @@ public enum SendOpcode {
|
||||
MESO_BAG_MESSAGE(0xD2),
|
||||
UPDATE_QUEST_INFO(0xD3),
|
||||
PLAYER_HINT(0xD6),
|
||||
MAKER_RESULT(0xD9),
|
||||
KOREAN_EVENT(0xDB),
|
||||
OPEN_UI(0xDC),
|
||||
LOCK_UI(0xDD),
|
||||
|
||||
@@ -57,6 +57,7 @@ import net.server.channel.Channel;
|
||||
import net.server.guild.MapleAlliance;
|
||||
import net.server.guild.MapleGuild;
|
||||
import net.server.guild.MapleGuildCharacter;
|
||||
import net.server.worker.BossLogWorker;
|
||||
import net.server.worker.CharacterDiseaseWorker;
|
||||
import net.server.worker.CouponWorker;
|
||||
import net.server.worker.EventRecallCoordinatorWorker;
|
||||
@@ -96,6 +97,7 @@ import server.CashShop.CashItemFactory;
|
||||
import server.MapleSkillbookInformationProvider;
|
||||
import server.ThreadManager;
|
||||
import server.TimerManager;
|
||||
import server.expeditions.MapleExpeditionBossLog;
|
||||
import server.life.MaplePlayerNPCFactory;
|
||||
import server.quest.MapleQuest;
|
||||
import tools.AutoJCE;
|
||||
@@ -527,6 +529,16 @@ public class Server {
|
||||
return Math.max(0, nextHour.getTimeInMillis() - System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private static long getTimeLeftForNextDay() {
|
||||
Calendar nextDay = Calendar.getInstance();
|
||||
nextDay.add(Calendar.DAY_OF_MONTH, 1);
|
||||
nextDay.set(Calendar.HOUR, 0);
|
||||
nextDay.set(Calendar.MINUTE, 0);
|
||||
nextDay.set(Calendar.SECOND, 0);
|
||||
|
||||
return Math.max(0, nextDay.getTimeInMillis() - System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public Map<Integer, Integer> getCouponRates() {
|
||||
return couponRates;
|
||||
}
|
||||
@@ -895,6 +907,10 @@ public class Server {
|
||||
tMan.register(new FredrickWorker(), 60 * 60 * 1000, 60 * 60 * 1000);
|
||||
tMan.register(new InvitationWorker(), 30 * 1000, 30 * 1000);
|
||||
|
||||
timeLeft = getTimeLeftForNextDay();
|
||||
MapleExpeditionBossLog.resetBossLogTable();
|
||||
tMan.register(new BossLogWorker(), 24 * 60 * 60 * 1000, timeLeft);
|
||||
|
||||
long timeToTake = System.currentTimeMillis();
|
||||
SkillFactory.loadAllSkills();
|
||||
System.out.println("Skills loaded in " + ((System.currentTimeMillis() - timeToTake) / 1000.0) + " seconds");
|
||||
|
||||
@@ -199,12 +199,12 @@ public final class Channel {
|
||||
disconnectAwayPlayers();
|
||||
players.disconnectAll();
|
||||
|
||||
mapManager.dispose();
|
||||
mapManager = null;
|
||||
|
||||
eventSM.cancel();
|
||||
eventSM = null;
|
||||
|
||||
mapManager.dispose();
|
||||
mapManager = null;
|
||||
|
||||
closeChannelSchedules();
|
||||
players = null;
|
||||
|
||||
@@ -683,7 +683,7 @@ public final class Channel {
|
||||
if(dungeons.containsKey(dungeonid)) return false;
|
||||
|
||||
MapleMiniDungeonInfo mmdi = MapleMiniDungeonInfo.getDungeon(dungeonid);
|
||||
MapleMiniDungeon mmd = new MapleMiniDungeon(mmdi.getBase(), 30); // all minidungeons timeout on 30 mins
|
||||
MapleMiniDungeon mmd = new MapleMiniDungeon(mmdi.getBase(), this.getMapFactory().getMap(mmdi.getDungeonId()).getTimeLimit()); // thanks Conrad for noticing hardcoded time limit for minidungeons
|
||||
|
||||
dungeons.put(dungeonid, mmd);
|
||||
return true;
|
||||
|
||||
@@ -32,8 +32,11 @@ public final class CancelChairHandler extends AbstractMaplePacketHandler {
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
int id = slea.readShort();
|
||||
MapleCharacter mc = c.getPlayer();
|
||||
if(!mc.isLoggedinWorld()) return;
|
||||
|
||||
mc.sitChair(id == -1 ? 0 : id);
|
||||
if (id >= mc.getMap().getSeats()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mc.sitChair(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,9 @@ public final class DueyHandler extends AbstractMaplePacketHandler {
|
||||
short amount = slea.readShort();
|
||||
int mesos = slea.readInt();
|
||||
String recipient = slea.readMapleAsciiString();
|
||||
String message = slea.readByte() != 0 ? slea.readMapleAsciiString() : "";
|
||||
DueyProcessor.dueySendItem(c, inventId, itemPos, amount, mesos, message, recipient);
|
||||
boolean quick = slea.readByte() != 0;
|
||||
String message = quick ? slea.readMapleAsciiString() : "";
|
||||
DueyProcessor.dueySendItem(c, inventId, itemPos, amount, mesos, message, recipient, quick);
|
||||
} else if (operation == DueyProcessor.Actions.TOSERVER_REMOVE_PACKAGE.getCode()) {
|
||||
int packageid = slea.readInt();
|
||||
|
||||
@@ -55,6 +56,8 @@ public final class DueyHandler extends AbstractMaplePacketHandler {
|
||||
int packageid = slea.readInt();
|
||||
|
||||
DueyProcessor.dueyClaimPackage(c, packageid);
|
||||
} else if (operation == DueyProcessor.Actions.TOSERVER_CLAIM_PACKAGE.getCode()) {
|
||||
DueyProcessor.dueySendTalk(c, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,15 +34,17 @@ public final class FaceExpressionHandler extends AbstractMaplePacketHandler {
|
||||
int emote = slea.readInt();
|
||||
|
||||
if (emote > 7) {
|
||||
int emoteid = 5159992 + emote;
|
||||
if (chr.getInventory(ItemConstants.getInventoryType(emoteid)).findById(emoteid) == null) {
|
||||
int itemid = 5159992 + emote; // thanks Rajan (Darter) for reporting unchecked emote itemid
|
||||
if (!ItemConstants.isFaceExpression(itemid) || chr.getInventory(ItemConstants.getInventoryType(itemid)).findById(itemid) == null) {
|
||||
return;
|
||||
}
|
||||
} else if (emote < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(c.tryacquireClient()) {
|
||||
try { // expecting players never intends to wear the emote 0 (default face, that changes back after 5sec timeout)
|
||||
if (emote != 0 && chr.isLoggedinWorld()) {
|
||||
if (chr.isLoggedinWorld()) {
|
||||
chr.changeFaceExpression(emote);
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -72,8 +72,15 @@ public final class MobDamageMobFriendlyHandler extends AbstractMaplePacketHandle
|
||||
map.addBunnyHit();
|
||||
}
|
||||
}
|
||||
|
||||
monster.applyAndGetHpDamage(damage, false);
|
||||
int remainingHp = monster.getHp();
|
||||
if(remainingHp <= 0) {
|
||||
remainingHp = 0;
|
||||
monster.getMap().removeMapObject(monster);
|
||||
}
|
||||
|
||||
c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.MobDamageMobFriendly(monster, damage), monster.getPosition());
|
||||
c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.MobDamageMobFriendly(monster, damage, remainingHp), monster.getPosition());
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
}
|
||||
@@ -21,16 +21,24 @@
|
||||
*/
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import java.util.Map;
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import client.autoban.AutobanFactory;
|
||||
import client.status.MonsterStatus;
|
||||
import client.status.MonsterStatusEffect;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import server.life.MapleMonster;
|
||||
import server.life.MapleMonsterInformationProvider;
|
||||
import server.maps.MapleMap;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jay Estrella
|
||||
* @author Ronan
|
||||
*/
|
||||
public final class MobDamageMobHandler extends AbstractMaplePacketHandler {
|
||||
@Override
|
||||
@@ -38,14 +46,69 @@ public final class MobDamageMobHandler extends AbstractMaplePacketHandler {
|
||||
int from = slea.readInt();
|
||||
slea.readInt();
|
||||
int to = slea.readInt();
|
||||
slea.readByte();
|
||||
boolean magic = slea.readByte() == 0;
|
||||
int dmg = slea.readInt();
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
|
||||
MapleMap map = chr.getMap();
|
||||
if (map.getMonsterByOid(from) != null && map.getMonsterByOid(to) != null) {
|
||||
map.damageMonster(chr, map.getMonsterByOid(to), dmg);
|
||||
map.broadcastMessage(MaplePacketCreator.damageMonster(to, dmg));
|
||||
MapleMonster attacker = map.getMonsterByOid(from);
|
||||
MapleMonster damaged = map.getMonsterByOid(to);
|
||||
|
||||
if (attacker != null && damaged != null) {
|
||||
int maxDmg = calcMaxDamage(attacker, damaged, magic); // thanks Darter (YungMoozi) for reporting unchecked dmg
|
||||
|
||||
if (dmg > maxDmg) {
|
||||
AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing hypnotize damage exploit."); // thanks Rien dev team
|
||||
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " had hypnotized " + MapleMonsterInformationProvider.getInstance().getMobNameFromId(attacker.getId()) + " to attack " + MapleMonsterInformationProvider.getInstance().getMobNameFromId(damaged.getId()) + " with damage " + dmg + " (max: " + maxDmg + ")");
|
||||
dmg = maxDmg;
|
||||
}
|
||||
|
||||
map.damageMonster(chr, damaged, dmg);
|
||||
map.broadcastMessage(chr, MaplePacketCreator.damageMonster(to, dmg), false);
|
||||
}
|
||||
}
|
||||
|
||||
private static int calcMaxDamage(MapleMonster attacker, MapleMonster damaged, boolean magic) {
|
||||
int attackerAtk, damagedDef, attackerLevel = attacker.getLevel();
|
||||
double maxDamage;
|
||||
if (magic) {
|
||||
int atkRate = calcModifier(attacker, MonsterStatus.MAGIC_ATTACK_UP, MonsterStatus.MATK);
|
||||
attackerAtk = (attacker.getStats().getMADamage() * atkRate) / 100;
|
||||
|
||||
int defRate = calcModifier(damaged, MonsterStatus.MAGIC_DEFENSE_UP, MonsterStatus.MDEF);
|
||||
damagedDef = (damaged.getStats().getMDDamage() * defRate) / 100;
|
||||
|
||||
maxDamage = ((attackerAtk * (1.15 + (0.025 * attackerLevel))) - (0.75 * damagedDef)) * (Math.log(Math.abs(damagedDef - attackerAtk)) / Math.log(12));
|
||||
} else {
|
||||
int atkRate = calcModifier(attacker, MonsterStatus.WEAPON_ATTACK_UP, MonsterStatus.WATK);
|
||||
attackerAtk = (attacker.getStats().getPADamage() * atkRate) / 100;
|
||||
|
||||
int defRate = calcModifier(damaged, MonsterStatus.WEAPON_DEFENSE_UP, MonsterStatus.WDEF);
|
||||
damagedDef = (damaged.getStats().getPDDamage() * defRate) / 100;
|
||||
|
||||
maxDamage = ((attackerAtk * (1.15 + (0.025 * attackerLevel))) - (0.75 * damagedDef)) * (Math.log(Math.abs(damagedDef - attackerAtk)) / Math.log(17));
|
||||
}
|
||||
|
||||
return (int) maxDamage;
|
||||
}
|
||||
|
||||
private static int calcModifier(MapleMonster monster, MonsterStatus buff, MonsterStatus nerf) {
|
||||
int atkModifier;
|
||||
final Map<MonsterStatus, MonsterStatusEffect> monsterStati = monster.getStati();
|
||||
|
||||
MonsterStatusEffect atkBuff = monsterStati.get(buff);
|
||||
if (atkBuff != null) {
|
||||
atkModifier = atkBuff.getStati().get(buff);
|
||||
} else {
|
||||
atkModifier = 100;
|
||||
}
|
||||
|
||||
MonsterStatusEffect atkNerf = monsterStati.get(nerf);
|
||||
if (atkNerf != null) {
|
||||
atkModifier -= atkNerf.getStati().get(nerf);
|
||||
}
|
||||
|
||||
return atkModifier;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +53,7 @@ public final class NPCTalkHandler extends AbstractMaplePacketHandler {
|
||||
if(ServerConstants.USE_DEBUG == true) c.getPlayer().dropMessage(5, "Talking to NPC " + npc.getId());
|
||||
|
||||
if (npc.getId() == 9010009) { //is duey
|
||||
c.getPlayer().setNpcCooldown(currentServerTime());
|
||||
DueyProcessor.dueySendTalk(c);
|
||||
DueyProcessor.dueySendTalk(c, false);
|
||||
} else {
|
||||
if (c.getCM() != null || c.getQM() != null) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
|
||||
@@ -424,22 +424,22 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
ps = con.prepareStatement("SELECT Mesos FROM dueypackages WHERE ReceiverId = ? and Checked = 1");
|
||||
ps = con.prepareStatement("SELECT Type FROM dueypackages WHERE ReceiverId = ? AND Checked = 1 ORDER BY Type DESC");
|
||||
ps.setInt(1, player.getId());
|
||||
rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
try {
|
||||
Connection con2 = DatabaseConnection.getConnection();
|
||||
pss = con2.prepareStatement("UPDATE dueypackages SET Checked = 0 where ReceiverId = ?");
|
||||
pss = con2.prepareStatement("UPDATE dueypackages SET Checked = 0 WHERE ReceiverId = ?");
|
||||
pss.setInt(1, player.getId());
|
||||
pss.executeUpdate();
|
||||
pss.close();
|
||||
con2.close();
|
||||
|
||||
c.announce(MaplePacketCreator.sendDueyParcelNotification(rs.getInt("Type") == 1));
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.sendDueyNotification(false));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -38,13 +38,19 @@ public final class QuestActionHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
// isNpcNearby credits to GabrielSin
|
||||
private static boolean isNpcNearby(SeekableLittleEndianAccessor slea, MapleCharacter player, MapleQuest quest, int npcId) {
|
||||
Point playerP = null;
|
||||
Point playerP;
|
||||
Point pos = player.getPosition();
|
||||
|
||||
if(slea.available() >= 4) {
|
||||
playerP = new Point(slea.readShort(), slea.readShort());
|
||||
if (playerP.distance(pos) > 1000) { // thanks Darter (YungMoozi) for reporting unchecked player position
|
||||
playerP = pos;
|
||||
}
|
||||
} else {
|
||||
playerP = pos;
|
||||
}
|
||||
|
||||
if (playerP != null && !quest.isAutoStart() && !quest.isAutoComplete()) {
|
||||
if (!quest.isAutoStart() && !quest.isAutoComplete()) {
|
||||
MapleNPC npc = player.getMap().getNPCById(npcId);
|
||||
if(npc == null) {
|
||||
return false;
|
||||
|
||||
71
src/net/server/channel/handlers/RPSActionHandler.java
Normal file
71
src/net/server/channel/handlers/RPSActionHandler.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import server.minigame.MapleRockPaperScissor;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
* @Author Arnah
|
||||
* @Website http://Vertisy.ca/
|
||||
* @since Aug 15, 2016
|
||||
*/
|
||||
public final class RPSActionHandler extends AbstractMaplePacketHandler{
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c){
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MapleRockPaperScissor rps = chr.getRPS();
|
||||
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
if(slea.available() == 0 || !chr.getMap().containsNPC(9000019)){
|
||||
if(rps != null){
|
||||
rps.dispose(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final byte mode = slea.readByte();
|
||||
switch (mode){
|
||||
case 0: // start game
|
||||
case 5: // retry
|
||||
if(rps != null){
|
||||
rps.reward(c);
|
||||
}
|
||||
if(chr.getMeso() >= 1000){
|
||||
chr.setRPS(new MapleRockPaperScissor(c, mode));
|
||||
}else{
|
||||
c.announce(MaplePacketCreator.rpsMesoError(-1));
|
||||
}
|
||||
break;
|
||||
case 1: // answer
|
||||
if(rps == null || !rps.answer(c, slea.readByte())){
|
||||
c.announce(MaplePacketCreator.rpsMode((byte) 0x0D));// 13
|
||||
}
|
||||
break;
|
||||
case 2: // time over
|
||||
if(rps == null || !rps.timeOut(c)){
|
||||
c.announce(MaplePacketCreator.rpsMode((byte) 0x0D));
|
||||
}
|
||||
break;
|
||||
case 3: // continue
|
||||
if(rps == null || !rps.nextRound(c)){
|
||||
c.announce(MaplePacketCreator.rpsMode((byte) 0x0D));
|
||||
}
|
||||
break;
|
||||
case 4: // leave
|
||||
if(rps != null){
|
||||
rps.dispose(c);
|
||||
}else{
|
||||
c.announce(MaplePacketCreator.rpsMode((byte) 0x0D));
|
||||
}
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,17 +78,17 @@ public final class ScrollHandler extends AbstractMaplePacketHandler {
|
||||
if (ItemConstants.isCleanSlate(scroll.getItemId())) {
|
||||
Map<String, Integer> eqStats = ii.getEquipStats(toScroll.getItemId()); // clean slate issue found thanks to Masterrulax
|
||||
if (eqStats == null || eqStats.get("tuc") == 0) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
announceCannotScroll(c, legendarySpirit);
|
||||
return;
|
||||
}
|
||||
} else if (!ItemConstants.isModifierScroll(scroll.getItemId()) && ((Equip) toScroll).getUpgradeSlots() < 1) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
announceCannotScroll(c, legendarySpirit); // thanks onechord for noticing zero upgrade slots freezing Legendary Scroll UI
|
||||
return;
|
||||
}
|
||||
|
||||
List<Integer> scrollReqs = ii.getScrollReqs(scroll.getItemId());
|
||||
if (scrollReqs.size() > 0 && !scrollReqs.contains(toScroll.getItemId())) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
announceCannotScroll(c, legendarySpirit);
|
||||
return;
|
||||
}
|
||||
if (whiteScroll) {
|
||||
@@ -100,11 +100,13 @@ public final class ScrollHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
if (!ItemConstants.isChaosScroll(scroll.getItemId()) && !ItemConstants.isCleanSlate(scroll.getItemId())) {
|
||||
if (!canScroll(scroll.getItemId(), toScroll.getItemId())) {
|
||||
announceCannotScroll(c, legendarySpirit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ItemConstants.isCleanSlate(scroll.getItemId()) && !ii.canUseCleanSlate(toScroll)) {
|
||||
announceCannotScroll(c, legendarySpirit);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -119,11 +121,13 @@ public final class ScrollHandler extends AbstractMaplePacketHandler {
|
||||
useInventory.lockInventory();
|
||||
try {
|
||||
if (scroll.getQuantity() < 1) {
|
||||
announceCannotScroll(c, legendarySpirit);
|
||||
return;
|
||||
}
|
||||
|
||||
if (whiteScroll && !ItemConstants.isCleanSlate(scroll.getItemId())) {
|
||||
if (wscroll.getQuantity() < 1) {
|
||||
announceCannotScroll(c, legendarySpirit);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -171,7 +175,7 @@ public final class ScrollHandler extends AbstractMaplePacketHandler {
|
||||
mods.add(new ModifyInventory(0, scrolled));
|
||||
}
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, mods));
|
||||
chr.getMap().broadcastMessage(MaplePacketCreator.getScrollEffect(chr.getId(), scrollSuccess, legendarySpirit));
|
||||
chr.getMap().broadcastMessage(MaplePacketCreator.getScrollEffect(chr.getId(), scrollSuccess, legendarySpirit, whiteScroll));
|
||||
if (dst < 0 && (scrollSuccess == Equip.ScrollResult.SUCCESS || scrollSuccess == Equip.ScrollResult.CURSE)) {
|
||||
chr.equipChanged();
|
||||
}
|
||||
@@ -180,6 +184,14 @@ public final class ScrollHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void announceCannotScroll(MapleClient c, boolean legendarySpirit) {
|
||||
if (legendarySpirit) {
|
||||
c.announce(MaplePacketCreator.getScrollEffect(c.getPlayer().getId(), Equip.ScrollResult.FAIL, false, false));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canScroll(int scrollid, int itemid) {
|
||||
int sid = scrollid / 100;
|
||||
|
||||
@@ -101,7 +101,8 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler {
|
||||
monster.aggroClearDamages();
|
||||
monster.aggroMonsterDamage(chr, 1);
|
||||
|
||||
// thanks onechord for pointing out Magnet disconnecting the caster (issue would actually happen upon failing to catch mob)
|
||||
// thanks onechord for pointing out Magnet crashing the caster (issue would actually happen upon failing to catch mob)
|
||||
// thanks Conrad for noticing Magnet crashing when trying to pull bosses and fixed mobs
|
||||
monster.aggroSwitchController(chr, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,24 +25,32 @@ import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.autoban.AutobanFactory;
|
||||
import client.status.MonsterStatusEffect;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import server.MapleStatEffect;
|
||||
import server.life.MapleMonster;
|
||||
import server.life.MapleMonsterInformationProvider;
|
||||
import server.maps.MapleSummon;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
public final class SummonDamageHandler extends AbstractDealDamageHandler {
|
||||
|
||||
public final class SummonAttackEntry {
|
||||
|
||||
private int monsterOid;
|
||||
private int damage;
|
||||
private boolean magic;
|
||||
|
||||
public SummonAttackEntry(int monsterOid, int damage) {
|
||||
public SummonAttackEntry(int monsterOid, int damage, boolean magic) {
|
||||
this.monsterOid = monsterOid;
|
||||
this.damage = damage;
|
||||
this.magic = magic;
|
||||
}
|
||||
|
||||
public int getMonsterOid() {
|
||||
@@ -52,6 +60,10 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler {
|
||||
public int getDamage() {
|
||||
return damage;
|
||||
}
|
||||
|
||||
public boolean isMagic() {
|
||||
return magic;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,18 +91,34 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler {
|
||||
slea.skip(8); //Thanks Gerald :D, I failed lol (mob x,y and summon x,y)
|
||||
for (int x = 0; x < numAttacked; x++) {
|
||||
int monsterOid = slea.readInt(); // attacked oid
|
||||
slea.skip(18);
|
||||
slea.skip(17);
|
||||
boolean magic = slea.readByte() != 0;
|
||||
int damage = slea.readInt();
|
||||
allDamage.add(new SummonAttackEntry(monsterOid, damage));
|
||||
allDamage.add(new SummonAttackEntry(monsterOid, damage, magic));
|
||||
}
|
||||
player.getMap().broadcastMessage(player, MaplePacketCreator.summonAttack(player.getId(), summon.getObjectId(), direction, allDamage), summon.getPosition());
|
||||
if (player.getMap().isOwnershipRestricted(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<Integer, Integer> maxDmgEntries = new HashMap<>();
|
||||
for (SummonAttackEntry attackEntry : allDamage) {
|
||||
int damage = attackEntry.getDamage();
|
||||
MapleMonster target = player.getMap().getMonsterByOid(attackEntry.getMonsterOid());
|
||||
if (target != null) {
|
||||
Integer maxDmg = maxDmgEntries.get(attackEntry.getMonsterOid());
|
||||
if (maxDmg == null) {
|
||||
maxDmg = calcMaxDamage(summonEffect, player, attackEntry.isMagic()); // thanks Darter (YungMoozi) for reporting unchecked max dmg
|
||||
maxDmgEntries.put(attackEntry.getMonsterOid(), maxDmg);
|
||||
}
|
||||
|
||||
if (damage > maxDmg) {
|
||||
AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing summon damage exploit.");
|
||||
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " used a summon of skillid " + summon.getSkill() + " to attack " + MapleMonsterInformationProvider.getInstance().getMobNameFromId(target.getId()) + " with damage " + damage + " (max: " + maxDmg + ")");
|
||||
damage = maxDmg;
|
||||
}
|
||||
|
||||
if (damage > 0 && summonEffect.getMonsterStati().size() > 0) {
|
||||
if (summonEffect.makeChanceResult()) {
|
||||
target.applyStatus(player, new MonsterStatusEffect(summonEffect.getMonsterStati(), summonSkill, null, false), summonEffect.isPoison(), 4000);
|
||||
@@ -100,4 +128,16 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int calcMaxDamage(MapleStatEffect summonEffect, MapleCharacter player, boolean magic) {
|
||||
double maxDamage;
|
||||
|
||||
if (magic) {
|
||||
maxDamage = player.calculateMaxBaseMagicDamage() * (0.05 * summonEffect.getMatk());
|
||||
} else {
|
||||
maxDamage = player.calculateMaxBaseDamage(player.getTotalWatk()) * (0.021 * summonEffect.getWatk());
|
||||
}
|
||||
|
||||
return (int) maxDamage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ import java.util.List;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import server.MapleStatEffect;
|
||||
import server.life.MapleLifeFactory;
|
||||
import server.life.MapleLifeFactory.loseItem;
|
||||
import server.life.MapleMonster;
|
||||
import server.life.MobAttackInfo;
|
||||
@@ -88,16 +87,6 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (monsteridfrom == 9300166 && attacker == null) {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
attacker = MapleLifeFactory.getMonster(monsteridfrom);
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attacker != null) {
|
||||
if (attacker.isBuffed(MonsterStatus.NEUTRALISE)) {
|
||||
return;
|
||||
@@ -149,7 +138,7 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
|
||||
map.removeMapObject(attacker);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (damagefrom != 0 || !map.removeSelfDestructive(oid)) { // thanks inhyuk for noticing self-destruct damage not being handled properly
|
||||
return;
|
||||
}
|
||||
} catch(ClassCastException e) {
|
||||
@@ -196,21 +185,23 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (damage == -1) {
|
||||
fake = 4020002 + (chr.getJob().getId() / 10 - 40) * 100000;
|
||||
}
|
||||
|
||||
if (damage > 0) {
|
||||
chr.getAutobanManager().resetMisses();
|
||||
} else {
|
||||
chr.getAutobanManager().addMiss();
|
||||
}
|
||||
|
||||
//in dojo player cannot use pot, so deadly attacks should be turned off as well
|
||||
if(is_deadly && chr.getMap().isDojoMap() && !ServerConstants.USE_DEADLY_DOJO) {
|
||||
damage = 0;
|
||||
mpattack = 0;
|
||||
}
|
||||
|
||||
if (damage == 0) {
|
||||
chr.getAutobanManager().addMiss();
|
||||
} else {
|
||||
chr.getAutobanManager().resetMisses();
|
||||
}
|
||||
if (damage > 0 && !chr.isHidden()) {
|
||||
if (attacker != null) {
|
||||
if (damagefrom == -1) {
|
||||
|
||||
@@ -294,11 +294,9 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
if (item == null) //hack
|
||||
{
|
||||
return;
|
||||
} else if (item.isUntradeable() || ii.isUnmerchable(item.getItemId())) {
|
||||
player.dropMessage(1, "You cannot trade this item.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
// thanks Conrad for noticing that untradeable items should be allowed in megas
|
||||
}
|
||||
Server.getInstance().broadcastMessage(c.getWorld(), MaplePacketCreator.itemMegaphone(msg, whisper, c.getChannel(), item));
|
||||
break;
|
||||
@@ -398,7 +396,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
ii.getItemEffect(itemId).applyTo(player);
|
||||
remove(c, position, itemId);
|
||||
} else if (itemType == 533) {
|
||||
DueyProcessor.dueySendTalk(c);
|
||||
DueyProcessor.dueySendTalk(c, true);
|
||||
} else if (itemType == 537) {
|
||||
if (GameConstants.isFreeMarketRoom(player.getMapId())) {
|
||||
player.dropMessage(5, "You cannot use the chalkboard here.");
|
||||
@@ -569,7 +567,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
client.announce(MaplePacketCreator.modifyInventory(true, mods));
|
||||
|
||||
ScrollResult scrollResult = scrolled.getLevel() > curlevel ? ScrollResult.SUCCESS : ScrollResult.FAIL;
|
||||
player.getMap().broadcastMessage(MaplePacketCreator.getScrollEffect(player.getId(), scrollResult, false));
|
||||
player.getMap().broadcastMessage(MaplePacketCreator.getScrollEffect(player.getId(), scrollResult, false, false));
|
||||
if (eSlot < 0 && (scrollResult == ScrollResult.SUCCESS)) {
|
||||
player.equipChanged();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ package net.server.channel.handlers;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import constants.ItemConstants;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
@@ -30,7 +31,9 @@ public final class UseChairHandler extends AbstractMaplePacketHandler {
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
int itemId = slea.readInt();
|
||||
if (c.getPlayer().getInventory(MapleInventoryType.SETUP).findById(itemId) == null) {
|
||||
|
||||
// thanks Darter (YungMoozi) for reporting unchecked chair item
|
||||
if (!ItemConstants.isChair(itemId) || c.getPlayer().getInventory(MapleInventoryType.SETUP).findById(itemId) == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
33
src/net/server/worker/BossLogWorker.java
Normal file
33
src/net/server/worker/BossLogWorker.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
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
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.worker;
|
||||
|
||||
import server.expeditions.MapleExpeditionBossLog;
|
||||
|
||||
/**
|
||||
* @author Ronan
|
||||
*/
|
||||
public class BossLogWorker implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
MapleExpeditionBossLog.resetBossLogTable();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user