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:
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
package client;
|
||||
|
||||
import server.minigame.MapleRockPaperScissor;
|
||||
import java.awt.Point;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.sql.Connection;
|
||||
@@ -94,11 +95,10 @@ import server.maps.MapleMapObject;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import server.maps.MapleMiniGame;
|
||||
import server.maps.MapleMiniGame.MiniGameResult;
|
||||
import server.life.MaplePlayerNPC;
|
||||
import server.maps.MaplePlayerShop;
|
||||
import server.maps.MaplePlayerShopItem;
|
||||
import server.maps.MapleSummon;
|
||||
import server.life.MaplePlayerNPC;
|
||||
import server.life.MonsterDropEntry;
|
||||
import server.maps.SavedLocation;
|
||||
import server.maps.SavedLocationType;
|
||||
import server.partyquest.AriantColiseum;
|
||||
@@ -221,7 +221,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
private AtomicInteger exp = new AtomicInteger();
|
||||
private AtomicInteger gachaexp = new AtomicInteger();
|
||||
private AtomicInteger meso = new AtomicInteger();
|
||||
private AtomicInteger chair = new AtomicInteger();
|
||||
private AtomicInteger chair = new AtomicInteger(-1);
|
||||
private int merchantmeso;
|
||||
private BuddyList buddylist;
|
||||
private EventInstanceManager eventInstance = null;
|
||||
@@ -233,6 +233,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
private MapleJob job = MapleJob.BEGINNER;
|
||||
private MapleMessenger messenger = null;
|
||||
private MapleMiniGame miniGame;
|
||||
private MapleRockPaperScissor rps;
|
||||
private MapleMount maplemount;
|
||||
private MapleParty party;
|
||||
private MaplePet[] pets = new MaplePet[3];
|
||||
@@ -248,6 +249,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
private SkillMacro[] skillMacros = new SkillMacro[5];
|
||||
private List<Integer> lastmonthfameids;
|
||||
private List<WeakReference<MapleMap>> lastVisitedMaps = new LinkedList<>();
|
||||
private WeakReference<MapleMap> ownedMap = new WeakReference<>(null);
|
||||
private final Map<Short, MapleQuestStatus> quests;
|
||||
private Set<MapleMonster> controlled = new LinkedHashSet<>();
|
||||
private Map<Integer, String> entered = new LinkedHashMap<>();
|
||||
@@ -826,6 +828,26 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
return maxbasedamage;
|
||||
}
|
||||
|
||||
public int calculateMaxBaseMagicDamage() {
|
||||
int maxbasedamage = getTotalMagic();
|
||||
int totalint = getTotalInt();
|
||||
|
||||
if (totalint > 2000) {
|
||||
maxbasedamage -= 2000;
|
||||
maxbasedamage += (int) ((0.09033024267 * totalint) + 3823.8038);
|
||||
} else {
|
||||
maxbasedamage -= totalint;
|
||||
|
||||
if (totalint > 1700) {
|
||||
maxbasedamage += (int) (0.1996049769 * Math.pow(totalint, 1.300631341));
|
||||
} else {
|
||||
maxbasedamage += (int) (0.1996049769 * Math.pow(totalint, 1.290631341));
|
||||
}
|
||||
}
|
||||
|
||||
return (maxbasedamage * 107) / 100;
|
||||
}
|
||||
|
||||
public void setCombo(short count) {
|
||||
if (count < combocounter) {
|
||||
@@ -1664,6 +1686,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
petLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void setOwnedMap(MapleMap map) {
|
||||
ownedMap = new WeakReference<>(map);
|
||||
}
|
||||
|
||||
public MapleMap getOwnedMap() {
|
||||
return ownedMap.get();
|
||||
}
|
||||
|
||||
public void notifyMapTransferToPartner(int mapid) {
|
||||
if(partnerId > 0) {
|
||||
@@ -2435,7 +2465,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
private void startChairTask() {
|
||||
if (chair.get() == 0) {
|
||||
if (chair.get() < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2644,8 +2674,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
chrLock.unlock();
|
||||
}
|
||||
|
||||
if (disease == MapleDisease.SEDUCE && chair.get() != 0) {
|
||||
sitChair(0);
|
||||
if (disease == MapleDisease.SEDUCE && chair.get() < 0) {
|
||||
sitChair(-1);
|
||||
}
|
||||
|
||||
final List<Pair<MapleDisease, Integer>> debuff = Collections.singletonList(new Pair<>(disease, Integer.valueOf(skill.getX())));
|
||||
@@ -5080,7 +5110,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
public int getTotalMagic() {
|
||||
return localmagic;
|
||||
}
|
||||
|
||||
|
||||
public int getTotalWatk() {
|
||||
return localwatk;
|
||||
}
|
||||
@@ -5339,6 +5369,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
return playerShop;
|
||||
}
|
||||
|
||||
public MapleRockPaperScissor getRPS() { // thanks inhyuk for suggesting RPS addition
|
||||
return rps;
|
||||
}
|
||||
|
||||
public void setGMLevel(int level) {
|
||||
this.gmLevel = Math.min(level, 6);
|
||||
this.gmLevel = Math.max(level, 0);
|
||||
@@ -5356,6 +5390,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
closeTrade();
|
||||
closePlayerShop();
|
||||
closeMiniGame(true);
|
||||
closeRPS();
|
||||
closeHiredMerchant(false);
|
||||
closePlayerMessenger();
|
||||
|
||||
@@ -5512,23 +5547,19 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
|
||||
public final byte getQuestStatus(final int quest) {
|
||||
synchronized (quests) {
|
||||
for (final MapleQuestStatus q : quests.values()) {
|
||||
if (q.getQuest().getId() == quest) {
|
||||
return (byte) q.getStatus().getId();
|
||||
}
|
||||
MapleQuestStatus mqs = quests.get((short) quest);
|
||||
if (mqs != null) {
|
||||
return (byte) mqs.getStatus().getId();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public final MapleQuestStatus getMapleQuestStatus(final int quest) {
|
||||
synchronized (quests) {
|
||||
for (final MapleQuestStatus q : quests.values()) {
|
||||
if (q.getQuest().getId() == quest) {
|
||||
return q;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
MapleQuestStatus mqs = quests.get((short) quest);
|
||||
return mqs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6085,15 +6116,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
public void leaveMap() {
|
||||
releaseControlledMonsters();
|
||||
visibleMapObjects.clear();
|
||||
setChair(0);
|
||||
setChair(-1);
|
||||
if (hpDecreaseTask != null) {
|
||||
hpDecreaseTask.cancel(false);
|
||||
}
|
||||
|
||||
if (map.unclaimOwnership(this)) {
|
||||
map.dropMessage(5, "This lawn is now free real estate.");
|
||||
}
|
||||
|
||||
AriantColiseum arena = this.getAriantColiseum();
|
||||
if (arena != null) {
|
||||
arena.leaveArena(this);
|
||||
@@ -7411,12 +7438,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
|
||||
private void unsitChairInternal() {
|
||||
int chairid = chair.get();
|
||||
if (chairid != 0) {
|
||||
if (chairid >= 0) {
|
||||
if (ItemConstants.isFishingChair(chairid)) {
|
||||
this.getWorldServer().unregisterFisherPlayer(this);
|
||||
}
|
||||
|
||||
setChair(0);
|
||||
setChair(-1);
|
||||
if (unregisterChairBuff()) {
|
||||
getMap().broadcastMessage(this, MaplePacketCreator.cancelForeignChairSkillEffect(this.getId()), false);
|
||||
}
|
||||
@@ -7430,22 +7457,24 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
public void sitChair(int itemId) {
|
||||
if (client.tryacquireClient()) {
|
||||
try {
|
||||
if (itemId >= 1000000) { // sit on item chair
|
||||
if (chair.get() == 0) {
|
||||
setChair(itemId);
|
||||
getMap().broadcastMessage(this, MaplePacketCreator.showChair(this.getId(), itemId), false);
|
||||
}
|
||||
announce(MaplePacketCreator.enableActions());
|
||||
} else if (itemId != 0) { // sit on map chair
|
||||
if (chair.get() == 0) {
|
||||
setChair(itemId);
|
||||
if (registerChairBuff()) {
|
||||
getMap().broadcastMessage(this, MaplePacketCreator.giveForeignChairSkillEffect(this.getId()), false);
|
||||
if (this.isLoggedinWorld()) {
|
||||
if (itemId >= 1000000) { // sit on item chair
|
||||
if (chair.get() < 0) {
|
||||
setChair(itemId);
|
||||
getMap().broadcastMessage(this, MaplePacketCreator.showChair(this.getId(), itemId), false);
|
||||
}
|
||||
announce(MaplePacketCreator.cancelChair(itemId));
|
||||
announce(MaplePacketCreator.enableActions());
|
||||
} else if (itemId >= 0) { // sit on map chair
|
||||
if (chair.get() < 0) {
|
||||
setChair(itemId);
|
||||
if (registerChairBuff()) {
|
||||
getMap().broadcastMessage(this, MaplePacketCreator.giveForeignChairSkillEffect(this.getId()), false);
|
||||
}
|
||||
announce(MaplePacketCreator.cancelChair(itemId));
|
||||
}
|
||||
} else { // stand up
|
||||
unsitChairInternal();
|
||||
}
|
||||
} else { // stand up
|
||||
unsitChairInternal();
|
||||
}
|
||||
} finally {
|
||||
client.releaseClient();
|
||||
@@ -8955,6 +8984,18 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setRPS(MapleRockPaperScissor rps) {
|
||||
this.rps = rps;
|
||||
}
|
||||
|
||||
public void closeRPS() {
|
||||
MapleRockPaperScissor rps = this.rps;
|
||||
if (rps != null) {
|
||||
rps.dispose(client);
|
||||
setRPS(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void changeName(String name) {
|
||||
FredrickProcessor.removeFredrickReminders(this.getId());
|
||||
|
||||
@@ -27,6 +27,7 @@ import client.MapleClient;
|
||||
|
||||
public abstract class Command {
|
||||
|
||||
protected int rank;
|
||||
protected String description;
|
||||
|
||||
public abstract void execute(MapleClient client, String[] params);
|
||||
@@ -38,6 +39,14 @@ public abstract class Command {
|
||||
protected void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public int getRank() {
|
||||
return rank;
|
||||
}
|
||||
|
||||
public void setRank(int rank) {
|
||||
this.rank = rank;
|
||||
}
|
||||
|
||||
protected String joinStringFrom(String arr[], int start) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
@@ -62,7 +62,7 @@ public class CommandsExecutor {
|
||||
return heading == USER_HEADING;
|
||||
}
|
||||
|
||||
private HashMap<String, RegisteredCommand> registeredCommands = new HashMap<>();
|
||||
private HashMap<String, Command> registeredCommands = new HashMap<>();
|
||||
private Pair<List<String>, List<String>> levelCommandsCursor;
|
||||
private List<Pair<List<String>, List<String>>> commandsNameDesc = new ArrayList<>();
|
||||
|
||||
@@ -94,7 +94,7 @@ public class CommandsExecutor {
|
||||
|
||||
private void handleInternal(MapleClient client, String message){
|
||||
if (client.getPlayer().getMapId() == 300000012) {
|
||||
client.getPlayer().yellowMessage("You not have permission to use this command while in jail.");
|
||||
client.getPlayer().yellowMessage("You do not have permission to use commands while in jail.");
|
||||
return;
|
||||
}
|
||||
final String splitRegex = "[ ]";
|
||||
@@ -107,13 +107,13 @@ public class CommandsExecutor {
|
||||
final String commandName = splitedMessage[0].toLowerCase();
|
||||
final String[] lowercaseParams = splitedMessage[1].toLowerCase().split(splitRegex);
|
||||
|
||||
final RegisteredCommand command = registeredCommands.get(commandName);
|
||||
final Command command = registeredCommands.get(commandName);
|
||||
if (command == null){
|
||||
client.getPlayer().yellowMessage("Command '" + commandName + "' is not available. See @commands for a list of available commands.");
|
||||
return;
|
||||
}
|
||||
if (client.getPlayer().gmLevel() < command.getRank()){
|
||||
client.getPlayer().yellowMessage("You not have permission to use this command.");
|
||||
client.getPlayer().yellowMessage("You do not have permission to use this command.");
|
||||
return;
|
||||
}
|
||||
String[] params;
|
||||
@@ -122,16 +122,9 @@ public class CommandsExecutor {
|
||||
} else {
|
||||
params = new String[]{};
|
||||
}
|
||||
try {
|
||||
Command commandInstance = command.getCommandClass().newInstance();
|
||||
commandInstance.execute(client, params);
|
||||
writeLog(client, message);
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
command.execute(client, params);
|
||||
writeLog(client, message);
|
||||
}
|
||||
|
||||
private void writeLog(MapleClient client, String command){
|
||||
@@ -172,11 +165,19 @@ public class CommandsExecutor {
|
||||
return;
|
||||
}
|
||||
|
||||
RegisteredCommand registeredCommand = new RegisteredCommand(commandClass, rank);
|
||||
|
||||
String commandName = syntax.toLowerCase();
|
||||
addCommandInfo(commandName, commandClass);
|
||||
registeredCommands.put(commandName, registeredCommand);
|
||||
|
||||
try {
|
||||
Command commandInstance = commandClass.newInstance(); // thanks Halcyon for noticing commands getting reinstanced every call
|
||||
commandInstance.setRank(rank);
|
||||
|
||||
registeredCommands.put(commandName, commandInstance);
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void registerLv0Commands(){
|
||||
|
||||
@@ -27,6 +27,7 @@ import client.command.Command;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import constants.ServerConstants;
|
||||
import server.maps.MapleMap;
|
||||
|
||||
public class MapOwnerClaimCommand extends Command {
|
||||
{
|
||||
@@ -41,12 +42,20 @@ public class MapOwnerClaimCommand extends Command {
|
||||
|
||||
if (ServerConstants.USE_MAP_OWNERSHIP_SYSTEM) {
|
||||
if (chr.getEventInstance() == null) {
|
||||
if (chr.getMap().unclaimOwnership(chr)) {
|
||||
chr.dropMessage(5, "This lawn is now free real estate.");
|
||||
} else if (chr.getMap().claimOwnership(chr)) {
|
||||
MapleMap ownedMap = chr.getOwnedMap(); // thanks Conrad for suggesting not unlease a map as soon as player exits it
|
||||
if (ownedMap != null) {
|
||||
ownedMap.unclaimOwnership(chr);
|
||||
|
||||
if (chr.getMap() == ownedMap) {
|
||||
chr.dropMessage(5, "This lawn is now free real estate.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (chr.getMap().claimOwnership(chr)) {
|
||||
chr.dropMessage(5, "You have leased this lawn for a while, until you leave here or after 1 minute of inactivity.");
|
||||
} else {
|
||||
chr.dropMessage(5, "This lawn has already been leased by another player.");
|
||||
chr.dropMessage(5, "This lawn has already been leased by a player.");
|
||||
}
|
||||
} else {
|
||||
chr.dropMessage(5, "This lawn cannot be leased.");
|
||||
|
||||
@@ -105,7 +105,7 @@ public class GotoCommand extends Command {
|
||||
gotomaps = new HashMap<>(GameConstants.GOTO_AREAS); // distinct map registry for GM/users suggested thanks to Vcoc
|
||||
gotomaps.putAll(GameConstants.GOTO_TOWNS); // thanks Halcyon for pointing out duplicates on listed entries functionality
|
||||
} else {
|
||||
gotomaps = new HashMap<>(GameConstants.GOTO_TOWNS);
|
||||
gotomaps = GameConstants.GOTO_TOWNS;
|
||||
}
|
||||
|
||||
if (gotomaps.containsKey(params[0])) {
|
||||
|
||||
@@ -55,7 +55,7 @@ public class IdCommand extends Command {
|
||||
}
|
||||
sb.append(String.format("Results found: #r%d#k | Returned: #b%d#k/100 | Refine search query to improve time.", resultList.size(), count) + "\r\n");
|
||||
|
||||
player.getClient().getAbstractPlayerInteraction().npcTalk(9010000, sb.toString());
|
||||
player.getAbstractPlayerInteraction().npcTalk(9010000, sb.toString());
|
||||
} else {
|
||||
player.yellowMessage(String.format("Id not found for item: %s, of type: %s.", queryItem, params[0]));
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ package client.command.commands.gm3;
|
||||
import client.command.Command;
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import server.quest.MapleQuest;
|
||||
|
||||
public class QuestCompleteCommand extends Command {
|
||||
{
|
||||
@@ -45,14 +44,8 @@ public class QuestCompleteCommand extends Command {
|
||||
int questId = Integer.parseInt(params[0]);
|
||||
|
||||
if (player.getQuestStatus(questId) == 1) {
|
||||
MapleQuest quest = MapleQuest.getInstance(questId);
|
||||
if (quest != null) {
|
||||
int npcid = quest.getNpcRequirement(true);
|
||||
quest.forceComplete(player, npcid);
|
||||
player.dropMessage(5, "QUEST " + questId + " completed.");
|
||||
} else { // should not occur
|
||||
player.dropMessage(5, "QUESTID " + questId + " is invalid.");
|
||||
}
|
||||
c.getAbstractPlayerInteraction().forceCompleteQuest(questId);
|
||||
player.dropMessage(5, "QUEST " + questId + " completed.");
|
||||
} else {
|
||||
player.dropMessage(5, "QUESTID " + questId + " not started or already completed.");
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ package client.command.commands.gm3;
|
||||
import client.command.Command;
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import server.quest.MapleQuest;
|
||||
|
||||
public class QuestStartCommand extends Command {
|
||||
{
|
||||
@@ -45,14 +44,8 @@ public class QuestStartCommand extends Command {
|
||||
int questid = Integer.parseInt(params[0]);
|
||||
|
||||
if (player.getQuestStatus(questid) == 0) {
|
||||
MapleQuest quest = MapleQuest.getInstance(questid);
|
||||
if (quest != null) {
|
||||
int npcid = quest.getNpcRequirement(false);
|
||||
quest.forceStart(player, npcid);
|
||||
player.dropMessage(5, "QUEST " + questid + " started.");
|
||||
} else {
|
||||
player.dropMessage(5, "QUESTID " + questid + " is invalid.");
|
||||
}
|
||||
c.getAbstractPlayerInteraction().forceStartQuest(questid);
|
||||
player.dropMessage(5, "QUEST " + questid + " started.");
|
||||
} else {
|
||||
player.dropMessage(5, "QUESTID " + questid + " already started/completed.");
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import server.life.SpawnPoint;
|
||||
import server.maps.MapleMapObject;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import server.maps.MapleReactor;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -112,10 +112,14 @@ public class DueyProcessor {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getCurrentDate() {
|
||||
private static String getCurrentDate(boolean quick) {
|
||||
String date = "";
|
||||
Calendar cal = Calendar.getInstance();
|
||||
int day = cal.get(Calendar.DATE) - 1; // instant duey ?
|
||||
if (!quick) {
|
||||
cal.add(Calendar.DATE, 1);
|
||||
}
|
||||
|
||||
int day = cal.get(Calendar.DATE);
|
||||
int month = cal.get(Calendar.MONTH) + 1; // its an array of months.
|
||||
int year = cal.get(Calendar.YEAR);
|
||||
date += day <= 9 ? "0" + day + "-" : "" + day + "-";
|
||||
@@ -132,7 +136,7 @@ public class DueyProcessor {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
ps = con.prepareStatement("SELECT Mesos FROM dueypackages WHERE ReceiverId = ? and Checked = 1");
|
||||
ps = con.prepareStatement("SELECT SenderName, Type FROM dueypackages WHERE ReceiverId = ? AND Checked = 1 ORDER BY Type DESC");
|
||||
ps.setInt(1, player.getId());
|
||||
rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
@@ -143,11 +147,11 @@ public class DueyProcessor {
|
||||
pss.executeUpdate();
|
||||
pss.close();
|
||||
con2.close();
|
||||
|
||||
c.announce(MaplePacketCreator.sendDueyParcelReceived(rs.getString("SenderName"), rs.getInt("Type") == 1));
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.sendDueyNotification(false));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
@@ -241,7 +245,7 @@ public class DueyProcessor {
|
||||
return packages;
|
||||
}
|
||||
|
||||
private static int createPackage(int mesos, String message, String sender, int toCid) {
|
||||
private static int createPackage(int mesos, String message, String sender, int toCid, boolean quick) {
|
||||
try {
|
||||
Connection con = null;
|
||||
PreparedStatement ps = null;
|
||||
@@ -249,16 +253,17 @@ public class DueyProcessor {
|
||||
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
ps = con.prepareStatement("INSERT INTO `dueypackages` (ReceiverId, SenderName, Mesos, TimeStamp, Message, Checked) VALUES (?, ?, ?, ?, ?, 1)", Statement.RETURN_GENERATED_KEYS);
|
||||
ps = con.prepareStatement("INSERT INTO `dueypackages` (ReceiverId, SenderName, Mesos, TimeStamp, Message, Type, Checked) VALUES (?, ?, ?, ?, ?, ?, 1)", Statement.RETURN_GENERATED_KEYS);
|
||||
ps.setInt(1, toCid);
|
||||
ps.setString(2, sender);
|
||||
ps.setInt(3, mesos);
|
||||
ps.setString(4, getCurrentDate());
|
||||
ps.setString(4, getCurrentDate(quick));
|
||||
ps.setString(5, message);
|
||||
ps.setInt(6, quick ? 1 : 0);
|
||||
|
||||
int updateRows = ps.executeUpdate();
|
||||
if (updateRows < 1) {
|
||||
FilePrinter.printError(FilePrinter.INSERT_CHAR, "Error trying to create package [mesos: " + mesos + ", " + sender + ", to CharacterId: " + toCid + "]");
|
||||
FilePrinter.printError(FilePrinter.INSERT_CHAR, "Error trying to create package [mesos: " + mesos + ", " + sender + ", quick: " + quick + ", to CharacterId: " + toCid + "]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -267,7 +272,7 @@ public class DueyProcessor {
|
||||
if (rs.next()) {
|
||||
packageId = rs.getInt(1);
|
||||
} else {
|
||||
FilePrinter.printError(FilePrinter.INSERT_CHAR, "Failed inserting package [mesos: " + mesos + ", " + sender + ", to CharacterId: " + toCid + "]");
|
||||
FilePrinter.printError(FilePrinter.INSERT_CHAR, "Failed inserting package [mesos: " + mesos + ", " + sender + ", quick: " + quick + ", to CharacterId: " + toCid + "]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -348,10 +353,18 @@ public class DueyProcessor {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void dueySendItem(MapleClient c, byte invTypeId, short itemPos, short amount, int sendMesos, String sendMessage, String recipient) {
|
||||
public static void dueySendItem(MapleClient c, byte invTypeId, short itemPos, short amount, int sendMesos, String sendMessage, String recipient, boolean quick) {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
final int fee = 5000 + MapleTrade.getFee(sendMesos);
|
||||
int fee = MapleTrade.getFee(sendMesos);
|
||||
if (!quick) {
|
||||
fee += 5000;
|
||||
} else if (!c.getPlayer().haveItem(5330000)) {
|
||||
AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with Quick Delivery on duey.");
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use duey with Quick Delivery, mesos " + sendMesos + " and amount " + amount);
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
long finalcost = (long) sendMesos + fee;
|
||||
if (finalcost < 0 || finalcost > Integer.MAX_VALUE || (amount < 1 && sendMesos == 0)) {
|
||||
@@ -385,7 +398,11 @@ public class DueyProcessor {
|
||||
return;
|
||||
}
|
||||
|
||||
int packageId = createPackage(sendMesos, sendMessage, c.getPlayer().getName(), recipientCid);
|
||||
if (quick) {
|
||||
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, 5330000, (short) 1, false, false);
|
||||
}
|
||||
|
||||
int packageId = createPackage(sendMesos, sendMessage, c.getPlayer().getName(), recipientCid, quick);
|
||||
if (packageId == -1) {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_ENABLE_ACTIONS.getCode()));
|
||||
return;
|
||||
@@ -490,10 +507,17 @@ public class DueyProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
public static void dueySendTalk(MapleClient c) {
|
||||
public static void dueySendTalk(MapleClient c, boolean quickDelivery) {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
c.announce(MaplePacketCreator.sendDuey((byte) 8, loadPackages(c.getPlayer())));
|
||||
long timeNow = System.currentTimeMillis();
|
||||
if(timeNow - c.getPlayer().getNpcCooldown() < ServerConstants.BLOCK_NPC_RACE_CONDT) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
c.getPlayer().setNpcCooldown(timeNow);
|
||||
|
||||
c.announce(MaplePacketCreator.sendDuey(quickDelivery ? 0x1A : 0x8, loadPackages(c.getPlayer())));
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
@@ -501,7 +525,7 @@ public class DueyProcessor {
|
||||
}
|
||||
|
||||
public static void dueyCreatePackage(Item item, int mesos, String sender, int recipientCid) {
|
||||
int packageId = createPackage(mesos, "", sender, recipientCid);
|
||||
int packageId = createPackage(mesos, "", sender, recipientCid, false);
|
||||
if (packageId != -1) {
|
||||
insertPackageItem(packageId, item);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import server.MakerItemFactory;
|
||||
import server.MakerItemFactory.MakerItemCreateEntry;
|
||||
import server.MapleItemInformationProvider;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
@@ -44,6 +45,7 @@ import tools.data.input.SeekableLittleEndianAccessor;
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MakerProcessor {
|
||||
|
||||
private static MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
|
||||
public static void makerAction(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
@@ -54,7 +56,7 @@ public class MakerProcessor {
|
||||
int toDisassemble = -1, pos = -1;
|
||||
boolean makerSucceeded = true;
|
||||
|
||||
MakerItemFactory.MakerItemCreateEntry recipe;
|
||||
MakerItemCreateEntry recipe;
|
||||
Map<Integer, Short> reagentids = new LinkedHashMap<>();
|
||||
int stimulantid = -1;
|
||||
|
||||
@@ -62,29 +64,31 @@ public class MakerProcessor {
|
||||
int fromLeftover = toCreate;
|
||||
toCreate = ii.getMakerCrystalFromLeftover(toCreate);
|
||||
if(toCreate == -1) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, ii.getName(toCreate) + " is unavailable for Monster Crystal conversion."));
|
||||
c.announce(MaplePacketCreator.serverNotice(1, ii.getName(fromLeftover) + " is unavailable for Monster Crystal conversion."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
recipe = MakerItemFactory.generateLeftoverCrystalEntry(fromLeftover);
|
||||
|
||||
recipe = MakerItemFactory.generateLeftoverCrystalEntry(fromLeftover, toCreate);
|
||||
} else if(type == 4) { // disassembling
|
||||
slea.readInt(); // 1... probably inventory type
|
||||
pos = slea.readInt();
|
||||
|
||||
Item it = c.getPlayer().getInventory(MapleInventoryType.EQUIP).getItem((short) pos);
|
||||
if(it != null && it.getItemId() == toCreate) {
|
||||
Pair<Integer, Integer> p;
|
||||
|
||||
if((p = generateDisassemblyInfo(toCreate)) != null) {
|
||||
recipe = MakerItemFactory.generateDisassemblyCrystalEntry(p.getLeft(), p.getRight());
|
||||
toDisassemble = toCreate;
|
||||
toCreate = ii.getMakerCrystalFromEquip(toCreate);
|
||||
toDisassemble = toCreate;
|
||||
|
||||
Pair<Integer, List<Pair<Integer, Integer>>> p = generateDisassemblyInfo(toDisassemble);
|
||||
if(p != null) {
|
||||
recipe = MakerItemFactory.generateDisassemblyCrystalEntry(toDisassemble, p.getLeft(), p.getRight());
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, ii.getName(toCreate) + " is unavailable for Monster Crystal disassembly."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "An unknown error occurred when trying to apply that item for disassembly."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@@ -132,6 +136,7 @@ public class MakerProcessor {
|
||||
if(!reagentids.isEmpty()) {
|
||||
if(!removeOddMakerReagents(toCreate, reagentids)) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You can only use WATK and MATK Strengthening Gems on weapon items."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -146,65 +151,80 @@ public class MakerProcessor {
|
||||
case -1:// non-available for Maker itemid has been tried to forge
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS, "Player " + c.getPlayer().getName() + " tried to craft itemid " + toCreate + " using the Maker skill.");
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "The requested item could not be crafted on this operation."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
break;
|
||||
|
||||
case 1: // no items
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You don't have all required items in your inventory to make " + recipe.getRewardAmount() + " " + ii.getName(toCreate) + "."));
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You don't have all required items in your inventory to make " + ii.getName(toCreate) + "."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
break;
|
||||
|
||||
case 2: // no meso
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough mesos (" + GameConstants.numberWithCommas(recipe.getCost()) + ") to complete this operation."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
break;
|
||||
|
||||
case 3: // no req level
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough level to complete this operation."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
break;
|
||||
|
||||
case 4: // no req skill level
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough Maker level to complete this operation."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
break;
|
||||
|
||||
case 5: // inventory full
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Your inventory is full."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
break;
|
||||
|
||||
default:
|
||||
if (MapleInventoryManipulator.checkSpace(c, toCreate, (short) recipe.getRewardAmount(), "")) {
|
||||
if(toDisassemble != -1) {
|
||||
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.EQUIP, (short) pos, (short) 1, false);
|
||||
} else {
|
||||
for (Pair<Integer, Integer> p : recipe.getReqItems()) {
|
||||
c.getAbstractPlayerInteraction().gainItem(p.getLeft(), (short) -p.getRight());
|
||||
c.getAbstractPlayerInteraction().gainItem(p.getLeft(), (short) -p.getRight(), false);
|
||||
}
|
||||
}
|
||||
|
||||
if(toDisassemble != -1) {
|
||||
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.EQUIP, (short) pos, (short) 1, false);
|
||||
c.announce(MaplePacketCreator.getShowItemGain(toDisassemble, (short) -1, true));
|
||||
}
|
||||
|
||||
int cost = recipe.getCost();
|
||||
if(stimulantid == -1 && reagentids.isEmpty()) {
|
||||
if(cost > 0) c.getPlayer().gainMeso(-cost);
|
||||
|
||||
int cost = recipe.getCost();
|
||||
if(stimulantid == -1 && reagentids.isEmpty()) {
|
||||
if(cost > 0) c.getPlayer().gainMeso(-cost, false);
|
||||
|
||||
for (Pair<Integer, Integer> p : recipe.getGainItems()) {
|
||||
c.getPlayer().setCS(true);
|
||||
c.getAbstractPlayerInteraction().gainItem(toCreate, (short) recipe.getRewardAmount());
|
||||
c.getAbstractPlayerInteraction().gainItem(p.getLeft(), p.getRight().shortValue(), false);
|
||||
c.getPlayer().setCS(false);
|
||||
} else {
|
||||
if(stimulantid != -1) c.getAbstractPlayerInteraction().gainItem(stimulantid, (short) -1);
|
||||
if(!reagentids.isEmpty()) {
|
||||
for(Map.Entry<Integer, Short> r : reagentids.entrySet()) {
|
||||
c.getAbstractPlayerInteraction().gainItem(r.getKey(), (short) (-1 * r.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
if(cost > 0) c.getPlayer().gainMeso(-cost);
|
||||
makerSucceeded = addBoostedMakerItem(c, toCreate, stimulantid, reagentids);
|
||||
}
|
||||
|
||||
if(makerSucceeded) c.announce(MaplePacketCreator.serverNotice(1, "You have successfully created " + recipe.getRewardAmount() + " " + ii.getName(toCreate) + "."));
|
||||
else c.getPlayer().dropMessage(5, "The Maker skill lights up, but the skill winds up as if nothing happened.");
|
||||
|
||||
c.announce(MaplePacketCreator.showMakerEffect(makerSucceeded));
|
||||
c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignMakerEffect(c.getPlayer().getId(), makerSucceeded), false);
|
||||
|
||||
if(toCreate == 4260003 && type == 3 && c.getPlayer().getQuestStatus(6033) == 1) {
|
||||
c.getAbstractPlayerInteraction().setQuestProgress(6033, 1);
|
||||
}
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Your inventory is full."));
|
||||
toCreate = recipe.getGainItems().get(0).getLeft();
|
||||
|
||||
if(stimulantid != -1) c.getAbstractPlayerInteraction().gainItem(stimulantid, (short) -1, false);
|
||||
if(!reagentids.isEmpty()) {
|
||||
for(Map.Entry<Integer, Short> r : reagentids.entrySet()) {
|
||||
c.getAbstractPlayerInteraction().gainItem(r.getKey(), (short) (-1 * r.getValue()), false);
|
||||
}
|
||||
}
|
||||
|
||||
if(cost > 0) c.getPlayer().gainMeso(-cost, false);
|
||||
makerSucceeded = addBoostedMakerItem(c, toCreate, stimulantid, reagentids);
|
||||
}
|
||||
|
||||
// thanks inhyuk for noticing missing MAKER_RESULT packets
|
||||
if (type == 3) {
|
||||
c.announce(MaplePacketCreator.makerResultCrystal(recipe.getGainItems().get(0).getLeft(), recipe.getReqItems().get(0).getLeft()));
|
||||
} else if (type == 4) {
|
||||
c.announce(MaplePacketCreator.makerResultDesynth(recipe.getReqItems().get(0).getLeft(), recipe.getCost(), recipe.getGainItems()));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.makerResult(makerSucceeded, recipe.getGainItems().get(0).getLeft(), recipe.getGainItems().get(0).getRight(), recipe.getCost(), recipe.getReqItems(), stimulantid, new LinkedList<>(reagentids.keySet())));
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.showMakerEffect(makerSucceeded));
|
||||
c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignMakerEffect(c.getPlayer().getId(), makerSucceeded), false);
|
||||
|
||||
if(toCreate == 4260003 && type == 3 && c.getPlayer().getQuestStatus(6033) == 1) {
|
||||
c.getAbstractPlayerInteraction().setQuestProgress(6033, 1);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -271,12 +291,12 @@ public class MakerProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private static Pair<Integer, Integer> generateDisassemblyInfo(int itemId) {
|
||||
private static Pair<Integer, List<Pair<Integer, Integer>>> generateDisassemblyInfo(int itemId) {
|
||||
int recvFee = ii.getMakerDisassembledFee(itemId);
|
||||
if(recvFee > -1) {
|
||||
int recvQty = ii.getMakerDisassembledQuantity(itemId);
|
||||
if(recvQty > 0) {
|
||||
return new Pair<>(recvFee, recvQty);
|
||||
List<Pair<Integer, Integer>> gains = ii.getMakerDisassembledItems(itemId);
|
||||
if(!gains.isEmpty()) {
|
||||
return new Pair<>(recvFee, gains);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +307,7 @@ public class MakerProcessor {
|
||||
return chr.getSkillLevel((chr.getJob().getId() / 1000) * 10000000 + 1007);
|
||||
}
|
||||
|
||||
private static short getCreateStatus(MapleClient c, MakerItemFactory.MakerItemCreateEntry recipe) {
|
||||
private static short getCreateStatus(MapleClient c, MakerItemCreateEntry recipe) {
|
||||
if(recipe == null) {
|
||||
return -1;
|
||||
}
|
||||
@@ -308,10 +328,29 @@ public class MakerProcessor {
|
||||
return 4;
|
||||
}
|
||||
|
||||
List<Integer> addItemids = new LinkedList<>();
|
||||
List<Integer> addQuantity = new LinkedList<>();
|
||||
List<Integer> rmvItemids = new LinkedList<>();
|
||||
List<Integer> rmvQuantity = new LinkedList<>();
|
||||
|
||||
for (Pair<Integer, Integer> p : recipe.getReqItems()) {
|
||||
rmvItemids.add(p.getLeft());
|
||||
rmvQuantity.add(p.getRight());
|
||||
}
|
||||
|
||||
for (Pair<Integer, Integer> p : recipe.getGainItems()) {
|
||||
addItemids.add(p.getLeft());
|
||||
addQuantity.add(p.getRight());
|
||||
}
|
||||
|
||||
if (!c.getAbstractPlayerInteraction().canHoldAllAfterRemoving(addItemids, addQuantity, rmvItemids, rmvQuantity)) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static boolean hasItems(MapleClient c, MakerItemFactory.MakerItemCreateEntry recipe) {
|
||||
private static boolean hasItems(MapleClient c, MakerItemCreateEntry recipe) {
|
||||
for (Pair<Integer, Integer> p : recipe.getReqItems()) {
|
||||
int itemId = p.getLeft();
|
||||
if (c.getPlayer().getInventory(ItemConstants.getInventoryType(itemId)).countById(itemId) < p.getRight()) {
|
||||
@@ -397,7 +436,6 @@ public class MakerProcessor {
|
||||
}
|
||||
|
||||
MapleInventoryManipulator.addFromDrop(c, item, false, -1);
|
||||
c.announce(MaplePacketCreator.getShowItemGain(itemid, (short) 1, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,4 +247,12 @@ public final class ItemConstants {
|
||||
public static boolean isHair(int itemId) {
|
||||
return itemId >= 30000 && itemId < 35000;
|
||||
}
|
||||
|
||||
public static boolean isFaceExpression(int itemId) {
|
||||
return itemId / 10000 == 516;
|
||||
}
|
||||
|
||||
public static boolean isChair(int itemId) {
|
||||
return itemId / 10000 == 301;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ public class ScriptableNPCConstants {
|
||||
|
||||
public static final Set<Pair<Integer, String>> SCRIPTABLE_NPCS = new HashSet<Pair<Integer, String>>(){{
|
||||
add(new Pair<>(9200000, "Cody"));
|
||||
add(new Pair<>(9001105, "Grandpa Moon Bunny"));
|
||||
}};
|
||||
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class ServerConstants {
|
||||
public static final boolean USE_ENFORCE_JOB_LEVEL_RANGE = false;//Caps the player level on the minimum required to advance their current jobs.
|
||||
public static final boolean USE_ENFORCE_JOB_SP_RANGE = false; //Caps the player SP level on the total obtainable by their current jobs. After changing jobs, missing SP will be retrieved.
|
||||
public static final boolean USE_ENFORCE_ITEM_SUGGESTION = false;//Forces the Owl of Minerva and the Cash Shop to always display the defined item array instead of those featured by the players.
|
||||
public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = false;//Forces players to not sell CASH items via merchants.
|
||||
public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = true;//Forces players to not sell CASH items via merchants.
|
||||
public static final boolean USE_ENFORCE_UNMERCHABLE_PET = false; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues)
|
||||
public static final boolean USE_ENFORCE_MERCHANT_SAVE = true; //Forces automatic DB save on merchant owners, at every item movement on shop.
|
||||
public static final boolean USE_ENFORCE_MDOOR_POSITION = false; //Forces mystic door to be spawned near spawnpoints.
|
||||
@@ -112,7 +112,7 @@ public class ServerConstants {
|
||||
public static final boolean USE_ENABLE_CHAT_LOG = false; //Write in-game chat to log
|
||||
public static final boolean USE_REBIRTH_SYSTEM = false; //Flag to enable/disable rebirth system
|
||||
public static final boolean USE_MAP_OWNERSHIP_SYSTEM = true; //Flag to enable/disable map ownership system
|
||||
public static final boolean USE_FISHING_SYSTEM = true; //Flag to enable/disable fishing system
|
||||
public static final boolean USE_FISHING_SYSTEM = true; //Flag to enable/disable custom fishing system
|
||||
public static final boolean USE_NPCS_SCRIPTABLE = true; //Flag to enable/disable serverside predefined script NPCs.
|
||||
|
||||
//Events/PQs Configuration
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+11
-21
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
|
||||
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
|
||||
@@ -17,27 +17,17 @@
|
||||
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;
|
||||
|
||||
/*
|
||||
@Author: Arthur L - Refactored command content into modules
|
||||
*/
|
||||
package client.command;
|
||||
import server.expeditions.MapleExpeditionBossLog;
|
||||
|
||||
class RegisteredCommand {
|
||||
|
||||
private final Class<? extends Command> commandClass;
|
||||
private final int rank;
|
||||
|
||||
RegisteredCommand(Class<? extends Command> commandClass, int rank){
|
||||
this.commandClass = commandClass;
|
||||
this.rank = rank;
|
||||
}
|
||||
|
||||
public Class<? extends Command> getCommandClass() {
|
||||
return commandClass;
|
||||
}
|
||||
|
||||
public int getRank() {
|
||||
return rank;
|
||||
/**
|
||||
* @author Ronan
|
||||
*/
|
||||
public class BossLogWorker implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
MapleExpeditionBossLog.resetBossLogTable();
|
||||
}
|
||||
}
|
||||
@@ -1073,12 +1073,12 @@ public class AbstractPlayerInteraction {
|
||||
|
||||
public boolean createExpedition(MapleExpeditionType type, boolean silent, int minPlayers, int maxPlayers) {
|
||||
MapleExpedition exped = new MapleExpedition(getPlayer(), type, silent, minPlayers, maxPlayers);
|
||||
return getPlayer().getClient().getChannelServer().addExpedition(exped);
|
||||
return exped.addChannelExpedition(getPlayer().getClient().getChannelServer());
|
||||
}
|
||||
|
||||
public void endExpedition(MapleExpedition exped) {
|
||||
exped.dispose(true);
|
||||
getPlayer().getClient().getChannelServer().removeExpedition(exped);
|
||||
exped.removeChannelExpedition(getPlayer().getClient().getChannelServer());
|
||||
}
|
||||
|
||||
public MapleExpedition getExpedition(MapleExpeditionType type) {
|
||||
|
||||
@@ -1062,7 +1062,7 @@ public class EventInstanceManager {
|
||||
|
||||
sL.lock();
|
||||
try {
|
||||
em.getChannelServer().removeExpedition(expedition);
|
||||
expedition.removeChannelExpedition(em.getChannelServer());
|
||||
} finally {
|
||||
sL.unlock();
|
||||
}
|
||||
@@ -1127,8 +1127,8 @@ public class EventInstanceManager {
|
||||
if(eventCleared) {
|
||||
if(leavingEventMap && getPlayerCount() <= 1) return true;
|
||||
} else {
|
||||
if(leavingEventMap && getLeaderId() == quitter.getId()) return true;
|
||||
if(getPlayerCount() <= (ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : minPlayers)) return true;
|
||||
// thanks Conrad for noticing expeditions don't need to have neither the leader nor meet the minimum requirement inside the event
|
||||
if(getPlayerCount() <= 1) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -78,6 +78,10 @@ public class DueyPackage {
|
||||
public long sentTimeInMilliseconds() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.set(year, month, day);
|
||||
cal.set(Calendar.HOUR, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
return cal.getTimeInMillis();
|
||||
}
|
||||
|
||||
|
||||
@@ -57,14 +57,19 @@ public class MakerItemFactory {
|
||||
return makerEntry;
|
||||
}
|
||||
|
||||
public static MakerItemCreateEntry generateLeftoverCrystalEntry(int fromLeftoverid) {
|
||||
MakerItemCreateEntry ret = new MakerItemCreateEntry(0, 0, 1, 1);
|
||||
public static MakerItemCreateEntry generateLeftoverCrystalEntry(int fromLeftoverid, int crystalId) {
|
||||
MakerItemCreateEntry ret = new MakerItemCreateEntry(0, 0, 1);
|
||||
ret.addReqItem(fromLeftoverid, 100);
|
||||
ret.addGainItem(crystalId, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static MakerItemCreateEntry generateDisassemblyCrystalEntry(int cost, int crystalGain) { // equipment at specific position already taken
|
||||
MakerItemCreateEntry ret = new MakerItemCreateEntry(cost, 0, 1, crystalGain);
|
||||
public static MakerItemCreateEntry generateDisassemblyCrystalEntry(int fromEquipid, int cost, List<Pair<Integer, Integer>> gains) { // equipment at specific position already taken
|
||||
MakerItemCreateEntry ret = new MakerItemCreateEntry(cost, 0, 1);
|
||||
ret.addReqItem(fromEquipid, 1);
|
||||
for (Pair<Integer, Integer> p : gains) {
|
||||
ret.addGainItem(p.getLeft(), p.getRight());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -143,33 +148,35 @@ public class MakerItemFactory {
|
||||
private double cost;
|
||||
private int reqCost;
|
||||
private List<Pair<Integer, Integer>> reqItems = new ArrayList<>(); // itemId / amount
|
||||
private int toGive;
|
||||
private List<Pair<Integer, Integer>> gainItems = new ArrayList<>(); // itemId / amount
|
||||
|
||||
public MakerItemCreateEntry(int cost, int reqLevel, int reqMakerLevel, int toGive) {
|
||||
public MakerItemCreateEntry(int cost, int reqLevel, int reqMakerLevel) {
|
||||
this.cost = cost;
|
||||
this.reqLevel = reqLevel;
|
||||
this.reqMakerLevel = reqMakerLevel;
|
||||
this.toGive = toGive;
|
||||
}
|
||||
|
||||
public MakerItemCreateEntry(MakerItemCreateEntry mi) {
|
||||
this.cost = mi.cost;
|
||||
this.reqLevel = mi.reqLevel;
|
||||
this.reqMakerLevel = mi.reqMakerLevel;
|
||||
this.toGive = mi.toGive;
|
||||
|
||||
for(Pair<Integer, Integer> p : mi.reqItems) {
|
||||
reqItems.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
public int getRewardAmount() {
|
||||
return toGive;
|
||||
|
||||
for(Pair<Integer, Integer> p : mi.gainItems) {
|
||||
gainItems.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Pair<Integer, Integer>> getReqItems() {
|
||||
return reqItems;
|
||||
}
|
||||
|
||||
public List<Pair<Integer, Integer>> getGainItems() {
|
||||
return gainItems;
|
||||
}
|
||||
|
||||
public int getReqLevel() {
|
||||
return reqLevel;
|
||||
@@ -191,6 +198,10 @@ public class MakerItemFactory {
|
||||
reqItems.add(new Pair<>(itemId, amount));
|
||||
}
|
||||
|
||||
protected void addGainItem(int itemId, int amount) {
|
||||
gainItems.add(new Pair<>(itemId, amount));
|
||||
}
|
||||
|
||||
public void trimCost() {
|
||||
reqCost = (int) (cost / 1000);
|
||||
reqCost *= 1000;
|
||||
|
||||
@@ -1968,7 +1968,8 @@ public class MapleItemInformationProvider {
|
||||
}
|
||||
ps.close();
|
||||
rs.close();
|
||||
makerEntry = new MakerItemCreateEntry(cost, reqLevel, reqMakerLevel, toGive);
|
||||
makerEntry = new MakerItemCreateEntry(cost, reqLevel, reqMakerLevel);
|
||||
makerEntry.addGainItem(toCreate, toGive);
|
||||
ps = con.prepareStatement("SELECT req_item, count FROM makerrecipedata WHERE itemid = ?");
|
||||
ps.setInt(1, toCreate);
|
||||
rs = ps.executeQuery();
|
||||
@@ -2008,17 +2009,18 @@ public class MapleItemInformationProvider {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getMakerDisassembledQuantity(Integer itemId) {
|
||||
int avail = 0;
|
||||
public List<Pair<Integer, Integer>> getMakerDisassembledItems(Integer itemId) {
|
||||
List<Pair<Integer, Integer>> items = new LinkedList<>();
|
||||
|
||||
Connection con;
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT count FROM makerrecipedata WHERE itemid = ? AND req_item >= 4260000 AND req_item <= 4260008 ORDER BY count DESC");
|
||||
PreparedStatement ps = con.prepareStatement("SELECT req_item, count FROM makerrecipedata WHERE itemid = ? AND req_item >= 4260000 AND req_item < 4270000");
|
||||
ps.setInt(1, itemId);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
if(rs.next()) {
|
||||
avail = (int) Math.ceil(rs.getInt("count") / 2); // return to the player half of the crystals needed
|
||||
while (rs.next()) {
|
||||
items.add(new Pair<>(rs.getInt("req_item"), rs.getInt("count") / 2)); // return to the player half of the crystals needed
|
||||
}
|
||||
|
||||
rs.close();
|
||||
@@ -2028,7 +2030,7 @@ public class MapleItemInformationProvider {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return avail;
|
||||
return items;
|
||||
}
|
||||
|
||||
public int getMakerDisassembledFee(Integer itemId) {
|
||||
|
||||
@@ -13,57 +13,46 @@ import client.SkillFactory;
|
||||
* @author kevintjuh93
|
||||
*/
|
||||
public class RescueGaga extends MapleEvents {
|
||||
private byte fallen;
|
||||
private int completed;
|
||||
|
||||
private int completed;
|
||||
|
||||
public RescueGaga(int completed) {
|
||||
super();
|
||||
this.completed = completed;
|
||||
}
|
||||
|
||||
public RescueGaga(int completed) {
|
||||
super();
|
||||
this.completed = completed;
|
||||
this.fallen = 0;
|
||||
}
|
||||
public int getCompleted() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
public int fallAndGet() {
|
||||
fallen++;
|
||||
if (fallen > 3) {
|
||||
fallen = 0;
|
||||
return 4;
|
||||
}
|
||||
return fallen;
|
||||
}
|
||||
public void complete() {
|
||||
completed++;
|
||||
}
|
||||
|
||||
public byte getFallen() {
|
||||
return fallen;
|
||||
}
|
||||
|
||||
public int getCompleted() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
public void complete() {
|
||||
completed++;
|
||||
@Override
|
||||
public int getInfo() {
|
||||
return getCompleted();
|
||||
}
|
||||
|
||||
public void giveSkill(MapleCharacter chr) {
|
||||
int skillid = 0;
|
||||
switch (chr.getJobType()) {
|
||||
case 0:
|
||||
skillid = 1013;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
skillid = 10001014;
|
||||
}
|
||||
|
||||
public int getInfo() {
|
||||
return getCompleted();
|
||||
}
|
||||
|
||||
public void giveSkill(MapleCharacter chr) {
|
||||
int skillid = 0;
|
||||
switch (chr.getJobType()) {
|
||||
case 0:
|
||||
skillid = 1013;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
skillid = 10001014;
|
||||
}
|
||||
long expiration = (System.currentTimeMillis() + (long) (3600 * 24 * 20 * 1000));//20 days
|
||||
if (completed < 20) {
|
||||
chr.changeSkillLevel(SkillFactory.getSkill(skillid), (byte) 1, 1, expiration);
|
||||
chr.changeSkillLevel(SkillFactory.getSkill(skillid + 1), (byte) 1, 1, expiration);
|
||||
chr.changeSkillLevel(SkillFactory.getSkill(skillid + 2), (byte) 1, 1, expiration);
|
||||
} else {
|
||||
chr.changeSkillLevel(SkillFactory.getSkill(skillid), (byte) 2, 2, chr.getSkillExpiration(skillid));
|
||||
}
|
||||
long expiration = (System.currentTimeMillis() + 3600 * 24 * 20 * 1000);//20 days
|
||||
if (completed < 20) {
|
||||
chr.changeSkillLevel(SkillFactory.getSkill(skillid), (byte) 1, 1, expiration);
|
||||
chr.changeSkillLevel(SkillFactory.getSkill(skillid + 1), (byte) 1, 1, expiration);
|
||||
chr.changeSkillLevel(SkillFactory.getSkill(skillid + 2), (byte) 1, 1, expiration);
|
||||
} else {
|
||||
chr.changeSkillLevel(SkillFactory.getSkill(skillid), (byte) 2, 2, chr.getSkillExpiration(skillid));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import java.util.Properties;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import net.server.channel.Channel;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -137,7 +138,7 @@ public class MapleExpedition {
|
||||
@Override
|
||||
public void run() {
|
||||
if (registering){
|
||||
startMap.getChannelServer().removeExpedition(exped);
|
||||
exped.removeChannelExpedition(startMap.getChannelServer());
|
||||
if (!silent) startMap.broadcastMessage(MaplePacketCreator.serverNotice(6, "[Expedition] The time limit has been reached. Expedition has been disbanded."));
|
||||
|
||||
dispose(false);
|
||||
@@ -163,6 +164,7 @@ public class MapleExpedition {
|
||||
|
||||
public void start(){
|
||||
finishRegistration();
|
||||
registerExpeditionAttempt();
|
||||
broadcastExped(MaplePacketCreator.removeClock());
|
||||
if (!silent) broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] The expedition has started! Good luck, brave heroes!"));
|
||||
startTime = System.currentTimeMillis();
|
||||
@@ -180,6 +182,11 @@ public class MapleExpedition {
|
||||
return "Sorry, this expedition is full!";
|
||||
}
|
||||
|
||||
int channel = this.getRecruitingMap().getChannelServer().getId();
|
||||
if (!MapleExpeditionBossLog.attemptBoss(player.getId(), channel, this, false)) { // thanks Conrad, Cato for noticing some expeditions have entry limit
|
||||
return "Sorry, you've already reached the quota of attempts for this expedition! Try again another day...";
|
||||
}
|
||||
|
||||
members.put(player.getId(), player.getName());
|
||||
player.announce(MaplePacketCreator.getClock((int)(startTime - System.currentTimeMillis()) / 1000));
|
||||
if (!silent) broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + player.getName() + " has joined the expedition!"));
|
||||
@@ -202,13 +209,21 @@ public class MapleExpedition {
|
||||
if (!silent) broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + player.getName() + " has joined the expedition!"));
|
||||
return 0; //"You have registered for the expedition successfully!";
|
||||
}
|
||||
|
||||
private void registerExpeditionAttempt(){
|
||||
int channel = this.getRecruitingMap().getChannelServer().getId();
|
||||
|
||||
for (MapleCharacter chr : getActiveMembers()){
|
||||
MapleExpeditionBossLog.attemptBoss(chr.getId(), channel, this, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastExped(byte[] packet){
|
||||
for (MapleCharacter chr : getActiveMembers()){
|
||||
chr.announce(packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean removeMember(MapleCharacter chr) {
|
||||
if(members.remove(chr.getId()) != null) {
|
||||
chr.announce(MaplePacketCreator.removeClock());
|
||||
@@ -359,6 +374,14 @@ public class MapleExpedition {
|
||||
chr.changeMap(warpTo, toSp);
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean addChannelExpedition(Channel ch) {
|
||||
return ch.addExpedition(this);
|
||||
}
|
||||
|
||||
public final void removeChannelExpedition(Channel ch) {
|
||||
ch.removeExpedition(this);
|
||||
}
|
||||
|
||||
public MapleCharacter getLeader(){
|
||||
return leader;
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
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 server.expeditions;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Calendar;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Conrad
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleExpeditionBossLog {
|
||||
|
||||
public enum BossLogEntry {
|
||||
ZAKUM(2, 1, false),
|
||||
HORNTAIL(2, 1, false),
|
||||
PINKBEAN(1, 1, false),
|
||||
SCARGA(1, 1, false),
|
||||
PAPULATUS(2, 1, false);
|
||||
|
||||
private int entries;
|
||||
private int timeLength;
|
||||
private int minChannel, maxChannel;
|
||||
private boolean week;
|
||||
|
||||
private BossLogEntry(int entries, int timeLength, boolean week) {
|
||||
this(entries, 0, Integer.MAX_VALUE, timeLength, week);
|
||||
}
|
||||
|
||||
private BossLogEntry(int entries, int minChannel, int maxChannel, int timeLength, boolean week) {
|
||||
this.entries = entries;
|
||||
this.minChannel = minChannel;
|
||||
this.maxChannel = maxChannel;
|
||||
this.timeLength = timeLength;
|
||||
this.week = week;
|
||||
}
|
||||
|
||||
private static List<Pair<Timestamp, BossLogEntry>> getBossLogResetTimestamps(Calendar timeNow, boolean week) {
|
||||
List<Pair<Timestamp, BossLogEntry>> resetTimestamps = new LinkedList<>();
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
return resetTimestamps;
|
||||
}
|
||||
|
||||
private static BossLogEntry getBossEntryByName(String name) {
|
||||
for (BossLogEntry b : BossLogEntry.values()) {
|
||||
if (name.contentEquals(b.name())) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void resetBossLogTable() {
|
||||
/*
|
||||
Boss logs resets 12am, weekly thursday 12AM - thanks Smitty Werbenjagermanjensen (superadlez) - https://www.reddit.com/r/Maplestory/comments/61tiup/about_reset_time/
|
||||
*/
|
||||
|
||||
Calendar thursday = Calendar.getInstance();
|
||||
thursday.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
|
||||
thursday.set(Calendar.HOUR, 0);
|
||||
thursday.set(Calendar.MINUTE, 0);
|
||||
thursday.set(Calendar.SECOND, 0);
|
||||
|
||||
Calendar now = Calendar.getInstance();
|
||||
|
||||
long weekLength = 7 * 24 * 60 * 60 * 1000;
|
||||
long halfDayLength = 12 * 60 * 60 * 1000;
|
||||
|
||||
long deltaTime = now.getTime().getTime() - thursday.getTime().getTime(); // 2x time: get Date into millis
|
||||
deltaTime += halfDayLength;
|
||||
deltaTime %= weekLength;
|
||||
deltaTime -= halfDayLength;
|
||||
|
||||
if (deltaTime < halfDayLength) {
|
||||
MapleExpeditionBossLog.resetBossLogTable(true, thursday);
|
||||
}
|
||||
|
||||
now.set(Calendar.HOUR, 0);
|
||||
now.set(Calendar.MINUTE, 0);
|
||||
now.set(Calendar.SECOND, 0);
|
||||
|
||||
MapleExpeditionBossLog.resetBossLogTable(false, now);
|
||||
}
|
||||
|
||||
private static void resetBossLogTable(boolean week, Calendar c) {
|
||||
List<Pair<Timestamp, BossLogEntry>> resetTimestamps = BossLogEntry.getBossLogResetTimestamps(c, week);
|
||||
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
for (Pair<Timestamp, BossLogEntry> p : resetTimestamps) {
|
||||
PreparedStatement ps = con.prepareStatement("DELETE FROM " + getBossLogTable(week) + " WHERE attempttime <= ? AND bosstype LIKE ?");
|
||||
ps.setTimestamp(1, p.getLeft());
|
||||
ps.setString(2, p.getRight().name());
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static String getBossLogTable(boolean week) {
|
||||
return week ? "bosslog_weekly" : "bosslog_daily";
|
||||
}
|
||||
|
||||
private static int countPlayerEntries(int cid, BossLogEntry boss) {
|
||||
int ret_count = 0;
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps;
|
||||
ps = con.prepareStatement("SELECT COUNT(*) FROM " + getBossLogTable(boss.week) + " WHERE characterid = ? AND bosstype LIKE ?");
|
||||
ps.setInt(1, cid);
|
||||
ps.setString(2, boss.name());
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
ret_count = rs.getInt(1);
|
||||
} else {
|
||||
ret_count = -1;
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
return ret_count;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void insertPlayerEntry(int cid, BossLogEntry boss) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("INSERT INTO " + getBossLogTable(boss.week) + " (characterid, bosstype) VALUES (?,?)");
|
||||
ps.setInt(1, cid);
|
||||
ps.setString(2, boss.name());
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean attemptBoss(int cid, int channel, MapleExpedition exped, boolean log) {
|
||||
BossLogEntry boss = BossLogEntry.getBossEntryByName(exped.getType().name());
|
||||
if (boss == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (channel < boss.minChannel || channel > boss.maxChannel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (countPlayerEntries(cid, boss) >= boss.entries) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (log) {
|
||||
insertPlayerEntry(cid, boss);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -415,7 +415,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
*/
|
||||
|
||||
if (damage > 0) {
|
||||
this.applyDamage(attacker, damage, stayAlive);
|
||||
this.applyDamage(attacker, damage, stayAlive, false);
|
||||
if (!this.isAlive()) { // monster just died
|
||||
lastHit = true;
|
||||
}
|
||||
@@ -433,7 +433,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
* @param damage
|
||||
* @param stayAlive
|
||||
*/
|
||||
private void applyDamage(MapleCharacter from, int damage, boolean stayAlive) {
|
||||
private void applyDamage(MapleCharacter from, int damage, boolean stayAlive, boolean fake) {
|
||||
Integer trueDamage = applyAndGetHpDamage(damage, stayAlive);
|
||||
if (trueDamage == null) {
|
||||
return;
|
||||
@@ -442,8 +442,11 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
if (ServerConstants.USE_DEBUG) {
|
||||
from.dropMessage(5, "Hitted MOB " + this.getId() + ", OID " + this.getObjectId());
|
||||
}
|
||||
dispatchMonsterDamaged(from, trueDamage);
|
||||
|
||||
|
||||
if (!fake) {
|
||||
dispatchMonsterDamaged(from, trueDamage);
|
||||
}
|
||||
|
||||
if (!takenDamage.containsKey(from.getId())) {
|
||||
takenDamage.put(from.getId(), new AtomicLong(trueDamage));
|
||||
} else {
|
||||
@@ -453,6 +456,10 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
broadcastMobHpBar(from);
|
||||
}
|
||||
|
||||
public void applyFakeDamage(MapleCharacter from, int damage, boolean stayAlive) {
|
||||
applyDamage(from, damage, stayAlive, true);
|
||||
}
|
||||
|
||||
public void heal(int hp, int mp) {
|
||||
Integer hpHealed = applyAndGetHpDamage(-hp, false);
|
||||
if (hpHealed == null) {
|
||||
@@ -744,21 +751,6 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
if (toSpawn.contains(timeMob.getLeft())) {
|
||||
reviveMap.broadcastMessage(MaplePacketCreator.serverNotice(6, timeMob.getRight()));
|
||||
}
|
||||
|
||||
if (timeMob.getLeft() == 9300338 && (reviveMap.getId() >= 922240100 && reviveMap.getId() <= 922240119)) {
|
||||
if (!reviveMap.containsNPC(9001108)) {
|
||||
MapleNPC npc = MapleLifeFactory.getNPC(9001108);
|
||||
npc.setPosition(new Point(172, 9));
|
||||
npc.setCy(9);
|
||||
npc.setRx0(172 + 50);
|
||||
npc.setRx1(172 - 50);
|
||||
npc.setFh(27);
|
||||
reviveMap.addMapObject(npc);
|
||||
reviveMap.broadcastMessage(MaplePacketCreator.spawnNPC(npc));
|
||||
} else {
|
||||
reviveMap.toggleHiddenNPC(9001108);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(toSpawn.size() > 0) {
|
||||
@@ -1621,7 +1613,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
if (damage > 0) {
|
||||
lockMonster();
|
||||
try {
|
||||
applyDamage(chr, damage, true);
|
||||
applyDamage(chr, damage, true, false);
|
||||
} finally {
|
||||
unlockMonster();
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ public class MapleMap {
|
||||
private static final Map<Integer, Pair<Integer, Integer>> dropBoundsCache = new HashMap<>(100);
|
||||
|
||||
private Map<Integer, MapleMapObject> mapobjects = new LinkedHashMap<>();
|
||||
private Set<Integer> selfDestructives = new LinkedHashSet<>();
|
||||
private Collection<SpawnPoint> monsterSpawn = Collections.synchronizedList(new LinkedList<SpawnPoint>());
|
||||
private Collection<SpawnPoint> allMonsterSpawn = Collections.synchronizedList(new LinkedList<SpawnPoint>());
|
||||
private AtomicInteger spawnedMonstersOnMap = new AtomicInteger(0);
|
||||
@@ -122,6 +123,7 @@ public class MapleMap {
|
||||
private AtomicInteger runningOid = new AtomicInteger(1000000001);
|
||||
private int returnMapId;
|
||||
private int channel, world;
|
||||
private int seats;
|
||||
private byte monsterRate;
|
||||
private boolean clock;
|
||||
private boolean boat;
|
||||
@@ -132,7 +134,8 @@ public class MapleMap {
|
||||
private MapleMapEffect mapEffect = null;
|
||||
private boolean everlast = false;
|
||||
private int forcedReturnMap = 999999999;
|
||||
private long timeLimit;
|
||||
private int timeLimit;
|
||||
private long mapTimer;
|
||||
private int decHP = 0;
|
||||
private int protectItem = 0;
|
||||
private boolean town;
|
||||
@@ -145,7 +148,6 @@ public class MapleMap {
|
||||
private int fieldLimit = 0;
|
||||
private int mobCapacity = -1;
|
||||
private MapleMonsterAggroCoordinator aggroMonitor = null; // aggroMonitor activity in sync with itemMonitor
|
||||
private ScheduledFuture<?> mapMonitor = null;
|
||||
private ScheduledFuture<?> itemMonitor = null;
|
||||
private ScheduledFuture<?> expireItemsTask = null;
|
||||
private ScheduledFuture<?> mobSpawnLootTask = null;
|
||||
@@ -307,7 +309,7 @@ public class MapleMap {
|
||||
this.forcedReturnMap = map;
|
||||
}
|
||||
|
||||
public long getTimeLimit() {
|
||||
public int getTimeLimit() {
|
||||
return timeLimit;
|
||||
}
|
||||
|
||||
@@ -316,7 +318,7 @@ public class MapleMap {
|
||||
}
|
||||
|
||||
public int getTimeLeft() {
|
||||
return (int) ((timeLimit - System.currentTimeMillis()) / 1000);
|
||||
return (int) ((mapTimer - System.currentTimeMillis()) / 1000);
|
||||
}
|
||||
|
||||
public void setReactorState() {
|
||||
@@ -398,6 +400,16 @@ public class MapleMap {
|
||||
}
|
||||
}
|
||||
|
||||
public void addSelfDestructive(MapleMonster mob) {
|
||||
if (mob.getStats().selfDestruction() != null) {
|
||||
this.selfDestructives.add(mob.getObjectId());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeSelfDestructive(int mapobjectid) {
|
||||
return this.selfDestructives.remove(mapobjectid);
|
||||
}
|
||||
|
||||
private void spawnAndAddRangedMapObject(MapleMapObject mapobject, DelayedPacketCreation packetbakery) {
|
||||
spawnAndAddRangedMapObject(mapobject, packetbakery, null);
|
||||
}
|
||||
@@ -1331,9 +1343,11 @@ public class MapleMap {
|
||||
}
|
||||
if (monster.isAlive()) {
|
||||
boolean killed = monster.damage(chr, damage, false);
|
||||
if (monster.getStats().selfDestruction() != null && monster.getStats().selfDestruction().getHp() > -1) {// should work ;p
|
||||
if (monster.getHp() <= monster.getStats().selfDestruction().getHp()) {
|
||||
killMonster(monster, chr, true, monster.getStats().selfDestruction().getAction());
|
||||
|
||||
selfDestruction selfDestr = monster.getStats().selfDestruction();
|
||||
if (selfDestr != null && selfDestr.getHp() > -1) {// should work ;p
|
||||
if (monster.getHp() <= selfDestr.getHp()) {
|
||||
killMonster(monster, chr, true, selfDestr.getAction());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1907,7 +1921,6 @@ public class MapleMap {
|
||||
spawnAndAddRangedMapObject(monster, new DelayedPacketCreation() {
|
||||
@Override
|
||||
public void sendPackets(MapleClient c) {
|
||||
|
||||
c.announce(MaplePacketCreator.spawnMonster(monster, false));
|
||||
}
|
||||
});
|
||||
@@ -1919,6 +1932,7 @@ public class MapleMap {
|
||||
}
|
||||
|
||||
spawnedMonstersOnMap.incrementAndGet();
|
||||
addSelfDestructive(monster);
|
||||
applyRemoveAfter(monster);
|
||||
}
|
||||
|
||||
@@ -2052,6 +2066,7 @@ public class MapleMap {
|
||||
}
|
||||
|
||||
spawnedMonstersOnMap.incrementAndGet();
|
||||
addSelfDestructive(monster);
|
||||
applyRemoveAfter(monster); // thanks LightRyuzaki for pointing issues with spawned CWKPQ mobs not applying this
|
||||
}
|
||||
|
||||
@@ -2088,6 +2103,7 @@ public class MapleMap {
|
||||
}
|
||||
|
||||
spawnedMonstersOnMap.incrementAndGet();
|
||||
addSelfDestructive(monster);
|
||||
applyRemoveAfter(monster);
|
||||
}
|
||||
|
||||
@@ -2102,6 +2118,7 @@ public class MapleMap {
|
||||
});
|
||||
|
||||
spawnedMonstersOnMap.incrementAndGet();
|
||||
addSelfDestructive(monster);
|
||||
}
|
||||
|
||||
public void makeMonsterReal(final MapleMonster monster) {
|
||||
@@ -3812,6 +3829,14 @@ public class MapleMap {
|
||||
public boolean getDocked() {
|
||||
return this.docked;
|
||||
}
|
||||
|
||||
public void setSeats(int seats) {
|
||||
this.seats = seats;
|
||||
}
|
||||
|
||||
public int getSeats() {
|
||||
return seats;
|
||||
}
|
||||
|
||||
public void broadcastGMMessage(MapleCharacter source, final byte[] packet, boolean repeatToSource) {
|
||||
broadcastGMMessage(repeatToSource ? null : source, packet, Double.POSITIVE_INFINITY, source.getPosition());
|
||||
@@ -3904,35 +3929,7 @@ public class MapleMap {
|
||||
this.broadcastMessage(MaplePacketCreator.removeItemFromMap(i.getObjectId(), 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
public void addMapTimer(int time) {
|
||||
timeLimit = System.currentTimeMillis() + (time * 1000);
|
||||
broadcastMessage(MaplePacketCreator.getClock(time));
|
||||
mapMonitor = TimerManager.getInstance().register(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (timeLimit != 0 && timeLimit < System.currentTimeMillis()) {
|
||||
warpEveryone(getForcedReturnId());
|
||||
}
|
||||
|
||||
if (getCharacters().isEmpty()) {
|
||||
resetReactors();
|
||||
killAllMonsters();
|
||||
clearDrops();
|
||||
timeLimit = 0;
|
||||
if (mapid >= 922240100 && mapid <= 922240119) {
|
||||
toggleHiddenNPC(9001108);
|
||||
}
|
||||
|
||||
if (mapMonitor != null) {
|
||||
mapMonitor.cancel(true);
|
||||
mapMonitor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
||||
public void setFieldLimit(int fieldLimit) {
|
||||
this.fieldLimit = fieldLimit;
|
||||
}
|
||||
@@ -4079,7 +4076,7 @@ public class MapleMap {
|
||||
return this.mapid >= 109010000 && this.mapid < 109050000 || this.mapid > 109050001 && this.mapid <= 109090000;
|
||||
}
|
||||
|
||||
public void timeMob(int id, String msg) {
|
||||
public void setTimeMob(int id, String msg) {
|
||||
timeMob = new Pair<>(id, msg);
|
||||
}
|
||||
|
||||
@@ -4206,8 +4203,8 @@ public class MapleMap {
|
||||
|
||||
@Override
|
||||
public void monsterDamaged(MapleCharacter from, int trueDmg) {
|
||||
ht.applyAndGetHpDamage(trueDmg, true);
|
||||
ht.broadcastMobHpBar(from);
|
||||
// thanks Halcyon for noticing HT not dropping loots due to propagated damage not registering attacker
|
||||
ht.applyFakeDamage(from, trueDmg, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -4222,7 +4219,9 @@ public class MapleMap {
|
||||
|
||||
public boolean claimOwnership(MapleCharacter chr) {
|
||||
if (mapOwner == null) {
|
||||
mapOwner = chr;
|
||||
this.mapOwner = chr;
|
||||
chr.setOwnedMap(this);
|
||||
|
||||
mapOwnerLastActivityTime = Server.getInstance().getCurrentTime();
|
||||
|
||||
getChannelServer().registerOwnedMap(this);
|
||||
@@ -4234,7 +4233,9 @@ public class MapleMap {
|
||||
|
||||
public boolean unclaimOwnership(MapleCharacter chr) {
|
||||
if (mapOwner == chr) {
|
||||
mapOwner = null;
|
||||
this.mapOwner = null;
|
||||
chr.setOwnedMap(null);
|
||||
|
||||
mapOwnerLastActivityTime = Long.MAX_VALUE;
|
||||
|
||||
getChannelServer().unregisterOwnedMap(this);
|
||||
@@ -4533,11 +4534,6 @@ public class MapleMap {
|
||||
portals.clear();
|
||||
mapEffect = null;
|
||||
|
||||
if(mapMonitor != null) {
|
||||
mapMonitor.cancel(false);
|
||||
mapMonitor = null;
|
||||
}
|
||||
|
||||
chrWLock.lock();
|
||||
try {
|
||||
aggroMonitor.dispose();
|
||||
|
||||
@@ -172,8 +172,7 @@ public class MapleMapFactory {
|
||||
}
|
||||
MapleData timeMob = infoData.getChildByPath("timeMob");
|
||||
if (timeMob != null) {
|
||||
map.timeMob(MapleDataTool.getInt(timeMob.getChildByPath("id")),
|
||||
MapleDataTool.getString(timeMob.getChildByPath("message")));
|
||||
map.setTimeMob(MapleDataTool.getInt(timeMob.getChildByPath("id")), MapleDataTool.getString(timeMob.getChildByPath("message")));
|
||||
}
|
||||
|
||||
int bounds[] = new int[4];
|
||||
@@ -243,6 +242,10 @@ public class MapleMapFactory {
|
||||
map.addMapleArea(new Rectangle(x1, y1, (x2 - x1), (y2 - y1)));
|
||||
}
|
||||
}
|
||||
if (mapData.getChildByPath("seat") != null) {
|
||||
int seats = mapData.getChildByPath("seat").getChildren().size();
|
||||
map.setSeats(seats);
|
||||
}
|
||||
if (event == null) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
@@ -42,9 +42,9 @@ public class MapleMiniDungeon {
|
||||
int baseMap;
|
||||
long expireTime;
|
||||
|
||||
public MapleMiniDungeon(int base, int durationMin) {
|
||||
public MapleMiniDungeon(int base, long timeLimit) {
|
||||
baseMap = base;
|
||||
expireTime = durationMin * 60 * 1000;
|
||||
expireTime = timeLimit * 1000;
|
||||
|
||||
timeoutTask = TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package server.minigame;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Randomizer;
|
||||
|
||||
/**
|
||||
* @Author Arnah
|
||||
* @Website http://Vertisy.ca/
|
||||
* @since Aug 15, 2016
|
||||
*/
|
||||
public class MapleRockPaperScissor{
|
||||
|
||||
private int round = 0;
|
||||
private boolean ableAnswer = true;
|
||||
private boolean win = false;
|
||||
|
||||
public MapleRockPaperScissor(final MapleClient c, final byte mode){
|
||||
c.announce(MaplePacketCreator.rpsMode((byte) (9 + mode)));
|
||||
if(mode == 0){
|
||||
c.getPlayer().gainMeso(-1000, true, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean answer(final MapleClient c, final int answer){
|
||||
if(ableAnswer && !win && answer >= 0 && answer <= 2){
|
||||
final int response = Randomizer.nextInt(3);
|
||||
if(response == answer){
|
||||
c.announce(MaplePacketCreator.rpsSelection((byte) response, (byte) round));
|
||||
// dont do anything. they can still answer once a draw
|
||||
}else if((answer == 0 && response == 2) || (answer == 1 && response == 0) || (answer == 2 && response == 1)){ // they win
|
||||
c.announce(MaplePacketCreator.rpsSelection((byte) response, (byte) (round + 1)));
|
||||
ableAnswer = false;
|
||||
win = true;
|
||||
}else{ // they lose
|
||||
c.announce(MaplePacketCreator.rpsSelection((byte) response, (byte) -1));
|
||||
ableAnswer = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
reward(c);
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean timeOut(final MapleClient c){
|
||||
if(ableAnswer && !win){
|
||||
ableAnswer = false;
|
||||
c.announce(MaplePacketCreator.rpsMode((byte) 0x0A));
|
||||
return true;
|
||||
}
|
||||
reward(c);
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean nextRound(final MapleClient c){
|
||||
if(win){
|
||||
round++;
|
||||
if(round < 10){
|
||||
win = false;
|
||||
ableAnswer = true;
|
||||
c.announce(MaplePacketCreator.rpsMode((byte) 0x0C));
|
||||
return true;
|
||||
} else {
|
||||
round = 10;
|
||||
}
|
||||
}
|
||||
reward(c);
|
||||
return false;
|
||||
}
|
||||
|
||||
public final void reward(final MapleClient c){
|
||||
if(win){
|
||||
MapleInventoryManipulator.addFromDrop(c, new Item(4031332 + round, (short) 0, (short) 1), true);
|
||||
}
|
||||
c.getPlayer().setRPS(null);
|
||||
}
|
||||
|
||||
public final void dispose(final MapleClient c){
|
||||
reward(c);
|
||||
c.announce(MaplePacketCreator.rpsMode((byte) 0x0D));
|
||||
}
|
||||
}
|
||||
@@ -272,7 +272,7 @@ public class AriantColiseum {
|
||||
}
|
||||
|
||||
private void enterKingsRoom() {
|
||||
map.getChannelServer().removeExpedition(exped);
|
||||
exped.removeChannelExpedition(map.getChannelServer());
|
||||
cancelAriantSchedules();
|
||||
|
||||
for (MapleCharacter chr : map.getAllPlayers()) {
|
||||
|
||||
@@ -73,9 +73,9 @@ public class MapleQuest {
|
||||
private boolean repeatable = false;
|
||||
private String name = "", parent = "";
|
||||
private final static MapleDataProvider questData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Quest.wz"));
|
||||
private static MapleData questInfo;
|
||||
private static MapleData questAct;
|
||||
private static MapleData questReq;
|
||||
private final static MapleData questInfo = questData.getData("QuestInfo.img");
|
||||
private final static MapleData questAct = questData.getData("Act.img");
|
||||
private final static MapleData questReq = questData.getData("Check.img");
|
||||
|
||||
private MapleQuest(int id) {
|
||||
this.id = (short) id;
|
||||
@@ -186,10 +186,6 @@ public class MapleQuest {
|
||||
public static MapleQuest getInstance(int id) {
|
||||
MapleQuest ret = quests.get(id);
|
||||
if (ret == null) {
|
||||
questInfo = questData.getData("QuestInfo.img");
|
||||
questReq = questData.getData("Check.img");
|
||||
questAct = questData.getData("Act.img");
|
||||
|
||||
ret = new MapleQuest(id);
|
||||
quests.put(id, ret);
|
||||
}
|
||||
@@ -590,10 +586,6 @@ public class MapleQuest {
|
||||
}
|
||||
|
||||
public static void loadAllQuest() {
|
||||
questInfo = questData.getData("QuestInfo.img");
|
||||
questReq = questData.getData("Check.img");
|
||||
questAct = questData.getData("Act.img");
|
||||
|
||||
try {
|
||||
for(MapleData quest : questInfo.getChildren()) {
|
||||
int questID = Integer.parseInt(quest.getName());
|
||||
|
||||
@@ -110,6 +110,7 @@ import constants.ServerConstants;
|
||||
import constants.skills.Buccaneer;
|
||||
import constants.skills.Corsair;
|
||||
import constants.skills.ThunderBreaker;
|
||||
import scripting.npc.NPCConversationManager;
|
||||
import server.maps.AbstractMapleMapObject;
|
||||
|
||||
/**
|
||||
@@ -479,8 +480,7 @@ public class MaplePacketCreator {
|
||||
equipped.add((Item) item);
|
||||
}
|
||||
}
|
||||
Collections.sort(equipped);
|
||||
for (Item item : equipped) {
|
||||
for (Item item : equipped) { // equipped doesn't actually need sorting, thanks Pllsz
|
||||
addItemInfo(mplew, item);
|
||||
}
|
||||
mplew.writeShort(0); // start of equip cash
|
||||
@@ -1194,7 +1194,7 @@ public class MaplePacketCreator {
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] removeKite(int objectid, int animationType) { // thanks to Arnah
|
||||
public static byte[] removeKite(int objectid, int animationType) { // thanks to Arnah (Vertisy)
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.REMOVE_KITE.getValue());
|
||||
mplew.write(animationType); // 0 is 10/10, 1 just vanishes
|
||||
@@ -2511,25 +2511,14 @@ public class MaplePacketCreator {
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] getScrollEffect(int chr, ScrollResult scrollSuccess, boolean legendarySpirit) {
|
||||
public static byte[] getScrollEffect(int chr, ScrollResult scrollSuccess, boolean legendarySpirit, boolean whiteScroll) { // thanks to Rien dev team
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.SHOW_SCROLL_EFFECT.getValue());
|
||||
mplew.writeInt(chr);
|
||||
switch (scrollSuccess) {
|
||||
case SUCCESS:
|
||||
mplew.writeShort(1);
|
||||
mplew.writeShort(legendarySpirit ? 1 : 0);
|
||||
break;
|
||||
case FAIL:
|
||||
mplew.writeShort(0);
|
||||
mplew.writeShort(legendarySpirit ? 1 : 0);
|
||||
break;
|
||||
case CURSE:
|
||||
mplew.write(0);
|
||||
mplew.write(1);
|
||||
mplew.writeShort(legendarySpirit ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
mplew.writeBool(scrollSuccess == ScrollResult.SUCCESS);
|
||||
mplew.writeBool(scrollSuccess == ScrollResult.CURSE);
|
||||
mplew.writeBool(legendarySpirit);
|
||||
mplew.writeBool(whiteScroll);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
@@ -3441,6 +3430,44 @@ public class MaplePacketCreator {
|
||||
mplew.writeInt(0);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
// thanks NPC Quiz packets thanks to Eric
|
||||
public static byte[] OnAskQuiz(int nSpeakerTypeID, int nSpeakerTemplateID, int nResCode, String sTitle, String sProblemText, String sHintText, int nMinInput, int nMaxInput, int tRemainInitialQuiz) {
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.NPC_TALK.getValue());
|
||||
mplew.write(nSpeakerTypeID);
|
||||
mplew.writeInt(nSpeakerTemplateID);
|
||||
mplew.write(0x6);
|
||||
mplew.write(0);
|
||||
mplew.write(nResCode);
|
||||
if (nResCode == 0x0) {//fail has no bytes <3
|
||||
mplew.writeMapleAsciiString(sTitle);
|
||||
mplew.writeMapleAsciiString(sProblemText);
|
||||
mplew.writeMapleAsciiString(sHintText);
|
||||
mplew.writeShort(nMinInput);
|
||||
mplew.writeShort(nMaxInput);
|
||||
mplew.writeInt(tRemainInitialQuiz);
|
||||
}
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] OnAskSpeedQuiz(int nSpeakerTypeID, int nSpeakerTemplateID, int nResCode, int nType, int dwAnswer, int nCorrect, int nRemain, int tRemainInitialQuiz) {
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.NPC_TALK.getValue());
|
||||
mplew.write(nSpeakerTypeID);
|
||||
mplew.writeInt(nSpeakerTemplateID);
|
||||
mplew.write(0x7);
|
||||
mplew.write(0);
|
||||
mplew.write(nResCode);
|
||||
if (nResCode == 0x0) {//fail has no bytes <3
|
||||
mplew.writeInt(nType);
|
||||
mplew.writeInt(dwAnswer);
|
||||
mplew.writeInt(nCorrect);
|
||||
mplew.writeInt(nRemain);
|
||||
mplew.writeInt(tRemainInitialQuiz);
|
||||
}
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] showBuffeffect(int cid, int skillid, int effectid) {
|
||||
return showBuffeffect(cid, skillid, effectid, (byte) 3);
|
||||
@@ -4246,7 +4273,7 @@ public class MaplePacketCreator {
|
||||
public static byte[] cancelChair(int id) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.CANCEL_CHAIR.getValue());
|
||||
if (id == -1) {
|
||||
if (id < 0) {
|
||||
mplew.write(0);
|
||||
} else {
|
||||
mplew.write(1);
|
||||
@@ -5414,6 +5441,41 @@ public class MaplePacketCreator {
|
||||
}
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
// RPS_GAME packets thanks to Arnah (Vertisy)
|
||||
public static byte[] openRPSNPC() {
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.RPS_GAME.getValue());
|
||||
mplew.write(8);// open npc
|
||||
mplew.writeInt(9000019);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] rpsMesoError(int mesos) {
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.RPS_GAME.getValue());
|
||||
mplew.write(0x06);
|
||||
if (mesos != -1) {
|
||||
mplew.writeInt(mesos);
|
||||
}
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] rpsSelection(byte selection, byte answer) {
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.RPS_GAME.getValue());
|
||||
mplew.write(0x0B);// 11l
|
||||
mplew.write(selection);
|
||||
mplew.write(answer);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] rpsMode(byte mode) {
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.RPS_GAME.getValue());
|
||||
mplew.write(mode);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] fredrickMessage(byte operation) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
@@ -6678,6 +6740,72 @@ public class MaplePacketCreator {
|
||||
mplew.writeShort(quest);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
// MAKER_RESULT packets thanks to Arnah (Vertisy)
|
||||
public static byte[] makerResult(boolean success, int itemMade, int itemCount, int mesos, List<Pair<Integer, Integer>> itemsLost, int catalystID, List<Integer> INCBuffGems) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.MAKER_RESULT.getValue());
|
||||
mplew.writeInt(success ? 0 : 1); // 0 = success, 1 = fail
|
||||
mplew.writeInt(1); // 1 or 2 doesn't matter, same methods
|
||||
mplew.writeBool(!success);
|
||||
if (success) {
|
||||
mplew.writeInt(itemMade);
|
||||
mplew.writeInt(itemCount);
|
||||
}
|
||||
mplew.writeInt(itemsLost.size()); // Loop
|
||||
for (Pair<Integer, Integer> item : itemsLost) {
|
||||
mplew.writeInt(item.getLeft());
|
||||
mplew.writeInt(item.getRight());
|
||||
}
|
||||
mplew.writeInt(INCBuffGems.size());
|
||||
for (Integer gem : INCBuffGems) {
|
||||
mplew.writeInt(gem);
|
||||
}
|
||||
if (catalystID != -1) {
|
||||
mplew.write(1); // stimulator
|
||||
mplew.writeInt(catalystID);
|
||||
} else {
|
||||
mplew.write(0);
|
||||
}
|
||||
|
||||
mplew.writeInt(mesos);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] makerResultCrystal(int itemIdGained, int itemIdLost) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.MAKER_RESULT.getValue());
|
||||
mplew.writeInt(0); // Always successful!
|
||||
mplew.writeInt(3); // Monster Crystal
|
||||
mplew.writeInt(itemIdGained);
|
||||
mplew.writeInt(itemIdLost);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] makerResultDesynth(int itemId, int mesos, List<Pair<Integer, Integer>> itemsGained) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.MAKER_RESULT.getValue());
|
||||
mplew.writeInt(0); // Always successful!
|
||||
mplew.writeInt(4); // Mode Desynth
|
||||
mplew.writeInt(itemId); // Item desynthed
|
||||
mplew.writeInt(itemsGained.size()); // Loop of items gained, (int, int)
|
||||
for (Pair<Integer, Integer> item : itemsGained) {
|
||||
mplew.writeInt(item.getLeft());
|
||||
mplew.writeInt(item.getRight());
|
||||
}
|
||||
mplew.writeInt(mesos); // Mesos spent.
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] makerEnableActions() {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.MAKER_RESULT.getValue());
|
||||
mplew.writeInt(0); // Always successful!
|
||||
mplew.writeInt(0); // Monster Crystal
|
||||
mplew.writeInt(0);
|
||||
mplew.writeInt(0);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] getMultiMegaphone(String[] messages, int channel, boolean showEar) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
@@ -7025,13 +7153,21 @@ public class MaplePacketCreator {
|
||||
mplew.write(remove ? 3 : 4);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] sendDueyParcelReceived(String from, boolean quick) { // thanks inhyuk
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.PARCEL.getValue());
|
||||
mplew.write(0x19);
|
||||
mplew.writeMapleAsciiString(from);
|
||||
mplew.writeBool(quick);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] sendDueyNotification(boolean quickDelivery) {
|
||||
public static byte[] sendDueyParcelNotification(boolean quick) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.PARCEL.getValue());
|
||||
mplew.write(0x1B);
|
||||
mplew.writeBool(quickDelivery); // 0 : package received, 1 : quick delivery package
|
||||
|
||||
mplew.writeBool(quick); // 0 : package received, 1 : quick delivery package
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
@@ -7039,7 +7175,7 @@ public class MaplePacketCreator {
|
||||
return sendDuey(operation, null);
|
||||
}
|
||||
|
||||
public static byte[] sendDuey(byte operation, List<DueyPackage> packages) {
|
||||
public static byte[] sendDuey(int operation, List<DueyPackage> packages) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.PARCEL.getValue());
|
||||
mplew.write(operation);
|
||||
@@ -7261,20 +7397,12 @@ public class MaplePacketCreator {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static byte[] MobDamageMobFriendly(MapleMonster mob, int damage) {
|
||||
public static byte[] MobDamageMobFriendly(MapleMonster mob, int damage, int remainingHp) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.DAMAGE_MONSTER.getValue());
|
||||
mplew.writeInt(mob.getObjectId());
|
||||
mplew.write(1); // direction ?
|
||||
mplew.writeInt(damage);
|
||||
|
||||
mob.applyAndGetHpDamage(damage, false);
|
||||
int remainingHp = mob.getHp();
|
||||
if(remainingHp <= 0) {
|
||||
remainingHp = 0;
|
||||
mob.getMap().removeMapObject(mob);
|
||||
}
|
||||
|
||||
mplew.writeInt(remainingHp);
|
||||
mplew.writeInt(mob.getMaxHp());
|
||||
return mplew.getPacket();
|
||||
|
||||
Reference in New Issue
Block a user