Quest progress overview + Raise UI scripting + Shelved events loadout

Performed a syllabus over quest progress tracking. Quests that were supposed to show up as startable/completable upon achieved progress should be able to do so. Reviewed progress tracking on scripts to adequate to this scenario.
Fixed some scenarios on where quest dialog popups would appear when updating a quest progress.
Fixed some scripts not using updated package addresses after the recent package refactor.
Reviewed Raise UI, no longer rendering players unable to access CS/MTS in certain scenarios.
Fixed a check of available space in inventory, when trying to obtain items from quests, not informing the player it happened due to a one-of-a-kind item already present.
Fixed quest dialog (feature present in many quests) not showing to players when completing it.
Fixed several issues with the Cygnus 1st job advancement quests.
Added scripting within Raise UI open action. Mimiana egg uses this to keep track of player's EXP progress.
Fixed pets not getting despawned as expiration takes place.
Fixed hidden players being able to control mobs when either entering map or hidden state.
Fixed estimated HP/MP alert not taking bonuses (such as from buffs or equipments) into account.
Fixed Energy Charge refreshing buff time upon touching mobs, skewing the  uptime of the skill's stat buffs.
Switched SnakeYaml for YamlBeans, which makes up for a single JAR artifact.
Refactored a channel's event scripts loadout, now taking place after the server bootup phase.
This commit is contained in:
ronancpl
2019-10-03 20:01:09 -03:00
parent eae6dccbc0
commit 7ee947b404
190 changed files with 3281 additions and 1880 deletions
+123 -124
View File
@@ -946,6 +946,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
for(MapleSummon ms: this.getSummonsValues()) {
getMap().broadcastNONGMMessage(this, MaplePacketCreator.spawnSummon(ms, false), false);
}
for (MapleMapObject mo : this.getMap().getMonsters()) {
MapleMonster m = (MapleMonster) mo;
m.aggroUpdateController();
}
} else {
this.hidden = true;
announce(MaplePacketCreator.getGMEffect(0x10, (byte) 1));
@@ -3050,6 +3055,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
deletedCoupon = true;
}
} else {
MaplePet pet = item.getPet(); // thanks Lame for noticing pets not getting despawned after expiration time
if (pet != null) {
unequipPet(pet, true);
}
if (ItemConstants.isExpirablePet(item.getItemId())) {
client.announce(MaplePacketCreator.itemExpired(item.getItemId()));
toberemove.add(item);
@@ -4711,19 +4721,23 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
return client.getAbstractPlayerInteraction();
}
public final List<MapleQuestStatus> getCompletedQuests() {
private List<MapleQuestStatus> getQuests() {
synchronized (quests) {
List<MapleQuestStatus> ret = new LinkedList<>();
for (MapleQuestStatus q : quests.values()) {
if (q.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) {
ret.add(q);
}
}
return Collections.unmodifiableList(ret);
return new ArrayList<>(quests.values());
}
}
public final List<MapleQuestStatus> getCompletedQuests() {
List<MapleQuestStatus> ret = new LinkedList<>();
for (MapleQuestStatus qs : getQuests()) {
if (qs.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) {
ret.add(qs);
}
}
return Collections.unmodifiableList(ret);
}
public List<MapleRing> getCrushRings() {
Collections.sort(crushRings);
return crushRings;
@@ -5780,19 +5794,19 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
}
public final MapleQuestStatus getMapleQuestStatus(final int quest) {
synchronized (quests) {
MapleQuestStatus mqs = quests.get((short) quest);
return mqs;
}
public MapleQuestStatus getQuest(final int quest) {
return getQuest(MapleQuest.getInstance(quest));
}
public MapleQuestStatus getQuest(MapleQuest quest) {
synchronized (quests) {
if (!quests.containsKey(quest.getId())) {
return new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED);
short questid = quest.getId();
MapleQuestStatus qs = quests.get(questid);
if (qs == null) {
qs = new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED);
quests.put(questid, qs);
}
return quests.get(quest.getId());
return qs;
}
}
@@ -5928,32 +5942,15 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
public final List<MapleQuestStatus> getStartedQuests() {
synchronized (quests) {
List<MapleQuestStatus> ret = new LinkedList<>();
for (MapleQuestStatus q : quests.values()) {
if (q.getStatus().equals(MapleQuestStatus.Status.STARTED)) {
ret.add(q);
}
List<MapleQuestStatus> ret = new LinkedList<>();
for (MapleQuestStatus qs : getQuests()) {
if (qs.getStatus().equals(MapleQuestStatus.Status.STARTED)) {
ret.add(qs);
}
return Collections.unmodifiableList(ret);
}
return Collections.unmodifiableList(ret);
}
public final int getStartedQuestsSize() {
synchronized (quests) {
int i = 0;
for (MapleQuestStatus q : quests.values()) {
if (q.getStatus().equals(MapleQuestStatus.Status.STARTED)) {
if (q.getQuest().getInfoNumber() > 0) {
i++;
}
i++;
}
}
return i;
}
}
public MapleStatEffect getStatForBuff(MapleBuffStat effect) {
effLock.lock();
chrLock.lock();
@@ -7491,7 +7488,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
public void reloadQuestExpirations() {
for(MapleQuestStatus mqs: quests.values()) {
for(MapleQuestStatus mqs: getQuests()) {
if(mqs.getExpirationTime() > 0) {
questTimeLimit2(mqs.getQuest(), mqs.getExpirationTime());
}
@@ -7545,31 +7542,31 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
announce(MaplePacketCreator.sendYellowTip(m));
}
public void updateQuestMobCount(int id) {
public void raiseQuestMobCount(int id) {
// It seems nexon uses monsters that don't exist in the WZ (except string) to merge multiple mobs together for these 3 monsters.
// We also want to run mobKilled for both since there are some quest that don't use the updated ID...
if (id == 1110100 || id == 1110130) {
updateQuestMobCount(9101000);
raiseQuestMobCount(9101000);
} else if (id == 2230101 || id == 2230131) {
updateQuestMobCount(9101001);
raiseQuestMobCount(9101001);
} else if (id == 1140100 || id == 1140130) {
updateQuestMobCount(9101002);
raiseQuestMobCount(9101002);
}
int lastQuestProcessed = 0;
try {
synchronized (quests) {
for (MapleQuestStatus q : quests.values()) {
lastQuestProcessed = q.getQuest().getId();
if (q.getStatus() == MapleQuestStatus.Status.COMPLETED || q.getQuest().canComplete(this, null)) {
for (MapleQuestStatus qs : getQuests()) {
lastQuestProcessed = qs.getQuest().getId();
if (qs.getStatus() == MapleQuestStatus.Status.COMPLETED || qs.getQuest().canComplete(this, null)) {
continue;
}
String progress = q.getProgress(id);
if (!progress.isEmpty() && Integer.parseInt(progress) >= q.getQuest().getMobAmountNeeded(id)) {
continue;
}
if (q.progress(id)) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, q, false);
if (qs.progress(id)) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false);
if (qs.getInfoNumber() > 0) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true);
}
}
}
}
@@ -8692,36 +8689,33 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
psf = con.prepareStatement("INSERT INTO medalmaps VALUES (DEFAULT, ?, ?, ?)");
ps.setInt(1, id);
synchronized (quests) {
for (MapleQuestStatus q : quests.values()) {
ps.setInt(2, q.getQuest().getId());
ps.setInt(3, q.getStatus().getId());
ps.setInt(4, (int) (q.getCompletionTime() / 1000));
ps.setLong(5, q.getExpirationTime());
ps.setInt(6, q.getForfeited());
ps.setInt(7, q.getCompleted());
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
rs.next();
for (int mob : q.getProgress().keySet()) {
pse.setInt(1, id);
pse.setInt(2, rs.getInt(1));
pse.setInt(3, mob);
pse.setString(4, q.getProgress(mob));
pse.addBatch();
}
for (int i = 0; i < q.getMedalMaps().size(); i++) {
psf.setInt(1, id);
psf.setInt(2, rs.getInt(1));
psf.setInt(3, q.getMedalMaps().get(i));
psf.addBatch();
}
pse.executeBatch();
psf.executeBatch();
for (MapleQuestStatus qs : getQuests()) {
ps.setInt(2, qs.getQuest().getId());
ps.setInt(3, qs.getStatus().getId());
ps.setInt(4, (int) (qs.getCompletionTime() / 1000));
ps.setLong(5, qs.getExpirationTime());
ps.setInt(6, qs.getForfeited());
ps.setInt(7, qs.getCompleted());
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
rs.next();
for (int mob : qs.getProgress().keySet()) {
pse.setInt(1, id);
pse.setInt(2, rs.getInt(1));
pse.setInt(3, mob);
pse.setString(4, qs.getProgress(mob));
pse.addBatch();
}
for (int i = 0; i < qs.getMedalMaps().size(); i++) {
psf.setInt(1, id);
psf.setInt(2, rs.getInt(1));
psf.setInt(3, qs.getMedalMaps().get(i));
psf.addBatch();
}
pse.executeBatch();
psf.executeBatch();
}
}
}
psf.close();
ps.close();
@@ -9149,7 +9143,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
MapleKeyBinding autohpPot = this.getKeymap().get(91);
if (autohpPot != null) {
int autohpItemid = autohpPot.getAction();
if (((float) this.getHp()) / this.getMaxHp() <= this.getAutopotHpAlert()) { // try within user settings... thanks Lame, Optimist, Stealth2800
if (((float) this.getHp()) / this.getCurrentMaxHp() <= this.getAutopotHpAlert()) { // try within user settings... thanks Lame, Optimist, Stealth2800
Item autohpItem = this.getInventory(MapleInventoryType.USE).findById(autohpItemid);
if (autohpItem != null) {
PetAutopotProcessor.runAutopotAction(client, autohpItem.getPosition(), autohpItemid);
@@ -9162,7 +9156,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
MapleKeyBinding autompPot = this.getKeymap().get(92);
if (autompPot != null) {
int autompItemid = autompPot.getAction();
if (((float) this.getMp()) / this.getMaxMp() <= this.getAutopotMpAlert()) {
if (((float) this.getMp()) / this.getCurrentMaxMp() <= this.getAutopotMpAlert()) {
Item autompItem = this.getInventory(MapleInventoryType.USE).findById(autompItemid);
if (autompItem != null) {
PetAutopotProcessor.runAutopotAction(client, autompItem.getPosition(), autompItemid);
@@ -9845,26 +9839,23 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
}
}
public String getQuestInfo(int quest) {
MapleQuestStatus qs = getQuest(MapleQuest.getInstance(quest));
return qs.getInfo();
}
public void updateQuestInfo(int quest, String info) {
MapleQuest q = MapleQuest.getInstance(quest);
public void setQuestProgress(int id, int infoNumber, String progress) {
MapleQuest q = MapleQuest.getInstance(id);
MapleQuestStatus qs = getQuest(q);
qs.setInfo(info);
synchronized (quests) {
quests.put(q.getId(), qs);
if (qs.getInfoNumber() == infoNumber && infoNumber > 0) {
MapleQuest iq = MapleQuest.getInstance(infoNumber);
MapleQuestStatus iqs = getQuest(iq);
iqs.setProgress(0, progress);
} else {
qs.setProgress(infoNumber, progress); // quest progress is thoroughly a string match, infoNumber is actually another questid
}
announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false);
if (qs.getQuest().getInfoNumber() > 0) {
if (qs.getInfoNumber() > 0) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true);
}
announce(MaplePacketCreator.updateQuestInfo((short) qs.getQuest().getId(), qs.getNpc()));
}
public void awardQuestPoint(int awardedPoints) {
@@ -9886,15 +9877,15 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
public enum DelayedQuestUpdate { // quest updates allow player actions during NPC talk...
UPDATE, FORFEIT, COMPLETE
UPDATE, FORFEIT, COMPLETE, INFO
}
private void announceUpdateQuestInternal(Pair<DelayedQuestUpdate, Object[]> questUpdate) {
private void announceUpdateQuestInternal(MapleCharacter chr, Pair<DelayedQuestUpdate, Object[]> questUpdate) {
Object[] objs = questUpdate.getRight();
switch (questUpdate.getLeft()) {
case UPDATE:
announce(MaplePacketCreator.updateQuest((MapleQuestStatus) objs[0], (Boolean) objs[1]));
announce(MaplePacketCreator.updateQuest(chr, (MapleQuestStatus) objs[0], (Boolean) objs[1]));
break;
case FORFEIT:
@@ -9904,6 +9895,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
case COMPLETE:
announce(MaplePacketCreator.completeQuest((Short) objs[0], (Long) objs[1]));
break;
case INFO:
MapleQuestStatus qs = (MapleQuestStatus) objs[0];
announce(MaplePacketCreator.updateQuestInfo(qs.getQuest().getId(), qs.getNpc()));
break;
}
}
@@ -9915,7 +9911,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
npcUpdateQuests.add(p);
}
} else {
announceUpdateQuestInternal(p);
announceUpdateQuestInternal(this, p);
}
}
@@ -9928,49 +9924,52 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
for (Pair<DelayedQuestUpdate, Object[]> q : qmQuestUpdateList) {
announceUpdateQuestInternal(q);
announceUpdateQuestInternal(this, q);
}
}
public void updateQuest(MapleQuestStatus quest) {
public void updateQuestStatus(MapleQuestStatus qs) {
synchronized (quests) {
quests.put(quest.getQuestID(), quest);
quests.put(qs.getQuestID(), qs);
}
if (quest.getStatus().equals(MapleQuestStatus.Status.STARTED)) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, quest, false);
if (quest.getQuest().getInfoNumber() > 0) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, quest, true);
if (qs.getStatus().equals(MapleQuestStatus.Status.STARTED)) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false);
if (qs.getInfoNumber() > 0) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true);
}
announce(MaplePacketCreator.updateQuestInfo((short) quest.getQuest().getId(), quest.getNpc()));
} else if (quest.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) {
MapleQuest mquest = quest.getQuest();
announceUpdateQuest(DelayedQuestUpdate.INFO, qs);
} else if (qs.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) {
MapleQuest mquest = qs.getQuest();
short questid = mquest.getId();
if (!mquest.isSameDayRepeatable() && !MapleQuest.isExploitableQuest(questid)) {
awardQuestPoint(YamlConfig.config.server.QUEST_POINT_PER_QUEST_COMPLETE);
}
quest.setCompleted(quest.getCompleted() + 1); // count quest completed Jayd's idea
qs.setCompleted(qs.getCompleted() + 1); // count quest completed Jayd's idea
announceUpdateQuest(DelayedQuestUpdate.COMPLETE, questid, quest.getCompletionTime());
} else if (quest.getStatus().equals(MapleQuestStatus.Status.NOT_STARTED)) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, quest, false);
if (quest.getQuest().getInfoNumber() > 0) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, quest, true);
announceUpdateQuest(DelayedQuestUpdate.COMPLETE, questid, qs.getCompletionTime());
announceUpdateQuest(DelayedQuestUpdate.INFO, qs);
} else if (qs.getStatus().equals(MapleQuestStatus.Status.NOT_STARTED)) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false);
if (qs.getInfoNumber() > 0) {
announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true);
}
}
}
private void expireQuest(MapleQuest quest) {
if(getQuestStatus(quest.getId()) == MapleQuestStatus.Status.COMPLETED.getId()) {
MapleQuestStatus mqs = getQuest(quest);
if(mqs.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) {
return;
}
if(System.currentTimeMillis() < getMapleQuestStatus(quest.getId()).getExpirationTime()) {
if(System.currentTimeMillis() < mqs.getExpirationTime()) {
return;
}
announce(MaplePacketCreator.questExpire(quest.getId()));
MapleQuestStatus newStatus = new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED);
newStatus.setForfeited(getQuest(quest).getForfeited() + 1);
updateQuest(newStatus);
newStatus.setForfeited(mqs.getForfeited() + 1);
updateQuestStatus(newStatus);
}
public void cancelQuestExpirationTask() {
@@ -11016,7 +11015,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET world = ?, meso = ?, guildid = ?, guildrank = ? WHERE id = ?")) {
ps.setInt(1, newWorld);
ps.setInt(2, Math.min(mesos, 1000000)); //might want a limit in YamlConfig.config.server.for this
ps.setInt(2, Math.min(mesos, 1000000)); // might want a limit in "YamlConfig.config.server" for this
ps.setInt(3, 0);
ps.setInt(4, 5);
ps.setInt(5, characterId);
+1 -1
View File
@@ -1002,7 +1002,7 @@ public class MapleClient {
if (quest.getTimeLimit() > 0) {
MapleQuestStatus newStatus = new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED);
newStatus.setForfeited(player.getQuest(quest).getForfeited() + 1);
player.updateQuest(newStatus);
player.updateQuestStatus(newStatus);
}
}
if (guild != null) {
+42 -28
View File
@@ -151,14 +151,20 @@ public class MapleQuestStatus {
}
public boolean progress(int id) {
if (progress.get(id) != null) {
int current = Integer.parseInt(progress.get(id));
String str = StringUtil.getLeftPaddedStr(Integer.toString(current + 1), '0', 3);
progress.put(id, str);
//this.setUpdated();
return true;
String currentStr = progress.get(id);
if (currentStr == null) {
return false;
}
return false;
int current = Integer.parseInt(currentStr);
if (current >= this.getQuest().getMobAmountNeeded(id)) {
return false;
}
String str = StringUtil.getLeftPaddedStr(Integer.toString(++current), '0', 3);
progress.put(id, str);
//this.setUpdated();
return true;
}
public void setProgress(int id, String pr) {
@@ -169,15 +175,14 @@ public class MapleQuestStatus {
public boolean madeProgress() {
return progress.size() > 0;
}
public Integer getAnyProgressKey() {
if (!progress.isEmpty()) return progress.entrySet().iterator().next().getKey();
return 0;
}
public String getProgress(int id) {
if (progress.get(id) == null) return "";
return progress.get(id);
String ret = progress.get(id);
if (ret == null) {
return "";
} else {
return ret;
}
}
public void resetProgress(int id) {
@@ -193,6 +198,27 @@ public class MapleQuestStatus {
public Map<Integer, String> getProgress() {
return Collections.unmodifiableMap(progress);
}
public short getInfoNumber() {
MapleQuest q = this.getQuest();
Status s = this.getStatus();
return q.getInfoNumber(s);
}
public String getInfoEx(int index) {
MapleQuest q = this.getQuest();
Status s = this.getStatus();
return q.getInfoEx(s, index);
}
public List<String> getInfoEx() {
MapleQuest q = this.getQuest();
Status s = this.getStatus();
return q.getInfoEx(s);
}
public long getCompletionTime() {
return completionTime;
@@ -217,18 +243,6 @@ public class MapleQuestStatus {
public int getCompleted() {
return completed;
}
public String getInfo() {
if(!progress.containsKey(0) && !getMedalMaps().isEmpty()) {
return Integer.toString(getMedalProgress());
}
return getProgress(0);
}
public void setInfo(String newInfo) {
progress.put(0, newInfo);
//this.setUpdated();
}
public void setForfeited(int forfeited) {
if (forfeited >= this.forfeited) {
@@ -254,11 +268,11 @@ public class MapleQuestStatus {
return customData;
}
public String getQuestData() {
public String getProgressData() {
StringBuilder str = new StringBuilder();
for (String ps : progress.values()) {
str.append(ps);
}
return str.toString();
}
}
}
@@ -25,7 +25,7 @@ package client.command.commands.gm5;
import client.command.Command;
import client.MapleClient;
import config.YamlConfig;
import constants.net.ServerConstants;
public class SetCommand extends Command {
{
@@ -35,7 +35,7 @@ public class SetCommand extends Command {
@Override
public void execute(MapleClient c, String[] params) {
for (int i = 0; i < params.length; i++) {
YamlConfig.config.server.DEBUG_VALUES[i] = Integer.parseInt(params[i]);
ServerConstants.DEBUG_VALUES[i] = Integer.parseInt(params[i]);
}
}
}
-2
View File
@@ -301,6 +301,4 @@ public class ServerConfig {
//Event End Timestamp
public long EVENT_END_TIMESTAMP;
//Debug Variables
public int DEBUG_VALUES[];
}
+7 -5
View File
@@ -1,10 +1,9 @@
package config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.esotericsoftware.yamlbeans.YamlReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
@@ -12,13 +11,16 @@ import java.util.List;
public class YamlConfig {
public static final YamlConfig config = fromFile("config.yaml");
public List<WorldConfig> worlds;
public ServerConfig server;
public static YamlConfig fromFile(String filename) {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
try {
return mapper.readValue(new File(filename), YamlConfig.class);
YamlReader reader = new YamlReader(new FileReader(filename));
YamlConfig config = reader.read(YamlConfig.class);
reader.close();
return config;
} catch (FileNotFoundException e) {
String message = "Could not read config file " + filename + ": " + e.getMessage();
throw new RuntimeException(message);
+2 -299
View File
@@ -1,313 +1,16 @@
package constants.net;
public class ServerConstants {
//Server Version
public static short VERSION = 83;
//Java Configuration
public static final boolean JAVA_8 = getJavaVersion() >= 8; //Max amount of times a party leader is allowed to persist on the Party Search before entry expiration (thus needing to manually restart the Party Search to be able to search for members).
//Login Configuration
public static final int WLDLIST_SIZE = 21; //Max possible worlds on the server.
public static final int CHANNEL_SIZE = 20; //Max possible channels per world (which is 20, based on the channel list on login phase).
public static final int CHANNEL_LOAD = 100; //Max players per channel (limit actually used to calculate the World server capacity).
public static final int CHANNEL_LOCKS = 20; //Total number of structure management locks each channel has.
public static final long RESPAWN_INTERVAL = 10 * 1000; //10 seconds, 10000.
public static final long PURGING_INTERVAL = 5 * 60 * 1000;
public static final long RANKING_INTERVAL = 60 * 60 * 1000; //60 minutes, 3600000.
public static final long COUPON_INTERVAL = 60 * 60 * 1000; //60 minutes, 3600000.
public static final long UPDATE_INTERVAL = 777; //Dictates the frequency on which the "centralized server time" is updated.
public static final boolean ENABLE_PIC = false; //Pick true/false to enable or disable Pic. Delete character requires PIC available.
public static final boolean ENABLE_PIN = false; //Pick true/false to enable or disable Pin.
public static final int BYPASS_PIC_EXPIRATION = 20; //Enables PIC bypass, which will remain active for that account by that client machine for N minutes. Set 0 to disable.
public static final int BYPASS_PIN_EXPIRATION = 15; //Enables PIN bypass, which will remain active for that account by that client machine for N minutes. Set 0 to disable.
public static final boolean AUTOMATIC_REGISTER = true; //Automatically register players when they login with a nonexistent username.
public static final boolean BCRYPT_MIGRATION = true; //Performs a migration from old SHA-1 and SHA-512 password to bcrypt.
public static final boolean COLLECTIVE_CHARSLOT = false; //Available character slots are contabilized globally rather than per world server.
public static final boolean DETERRED_MULTICLIENT = false; //Enables detection of multi-client and suspicious remote IP on the login system.
//Besides blocking logging in with several client sessions on the same machine, this also blocks suspicious login attempts for players that tries to login on an account using several diferent remote addresses.
//Multiclient Coordinator Configuration
public static final int MAX_ALLOWED_ACCOUNT_HWID = 4; //Allows up to N concurrent HWID's for an account. HWID's remains linked to an account longer the more times it's used to login.
public static final int MAX_ACCOUNT_LOGIN_ATTEMPT = 15; //After N tries on an account, login on that account gets disabled for a short period.
public static final int LOGIN_ATTEMPT_DURATION = 120; //Period in seconds the login attempt remains registered on the system.
//Ip Configuration
public static String HOST;
public static boolean LOCALSERVER;
//Other Configuration
public static boolean SHUTDOWNHOOK;
//Server Flags
public static final boolean USE_CUSTOM_KEYSET = true; //Enables auto-setup of the HeavenMS's custom keybindings when creating characters.
public static final boolean USE_DEBUG = false; //Will enable some text prints on the client, oriented for debugging purposes.
public static final boolean USE_DEBUG_SHOW_INFO_EQPEXP = false; //Prints on the cmd all equip exp gain info.
public static boolean USE_DEBUG_SHOW_RCVD_PACKET = false; //Prints on the cmd all received packet ids.
public static boolean USE_DEBUG_SHOW_RCVD_MVLIFE = false; //Prints on the cmd all received move life content.
public static final boolean USE_DEBUG_SHOW_PACKET = false;
public static boolean USE_SUPPLY_RATE_COUPONS = true; //Allows rate coupons to be sold through the Cash Shop.
public static final boolean USE_IP_VALIDATION = true; //Enables IP checking when logging in.
public static final boolean USE_MAXRANGE = true; //Will send and receive packets from all events on a map, rather than those of only view range.
public static final boolean USE_MAXRANGE_ECHO_OF_HERO = true;
public static final boolean USE_MTS = false;
public static final boolean USE_CPQ = true; //Renders the CPQ available or not.
public static final boolean USE_AUTOHIDE_GM = false; //When enabled, GMs are automatically hidden when joining. Thanks to Steven Deblois (steven1152).
public static final boolean USE_BUYBACK_SYSTEM = true; //Enables the HeavenMS-builtin buyback system, to be used by dead players when clicking the MTS button.
public static final boolean USE_FIXED_RATIO_HPMP_UPDATE = true; //Enables the HeavenMS-builtin HPMP update based on the current pool to max pool ratio.
public static final boolean USE_FAMILY_SYSTEM = true;
public static final boolean USE_DUEY = true;
public static final boolean USE_RANDOMIZE_HPMP_GAIN = true; //Enables randomizing on MaxHP/MaxMP gains and INT accounting for the MaxMP gain on level up.
public static final boolean USE_STORAGE_ITEM_SORT = true; //Enables storage "Arrange Items" feature.
public static final boolean USE_ITEM_SORT = true; //Enables inventory "Item Sort/Merge" feature.
public static final boolean USE_ITEM_SORT_BY_NAME = false; //Item sorting based on name rather than id.
public static final boolean USE_PARTY_FOR_STARTERS = true; //Players level 10 or below can create/invite other players on the given level range.
public static final boolean USE_AUTOASSIGN_STARTERS_AP = false; //Beginners level 10 or below have their AP autoassigned (they can't choose to levelup a stat). Set true ONLY if the localhost doesn't support AP assigning for beginners level 10 or below.
public static final boolean USE_AUTOASSIGN_SECONDARY_CAP = true;//Prevents AP autoassign from spending on secondary stats after the player class' cap (defined on the autoassign handler) has been reached.
public static final boolean USE_STARTING_AP_4 = true; //Use early-GMS 4/4/4/4 starting stats. To overcome AP shortage, this gives 4AP/5AP at 1st/2nd job advancements.
public static final boolean USE_AUTOBAN = false; //Commands the server to detect infractors automatically.
public static final boolean USE_AUTOBAN_LOG = true; //Log autoban related messages. Still logs even with USE_AUTOBAN disabled.
public static final boolean USE_AUTOSAVE = true; //Enables server autosaving feature (saves characters to DB each 1 hour).
public static final boolean USE_SERVER_AUTOASSIGNER = true; //HeavenMS-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments.
public static final boolean USE_REFRESH_RANK_MOVE = true;
public static final boolean USE_ENFORCE_ADMIN_ACCOUNT = false; //Forces accounts having GM characters to be treated as a "GM account" by the client (localhost). Some of the GM account perks is the ability to FLY, but unable to TRADE.
public static final boolean USE_ENFORCE_NOVICE_EXPRATE = false; //Hardsets experience rate 1x for beginners level 10 or under. Ideal for roaming on novice areas without caring too much about losing some stats.
public static final boolean USE_ENFORCE_HPMP_SWAP = false; //Forces players to reuse stats (via AP Resetting) located on HP/MP pool only inside the HP/MP stats.
public static final boolean USE_ENFORCE_MOB_LEVEL_RANGE = true; //Players N levels below the killed mob will gain no experience from defeating it.
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 = true;//Forces players to not sell CASH items via merchants, drops of it disappears.
public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants, drops of it disappears. (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.
public static final boolean USE_SPAWN_CLEAN_MDOOR = false; //Makes mystic doors to be spawned without deploy animation. This clears disconnecting issues that may happen when trying to cancel doors a couple seconds after deployment.
public static final boolean USE_SPAWN_LOOT_ON_ANIMATION = false;//Makes loot appear some time after the mob has been killed (following the mob death animation, instead of instantly).
public static final boolean USE_SPAWN_RELEVANT_LOOT = true; //Forces to only spawn loots that are collectable by the player or any of their party members.
public static final boolean USE_ERASE_PERMIT_ON_OPENSHOP = true;//Forces "shop permit" item to be consumed when player deploy his/her player shop.
public static final boolean USE_ERASE_UNTRADEABLE_DROP = true; //Forces flagged untradeable items to disappear when dropped.
public static final boolean USE_ERASE_PET_ON_EXPIRATION = false;//Forces pets to be removed from inventory when expire time comes, rather than converting it to a doll.
public static final boolean USE_BUFF_MOST_SIGNIFICANT = true; //When applying buffs, the player will stick with the highest stat boost among the listed, rather than overwriting stats.
public static final boolean USE_BUFF_EVERLASTING = false; //Every applied buff on players holds expiration time so high it'd be considered permanent. Suggestion thanks to Vcoc.
public static final boolean USE_MULTIPLE_SAME_EQUIP_DROP = true;//Enables multiple drops by mobs of the same equipment, number of possible drops based on the quantities provided at the drop data.
public static final boolean USE_BANISHABLE_TOWN_SCROLL = true; //Enables town scrolls to act as if it's a "player banish", rendering the antibanish scroll effect available.
public static final boolean USE_ENABLE_FULL_RESPAWN = true; //At respawn task, always respawn missing mobs when they're available. Spawn count doesn't depend on how many players are currently there.
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 custom fishing system
public static final boolean USE_NPCS_SCRIPTABLE = true; //Flag to enable/disable serverside predefined script NPCs.
//Events/PQs Configuration
public static final boolean USE_OLD_GMS_STYLED_PQ_NPCS = true; //Enables PQ NPCs with similar behaviour to old GMS style, that skips info about the PQs and immediately tries to register the party in.
public static final boolean USE_ENABLE_SOLO_EXPEDITIONS = true; //Enables start expeditions with any number of players. This will also bypass all the Zakum prequest.
public static final boolean USE_ENABLE_DAILY_EXPEDITIONS = false;//Enables daily entry limitations in expeditions.
public static final boolean USE_ENABLE_RECALL_EVENT = false; //Enables a disconnected player to reaccess the last event instance they were in before logging out. Recall only works if the event isn't cleared or disposed yet. Suggestion thanks to Alisson (Goukken).
//Announcement Configuration
public static final boolean USE_ANNOUNCE_SHOPITEMSOLD = false; //Automatic message sent to owner when an item from the Player Shop or Hired Merchant is sold.
public static final boolean USE_ANNOUNCE_CHANGEJOB = false; //Automatic message sent to acquantainces when changing jobs.
//Cash Shop Configuration
public static final boolean USE_JOINT_CASHSHOP_INVENTORY = true;//Enables usage of a same cash shop inventory for explorers, cygnus and legends. Items from exclusive cash shop inventories won't show up on the shared inventory, though.
public static final boolean USE_CLEAR_OUTDATED_COUPONS = true; //Enables deletion of older code coupon registry from the DB, freeing so-long irrelevant data.
public static final boolean ALLOW_CASHSHOP_NAME_CHANGE = true; //Allows players to buy name changes in the cash shop.
public static final boolean ALLOW_CASHSHOP_WORLD_TRANSFER =true;//Allows players to buy world transfers in the cash shop.
//Maker Configuration
public static final boolean USE_MAKER_PERMISSIVE_ATKUP = true; //Allows players to use attack-based strengthening gems on non-weapon items.
public static final boolean USE_MAKER_FEE_HEURISTICS = true; //Apply compiled values for stimulants and reagents into the Maker fee calculations (max error revolves around 50k mesos). Set false to use basic constant values instead (results are never higher than requested by the client-side).
//Custom Configuration
public static final boolean USE_ENABLE_CUSTOM_NPC_SCRIPT = true;//Enables usage of custom HeavenMS NPC scripts (Agent E, Coco, etc). Will not disable Abdula (it's actually useful for the gameplay) or quests.
public static final boolean USE_STARTER_MERGE = false; //Allows any players to use the Equipment Merge custom mechanic (as opposed to the high-level, Maker lv3 requisites).
//Commands Configuration
public static final boolean BLOCK_GENERATE_CASH_ITEM = false; //Prevents creation of cash items with the item/drop command.
public static final boolean USE_WHOLE_SERVER_RANKING = false; //Enables a ranking pool made from every character registered on the server for the "ranks" command, instead of separated by worlds.
//Server Rates And Experience
public static final int EXP_RATE = 10; //NOTE: World-specific rates within "world.ini" OVERRIDES the default rates from here.
public static final int MESO_RATE = 10;
public static final int DROP_RATE = 10;
public static final int BOSS_DROP_RATE = 10; //NOTE: Boss drop rate OVERRIDES common drop rate, for bosses-only.
public static final int QUEST_RATE = 5; //Multiplier for Exp & Meso gains when completing a quest. Only available when USE_QUEST_RATE is true. Stacks with server Exp & Meso rates.
public static final int FISHING_RATE = 10; //Multiplier for success likelihood on meso thrown during fishing.
public static final int TRAVEL_RATE = 10; //Means of transportation rides/departs using 1/N of the default time.
public static final double EQUIP_EXP_RATE = 1.0; //Rate for equipment exp gain, grows linearly. Set 1.0 for default (about 100~200 same-level range mobs killed to pass equip from level 1 to 2).
public static final double PQ_BONUS_EXP_RATE = 0.5; //Rate for the PQ exp reward.
public static final byte EXP_SPLIT_LEVEL_INTERVAL = 5; //Non-contributing players must be within N level between the mob to receive EXP.
public static final byte EXP_SPLIT_LEECH_INTERVAL = 5; //Non-contributing players must be within N level between any contributing party member to receive EXP.
public static final float EXP_SPLIT_MVP_MOD = 0.2f;
public static final float EXP_SPLIT_COMMON_MOD = 0.8f;
public static final float PARTY_BONUS_EXP_RATE = 1.0f; //Rate for the party exp bonus reward.
//Miscellaneous Configuration
public static String TIMEZONE = "GMT-3";
public static boolean USE_DISPLAY_NUMBERS_WITH_COMMA = true; //Enforce comma on displayed strings (use this when USE_UNITPRICE_WITH_COMMA is active and you still want to display comma-separated values).
public static boolean USE_UNITPRICE_WITH_COMMA = true; //Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions.
public static final byte MAX_MONITORED_BUFFSTATS = 5; //Limits accounting for "dormant" buff effects, that should take place when stronger stat buffs expires.
public static final int MAX_AP = 32767; //Max AP allotted on the auto-assigner.
public static final int MAX_EVENT_LEVELS = 8; //Event has different levels of rewarding system.
public static final long BLOCK_NPC_RACE_CONDT = (long)(0.5 * 1000); //Time the player client must wait before reopening a conversation with an NPC.
public static final long PET_LOOT_UPON_ATTACK = (long)(0.7 * 1000); //Time the pet must wait before trying to pick items up.
public static final int TOT_MOB_QUEST_REQUIREMENT = 77; //Overwrites old 999-mobs requirement for the ToT questline with new requirement value, set 0 for default.
public static final int MOB_REACTOR_REFRESH_TIME = 30 * 1000; //Overwrites refresh time for those reactors oriented to inflict damage to bosses (Ice Queen, Riche), set 0 for default.
public static final int PARTY_SEARCH_REENTRY_LIMIT = 10; //Max amount of times a party leader is allowed to persist on the Party Search before entry expiration (thus needing to manually restart the Party Search to be able to search for members).
public static final int NAME_CHANGE_COOLDOWN = 30*24*60*60*1000; //Cooldown for name changes, default (GMS) is 30 days.
public static final int WORLD_TRANSFER_COOLDOWN=NAME_CHANGE_COOLDOWN;//Cooldown for world tranfers, default is same as name change (30 days).
public static final boolean INSTANT_NAME_CHANGE = false; //Whether or not to wait for server restart to apply name changes. Does on reconnect otherwise (requires queries on every login).
//Dangling Items/Locks Configuration
public static final int ITEM_EXPIRE_TIME = 3 * 60 * 1000; //Time before items start disappearing. Recommended to be set up to 3 minutes.
public static final int KITE_EXPIRE_TIME = 60 * 60 * 1000; //Time before kites (cash item) disappears.
public static final int ITEM_MONITOR_TIME = 5 * 60 * 1000; //Interval between item monitoring tasks on maps, which checks for dangling (null) item objects on the map item history.
public static final int LOCK_MONITOR_TIME = 30 * 1000; //Waiting time for a lock to be released. If it reaches timeout, a critical server deadlock has made present.
//Map Monitor Configuration
public static final int ITEM_EXPIRE_CHECK = 10 * 1000; //Interval between item expiring tasks on maps, which checks and makes disappear expired items.
public static final int ITEM_LIMIT_ON_MAP = 200; //Max number of items allowed on a map.
public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids visited by a player. This is used to recover and update drops on these maps accordingly with player actions.
public static final int MAP_DAMAGE_OVERTIME_INTERVAL = 5000;//Interval in milliseconds between map environment damage (e.g. El Nath and Aqua Road surrondings).
//Channel Mob Disease Monitor Configuration
public static final int MOB_STATUS_MONITOR_PROC = 200; //Frequency in milliseconds between each proc on the mob disease monitor schedule.
public static final int MOB_STATUS_MONITOR_LIFE = 84; //Idle proc count the mob disease monitor is allowed to be there before closing it due to inactivity.
public static final int MOB_STATUS_AGGRO_PERSISTENCE = 2; //Idle proc count on aggro update for a mob to keep following the current controller, given him/her is the leading damage dealer.
public static final int MOB_STATUS_AGGRO_INTERVAL = 5000; //Interval in milliseconds between aggro logistics update.
//Some Gameplay Enhancing Configurations
//Scroll Configuration
public static final boolean USE_PERFECT_GM_SCROLL = true; //Scrolls from GMs never uses up slots nor fails.
public static final boolean USE_PERFECT_SCROLLING = true; //Scrolls doesn't use slots upon failure.
public static final boolean USE_ENHANCED_CHSCROLL = true; //Equips even more powerful with chaos upgrade.
public static final boolean USE_ENHANCED_CRAFTING = true; //Apply chaos scroll on every equip crafted.
public static final boolean USE_ENHANCED_CLNSLATE = true; //Clean slates can be applied to recover successfully used slots as well.
public static final int SCROLL_CHANCE_ROLLS = 10; //Number of rolls for success on a scroll, set 1 for default.
public static final int CHSCROLL_STAT_ROLLS = 3; //Number of rolls of stat upgrade on a successfully applied chaos scroll, set 1 for default.
public static final int CHSCROLL_STAT_RANGE = 6; //Stat upgrade range (-N, N) on chaos scrolls.
//Beginner Skills Configuration
public static final boolean USE_ULTRA_NIMBLE_FEET = true; //Massive speed & jump upgrade.
public static final boolean USE_ULTRA_RECOVERY = true; //Massive recovery amounts overtime.
public static final boolean USE_ULTRA_THREE_SNAILS = true; //Massive damage on shell toss.
//Other Skills Configuration
public static final boolean USE_FULL_ARAN_SKILLSET = false; //Enables starter availability to all Aran job skills. Suggestion thanks to Masterrulax.
public static final boolean USE_FAST_REUSE_HERO_WILL = true;//Greatly reduce cooldown on Hero's Will.
public static final boolean USE_ANTI_IMMUNITY_CRASH = true; //Crash skills additionally removes the mob's invincibility buffs. Suggestion thanks to Celestial.
public static final boolean USE_UNDISPEL_HOLY_SHIELD = true;//Holy shield buff also prevents players from suffering dispel from mobs.
public static final boolean USE_FULL_HOLY_SYMBOL = true; //Holy symbol doesn't require EXP sharers to work in full.
//Character Configuration
public static final boolean USE_ADD_SLOTS_BY_LEVEL = true; //Slots are added each 20 levels.
public static final boolean USE_ADD_RATES_BY_LEVEL = true; //Rates are added each 20 levels.
public static final boolean USE_STACK_COUPON_RATES = false; //Multiple coupons effects builds up together.
public static final boolean USE_PERFECT_PITCH = true; //For lvl 30 or above, each lvlup grants player 1 perfect pitch.
//Quest Configuration
public static final boolean USE_QUEST_RATE = false; //Exp/Meso gained by quests uses fixed server exp/meso rate times quest rate as multiplier, instead of player rates.
//Quest Points Configuration
public static final int QUEST_POINT_REPEATABLE_INTERVAL = 24;//Minimum interval between repeatable quest completions for quest points to be awarded.
public static final int QUEST_POINT_REQUIREMENT = 16; //Exchange factor between N quest points to +1 fame, set 0 to disable the entire quest point mechanism.
public static final int QUEST_POINT_PER_QUEST_COMPLETE = 4; //Each completed quest awards N quest points, set 0 to disable.
public static final int QUEST_POINT_PER_EVENT_CLEAR = 1; //Each completed event instance awards N quest points, set 0 to disable.
//Guild Configuration
public static final int CREATE_GUILD_MIN_PARTNERS = 6; //Minimum number of members on Guild Headquarters to establish a new guild.
public static final int CREATE_GUILD_COST = 1500000;
public static final int CHANGE_EMBLEM_COST = 5000000;
public static final int EXPAND_GUILD_BASE_COST = 500000;
public static final int EXPAND_GUILD_TIER_COST = 1000000;
public static final int EXPAND_GUILD_MAX_COST = 5000000;
//Family Configuration
public static final int FAMILY_REP_PER_KILL = 4; //Amount of rep gained per monster kill.
public static final int FAMILY_REP_PER_BOSS_KILL = 20; //Amount of rep gained per boss kill.
public static final int FAMILY_REP_PER_LEVELUP = 200; //Amount of rep gained upon leveling up.
public static final int FAMILY_MAX_GENERATIONS = 1000; //Maximum depth of family tree. (Distance from leader to farthest junior)
//Equipment Configuration
public static final boolean USE_EQUIPMNT_LVLUP_SLOTS = true;//Equips can upgrade slots at level up.
public static final boolean USE_EQUIPMNT_LVLUP_POWER = true;//Enable more powerful stat upgrades at equip level up.
public static final boolean USE_EQUIPMNT_LVLUP_CASH = true; //Enable equip leveling up on cash equipments as well.
public static final boolean USE_SPIKES_AVOID_BANISH = true; //Shoes equipped with spikes prevents mobs from banishing wearer.
public static final int MAX_EQUIPMNT_LVLUP_STAT_UP = 10000; //Max stat upgrade an equipment can have on a levelup.
public static final int MAX_EQUIPMNT_STAT = 32767; //Max stat on an equipment by leveling up.
public static final int USE_EQUIPMNT_LVLUP = 7; //All equips lvlup at max level of N, set 1 to disable.
//Map-Chair Configuration
public static final boolean USE_CHAIR_EXTRAHEAL = true; //Enable map chairs to further recover player's HP and MP (player must have the Chair Mastery skill).
public static final byte CHAIR_EXTRA_HEAL_MULTIPLIER = 10; //Due to only being able to be send up-to-255 heal values, values being actually updated is the one displayed times this.
public static final int CHAIR_EXTRA_HEAL_MAX_DELAY = 21; //Players are expected to recover fully after using this skill for N seconds.
//Player NPC Configuration
public static final int PLAYERNPC_INITIAL_X = 262; //Map frame width for putting PlayerNPCs.
public static final int PLAYERNPC_INITIAL_Y = 262; //Map frame height for putting PlayerNPCs.
public static final int PLAYERNPC_AREA_X = 320; //Initial width gap between PlayerNPCs.
public static final int PLAYERNPC_AREA_Y = 160; //Initial height gap between PlayerNPCs.
public static final int PLAYERNPC_AREA_STEPS = 4; //Max number of times gap is shortened to comport PlayerNPCs.
public static final boolean PLAYERNPC_ORGANIZE_AREA = true; //Automatically rearranges PlayerNPCs on the map if there is no space set the new NPC. Current distance gap between NPCs is decreased to solve this issue.
public static final boolean PLAYERNPC_AUTODEPLOY = true; //Makes PlayerNPC automatically deployed on the Hall of Fame at the instant one reaches max level. If false, eligible players must talk to 1st job instructor to deploy a NPC.
//Pet Auto-Pot Configuration
public static final boolean USE_COMPULSORY_AUTOPOT = true; //Pets will consume as many potions as needed to fulfill the AUTOHP/MP ratio threshold.
public static final boolean USE_EQUIPS_ON_AUTOPOT = true; //Player MaxHP and MaxMP check values on autopot handler will be updated by the HP/MP bonuses on equipped items.
public static final double PET_AUTOHP_RATIO = 0.99; //Will automatically consume potions until given ratio of the MaxHP/MaxMP is reached.
public static final double PET_AUTOMP_RATIO = 0.99;
//Pet & Mount Configuration
public static final byte PET_EXHAUST_COUNT = 3; //Number of proc counts (1 per minute) on the exhaust schedule for fullness.
public static final byte MOUNT_EXHAUST_COUNT = 1; //Number of proc counts (1 per minute) on the exhaust schedule for tiredness.
//Pet Hunger Configuration
public static final boolean PETS_NEVER_HUNGRY = false; //If true, pets and mounts will never grow hungry.
public static final boolean GM_PETS_NEVER_HUNGRY = true; //If true, pets and mounts owned by GMs will never grow hungry.
//Event Configuration
public static final int EVENT_MAX_GUILD_QUEUE = 10; //Max number of guilds in queue for GPQ.
public static final long EVENT_LOBBY_DELAY = 10; //Cooldown duration in seconds before reopening an event lobby.
//Dojo Configuration
public static final boolean USE_FAST_DOJO_UPGRADE = true; //Reduced Dojo training points amount required for a belt upgrade.
public static final boolean USE_DEADLY_DOJO = false; //Should bosses really use 1HP,1MP attacks in dojo?
public static final int DOJO_ENERGY_ATK = 100; //Dojo energy gain when deal attack
public static final int DOJO_ENERGY_DMG = 20; //Dojo energy gain when recv attack
//Wedding Configuration
public static final int WEDDING_RESERVATION_DELAY = 3; //Minimum idle slots before processing a wedding reservation.
public static final int WEDDING_RESERVATION_TIMEOUT = 10; //Limit time in minutes for the couple to show up before cancelling the wedding reservation.
public static final int WEDDING_RESERVATION_INTERVAL = 60; //Time between wedding starts in minutes.
public static final int WEDDING_BLESS_EXP = 30000; //Exp gained per bless count.
public static final int WEDDING_GIFT_LIMIT = 1; //Max number of gifts per person to same wishlist on marriage instances.
public static final boolean WEDDING_BLESSER_SHOWFX = true; //Pops bubble sprite effect on players blessing the couple. Setting this false shows the blessing effect on the couple instead.
//Buyback Configuration
public static final boolean USE_BUYBACK_WITH_MESOS = true; //Enables usage of either mesos or NX for the buyback fee.
public static final float BUYBACK_FEE = 77.70f; //Sets the base amount needed to buyback (level 30 or under will use the base value).
public static final float BUYBACK_LEVEL_STACK_FEE = 85.47f; //Sets the level-stacking portion of the amount needed to buyback (fee will sum up linearly until level 120, when it reaches the peak).
public static final int BUYBACK_MESO_MULTIPLIER = 1000; //Sets a multiplier for the fee when using meso as the charge unit.
public static final int BUYBACK_RETURN_MINUTES = 1; //Sets the maximum amount of time the player can wait before decide to buyback.
public static final int BUYBACK_COOLDOWN_MINUTES = 7; //Sets the time the player must wait before using buyback again.
// Login timeout by shavit
public static long TIMEOUT_DURATION = 3600000L; // Kicks clients who don't send any packet to the game server in due time (in millisseconds).
//Event End Timestamp
public static final long EVENT_END_TIMESTAMP = 1428897600000L;
//Debug Variables
public static int DEBUG_VALUES[] = new int[10]; // Field designed for packet testing purposes
// https://github.com/openstreetmap/josm/blob/a3a6e8a6b657cf4c5b4c64ea14d6e87be6280d65/src/org/openstreetmap/josm/tools/Utils.java#L1566-L1585
/**
* Returns the Java version as an int value.
+1
View File
@@ -29,5 +29,6 @@ public class FPWizard {
public static final int MP_EATER = 2100000;
public static final int MEDITATION = 2101001;
public static final int SLOW = 2101003;
public static final int FIRE_ARROW = 2101004;
public static final int POISON_BREATH = 2101005;
}
+9 -5
View File
@@ -862,7 +862,7 @@ public class Server {
}
applyAllNameChanges(); //name changes can be missed by INSTANT_NAME_CHANGE
applyAllWorldTransfers();
MaplePet.clearMissingPetsFromDb();
//MaplePet.clearMissingPetsFromDb(); // thanks Optimist for noticing this taking too long to run
MapleCashidGenerator.loadExistentCashIdsFromDb();
IoBuffer.setUseDirectBuffer(false);
@@ -877,17 +877,17 @@ public class Server {
disconnectIdlesOnLoginTask();
long timeLeft = getTimeLeftForNextHour();
tMan.register(new CharacterDiseaseTask(), ServerConstants.UPDATE_INTERVAL, ServerConstants.UPDATE_INTERVAL);
tMan.register(new CharacterDiseaseTask(), YamlConfig.config.server.UPDATE_INTERVAL, YamlConfig.config.server.UPDATE_INTERVAL);
tMan.register(new ReleaseLockTask(), 2 * 60 * 1000, 2 * 60 * 1000);
tMan.register(new CouponTask(), ServerConstants.COUPON_INTERVAL, timeLeft);
tMan.register(new CouponTask(), YamlConfig.config.server.COUPON_INTERVAL, timeLeft);
tMan.register(new RankingCommandTask(), 5 * 60 * 1000, 5 * 60 * 1000);
tMan.register(new RankingLoginTask(), ServerConstants.RANKING_INTERVAL, timeLeft);
tMan.register(new RankingLoginTask(), YamlConfig.config.server.RANKING_INTERVAL, timeLeft);
tMan.register(new LoginCoordinatorTask(), 60 * 60 * 1000, timeLeft);
tMan.register(new EventRecallCoordinatorTask(), 60 * 60 * 1000, timeLeft);
tMan.register(new LoginStorageTask(), 2 * 60 * 1000, 2 * 60 * 1000);
tMan.register(new DueyFredrickTask(), 60 * 60 * 1000, timeLeft);
tMan.register(new InvitationTask(), 30 * 1000, 30 * 1000);
tMan.register(new RespawnTask(), ServerConstants.RESPAWN_INTERVAL, ServerConstants.RESPAWN_INTERVAL);
tMan.register(new RespawnTask(), YamlConfig.config.server.RESPAWN_INTERVAL, YamlConfig.config.server.RESPAWN_INTERVAL);
timeLeft = getTimeLeftForNextDay();
MapleExpeditionBossLog.resetBossLogTable();
@@ -952,6 +952,10 @@ public class Server {
MapleSkillbookInformationProvider.getInstance();
OpcodeConstants.generateOpcodeNames();
CommandsExecutor.getInstance();
for (Channel ch : this.getAllChannels()) {
ch.reloadEventScriptManager();
}
}
public static void main(String args[]) {
+14 -10
View File
@@ -21,13 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.channel;
import net.server.channel.task.FaceExpressionScheduler;
import net.server.channel.task.MobMistScheduler;
import net.server.channel.task.OverallScheduler;
import net.server.channel.task.MobAnimationScheduler;
import net.server.channel.task.MobStatusScheduler;
import net.server.channel.task.MobClearSkillScheduler;
import net.server.channel.task.EventScheduler;
import java.io.File;
import java.net.InetSocketAddress;
import java.util.ArrayList;
@@ -57,6 +50,7 @@ import net.mina.MapleCodecFactory;
import net.server.PlayerStorage;
import net.server.Server;
import net.server.channel.task.*;
import net.server.world.World;
import net.server.world.MapleParty;
@@ -84,7 +78,6 @@ import tools.MaplePacketCreator;
import tools.Pair;
import client.MapleCharacter;
import client.status.MonsterStatusEffect;
import constants.net.ServerConstants;
import server.maps.MapleMiniDungeonInfo;
public final class Channel {
@@ -148,7 +141,6 @@ public final class Channel {
this.ongoingStartTime = startTime + 10000; // rude approach to a world's last channel boot time, placeholder for the 1st wedding reservation ever
this.mapManager = new MapleMapManager(null, world, channel);
try {
eventSM = new EventScriptManager(this, getEvents());
port = 7575 + this.channel - 1;
port += (world * 100);
ip = YamlConfig.config.server.HOST + ":" + port;
@@ -163,7 +155,14 @@ public final class Channel {
for (MapleExpeditionType exped : MapleExpeditionType.values()) {
expedType.add(exped);
}
eventSM.init();
if (Server.getInstance().isOnline()) { // postpone event loading to improve boot time... thanks Riizade, daronhudson for noticing slow startup times
eventSM = new EventScriptManager(this, getEvents());
eventSM.init();
} else {
String[] ev = {};
eventSM = new EventScriptManager(null, ev);
}
dojoStage = new int[20];
dojoFinishTime = new long[20];
@@ -493,6 +492,11 @@ public final class Channel {
return getPlayerStorage().getCharacterByName(name) != null;
}
public boolean isActive() {
EventScriptManager esm = this.getEventSM();
return esm != null && esm.isActive();
}
public boolean finishedShutdown() {
return finishedShutdown;
}
@@ -163,9 +163,11 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
int mobCount = attackEffect.getMobCount();
if (attack.skill != Cleric.HEAL) {
if (player.isAlive()) {
if(attack.skill == NightWalker.POISON_BOMB) {// Poison Bomb
if(attack.skill == Aran.BODY_PRESSURE || attack.skill == Marauder.ENERGY_CHARGE || attack.skill == ThunderBreaker.ENERGY_CHARGE) { // thanks IxianMace for noticing Energy Charge skills refreshing on touch, leading to misleading buff applies
// prevent touch dmg skills refreshing
} else if(attack.skill == NightWalker.POISON_BOMB) {// Poison Bomb
attackEffect.applyTo(player, new Point(attack.position.x, attack.position.y));
} else if(attack.skill != Aran.BODY_PRESSURE) {// prevent BP refreshing
} else {
attackEffect.applyTo(player);
if (attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Page.FINAL_ATTACK_BW || attack.skill == Page.FINAL_ATTACK_SWORD || attack.skill == Fighter.FINAL_ATTACK_SWORD
@@ -34,10 +34,11 @@ import server.movement.LifeMovementFragment;
import server.movement.RelativeLifeMovement;
import server.movement.TeleportMovement;
import tools.data.input.LittleEndianAccessor;
import tools.exceptions.EmptyMovementException;
public abstract class AbstractMovementPacketHandler extends AbstractMaplePacketHandler {
protected List<LifeMovementFragment> parseMovement(LittleEndianAccessor lea) {
protected List<LifeMovementFragment> parseMovement(LittleEndianAccessor lea) throws EmptyMovementException {
List<LifeMovementFragment> res = new ArrayList<>();
byte numCommands = lea.readByte();
for (byte i = 0; i < numCommands; i++) {
@@ -138,15 +139,20 @@ public abstract class AbstractMovementPacketHandler extends AbstractMaplePacketH
}
default:
System.out.println("Unhandled Case:" + command);
return null;
throw new EmptyMovementException(lea);
}
}
if (res.isEmpty()) {
throw new EmptyMovementException(lea);
}
return res;
}
protected void updatePosition(LittleEndianAccessor lea, AnimatedMapleMapObject target, int yOffset) {
protected void updatePosition(LittleEndianAccessor lea, AnimatedMapleMapObject target, int yOffset) throws EmptyMovementException {
byte numCommands = lea.readByte();
if (numCommands < 1) throw new EmptyMovementException(lea);
for (byte i = 0; i < numCommands; i++) {
byte command = lea.readByte();
switch (command) {
@@ -233,6 +239,5 @@ public abstract class AbstractMovementPacketHandler extends AbstractMaplePacketH
return;
}
}
return;
}
}
@@ -149,8 +149,8 @@ public final class BBSOperationHandler extends AbstractMaplePacketHandler {
}
private static void editBBSThread(MapleClient client, String title, String text, int icon, int localthreadid) {
MapleCharacter c = client.getPlayer();
if (c.getGuildId() < 1) {
MapleCharacter chr = client.getPlayer();
if (chr.getGuildId() < 1) {
return;
}
try {
@@ -160,10 +160,10 @@ public final class BBSOperationHandler extends AbstractMaplePacketHandler {
ps.setLong(2, currentServerTime());
ps.setInt(3, icon);
ps.setString(4, text);
ps.setInt(5, c.getGuildId());
ps.setInt(5, chr.getGuildId());
ps.setInt(6, localthreadid);
ps.setInt(7, c.getId());
ps.setBoolean(8, c.getGuildRank() < 3);
ps.setInt(7, chr.getId());
ps.setBoolean(8, chr.getGuildRank() < 3);
ps.execute();
}
con.close();
@@ -174,8 +174,8 @@ public final class BBSOperationHandler extends AbstractMaplePacketHandler {
}
private static void newBBSThread(MapleClient client, String title, String text, int icon, boolean bNotice) {
MapleCharacter c = client.getPlayer();
if (c.getGuildId() <= 0) {
MapleCharacter chr = client.getPlayer();
if (chr.getGuildId() <= 0) {
return;
}
int nextId = 0;
@@ -184,7 +184,7 @@ public final class BBSOperationHandler extends AbstractMaplePacketHandler {
PreparedStatement ps;
if (!bNotice) {
ps = con.prepareStatement("SELECT MAX(localthreadid) AS lastLocalId FROM bbs_threads WHERE guildid = ?");
ps.setInt(1, c.getGuildId());
ps.setInt(1, chr.getGuildId());
try (ResultSet rs = ps.executeQuery()) {
rs.next();
nextId = rs.getInt("lastLocalId") + 1;
@@ -192,12 +192,12 @@ public final class BBSOperationHandler extends AbstractMaplePacketHandler {
ps.close();
}
ps = con.prepareStatement("INSERT INTO bbs_threads " + "(`postercid`, `name`, `timestamp`, `icon`, `startpost`, " + "`guildid`, `localthreadid`) " + "VALUES(?, ?, ?, ?, ?, ?, ?)");
ps.setInt(1, c.getId());
ps.setInt(1, chr.getId());
ps.setString(2, title);
ps.setLong(3, currentServerTime());
ps.setInt(4, icon);
ps.setString(5, text);
ps.setInt(6, c.getGuildId());
ps.setInt(6, chr.getGuildId());
ps.setInt(7, nextId);
ps.execute();
ps.close();
@@ -30,7 +30,6 @@ import client.MapleCharacter;
import client.MapleClient;
import client.Skill;
import client.SkillFactory;
import constants.net.ServerConstants;
import constants.skills.Bishop;
import constants.skills.Evan;
import constants.skills.FPArchMage;
@@ -63,13 +62,9 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler {
c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy()));
}
byte[] packet;
if ((attack.skill == Evan.FIRE_BREATH || attack.skill == Evan.ICE_BREATH || attack.skill == FPArchMage.BIG_BANG || attack.skill == ILArchMage.BIG_BANG || attack.skill == Bishop.BIG_BANG)) {
packet = MaplePacketCreator.magicAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, attack.allDamage, attack.charge, attack.speed, attack.direction, attack.display);
} else {
packet = MaplePacketCreator.closeRangeAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, attack.allDamage, attack.speed, attack.direction, attack.display);
}
int charge = (attack.skill == Evan.FIRE_BREATH || attack.skill == Evan.ICE_BREATH || attack.skill == FPArchMage.BIG_BANG || attack.skill == ILArchMage.BIG_BANG || attack.skill == Bishop.BIG_BANG) ? attack.charge : -1;
byte[] packet = MaplePacketCreator.magicAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, attack.allDamage, charge, attack.speed, attack.direction, attack.display);
chr.getMap().broadcastMessage(chr, packet, false, true);
MapleStatEffect effect = attack.getAttackEffect(chr, null);
Skill skill = SkillFactory.getSkill(attack.skill);
@@ -28,6 +28,7 @@ import client.MapleClient;
import server.maps.MapleDragon;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.exceptions.EmptyMovementException;
public class MoveDragonHandler extends AbstractMovementPacketHandler {
@@ -35,19 +36,20 @@ public class MoveDragonHandler extends AbstractMovementPacketHandler {
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
final MapleCharacter chr = c.getPlayer();
final Point startPos = new Point(slea.readShort(), slea.readShort());
long movementDataStart = slea.getPosition();
final MapleDragon dragon = chr.getDragon();
if (dragon != null) {
updatePosition(slea, dragon, 0);
long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
if (movementDataLength > 0) {
try {
long movementDataStart = slea.getPosition();
updatePosition(slea, dragon, 0);
long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
slea.seek(movementDataStart);
if (chr.isHidden()) {
chr.getMap().broadcastGMMessage(chr, MaplePacketCreator.moveDragon(dragon, startPos, slea, movementDataLength));
} else {
chr.getMap().broadcastMessage(chr, MaplePacketCreator.moveDragon(dragon, startPos, slea, movementDataLength), dragon.getPosition());
}
}
} catch (EmptyMovementException e) {}
}
}
}
@@ -41,6 +41,7 @@ import tools.MaplePacketCreator;
import tools.Pair;
import tools.Randomizer;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.exceptions.EmptyMovementException;
/**
* @author Danny (Leifde)
@@ -142,11 +143,7 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
short start_y = slea.readShort(); // hmm...
Point startPos = new Point(start_x, start_y - 2);
Point serverStartPos = new Point(monster.getPosition());
long movementDataStart = slea.getPosition();
updatePosition(slea, monster, -2); // Thanks Doodle and ZERO傑洛 for noticing sponge-based bosses moving out of stage in case of no-offset applied
long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
Boolean aggro = monster.aggroMoveLifeUpdate(player);
if (aggro == null) return;
@@ -156,15 +153,21 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro));
}
if (movementDataLength > 0) {
if (YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_MVLIFE) {
System.out.println((isSkill ? "SKILL " : (isAttack ? "ATTCK " : " ")) + "castPos: " + castPos + " rawAct: " + rawActivity + " opt: " + pOption + " skillID: " + useSkillId + " skillLV: " + useSkillLevel + " " + "allowSkill: " + nextMovementCouldBeSkill + " mobMp: " + mobMp);
}
slea.seek(movementDataStart);
map.broadcastMessage(player, MaplePacketCreator.moveMonster(objectid, nextMovementCouldBeSkill, rawActivity, useSkillId, useSkillLevel, pOption, startPos, slea, movementDataLength), serverStartPos);
//updatePosition(res, monster, -2); //does this need to be done after the packet is broadcast?
map.moveMonster(monster, monster.getPosition());
}
try {
long movementDataStart = slea.getPosition();
updatePosition(slea, monster, -2); // Thanks Doodle and ZERO傑洛 for noticing sponge-based bosses moving out of stage in case of no-offset applied
long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
slea.seek(movementDataStart);
if (YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_MVLIFE) {
System.out.println((isSkill ? "SKILL " : (isAttack ? "ATTCK " : " ")) + "castPos: " + castPos + " rawAct: " + rawActivity + " opt: " + pOption + " skillID: " + useSkillId + " skillLV: " + useSkillLevel + " " + "allowSkill: " + nextMovementCouldBeSkill + " mobMp: " + mobMp);
}
map.broadcastMessage(player, MaplePacketCreator.moveMonster(objectid, nextMovementCouldBeSkill, rawActivity, useSkillId, useSkillLevel, pOption, startPos, slea, movementDataLength), serverStartPos);
//updatePosition(res, monster, -2); //does this need to be done after the packet is broadcast?
map.moveMonster(monster, monster.getPosition());
} catch (EmptyMovementException e) {}
if (banishPlayers != null) {
for (MapleCharacter chr : banishPlayers) {
@@ -27,6 +27,7 @@ import client.MapleClient;
import server.movement.LifeMovementFragment;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.exceptions.EmptyMovementException;
public final class MovePetHandler extends AbstractMovementPacketHandler {
@Override
@@ -34,8 +35,11 @@ public final class MovePetHandler extends AbstractMovementPacketHandler {
int petId = slea.readInt();
slea.readLong();
// Point startPos = StreamUtil.readShortPoint(slea);
List<LifeMovementFragment> res = parseMovement(slea);
if (res == null || res.isEmpty()) {
List<LifeMovementFragment> res;
try {
res = parseMovement(slea);
} catch (EmptyMovementException e) {
return;
}
MapleCharacter player = c.getPlayer();
@@ -24,22 +24,24 @@ package net.server.channel.handlers;
import client.MapleClient;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.exceptions.EmptyMovementException;
public final class MovePlayerHandler extends AbstractMovementPacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.skip(9);
long movementDataStart = slea.getPosition();
updatePosition(slea, c.getPlayer(), 0);
long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
if (movementDataLength > 0) {
slea.seek(movementDataStart);
try { // thanks Sa for noticing empty movement sequences crashing players
long movementDataStart = slea.getPosition();
updatePosition(slea, c.getPlayer(), 0);
long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
slea.seek(movementDataStart);
c.getPlayer().getMap().movePlayer(c.getPlayer(), c.getPlayer().getPosition());
if (c.getPlayer().isHidden()) {
c.getPlayer().getMap().broadcastGMMessage(c.getPlayer(), MaplePacketCreator.movePlayer(c.getPlayer().getId(), slea, movementDataLength), false);
} else {
c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.movePlayer(c.getPlayer().getId(), slea, movementDataLength), false);
}
}
} catch (EmptyMovementException e) {}
}
}
@@ -29,6 +29,7 @@ import client.MapleClient;
import server.maps.MapleSummon;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.exceptions.EmptyMovementException;
public final class MoveSummonHandler extends AbstractMovementPacketHandler {
@Override
@@ -45,11 +46,14 @@ public final class MoveSummonHandler extends AbstractMovementPacketHandler {
}
}
if (summon != null) {
long movementDataStart = slea.getPosition();
updatePosition(slea, summon, 0);
long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
slea.seek(movementDataStart);
player.getMap().broadcastMessage(player, MaplePacketCreator.moveSummon(player.getId(), oid, startPos, slea, movementDataLength), summon.getPosition());
try {
long movementDataStart = slea.getPosition();
updatePosition(slea, summon, 0);
long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
slea.seek(movementDataStart);
player.getMap().broadcastMessage(player, MaplePacketCreator.moveSummon(player.getId(), oid, startPos, slea, movementDataLength), summon.getPosition());
} catch (EmptyMovementException e) {}
}
}
}
@@ -43,12 +43,12 @@ public final class PetAutoPotHandler extends AbstractMaplePacketHandler {
MapleStatEffect stat = MapleItemInformationProvider.getInstance().getItemEffect(itemId);
if (stat.getHp() > 0 || stat.getHpRate() > 0.0) {
float estimatedHp = ((float) chr.getHp()) / chr.getMaxHp();
chr.setAutopotHpAlert(estimatedHp);
chr.setAutopotHpAlert(estimatedHp + 0.05f);
}
if (stat.getMp() > 0 || stat.getMpRate() > 0.0) {
float estimatedMp = ((float) chr.getMp()) / chr.getMaxMp();
chr.setAutopotMpAlert(estimatedMp);
chr.setAutopotMpAlert(estimatedMp + 0.05f);
}
PetAutopotProcessor.runAutopotAction(c, slot, itemId);
@@ -116,13 +116,16 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
}
Channel cserv = wserv.getChannel(c.getChannel());
if(cserv == null) {
if(cserv == null || !cserv.isActive()) {
c.setChannel(1);
cserv = wserv.getChannel(c.getChannel());
if(cserv == null) {
c.disconnect(true, false);
return;
} else if (!cserv.isActive()) {
c.announce(MaplePacketCreator.getAfterLoginError(7));
return;
}
}
@@ -51,19 +51,21 @@ public final class PlayerMapTransitionHandler extends AbstractMaplePacketHandler
chr.announce(MaplePacketCreator.giveBuff(1, beaconid, stat));
}
for (MapleMapObject mo : chr.getMap().getMonsters()) { // thanks BHB, IxianMace, Jefe for noticing several issues regarding mob statuses (such as freeze)
MapleMonster m = (MapleMonster) mo;
if (m.getSpawnEffect() == 0 || m.getHp() < m.getMaxHp()) { // avoid effect-spawning mobs
if (m.getController() == chr) {
c.announce(MaplePacketCreator.stopControllingMonster(m.getObjectId()));
m.sendDestroyData(c);
m.aggroRedirectController();
} else {
m.sendDestroyData(c);
}
if (!chr.isHidden()) { // thanks Lame for noticing hidden characters controlling mobs
for (MapleMapObject mo : chr.getMap().getMonsters()) { // thanks BHB, IxianMace, Jefe for noticing several issues regarding mob statuses (such as freeze)
MapleMonster m = (MapleMonster) mo;
if (m.getSpawnEffect() == 0 || m.getHp() < m.getMaxHp()) { // avoid effect-spawning mobs
if (m.getController() == chr) {
c.announce(MaplePacketCreator.stopControllingMonster(m.getObjectId()));
m.sendDestroyData(c);
m.aggroRedirectController();
} else {
m.sendDestroyData(c);
}
m.aggroSwitchController(chr, false);
m.sendSpawnData(c);
m.aggroSwitchController(chr, false);
m.sendSpawnData(c);
}
}
}
}
@@ -2,13 +2,17 @@ package net.server.channel.handlers;
import java.util.Map;
import client.MapleCharacter;
import client.MapleClient;
import client.MapleQuestStatus;
import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import net.AbstractMaplePacketHandler;
import client.inventory.manipulator.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
import server.MapleItemInformationProvider.QuestConsItem;
import server.quest.MapleQuest;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
/**
@@ -22,7 +26,7 @@ public class RaiseIncExpHandler extends AbstractMaplePacketHandler {
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
byte inventorytype = slea.readByte();//nItemIT
short slot = slea.readShort();//nSlotPosition
int itemid = slea.readInt();//nItemID
int itemid = slea.readInt();//nItemID
if (c.tryacquireClient()) {
try {
@@ -32,15 +36,22 @@ public class RaiseIncExpHandler extends AbstractMaplePacketHandler {
return;
}
int questid = consItem.questid;
int infoNumber = consItem.questid;
Map<Integer, Integer> consumables = consItem.items;
MapleCharacter chr = c.getPlayer();
MapleQuest quest = MapleQuest.getInstanceFromInfoNumber(infoNumber);
if (!chr.getQuest(quest).getStatus().equals(MapleQuestStatus.Status.STARTED)) {
c.announce(MaplePacketCreator.enableActions());
return;
}
int consId;
MapleInventory inv = c.getPlayer().getInventory(MapleInventoryType.getByType(inventorytype));
MapleInventory inv = chr.getInventory(MapleInventoryType.getByType(inventorytype));
inv.lockInventory();
try {
consId = inv.getItem(slot).getItemId();
if (!consumables.containsKey(consId) || !c.getPlayer().haveItem(consId)) {
if (!consumables.containsKey(consId) || !chr.haveItem(consId)) {
return;
}
@@ -48,9 +59,12 @@ public class RaiseIncExpHandler extends AbstractMaplePacketHandler {
} finally {
inv.unlockInventory();
}
int nextValue = Math.min(consumables.get(consId) + Integer.parseInt(c.getPlayer().getQuestInfo(questid)), consItem.exp * consItem.grade);
c.getPlayer().updateQuestInfo(questid, "" + nextValue);
int questid = quest.getId();
int nextValue = Math.min(consumables.get(consId) + c.getAbstractPlayerInteraction().getQuestProgressInt(questid, infoNumber), consItem.exp * consItem.grade);
c.getAbstractPlayerInteraction().setQuestProgress(questid, infoNumber, nextValue);
c.announce(MaplePacketCreator.enableActions());
} finally {
c.releaseClient();
}
@@ -1,9 +1,11 @@
package net.server.channel.handlers;
import client.MapleCharacter.DelayedQuestUpdate;
import client.MapleCharacter;
import client.MapleClient;
import client.MapleQuestStatus;
import net.AbstractMaplePacketHandler;
import scripting.quest.QuestScriptManager;
import server.quest.MapleQuest;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -15,19 +17,21 @@ public class RaiseUIStateHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
int questid = slea.readShort();
int infoNumber = slea.readShort();
if (c.tryacquireClient()) {
try {
MapleQuest quest = MapleQuest.getInstance(questid);
MapleQuestStatus mqs = c.getPlayer().getQuest(quest);
MapleCharacter chr = c.getPlayer();
MapleQuest quest = MapleQuest.getInstanceFromInfoNumber(infoNumber);
MapleQuestStatus mqs = chr.getQuest(quest);
QuestScriptManager.getInstance().raiseOpen(c, (short) infoNumber, mqs.getNpc());
if (mqs.getStatus() == MapleQuestStatus.Status.NOT_STARTED) {
quest.forceStart(c.getPlayer(), 22000);
c.getPlayer().updateQuestInfo(quest.getId(), "0");
quest.forceStart(chr, 22000);
c.getAbstractPlayerInteraction().setQuestProgress(quest.getId(), infoNumber, 0);
} else if (mqs.getStatus() == MapleQuestStatus.Status.STARTED) {
c.getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, mqs, false);
} else {
//c.announce(MaplePacketCreator.updateQuestInfo(mqs.getQuestID(), 22000, "0"));
chr.announceUpdateQuest(DelayedQuestUpdate.UPDATE, mqs, mqs.getInfoNumber() > 0);
}
} finally {
c.releaseClient();
+13 -13
View File
@@ -35,22 +35,22 @@ public class MapleGuildCharacter {
private boolean online;
private String name;
public MapleGuildCharacter(MapleCharacter c) {
this.character = c;
this.name = c.getName();
this.level = c.getLevel();
this.id = c.getId();
this.channel = c.getClient().getChannel();
this.world = c.getWorld();
this.jobid = c.getJob().getId();
this.guildrank = c.getGuildRank();
this.guildid = c.getGuildId();
public MapleGuildCharacter(MapleCharacter chr) {
this.character = chr;
this.name = chr.getName();
this.level = chr.getLevel();
this.id = chr.getId();
this.channel = chr.getClient().getChannel();
this.world = chr.getWorld();
this.jobid = chr.getJob().getId();
this.guildrank = chr.getGuildRank();
this.guildid = chr.getGuildId();
this.online = true;
this.allianceRank = c.getAllianceRank();
this.allianceRank = chr.getAllianceRank();
}
public MapleGuildCharacter(MapleCharacter c, int _id, int _lv, String _name, int _channel, int _world, int _job, int _rank, int _gid, boolean _on, int _allianceRank) {
this.character = c;
public MapleGuildCharacter(MapleCharacter chr, int _id, int _lv, String _name, int _channel, int _world, int _job, int _rank, int _gid, boolean _on, int _allianceRank) {
this.character = chr;
this.level = _lv;
this.id = _id;
this.name = _name;
@@ -24,6 +24,7 @@ package net.server.handlers.login;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import net.server.channel.Channel;
import net.server.world.World;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -42,7 +43,8 @@ public final class CharlistRequestHandler extends AbstractMaplePacketHandler {
}
int channel = slea.readByte() + 1;
if(wserv.getChannel(channel) == null) {
Channel ch = wserv.getChannel(channel);
if(ch == null || !ch.isActive()) {
c.announce(MaplePacketCreator.getServerStatus(2));
return;
}
+5 -5
View File
@@ -212,7 +212,7 @@ public class World {
merchantSchedule = tman.register(new HiredMerchantTask(this), 10 * 60 * 1000, 10 * 60 * 1000);
timedMapObjectsSchedule = tman.register(new TimedMapObjectTask(this), 60 * 1000, 60 * 1000);
charactersSchedule = tman.register(new CharacterAutosaverTask(this), 60 * 60 * 1000, 60 * 60 * 1000);
marriagesSchedule = tman.register(new WeddingReservationTask(this), ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000, ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000);
marriagesSchedule = tman.register(new WeddingReservationTask(this), YamlConfig.config.server.WEDDING_RESERVATION_INTERVAL * 60 * 1000, YamlConfig.config.server.WEDDING_RESERVATION_INTERVAL * 60 * 1000);
mapOwnershipSchedule = tman.register(new MapOwnershipTask(this), 20 * 1000, 20 * 1000);
fishingSchedule = tman.register(new FishingTask(this), 10 * 1000, 10 * 1000);
partySearchSchedule = tman.register(new PartySearchTask(this), 10 * 1000, 10 * 1000);
@@ -730,14 +730,14 @@ public class World {
}
public void sendPacket(List<Integer> targetIds, final byte[] packet, int exception) {
MapleCharacter c;
MapleCharacter chr;
for (int i : targetIds) {
if (i == exception) {
continue;
}
c = getPlayerStorage().getCharacterById(i);
if (c != null) {
c.getClient().announce(packet);
chr = getPlayerStorage().getCharacterById(i);
if (chr != null) {
chr.getClient().announce(packet);
}
}
}
+71 -70
View File
@@ -365,28 +365,7 @@ public class AbstractPlayerInteraction {
NPCScriptManager.getInstance().start(c, npcid, script, null);
}
public void updateQuest(int questid, int data) {
MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(questid));
updateQuest(questid, status.getAnyProgressKey(), data);
}
public void updateQuest(int questid, String data) {
MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(questid));
updateQuest(questid, status.getAnyProgressKey(), data);
}
public void updateQuest(int questid, int pid, int data) {
updateQuest(questid, pid, String.valueOf(data));
}
public void updateQuest(int questid, int pid, String data) {
MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(questid));
status.setStatus(MapleQuestStatus.Status.STARTED);
status.setProgress(pid, data);//override old if exists
c.getPlayer().updateQuest(status);
}
public int getQuestStatus(int id) {
public int getQuestStatus(int id) {
return c.getPlayer().getQuest(MapleQuest.getInstance(id)).getStatus().getId();
}
@@ -394,75 +373,93 @@ public class AbstractPlayerInteraction {
return c.getPlayer().getQuest(MapleQuest.getInstance(id)).getStatus();
}
public boolean isQuestCompleted(int quest) {
public boolean isQuestCompleted(int id) {
try {
return getQuestStat(quest) == MapleQuestStatus.Status.COMPLETED;
return getQuestStat(id) == MapleQuestStatus.Status.COMPLETED;
} catch (NullPointerException e) {
e.printStackTrace();
return false;
}
}
public boolean isQuestActive(int quest) {
return isQuestStarted(quest);
public boolean isQuestActive(int id) {
return isQuestStarted(id);
}
public boolean isQuestStarted(int quest) {
public boolean isQuestStarted(int id) {
try {
return getQuestStat(quest) == MapleQuestStatus.Status.STARTED;
return getQuestStat(id) == MapleQuestStatus.Status.STARTED;
} catch (NullPointerException e) {
e.printStackTrace();
return false;
}
}
public void setQuestProgress(int qid, int progress) {
MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(qid));
status.setProgress(status.getAnyProgressKey(), String.valueOf(progress));
public void setQuestProgress(int id, String progress) {
setQuestProgress(id, 0, progress);
}
public void setQuestProgress(int qid, int pid, int progress) {
MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(qid));
status.setProgress(pid, String.valueOf(progress));
}
public void setStringQuestProgress(int qid, int pid, String progress) {
MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(qid));
status.setProgress(pid, progress);
public void setQuestProgress(int id, int progress) {
setQuestProgress(id, 0, "" + progress);
}
public int getQuestProgress(int qid) {
MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(qid));
String progress = status.getProgress(status.getAnyProgressKey());
if (progress.isEmpty()) {
public void setQuestProgress(int id, int infoNumber, int progress) {
setQuestProgress(id, infoNumber, "" + progress);
}
public void setQuestProgress(int id, int infoNumber, String progress) {
c.getPlayer().setQuestProgress(id, infoNumber, progress);
}
public String getQuestProgress(int id) {
return getQuestProgress(id, 0);
}
public String getQuestProgress(int id, int infoNumber) {
MapleQuestStatus qs = getPlayer().getQuest(MapleQuest.getInstance(id));
if (qs.getInfoNumber() == infoNumber && infoNumber > 0) {
qs = getPlayer().getQuest(MapleQuest.getInstance(infoNumber));
infoNumber = 0;
}
if (qs != null) {
return qs.getProgress(infoNumber);
} else {
return "";
}
}
public int getQuestProgressInt(int id) {
try {
return Integer.valueOf(getQuestProgress(id));
} catch (NumberFormatException nfe) {
return 0;
}
return Integer.parseInt(progress);
}
public int getQuestProgress(int qid, int pid) {
if (getPlayer().getQuest(MapleQuest.getInstance(qid)).getProgress(pid).isEmpty()) {
return 0;
public int getQuestProgressInt(int id, int infoNumber) {
try {
return Integer.valueOf(getQuestProgress(id, infoNumber));
} catch (NumberFormatException nfe) {
return 0;
}
return Integer.parseInt(getPlayer().getQuest(MapleQuest.getInstance(qid)).getProgress(pid));
}
}
public String getStringQuestProgress(int qid, int pid) {
if (getPlayer().getQuest(MapleQuest.getInstance(qid)).getProgress(pid).isEmpty()) {
return "";
public void resetAllQuestProgress(int id) {
MapleQuestStatus qs = getPlayer().getQuest(MapleQuest.getInstance(id));
if (qs != null) {
qs.resetAllProgress();
getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false);
}
return getPlayer().getQuest(MapleQuest.getInstance(qid)).getProgress(pid);
}
public void resetAllQuestProgress(int qid) {
getPlayer().getQuest(MapleQuest.getInstance(qid)).resetAllProgress();
getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, getPlayer().getQuest(MapleQuest.getInstance(qid)), false);
}
public void resetQuestProgress(int qid, int pid) {
getPlayer().getQuest(MapleQuest.getInstance(qid)).resetProgress(pid);
getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, getPlayer().getQuest(MapleQuest.getInstance(qid)), false);
public void resetQuestProgress(int id, int infoNumber) {
MapleQuestStatus qs = getPlayer().getQuest(MapleQuest.getInstance(id));
if (qs != null) {
qs.resetProgress(infoNumber);
getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false);
}
}
public boolean forceStartQuest(int id) {
@@ -497,26 +494,26 @@ public class AbstractPlayerInteraction {
return completeQuest(id, 9010000);
}
public boolean startQuest(short id, int npcId) {
return startQuest((int) id, npcId);
public boolean startQuest(short id, int npc) {
return startQuest((int) id, npc);
}
public boolean completeQuest(short id, int npcId) {
return completeQuest((int) id, npcId);
public boolean completeQuest(short id, int npc) {
return completeQuest((int) id, npc);
}
public boolean startQuest(int id, int npcId) {
public boolean startQuest(int id, int npc) {
try {
return MapleQuest.getInstance(id).forceStart(getPlayer(), npcId);
return MapleQuest.getInstance(id).forceStart(getPlayer(), npc);
} catch (NullPointerException ex) {
ex.printStackTrace();
return false;
}
}
public boolean completeQuest(int id, int npcId) {
public boolean completeQuest(int id, int npc) {
try {
return MapleQuest.getInstance(id).forceComplete(getPlayer(), npcId);
return MapleQuest.getInstance(id).forceComplete(getPlayer(), npc);
} catch (NullPointerException ex) {
ex.printStackTrace();
return false;
@@ -682,6 +679,10 @@ public class AbstractPlayerInteraction {
public void message(String message) {
getPlayer().message(message);
}
public void dropMessage(int type, String message) {
getPlayer().dropMessage(type, message);
}
public void mapMessage(int type, String message) {
getPlayer().getMap().broadcastMessage(MaplePacketCreator.serverNotice(type, message));
@@ -898,7 +898,7 @@ public class EventInstanceManager {
}
}
public void dispatchUpdateQuestMobCount(int mobid, int mapid) {
public void dispatchRaiseQuestMobCount(int mobid, int mapid) {
Map<Integer, MapleCharacter> mapChars = getInstanceMap(mapid).getMapPlayers();
if(!mapChars.isEmpty()) {
List<MapleCharacter> eventMembers = getPlayers();
@@ -907,7 +907,7 @@ public class EventInstanceManager {
MapleCharacter chr = mapChars.get(evChr.getId());
if(chr != null && chr.isLoggedinWorld()) {
chr.updateQuestMobCount(mobid);
chr.raiseQuestMobCount(mobid);
}
}
}
+9 -1
View File
@@ -27,7 +27,6 @@ import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.ScriptEngine;
import jdk.nashorn.api.scripting.NashornScriptEngine;
import net.server.channel.Channel;
@@ -48,7 +47,9 @@ public class EventScriptManager extends AbstractScriptManager {
public NashornScriptEngine iv;
public EventManager em;
}
private Map<String, EventEntry> events = new LinkedHashMap<>();
private boolean active = false;
public EventScriptManager(Channel cserv, String[] scripts) {
super();
@@ -67,6 +68,10 @@ public class EventScriptManager extends AbstractScriptManager {
}
return entry.em;
}
public boolean isActive() {
return active;
}
public void init() {
for (EventEntry entry : events.values()) {
@@ -78,6 +83,8 @@ public class EventScriptManager extends AbstractScriptManager {
System.out.println("Error on script: " + entry.em.getName());
}
}
active = true;
}
private void reloadScripts() {
@@ -100,6 +107,7 @@ public class EventScriptManager extends AbstractScriptManager {
}
public void cancel() {
active = false;
for (EventEntry entry : events.values()) {
entry.em.cancel();
}
+10 -10
View File
@@ -94,13 +94,13 @@ public class MapScriptMethods extends AbstractPlayerInteraction {
return;
}
}
MapleQuestStatus q = getPlayer().getQuest(quest);
if (!q.addMedalMap(getPlayer().getMapId())) {
MapleQuestStatus qs = getPlayer().getQuest(quest);
if (!qs.addMedalMap(getPlayer().getMapId())) {
return;
}
String status = Integer.toString(q.getMedalProgress());
String infoex = quest.getInfoEx();
getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, q, true);
String status = Integer.toString(qs.getMedalProgress());
String infoex = qs.getInfoEx(0);
getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true);
StringBuilder smp = new StringBuilder();
StringBuilder etm = new StringBuilder();
if (status.equals(infoex)) {
@@ -123,15 +123,15 @@ public class MapScriptMethods extends AbstractPlayerInteraction {
return;
}
}
MapleQuestStatus q = getPlayer().getQuest(quest);
if (!q.addMedalMap(getPlayer().getMapId())) {
MapleQuestStatus qs = getPlayer().getQuest(quest);
if (!qs.addMedalMap(getPlayer().getMapId())) {
return;
}
String status = Integer.toString(q.getMedalProgress());
getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, q, true);
String status = Integer.toString(qs.getMedalProgress());
getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true);
getPlayer().announce(MaplePacketCreator.earnTitleMessage(status + "/5 Completed"));
getPlayer().announce(MaplePacketCreator.earnTitleMessage("The One Who's Touched the Sky title in progress."));
if (Integer.toString(q.getMedalProgress()).equals(quest.getInfoEx())) {
if (Integer.toString(qs.getMedalProgress()).equals(qs.getInfoEx(0))) {
showInfoText("The One Who's Touched the Sky" + rewardstring);
getPlayer().announce(MaplePacketCreator.getShowQuestCompletion(quest.getId()));
} else {
@@ -579,7 +579,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
return itemid != baseid && itemExists(baseid) ? baseid : -1;
}
private int getEquippedItemid(int itemid) {
private int getEquippedCosmeticid(int itemid) {
if (itemid < 30000) {
return getPlayer().getFace();
} else {
@@ -588,7 +588,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
}
public boolean isCosmeticEquipped(int itemid) {
return getEquippedItemid(itemid) == itemid;
return getEquippedCosmeticid(itemid) == itemid;
}
public boolean isUsingOldPqNpcStyle() {
+30 -4
View File
@@ -59,10 +59,6 @@ public class QuestScriptManager extends AbstractScriptManager {
public void start(MapleClient c, short questid, int npc) {
MapleQuest quest = MapleQuest.getInstance(questid);
if (!quest.canStartWithoutRequirements(c.getPlayer())) {
dispose(c);
return;
}
try {
QuestActionManager qm = new QuestActionManager(c, questid, npc, true);
if (qms.containsKey(c)) {
@@ -169,6 +165,36 @@ public class QuestScriptManager extends AbstractScriptManager {
}
}
public void raiseOpen(MapleClient c, short questid, int npc) {
try {
QuestActionManager qm = new QuestActionManager(c, questid, npc, true);
if (qms.containsKey(c)) {
return;
}
if(c.canClickNPC()) {
qms.put(c, qm);
NashornScriptEngine iv = getQuestScriptEngine(c, questid);
if (iv == null) {
//FilePrinter.printError(FilePrinter.QUEST_UNCODED, "RAISE Quest " + questid + " is uncoded.");
qm.dispose();
return;
}
iv.put("qm", qm);
scripts.put(c, iv);
c.setClickedNPC();
iv.invokeFunction("raiseOpen");
}
} catch (final UndeclaredThrowableException ute) {
FilePrinter.printError(FilePrinter.QUEST + questid + ".txt", ute);
dispose(c);
} catch (final Throwable t) {
FilePrinter.printError(FilePrinter.QUEST + getQM(c).getQuest() + ".txt", t);
dispose(c);
}
}
public void dispose(QuestActionManager qm, MapleClient c) {
qms.remove(c);
scripts.remove(c);
+10 -8
View File
@@ -2156,11 +2156,10 @@ public class MapleItemInformationProvider {
return questItemConsCache.get(itemId);
}
MapleData data = getItemData(itemId);
QuestConsItem qcItem = null;
MapleData infoData = data.getChildByPath("info");
MapleData ciData = infoData.getChildByPath("consumeItem");
QuestConsItem qcItem = null;
if (ciData != null) {
if (infoData.getChildByPath("uiData") != null) {
qcItem = new QuestConsItem();
qcItem.exp = MapleDataTool.getInt("exp", infoData);
qcItem.grade = MapleDataTool.getInt("grade", infoData);
@@ -2168,11 +2167,14 @@ public class MapleItemInformationProvider {
qcItem.items = new HashMap<>(2);
Map<Integer, Integer> cItems = qcItem.items;
for (MapleData ciItem : ciData.getChildren()) {
int itemid = MapleDataTool.getInt("0", ciItem);
int qty = MapleDataTool.getInt("1", ciItem);
cItems.put(itemid, qty);
MapleData ciData = infoData.getChildByPath("consumeItem");
if (ciData != null) {
for (MapleData ciItem : ciData.getChildren()) {
int itemid = MapleDataTool.getInt("0", ciItem);
int qty = MapleDataTool.getInt("1", ciItem);
cItems.put(itemid, qty);
}
}
}
+18 -18
View File
@@ -80,8 +80,8 @@ public class MapleTrade {
private byte number;
private boolean fullTrade = false;
public MapleTrade(byte number, MapleCharacter c) {
chr = c;
public MapleTrade(byte number, MapleCharacter chr) {
this.chr = chr;
this.number = number;
}
@@ -291,8 +291,8 @@ public class MapleTrade {
}
}
public static void completeTrade(MapleCharacter c) {
MapleTrade local = c.getTrade();
public static void completeTrade(MapleCharacter chr) {
MapleTrade local = chr.getTrade();
MapleTrade partner = local.getPartner();
if (local.checkCompleteHandshake()) {
local.fetchExchangedItems();
@@ -300,12 +300,12 @@ public class MapleTrade {
if (!local.fitsMeso()) {
cancelTrade(local.getChr(), TradeResult.UNSUCCESSFUL);
c.message("There is not enough meso inventory space to complete the trade.");
chr.message("There is not enough meso inventory space to complete the trade.");
partner.getChr().message("Partner does not have enough meso inventory space to complete the trade.");
return;
} else if (!partner.fitsMeso()) {
cancelTrade(partner.getChr(), TradeResult.UNSUCCESSFUL);
c.message("Partner does not have enough meso inventory space to complete the trade.");
chr.message("Partner does not have enough meso inventory space to complete the trade.");
partner.getChr().message("There is not enough meso inventory space to complete the trade.");
return;
}
@@ -313,7 +313,7 @@ public class MapleTrade {
if (!local.fitsInInventory()) {
if (local.fitsUniquesInInventory()) {
cancelTrade(local.getChr(), TradeResult.UNSUCCESSFUL);
c.message("There is not enough inventory space to complete the trade.");
chr.message("There is not enough inventory space to complete the trade.");
partner.getChr().message("Partner does not have enough inventory space to complete the trade.");
} else {
cancelTrade(local.getChr(), TradeResult.UNSUCCESSFUL_UNIQUE_ITEM_LIMIT);
@@ -323,11 +323,11 @@ public class MapleTrade {
} else if (!partner.fitsInInventory()) {
if (partner.fitsUniquesInInventory()) {
cancelTrade(partner.getChr(), TradeResult.UNSUCCESSFUL);
c.message("Partner does not have enough inventory space to complete the trade.");
chr.message("Partner does not have enough inventory space to complete the trade.");
partner.getChr().message("There is not enough inventory space to complete the trade.");
} else {
cancelTrade(partner.getChr(), TradeResult.UNSUCCESSFUL_UNIQUE_ITEM_LIMIT);
c.message("Partner cannot hold more than one one-of-a-kind item at a time.");
chr.message("Partner cannot hold more than one one-of-a-kind item at a time.");
}
return;
}
@@ -355,7 +355,7 @@ public class MapleTrade {
partner.completeTrade();
partner.getChr().setTrade(null);
c.setTrade(null);
chr.setTrade(null);
}
}
@@ -424,9 +424,9 @@ public class MapleTrade {
trade.cancelHandshake(result.getValue());
}
public static void startTrade(MapleCharacter c) {
if (c.getTrade() == null) {
c.setTrade(new MapleTrade((byte) 0, c));
public static void startTrade(MapleCharacter chr) {
if (chr.getTrade() == null) {
chr.setTrade(new MapleTrade((byte) 0, chr));
}
}
@@ -496,13 +496,13 @@ public class MapleTrade {
}
}
public static void declineTrade(MapleCharacter c) {
MapleTrade trade = c.getTrade();
public static void declineTrade(MapleCharacter chr) {
MapleTrade trade = chr.getTrade();
if (trade != null) {
if (trade.getPartner() != null) {
MapleCharacter other = trade.getPartner().getChr();
if (MapleInviteCoordinator.answerInvite(InviteType.TRADE, c.getId(), other.getId(), false).result == InviteResult.DENIED) {
other.message(c.getName() + " has declined your trade request.");
if (MapleInviteCoordinator.answerInvite(InviteType.TRADE, chr.getId(), other.getId(), false).result == InviteResult.DENIED) {
other.message(chr.getName() + " has declined your trade request.");
}
other.getTrade().cancel(TradeResult.PARTNER_CANCEL.getValue());
@@ -510,7 +510,7 @@ public class MapleTrade {
}
trade.cancel(TradeResult.NO_RESPONSE.getValue());
c.setTrade(null);
chr.setTrade(null);
}
}
+19 -3
View File
@@ -25,9 +25,11 @@ import java.awt.Point;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
@@ -43,7 +45,19 @@ public class MapleLifeFactory {
private static MapleData mobStringData = stringDataWZ.getData("Mob.img");
private static MapleData npcStringData = stringDataWZ.getData("Npc.img");
private static Map<Integer, MapleMonsterStats> monsterStats = new HashMap<>();
private static Set<Integer> hpbarBosses = getHpBarBosses();
private static Set<Integer> getHpBarBosses() {
Set<Integer> ret = new HashSet<>();
MapleDataProvider uiDataWZ = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/UI.wz"));
for (MapleData bossData : uiDataWZ.getData("UIWindow.img").getChildByPath("MobGage/Mob").getChildren()) {
ret.add(Integer.valueOf(bossData.getName()));
}
return ret;
}
public static AbstractLoadedMapleLife getLife(int id, String type) {
if (type.equalsIgnoreCase("n")) {
return getNPC(id);
@@ -150,9 +164,11 @@ public class MapleLifeFactory {
stats.setFirstAttack(firstAttack > 0);
stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, stats.getDropPeriod() / 10000) * 10000);
stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0));
stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0));
if (!(stats.isBoss() && !hpbarBosses.contains(mid))) { // thanks Riizade, Z1peR, Anesthetic for noticing some bosses crashing players due to missing requirements
stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0));
stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0));
}
for (MapleData idata : monsterData) {
if (!idata.getName().equals("info")) {
int delay = 0;
+4 -4
View File
@@ -743,7 +743,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
attacker.gainExp(_personalExp, _partyExp, true, false, white);
attacker.increaseEquipExp(_personalExp);
attacker.updateQuestMobCount(getId());
attacker.raiseQuestMobCount(getId());
}
}
@@ -873,7 +873,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}, delay, delay);
}
private void dispatchUpdateQuestMobCount() {
private void dispatchRaiseQuestMobCount() {
Set<Integer> attackerChrids = takenDamage.keySet();
if(!attackerChrids.isEmpty()) {
Map<Integer, MapleCharacter> mapChars = map.getMapPlayers();
@@ -884,7 +884,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
MapleCharacter chr = mapChars.get(chrid);
if(chr != null && chr.isLoggedinWorld()) {
chr.updateQuestMobCount(mobid);
chr.raiseQuestMobCount(mobid);
}
}
}
@@ -906,7 +906,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
private synchronized void processMonsterKilled(boolean hasKiller) {
if(!hasKiller) { // players won't gain EXP from a mob that has no killer, but a quest count they should
dispatchUpdateQuestMobCount();
dispatchRaiseQuestMobCount();
}
this.aggroClearDamages();
+8 -8
View File
@@ -3261,9 +3261,9 @@ public class MapleMap {
public MapleCharacter getCharacterById(int id) {
chrRLock.lock();
try {
for (MapleCharacter c : this.characters) {
if (c.getId() == id) {
return c;
for (MapleCharacter chr : this.characters) {
if (chr.getId() == id) {
return chr;
}
}
} finally {
@@ -3421,8 +3421,8 @@ public class MapleMap {
public void broadcastNightEffect() {
chrRLock.lock();
try {
for (MapleCharacter c : characters) {
sendNightEffect(c);
for (MapleCharacter chr : this.characters) {
sendNightEffect(chr);
}
} finally {
chrRLock.unlock();
@@ -3432,9 +3432,9 @@ public class MapleMap {
public MapleCharacter getCharacterByName(String name) {
chrRLock.lock();
try {
for (MapleCharacter c : this.characters) {
if (c.getName().toLowerCase().equals(name.toLowerCase())) {
return c;
for (MapleCharacter chr : this.characters) {
if (chr.getName().toLowerCase().equals(name.toLowerCase())) {
return chr;
}
}
} finally {
+2 -2
View File
@@ -89,8 +89,8 @@ public class MapleMiniGame extends AbstractMapleMapObject {
return visitor == null;
}
public boolean isOwner(MapleCharacter c) {
return owner.equals(c);
public boolean isOwner(MapleCharacter chr) {
return owner.equals(chr);
}
public void addVisitor(MapleCharacter challenger) {
+2 -2
View File
@@ -120,8 +120,8 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
}
}
public boolean isOwner(MapleCharacter c) {
return owner.equals(c);
public boolean isOwner(MapleCharacter chr) {
return owner.equals(chr);
}
private void addVisitor(MapleCharacter visitor) {
+194 -143
View File
@@ -28,11 +28,12 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import client.MapleCharacter;
import client.MapleCharacter.DelayedQuestUpdate;
import client.MapleQuestStatus;
import client.MapleQuestStatus.Status;
import config.YamlConfig;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Map.Entry;
import java.util.Set;
import provider.MapleData;
import provider.MapleDataProvider;
@@ -51,6 +52,7 @@ import tools.StringUtil;
public class MapleQuest {
private static Map<Integer, MapleQuest> quests = new HashMap<>();
private static Map<Integer, Integer> infoNumberQuests = new HashMap<>();
private static Map<Short, Integer> medals = new HashMap<>();
private static final Set<Short> exploitableQuests = new HashSet<>();
@@ -61,9 +63,8 @@ public class MapleQuest {
exploitableQuests.add((short) 21752);
}
protected short infoNumber, id;
protected short id;
protected int timeLimit, timeLimit2;
protected String infoex;
protected Map<MapleQuestRequirementType, MapleQuestRequirement> startReqs = new EnumMap<>(MapleQuestRequirementType.class);
protected Map<MapleQuestRequirementType, MapleQuestRequirement> completeReqs = new EnumMap<>(MapleQuestRequirementType.class);
protected Map<MapleQuestActionType, MapleQuestAction> startActs = new EnumMap<>(MapleQuestActionType.class);
@@ -111,8 +112,6 @@ public class MapleQuest {
MapleQuestRequirementType type = MapleQuestRequirementType.getByWZName(startReq.getName());
if (type.equals(MapleQuestRequirementType.INTERVAL)) {
repeatable = true;
} else if (type.equals(MapleQuestRequirementType.INFO_NUMBER)) {
infoNumber = (short) MapleDataTool.getInt(startReq, 0);
} else if (type.equals(MapleQuestRequirementType.MOB)) {
for (MapleData mob : startReq.getChildren()) {
relevantMobs.add(MapleDataTool.getInt(mob.getChildByPath("id")));
@@ -120,8 +119,9 @@ public class MapleQuest {
}
MapleQuestRequirement req = this.getRequirement(type, startReq);
if(req == null)
continue;
if (req == null) {
continue;
}
startReqs.put(type, req);
}
@@ -131,14 +131,13 @@ public class MapleQuest {
if (completeReqData != null) {
for (MapleData completeReq : completeReqData.getChildren()) {
MapleQuestRequirementType type = MapleQuestRequirementType.getByWZName(completeReq.getName());
MapleQuestRequirement req = this.getRequirement(type, completeReq);
if(req == null)
continue;
MapleQuestRequirement req = this.getRequirement(type, completeReq);
if (req == null) {
continue;
}
if (type.equals(MapleQuestRequirementType.INFO_NUMBER)) {
infoNumber = (short) MapleDataTool.getInt(completeReq, 0);
} else if (type.equals(MapleQuestRequirementType.MOB)) {
if (type.equals(MapleQuestRequirementType.MOB)) {
for (MapleData mob : completeReq.getChildren()) {
relevantMobs.add(MapleDataTool.getInt(mob.getChildByPath("id")));
}
@@ -193,35 +192,13 @@ public class MapleQuest {
return ret;
}
private String getIntervalTimeLeft(MapleCharacter c, IntervalRequirement r) {
StringBuilder str = new StringBuilder();
long futureTime = c.getQuest(MapleQuest.getInstance(getId())).getCompletionTime() + r.getInterval();
long leftTime = futureTime - System.currentTimeMillis();
byte mode = 0;
if(leftTime / (60*1000) > 0) {
mode++; //counts minutes
if(leftTime / (60*60*1000) > 0)
mode++; //counts hours
public static MapleQuest getInstanceFromInfoNumber(int infoNumber) {
Integer id = infoNumberQuests.get(infoNumber);
if (id == null) {
id = infoNumber;
}
switch(mode) {
case 2:
int hours = (int) ((leftTime / (1000*60*60)));
str.append(hours + " hours, ");
case 1:
int minutes = (int) ((leftTime / (1000*60)) % 60);
str.append(minutes + " minutes, ");
default:
int seconds = (int) (leftTime / 1000) % 60 ;
str.append(seconds + " seconds");
}
return str.toString();
return getInstance(id);
}
public boolean isSameDayRepeatable() {
@@ -231,105 +208,129 @@ public class MapleQuest {
return ir.getInterval() < YamlConfig.config.server.QUEST_POINT_REPEATABLE_INTERVAL * 60 * 60 * 1000;
}
public boolean canStartWithoutRequirements(MapleCharacter c) {
MapleQuestStatus mqs = c.getQuest(this);
return !(mqs.getStatus() != Status.NOT_STARTED && !(mqs.getStatus() == Status.COMPLETED && repeatable));
public boolean canStartQuestByStatus(MapleCharacter chr) {
MapleQuestStatus mqs = chr.getQuest(this);
return !(!mqs.getStatus().equals(Status.NOT_STARTED) && !(mqs.getStatus().equals(Status.COMPLETED) && repeatable));
}
public boolean canStart(MapleCharacter c, int npcid) {
if (!canStartWithoutRequirements(c)) {
public boolean canQuestByInfoProgress(MapleCharacter chr) {
MapleQuestStatus mqs = chr.getQuest(this);
List<String> ix = mqs.getInfoEx();
if (!ix.isEmpty()) {
short questid = mqs.getQuestID();
short infoNumber = mqs.getInfoNumber();
if (infoNumber <= 0) {
infoNumber = questid; // on default infoNumber mimics questid
}
int ixSize = ix.size();
for (int i = 0; i < ixSize; i++) {
String progress = chr.getClient().getAbstractPlayerInteraction().getQuestProgress(infoNumber, i);
String ixProgress = ix.get(i);
if (!progress.contentEquals(ixProgress)) {
return false;
}
}
}
return true;
}
public boolean canStart(MapleCharacter chr, int npcid) {
if (!canStartQuestByStatus(chr)) {
return false;
}
for (MapleQuestRequirement r : startReqs.values()) {
if (!r.check(c, npcid)) {
if(r.getType().getType() == MapleQuestRequirementType.INTERVAL.getType()) {
c.message("This quest will become available again in approximately " + getIntervalTimeLeft(c, (IntervalRequirement)r) + ".");
}
if (!r.check(chr, npcid)) {
return false;
}
}
return true;
}
public boolean canComplete(MapleCharacter c, Integer npcid) {
if (!c.getQuest(this).getStatus().equals(Status.STARTED)) {
if (!canQuestByInfoProgress(chr)) {
return false;
}
for (MapleQuestRequirement r : completeReqs.values()) {
if (r == null) {
return false;
} else if(!r.check(c, npcid)) {
if(r.getType() == MapleQuestRequirementType.MESO) { // TODO: find a way to tell the client about the new MESO requirement type.
c.dropMessage(5, "You don't have enough mesos to complete this quest.");
}
return false;
}
}
return true;
}
public void start(MapleCharacter c, int npc) {
if (autoStart || canStart(c, npc)) {
for (MapleQuestAction a : startActs.values()) {
if (!a.check(c, null)) { // would null be good ?
return;
}
a.run(c, null);
}
forceStart(c, npc);
public boolean canComplete(MapleCharacter chr, Integer npcid) {
MapleQuestStatus mqs = chr.getQuest(this);
if (!mqs.getStatus().equals(Status.STARTED)) {
return false;
}
for (MapleQuestRequirement r : completeReqs.values()) {
if (!r.check(chr, npcid)) {
return false;
}
}
if (!canQuestByInfoProgress(chr)) {
return false;
}
return true;
}
public void complete(MapleCharacter c, int npc) {
complete(c, npc, null);
}
public void complete(MapleCharacter c, int npc, Integer selection) {
if (autoPreComplete || canComplete(c, npc)) {
for (MapleQuestAction a : completeActs.values()) {
if (!a.check(c, selection)) {
public void start(MapleCharacter chr, int npc) {
if (autoStart || canStart(chr, npc)) {
Collection<MapleQuestAction> acts = startActs.values();
for (MapleQuestAction a : acts) {
if (!a.check(chr, null)) { // would null be good ?
return;
}
}
for (MapleQuestAction a : acts) {
a.run(chr, null);
}
forceStart(chr, npc);
}
}
forceComplete(c, npc);
for (MapleQuestAction a : completeActs.values()) {
a.run(c, selection);
public void complete(MapleCharacter chr, int npc) {
complete(chr, npc, null);
}
public void complete(MapleCharacter chr, int npc, Integer selection) {
if (autoPreComplete || canComplete(chr, npc)) {
Collection<MapleQuestAction> acts = completeActs.values();
for (MapleQuestAction a : acts) {
if (!a.check(chr, selection)) {
return;
}
}
forceComplete(chr, npc);
for (MapleQuestAction a : acts) {
a.run(chr, selection);
}
}
}
public void reset(MapleCharacter c) {
c.updateQuest(new MapleQuestStatus(this, MapleQuestStatus.Status.NOT_STARTED));
public void reset(MapleCharacter chr) {
chr.updateQuestStatus(new MapleQuestStatus(this, MapleQuestStatus.Status.NOT_STARTED));
}
public void forfeit(MapleCharacter c) {
if (!c.getQuest(this).getStatus().equals(Status.STARTED)) {
public void forfeit(MapleCharacter chr) {
if (!chr.getQuest(this).getStatus().equals(Status.STARTED)) {
return;
}
if (timeLimit > 0) {
c.announce(MaplePacketCreator.removeQuestTimeLimit(id));
chr.announce(MaplePacketCreator.removeQuestTimeLimit(id));
}
MapleQuestStatus newStatus = new MapleQuestStatus(this, MapleQuestStatus.Status.NOT_STARTED);
newStatus.setForfeited(c.getQuest(this).getForfeited() + 1);
c.updateQuest(newStatus);
newStatus.setForfeited(chr.getQuest(this).getForfeited() + 1);
chr.updateQuestStatus(newStatus);
}
public boolean forceStart(MapleCharacter c, int npc) {
public boolean forceStart(MapleCharacter chr, int npc) {
MapleQuestStatus newStatus = new MapleQuestStatus(this, MapleQuestStatus.Status.STARTED, npc);
newStatus.setForfeited(c.getQuest(this).getForfeited());
newStatus.setCompleted(c.getQuest(this).getCompleted());
if (timeLimit > 0) {
newStatus.setExpirationTime(System.currentTimeMillis() + (timeLimit * 1000));
c.questTimeLimit(this, timeLimit);
}
if (timeLimit2 > 0) {
newStatus.setExpirationTime(System.currentTimeMillis() + timeLimit2);
c.questTimeLimit2(this, newStatus.getExpirationTime());
}
c.updateQuest(newStatus);
MapleQuestStatus oldStatus = chr.getQuest(this.getId());
for (Entry<Integer, String> e : oldStatus.getProgress().entrySet()) {
newStatus.setProgress(e.getKey(), e.getValue());
}
if(id / 100 == 35 && YamlConfig.config.server.TOT_MOB_QUEST_REQUIREMENT > 0) {
int setProg = 999 - Math.min(999, YamlConfig.config.server.TOT_MOB_QUEST_REQUIREMENT);
@@ -338,27 +339,40 @@ public class MapleQuest {
if(pid >= 8200000 && pid <= 8200012) {
String pr = StringUtil.getLeftPaddedStr(Integer.toString(setProg), '0', 3);
newStatus.setProgress(pid, pr);
c.announceUpdateQuest(DelayedQuestUpdate.UPDATE, newStatus, false);
}
}
}
newStatus.setForfeited(chr.getQuest(this).getForfeited());
newStatus.setCompleted(chr.getQuest(this).getCompleted());
if (timeLimit > 0) {
newStatus.setExpirationTime(System.currentTimeMillis() + (timeLimit * 1000));
chr.questTimeLimit(this, timeLimit);
}
if (timeLimit2 > 0) {
newStatus.setExpirationTime(System.currentTimeMillis() + timeLimit2);
chr.questTimeLimit2(this, newStatus.getExpirationTime());
}
chr.updateQuestStatus(newStatus);
return true;
}
public boolean forceComplete(MapleCharacter c, int npc) {
public boolean forceComplete(MapleCharacter chr, int npc) {
if (timeLimit > 0) {
c.announce(MaplePacketCreator.removeQuestTimeLimit(id));
chr.announce(MaplePacketCreator.removeQuestTimeLimit(id));
}
MapleQuestStatus newStatus = new MapleQuestStatus(this, MapleQuestStatus.Status.COMPLETED, npc);
newStatus.setForfeited(c.getQuest(this).getForfeited());
newStatus.setCompleted(c.getQuest(this).getCompleted());
newStatus.setForfeited(chr.getQuest(this).getForfeited());
newStatus.setCompleted(chr.getQuest(this).getCompleted());
newStatus.setCompletionTime(System.currentTimeMillis());
c.updateQuest(newStatus);
chr.updateQuestStatus(newStatus);
c.announce(MaplePacketCreator.showSpecialEffect(9)); // Quest completion
c.getMap().broadcastMessage(c, MaplePacketCreator.showForeignEffect(c.getId(), 9), false); //use 9 instead of 12 for both
chr.announce(MaplePacketCreator.showSpecialEffect(9)); // Quest completion
chr.getMap().broadcastMessage(chr, MaplePacketCreator.showForeignEffect(chr.getId(), 9), false); //use 9 instead of 12 for both
return true;
}
@@ -390,32 +404,49 @@ public class MapleQuest {
public int getMobAmountNeeded(int mid) {
MapleQuestRequirement req = completeReqs.get(MapleQuestRequirementType.MOB);
if(req == null)
return 0;
MobRequirement mreq = (MobRequirement) req;
return mreq.getRequiredMobCount(mid);
if(req == null)
return 0;
MobRequirement mreq = (MobRequirement) req;
return mreq.getRequiredMobCount(mid);
}
public short getInfoNumber() {
return infoNumber;
public short getInfoNumber(Status qs) {
boolean checkEnd = qs.equals(Status.STARTED);
Map<MapleQuestRequirementType, MapleQuestRequirement> reqs = !checkEnd ? startReqs : completeReqs;
MapleQuestRequirement req = reqs.get(MapleQuestRequirementType.INFO_NUMBER);
if (req != null) {
InfoNumberRequirement inReq = (InfoNumberRequirement) req;
return inReq.getInfoNumber();
} else {
return 0;
}
}
public String getInfoEx() {
MapleQuestRequirement req = startReqs.get(MapleQuestRequirementType.INFO_EX);
String ret = "";
if(req != null) {
InfoExRequirement ireq = (InfoExRequirement) req;
ret = ireq.getFirstInfo();
} else { // Check complete requirements.
req = completeReqs.get(MapleQuestRequirementType.INFO_EX);
if(req != null) {
InfoExRequirement ireq = (InfoExRequirement) req;
ret = ireq.getFirstInfo();
}
}
return ret;
public String getInfoEx(Status qs, int index) {
boolean checkEnd = qs.equals(Status.STARTED);
Map<MapleQuestRequirementType, MapleQuestRequirement> reqs = !checkEnd ? startReqs : completeReqs;
try {
MapleQuestRequirement req = reqs.get(MapleQuestRequirementType.INFO_EX);
InfoExRequirement ixReq = (InfoExRequirement) req;
return ixReq.getInfo().get(index);
} catch (Exception e) {
return "";
}
}
public List<String> getInfoEx(Status qs) {
boolean checkEnd = qs.equals(Status.STARTED);
Map<MapleQuestRequirementType, MapleQuestRequirement> reqs = !checkEnd ? startReqs : completeReqs;
try {
MapleQuestRequirement req = reqs.get(MapleQuestRequirementType.INFO_EX);
InfoExRequirement ixReq = (InfoExRequirement) req;
return ixReq.getInfo();
} catch (Exception e) {
return new LinkedList<>();
}
}
public int getTimeLimit() {
@@ -447,6 +478,9 @@ public class MapleQuest {
case FIELD_ENTER:
ret = new FieldEnterRequirement(this, data);
break;
case INFO_NUMBER:
ret = new InfoNumberRequirement(this, data);
break;
case INFO_EX:
ret = new InfoExRequirement(this, data);
break;
@@ -495,7 +529,6 @@ public class MapleQuest {
case NORMAL_AUTO_START:
case START:
case END:
case INFO_NUMBER:
break;
default:
//FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Unhandled Requirement Type: " + type.toString() + " QuestID: " + this.getId());
@@ -540,6 +573,9 @@ public class MapleQuest {
case PETSPEED:
ret = new PetSpeedAction(this, data);
break;
case INFO:
ret = new InfoAction(this, data);
break;
default:
//FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Unhandled Action Type: " + type.toString() + " QuestID: " + this.getId());
break;
@@ -548,9 +584,11 @@ public class MapleQuest {
}
public boolean restoreLostItem(MapleCharacter chr, int itemid) {
ItemAction itemAct = (ItemAction) startActs.get(MapleQuestActionType.ITEM);
if (itemAct != null) {
return itemAct.restoreLostItem(chr, itemid);
if (chr.getQuest(this).equals(MapleQuestStatus.Status.STARTED)) {
ItemAction itemAct = (ItemAction) startActs.get(MapleQuestActionType.ITEM);
if (itemAct != null) {
return itemAct.restoreLostItem(chr, itemid);
}
}
return false;
@@ -561,8 +599,8 @@ public class MapleQuest {
return medalid != null ? medalid : -1;
}
public int getNpcRequirement(boolean complete) {
Map<MapleQuestRequirementType, MapleQuestRequirement> reqs = !complete ? startReqs : completeReqs;
public int getNpcRequirement(boolean checkEnd) {
Map<MapleQuestRequirementType, MapleQuestRequirement> reqs = !checkEnd ? startReqs : completeReqs;
MapleQuestRequirement mqr = reqs.get(MapleQuestRequirementType.NPC);
if (mqr != null) {
return ((NpcRequirement) mqr).get();
@@ -571,8 +609,8 @@ public class MapleQuest {
}
}
public boolean hasScriptRequirement(boolean complete) {
Map<MapleQuestRequirementType, MapleQuestRequirement> reqs = !complete ? startReqs : completeReqs;
public boolean hasScriptRequirement(boolean checkEnd) {
Map<MapleQuestRequirementType, MapleQuestRequirement> reqs = !checkEnd ? startReqs : completeReqs;
MapleQuestRequirement mqr = reqs.get(MapleQuestRequirementType.SCRIPT);
if (mqr != null) {
@@ -612,7 +650,20 @@ public class MapleQuest {
for(MapleData quest : questInfo.getChildren()) {
int questID = Integer.parseInt(quest.getName());
quests.put(questID, new MapleQuest(questID));
MapleQuest q = new MapleQuest(questID);
quests.put(questID, q);
int infoNumber;
infoNumber = q.getInfoNumber(Status.STARTED);
if (infoNumber > 0) {
infoNumberQuests.put(infoNumber, questID);
}
infoNumber = q.getInfoNumber(Status.COMPLETED);
if (infoNumber > 0) {
infoNumberQuests.put(infoNumber, questID);
}
}
} catch (Exception ex) {
ex.printStackTrace();
+3 -1
View File
@@ -26,7 +26,7 @@ package server.quest;
* @author Matze
*/
public enum MapleQuestActionType {
UNDEFINED(-1), EXP(0), ITEM(1), NEXTQUEST(2), MESO(3), QUEST(4), SKILL(5), FAME(6), BUFF(7), PETSKILL(8), YES(9), NO(10), NPC(11), MIN_LEVEL(12), NORMAL_AUTO_START(13), PETTAMENESS(14), PETSPEED(15), ZERO(16);
UNDEFINED(-1), EXP(0), ITEM(1), NEXTQUEST(2), MESO(3), QUEST(4), SKILL(5), FAME(6), BUFF(7), PETSKILL(8), YES(9), NO(10), NPC(11), MIN_LEVEL(12), NORMAL_AUTO_START(13), PETTAMENESS(14), PETSPEED(15), INFO(16), ZERO(16);
final byte type;
private MapleQuestActionType(int type) {
@@ -64,6 +64,8 @@ public enum MapleQuestActionType {
return PETTAMENESS;
} else if (name.equals("petspeed")) {
return PETSPEED;
} else if (name.equals("info")) {
return INFO;
} else if (name.equals("0")) {
return ZERO;
} else {
+54
View File
@@ -0,0 +1,54 @@
/*
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.quest.actions;
import client.MapleCharacter;
import provider.MapleData;
import provider.MapleDataTool;
import server.quest.MapleQuest;
import server.quest.MapleQuestActionType;
/**
*
* @author Ronan
*/
public class InfoAction extends MapleQuestAction {
private String info;
private int questID;
public InfoAction(MapleQuest quest, MapleData data) {
super(MapleQuestActionType.INFO, quest);
questID = quest.getId();
processData(data);
}
@Override
public void processData(MapleData data) {
info = MapleDataTool.getString(data, "");
}
@Override
public void run(MapleCharacter chr, Integer extSelection) {
chr.getAbstractPlayerInteraction().setQuestProgress(questID, info);
}
}
+19 -3
View File
@@ -195,7 +195,7 @@ public class ItemAction extends MapleQuestAction {
if(type.equals(MapleInventoryType.EQUIP) && chr.getInventory(MapleInventoryType.EQUIPPED).countById(item.getId()) > quantity)
continue;
chr.dropMessage(1, "Please check if you have enough items in your inventory.");
announceInventoryLimit(Collections.singletonList(item.getId()), chr);
return false;
} else {
int idx = type.getType() - 1; // more slots available from the given items!
@@ -217,7 +217,7 @@ public class ItemAction extends MapleQuestAction {
result = MapleInventoryManipulator.checkSpaceProgressively(c, it.getLeft().getItemId(), it.getLeft().getQuantity(), "", rndUsed.get(idx), false);
if(result % 2 == 0) {
chr.dropMessage(1, "Please check if you have enough space in your inventory.");
announceInventoryLimit(Collections.singletonList(it.getLeft().getItemId()), chr);
return false;
}
@@ -231,12 +231,28 @@ public class ItemAction extends MapleQuestAction {
}
if (!canHold(chr, gainList)) {
chr.dropMessage(1, "Please check if you have enough space in your inventory.");
List<Integer> gainItemids = new LinkedList<>();
for (Pair<Item, MapleInventoryType> it : gainList) {
gainItemids.add(it.getLeft().getItemId());
}
announceInventoryLimit(gainItemids, chr);
return false;
}
return true;
}
private void announceInventoryLimit(List<Integer> itemids, MapleCharacter chr) {
for (Integer id : itemids) {
if (MapleItemInformationProvider.getInstance().isPickupRestricted(id) && chr.haveItemWithId(id, true)) {
chr.dropMessage(1, "Please check if you already have a similar one-of-a-kind item in your inventory.");
return;
}
}
chr.dropMessage(1, "Please check if you have enough space in your inventory.");
}
private boolean canHold(MapleCharacter chr, List<Pair<Item, MapleInventoryType>> gainList) {
List<Integer> toAddItemids = new LinkedList<>();
List<Integer> toAddQuantity = new LinkedList<>();
@@ -43,10 +43,8 @@ public abstract class MapleQuestAction {
public abstract void run(MapleCharacter chr, Integer extSelection);
public abstract void processData(MapleData data);
public boolean check(MapleCharacter chr, Integer extSelection) {
MapleQuestStatus status = chr.getQuest(MapleQuest.getInstance(questID));
return !(status.getStatus() == MapleQuestStatus.Status.NOT_STARTED && status.getForfeited() > 0);
return true;
}
public MapleQuestActionType getType() {
+1 -1
View File
@@ -58,7 +58,7 @@ public class QuestAction extends MapleQuestAction {
public void run(MapleCharacter chr, Integer extSelection) {
for(Integer questID : quests.keySet()) {
int stat = quests.get(questID);
chr.updateQuest(new MapleQuestStatus(MapleQuest.getInstance(questID), MapleQuestStatus.Status.getById(stat)));
chr.updateQuestStatus(new MapleQuestStatus(MapleQuest.getInstance(questID), MapleQuestStatus.Status.getById(stat)));
}
}
}
@@ -22,7 +22,6 @@
package server.quest.requirements;
import client.MapleCharacter;
import client.MapleQuestStatus;
import java.util.ArrayList;
import java.util.List;
import provider.MapleData;
@@ -41,14 +40,14 @@ public class InfoExRequirement extends MapleQuestRequirement {
public InfoExRequirement(MapleQuest quest, MapleData data) {
super(MapleQuestRequirementType.INFO_EX);
processData(data);
questID = quest.getId();
processData(data);
}
@Override
public void processData(MapleData data) {
// Because we have to...
for(MapleData infoEx : data.getChildren()) {
for(MapleData infoEx : data.getChildren()) {
MapleData value = infoEx.getChildByPath("value");
infoExpected.add(MapleDataTool.getString(value, ""));
}
@@ -57,15 +56,10 @@ public class InfoExRequirement extends MapleQuestRequirement {
@Override
public boolean check(MapleCharacter chr, Integer npcid) {
MapleQuestStatus status = chr.getQuest(MapleQuest.getInstance(questID));
return infoExpected.contains(status.getInfo());
return true;
}
public List<String> getInfo() {
return infoExpected;
}
public String getFirstInfo() {
return !infoExpected.isEmpty() ? infoExpected.get(0) : "";
}
}
}
@@ -0,0 +1,57 @@
/*
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.quest.requirements;
import client.MapleCharacter;
import provider.MapleData;
import provider.MapleDataTool;
import server.quest.MapleQuest;
import server.quest.MapleQuestRequirementType;
/**
*
* @author Ronan
*/
public class InfoNumberRequirement extends MapleQuestRequirement {
private short infoNumber;
private int questID;
public InfoNumberRequirement(MapleQuest quest, MapleData data) {
super(MapleQuestRequirementType.INFO_NUMBER);
questID = quest.getId();
processData(data);
}
@Override
public void processData(MapleData data) {
infoNumber = (short) MapleDataTool.getIntConvert(data, 0);
}
@Override
public boolean check(MapleCharacter chr, Integer npcid) {
return true;
}
public short getInfoNumber() {
return infoNumber;
}
}
@@ -38,8 +38,8 @@ public class IntervalRequirement extends MapleQuestRequirement {
public IntervalRequirement(MapleQuest quest, MapleData data) {
super(MapleQuestRequirementType.INTERVAL);
processData(data);
questID = quest.getId();
processData(data);
}
public int getInterval() {
@@ -51,11 +51,47 @@ public class IntervalRequirement extends MapleQuestRequirement {
interval = MapleDataTool.getInt(data) * 60 * 1000;
}
private static String getIntervalTimeLeft(MapleCharacter chr, IntervalRequirement r) {
StringBuilder str = new StringBuilder();
long futureTime = chr.getQuest(MapleQuest.getInstance(r.questID)).getCompletionTime() + r.getInterval();
long leftTime = futureTime - System.currentTimeMillis();
byte mode = 0;
if(leftTime / (60*1000) > 0) {
mode++; //counts minutes
if(leftTime / (60*60*1000) > 0)
mode++; //counts hours
}
switch(mode) {
case 2:
int hours = (int) ((leftTime / (1000*60*60)));
str.append(hours + " hours, ");
case 1:
int minutes = (int) ((leftTime / (1000*60)) % 60);
str.append(minutes + " minutes, ");
default:
int seconds = (int) (leftTime / 1000) % 60 ;
str.append(seconds + " seconds");
}
return str.toString();
}
@Override
public boolean check(MapleCharacter chr, Integer npcid) {
boolean check = !chr.getQuest(MapleQuest.getInstance(questID)).getStatus().equals(MapleQuestStatus.Status.COMPLETED);
boolean check2 = chr.getQuest(MapleQuest.getInstance(questID)).getCompletionTime() <= System.currentTimeMillis() - interval;
return check || check2;
if (check || check2) {
return true;
} else {
chr.message("This quest will become available again in approximately " + getIntervalTimeLeft(chr, this) + ".");
return false;
}
}
}
@@ -45,6 +45,11 @@ public class MesoRequirement extends MapleQuestRequirement {
@Override
public boolean check(MapleCharacter chr, Integer npcid) {
return chr.getMeso() >= meso;
if (chr.getMeso() >= meso) {
return true;
} else {
chr.dropMessage(5, "You don't have enough mesos to complete this quest.");
return false;
}
}
}
@@ -42,8 +42,8 @@ public class MobRequirement extends MapleQuestRequirement {
public MobRequirement(MapleQuest quest, MapleData data) {
super(MapleQuestRequirementType.MOB);
processData(data);
questID = quest.getId();
processData(data);
}
/**
@@ -58,12 +58,12 @@ public class QuestRequirement extends MapleQuestRequirement {
public boolean check(MapleCharacter chr, Integer npcid) {
for(Integer questID : quests.keySet()) {
int stateReq = quests.get(questID);
MapleQuestStatus q = chr.getQuest(MapleQuest.getInstance(questID));
MapleQuestStatus qs = chr.getQuest(MapleQuest.getInstance(questID));
if(q == null && MapleQuestStatus.Status.getById(stateReq).equals(MapleQuestStatus.Status.NOT_STARTED))
if(qs == null && MapleQuestStatus.Status.getById(stateReq).equals(MapleQuestStatus.Status.NOT_STARTED))
continue;
if(q == null || !q.getStatus().equals(MapleQuestStatus.Status.getById(stateReq))) {
if(qs == null || !qs.getStatus().equals(MapleQuestStatus.Status.getById(stateReq))) {
return false;
}
+67 -53
View File
@@ -66,6 +66,7 @@ import config.YamlConfig;
import constants.game.ExpTable;
import constants.game.GameConstants;
import constants.inventory.ItemConstants;
import constants.net.ServerConstants;
import constants.skills.Buccaneer;
import constants.skills.Corsair;
import constants.skills.ThunderBreaker;
@@ -351,20 +352,31 @@ public class MaplePacketCreator {
}
private static void addQuestInfo(final MaplePacketLittleEndianWriter mplew, MapleCharacter chr) {
mplew.writeShort(chr.getStartedQuestsSize());
for (MapleQuestStatus q : chr.getStartedQuests()) {
mplew.writeShort(q.getQuest().getId());
mplew.writeMapleAsciiString(q.getQuestData());
if (q.getQuest().getInfoNumber() > 0) {
mplew.writeShort(q.getQuest().getInfoNumber());
mplew.writeMapleAsciiString(q.getQuestData());
List<MapleQuestStatus> started = chr.getStartedQuests();
int startedSize = 0;
for (MapleQuestStatus qs : started) {
if (qs.getInfoNumber() > 0) {
startedSize++;
}
startedSize++;
}
mplew.writeShort(startedSize);
for (MapleQuestStatus qs : started) {
mplew.writeShort(qs.getQuest().getId());
mplew.writeMapleAsciiString(qs.getProgressData());
short infoNumber = qs.getInfoNumber();
if (infoNumber > 0) {
MapleQuestStatus iqs = chr.getQuest(infoNumber);
mplew.writeShort(infoNumber);
mplew.writeMapleAsciiString(iqs.getProgressData());
}
}
List<MapleQuestStatus> completed = chr.getCompletedQuests();
mplew.writeShort(completed.size());
for (MapleQuestStatus q : completed) {
mplew.writeShort(q.getQuest().getId());
mplew.writeLong(getTime(q.getCompletionTime()));
for (MapleQuestStatus qs : completed) {
mplew.writeShort(qs.getQuest().getId());
mplew.writeLong(getTime(qs.getCompletionTime()));
}
}
@@ -2839,9 +2851,9 @@ public class MaplePacketCreator {
}
ArrayList<Short> medalQuests = new ArrayList<>();
List<MapleQuestStatus> completed = chr.getCompletedQuests();
for (MapleQuestStatus q : completed) {
if (q.getQuest().getId() >= 29000) { // && q.getQuest().getId() <= 29923
medalQuests.add(q.getQuest().getId());
for (MapleQuestStatus qs : completed) {
if (qs.getQuest().getId() >= 29000) { // && q.getQuest().getId() <= 29923
medalQuests.add(qs.getQuest().getId());
}
}
@@ -2989,18 +3001,20 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] updateQuest(MapleQuestStatus q, boolean infoUpdate) {
public static byte[] updateQuest(MapleCharacter chr, MapleQuestStatus qs, boolean infoUpdate) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.SHOW_STATUS_INFO.getValue());
mplew.write(1);
mplew.writeShort(infoUpdate ? q.getQuest().getInfoNumber() : q.getQuest().getId());
if (infoUpdate) {
MapleQuestStatus iqs = chr.getQuest(qs.getInfoNumber());
mplew.writeShort(iqs.getQuestID());
mplew.write(1);
mplew.writeMapleAsciiString(iqs.getProgressData());
} else {
mplew.write(q.getStatus().getId());
mplew.writeShort(qs.getQuest().getId());
mplew.write(qs.getStatus().getId());
mplew.writeMapleAsciiString(qs.getProgressData());
}
mplew.writeMapleAsciiString(q.getQuestData());
mplew.skip(5);
return mplew.getPacket();
}
@@ -3194,23 +3208,23 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] getPlayerShopChat(MapleCharacter c, String chat, boolean owner) {
public static byte[] getPlayerShopChat(MapleCharacter chr, String chat, boolean owner) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.CHAT.getCode());
mplew.write(PlayerInteractionHandler.Action.CHAT_THING.getCode());
mplew.write(owner ? 0 : 1);
mplew.writeMapleAsciiString(c.getName() + " : " + chat);
mplew.writeMapleAsciiString(chr.getName() + " : " + chat);
return mplew.getPacket();
}
public static byte[] getPlayerShopNewVisitor(MapleCharacter c, int slot) {
public static byte[] getPlayerShopNewVisitor(MapleCharacter chr, int slot) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.VISIT.getCode());
mplew.write(slot);
addCharLook(mplew, c, false);
mplew.writeMapleAsciiString(c.getName());
addCharLook(mplew, chr, false);
mplew.writeMapleAsciiString(chr.getName());
return mplew.getPacket();
}
@@ -3224,22 +3238,22 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] getTradePartnerAdd(MapleCharacter c) {
public static byte[] getTradePartnerAdd(MapleCharacter chr) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.VISIT.getCode());
mplew.write(1);
addCharLook(mplew, c, false);
mplew.writeMapleAsciiString(c.getName());
addCharLook(mplew, chr, false);
mplew.writeMapleAsciiString(chr.getName());
return mplew.getPacket();
}
public static byte[] tradeInvite(MapleCharacter c) {
public static byte[] tradeInvite(MapleCharacter chr) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.INVITE.getCode());
mplew.write(3);
mplew.writeMapleAsciiString(c.getName());
mplew.writeMapleAsciiString(chr.getName());
mplew.write(new byte[]{(byte) 0xB7, (byte) 0x50, 0, 0});
return mplew.getPacket();
}
@@ -4439,15 +4453,15 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] showGuildInfo(MapleCharacter c) {
public static byte[] showGuildInfo(MapleCharacter chr) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.GUILD_OPERATION.getValue());
mplew.write(0x1A); //signature for showing guild info
if (c == null) { //show empty guild (used for leaving, expelled)
if (chr == null) { //show empty guild (used for leaving, expelled)
mplew.write(0);
return mplew.getPacket();
}
MapleGuild g = c.getClient().getWorldServer().getGuild(c.getMGC());
MapleGuild g = chr.getClient().getWorldServer().getGuild(chr.getMGC());
if (g == null) { //failed to read from DB - don't show a guild
mplew.write(0);
return mplew.getPacket();
@@ -5292,17 +5306,17 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] getMiniGameNewVisitor(MapleMiniGame minigame, MapleCharacter c, int slot) {
public static byte[] getMiniGameNewVisitor(MapleMiniGame minigame, MapleCharacter chr, int slot) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.VISIT.getCode());
mplew.write(slot);
addCharLook(mplew, c, false);
mplew.writeMapleAsciiString(c.getName());
addCharLook(mplew, chr, false);
mplew.writeMapleAsciiString(chr.getName());
mplew.writeInt(1);
mplew.writeInt(c.getMiniGamePoints(MiniGameResult.WIN, true));
mplew.writeInt(c.getMiniGamePoints(MiniGameResult.TIE, true));
mplew.writeInt(c.getMiniGamePoints(MiniGameResult.LOSS, true));
mplew.writeInt(chr.getMiniGamePoints(MiniGameResult.WIN, true));
mplew.writeInt(chr.getMiniGamePoints(MiniGameResult.TIE, true));
mplew.writeInt(chr.getMiniGamePoints(MiniGameResult.LOSS, true));
mplew.writeInt(minigame.getVisitorScore());
return mplew.getPacket();
}
@@ -5447,17 +5461,17 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] getMatchCardNewVisitor(MapleMiniGame minigame, MapleCharacter c, int slot) {
public static byte[] getMatchCardNewVisitor(MapleMiniGame minigame, MapleCharacter chr, int slot) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.VISIT.getCode());
mplew.write(slot);
addCharLook(mplew, c, false);
mplew.writeMapleAsciiString(c.getName());
addCharLook(mplew, chr, false);
mplew.writeMapleAsciiString(chr.getName());
mplew.writeInt(1);
mplew.writeInt(c.getMiniGamePoints(MiniGameResult.WIN, false));
mplew.writeInt(c.getMiniGamePoints(MiniGameResult.TIE, false));
mplew.writeInt(c.getMiniGamePoints(MiniGameResult.LOSS, false));
mplew.writeInt(chr.getMiniGamePoints(MiniGameResult.WIN, false));
mplew.writeInt(chr.getMiniGamePoints(MiniGameResult.TIE, false));
mplew.writeInt(chr.getMiniGamePoints(MiniGameResult.LOSS, false));
mplew.writeInt(minigame.getVisitorScore());
return mplew.getPacket();
}
@@ -5559,19 +5573,19 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] addOmokBox(MapleCharacter c, int ammount, int type) {
public static byte[] addOmokBox(MapleCharacter chr, int ammount, int type) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue());
mplew.writeInt(c.getId());
addAnnounceBox(mplew, c.getMiniGame(), ammount, type);
mplew.writeInt(chr.getId());
addAnnounceBox(mplew, chr.getMiniGame(), ammount, type);
return mplew.getPacket();
}
public static byte[] addMatchCardBox(MapleCharacter c, int ammount, int type) {
public static byte[] addMatchCardBox(MapleCharacter chr, int ammount, int type) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue());
mplew.writeInt(c.getId());
addAnnounceBox(mplew, c.getMiniGame(), ammount, type);
mplew.writeInt(chr.getId());
addAnnounceBox(mplew, chr.getMiniGame(), ammount, type);
return mplew.getPacket();
}
@@ -5583,23 +5597,23 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] getPlayerShopChat(MapleCharacter c, String chat, byte slot) {
public static byte[] getPlayerShopChat(MapleCharacter chr, String chat, byte slot) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.CHAT.getCode());
mplew.write(PlayerInteractionHandler.Action.CHAT_THING.getCode());
mplew.write(slot);
mplew.writeMapleAsciiString(c.getName() + " : " + chat);
mplew.writeMapleAsciiString(chr.getName() + " : " + chat);
return mplew.getPacket();
}
public static byte[] getTradeChat(MapleCharacter c, String chat, boolean owner) {
public static byte[] getTradeChat(MapleCharacter chr, String chat, boolean owner) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.CHAT.getCode());
mplew.write(PlayerInteractionHandler.Action.CHAT_THING.getCode());
mplew.write(owner ? 0 : 1);
mplew.writeMapleAsciiString(c.getName() + " : " + chat);
mplew.writeMapleAsciiString(chr.getName() + " : " + chat);
return mplew.getPacket();
}
@@ -0,0 +1,35 @@
/*
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 tools.exceptions;
import tools.data.input.LittleEndianAccessor;
/**
*
* @author Ronan
*/
public class EmptyMovementException extends Exception {
public EmptyMovementException(LittleEndianAccessor lea) {
super("Empty movement: " + lea);
}
}