Reactor Loot + Obstacles damage mob + Static calls from scripts

Revised starting AP, now working out with flags. To supply the 9AP shortage of 4/4/4/4, two options: one giving out 9 AP from the start, other giving 4/5 AP when changing jobs (1st, 2nd). This change would also work with the autoassign for beginners flag.
Refactored several quest scripts, that would be glitching the player when doing quest start/complete and disposing under the same script status.
Cleared some cases with the quest reward system where it would call out a "full inventory" even though new inventory slots could get discovered when doing the quest loot transaction.
Fixed an issue with player stores being deployed overlapping in a few scenarios.
Fixed reduced EXP gain from kills when triggering skill Mortal Blow.
Added "open Duey" functionality when clicking "O" in the incoming package notification.
Fixed packages without messages (a quirk from quick delivery) not accounting visually as a "quick" one.
Fixed certain mounts (non-item skill mounts, such as Yeti or Spaceship) not showing up properly to other players when changing maps.
Added handler for mob damage by environment objects (OrbisPQ jail storage area).
Added a placeholder on mob's stolen items to prevent more steals to be placed as soon as the Steal mechanism is triggered.
Patched boss logs not removing recent entries from the DB tables (the reset method is actually supposed to clear every entry).
Revised a possible memory leak scenario happening due to an exception thrown midway monster kill method.
Improved reactor drops, now placing loots visible for the acting player centered (similar as to how mob loots work).
Refactored several issues in scripts, related to accessing static Java methods through an object, that would start appearing after transitioning to Java 8.
This commit is contained in:
ronancpl
2019-08-14 21:14:15 -03:00
parent 2c16a4d908
commit f958624f6a
233 changed files with 2368 additions and 888 deletions
+69 -15
View File
@@ -168,6 +168,7 @@ import server.maps.MapleMapItem;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import org.apache.mina.util.ConcurrentHashSet;
import scripting.quest.QuestActionManager;
import server.maps.FieldLimit;
public class MapleCharacter extends AbstractMapleCharacterObject {
@@ -305,6 +306,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
private List<Integer> viptrockmaps = new ArrayList<>();
private Map<String, MapleEvents> events = new LinkedHashMap<>();
private PartyQuest partyQuest = null;
private List<Pair<DelayedQuestUpdate, Object[]>> npcUpdateQuests = new LinkedList<>();
private MapleDragon dragon = null;
private MapleRing marriageRing;
private int marriageItemid = -1;
@@ -1173,7 +1175,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
if (this.isCygnus()) {
gainAp(7, true);
} else {
gainAp(5, true);
if (ServerConstants.USE_STARTING_AP_4 || newJob.getId() % 10 >= 1) {
gainAp(5, true);
}
}
} else { // thanks Periwinks for noticing an AP shortage from lower levels
if (ServerConstants.USE_STARTING_AP_4 && newJob.getId() % 1000 >= 1) {
gainAp(4, true);
}
}
@@ -3877,10 +3885,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
List<Pair<MapleBuffStat, MapleBuffStatValueHolder>> toCancel = deregisterBuffStats(buffstats);
if (effect.isMonsterRiding()) {
if (effect.getSourceId() != Corsair.BATTLE_SHIP) {
this.getClient().getWorldServer().unregisterMountHunger(this);
this.getMount().setActive(false);
}
this.getClient().getWorldServer().unregisterMountHunger(this);
this.getMount().setActive(false);
}
if (!overwrite) {
@@ -6168,7 +6174,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
spGain += (expectedSp - curSp);
}
return getSpGain(spGain, curSp, job);
return getSpGain(spGain, curSp, newJob);
}
private int getUsedSp(MapleJob job) {
@@ -7391,7 +7397,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
continue;
}
if (q.progress(id)) {
client.announce(MaplePacketCreator.updateQuest(q, false));
announceUpdateQuest(DelayedQuestUpdate.UPDATE, q, false);
}
}
}
@@ -7400,8 +7406,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
}
public void mount(int id, int skillid) {
public MapleMount mount(int id, int skillid) {
maplemount = new MapleMount(this, id, skillid);
return maplemount;
}
private void playerDead() {
@@ -9632,9 +9639,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
quests.put(q.getId(), qs);
}
announce(MaplePacketCreator.updateQuest(qs, false));
announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false);
if (qs.getQuest().getInfoNumber() > 0) {
announce(MaplePacketCreator.updateQuest(qs, true));
announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true);
}
announce(MaplePacketCreator.updateQuestInfo((short) qs.getQuest().getId(), qs.getNpc()));
}
@@ -9657,14 +9664,61 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
}
public enum DelayedQuestUpdate { // quest updates allow player actions during NPC talk...
UPDATE, FORFEIT, COMPLETE
}
private void announceUpdateQuestInternal(Pair<DelayedQuestUpdate, Object[]> questUpdate) {
Object[] objs = questUpdate.getRight();
switch (questUpdate.getLeft()) {
case UPDATE:
announce(MaplePacketCreator.updateQuest((MapleQuestStatus) objs[0], (Boolean) objs[1]));
break;
case FORFEIT:
announce(MaplePacketCreator.forfeitQuest((Short) objs[0]));
break;
case COMPLETE:
announce(MaplePacketCreator.completeQuest((Short) objs[0], (Long) objs[1]));
break;
}
}
public void announceUpdateQuest(DelayedQuestUpdate questUpdateType, Object... params) {
Pair<DelayedQuestUpdate, Object[]> p = new Pair<>(questUpdateType, params);
MapleClient c = this.getClient();
if (c.getQM() != null || c.getCM() != null) {
synchronized (npcUpdateQuests) {
npcUpdateQuests.add(p);
}
} else {
announceUpdateQuestInternal(p);
}
}
public void flushDelayedUpdateQuests() {
List<Pair<DelayedQuestUpdate, Object[]>> qmQuestUpdateList;
synchronized (npcUpdateQuests) {
qmQuestUpdateList = new ArrayList<>(npcUpdateQuests);
npcUpdateQuests.clear();
}
for (Pair<DelayedQuestUpdate, Object[]> q : qmQuestUpdateList) {
announceUpdateQuestInternal(q);
}
}
public void updateQuest(MapleQuestStatus quest) {
synchronized (quests) {
quests.put(quest.getQuestID(), quest);
}
if (quest.getStatus().equals(MapleQuestStatus.Status.STARTED)) {
announce(MaplePacketCreator.updateQuest(quest, false));
announceUpdateQuest(DelayedQuestUpdate.UPDATE, quest, false);
if (quest.getQuest().getInfoNumber() > 0) {
announce(MaplePacketCreator.updateQuest(quest, true));
announceUpdateQuest(DelayedQuestUpdate.UPDATE, quest, true);
}
announce(MaplePacketCreator.updateQuestInfo((short) quest.getQuest().getId(), quest.getNpc()));
} else if (quest.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) {
@@ -9675,11 +9729,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
quest.setCompleted(quest.getCompleted() + 1); // count quest completed Jayd's idea
announce(MaplePacketCreator.completeQuest(questid, quest.getCompletionTime()));
announceUpdateQuest(DelayedQuestUpdate.COMPLETE, questid, quest.getCompletionTime());
} else if (quest.getStatus().equals(MapleQuestStatus.Status.NOT_STARTED)) {
announce(MaplePacketCreator.updateQuest(quest, false));
announceUpdateQuest(DelayedQuestUpdate.UPDATE, quest, false);
if (quest.getQuest().getInfoNumber() > 0) {
announce(MaplePacketCreator.updateQuest(quest, true));
announceUpdateQuest(DelayedQuestUpdate.UPDATE, quest, true);
}
}
}
+11 -1
View File
@@ -23,6 +23,7 @@ import client.MapleJob;
import client.Skill;
import client.inventory.Item;
import client.inventory.MapleInventoryType;
import constants.ServerConstants;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.LinkedHashMap;
import java.util.LinkedList;
@@ -39,7 +40,7 @@ public class CharacterFactoryRecipe {
private int level, map, top, bottom, shoes, weapon;
private int str = 4, dex = 4, int_ = 4, luk = 4;
private int maxHp = 50, maxMp = 5;
private int ap = 9, sp = 0;
private int ap = 0, sp = 0;
private int meso = 0;
private List<Pair<Skill, Integer>> skills = new LinkedList<>();
@@ -54,6 +55,15 @@ public class CharacterFactoryRecipe {
this.bottom = bottom;
this.shoes = shoes;
this.weapon = weapon;
if (!ServerConstants.USE_STARTING_AP_4) {
if (ServerConstants.USE_AUTOASSIGN_STARTERS_AP) {
str = 12;
dex = 5;
} else {
ap = 9;
}
}
}
public void setStr(int v) {
+9 -13
View File
@@ -60,6 +60,7 @@ import tools.Pair;
public class DueyProcessor {
public enum Actions {
TOSERVER_RECV_ITEM(0x00),
TOSERVER_SEND_ITEM(0x02),
TOSERVER_CLAIM_PACKAGE(0x04),
TOSERVER_REMOVE_PACKAGE(0x05),
@@ -113,15 +114,6 @@ public class DueyProcessor {
return null;
}
private static Timestamp getCurrentDate(boolean quick) {
Calendar cal = Calendar.getInstance();
if (!quick) {
cal.add(Calendar.DATE, 1);
}
return new Timestamp(cal.getTime().getTime());
}
private static void showDueyNotification(MapleClient c, MapleCharacter player) {
Connection con = null;
PreparedStatement ps = null;
@@ -204,7 +196,7 @@ public class DueyProcessor {
dueypack.setSender(rs.getString("SenderName"));
dueypack.setMesos(rs.getInt("Mesos"));
dueypack.setSentTime(rs.getTimestamp("TimeStamp"));
dueypack.setSentTime(rs.getTimestamp("TimeStamp"), rs.getBoolean("Type"));
dueypack.setMessage(rs.getString("Message"));
return dueypack;
@@ -250,7 +242,7 @@ public class DueyProcessor {
ps.setInt(1, toCid);
ps.setString(2, sender);
ps.setInt(3, mesos);
ps.setTimestamp(4, getCurrentDate(quick));
ps.setTimestamp(4, new Timestamp(System.currentTimeMillis()));
ps.setString(5, message);
ps.setInt(6, quick ? 1 : 0);
@@ -515,7 +507,11 @@ public class DueyProcessor {
}
c.getPlayer().setNpcCooldown(timeNow);
c.announce(MaplePacketCreator.sendDuey(quickDelivery ? 0x1A : 0x8, loadPackages(c.getPlayer())));
if (quickDelivery) {
c.announce(MaplePacketCreator.sendDuey(0x1A, null));
} else {
c.announce(MaplePacketCreator.sendDuey(0x8, loadPackages(c.getPlayer())));
}
} finally {
c.releaseClient();
}
@@ -523,7 +519,7 @@ public class DueyProcessor {
}
public static void dueyCreatePackage(Item item, int mesos, String sender, int recipientCid) {
int packageId = createPackage(mesos, "", sender, recipientCid, false);
int packageId = createPackage(mesos, null, sender, recipientCid, false);
if (packageId != -1) {
insertPackageItem(packageId, item);
}
+37
View File
@@ -7,9 +7,16 @@ import java.util.HashMap;
import java.util.Map;
import client.MapleJob;
import constants.skills.Aran;
import java.io.File;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;
import provider.MapleData;
import provider.MapleDataDirectoryEntry;
import provider.MapleDataFileEntry;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import server.maps.MapleMap;
import server.maps.FieldLimit;
import server.quest.MapleQuest;
@@ -38,6 +45,8 @@ public class GameConstants {
public static final MapleDisease[] CPQ_DISEASES = {MapleDisease.SLOW, MapleDisease.SEDUCE, MapleDisease.STUN, MapleDisease.POISON,
MapleDisease.SEAL, MapleDisease.DARKNESS, MapleDisease.WEAKEN, MapleDisease.CURSE};
public static final int MAX_FIELD_MOB_DAMAGE = getMaxObstacleMobDamageFromWz() * 2;
public static int getPlayerBonusDropRate(int slot) {
return(DROP_RATE_GAIN[slot]);
}
@@ -672,4 +681,32 @@ public class GameConstants {
return 0.0f;
}
}
private static int getMaxObstacleMobDamageFromWz() {
MapleDataProvider mapSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz"));
int maxMobDmg = 0;
MapleDataDirectoryEntry root = mapSource.getRoot();
for (MapleDataDirectoryEntry objData : root.getSubdirectories()) {
if (!objData.getName().contentEquals("Obj")) {
continue;
}
for (MapleDataFileEntry obj : objData.getFiles()) {
for (MapleData l0 : mapSource.getData(objData.getName() + "/" + obj.getName()).getChildren()) {
for (MapleData l1 : l0.getChildren()) {
for (MapleData l2 : l1.getChildren()) {
int objDmg = MapleDataTool.getIntConvert("s1/mobdamage", l2, 0);
if (maxMobDmg < objDmg) {
maxMobDmg = objDmg;
}
}
}
}
}
}
return maxMobDmg;
}
}
+1
View File
@@ -80,6 +80,7 @@ public class ServerConstants {
public static final boolean USE_PARTY_FOR_STARTERS = true; //Players level 10 or below can create/invite other players on the given level range.
public static final boolean USE_AUTOASSIGN_STARTERS_AP = false; //Beginners level 10 or below have their AP autoassigned (they can't choose to levelup a stat). Set true ONLY if the localhost doesn't support AP assigning for beginners level 10 or below.
public static final boolean USE_AUTOASSIGN_SECONDARY_CAP = true;//Prevents AP autoassign from spending on secondary stats after the player class' cap (defined on the autoassign handler) has been reached.
public static final boolean USE_STARTING_AP_4 = true; //Use early-GMS 4/4/4/4 starting stats. To overcome AP shortage, this gives 4AP/5AP at 1st/2nd job advancements.
public static final boolean USE_AUTOBAN = false; //Commands the server to detect infractors automatically.
public static final boolean USE_AUTOBAN_LOG = true; //Log autoban related messages. Still logs even with USE_AUTOBAN disabled.
public static final boolean USE_AUTOSAVE = true; //Enables server autosaving feature (saves characters to DB each 1 hour).
+1
View File
@@ -245,6 +245,7 @@ public final class PacketProcessor {
registerHandler(RecvOpcode.PLAYER_MAP_TRANSFER, new PlayerMapTransitionHandler());
registerHandler(RecvOpcode.USE_MAPLELIFE, new UseMapleLifeHandler());
registerHandler(RecvOpcode.USE_CATCH_ITEM, new UseCatchItemHandler());
registerHandler(RecvOpcode.FIELD_DAMAGE_MOB, new FieldDamageMobHandler());
registerHandler(RecvOpcode.MOB_DAMAGE_MOB_FRIENDLY, new MobDamageMobFriendlyHandler());
registerHandler(RecvOpcode.PARTY_SEARCH_REGISTER, new PartySearchRegisterHandler());
registerHandler(RecvOpcode.PARTY_SEARCH_START, new PartySearchStartHandler());
+1
View File
@@ -170,6 +170,7 @@ public enum RecvOpcode {
MOVE_DRAGON(0xB5),
MOVE_LIFE(0xBC),
AUTO_AGGRO(0xBD),
FIELD_DAMAGE_MOB(0xBF),
MOB_DAMAGE_MOB_FRIENDLY(0xC0),
MONSTER_BOMB(0xC1),
MOB_DAMAGE_MOB(0xC2),
+3 -3
View File
@@ -1868,12 +1868,12 @@ public class Server {
}
}
resetServerWorlds();
ThreadManager.getInstance().stop();
TimerManager.getInstance().purge();
TimerManager.getInstance().stop();
resetServerWorlds();
System.out.println("Worlds + Channels are offline.");
acceptor.unbind();
acceptor = null;
@@ -321,8 +321,9 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
Skill steal = SkillFactory.getSkill(Bandit.STEAL);
if (monster.getStolen().size() < 1) { // One steal per mob <3
if (steal.getEffect(player.getSkillLevel(steal)).makeChanceResult()) {
MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance();
monster.addStolen(0);
MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance();
List<Integer> dropPool = mi.retrieveDropPool(monster.getId());
if(!dropPool.isEmpty()) {
Integer rndPool = (int) Math.floor(Math.random() * dropPool.get(dropPool.size() - 1));
@@ -441,11 +442,13 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
} else {
mortalBlow = SkillFactory.getSkill(Sniper.MORTAL_BLOW);
}
if (player.getSkillLevel(mortalBlow) > 0) {
MapleStatEffect mortal = mortalBlow.getEffect(player.getSkillLevel(mortalBlow));
int skillLevel = player.getSkillLevel(mortalBlow);
if (skillLevel > 0) {
MapleStatEffect mortal = mortalBlow.getEffect(skillLevel);
if (monster.getHp() <= (monster.getStats().getHp() * mortal.getX()) / 100) {
if (Randomizer.rand(1, 100) <= mortal.getY()) {
monster.getMap().killMonster(monster, player, true);
map.damageMonster(player, monster, Integer.MAX_VALUE); // thanks Conrad for noticing reduced EXP gain from skill kill
}
}
}
@@ -40,12 +40,10 @@ public class CashShopSurpriseHandler extends AbstractMaplePacketHandler {
Pair<Item, Item> cssResult = cs.openCashShopSurprise();
if(cssResult != null) {
//Item cssItem = cssResult.getLeft(), cssBox = cssResult.getRight();
//c.announce(MaplePacketCreator.onCashGachaponOpenSuccess(c.getAccID(), cssBox.getSN(), cssBox.getQuantity(), cssItem, cssItem.getItemId(), cssItem.getQuantity(), true));
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xA4));
Item cssItem = cssResult.getLeft(), cssBox = cssResult.getRight();
c.announce(MaplePacketCreator.onCashGachaponOpenSuccess(c.getAccID(), cssBox.getSN(), cssBox.getQuantity(), cssItem, cssItem.getItemId(), cssItem.getQuantity(), true));
} else {
//c.announce(MaplePacketCreator.onCashItemGachaponOpenFailed());
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0x00));
c.announce(MaplePacketCreator.onCashItemGachaponOpenFailed());
}
}
}
@@ -37,16 +37,18 @@ public final class DueyHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.enableActions());
return;
}
byte operation = slea.readByte();
if (operation == DueyProcessor.Actions.TOSERVER_SEND_ITEM.getCode()) {
if (operation == DueyProcessor.Actions.TOSERVER_RECV_ITEM.getCode()) { // on click 'O' Button, thanks inhyuk
DueyProcessor.dueySendTalk(c, false);
} else if (operation == DueyProcessor.Actions.TOSERVER_SEND_ITEM.getCode()) {
byte inventId = slea.readByte();
short itemPos = slea.readShort();
short amount = slea.readShort();
int mesos = slea.readInt();
String recipient = slea.readMapleAsciiString();
boolean quick = slea.readByte() != 0;
String message = quick ? slea.readMapleAsciiString() : "";
String message = quick ? slea.readMapleAsciiString() : null;
DueyProcessor.dueySendItem(c, inventId, itemPos, amount, mesos, message, recipient, quick);
} else if (operation == DueyProcessor.Actions.TOSERVER_REMOVE_PACKAGE.getCode()) {
@@ -0,0 +1,59 @@
/*
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.channel.handlers;
import client.MapleCharacter;
import client.MapleClient;
import constants.GameConstants;
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;
public class FieldDamageMobHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
int mobOid = slea.readInt(); // packet structure found thanks to Darter (Rajan)
int dmg = slea.readInt();
MapleCharacter chr = c.getPlayer();
MapleMap map = chr.getMap();
if (map.getEnvironment().isEmpty()) { // no environment objects activated to actually hit the mob
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use an obstacle on mapid " + map.getId() + " to attack.");
return;
}
MapleMonster mob = map.getMonsterByOid(mobOid);
if (mob != null) {
if (dmg < 0 || dmg > GameConstants.MAX_FIELD_MOB_DAMAGE) {
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use an obstacle on mapid " + map.getId() + " to attack " + MapleMonsterInformationProvider.getInstance().getMobNameFromId(mob.getId()) + " with damage " + dmg);
return;
}
map.broadcastMessage(chr, MaplePacketCreator.damageMonster(mobOid, dmg), true);
map.damageMonster(chr, mob, dmg);
}
}
}
@@ -25,10 +25,9 @@ import client.MapleCharacter;
import client.MapleClient;
import client.autoban.AutobanFactory;
import client.autoban.AutobanManager;
import constants.GameConstants;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import server.maps.MapleMapFactory;
import server.maps.MapleMap;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.MaplePacketCreator;
@@ -47,7 +46,8 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler {
abm.setTimestamp(8, timestamp, 28); // thanks Vcoc & Thora for pointing out d/c happening here
if ((abm.getLastSpam(0) + 1500) > timestamp) AutobanFactory.FAST_HP_HEALING.addPoint(abm, "Fast hp healing");
int abHeal = (int)(77 * MapleMapFactory.getMapRecoveryRate(chr.getMapId()) * 1.5); // thanks Ari for noticing players not getting healed in sauna in certain cases
MapleMap map = chr.getMap();
int abHeal = (int)(77 * map.getRecovery() * 1.5); // thanks Ari for noticing players not getting healed in sauna in certain cases
if (healHP > abHeal) {
AutobanFactory.HIGH_HP_HEALING.autoban(chr, "Healing: " + healHP + "; Max is " + abHeal + ".");
return;
@@ -60,7 +60,10 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler {
short healMP = slea.readShort();
if (healMP != 0 && healMP < 1000) {
abm.setTimestamp(9, timestamp, 28);
if ((abm.getLastSpam(1) + 1500) > timestamp) AutobanFactory.FAST_MP_HEALING.addPoint(abm, "Fast mp healing");
if ((abm.getLastSpam(1) + 1500) > timestamp) {
AutobanFactory.FAST_MP_HEALING.addPoint(abm, "Fast mp healing");
return; // thanks resinate for noticing mp being gained even after detection
}
chr.addMP(healMP);
abm.spam(1, timestamp);
}
@@ -27,8 +27,12 @@ import java.sql.SQLException;
import java.util.Arrays;
import client.MapleClient;
import constants.GameConstants;
import java.awt.Point;
import net.AbstractMaplePacketHandler;
import server.MaplePortal;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
import server.maps.MaplePlayerShop;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -40,7 +44,34 @@ public final class HiredMerchantRequest extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
if (chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.HIRED_MERCHANT)).isEmpty() && (GameConstants.isFreeMarketRoom(chr.getMapId()))) {
try {
for (MapleMapObject mmo : chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.HIRED_MERCHANT, MapleMapObjectType.PLAYER))) {
if (mmo instanceof MapleCharacter) {
MapleCharacter mc = (MapleCharacter) mmo;
MaplePlayerShop shop = mc.getPlayerShop();
if (shop != null && shop.isOwner(mc)) {
chr.announce(MaplePacketCreator.getMiniRoomError(13));
return;
}
} else {
chr.announce(MaplePacketCreator.getMiniRoomError(13));
return;
}
}
Point cpos = chr.getPosition();
MaplePortal portal = chr.getMap().findClosestTeleportPortal(cpos);
if (portal != null && portal.getPosition().distance(cpos) < 120.0) {
chr.announce(MaplePacketCreator.getMiniRoomError(10));
return;
}
} catch (Exception e) {
e.printStackTrace();
}
if (GameConstants.isFreeMarketRoom(chr.getMapId())) {
if (!chr.hasMerchant()) {
try {
if (ItemFactory.MERCHANT.loadItems(chr.getId(), false).isEmpty() && chr.getMerchantMeso() == 0) {
@@ -30,6 +30,10 @@ import tools.data.output.MaplePacketLittleEndianWriter;
public final class NPCAnimationHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
if (c.getPlayer().isChangingMaps()) { // possible cause of error 38 in some map transition scenarios, thanks Arnah
return;
}
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
int length = (int) slea.available();
if (length == 6) { // NPC Talk
@@ -34,11 +34,13 @@ import constants.ServerConstants;
import net.AbstractMaplePacketHandler;
import server.MapleItemInformationProvider;
import server.MaplePortal;
import server.MapleTrade;
import constants.GameConstants;
import server.maps.FieldLimit;
import server.maps.MapleHiredMerchant;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
import server.maps.MapleMiniGame;
import server.maps.MapleMiniGame.MiniGameType;
import server.maps.MaplePlayerShop;
@@ -49,6 +51,7 @@ import tools.data.input.SeekableLittleEndianAccessor;
import java.awt.Point;
import java.sql.SQLException;
import java.util.Arrays;
/**
*
@@ -237,13 +240,9 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
return;
}
try {
Point cpos = chr.getPosition();
if (chr.getMap().findClosestWarpPortal(cpos).getPosition().distance(cpos) < 120.0) {
chr.getClient().announce(MaplePacketCreator.getMiniRoomError(10));
return;
}
} catch (NullPointerException npe) {}
if (!canPlaceStore(chr)) {
return;
}
String desc = slea.readMapleAsciiString();
slea.skip(3);
@@ -362,6 +361,10 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.hiredMerchantOwnerMaintenanceLeave());
}
if (!canPlaceStore(chr)) { // thanks Ari for noticing player shops overlapping on opening time
return;
}
MaplePlayerShop shop = chr.getPlayerShop();
MapleHiredMerchant merchant = chr.getHiredMerchant();
@@ -797,4 +800,37 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
return false;
}
private static boolean canPlaceStore(MapleCharacter chr) {
try {
for (MapleMapObject mmo : chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.HIRED_MERCHANT, MapleMapObjectType.PLAYER))) {
if (mmo instanceof MapleCharacter) {
MapleCharacter mc = (MapleCharacter) mmo;
if (mc.getId() == chr.getId()) {
continue;
}
MaplePlayerShop shop = mc.getPlayerShop();
if (shop != null && shop.isOwner(mc)) {
chr.announce(MaplePacketCreator.getMiniRoomError(13));
return false;
}
} else {
chr.announce(MaplePacketCreator.getMiniRoomError(13));
return false;
}
}
Point cpos = chr.getPosition();
MaplePortal portal = chr.getMap().findClosestTeleportPortal(cpos);
if (portal != null && portal.getPosition().distance(cpos) < 120.0) {
chr.announce(MaplePacketCreator.getMiniRoomError(10));
return false;
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
}
@@ -1,10 +1,10 @@
package net.server.channel.handlers;
import client.MapleCharacter.DelayedQuestUpdate;
import client.MapleClient;
import client.MapleQuestStatus;
import net.AbstractMaplePacketHandler;
import server.quest.MapleQuest;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
/**
@@ -25,7 +25,7 @@ public class RaiseUIStateHandler extends AbstractMaplePacketHandler {
quest.forceStart(c.getPlayer(), 22000);
c.getPlayer().updateQuestInfo(quest.getId(), "0");
} else if (mqs.getStatus() == MapleQuestStatus.Status.STARTED) {
c.announce(MaplePacketCreator.updateQuest(mqs, false));
c.getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, mqs, false);
} else {
//c.announce(MaplePacketCreator.updateQuestInfo(mqs.getQuestID(), 22000, "0"));
}
+10 -2
View File
@@ -1,7 +1,9 @@
package net.server.worker;
import net.server.PlayerStorage;
import net.server.Server;
import net.server.channel.Channel;
import server.maps.MapleMapManager;
/**
* @author Resinate
@@ -11,8 +13,14 @@ public class RespawnWorker implements Runnable {
@Override
public void run() {
for (Channel ch : Server.getInstance().getAllChannels()) {
if (!ch.getPlayerStorage().getAllCharacters().isEmpty()) {
ch.getMapFactory().updateMaps();
PlayerStorage ps = ch.getPlayerStorage();
if (ps != null) {
if (!ps.getAllCharacters().isEmpty()) {
MapleMapManager mapManager = ch.getMapFactory();
if (mapManager != null) {
mapManager.updateMaps();
}
}
}
}
}
+3 -2
View File
@@ -53,6 +53,7 @@ import server.partyquest.Pyramid;
import server.quest.MapleQuest;
import tools.MaplePacketCreator;
import client.MapleCharacter;
import client.MapleCharacter.DelayedQuestUpdate;
import client.MapleClient;
import client.MapleQuestStatus;
import client.SkillFactory;
@@ -468,12 +469,12 @@ public class AbstractPlayerInteraction {
public void resetAllQuestProgress(int qid) {
getPlayer().getQuest(MapleQuest.getInstance(qid)).resetAllProgress();
getClient().announce(MaplePacketCreator.updateQuest(getPlayer().getQuest(MapleQuest.getInstance(qid)), false));
getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, getPlayer().getQuest(MapleQuest.getInstance(qid)), false);
}
public void resetQuestProgress(int qid, int pid) {
getPlayer().getQuest(MapleQuest.getInstance(qid)).resetProgress(pid);
getClient().announce(MaplePacketCreator.updateQuest(getPlayer().getQuest(MapleQuest.getInstance(qid)), false));
getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, getPlayer().getQuest(MapleQuest.getInstance(qid)), false);
}
public boolean forceStartQuest(int id) {
+1 -1
View File
@@ -70,7 +70,7 @@ public abstract class AbstractScriptManager {
NashornScriptEngine engine = c.getScriptEngine(cachePath);
if (engine == null) {
engine = getScriptEngine(cachePath);
engine = getScriptEngine(path);
c.setScriptEngine(path, engine);
}
+6
View File
@@ -347,6 +347,12 @@ public class EventManager {
private boolean startLobbyInstance(int lobbyId) {
lobbyLock.lock();
try {
if (lobbyId < 0) {
lobbyId = 0;
} else if (lobbyId >= maxLobbys) {
lobbyId = maxLobbys - 1;
}
if(!openedLobbys.get(lobbyId)) {
openedLobbys.set(lobbyId, true);
return true;
+3 -2
View File
@@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package scripting.map;
import client.MapleCharacter.DelayedQuestUpdate;
import client.MapleClient;
import client.MapleQuestStatus;
import scripting.AbstractPlayerInteraction;
@@ -99,7 +100,7 @@ public class MapScriptMethods extends AbstractPlayerInteraction {
}
String status = Integer.toString(q.getMedalProgress());
String infoex = quest.getInfoEx();
getPlayer().announce(MaplePacketCreator.updateQuest(q, true));
getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, q, true);
StringBuilder smp = new StringBuilder();
StringBuilder etm = new StringBuilder();
if (status.equals(infoex)) {
@@ -127,7 +128,7 @@ public class MapScriptMethods extends AbstractPlayerInteraction {
return;
}
String status = Integer.toString(q.getMedalProgress());
getPlayer().announce(MaplePacketCreator.updateQuest(q, true));
getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, q, true);
getPlayer().announce(MaplePacketCreator.earnTitleMessage(status + "/5 Completed"));
getPlayer().announce(MaplePacketCreator.earnTitleMessage("The One Who's Touched the Sky title in progress."));
if (Integer.toString(q.getMedalProgress()).equals(quest.getInfoEx())) {
+2
View File
@@ -200,6 +200,8 @@ public class NPCScriptManager extends AbstractScriptManager {
} else {
resetContext(scriptFolder + "/" + cm.getNpc() + ".js", c);
}
c.getPlayer().flushDelayedUpdateQuests();
}
public void dispose(MapleClient c) {
@@ -163,6 +163,7 @@ public class QuestScriptManager extends AbstractScriptManager {
scripts.remove(c);
c.getPlayer().setNpcCooldown(System.currentTimeMillis());
resetContext("quest/" + qm.getQuest() + ".js", c);
c.getPlayer().flushDelayedUpdateQuests();
}
public void dispose(MapleClient c) {
+60 -30
View File
@@ -30,6 +30,7 @@ import constants.ItemConstants;
import constants.ServerConstants;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.logging.Level;
@@ -57,24 +58,70 @@ import tools.MaplePacketCreator;
*/
public class ReactorActionManager extends AbstractPlayerInteraction {
private MapleReactor reactor;
private MapleClient client;
private Invocable iv;
private ScheduledFuture<?> sprayTask = null;
public ReactorActionManager(MapleClient c, MapleReactor reactor, Invocable iv) {
super(c);
this.reactor = reactor;
this.client = c;
this.iv = iv;
}
public void hitReactor() {
reactor.hitReactor(client);
reactor.hitReactor(c);
}
public void destroyNpc(int npcId) {
reactor.getMap().destroyNPC(npcId);
}
private static void sortDropEntries(List<ReactorDropEntry> from, List<ReactorDropEntry> item, List<ReactorDropEntry> visibleQuest, List<ReactorDropEntry> otherQuest, MapleCharacter chr) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
for(ReactorDropEntry mde : from) {
if(!ii.isQuestItem(mde.itemId)) {
item.add(mde);
} else {
if(chr.needQuestItem(mde.questid, mde.itemId)) {
visibleQuest.add(mde);
} else {
otherQuest.add(mde);
}
}
}
}
private static List<ReactorDropEntry> assembleReactorDropEntries(MapleCharacter chr, List<ReactorDropEntry> items) {
final List<ReactorDropEntry> dropEntry = new ArrayList<>();
final List<ReactorDropEntry> visibleQuestEntry = new ArrayList<>();
final List<ReactorDropEntry> otherQuestEntry = new ArrayList<>();
sortDropEntries(items, dropEntry, visibleQuestEntry, otherQuestEntry, chr);
Collections.shuffle(dropEntry);
Collections.shuffle(visibleQuestEntry);
Collections.shuffle(otherQuestEntry);
items.clear();
items.addAll(dropEntry);
items.addAll(visibleQuestEntry);
items.addAll(otherQuestEntry);
List<ReactorDropEntry> items1 = new ArrayList<>(items.size());
List<ReactorDropEntry> items2 = new ArrayList<>(items.size() / 2);
for (int i = 0; i < items.size(); i++) {
if (i % 2 == 0) {
items1.add(items.get(i));
} else {
items2.add(items.get(i));
}
}
Collections.reverse(items1);
items1.addAll(items2);
return items1;
}
public void sprayItems() {
sprayItems(false, 0, 0, 0, 0);
@@ -109,10 +156,10 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
}
public void dropItems(boolean delayed, int posX, int posY, boolean meso, int mesoChance, final int minMeso, final int maxMeso, int minItems) {
if(c.getPlayer() == null) return;
List<ReactorDropEntry> items = generateDropList(getDropChances(), c.getPlayer().getDropRate(), meso, mesoChance, minItems);
MapleCharacter chr = c.getPlayer();
if(chr == null) return;
List<ReactorDropEntry> items = assembleReactorDropEntries(chr, generateDropList(getDropChances(), chr.getDropRate(), meso, mesoChance, minItems));
if(items.size() % 2 == 0) posX -= 12;
final Point dropPos = new Point(posX, posY);
@@ -127,8 +174,8 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
if (d.itemId == 0) {
int range = maxMeso - minMeso;
int displayDrop = (int) (Math.random() * range) + minMeso;
int mesoDrop = (displayDrop * client.getWorldServer().getMesoRate());
reactor.getMap().spawnMesoDrop(mesoDrop, reactor.getMap().calcDropPos(dropPos, reactor.getPosition()), reactor, client.getPlayer(), false, (byte) 2);
int mesoDrop = (displayDrop * c.getWorldServer().getMesoRate());
reactor.getMap().spawnMesoDrop(mesoDrop, reactor.getMap().calcDropPos(dropPos, reactor.getPosition()), reactor, c.getPlayer(), false, (byte) 2);
} else {
Item drop;
@@ -142,10 +189,9 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
}
}
} else {
final MapleCharacter chr = client.getPlayer();
final MapleReactor r = reactor;
final List<ReactorDropEntry> dropItems = items;
final int worldMesoRate = client.getWorldServer().getMesoRate();
final int worldMesoRate = c.getWorldServer().getMesoRate();
dropPos.x -= (12 * items.size());
@@ -178,7 +224,7 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
dropPos.x += 25;
}
}, 100);
}, 200);
}
}
@@ -187,37 +233,21 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
}
private List<ReactorDropEntry> generateDropList(List<ReactorDropEntry> drops, int dropRate, boolean meso, int mesoChance, int minItems) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
List<ReactorDropEntry> items = new ArrayList<>();
List<ReactorDropEntry> questItems = new ArrayList<>();
int numItems = 0;
if (meso && Math.random() < (1 / (double) mesoChance)) {
items.add(new ReactorDropEntry(0, mesoChance, -1));
}
for(ReactorDropEntry mde : drops) {
if (Math.random() < (dropRate / (double) mde.chance)) {
if(!ii.isQuestItem(mde.itemId)) {
items.add(mde);
} else {
questItems.add(mde);
}
numItems++;
items.add(mde);
}
}
while (numItems < minItems) {
while (items.size() < minItems) {
items.add(new ReactorDropEntry(0, mesoChance, -1));
numItems++;
}
java.util.Collections.shuffle(items);
java.util.Collections.shuffle(questItems);
items.addAll(questItems);
return items;
}
@@ -226,7 +256,7 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
}
public void createMapMonitor(int mapId, String portal) {
new MapMonitor(client.getChannelServer().getMapFactory().getMap(mapId), portal);
new MapMonitor(c.getChannelServer().getMapFactory().getMap(mapId), portal);
}
public void spawnMonster(int id, int qty) {
+5 -5
View File
@@ -157,7 +157,7 @@ public class CashShop {
private static final Map<Integer, CashItem> items = new HashMap<>();
private static final Map<Integer, List<Integer>> packages = new HashMap<>();
private static final List<SpecialCashItem> specialcashitems = new ArrayList<>();
private static final List<Integer> randomitemids = new ArrayList<>();
private static final List<Integer> randomitemsns = new ArrayList<>();
static {
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz"));
@@ -184,7 +184,7 @@ public class CashShop {
for(Entry<Integer, CashItem> e : items.entrySet()) {
if(e.getValue().isOnSale()) {
randomitemids.add(e.getKey());
randomitemsns.add(e.getKey());
}
}
@@ -212,10 +212,10 @@ public class CashShop {
}
public static CashItem getRandomCashItem() {
if(randomitemids.isEmpty()) return null;
if(randomitemsns.isEmpty()) return null;
int rnd = (int)(Math.random() * randomitemids.size());
return items.get(randomitemids.get(rnd));
int rnd = (int)(Math.random() * randomitemsns.size());
return items.get(randomitemsns.get(rnd));
}
public static CashItem getItem(int sn) {
+8 -6
View File
@@ -29,7 +29,7 @@ public class DueyPackage {
private String sender = null;
private Item item = null;
private int mesos = 0;
private String message = "";
private String message = null;
private Calendar timestamp;
private int packageId = 0;
@@ -96,13 +96,15 @@ public class DueyPackage {
}
}
public void setSentTime(Timestamp ts) {
public void setSentTime(Timestamp ts, boolean quick) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(ts.getTime());
cal.set(Calendar.HOUR, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
if (quick) {
if (System.currentTimeMillis() - ts.getTime() < 24 * 60 * 60 * 1000) { // thanks inhyuk for noticing quick delivery packages unavailable to retrieve from the get-go
cal.add(Calendar.DATE, -1);
}
}
this.timestamp = cal;
}
+1
View File
@@ -25,6 +25,7 @@ import java.awt.Point;
import client.MapleClient;
public interface MaplePortal {
public final int TELEPORT_PORTAL = 1;
public final int MAP_PORTAL = 2;
public final int DOOR_PORTAL = 6;
public static boolean OPEN = true;
+6 -21
View File
@@ -1288,6 +1288,7 @@ public class MapleStatEffect {
if (mount != null) {
ridingMountId = mount.getItemId();
}
if (sourceid == Corsair.BATTLE_SHIP) {
ridingMountId = 1932000;
} else if (sourceid == Beginner.SPACESHIP || sourceid == Noblesse.SPACESHIP) {
@@ -1300,28 +1301,12 @@ public class MapleStatEffect {
ridingMountId = 1932005;
} else if (sourceid == Beginner.BALROG_MOUNT || sourceid == Noblesse.BALROG_MOUNT || sourceid == Legend.BALROG_MOUNT) {
ridingMountId = 1932010;
} else {
if (applyto.getMount() == null) {
applyto.mount(ridingMountId, sourceid);
}
applyto.getClient().getWorldServer().registerMountHunger(applyto);
}
if (sourceid == Corsair.BATTLE_SHIP) {
givemount = new MapleMount(applyto, 1932000, sourceid);
} else if (sourceid == Beginner.SPACESHIP || sourceid == Noblesse.SPACESHIP) {
givemount = new MapleMount(applyto, 1932000 + applyto.getSkillLevel(sourceid), sourceid);
} else if (sourceid == Beginner.YETI_MOUNT1 || sourceid == Noblesse.YETI_MOUNT1 || sourceid == Legend.YETI_MOUNT1) {
givemount = new MapleMount(applyto, 1932003, sourceid);
} else if (sourceid == Beginner.YETI_MOUNT2 || sourceid == Noblesse.YETI_MOUNT2 || sourceid == Legend.YETI_MOUNT2) {
givemount = new MapleMount(applyto, 1932004, sourceid);
} else if (sourceid == Beginner.WITCH_BROOMSTICK || sourceid == Noblesse.WITCH_BROOMSTICK || sourceid == Legend.WITCH_BROOMSTICK) {
givemount = new MapleMount(applyto, 1932005, sourceid);
} else if (sourceid == Beginner.BALROG_MOUNT || sourceid == Noblesse.BALROG_MOUNT || sourceid == Legend.BALROG_MOUNT) {
givemount = new MapleMount(applyto, 1932010, sourceid);
} else {
givemount = applyto.getMount();
}
// thanks inhyuk for noticing some skill mounts not acting properly for other players when changing maps
givemount = applyto.mount(ridingMountId, sourceid);
applyto.getClient().getWorldServer().registerMountHunger(applyto);
localDuration = sourceid;
localsourceid = ridingMountId;
localstatups = Collections.singletonList(new Pair<>(MapleBuffStat.MONSTER_RIDING, 0));
@@ -65,12 +65,9 @@ public class MapleExpeditionBossLog {
private static List<Pair<Timestamp, BossLogEntry>> getBossLogResetTimestamps(Calendar timeNow, boolean week) {
List<Pair<Timestamp, BossLogEntry>> resetTimestamps = new LinkedList<>();
Timestamp ts = new Timestamp(timeNow.getTime().getTime()); // reset all table entries actually, thanks Conrad
for (BossLogEntry b : BossLogEntry.values()) {
if (b.week == week) {
Calendar c = (Calendar) timeNow.clone();
c.roll(Calendar.DAY_OF_MONTH, -1 * (week ? 7 : 1) * b.timeLength);
Timestamp ts = new Timestamp(c.getTime().getTime());
resetTimestamps.add(new Pair<>(ts, b));
}
}
+1 -1
View File
@@ -97,7 +97,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
private Set<Integer> usedAttacks = new HashSet<>();
private Set<Integer> calledMobOids = null;
private WeakReference<MapleMonster> callerMob = new WeakReference<>(null);
private List<Integer> stolenItems = new ArrayList<>();
private List<Integer> stolenItems = new ArrayList<>(5);
private int team;
private int parentMobOid = 0;
private final HashMap<Integer, AtomicLong> takenDamage = new HashMap<>();
@@ -152,7 +152,7 @@ public class MapleMonsterInformationProvider {
int rnd = Randomizer.rand(mde.Minimum, mde.Maximum);
for (int i = 0; i < rnd - 1; i++) {
extra.add(mde); // this passes copies of the equips' MDE with min/max quantity > 1, but idc it'll be unused anyways
extra.add(mde); // this passes copies of the equips' MDE with min/max quantity > 1, but idc on equips they are unused anyways
}
}
}
+75 -62
View File
@@ -137,6 +137,7 @@ public class MapleMap {
private int timeLimit;
private long mapTimer;
private int decHP = 0;
private float recovery = 1.0f;
private int protectItem = 0;
private boolean town;
private MapleOxQuiz ox;
@@ -1408,81 +1409,85 @@ public class MapleMap {
}
} else {
if (removeKilledMonsterObject(monster)) {
if (monster.getStats().getLevel() >= chr.getLevel() + 30 && !chr.isGM()) {
AutobanFactory.GENERAL.alert(chr, " for killing a " + monster.getName() + " which is over 30 levels higher.");
}
/*if (chr.getQuest(MapleQuest.getInstance(29400)).getStatus().equals(MapleQuestStatus.Status.STARTED)) {
if (chr.getLevel() >= 120 && monster.getStats().getLevel() >= 120) {
//FIX MEDAL SHET
} else if (monster.getStats().getLevel() >= chr.getLevel()) {
}
}*/
if (monster.getCP() > 0 && chr.getMap().isCPQMap()) {
chr.gainCP(monster.getCP());
}
int buff = monster.getBuffToGive();
if (buff > -1) {
MapleItemInformationProvider mii = MapleItemInformationProvider.getInstance();
for (MapleMapObject mmo : this.getPlayers()) {
MapleCharacter character = (MapleCharacter) mmo;
if (character.isAlive()) {
MapleStatEffect statEffect = mii.getItemEffect(buff);
character.getClient().announce(MaplePacketCreator.showOwnBuffEffect(buff, 1));
broadcastMessage(character, MaplePacketCreator.showBuffeffect(character.getId(), buff, 1), false);
statEffect.applyTo(character);
}
try {
if (monster.getStats().getLevel() >= chr.getLevel() + 30 && !chr.isGM()) {
AutobanFactory.GENERAL.alert(chr, " for killing a " + monster.getName() + " which is over 30 levels higher.");
}
}
if (monster.getId() >= 8800003 && monster.getId() <= 8800010) {
boolean makeZakReal = true;
Collection<MapleMapObject> objects = getMapObjects();
for (MapleMapObject object : objects) {
MapleMonster mons = getMonsterByOid(object.getObjectId());
if (mons != null) {
if (mons.getId() >= 8800003 && mons.getId() <= 8800010) {
makeZakReal = false;
break;
/*if (chr.getQuest(MapleQuest.getInstance(29400)).getStatus().equals(MapleQuestStatus.Status.STARTED)) {
if (chr.getLevel() >= 120 && monster.getStats().getLevel() >= 120) {
//FIX MEDAL SHET
} else if (monster.getStats().getLevel() >= chr.getLevel()) {
}
}*/
if (monster.getCP() > 0 && chr.getMap().isCPQMap()) {
chr.gainCP(monster.getCP());
}
int buff = monster.getBuffToGive();
if (buff > -1) {
MapleItemInformationProvider mii = MapleItemInformationProvider.getInstance();
for (MapleMapObject mmo : this.getPlayers()) {
MapleCharacter character = (MapleCharacter) mmo;
if (character.isAlive()) {
MapleStatEffect statEffect = mii.getItemEffect(buff);
character.getClient().announce(MaplePacketCreator.showOwnBuffEffect(buff, 1));
broadcastMessage(character, MaplePacketCreator.showBuffeffect(character.getId(), buff, 1), false);
statEffect.applyTo(character);
}
}
}
if (makeZakReal) {
MapleMap map = chr.getMap();
if (monster.getId() >= 8800003 && monster.getId() <= 8800010) {
boolean makeZakReal = true;
Collection<MapleMapObject> objects = getMapObjects();
for (MapleMapObject object : objects) {
MapleMonster mons = map.getMonsterByOid(object.getObjectId());
MapleMonster mons = getMonsterByOid(object.getObjectId());
if (mons != null) {
if (mons.getId() == 8800000) {
makeMonsterReal(mons);
mons.aggroUpdateController();
if (mons.getId() >= 8800003 && mons.getId() <= 8800010) {
makeZakReal = false;
break;
}
}
}
}
}
if (makeZakReal) {
MapleMap map = chr.getMap();
MapleCharacter dropOwner = monster.killBy(chr);
if (withDrops && !monster.dropsDisabled()) {
if (dropOwner == null) {
dropOwner = chr;
}
dropFromMonster(dropOwner, monster, false);
}
if (monster.hasBossHPBar()) {
for(MapleCharacter mc : this.getAllPlayers()) {
if(mc.getTargetHpBarHash() == monster.hashCode()) {
mc.resetPlayerAggro();
for (MapleMapObject object : objects) {
MapleMonster mons = map.getMonsterByOid(object.getObjectId());
if (mons != null) {
if (mons.getId() == 8800000) {
makeMonsterReal(mons);
mons.aggroUpdateController();
break;
}
}
}
}
}
}
monster.dispatchMonsterKilled(true);
broadcastMessage(MaplePacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition());
MapleCharacter dropOwner = monster.killBy(chr);
if (withDrops && !monster.dropsDisabled()) {
if (dropOwner == null) {
dropOwner = chr;
}
dropFromMonster(dropOwner, monster, false);
}
if (monster.hasBossHPBar()) {
for(MapleCharacter mc : this.getAllPlayers()) {
if(mc.getTargetHpBarHash() == monster.hashCode()) {
mc.resetPlayerAggro();
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally { // thanks resinate for pointing out a memory leak possibly from an exception thrown
monster.dispatchMonsterKilled(true);
broadcastMessage(MaplePacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition());
}
}
}
}
@@ -2707,12 +2712,12 @@ public class MapleMap {
return portal != null ? portal : getPortal(0);
}
public MaplePortal findClosestWarpPortal(Point from) {
public MaplePortal findClosestTeleportPortal(Point from) {
MaplePortal closest = null;
double shortestDistance = Double.POSITIVE_INFINITY;
for (MaplePortal portal : portals.values()) {
double distance = portal.getPosition().distanceSq(from);
if (portal.getType() == MaplePortal.MAP_PORTAL && distance < shortestDistance && portal.getTargetMapId() == 999999999) {
if (portal.getType() == MaplePortal.TELEPORT_PORTAL && distance < shortestDistance && portal.getTargetMapId() != 999999999) {
closest = portal;
shortestDistance = distance;
}
@@ -3777,6 +3782,14 @@ public class MapleMap {
public void setHPDecProtect(int delta) {
this.protectItem = delta;
}
public float getRecovery() {
return recovery;
}
public void setRecovery(float recRate) {
recovery = recRate;
}
private int hasBoat() {
return !boat ? 0 : (docked ? 1 : 2);
+3 -11
View File
@@ -31,7 +31,6 @@ import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
@@ -49,8 +48,6 @@ import tools.StringUtil;
public class MapleMapFactory {
private static Map<Integer, Float> mapRecoveryRateCache = new HashMap<>();
private static MapleData nameData;
private static MapleDataProvider mapSource;
@@ -336,11 +333,10 @@ public class MapleMapFactory {
map.setTimeLimit(MapleDataTool.getIntConvert("timeLimit", infoData, -1));
map.setFieldType(MapleDataTool.getIntConvert("fieldType", infoData, 0));
map.setMobCapacity(MapleDataTool.getIntConvert("fixedMobCapacity", infoData, 500));//Is there a map that contains more than 500 mobs?
MapleData recData = infoData.getChildByPath("recovery");
if (recData != null) {
float recoveryRate = MapleDataTool.getFloat(recData);
mapRecoveryRateCache.put(mapid, recoveryRate);
map.setRecovery(MapleDataTool.getFloat(recData));
}
HashMap<Integer, Integer> backTypes = new HashMap<>();
@@ -438,9 +434,5 @@ public class MapleMapFactory {
builder.append("/").append(mapid);
return builder.toString();
}
public static float getMapRecoveryRate(int mapid) {
Float recRate = mapRecoveryRateCache.get(mapid);
return recRate != null ? recRate : 1.0f;
}
}
-3
View File
@@ -139,7 +139,4 @@ public class MapleMapManager {
this.event = null;
}
public static float getMapRecoveryRate(int mapid) {
return MapleMapFactory.getMapRecoveryRate(mapid);
}
}
+2 -1
View File
@@ -28,6 +28,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import client.MapleCharacter;
import client.MapleCharacter.DelayedQuestUpdate;
import client.MapleQuestStatus;
import client.MapleQuestStatus.Status;
import constants.ServerConstants;
@@ -337,7 +338,7 @@ public class MapleQuest {
if(pid >= 8200000 && pid <= 8200012) {
String pr = StringUtil.getLeftPaddedStr(Integer.toString(setProg), '0', 3);
newStatus.setProgress(pid, pr);
c.announce(MaplePacketCreator.updateQuest(newStatus, false));
c.announceUpdateQuest(DelayedQuestUpdate.UPDATE, newStatus, false);
}
}
}
+29 -6
View File
@@ -109,6 +109,7 @@ public class ItemAction extends MapleQuestAction {
if (!canGetItem(iEntry, chr)) {
continue;
}
if(iEntry.getProp() != null) {
if(iEntry.getProp() == -1) {
if(extSelection != extNum++)
@@ -181,11 +182,11 @@ public class ItemAction extends MapleQuestAction {
}
} else {
if(item.getCount() > 0) {
// Make sure they can hold the item.
Item toItem = new Item(item.getId(), (short) 0, (short) item.getCount());
gainList.add(new Pair<>(toItem, type));
} else {
// Make sure they can hold the item.
Item toItem = new Item(item.getId(), (short) 0, (short) item.getCount());
gainList.add(new Pair<>(toItem, type));
if(item.getCount() < 0) {
// Make sure they actually have the item.
int quantity = item.getCount() * -1;
@@ -229,12 +230,34 @@ public class ItemAction extends MapleQuestAction {
gainList.add(selected);
}
if (!MapleInventory.checkSpots(chr, gainList, allSlotUsed, false)) {
if (!canHold(chr, gainList)) {
chr.dropMessage(1, "Please check if you have enough space in your inventory.");
return false;
}
return true;
}
private boolean canHold(MapleCharacter chr, List<Pair<Item, MapleInventoryType>> gainList) {
List<Integer> toAddItemids = new LinkedList<>();
List<Integer> toAddQuantity = new LinkedList<>();
List<Integer> toRemoveItemids = new LinkedList<>();
List<Integer> toRemoveQuantity = new LinkedList<>();
for (Pair<Item, MapleInventoryType> item : gainList) {
Item it = item.getLeft();
if (it.getQuantity() > 0) {
toAddItemids.add(it.getItemId());
toAddQuantity.add((int) it.getQuantity());
} else {
toRemoveItemids.add(it.getItemId());
toRemoveQuantity.add(-1 * ((int) it.getQuantity()));
}
}
// thanks onechord for noticing quests unnecessarily giving out "full inventory" from quests that also takes items from players
return chr.getClient().getAbstractPlayerInteraction().canHoldAllAfterRemoving(toAddItemids, toAddQuantity, toRemoveItemids, toRemoveQuantity);
}
private boolean canGetItem(ItemData item, MapleCharacter chr) {
if (item.getGender() != 2 && item.getGender() != chr.getGender()) {
+15 -20
View File
@@ -1916,17 +1916,12 @@ public class MaplePacketCreator {
Integer bv = chr.getBuffedValue(MapleBuffStat.MONSTER_RIDING);
if (bv != null) {
if(bv.equals(Corsair.BATTLE_SHIP)) {
mplew.writeInt(1932000);
mplew.writeInt(Corsair.BATTLE_SHIP);
MapleMount mount = chr.getMount();
if (mount != null) {
mplew.writeInt(mount.getItemId());
mplew.writeInt(mount.getSkillId());
} else {
final Item mount = chr.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -18);
if(mount != null) {
mplew.writeInt(mount.getItemId());
mplew.writeInt(1004);
} else {
mplew.writeLong(0);
}
mplew.writeLong(0);
}
} else {
mplew.writeLong(0);
@@ -6975,14 +6970,14 @@ public class MaplePacketCreator {
public static byte[] onCashItemGachaponOpenFailed(){
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.CASHSHOP_CASH_ITEM_GACHAPON_RESULT.getValue());
mplew.write(189);
mplew.write(0xE4);
return mplew.getPacket();
}
public static byte[] onCashGachaponOpenSuccess(int accountid, long sn, int remainingBoxes, Item item, int itemid, int nSelectedItemCount, boolean bJackpot){
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.CASHSHOP_CASH_ITEM_GACHAPON_RESULT.getValue());
mplew.write(190);
mplew.write(0xE5); // subopcode thanks to Ubaware
mplew.writeLong(sn);// sn of the box used
mplew.writeInt(remainingBoxes);
addCashItemInformation(mplew, item, accountid);
@@ -7253,15 +7248,15 @@ public class MaplePacketCreator {
mplew.writeLong(getTime(dp.sentTimeInMilliseconds()));
String msg = dp.getMessage();
if (!msg.isEmpty()) {
mplew.writeInt(1);
mplew.writeAsciiString(msg);
for (int i = msg.length(); i < 200; i++) {
mplew.write(0);
}
if (msg != null) {
mplew.writeInt(1);
mplew.writeAsciiString(msg);
for (int i = msg.length(); i < 200; i++) {
mplew.write(0);
}
} else {
mplew.writeInt(0);
mplew.skip(200);
mplew.writeInt(0);
mplew.skip(200);
}
mplew.write(0);