Event Recall + Cash Shop bestsellers + MapleSessionCoordinator rework

Implemented an event recall system. Players that went disconnected during an event instance are able to rejoin the ongoing event upon relogin.
Implemented a player-activity backed best-sellers system for the Cash Shop.
Patched the recently added selective loot system interfering with quest items, ever disabling drops after the player picked up one item.
Implemented a server flag for everlasting buffs.
Fixed some inconsistencies with Priest Dispel skill, sometimes crashing party players.
Fixed change job not properly showing effects for other players.
Fixed wrong fee value being taken from players that expands their guild size. Also, implemented GMS-like fee for this action.
Reworked the MapleSessionCoordinator, now evaluating client's HWID as well as remote IP. This's expected to lessen account drought time for players that are constantly changing their IP.

Last but not least, added world maps for Mushroom Castle, Zipangu, CBD/Malaysia and Ellin Forest regions. Original artwork content used on files depicted in this topic are rightful property of Nexon Corps., these files thoroughly trying to adhere the "Fair Use" disclaimer policy, their purpose being solely to fulfill gaming experience for the areas that were already present on v83 GMS but still lacked worldmaps. For more info regarding Fair Use, please refer to "http://www.dmlp.org/legal-guide/fair-use".
This commit is contained in:
ronancpl committed 2018-09-07 14:55:29 -03:00
1 parent 132f286391
commit c3e3c6dfbb
82 files changed
+2470 -378

No files matched your search

+27
View File
@@ -1266,3 +1266,30 @@ Corrigido setGender não modificando estado do cliente para não-logado ao cance
Melhorado mecânica de logout de líder de party, passando a liderança adiante antes de efetivamente deslogar, permitindo assim instâncias a continuarem após a saída do mesmo, dadas circunstâncias favoráveis. Melhorado mecânica de logout de líder de party, passando a liderança adiante antes de efetivamente deslogar, permitindo assim instâncias a continuarem após a saída do mesmo, dadas circunstâncias favoráveis.
Outra correção no XMLDomMapleData, que ainda continua dando NullPointerExceptions. Outra correção no XMLDomMapleData, que ainda continua dando NullPointerExceptions.
Corrigido stati de mobs não protegido concorrentemente em certos casos. Corrigido stati de mobs não protegido concorrentemente em certos casos.
28 Agosto 2018,
Implementado sistema de rewarp para instâncias de eventos, usado por jogadores ao se reconectar ao jogo. Ideia de Alisson.
Adicionado contador de compras de itens do cash shop. Sugestão de mais comprados do cash shop implementado.
Otimizado função goto, agora não mais gerando mapas de cidade/mapid a todo uso de comando.
29 - 31 Agosto 2018,
Melhorado função de disconnect do MapleClient, evitando múltiplos envios de dados do jogador à DB.
Desenhado (todo conteúdo de imagens creditado à Nexon) e implementado novos mapas-mundi referente a regiões de M. Shrine, Showa, CBD e Metropolis/Kampung.
03 Agosto 2018,
Implementado buffs inexpiráveis.
Corrigido comando resetStats não levando em conta AP's atuais dos jogadores.
Corrigido novo sistema gerenciador de loots removendo a possibilidade de conseguir novos itens de quest após coleta do primeiro item.
04 Agosto 2018,
Corrigido um flicker na animação do efeito da skill Hurricane.
Ajustado MapleSessionCoordinator, agora verificando HWID's ao invés de contar somente com o remote IP, evitando negação de serviço para usuários de VPNs.
Corrigido dispel normal incorretamente mostrando efeitos aleatórios a outros jogadores, issue apontado por Thora.
Corrigido change job não mostrando efeito a outros jogadores.
Corrigido valores incorretos sendo retirado de jogadores para expansão de guild, issue apontado por Thora.
05 Agosto 2018,
Adicionado world map em Ellin Forest.
Protegido concorrentemente sistema de fames.
Adicionado ganho de quest points para jogadores que participam de PQs. Reformulado sistema de quest points para viabilizar a nova feature.
Otimizado método de ganho de experiência em equipamentos, agora devidamente cacheado e sem busca em strings no processo.
+1 -1
View File
@@ -7,4 +7,4 @@ file.reference.slf4j-api-1.6.6.jar=C:\\Nexon\\HeavenMS\\cores\\slf4j-api-1.6.6.j
file.reference.slf4j-jdk14-1.7.5.jar=C:\\Nexon\\HeavenMS\\cores\\slf4j-jdk14-1.7.5.jar file.reference.slf4j-jdk14-1.7.5.jar=C:\\Nexon\\HeavenMS\\cores\\slf4j-jdk14-1.7.5.jar
javac.debug=true javac.debug=true
javadoc.preview=true javadoc.preview=true
user.properties.file=C:\\Users\\USER\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties user.properties.file=C:\\Users\\RonanLana\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties
+11
View File
@@ -142,6 +142,11 @@ function scheduledTimeout(eim) {
} }
} }
function bunnyDefeated(eim) {
eim.dropMessage(5, "Due to your failure to protect the Moon Bunny, you have been transported to the Exile Map.");
end(eim);
}
function playerUnregistered(eim, player) {} function playerUnregistered(eim, player) {}
function playerExit(eim, player) { function playerExit(eim, player) {
@@ -232,6 +237,12 @@ function clearPQ(eim) {
function monsterKilled(mob, eim) {} function monsterKilled(mob, eim) {}
function friendlyKilled(mob, eim) {
if (mob.getId() == 9300061) {
eim.schedule("bunnyDefeated", 5 * 1000);
}
}
function allMonstersDead(eim) {} function allMonstersDead(eim) {}
function cancelSchedule() {} function cancelSchedule() {}
+45
View File
@@ -0,0 +1,45 @@
/*
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/>.
*/
var status;
function start() {
status = -1;
action(1, 0, 0);
}
function action(mode, type, selection) {
if (mode == -1) {
cm.dispose();
} else {
if (mode == 0 && type > 0) {
cm.dispose();
return;
}
if (mode == 1)
status++;
else
status--;
if(status == 0) {
cm.dispose();
}
}
}
+7 -4
View File
@@ -32,7 +32,7 @@ var ambientSong = "Bgm04/Shinin'Harbor";
var feature_tree = []; var feature_tree = [];
var feature_cursor; var feature_cursor;
var tabs = ["PQs", "Skills", "Quests", "Player Social Network", "Cash & Items", "Monsters, Maps & Reactors", "PQ potentials", "Player potentials", "Server potentials", "Admin/GM commands", "Custom NPCs", "Localhost edits", "Project"]; var tabs = ["PQs", "Skills", "Quests", "Player Social Network", "Cash & Items", "Monsters, Maps & Reactors", "PQ potentials", "Player potentials", "Server potentials", "Commands", "Custom NPCs", "Localhost edits", "Project"];
function addFeature(feature) { function addFeature(feature) {
feature_cursor.push(feature); feature_cursor.push(feature);
@@ -43,7 +43,7 @@ function writeFeatureTab_PQs() {
addFeature("RnJPQ/HorntailPQ/TreasurePQ/ElnathPQ/HolidayPQ."); addFeature("RnJPQ/HorntailPQ/TreasurePQ/ElnathPQ/HolidayPQ.");
addFeature("CWKPQ as Expedition-based instance."); addFeature("CWKPQ as Expedition-based instance.");
addFeature("Scarga/Horntail/Showa/Balrog/Zakum/Pinkbean."); addFeature("Scarga/Horntail/Showa/Balrog/Zakum/Pinkbean.");
addFeature("GuildPQ & queue with multi-lobby systems available."); addFeature("GuildPQ & queue with multi-lobby system available.");
addFeature("Brand-new PQs: BossRushPQ, CafePQ."); addFeature("Brand-new PQs: BossRushPQ, CafePQ.");
addFeature("Mu Lung Dojo."); addFeature("Mu Lung Dojo.");
addFeature("Capt. Latanica with party fighting the boss."); addFeature("Capt. Latanica with party fighting the boss.");
@@ -128,6 +128,7 @@ function writeFeatureTab_MonstersMapsReactors() {
addFeature("Reactors pick items up smartly from the field."); addFeature("Reactors pick items up smartly from the field.");
addFeature("Updated scripted portals now with proper portal SFX."); addFeature("Updated scripted portals now with proper portal SFX.");
addFeature("Reviewed Masteria, W. Tour, N. Desert and Neo City."); addFeature("Reviewed Masteria, W. Tour, N. Desert and Neo City.");
addFeature("Added world maps for M. Castle, W. Tour & Ellin areas.");
addFeature("Giant Cake boss drops s. bags and Maple items."); addFeature("Giant Cake boss drops s. bags and Maple items.");
} }
@@ -137,13 +138,14 @@ function writeFeatureTab_PQpotentials() {
addFeature("Exped system: Many parties can join a same instance."); addFeature("Exped system: Many parties can join a same instance.");
addFeature("Guild queue: guild registration for the GPQ."); addFeature("Guild queue: guild registration for the GPQ.");
addFeature("EIM Pool system: optimized instance loadouts."); addFeature("EIM Pool system: optimized instance loadouts.");
addFeature("Recall system: players can rejoin PQ after d/c.");
} }
function writeFeatureTab_Playerpotentials() { function writeFeatureTab_Playerpotentials() {
addFeature("Adventurer Mount quests functional."); addFeature("Adventurer Mount quests functional.");
addFeature("All Equipment levels up."); addFeature("All Equipment levels up.");
addFeature("Player level rates."); addFeature("Player level rates.");
addFeature("Gain fame by quests."); addFeature("Gain fame by quests and event instances.");
addFeature("Pet evolutions functional (not GMS-like)."); addFeature("Pet evolutions functional (not GMS-like).");
addFeature("Reviewed keybinding system."); addFeature("Reviewed keybinding system.");
addFeature("Character slots per world/server-wide."); addFeature("Character slots per world/server-wide.");
@@ -160,6 +162,7 @@ function writeFeatureTab_Serverpotentials() {
addFeature("Enhanced AP auto-assigner: focus on eqp demands."); addFeature("Enhanced AP auto-assigner: focus on eqp demands.");
addFeature("Enhanced inventory check: free slots smartly fetched."); addFeature("Enhanced inventory check: free slots smartly fetched.");
addFeature("Enhanced petloot handler: no brute-force inv. checks."); addFeature("Enhanced petloot handler: no brute-force inv. checks.");
addFeature("Players-appointed bestsellers for Owl and Cash Shop.");
addFeature("Tweaked pet/mount hunger to a balanced growth rate."); addFeature("Tweaked pet/mount hunger to a balanced growth rate.");
addFeature("Consistent experience and meso gain system."); addFeature("Consistent experience and meso gain system.");
addFeature("NPC crafters won't take items freely anymore."); addFeature("NPC crafters won't take items freely anymore.");
@@ -184,7 +187,7 @@ function writeFeatureTab_Serverpotentials() {
addFeature("Automatic account registration - thanks shavit!"); addFeature("Automatic account registration - thanks shavit!");
} }
function writeFeatureTab_AdminGMcommands() { function writeFeatureTab_Commands() {
addFeature("Spawn Zakum/Horntail/Pinkbean."); addFeature("Spawn Zakum/Horntail/Pinkbean.");
addFeature("Several new commands."); addFeature("Several new commands.");
addFeature("Rank command highlighting users by world or overall."); addFeature("Rank command highlighting users by world or overall.");
+1 -1
View File
@@ -47,7 +47,7 @@ function action(mode, type, selection) {
cm.sendSimple(sendStr); cm.sendSimple(sendStr);
} else if(status == 1) { } else if(status == 1) {
var lvComm, lvDesc, lvHead = (cm.getPlayer().gmLevel() < 2) ? common_heading : staff_heading; var lvComm, lvDesc, lvHead = (selection < 2) ? common_heading : staff_heading;
if(selection > 6) { if(selection > 6) {
selection = 6; selection = 6;
+8 -8
View File
@@ -12935,6 +12935,14 @@ CREATE TABLE IF NOT EXISTS `htsquads` (
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `hwidaccounts` (
`accountid` int(11) NOT NULL DEFAULT '0',
`hwid` varchar(40) NOT NULL DEFAULT '',
`relevance` tinyint(2) NOT NULL DEFAULT '0',
`expiresat` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`accountid`,`hwid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `hwidbans` ( CREATE TABLE IF NOT EXISTS `hwidbans` (
`hwidbanid` int(10) unsigned NOT NULL AUTO_INCREMENT, `hwidbanid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`hwid` varchar(30) NOT NULL, `hwid` varchar(30) NOT NULL,
@@ -13005,14 +13013,6 @@ CREATE TABLE IF NOT EXISTS `ipbans` (
PRIMARY KEY (`ipbanid`) PRIMARY KEY (`ipbanid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `ipaccounts` (
`accountid` int(11) NOT NULL DEFAULT '0',
`ip` varchar(40) NOT NULL DEFAULT '',
`relevance` tinyint(2) NOT NULL DEFAULT '0',
`expiresat` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`accountid`,`ip`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `keymap` ( CREATE TABLE IF NOT EXISTS `keymap` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`characterid` int(11) NOT NULL DEFAULT '0', `characterid` int(11) NOT NULL DEFAULT '0',
+2 -4
View File
@@ -1871,7 +1871,7 @@ USE `heavenms`;
(2230102, 4000020, 1, 1, 0, 200000), (2230102, 4000020, 1, 1, 0, 200000),
(2230102, 4000021, 1, 1, 0, 200000), (2230102, 4000021, 1, 1, 0, 200000),
(2230102, 4003004, 1, 1, 0, 7000), (2230102, 4003004, 1, 1, 0, 7000),
(2230102, 4001372, 1, 1, 0, 200000), (2230102, 4001372, 1, 1, 28282, 200000),
(2230102, 2000001, 1, 1, 0, 40000), (2230102, 2000001, 1, 1, 0, 40000),
(2230102, 2000003, 1, 1, 0, 40000), (2230102, 2000003, 1, 1, 0, 40000),
(2230102, 2002004, 1, 1, 0, 10000), (2230102, 2002004, 1, 1, 0, 10000),
@@ -2107,7 +2107,7 @@ USE `heavenms`;
(2230112, 1082186, 1, 1, 0, 700), (2230112, 1082186, 1, 1, 0, 700),
(2230100, 4000007, 1, 1, 0, 200000), (2230100, 4000007, 1, 1, 0, 200000),
(2230100, 4030012, 1, 1, 0, 125000), (2230100, 4030012, 1, 1, 0, 125000),
(2230100, 4001373, 1, 1, 0, 7000), (2230100, 4001373, 1, 1, 28282, 200000),
(2230100, 2000001, 1, 1, 0, 40000), (2230100, 2000001, 1, 1, 0, 40000),
(2230100, 2000003, 1, 1, 0, 40000), (2230100, 2000003, 1, 1, 0, 40000),
(2230100, 2002001, 1, 1, 0, 10000), (2230100, 2002001, 1, 1, 0, 10000),
@@ -20795,8 +20795,6 @@ USE `heavenms`;
UPDATE drop_data SET chance=0 WHERE itemid=2050099; UPDATE drop_data SET chance=0 WHERE itemid=2050099;
UPDATE drop_data SET questid=6191 WHERE itemid=4031477; UPDATE drop_data SET questid=6191 WHERE itemid=4031477;
UPDATE drop_data SET questid=6190 WHERE itemid=4001111; UPDATE drop_data SET questid=6190 WHERE itemid=4001111;
UPDATE drop_data SET questid=28344 WHERE itemid=4001372;
UPDATE drop_data SET questid=28282 WHERE itemid=4001373;
# two items named "Sparta": remove the entries where lv100 Sparta is being dropped by low-level mobs. # two items named "Sparta": remove the entries where lv100 Sparta is being dropped by low-level mobs.
UPDATE IGNORE drop_data SET itemid=1402011 WHERE itemid=1302056 AND dropperid < 8000000; UPDATE IGNORE drop_data SET itemid=1402011 WHERE itemid=1302056 AND dropperid < 8000000;
+76 -33
View File
@@ -998,7 +998,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
} }
public FameStatus canGiveFame(MapleCharacter from) { public FameStatus canGiveFame(MapleCharacter from) {
if (gmLevel > 0) { if (this.isGM()) {
return FameStatus.OK; return FameStatus.OK;
} else if (lastfametime >= System.currentTimeMillis() - 3600000 * 24) { } else if (lastfametime >= System.currentTimeMillis() - 3600000 * 24) {
return FameStatus.NOT_TODAY; return FameStatus.NOT_TODAY;
@@ -1171,9 +1171,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
setMasteries(this.job.getId()); setMasteries(this.job.getId());
guildUpdate(); guildUpdate();
getMap().broadcastMessage(this, MaplePacketCreator.removePlayerFromMap(this.getId()), false); getMap().broadcastMessage(this, MaplePacketCreator.showForeignEffect(this.getId(), 8), false);
getMap().broadcastMessage(this, MaplePacketCreator.spawnPlayerMapObject(this), false);
getMap().broadcastMessage(this, MaplePacketCreator.showForeignEffect(getId(), 8), false);
if (GameConstants.hasSPTable(newJob) && newJob.getId() != 2001) { if (GameConstants.hasSPTable(newJob) && newJob.getId() != 2001) {
if (getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null) { if (getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null) {
@@ -1183,7 +1181,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
} }
if (ServerConstants.USE_ANNOUNCE_CHANGEJOB) { if (ServerConstants.USE_ANNOUNCE_CHANGEJOB) {
if(gmLevel > 1) { if (!this.isGM()) {
broadcastAcquaintances(6, "[" + GameConstants.ordinal(GameConstants.getJobBranch(newJob)) + " Job] " + name + " has just become a " + newJob.name() + "."); broadcastAcquaintances(6, "[" + GameConstants.ordinal(GameConstants.getJobBranch(newJob)) + " Job] " + name + " has just become a " + newJob.name() + ".");
} }
} }
@@ -2793,7 +2791,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
} }
public enum FameStatus { public enum FameStatus {
OK, NOT_TODAY, NOT_THIS_MONTH OK, NOT_TODAY, NOT_THIS_MONTH
} }
@@ -2887,9 +2884,39 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
} }
} }
private synchronized int applyFame(int delta) {
int newFame = fame + delta;
if (newFame < -30000) {
delta = -(30000 + fame);
} else if (newFame > 30000) {
delta = 30000 - fame;
}
fame += delta;
return delta;
}
public void gainFame(int delta) { public void gainFame(int delta) {
this.addFame(delta); gainFame(delta, null, 0);
this.updateSingleStat(MapleStat.FAME, this.fame); }
public boolean gainFame(int delta, MapleCharacter fromPlayer, int mode) {
delta = applyFame(delta);
if (delta != 0) {
int thisFame = fame;
updateSingleStat(MapleStat.FAME, thisFame);
if (fromPlayer != null) {
fromPlayer.announce(MaplePacketCreator.giveFameResponse(mode, getName(), thisFame));
announce(MaplePacketCreator.receiveFame(mode, fromPlayer.getName()));
} else {
announce(MaplePacketCreator.getShowFameGain(delta));
}
return true;
} else {
return false;
}
} }
public void gainMeso(int gain) { public void gainMeso(int gain) {
@@ -5417,16 +5444,18 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
return getInventory(MapleInventoryType.getByType(invType)).getNextFreeSlot() > -1; return getInventory(MapleInventoryType.getByType(invType)).getNextFreeSlot() > -1;
} }
public void increaseGuildCapacity() { //hopefully nothing is null public void increaseGuildCapacity() {
if (getMeso() < MapleGuild.getIncreaseGuildCost(getGuild().getCapacity())) { int cost = MapleGuild.getIncreaseGuildCost(getGuild().getCapacity());
if (getMeso() < cost) {
dropMessage(1, "You don't have enough mesos."); dropMessage(1, "You don't have enough mesos.");
return; return;
} }
if(Server.getInstance().increaseGuildCapacity(guildid)) { if(Server.getInstance().increaseGuildCapacity(guildid)) {
gainMeso(-MapleGuild.getIncreaseGuildCost(getGuild().getCapacity()), true, false, false); gainMeso(-cost, true, false, true);
} else { } else {
dropMessage(1, "You guild already reached the maximum capacity of players."); dropMessage(1, "Your guild already reached the maximum capacity of players.");
} }
} }
@@ -5581,6 +5610,8 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
} }
public void levelUp(boolean takeexp) { public void levelUp(boolean takeexp) {
petLock.lock();
try {
Skill improvingMaxHP = null; Skill improvingMaxHP = null;
Skill improvingMaxMP = null; Skill improvingMaxMP = null;
int improvingMaxHPLevel = 0; int improvingMaxHPLevel = 0;
@@ -5730,6 +5761,9 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
levelUpMessages(); levelUpMessages();
guildUpdate(); guildUpdate();
} finally {
petLock.unlock();
}
} }
public void gainAp(int amount) { public void gainAp(int amount) {
@@ -6939,27 +6973,25 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
visibleMapObjects.remove(mo); visibleMapObjects.remove(mo);
} }
public void resetStats() { public synchronized void resetStats() {
if(!ServerConstants.USE_AUTOASSIGN_STARTERS_AP) { if(!ServerConstants.USE_AUTOASSIGN_STARTERS_AP) {
return; return;
} }
List<Pair<MapleStat, Integer>> statup = new ArrayList<>(5); List<Pair<MapleStat, Integer>> statup = new ArrayList<>(5);
int tap = 0, tsp = 1; int tap = remainingAp + str + dex + int_ + luk, tsp = 1;
int tstr = 4, tdex = 4, tint = 4, tluk = 4; int tstr = 4, tdex = 4, tint = 4, tluk = 4;
int levelap = (isCygnus() ? 6 : 5);
switch (job.getId()) { switch (job.getId()) {
case 100: case 100:
case 1100: case 1100:
case 2100: case 2100:
tstr = 35; tstr = 35;
tap = ((getLevel() - 10) * levelap) + 14;
tsp += ((getLevel() - 10) * 3); tsp += ((getLevel() - 10) * 3);
break; break;
case 200: case 200:
case 1200: case 1200:
tint = 20; tint = 20;
tap = ((getLevel() - 8) * levelap) + 29;
tsp += ((getLevel() - 8) * 3); tsp += ((getLevel() - 8) * 3);
break; break;
case 300: case 300:
@@ -6967,21 +6999,26 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
case 400: case 400:
case 1400: case 1400:
tdex = 25; tdex = 25;
tap = ((getLevel() - 10) * levelap) + 24;
tsp += ((getLevel() - 10) * 3); tsp += ((getLevel() - 10) * 3);
break; break;
case 500: case 500:
case 1500: case 1500:
tdex = 20; tdex = 20;
tap = ((getLevel() - 10) * levelap) + 29;
tsp += ((getLevel() - 10) * 3); tsp += ((getLevel() - 10) * 3);
break; break;
} }
tap -= tstr;
tap -= tdex;
tap -= tint;
tap -= tluk;
if (tap >= 0) {
this.remainingAp = tap; this.remainingAp = tap;
this.remainingSp[GameConstants.getSkillBook(job.getId())] = tsp; this.remainingSp[GameConstants.getSkillBook(job.getId())] = tsp;
this.str = tstr;
this.dex = tdex; this.dex = tdex;
this.int_ = tint; this.int_ = tint;
this.str = tstr;
this.luk = tluk; this.luk = tluk;
statup.add(new Pair<>(MapleStat.AVAILABLEAP, tap)); statup.add(new Pair<>(MapleStat.AVAILABLEAP, tap));
statup.add(new Pair<>(MapleStat.AVAILABLESP, tsp)); statup.add(new Pair<>(MapleStat.AVAILABLESP, tsp));
@@ -6990,6 +7027,9 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
statup.add(new Pair<>(MapleStat.INT, tint)); statup.add(new Pair<>(MapleStat.INT, tint));
statup.add(new Pair<>(MapleStat.LUK, tluk)); statup.add(new Pair<>(MapleStat.LUK, tluk));
announce(MaplePacketCreator.updatePlayerStats(statup, this)); announce(MaplePacketCreator.updatePlayerStats(statup, this));
} else {
FilePrinter.print(FilePrinter.EXCEPTION_CAUGHT, name + " tried to get their stats reseted, without having enough AP available.");
}
} }
public void resetBattleshipHp() { public void resetBattleshipHp() {
@@ -8475,17 +8515,19 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
announce(MaplePacketCreator.updateQuestInfo((short) qs.getQuest().getId(), qs.getNpc())); announce(MaplePacketCreator.updateQuestInfo((short) qs.getQuest().getId(), qs.getNpc()));
} }
private void fameGainByQuest() { public void awardQuestPoint(int awardedPoints) {
synchronized (quests) { if (ServerConstants.QUEST_POINT_REQUIREMENT < 1 || awardedPoints < 1) return;
quest_fame += 1;
int delta = quest_fame / ServerConstants.FAME_GAIN_BY_QUEST; int delta;
if(delta > 0) { synchronized (quests) {
gainFame(delta); quest_fame += awardedPoints;
client.announce(MaplePacketCreator.getShowFameGain(delta));
delta = quest_fame / ServerConstants.QUEST_POINT_REQUIREMENT;
quest_fame %= ServerConstants.QUEST_POINT_REQUIREMENT;
} }
quest_fame %= ServerConstants.FAME_GAIN_BY_QUEST; if(delta > 0) {
gainFame(delta);
} }
} }
@@ -8503,8 +8545,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
MapleQuest mquest = quest.getQuest(); MapleQuest mquest = quest.getQuest();
short questid = mquest.getId(); short questid = mquest.getId();
if(!mquest.isSameDayRepeatable() && !MapleQuest.isExploitableQuest(questid)) { if(!mquest.isSameDayRepeatable() && !MapleQuest.isExploitableQuest(questid)) {
if(ServerConstants.FAME_GAIN_BY_QUEST > 0) awardQuestPoint(ServerConstants.QUEST_POINT_PER_QUEST_COMPLETE);
fameGainByQuest();
} }
announce(MaplePacketCreator.completeQuest(questid, quest.getCompletionTime())); announce(MaplePacketCreator.completeQuest(questid, quest.getCompletionTime()));
@@ -8572,7 +8613,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private void runQuestExpireTask() { private void runQuestExpireTask() {
petLock.lock(); petLock.lock();
try { try {
long timeNow = System.currentTimeMillis(); long timeNow = Server.getInstance().getCurrentTime();
List<MapleQuest> expireList = new LinkedList<>(); List<MapleQuest> expireList = new LinkedList<>();
for(Entry<MapleQuest, Long> qe : questExpirations.entrySet()) { for(Entry<MapleQuest, Long> qe : questExpirations.entrySet()) {
@@ -8607,7 +8648,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}, 10 * 1000); }, 10 * 1000);
} }
questExpirations.put(quest, System.currentTimeMillis() + time); questExpirations.put(quest, Server.getInstance().getCurrentTime() + time);
} finally { } finally {
petLock.unlock(); petLock.unlock();
} }
@@ -8880,6 +8921,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
return false; return false;
} }
//EVENTS //EVENTS
private byte team = 0; private byte team = 0;
private MapleFitness fitness; private MapleFitness fitness;
@@ -8917,6 +8959,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
public void setLastSnowballAttack(long time) { public void setLastSnowballAttack(long time) {
this.snowballattack = time; this.snowballattack = time;
} }
//Monster Carnival //Monster Carnival
private int cp = 0; private int cp = 0;
private int obtainedcp = 0; private int obtainedcp = 0;
@@ -9059,7 +9102,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
continue; continue;
} }
if ((nEquip.getItemLevel() < ServerConstants.USE_EQUIPMNT_LVLUP) || (itemName.contains("Reverse") && nEquip.getItemLevel() < 4) || (itemName.contains("Timeless") && nEquip.getItemLevel() < 6)) { if ((nEquip.getItemLevel() < ServerConstants.USE_EQUIPMNT_LVLUP) || (nEquip.getItemLevel() < ii.getEquipLevel(nEquip.getItemId(), true))) {
nEquip.gainItemExp(client, expGain); nEquip.gainItemExp(client, expGain);
} }
} }
+9 -19
View File
@@ -85,6 +85,8 @@ public class MapleClient {
public static final int LOGIN_SERVER_TRANSITION = 1; public static final int LOGIN_SERVER_TRANSITION = 1;
public static final int LOGIN_LOGGEDIN = 2; public static final int LOGIN_LOGGEDIN = 2;
public static final String CLIENT_KEY = "CLIENT"; public static final String CLIENT_KEY = "CLIENT";
public static final String CLIENT_HWID = "HWID";
public static final String CLIENT_NIBBLEHWID = "HWID2";
private MapleAESOFB send; private MapleAESOFB send;
private MapleAESOFB receive; private MapleAESOFB receive;
private final IoSession session; private final IoSession session;
@@ -507,7 +509,7 @@ public class MapleClient {
return false; return false;
} }
public int login(String login, String pwd) { public int login(String login, String pwd, String nibbleHwid) {
loginattempt++; loginattempt++;
if (loginattempt > 4) { if (loginattempt > 4) {
MapleSessionCoordinator.getInstance().closeSession(session, false); MapleSessionCoordinator.getInstance().closeSession(session, false);
@@ -571,7 +573,7 @@ public class MapleClient {
} }
if (loginok == 0 || loginok == 4) { if (loginok == 0 || loginok == 4) {
AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptSessionLogin(session, accId, loginok == 4); AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptLoginSession(session, nibbleHwid, accId, loginok == 4);
switch (res) { switch (res) {
case SUCCESS: case SUCCESS:
@@ -887,22 +889,9 @@ public class MapleClient {
final World wserv = getWorldServer(); // obviously wserv is NOT null if this player was online on it final World wserv = getWorldServer(); // obviously wserv is NOT null if this player was online on it
try { try {
if (channel == -1 || shutdown) {
if(chrg != null) chrg.setCharacter(null);
wserv.removePlayer(player);
removePlayer(wserv);
player.saveCooldowns();
player.cancelAllDebuffs();
player.saveCharToDB(true);
clear();
return;
}
removePlayer(wserv); removePlayer(wserv);
if (!(channel == -1 || shutdown)) {
if (!cashshop) { if (!cashshop) {
if (!this.serverTransition) { // meaning not changing channels if (!this.serverTransition) { // meaning not changing channels
if (messengerid > 0) { if (messengerid > 0) {
@@ -938,20 +927,21 @@ public class MapleClient {
} }
} }
} }
}
} catch (final Exception e) { } catch (final Exception e) {
FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, e); FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, e);
} finally { } finally {
if (!this.serverTransition) { if (!this.serverTransition) {
if(chrg != null) chrg.setCharacter(null);
wserv.removePlayer(player); wserv.removePlayer(player);
//getChannelServer().removePlayer(player); already being done //getChannelServer().removePlayer(player); already being done
player.saveCooldowns(); player.saveCooldowns();
player.cancelAllDebuffs(); player.cancelAllDebuffs();
player.saveCharToDB(true); player.saveCharToDB(true);
if (player != null) {//no idea, occur :(
player.empty(false);
}
player.logOff(); player.logOff();
clear();
} else { } else {
getChannelServer().removePlayer(player); getChannelServer().removePlayer(player);
+1 -1
View File
@@ -301,7 +301,7 @@ public class CommandsExecutor {
addCommand("servermessage", 4, ServerMessageCommand.class); addCommand("servermessage", 4, ServerMessageCommand.class);
addCommand("proitem", 4, ProItemCommand.class); addCommand("proitem", 4, ProItemCommand.class);
addCommand("seteqstat", 4, SetQStatCommand.class); addCommand("seteqstat", 4, SetEqStatCommand.class);
addCommand("exprate", 4, ExpRateCommand.class); addCommand("exprate", 4, ExpRateCommand.class);
addCommand("mesorate", 4, MesoRateCommand.class); addCommand("mesorate", 4, MesoRateCommand.class);
addCommand("droprate", 4, DropRateCommand.class); addCommand("droprate", 4, DropRateCommand.class);
@@ -26,6 +26,7 @@ package client.command.commands.v1;
import client.MapleCharacter; import client.MapleCharacter;
import client.command.Command; import client.command.Command;
import client.MapleClient; import client.MapleClient;
import constants.GameConstants;
import server.MaplePortal; import server.MaplePortal;
import server.maps.MapleMap; import server.maps.MapleMap;
@@ -38,56 +39,7 @@ public class GotoCommand extends Command {
@Override @Override
public void execute(MapleClient c, String[] params) { public void execute(MapleClient c, String[] params) {
final HashMap<String, Integer> gotomaps = new HashMap<String, Integer>(); final HashMap<String, Integer> gotomaps = GameConstants.GOTO_MAPS;
gotomaps.put("gmmap", 180000000);
gotomaps.put("southperry", 60000);
gotomaps.put("amherst", 1000000);
gotomaps.put("henesys", 100000000);
gotomaps.put("ellinia", 101000000);
gotomaps.put("perion", 102000000);
gotomaps.put("kerning", 103000000);
gotomaps.put("lith", 104000000);
gotomaps.put("sleepywood", 105040300);
gotomaps.put("florina", 110000000);
gotomaps.put("nautilus", 120000000);
gotomaps.put("ereve", 130000000);
gotomaps.put("rien", 140000000);
gotomaps.put("orbis", 200000000);
gotomaps.put("happy", 209000000);
gotomaps.put("elnath", 211000000);
gotomaps.put("ludi", 220000000);
gotomaps.put("aqua", 230000000);
gotomaps.put("leafre", 240000000);
gotomaps.put("mulung", 250000000);
gotomaps.put("herb", 251000000);
gotomaps.put("omega", 221000000);
gotomaps.put("korean", 222000000);
gotomaps.put("ellin", 300000000);
gotomaps.put("nlc", 600000000);
gotomaps.put("excavation", 990000000);
gotomaps.put("pianus", 230040420);
gotomaps.put("horntail", 240060200);
gotomaps.put("mushmom", 100000005);
gotomaps.put("griffey", 240020101);
gotomaps.put("manon", 240020401);
gotomaps.put("horseman", 682000001);
gotomaps.put("balrog", 105090900);
gotomaps.put("zakum", 211042300);
gotomaps.put("papu", 220080001);
gotomaps.put("showa", 801000000);
gotomaps.put("guild", 200000301);
gotomaps.put("shrine", 800000000);
gotomaps.put("skelegon", 240040511);
gotomaps.put("hpq", 100000200);
gotomaps.put("ht", 240050400);
gotomaps.put("ariant", 260000000);
gotomaps.put("magatia", 261000000);
gotomaps.put("singapore", 540000000);
gotomaps.put("keep", 610020006);
gotomaps.put("amoria", 680000000);
gotomaps.put("temple", 270000100);
gotomaps.put("neo", 240070000);
gotomaps.put("fm", 910000000);
MapleCharacter player = c.getPlayer(); MapleCharacter player = c.getPlayer();
if (params.length < 1){ if (params.length < 1){
@@ -42,11 +42,12 @@ public class ProItemCommand extends Command {
public void execute(MapleClient c, String[] params) { public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer(); MapleCharacter player = c.getPlayer();
if (params.length < 2) { if (params.length < 2) {
player.yellowMessage("Syntax: !proitem <itemid> <statvalue>"); player.yellowMessage("Syntax: !proitem <itemid> <stat value> [<spdjmp value>]");
return; return;
} }
int itemid = Integer.parseInt(params[0]); int itemid = Integer.parseInt(params[0]);
short multiply = Short.parseShort(params[1]); short stat = (short) Math.max(0, Short.parseShort(params[1]));
short spdjmp = params.length >= 3 ? (short) Math.max(0, Short.parseShort(params[2])) : 0;
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
MapleInventoryType type = ItemConstants.getInventoryType(itemid); MapleInventoryType type = ItemConstants.getInventoryType(itemid);
@@ -54,13 +55,13 @@ public class ProItemCommand extends Command {
Item it = ii.getEquipById(itemid); Item it = ii.getEquipById(itemid);
it.setOwner(player.getName()); it.setOwner(player.getName());
hardsetItemStats((Equip) it, multiply); hardsetItemStats((Equip) it, stat, spdjmp);
MapleInventoryManipulator.addFromDrop(c, it); MapleInventoryManipulator.addFromDrop(c, it);
} else { } else {
player.dropMessage(6, "Make sure it's an equippable item."); player.dropMessage(6, "Make sure it's an equippable item.");
} }
} }
private static void hardsetItemStats(Equip equip, short stat) { private static void hardsetItemStats(Equip equip, short stat, short spdjmp) {
equip.setStr(stat); equip.setStr(stat);
equip.setDex(stat); equip.setDex(stat);
equip.setInt(stat); equip.setInt(stat);
@@ -69,8 +70,8 @@ public class ProItemCommand extends Command {
equip.setWatk(stat); equip.setWatk(stat);
equip.setAcc(stat); equip.setAcc(stat);
equip.setAvoid(stat); equip.setAvoid(stat);
equip.setJump(stat); equip.setJump(spdjmp);
equip.setSpeed(stat); equip.setSpeed(spdjmp);
equip.setWdef(stat); equip.setWdef(stat);
equip.setMdef(stat); equip.setMdef(stat);
equip.setHp(stat); equip.setHp(stat);
@@ -31,7 +31,7 @@ import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType; import client.inventory.MapleInventoryType;
import constants.ItemConstants; import constants.ItemConstants;
public class SetQStatCommand extends Command { public class SetEqStatCommand extends Command {
{ {
setDescription(""); setDescription("");
} }
@@ -40,11 +40,12 @@ public class SetQStatCommand extends Command {
public void execute(MapleClient c, String[] params) { public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer(); MapleCharacter player = c.getPlayer();
if (params.length < 1) { if (params.length < 1) {
player.yellowMessage("Syntax: !seteqstat <statvalue>"); player.yellowMessage("Syntax: !seteqstat <stat value> [<spdjmp value>]");
return; return;
} }
int newStat = Integer.parseInt(params[0]); short newStat = (short) Math.max(0, Integer.parseInt(params[0]));
short newSpdJmp = params.length >= 2 ? (short) Integer.parseInt(params[1]) : 0;
MapleInventory equip = player.getInventory(MapleInventoryType.EQUIP); MapleInventory equip = player.getInventory(MapleInventoryType.EQUIP);
for (byte i = 1; i <= equip.getSlotLimit(); i++) { for (byte i = 1; i <= equip.getSlotLimit(); i++) {
@@ -52,21 +53,20 @@ public class SetQStatCommand extends Command {
Equip eu = (Equip) equip.getItem(i); Equip eu = (Equip) equip.getItem(i);
if (eu == null) continue; if (eu == null) continue;
short incval = (short) newStat; eu.setWdef(newStat);
eu.setWdef(incval); eu.setAcc(newStat);
eu.setAcc(incval); eu.setAvoid(newStat);
eu.setAvoid(incval); eu.setJump(newSpdJmp);
eu.setJump(incval); eu.setMatk(newStat);
eu.setMatk(incval); eu.setMdef(newStat);
eu.setMdef(incval); eu.setHp(newStat);
eu.setHp(incval); eu.setMp(newStat);
eu.setMp(incval); eu.setSpeed(newSpdJmp);
eu.setSpeed(incval); eu.setWatk(newStat);
eu.setWatk(incval); eu.setDex(newStat);
eu.setDex(incval); eu.setInt(newStat);
eu.setInt(incval); eu.setStr(newStat);
eu.setStr(incval); eu.setLuk(newStat);
eu.setLuk(incval);
byte flag = eu.getFlag(); byte flag = eu.getFlag();
flag |= ItemConstants.UNTRADEABLE; flag |= ItemConstants.UNTRADEABLE;
+7 -11
View File
@@ -66,7 +66,7 @@ public class Equip extends Item {
private float itemExp; private float itemExp;
private int ringid = -1; private int ringid = -1;
private boolean wear = false; private boolean wear = false;
private boolean isUpgradeable, isElemental = false; // timeless or reverse private boolean isUpgradeable, isElemental = false; // timeless or reverse, or any equip that could levelup on GMS for all effects
public Equip(int id, short position) { public Equip(int id, short position) {
this(id, position, 0); this(id, position, 0);
@@ -78,8 +78,7 @@ public class Equip extends Item {
this.itemExp = 0; this.itemExp = 0;
this.itemLevel = 1; this.itemLevel = 1;
String itemName = MapleItemInformationProvider.getInstance().getName(id); this.isElemental = (MapleItemInformationProvider.getInstance().getEquipLevel(id, false) > 1);
if(itemName != null) this.isElemental = (itemName.contains("Timeless") || itemName.contains("Reverse"));
} }
@Override @Override
@@ -533,17 +532,14 @@ public class Equip extends Item {
} }
} }
private boolean reachedMaxLevel(String eqpName) { private boolean reachedMaxLevel() {
if (isElemental) { if (isElemental) {
if(eqpName.contains("Timeless")) { if (itemLevel < MapleItemInformationProvider.getInstance().getEquipLevel(getItemId(), true)) {
if(itemLevel < 6) return false; return false;
} else {
if(itemLevel < 4) return false;
} }
} }
if(itemLevel < ServerConstants.USE_EQUIPMNT_LVLUP) return false; return itemLevel >= ServerConstants.USE_EQUIPMNT_LVLUP;
return true;
} }
public String showEquipFeatures(MapleClient c) { public String showEquipFeatures(MapleClient c) {
@@ -551,7 +547,7 @@ public class Equip extends Item {
if(!ii.isUpgradeable(this.getItemId())) return ""; if(!ii.isUpgradeable(this.getItemId())) return "";
String eqpName = ii.getName(getItemId()); String eqpName = ii.getName(getItemId());
String eqpInfo = reachedMaxLevel(eqpName) ? " #e#rMAX LEVEL#k#n" : (" EXP: #e#b" + (int)itemExp + "#k#n / " + ExpTable.getEquipExpNeededForLevel(itemLevel)); String eqpInfo = reachedMaxLevel() ? " #e#rMAX LEVEL#k#n" : (" EXP: #e#b" + (int)itemExp + "#k#n / " + ExpTable.getEquipExpNeededForLevel(itemLevel));
return "'" + eqpName + "' -> LV: #e#b" + itemLevel + "#k#n " + eqpInfo + "\r\n"; return "'" + eqpName + "' -> LV: #e#b" + itemLevel + "#k#n " + eqpInfo + "\r\n";
} }
+4 -2
View File
@@ -25,10 +25,12 @@ import constants.ItemConstants;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.concurrent.atomic.AtomicInteger;
public class Item implements Comparable<Item> { public class Item implements Comparable<Item> {
private static AtomicInteger runningCashId = new AtomicInteger(0);
private int id, cashId, sn; private int id, cashId, sn;
private short position; private short position;
private short quantity; private short quantity;
@@ -81,7 +83,7 @@ public class Item implements Comparable<Item> {
public int getCashId() { public int getCashId() {
if (cashId == 0) { if (cashId == 0) {
cashId = new Random().nextInt(Integer.MAX_VALUE) + 1; cashId = runningCashId.incrementAndGet();
} }
return cashId; return cashId;
} }
+57
View File
@@ -18,6 +18,7 @@ import server.quest.MapleQuest;
public class GameConstants { public class GameConstants {
public static String[] WORLD_NAMES = {"Scania", "Bera", "Broa", "Windia", "Khaini", "Bellocan", "Mardia", "Kradia", "Yellonde", "Demethos", "Galicia", "El Nido", "Zenith", "Arcenia", "Kastia", "Judis", "Plana", "Kalluna", "Stius", "Croa", "Medere"}; public static String[] WORLD_NAMES = {"Scania", "Bera", "Broa", "Windia", "Khaini", "Bellocan", "Mardia", "Kradia", "Yellonde", "Demethos", "Galicia", "El Nido", "Zenith", "Arcenia", "Kastia", "Judis", "Plana", "Kalluna", "Stius", "Croa", "Medere"};
public static final int[] OWL_DATA = new int[]{1082002, 2070005, 2070006, 1022047, 1102041, 2044705, 2340000, 2040017, 1092030, 2040804}; public static final int[] OWL_DATA = new int[]{1082002, 2070005, 2070006, 1022047, 1102041, 2044705, 2340000, 2040017, 1092030, 2040804};
public static final int[] CASH_DATA = new int[]{50200004, 50200069, 50200117, 50100008, 50000047};
// Ronan's rates upgrade system // Ronan's rates upgrade system
private static final int[] DROP_RATE_GAIN = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; private static final int[] DROP_RATE_GAIN = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
@@ -40,6 +41,62 @@ public class GameConstants {
return(EXP_RATE_GAIN[slot]); return(EXP_RATE_GAIN[slot]);
} }
// used by the "goto" command
public static final HashMap<String, Integer> GOTO_MAPS = new HashMap<String, Integer>() {{
put("gmmap", 180000000);
put("southperry", 60000);
put("amherst", 1000000);
put("henesys", 100000000);
put("ellinia", 101000000);
put("perion", 102000000);
put("kerning", 103000000);
put("lith", 104000000);
put("sleepywood", 105040300);
put("florina", 110000000);
put("nautilus", 120000000);
put("ereve", 130000000);
put("rien", 140000000);
put("orbis", 200000000);
put("happy", 209000000);
put("elnath", 211000000);
put("ludi", 220000000);
put("aqua", 230000000);
put("leafre", 240000000);
put("mulung", 250000000);
put("herb", 251000000);
put("omega", 221000000);
put("korean", 222000000);
put("ellin", 300000000);
put("nlc", 600000000);
put("excavation", 990000000);
put("pianus", 230040420);
put("horntail", 240060200);
put("mushmom", 100000005);
put("griffey", 240020101);
put("manon", 240020401);
put("horseman", 682000001);
put("balrog", 105090900);
put("zakum", 211042300);
put("papu", 220080001);
put("showa", 801000000);
put("guild", 200000301);
put("shrine", 800000000);
put("skelegon", 240040511);
put("hpq", 100000200);
put("ht", 240050400);
put("ariant", 260000000);
put("magatia", 261000000);
put("singapore", 540000000);
put("quay", 541000000);
put("kampung", 551000000);
put("keep", 610020006);
put("amoria", 680000000);
put("temple", 270000100);
put("square", 103040000);
put("neo", 240070000);
put("fm", 910000000);
}};
// MapleStory default keyset // MapleStory default keyset
private static final int[] DEFAULT_KEY = {18, 65, 2, 23, 3, 4, 5, 6, 16, 17, 19, 25, 26, 27, 31, 34, 35, 37, 38, 40, 43, 44, 45, 46, 50, 56, 59, 60, 61, 62, 63, 64, 57, 48, 29, 7, 24, 33, 41, 39}; private static final int[] DEFAULT_KEY = {18, 65, 2, 23, 3, 4, 5, 6, 16, 17, 19, 25, 26, 27, 31, 34, 35, 37, 38, 40, 43, 44, 45, 46, 50, 56, 59, 60, 61, 62, 63, 64, 57, 48, 29, 7, 24, 33, 41, 39};
private static final int[] DEFAULT_TYPE = {4, 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 4, 4, 5, 6, 6, 6, 6, 6, 6, 5, 4, 5, 4, 4, 4, 4, 4}; private static final int[] DEFAULT_TYPE = {4, 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 4, 4, 5, 6, 6, 6, 6, 6, 6, 5, 4, 5, 4, 4, 4, 4, 4};
+16 -5
View File
@@ -38,7 +38,7 @@ public class ServerConstants {
//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. //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 //Multiclient Coordinator Configuration
public static final int MAX_ALLOWED_ACCOUNT_IP = 4; //Allows up to N concurrent IP's for an account. IP's remains linked to an account longer the more times it's used to login. 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 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. public static final int LOGIN_ATTEMPT_DURATION = 120; //Period in seconds the login attempt remains registered on the system.
@@ -77,7 +77,7 @@ public class ServerConstants {
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_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_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_LEVEL_RANGE = false;//Caps the player level on the minimum required to advance their current jobs.
public static final boolean USE_ENFORCE_OWL_SUGGESTIONS = false;//Forces the Owl of Minerva to always display the defined item array on GameConstants.OWL_DATA instead of those featured by the players. 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. public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = true;//Forces players to not sell CASH items via merchants.
public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues) public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues)
public static final boolean USE_ENFORCE_MDOOR_POSITION = false; //Forces mystic door to be spawned near spawnpoints. public static final boolean USE_ENFORCE_MDOOR_POSITION = false; //Forces mystic door to be spawned near spawnpoints.
@@ -87,11 +87,15 @@ public class ServerConstants {
public static final boolean USE_ERASE_UNTRADEABLE_DROP = true; //Forces flagged untradeable items to disappear when dropped. 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_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_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_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_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.
//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_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_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_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_RECALL_EVENT = true; //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 //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_SHOPITEMSOLD = false; //Automatic message sent to owner when an item from the Player Shop or Hired Merchant is sold.
@@ -178,12 +182,19 @@ public class ServerConstants {
//Quest Configuration //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. 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.
public static final int FAME_GAIN_MIN_HOUR_INTERVAL = 24; //Minimum time interval in hours the repeatable quest must have to be accounted for the "fame gain by quest".
public static final int FAME_GAIN_BY_QUEST = 4; //Fame gain each N quest completes, set 0 to disable. //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 //Guild Configuration
public static final int CREATE_GUILD_COST = 1500000; public static final int CREATE_GUILD_COST = 1500000;
public static final int CHANGE_EMBLEM_COST = 5000000; 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;
//Equipment Configuration //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_SLOTS = true;//Equips can upgrade slots at level up.
+2
View File
@@ -59,6 +59,7 @@ import net.server.guild.MapleGuild;
import net.server.guild.MapleGuildCharacter; import net.server.guild.MapleGuildCharacter;
import net.server.worker.CharacterDiseaseWorker; import net.server.worker.CharacterDiseaseWorker;
import net.server.worker.CouponWorker; import net.server.worker.CouponWorker;
import net.server.worker.EventRecallCoordinatorWorker;
import net.server.worker.LoginCoordinatorWorker; import net.server.worker.LoginCoordinatorWorker;
import net.server.worker.LoginStorageWorker; import net.server.worker.LoginStorageWorker;
import net.server.worker.RankingCommandWorker; import net.server.worker.RankingCommandWorker;
@@ -841,6 +842,7 @@ public class Server {
tMan.register(new RankingCommandWorker(), 5 * 60 * 1000, 5 * 60 * 1000); tMan.register(new RankingCommandWorker(), 5 * 60 * 1000, 5 * 60 * 1000);
tMan.register(new RankingLoginWorker(), ServerConstants.RANKING_INTERVAL, timeLeft); tMan.register(new RankingLoginWorker(), ServerConstants.RANKING_INTERVAL, timeLeft);
tMan.register(new LoginCoordinatorWorker(), 60 * 60 * 1000, timeLeft); tMan.register(new LoginCoordinatorWorker(), 60 * 60 * 1000, timeLeft);
tMan.register(new EventRecallCoordinatorWorker(), 60 * 60 * 1000, timeLeft);
tMan.register(new LoginStorageWorker(), 2 * 60 * 1000, 2 * 60 * 1000); tMan.register(new LoginStorageWorker(), 2 * 60 * 1000, 2 * 60 * 1000);
long timeToTake = System.currentTimeMillis(); long timeToTake = System.currentTimeMillis();
@@ -70,6 +70,7 @@ public enum MonitoredLockType {
WORLD_PSHOPS, WORLD_PSHOPS,
WORLD_MERCHS, WORLD_MERCHS,
WORLD_MAPOBJS, WORLD_MAPOBJS,
WORLD_SUGGEST,
EIM, EIM,
EIM_PARTY, EIM_PARTY,
EIM_SCRIPT, EIM_SCRIPT,
@@ -53,14 +53,14 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.enableActions()); c.announce(MaplePacketCreator.enableActions());
return; return;
} }
final int action = slea.readByte();
final int action = slea.readByte();
if (action == 0x03 || action == 0x1E) { if (action == 0x03 || action == 0x1E) {
slea.readByte(); slea.readByte();
final int useNX = slea.readInt(); final int useNX = slea.readInt();
final int snCS = slea.readInt(); final int snCS = slea.readInt();
CashItem cItem = CashItemFactory.getItem(snCS); CashItem cItem = CashItemFactory.getItem(snCS);
if (cItem == null || !cItem.isOnSale() || cs.getCash(useNX) < cItem.getPrice()) { if (!canBuy(cItem, cs.getCash(useNX))) {
FilePrinter.printError(FilePrinter.ITEM, "Denied to sell cash item with SN " + cItem.getSN()); FilePrinter.printError(FilePrinter.ITEM, "Denied to sell cash item with SN " + cItem.getSN());
c.announce(MaplePacketCreator.enableActions()); c.announce(MaplePacketCreator.enableActions());
return; return;
@@ -87,7 +87,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
} }
c.announce(MaplePacketCreator.showBoughtCashPackage(cashPackage, c.getAccID())); c.announce(MaplePacketCreator.showBoughtCashPackage(cashPackage, c.getAccID()));
} }
cs.gainCash(useNX, -cItem.getPrice()); cs.gainCash(useNX, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr)); c.announce(MaplePacketCreator.showCash(chr));
} else if (action == 0x04) {//TODO check for gender } else if (action == 0x04) {//TODO check for gender
int birthday = slea.readInt(); int birthday = slea.readInt();
@@ -113,7 +113,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
} }
cs.gift(Integer.parseInt(recipient.get("id")), chr.getName(), message, cItem.getSN()); cs.gift(Integer.parseInt(recipient.get("id")), chr.getName(), message, cItem.getSN());
c.announce(MaplePacketCreator.showGiftSucceed(recipient.get("name"), cItem)); c.announce(MaplePacketCreator.showGiftSucceed(recipient.get("name"), cItem));
cs.gainCash(4, -cItem.getPrice()); cs.gainCash(4, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr)); c.announce(MaplePacketCreator.showCash(chr));
try { try {
chr.sendNote(recipient.get("name"), chr.getName() + " has sent you a gift! Go check out the Cash Shop.", (byte) 0); //fame or not chr.sendNote(recipient.get("name"), chr.getName() + " has sent you a gift! Go check out the Cash Shop.", (byte) 0); //fame or not
@@ -156,7 +156,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
} }
if (chr.gainSlots(type, 8, false)) { if (chr.gainSlots(type, 8, false)) {
c.announce(MaplePacketCreator.showBoughtInventorySlots(type, chr.getSlots(type))); c.announce(MaplePacketCreator.showBoughtInventorySlots(type, chr.getSlots(type)));
cs.gainCash(cash, -cItem.getPrice()); cs.gainCash(cash, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr)); c.announce(MaplePacketCreator.showCash(chr));
} }
} }
@@ -186,7 +186,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
} }
if (chr.getStorage().gainSlots(8)) { if (chr.getStorage().gainSlots(8)) {
c.announce(MaplePacketCreator.showBoughtStorageSlots(chr.getStorage().getSlots())); c.announce(MaplePacketCreator.showBoughtStorageSlots(chr.getStorage().getSlots()));
cs.gainCash(cash, -cItem.getPrice()); cs.gainCash(cash, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr)); c.announce(MaplePacketCreator.showCash(chr));
} }
} }
@@ -202,7 +202,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
if (c.gainCharacterSlot()) { if (c.gainCharacterSlot()) {
c.announce(MaplePacketCreator.showBoughtCharacterSlot(c.getCharacterSlots())); c.announce(MaplePacketCreator.showBoughtCharacterSlot(c.getCharacterSlots()));
cs.gainCash(cash, -cItem.getPrice()); cs.gainCash(cash, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr)); c.announce(MaplePacketCreator.showCash(chr));
} else { } else {
chr.dropMessage(1, "You have already used up all 12 extra character slots."); chr.dropMessage(1, "You have already used up all 12 extra character slots.");
@@ -276,7 +276,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
cs.addToInventory(eqp); cs.addToInventory(eqp);
c.announce(MaplePacketCreator.showBoughtCashItem(eqp, c.getAccID())); c.announce(MaplePacketCreator.showBoughtCashItem(eqp, c.getAccID()));
cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), (ringid + 1)); cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), (ringid + 1));
cs.gainCash(toCharge, -itemRing.getPrice()); cs.gainCash(toCharge, itemRing, chr.getWorld());
chr.addCrushRing(MapleRing.loadFromDb(ringid)); chr.addCrushRing(MapleRing.loadFromDb(ringid));
try { try {
chr.sendNote(partner.getName(), text, (byte) 1); chr.sendNote(partner.getName(), text, (byte) 1);
@@ -357,7 +357,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
return c.checkBirthDate(cal); return c.checkBirthDate(cal);
} }
public static boolean canBuy(CashItem item, int cash) { private static boolean canBuy(CashItem item, int cash) {
return item != null && item.isOnSale() && item.getPrice() <= cash; return item != null && item.isOnSale() && item.getPrice() <= cash;
} }
} }
@@ -47,17 +47,16 @@ public final class GiveFameHandler extends AbstractMaplePacketHandler {
c.disconnect(true, false); c.disconnect(true, false);
return; return;
} }
FameStatus status = player.canGiveFame(target); FameStatus status = player.canGiveFame(target);
if (status == FameStatus.OK || player.isGM()){ if (status == FameStatus.OK) {
if (Math.abs(target.getFame() + famechange) < 30001) { if (target.gainFame(famechange, player, mode)) {
target.addFame(famechange);
target.updateSingleStat(MapleStat.FAME, target.getFame());
}
if (!player.isGM()) { if (!player.isGM()) {
player.hasGivenFame(target); player.hasGivenFame(target);
} }
c.announce(MaplePacketCreator.giveFameResponse(mode, target.getName(), target.getFame())); } else {
target.getClient().announce(MaplePacketCreator.receiveFame(mode, player.getName())); player.message("Could not process the request, since this character currently has the minimum/maximum level of fame.");
}
} else { } else {
c.announce(MaplePacketCreator.giveFameErrorResponse(status == FameStatus.NOT_TODAY ? 3 : 4)); c.announce(MaplePacketCreator.giveFameErrorResponse(status == FameStatus.NOT_TODAY ? 3 : 4));
} }
@@ -77,7 +77,6 @@ public final class NoteActionHandler extends AbstractMaplePacketHandler {
} }
if (fame > 0) { if (fame > 0) {
c.getPlayer().gainFame(fame); c.getPlayer().gainFame(fame);
c.announce(MaplePacketCreator.getShowFameGain(fame));
} }
} }
} }
@@ -62,7 +62,11 @@ import java.net.InetSocketAddress;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Map; import java.util.Map;
import net.server.coordinator.MapleEventRecallCoordinator;
import net.server.coordinator.MapleSessionCoordinator;
import org.apache.mina.core.session.IoSession;
import server.life.MobSkill; import server.life.MobSkill;
import scripting.event.EventInstanceManager;
import tools.packets.Wedding; import tools.packets.Wedding;
public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
@@ -79,11 +83,23 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
MapleCharacter player = c.getWorldServer().getPlayerStorage().getCharacterById(cid); MapleCharacter player = c.getWorldServer().getPlayerStorage().getCharacterById(cid);
boolean newcomer = false; boolean newcomer = false;
if (player == null) { if (player == null) {
if(!server.validateCharacteridInTransition((InetSocketAddress) c.getSession().getRemoteAddress(), cid)) { IoSession session = c.getSession();
if (!server.validateCharacteridInTransition((InetSocketAddress) session.getRemoteAddress(), cid)) {
c.disconnect(true, false); c.disconnect(true, false);
return; return;
} }
if (ServerConstants.DETERRED_MULTICLIENT) {
String remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(session);
if (remoteHwid == null) {
c.disconnect(true, false);
return;
}
session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid);
}
try { try {
player = MapleCharacter.loadCharFromDB(cid, c, true); player = MapleCharacter.loadCharFromDB(cid, c, true);
newcomer = true; newcomer = true;
@@ -332,6 +348,13 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
partner.announce(Wedding.OnNotifyWeddingPartnerTransfer(player.getId(), player.getMapId())); partner.announce(Wedding.OnNotifyWeddingPartnerTransfer(player.getId(), player.getMapId()));
} }
} }
if (newcomer) {
EventInstanceManager eim = MapleEventRecallCoordinator.getInstance().recallEventInstance(cid);
if (eim != null) {
eim.registerPlayer(player);
}
}
} }
private static void showDueyNotification(MapleClient c, MapleCharacter player) { private static void showDueyNotification(MapleClient c, MapleCharacter player) {
@@ -117,8 +117,8 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler {
int gain = lose * (ef.getY() / 100); int gain = lose * (ef.getY() / 100);
chr.setMp(chr.getMp() + gain); chr.setMp(chr.getMp() + gain);
chr.updateSingleStat(MapleStat.MP, chr.getMp()); chr.updateSingleStat(MapleStat.MP, chr.getMp());
} else if (skillid == Priest.DISPEL || skillid == SuperGM.HEAL_PLUS_DISPEL) { } else if (skillid == SuperGM.HEAL_PLUS_DISPEL) {
slea.skip((skillid == Priest.DISPEL) ? 10 : 11); slea.skip(11);
chr.getMap().broadcastMessage(chr, MaplePacketCreator.showBuffeffect(chr.getId(), skillid, chr.getSkillLevel(skillid)), false); chr.getMap().broadcastMessage(chr, MaplePacketCreator.showBuffeffect(chr.getId(), skillid, chr.getSkillLevel(skillid)), false);
} else if (skillid % 10000000 == 1004) { } else if (skillid % 10000000 == 1004) {
slea.readShort(); slea.readShort();
@@ -352,7 +352,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
} else if (itemType == 523) { } else if (itemType == 523) {
int itemid = slea.readInt(); int itemid = slea.readInt();
if(!ServerConstants.USE_ENFORCE_OWL_SUGGESTIONS) c.getWorldServer().addOwlItemSearch(itemid); if(!ServerConstants.USE_ENFORCE_ITEM_SUGGESTION) c.getWorldServer().addOwlItemSearch(itemid);
player.setOwlSearch(itemid); player.setOwlSearch(itemid);
List<Pair<MaplePlayerShopItem, AbstractMapleMapObject>> hmsAvailable = c.getWorldServer().getAvailableItemBundles(itemid); List<Pair<MaplePlayerShopItem, AbstractMapleMapObject>> hmsAvailable = c.getWorldServer().getAvailableItemBundles(itemid);
if(!hmsAvailable.isEmpty()) remove(c, itemId); if(!hmsAvailable.isEmpty()) remove(c, itemId);
@@ -56,7 +56,7 @@ public final class UseOwlOfMinervaHandler extends AbstractMaplePacketHandler {
} }
}; };
PriorityQueue<Pair<Integer, Integer>> queue = new PriorityQueue<>(10, comparator); PriorityQueue<Pair<Integer, Integer>> queue = new PriorityQueue<>(Math.max(1, owlSearched.size()), comparator);
for(Pair<Integer, Integer> p : owlSearched) { for(Pair<Integer, Integer> p : owlSearched) {
queue.add(p); queue.add(p);
} }
+1 -1
View File
@@ -34,7 +34,7 @@ import net.server.Server;
*/ */
public class LoginStorage { public class LoginStorage {
ConcurrentHashMap<Integer, List<Long>> loginHistory = new ConcurrentHashMap<>(); private ConcurrentHashMap<Integer, List<Long>> loginHistory = new ConcurrentHashMap<>();
public boolean registerLogin(int accountId) { public boolean registerLogin(int accountId) {
List<Long> accHist = loginHistory.putIfAbsent(accountId, new LinkedList<Long>()); List<Long> accHist = loginHistory.putIfAbsent(accountId, new LinkedList<Long>());
@@ -0,0 +1,73 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.coordinator;
import constants.ServerConstants;
import scripting.event.EventInstanceManager;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* @author Ronan
*/
public class MapleEventRecallCoordinator {
private final static MapleEventRecallCoordinator instance = new MapleEventRecallCoordinator();
public static MapleEventRecallCoordinator getInstance() {
return instance;
}
private ConcurrentHashMap<Integer, EventInstanceManager> eventHistory = new ConcurrentHashMap<>();
private static boolean isRecallableEvent(EventInstanceManager eim) {
return eim != null && !eim.isEventDisposed() && !eim.isEventCleared();
}
public EventInstanceManager recallEventInstance(int characterId) {
EventInstanceManager eim = eventHistory.remove(characterId);
return isRecallableEvent(eim) ? eim : null;
}
public void storeEventInstance(int characterId, EventInstanceManager eim) {
if (ServerConstants.USE_ENABLE_RECALL_EVENT && isRecallableEvent(eim)) {
eventHistory.put(characterId, eim);
}
}
public void manageEventInstances() {
if (!eventHistory.isEmpty()) {
List<Integer> toRemove = new LinkedList<>();
for (Entry<Integer, EventInstanceManager> eh : eventHistory.entrySet()) {
if (!isRecallableEvent(eh.getValue())) {
toRemove.add(eh.getKey());
}
}
for (Integer r : toRemove) {
eventHistory.remove(r);
}
}
}
}
@@ -19,6 +19,7 @@
*/ */
package net.server.coordinator; package net.server.coordinator;
import client.MapleClient;
import constants.ServerConstants; import constants.ServerConstants;
import net.server.Server; import net.server.Server;
@@ -33,11 +34,14 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@@ -58,14 +62,18 @@ public class MapleSessionCoordinator {
REMOTE_LOGGEDIN, REMOTE_LOGGEDIN,
REMOTE_REACHED_LIMIT, REMOTE_REACHED_LIMIT,
REMOTE_PROCESSING, REMOTE_PROCESSING,
REMOTE_NO_MATCH,
MANY_ACCOUNT_ATTEMPTS, MANY_ACCOUNT_ATTEMPTS,
COORDINATOR_ERROR COORDINATOR_ERROR
} }
private final LoginStorage loginStorage = new LoginStorage(); private final LoginStorage loginStorage = new LoginStorage();
private final Set<String> onlineRemoteHosts = new HashSet<>(); private final Set<String> onlineRemoteHwids = new HashSet<>();
private final Map<String, Set<IoSession>> loginRemoteHosts = new HashMap<>(); private final Map<String, Set<IoSession>> loginRemoteHosts = new HashMap<>();
private final Set<String> pooledRemoteHosts = new HashSet<>(); private final Set<String> pooledRemoteHosts = new HashSet<>();
private final ConcurrentHashMap<String, String> cachedHostHwids = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Long> cachedHostTimeout = new ConcurrentHashMap<>();
private final List<ReentrantLock> poolLock = new ArrayList<>(100); private final List<ReentrantLock> poolLock = new ArrayList<>(100);
private MapleSessionCoordinator() { private MapleSessionCoordinator() {
@@ -74,7 +82,7 @@ public class MapleSessionCoordinator {
} }
} }
private static long ipExpirationUpdate(int relevance) { private static long hwidExpirationUpdate(int relevance) {
int degree = 1, i = relevance, subdegree; int degree = 1, i = relevance, subdegree;
while ((subdegree = 5 * degree) <= i) { while ((subdegree = 5 * degree) <= i) {
i -= subdegree; i -= subdegree;
@@ -109,59 +117,90 @@ public class MapleSessionCoordinator {
return 3600000 * (baseTime + subdegreeTime); return 3600000 * (baseTime + subdegreeTime);
} }
private static void updateAccessAccount(Connection con, String remoteHost, int accountId, int loginRelevance) throws SQLException { private static void updateAccessAccount(Connection con, String remoteHwid, int accountId, int loginRelevance) throws SQLException {
java.sql.Timestamp nextTimestamp = new java.sql.Timestamp(Server.getInstance().getCurrentTime() + ipExpirationUpdate(loginRelevance)); java.sql.Timestamp nextTimestamp = new java.sql.Timestamp(Server.getInstance().getCurrentTime() + hwidExpirationUpdate(loginRelevance));
if(loginRelevance < Byte.MAX_VALUE) { if(loginRelevance < Byte.MAX_VALUE) {
loginRelevance++; loginRelevance++;
} }
try (PreparedStatement ps = con.prepareStatement("UPDATE ipaccounts SET relevance = ?, expiresat = ? WHERE accountid = ? AND ip LIKE ?")) { try (PreparedStatement ps = con.prepareStatement("UPDATE hwidaccounts SET relevance = ?, expiresat = ? WHERE accountid = ? AND hwid LIKE ?")) {
ps.setInt(1, loginRelevance); ps.setInt(1, loginRelevance);
ps.setTimestamp(2, nextTimestamp); ps.setTimestamp(2, nextTimestamp);
ps.setInt(3, accountId); ps.setInt(3, accountId);
ps.setString(4, remoteHost); ps.setString(4, remoteHwid);
ps.executeUpdate(); ps.executeUpdate();
} }
} }
private static void registerAccessAccount(Connection con, String remoteHost, int accountId) throws SQLException { private static void registerAccessAccount(Connection con, String remoteHwid, int accountId) throws SQLException {
try (PreparedStatement ps = con.prepareStatement("INSERT INTO ipaccounts (accountid, ip, expiresat) VALUES (?, ?, ?)")) { try (PreparedStatement ps = con.prepareStatement("INSERT INTO hwidaccounts (accountid, hwid, expiresat) VALUES (?, ?, ?)")) {
ps.setInt(1, accountId); ps.setInt(1, accountId);
ps.setString(2, remoteHost); ps.setString(2, remoteHwid);
ps.setTimestamp(3, new java.sql.Timestamp(Server.getInstance().getCurrentTime() + ipExpirationUpdate(0))); ps.setTimestamp(3, new java.sql.Timestamp(Server.getInstance().getCurrentTime() + hwidExpirationUpdate(0)));
ps.executeUpdate(); ps.executeUpdate();
} }
} }
private static boolean attemptAccessAccount(String remoteHost, int accountId, boolean routineCheck) { private static boolean associateHwidAccountIfAbsent(String remoteHwid, int accountId) {
try { try {
Connection con = DatabaseConnection.getConnection(); Connection con = DatabaseConnection.getConnection();
int ipCount = 0; int hwidCount = 0;
try (PreparedStatement ps = con.prepareStatement("SELECT SQL_CACHE * FROM ipaccounts WHERE accountid = ?")) { try (PreparedStatement ps = con.prepareStatement("SELECT SQL_CACHE hwid FROM hwidaccounts WHERE accountid = ?")) {
ps.setInt(1, accountId); ps.setInt(1, accountId);
try (ResultSet rs = ps.executeQuery()) { try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) { while (rs.next()) {
if (remoteHost.contentEquals(rs.getString("ip"))) { String rsHwid = rs.getString("hwid");
if (rsHwid.contentEquals(remoteHwid)) {
return false;
}
hwidCount++;
}
}
if (hwidCount < ServerConstants.MAX_ALLOWED_ACCOUNT_HWID) {
registerAccessAccount(con, remoteHwid, accountId);
return true;
}
} finally {
con.close();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
return false;
}
private static boolean attemptAccessAccount(String nibbleHwid, int accountId, boolean routineCheck) {
try {
Connection con = DatabaseConnection.getConnection();
int hwidCount = 0;
try (PreparedStatement ps = con.prepareStatement("SELECT SQL_CACHE * FROM hwidaccounts WHERE accountid = ?")) {
ps.setInt(1, accountId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
String rsHwid = rs.getString("hwid");
if (rsHwid.endsWith(nibbleHwid)) {
if (!routineCheck) { if (!routineCheck) {
// better update HWID relevance as soon as the login is authenticated
int loginRelevance = rs.getInt("relevance"); int loginRelevance = rs.getInt("relevance");
updateAccessAccount(con, remoteHost, accountId, loginRelevance); updateAccessAccount(con, rsHwid, accountId, loginRelevance);
} }
return true; return true;
} }
ipCount++; hwidCount++;
} }
} }
if (ipCount < ServerConstants.MAX_ALLOWED_ACCOUNT_IP) { if (hwidCount < ServerConstants.MAX_ALLOWED_ACCOUNT_HWID) {
if (!routineCheck) {
registerAccessAccount(con, remoteHost, accountId);
}
return true; return true;
} }
} finally { } finally {
@@ -218,7 +257,14 @@ public class MapleSessionCoordinator {
} }
try { try {
if (onlineRemoteHosts.contains(remoteHost) || loginRemoteHosts.containsKey(remoteHost)) { String knownHwid = cachedHostHwids.get(remoteHost);
if (knownHwid != null) {
if (onlineRemoteHwids.contains(knownHwid)) {
return false;
}
}
if (loginRemoteHosts.containsKey(remoteHost)) {
return false; return false;
} }
@@ -244,11 +290,16 @@ public class MapleSessionCoordinator {
lrh.remove(session); lrh.remove(session);
if (lrh.isEmpty()) { if (lrh.isEmpty()) {
loginRemoteHosts.remove(remoteIp); loginRemoteHosts.remove(remoteIp);
String nibbleHwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID);
if (nibbleHwid != null) {
onlineRemoteHwids.remove(nibbleHwid);
}
} }
} }
} }
public AntiMulticlientResult attemptSessionLogin(IoSession session, int accountId, boolean routineCheck) { public AntiMulticlientResult attemptLoginSession(IoSession session, String nibbleHwid, int accountId, boolean routineCheck) {
if (!ServerConstants.DETERRED_MULTICLIENT) return AntiMulticlientResult.SUCCESS; if (!ServerConstants.DETERRED_MULTICLIENT) return AntiMulticlientResult.SUCCESS;
String remoteHost = getRemoteIp(session); String remoteHost = getRemoteIp(session);
@@ -289,17 +340,18 @@ public class MapleSessionCoordinator {
} }
if (!routineCheck) { if (!routineCheck) {
if (onlineRemoteHosts.contains(remoteHost)) { if (onlineRemoteHwids.contains(nibbleHwid)) {
return AntiMulticlientResult.REMOTE_LOGGEDIN; return AntiMulticlientResult.REMOTE_LOGGEDIN;
} }
if (!attemptAccessAccount(remoteHost, accountId, routineCheck)) { if (!attemptAccessAccount(nibbleHwid, accountId, routineCheck)) {
return AntiMulticlientResult.REMOTE_REACHED_LIMIT; return AntiMulticlientResult.REMOTE_REACHED_LIMIT;
} }
onlineRemoteHosts.add(remoteHost); session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, nibbleHwid);
onlineRemoteHwids.add(nibbleHwid);
} else { } else {
if (!attemptAccessAccount(remoteHost, accountId, routineCheck)) { if (!attemptAccessAccount(nibbleHwid, accountId, routineCheck)) {
return AntiMulticlientResult.REMOTE_REACHED_LIMIT; return AntiMulticlientResult.REMOTE_REACHED_LIMIT;
} }
} }
@@ -315,15 +367,99 @@ public class MapleSessionCoordinator {
} }
} }
public void closeSession(IoSession session, Boolean immediately) { public AntiMulticlientResult attemptGameSession(IoSession session, int accountId, String remoteHwid) {
onlineRemoteHosts.remove(getRemoteIp(session)); if (!ServerConstants.DETERRED_MULTICLIENT) return AntiMulticlientResult.SUCCESS;
if (immediately != null) session.close(immediately);
String remoteHost = getRemoteIp(session);
Lock lock = getCoodinatorLock(remoteHost);
try {
int tries = 0;
while (true) {
if (lock.tryLock()) {
try {
if (pooledRemoteHosts.contains(remoteHost)) {
return AntiMulticlientResult.REMOTE_PROCESSING;
} }
public void runUpdateIpHistory() { pooledRemoteHosts.add(remoteHost);
} finally {
lock.unlock();
}
break;
} else {
if(tries == 2) {
return AntiMulticlientResult.COORDINATOR_ERROR;
}
tries++;
Thread.sleep(1777);
}
}
} catch (Exception e) {
e.printStackTrace();
return AntiMulticlientResult.COORDINATOR_ERROR;
}
try {
String nibbleHwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID);
if (nibbleHwid != null) {
onlineRemoteHwids.remove(nibbleHwid);
if (remoteHwid.endsWith(nibbleHwid)) {
if (!onlineRemoteHwids.contains(remoteHwid)) {
// assumption: after a SUCCESSFUL login attempt, the incoming client WILL receive a new IoSession from the game server
// updated session CLIENT_HWID attribute will be set when the player log in the game
onlineRemoteHwids.add(remoteHwid);
cachedHostHwids.put(remoteHost, remoteHwid);
cachedHostTimeout.put(remoteHost, Server.getInstance().getCurrentTime() + 604800000); // 1 week-time entry
associateHwidAccountIfAbsent(remoteHwid, accountId);
return AntiMulticlientResult.SUCCESS;
} else {
return AntiMulticlientResult.REMOTE_LOGGEDIN;
}
} else {
return AntiMulticlientResult.REMOTE_NO_MATCH;
}
} else {
return AntiMulticlientResult.REMOTE_NO_MATCH;
}
} finally {
lock.lock();
try {
pooledRemoteHosts.remove(remoteHost);
} finally {
lock.unlock();
}
}
}
public void closeSession(IoSession session, Boolean immediately) {
String hwid = (String) session.removeAttribute(MapleClient.CLIENT_HWID);
onlineRemoteHwids.remove(hwid);
hwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); // making sure to clean up calls to this function on login phase
onlineRemoteHwids.remove(hwid);
if (immediately != null) {
session.close(immediately);
}
}
public String getGameSessionHwid(IoSession session) {
String remoteHost = getRemoteIp(session);
return cachedHostHwids.get(remoteHost);
}
public void runUpdateHwidHistory() {
try { try {
Connection con = DatabaseConnection.getConnection(); Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("DELETE FROM ipaccounts WHERE expiresat < CURRENT_TIMESTAMP")) { try (PreparedStatement ps = con.prepareStatement("DELETE FROM hwidaccounts WHERE expiresat < CURRENT_TIMESTAMP")) {
ps.execute(); ps.execute();
} finally { } finally {
con.close(); con.close();
@@ -331,6 +467,21 @@ public class MapleSessionCoordinator {
} catch (SQLException ex) { } catch (SQLException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
long timeNow = Server.getInstance().getCurrentTime();
List<String> toRemove = new LinkedList<>();
for (Entry<String, Long> cht : cachedHostTimeout.entrySet()) {
if (cht.getValue() < timeNow) {
toRemove.add(cht.getKey());
}
}
if (!toRemove.isEmpty()) {
for (String s : toRemove) {
cachedHostHwids.remove(s);
cachedHostTimeout.remove(s);
}
}
} }
public void runUpdateLoginHistory() { public void runUpdateLoginHistory() {
+8 -1
View File
@@ -23,6 +23,7 @@ package net.server.guild;
import client.MapleCharacter; import client.MapleCharacter;
import client.MapleClient; import client.MapleClient;
import constants.ServerConstants;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@@ -752,6 +753,12 @@ public class MapleGuild {
} }
public static int getIncreaseGuildCost(int size) { public static int getIncreaseGuildCost(int size) {
return 500000 * (size - 6) / 6; int cost = ServerConstants.EXPAND_GUILD_BASE_COST + Math.max(0, (size - 15) / 5) * ServerConstants.EXPAND_GUILD_TIER_COST;
if (size > 30) {
return Math.min(ServerConstants.EXPAND_GUILD_MAX_COST, Math.max(cost, 5000000));
} else {
return cost;
}
} }
} }
@@ -27,28 +27,64 @@ import java.net.InetSocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import net.AbstractMaplePacketHandler; import net.AbstractMaplePacketHandler;
import net.server.Server; import net.server.Server;
import net.server.coordinator.MapleSessionCoordinator;
import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult;
import net.server.world.World; import net.server.world.World;
import org.apache.mina.core.session.IoSession;
import tools.MaplePacketCreator; import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor; import tools.data.input.SeekableLittleEndianAccessor;
public final class CharSelectedHandler extends AbstractMaplePacketHandler { public final class CharSelectedHandler extends AbstractMaplePacketHandler {
private static int parseAntiMulticlientError(AntiMulticlientResult res) {
switch (res) {
case REMOTE_PROCESSING:
return 10;
case REMOTE_LOGGEDIN:
return 7;
case REMOTE_NO_MATCH:
return 17;
case COORDINATOR_ERROR:
return 8;
default:
return 9;
}
}
@Override @Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
int charId = slea.readInt(); int charId = slea.readInt();
String macs = slea.readMapleAsciiString(); String macs = slea.readMapleAsciiString();
String hwid = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString();
if (!hwid.matches("[0-9A-F]{12}_[0-9A-F]{8}")) {
c.announce(MaplePacketCreator.getAfterLoginError(17));
return;
}
c.updateMacs(macs); c.updateMacs(macs);
c.updateHWID(hwid); c.updateHWID(hwid);
IoSession session = c.getSession();
AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid);
if (res != AntiMulticlientResult.SUCCESS) {
c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res)));
return;
}
if (c.hasBannedMac() || c.hasBannedHWID()) { if (c.hasBannedMac() || c.hasBannedHWID()) {
c.getSession().close(true); session.close(true);
return; return;
} }
Server server = Server.getInstance(); Server server = Server.getInstance();
if(!server.haveCharacterEntry(c.getAccID(), charId)) { if(!server.haveCharacterEntry(c.getAccID(), charId)) {
c.getSession().close(true); session.close(true);
return; return;
} }
@@ -67,7 +103,7 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler {
server.unregisterLoginState(c); server.unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
server.setCharacteridInTransition((InetSocketAddress) c.getSession().getRemoteAddress(), charId); server.setCharacteridInTransition((InetSocketAddress) session.getRemoteAddress(), charId);
try { try {
c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId)); c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId));
@@ -6,13 +6,35 @@ import java.net.UnknownHostException;
import net.AbstractMaplePacketHandler; import net.AbstractMaplePacketHandler;
import net.server.Server; import net.server.Server;
import net.server.coordinator.MapleSessionCoordinator;
import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult;
import net.server.world.World; import net.server.world.World;
import org.apache.mina.core.session.IoSession;
import tools.MaplePacketCreator; import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor; import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleClient; import client.MapleClient;
public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler { public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler {
private static int parseAntiMulticlientError(AntiMulticlientResult res) {
switch (res) {
case REMOTE_PROCESSING:
return 10;
case REMOTE_LOGGEDIN:
return 7;
case REMOTE_NO_MATCH:
return 17;
case COORDINATOR_ERROR:
return 8;
default:
return 9;
}
}
@Override @Override
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
String pic = slea.readMapleAsciiString(); String pic = slea.readMapleAsciiString();
@@ -20,8 +42,22 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler {
String macs = slea.readMapleAsciiString(); String macs = slea.readMapleAsciiString();
String hwid = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString();
if (!hwid.matches("[0-9A-F]{12}_[0-9A-F]{8}")) {
c.announce(MaplePacketCreator.getAfterLoginError(17));
return;
}
c.updateMacs(macs); c.updateMacs(macs);
c.updateHWID(hwid); c.updateHWID(hwid);
IoSession session = c.getSession();
AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid);
if (res != AntiMulticlientResult.SUCCESS) {
c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res)));
return;
}
if (c.hasBannedMac() || c.hasBannedHWID()) { if (c.hasBannedMac() || c.hasBannedHWID()) {
c.getSession().close(true); c.getSession().close(true);
return; return;
@@ -31,6 +31,7 @@ import net.MaplePacketHandler;
import net.server.Server; import net.server.Server;
import tools.BCrypt; import tools.BCrypt;
import tools.DatabaseConnection; import tools.DatabaseConnection;
import tools.HexTool;
import tools.MaplePacketCreator; import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor; import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleClient; import client.MapleClient;
@@ -52,7 +53,9 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
String pwd = slea.readMapleAsciiString(); String pwd = slea.readMapleAsciiString();
c.setAccountName(login); c.setAccountName(login);
int loginok = c.login(login, pwd); slea.skip(6); // localhost masked the initial part with zeroes...
byte[] hwidNibbles = slea.read(4);
int loginok = c.login(login, pwd, HexTool.toCompressedString(hwidNibbles));
Connection con = null; Connection con = null;
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -63,8 +66,8 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
ps = con.prepareStatement("INSERT INTO accounts (name, password, birthday, tempban) VALUES (?, ?, ?, ?);", Statement.RETURN_GENERATED_KEYS); //Jayd: Added birthday, tempban ps = con.prepareStatement("INSERT INTO accounts (name, password, birthday, tempban) VALUES (?, ?, ?, ?);", Statement.RETURN_GENERATED_KEYS); //Jayd: Added birthday, tempban
ps.setString(1, login); ps.setString(1, login);
ps.setString(2, BCrypt.hashpw(pwd, BCrypt.gensalt(12))); ps.setString(2, BCrypt.hashpw(pwd, BCrypt.gensalt(12)));
ps.setString(3, "2018-06-20"); //Jayd: was added to solve the MySQL 5.7 strict checking (birthday) ps.setString(3, "2018-06-20"); //Jayd's idea: was added to solve the MySQL 5.7 strict checking (birthday)
ps.setString(4, "2018-06-20"); //Jayd: was added to solve the MySQL 5.7 strict checking (tempban) ps.setString(4, "2018-06-20"); //Jayd's idea: was added to solve the MySQL 5.7 strict checking (tempban)
ps.executeUpdate(); ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys(); ResultSet rs = ps.getGeneratedKeys();
@@ -75,7 +78,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
e.printStackTrace(); e.printStackTrace();
} finally { } finally {
disposeSql(con, ps); disposeSql(con, ps);
loginok = c.login(login, pwd); loginok = c.login(login, pwd, HexTool.toCompressedString(hwidNibbles));
} }
} }
@@ -100,7 +103,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
} }
Calendar tempban = c.getTempBanCalendar(); Calendar tempban = c.getTempBanCalendar();
if (tempban != null) { if (tempban != null) {
if (tempban.getTimeInMillis() > System.currentTimeMillis()) { if (tempban.getTimeInMillis() > Calendar.getInstance().getTimeInMillis()) {
c.announce(MaplePacketCreator.getTempBan(tempban.getTimeInMillis(), c.getGReason())); c.announce(MaplePacketCreator.getTempBan(tempban.getTimeInMillis(), c.getGReason()));
return; return;
} }
@@ -10,9 +10,31 @@ import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor; import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleClient; import client.MapleClient;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import net.server.coordinator.MapleSessionCoordinator;
import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult;
import org.apache.mina.core.session.IoSession;
public final class RegisterPicHandler extends AbstractMaplePacketHandler { public final class RegisterPicHandler extends AbstractMaplePacketHandler {
private static int parseAntiMulticlientError(AntiMulticlientResult res) {
switch (res) {
case REMOTE_PROCESSING:
return 10;
case REMOTE_LOGGEDIN:
return 7;
case REMOTE_NO_MATCH:
return 17;
case COORDINATOR_ERROR:
return 8;
default:
return 9;
}
}
@Override @Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.readByte(); slea.readByte();
@@ -20,8 +42,22 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler {
String macs = slea.readMapleAsciiString(); String macs = slea.readMapleAsciiString();
String hwid = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString();
if (!hwid.matches("[0-9A-F]{12}_[0-9A-F]{8}")) {
c.announce(MaplePacketCreator.getAfterLoginError(17));
return;
}
c.updateMacs(macs); c.updateMacs(macs);
c.updateHWID(hwid); c.updateHWID(hwid);
IoSession session = c.getSession();
AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid);
if (res != AntiMulticlientResult.SUCCESS) {
c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res)));
return;
}
if (c.hasBannedMac() || c.hasBannedHWID()) { if (c.hasBannedMac() || c.hasBannedHWID()) {
c.getSession().close(true); c.getSession().close(true);
return; return;
@@ -0,0 +1,34 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.worker;
import net.server.coordinator.MapleEventRecallCoordinator;
/**
*
* @author Ronan
*/
public class EventRecallCoordinatorWorker implements Runnable {
@Override
public void run() {
MapleEventRecallCoordinator.getInstance().manageEventInstances();
}
}
@@ -29,6 +29,6 @@ public class LoginCoordinatorWorker implements Runnable {
@Override @Override
public void run() { public void run() {
MapleSessionCoordinator.getInstance().runUpdateIpHistory(); MapleSessionCoordinator.getInstance().runUpdateHwidHistory();
} }
} }
+114 -5
View File
@@ -27,6 +27,7 @@ import client.BuddyList.BuddyOperation;
import client.BuddylistEntry; import client.BuddylistEntry;
import client.MapleCharacter; import client.MapleCharacter;
import client.MapleFamily; import client.MapleFamily;
import constants.GameConstants;
import constants.ServerConstants; import constants.ServerConstants;
import java.sql.Connection; import java.sql.Connection;
@@ -53,6 +54,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.Set; import java.util.Set;
import java.util.HashSet; import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import scripting.event.EventInstanceManager; import scripting.event.EventInstanceManager;
@@ -122,6 +124,11 @@ public class World {
private MonitoredReentrantLock partyLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_PARTY, true); private MonitoredReentrantLock partyLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_PARTY, true);
private Map<Integer, Integer> owlSearched = new LinkedHashMap<>(); private Map<Integer, Integer> owlSearched = new LinkedHashMap<>();
private List<Map<Integer, Integer>> cashItemBought = new ArrayList<>(9);
private final ReentrantReadWriteLock suggestLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_SUGGEST, true);
private ReadLock suggestRLock = suggestLock.readLock();
private WriteLock suggestWLock = suggestLock.writeLock();
private Map<Integer, Integer> disabledServerMessages = new HashMap<>(); // reuse owl lock private Map<Integer, Integer> disabledServerMessages = new HashMap<>(); // reuse owl lock
private MonitoredReentrantLock srvMessagesLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_SRVMESSAGES); private MonitoredReentrantLock srvMessagesLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_SRVMESSAGES);
private ScheduledFuture<?> srvMessagesSchedule; private ScheduledFuture<?> srvMessagesSchedule;
@@ -166,6 +173,10 @@ public class World {
petUpdate = System.currentTimeMillis(); petUpdate = System.currentTimeMillis();
mountUpdate = petUpdate; mountUpdate = petUpdate;
for (int i = 0; i < 9; i++) {
cashItemBought.add(new LinkedHashMap<Integer, Integer>());
}
TimerManager tman = TimerManager.getInstance(); TimerManager tman = TimerManager.getInstance();
petsSchedule = tman.register(new PetFullnessWorker(this), 60 * 1000, 60 * 1000); petsSchedule = tman.register(new PetFullnessWorker(this), 60 * 1000, 60 * 1000);
srvMessagesSchedule = tman.register(new ServerMessageWorker(this), 10 * 1000, 10 * 1000); srvMessagesSchedule = tman.register(new ServerMessageWorker(this), 10 * 1000, 10 * 1000);
@@ -1152,7 +1163,7 @@ public class World {
} }
public void addOwlItemSearch(Integer itemid) { public void addOwlItemSearch(Integer itemid) {
srvMessagesLock.lock(); suggestWLock.lock();
try { try {
Integer cur = owlSearched.get(itemid); Integer cur = owlSearched.get(itemid);
if(cur != null) { if(cur != null) {
@@ -1161,16 +1172,16 @@ public class World {
owlSearched.put(itemid, 1); owlSearched.put(itemid, 1);
} }
} finally { } finally {
srvMessagesLock.unlock(); suggestWLock.unlock();
} }
} }
public List<Pair<Integer, Integer>> getOwlSearchedItems() { public List<Pair<Integer, Integer>> getOwlSearchedItems() {
if(ServerConstants.USE_ENFORCE_OWL_SUGGESTIONS) { if(ServerConstants.USE_ENFORCE_ITEM_SUGGESTION) {
return new ArrayList<>(0); return new ArrayList<>(0);
} }
srvMessagesLock.lock(); suggestRLock.lock();
try { try {
List<Pair<Integer, Integer>> searchCounts = new ArrayList<>(owlSearched.size()); List<Pair<Integer, Integer>> searchCounts = new ArrayList<>(owlSearched.size());
@@ -1180,10 +1191,108 @@ public class World {
return searchCounts; return searchCounts;
} finally { } finally {
srvMessagesLock.unlock(); suggestRLock.unlock();
} }
} }
public void addCashItemBought(Integer snid) {
suggestWLock.lock();
try {
Map<Integer, Integer> tabItemBought = cashItemBought.get(snid / 10000000);
Integer cur = tabItemBought.get(snid);
if (cur != null) {
tabItemBought.put(snid, cur + 1);
} else {
tabItemBought.put(snid, 1);
}
} finally {
suggestWLock.unlock();
}
}
private List<List<Pair<Integer, Integer>>> getBoughtCashItems() {
if (ServerConstants.USE_ENFORCE_ITEM_SUGGESTION) {
return new ArrayList<>(0);
}
suggestRLock.lock();
try {
List<List<Pair<Integer, Integer>>> boughtCounts = new ArrayList<>(cashItemBought.size());
for (Map<Integer, Integer> tab : cashItemBought) {
List<Pair<Integer, Integer>> tabItems = new LinkedList<>();
boughtCounts.add(tabItems);
for (Entry<Integer, Integer> e : tab.entrySet()) {
tabItems.add(new Pair<>(e.getKey(), e.getValue()));
}
}
return boughtCounts;
} finally {
suggestRLock.unlock();
}
}
private List<Integer> getMostSellerOnTab(List<Pair<Integer, Integer>> tabSellers) {
List<Integer> tabLeaderboards;
Comparator<Pair<Integer, Integer>> comparator = new Comparator<Pair<Integer, Integer>>() { // descending order
@Override
public int compare(Pair<Integer, Integer> p1, Pair<Integer, Integer> p2) {
return p2.getRight().compareTo(p1.getRight());
}
};
PriorityQueue<Pair<Integer, Integer>> queue = new PriorityQueue<>(Math.max(1, tabSellers.size()), comparator);
for(Pair<Integer, Integer> p : tabSellers) {
queue.add(p);
}
tabLeaderboards = new LinkedList<>();
for(int i = 0; i < Math.min(tabSellers.size(), 5); i++) {
tabLeaderboards.add(queue.remove().getLeft());
}
return tabLeaderboards;
}
public List<List<Integer>> getMostSellerCashItems() {
List<List<Pair<Integer, Integer>>> mostSellers = this.getBoughtCashItems();
List<List<Integer>> cashLeaderboards = new ArrayList<>(9);
List<Integer> tabLeaderboards;
List<Integer> allLeaderboards = null;
for(List<Pair<Integer, Integer>> tabSellers : mostSellers) {
if (tabSellers.size() < 5) {
if (allLeaderboards == null) {
List<Pair<Integer, Integer>> allSellers = new LinkedList<>();
for (List<Pair<Integer, Integer>> tabItems : mostSellers) {
allSellers.addAll(tabItems);
}
allLeaderboards = getMostSellerOnTab(allSellers);
}
tabLeaderboards = new LinkedList<>();
if (allLeaderboards.size() < 5) {
for(int i : GameConstants.CASH_DATA) {
tabLeaderboards.add(i);
}
} else {
tabLeaderboards.addAll(allLeaderboards);
}
} else {
tabLeaderboards = getMostSellerOnTab(tabSellers);
}
cashLeaderboards.add(tabLeaderboards);
}
return cashLeaderboards;
}
public void registerPetHunger(MapleCharacter chr, byte petSlot) { public void registerPetHunger(MapleCharacter chr, byte petSlot) {
if(chr.isGM() && ServerConstants.GM_PETS_NEVER_HUNGRY || ServerConstants.PETS_NEVER_HUNGRY) { if(chr.isGM() && ServerConstants.GM_PETS_NEVER_HUNGRY || ServerConstants.PETS_NEVER_HUNGRY) {
return; return;
+2 -2
View File
@@ -54,6 +54,7 @@ import tools.MaplePacketCreator;
import client.MapleCharacter; import client.MapleCharacter;
import client.MapleClient; import client.MapleClient;
import client.MapleQuestStatus; import client.MapleQuestStatus;
import client.MapleStat;
import client.SkillFactory; import client.SkillFactory;
import client.inventory.Equip; import client.inventory.Equip;
import client.inventory.Item; import client.inventory.Item;
@@ -581,8 +582,7 @@ public class AbstractPlayerInteraction {
} }
public void gainFame(int delta) { public void gainFame(int delta) {
c.getPlayer().addFame(delta); getPlayer().gainFame(delta);
c.announce(MaplePacketCreator.getShowFameGain(delta));
} }
public void changeMusic(String songName) { public void changeMusic(String songName) {
@@ -65,6 +65,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.server.coordinator.MapleEventRecallCoordinator;
import scripting.AbstractPlayerInteraction; import scripting.AbstractPlayerInteraction;
import server.MapleItemInformationProvider; import server.MapleItemInformationProvider;
import server.life.MapleLifeFactory; import server.life.MapleLifeFactory;
@@ -599,6 +600,8 @@ public class EventInstanceManager {
} catch (ScriptException | NoSuchMethodException ex) { } catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
MapleEventRecallCoordinator.getInstance().storeEventInstance(chr.getId(), this);
} }
/** /**
@@ -1157,6 +1160,10 @@ public class EventInstanceManager {
public final void setEventCleared() { public final void setEventCleared() {
eventCleared = true; eventCleared = true;
for (MapleCharacter chr : getPlayers()) {
chr.awardQuestPoint(ServerConstants.QUEST_POINT_PER_EVENT_CLEAR);
}
sL.lock(); sL.lock();
try { try {
em.disposeInstance(name); em.disposeInstance(name);
@@ -1171,6 +1178,10 @@ public class EventInstanceManager {
return eventCleared; return eventCleared;
} }
public final boolean isEventDisposed() {
return disposed;
}
private boolean isEventTeamLeaderOn() { private boolean isEventTeamLeaderOn() {
for(MapleCharacter chr: getPlayers()) { for(MapleCharacter chr: getPlayers()) {
if(chr.getId() == getLeaderId()) return true; if(chr.getId() == getLeaderId()) return true;
@@ -285,6 +285,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
return getPlayer().getLevel(); return getPlayer().getLevel();
} }
@Override
public void showEffect(String effect) { public void showEffect(String effect) {
getPlayer().getMap().broadcastMessage(MaplePacketCreator.environmentChange(effect, 3)); getPlayer().getMap().broadcastMessage(MaplePacketCreator.environmentChange(effect, 3));
} }
+1 -1
View File
@@ -42,7 +42,7 @@ public class QuestScriptManager extends AbstractScriptManager {
private static QuestScriptManager instance = new QuestScriptManager(); private static QuestScriptManager instance = new QuestScriptManager();
public synchronized static QuestScriptManager getInstance() { public static QuestScriptManager getInstance() {
return instance; return instance;
} }
@@ -44,7 +44,7 @@ public class ReactorScriptManager extends AbstractScriptManager {
private static ReactorScriptManager instance = new ReactorScriptManager(); private static ReactorScriptManager instance = new ReactorScriptManager();
public synchronized static ReactorScriptManager getInstance() { public static ReactorScriptManager getInstance() {
return instance; return instance;
} }
+12 -5
View File
@@ -32,6 +32,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import net.server.Server;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import provider.MapleData; import provider.MapleData;
@@ -107,18 +109,18 @@ public class CashShop {
if (ItemConstants.EXPIRING_ITEMS) { if (ItemConstants.EXPIRING_ITEMS) {
if(period == 1) { if(period == 1) {
if(itemId == 5211048 || itemId == 5360042) { // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day. if(itemId == 5211048 || itemId == 5360042) { // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day.
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 4)); item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 4));
/* /*
} else if(itemId == 5211047 || itemId == 5360014) { // 3 Hour 2X coupons, unused as of now } else if(itemId == 5211047 || itemId == 5360014) { // 3 Hour 2X coupons, unused as of now
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 3)); item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 3));
*/ */
} else if(itemId == 5211060) { // 2 Hour 3X coupons. } else if(itemId == 5211060) { // 2 Hour 3X coupons.
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 2)); item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 2));
} else { } else {
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 24)); item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 24));
} }
} else { } else {
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 24 * period)); item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 24 * period));
} }
} }
@@ -354,6 +356,11 @@ public class CashShop {
} }
} }
public void gainCash(int type, CashItem buyItem, int world) {
gainCash(type, -buyItem.getPrice());
if(!ServerConstants.USE_ENFORCE_ITEM_SUGGESTION) Server.getInstance().getWorld(world).addCashItemBought(buyItem.getSN());
}
public boolean isOpened() { public boolean isOpened() {
return opened; return opened;
} }
+62 -11
View File
@@ -97,6 +97,8 @@ public class MapleItemInformationProvider {
protected Map<Integer, MapleStatEffect> itemEffects = new HashMap<>(); protected Map<Integer, MapleStatEffect> itemEffects = new HashMap<>();
protected Map<Integer, Map<String, Integer>> equipStatsCache = new HashMap<>(); protected Map<Integer, Map<String, Integer>> equipStatsCache = new HashMap<>();
protected Map<Integer, Equip> equipCache = new HashMap<>(); protected Map<Integer, Equip> equipCache = new HashMap<>();
protected Map<Integer, MapleData> equipLevelInfoCache = new HashMap<>();
protected Map<Integer, Integer> equipMaxLevelCache = new HashMap<>();
protected Map<Integer, Integer> wholePriceCache = new HashMap<>(); protected Map<Integer, Integer> wholePriceCache = new HashMap<>();
protected Map<Integer, Double> unitPriceCache = new HashMap<>(); protected Map<Integer, Double> unitPriceCache = new HashMap<>();
protected Map<Integer, Integer> projectileWatkCache = new HashMap<>(); protected Map<Integer, Integer> projectileWatkCache = new HashMap<>();
@@ -1217,7 +1219,7 @@ public class MapleItemInformationProvider {
return ret; return ret;
} }
public boolean isDropRestricted(int itemId) { public boolean isLootRestricted(int itemId) {
if (dropRestrictionCache.containsKey(itemId)) { if (dropRestrictionCache.containsKey(itemId)) {
return dropRestrictionCache.get(itemId); return dropRestrictionCache.get(itemId);
} }
@@ -1229,15 +1231,16 @@ public class MapleItemInformationProvider {
if (!bRestricted) { if (!bRestricted) {
bRestricted = MapleDataTool.getIntConvert("info/accountSharable", data, 0) == 1; bRestricted = MapleDataTool.getIntConvert("info/accountSharable", data, 0) == 1;
} }
if (!bRestricted) {
bRestricted = MapleDataTool.getIntConvert("info/quest", data, 0) == 1;
}
} }
dropRestrictionCache.put(itemId, bRestricted); dropRestrictionCache.put(itemId, bRestricted);
return bRestricted; return bRestricted;
} }
public boolean isDropRestricted(int itemId) {
return isLootRestricted(itemId) || isQuestItem(itemId);
}
public boolean isPickupRestricted(int itemId) { public boolean isPickupRestricted(int itemId) {
if (pickupRestrictionCache.containsKey(itemId)) { if (pickupRestrictionCache.containsKey(itemId)) {
return pickupRestrictionCache.get(itemId); return pickupRestrictionCache.get(itemId);
@@ -1674,8 +1677,7 @@ public class MapleItemInformationProvider {
return true; return true;
} }
public ArrayList<Pair<Integer, String>> getItemDataByName(String name) public ArrayList<Pair<Integer, String>> getItemDataByName(String name) {
{
ArrayList<Pair<Integer, String>> ret = new ArrayList<>(); ArrayList<Pair<Integer, String>> ret = new ArrayList<>();
for (Pair<Integer, String> itemPair : MapleItemInformationProvider.getInstance().getAllItems()) { for (Pair<Integer, String> itemPair : MapleItemInformationProvider.getInstance().getAllItems()) {
if (itemPair.getRight().toLowerCase().contains(name.toLowerCase())) { if (itemPair.getRight().toLowerCase().contains(name.toLowerCase())) {
@@ -1685,13 +1687,62 @@ public class MapleItemInformationProvider {
return ret; return ret;
} }
private MapleData getEquipLevelInfo(int itemId) {
MapleData equipLevelData = equipLevelInfoCache.get(itemId);
if (equipLevelData == null) {
if (equipLevelInfoCache.containsKey(itemId)) return null;
MapleData iData = getItemData(itemId);
if (iData != null) {
MapleData data = iData.getChildByPath("info/level");
if (data != null) {
equipLevelData = data.getChildByPath("info");
}
}
equipLevelInfoCache.put(itemId, equipLevelData);
}
return equipLevelData;
}
public int getEquipLevel(int itemId, boolean getMaxLevel) {
Integer eqLevel = equipMaxLevelCache.get(itemId);
if (eqLevel == null) {
eqLevel = 1; // greater than 1 means that it was supposed to levelup on GMS
MapleData data = getEquipLevelInfo(itemId);
if (data != null) {
if (getMaxLevel) {
int curLevel = 1;
while (true) {
MapleData data2 = data.getChildByPath(Integer.toString(curLevel));
if (data2 == null || data2.getChildren().size() <= 1) {
eqLevel = curLevel;
equipMaxLevelCache.put(itemId, eqLevel);
break;
}
curLevel++;
}
} else {
MapleData data2 = data.getChildByPath("1");
if (data2 != null && data2.getChildren().size() > 1) {
eqLevel = 2;
}
}
}
}
return eqLevel;
}
public List<Pair<String, Integer>> getItemLevelupStats(int itemId, int level) { public List<Pair<String, Integer>> getItemLevelupStats(int itemId, int level) {
List<Pair<String, Integer>> list = new LinkedList<>(); List<Pair<String, Integer>> list = new LinkedList<>();
MapleData data = getItemData(itemId); MapleData data = getEquipLevelInfo(itemId);
MapleData data1 = data.getChildByPath("info").getChildByPath("level"); if (data != null) {
MapleData data2 = data.getChildByPath(Integer.toString(level));
if (data1 != null) {
MapleData data2 = data1.getChildByPath("info").getChildByPath(Integer.toString(level));
if (data2 != null) { if (data2 != null) {
for (MapleData da : data2.getChildren()) { for (MapleData da : data2.getChildren()) {
if (Math.random() < 0.9) { if (Math.random() < 0.9) {
+13 -9
View File
@@ -30,7 +30,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import net.server.world.MaplePartyCharacter; import net.server.Server;
import provider.MapleData; import provider.MapleData;
import provider.MapleDataTool; import provider.MapleDataTool;
import server.life.MapleMonster; import server.life.MapleMonster;
@@ -997,11 +997,15 @@ public class MapleStatEffect {
return bounds; return bounds;
} }
public int getBuffLocalDuration() {
return !ServerConstants.USE_BUFF_EVERLASTING ? duration : Integer.MAX_VALUE;
}
public void silentApplyBuff(MapleCharacter chr, long localStartTime) { public void silentApplyBuff(MapleCharacter chr, long localStartTime) {
int localDuration = duration; int localDuration = getBuffLocalDuration();
localDuration = alchemistModifyVal(chr, localDuration, false); localDuration = alchemistModifyVal(chr, localDuration, false);
//CancelEffectAction cancelAction = new CancelEffectAction(chr, this, starttime); //CancelEffectAction cancelAction = new CancelEffectAction(chr, this, starttime);
//ScheduledFuture<?> schedule = TimerManager.getInstance().schedule(cancelAction, ((starttime + localDuration) - System.currentTimeMillis())); //ScheduledFuture<?> schedule = TimerManager.getInstance().schedule(cancelAction, ((starttime + localDuration) - Server.getInstance().getCurrentTime()));
chr.registerEffect(this, localStartTime, localStartTime + localDuration, true); chr.registerEffect(this, localStartTime, localStartTime + localDuration, true);
SummonMovementType summonMovementType = getSummonMovementType(); SummonMovementType summonMovementType = getSummonMovementType();
@@ -1021,17 +1025,17 @@ public class MapleStatEffect {
final List<Pair<MapleBuffStat, Integer>> stat = Collections.singletonList(new Pair<>(MapleBuffStat.ARAN_COMBO, combo)); final List<Pair<MapleBuffStat, Integer>> stat = Collections.singletonList(new Pair<>(MapleBuffStat.ARAN_COMBO, combo));
applyto.announce(MaplePacketCreator.giveBuff(sourceid, 99999, stat)); applyto.announce(MaplePacketCreator.giveBuff(sourceid, 99999, stat));
final long starttime = System.currentTimeMillis(); final long starttime = Server.getInstance().getCurrentTime();
// final CancelEffectAction cancelAction = new CancelEffectAction(applyto, this, starttime); // final CancelEffectAction cancelAction = new CancelEffectAction(applyto, this, starttime);
// final ScheduledFuture<?> schedule = TimerManager.getInstance().schedule(cancelAction, ((starttime + 99999) - System.currentTimeMillis())); // final ScheduledFuture<?> schedule = TimerManager.getInstance().schedule(cancelAction, ((starttime + 99999) - Server.getInstance().getCurrentTime()));
applyto.registerEffect(this, starttime, Long.MAX_VALUE, false); applyto.registerEffect(this, starttime, Long.MAX_VALUE, false);
} }
public void updateBuffEffect(MapleCharacter target, List<Pair<MapleBuffStat, Integer>> activeStats, long starttime) { public void updateBuffEffect(MapleCharacter target, List<Pair<MapleBuffStat, Integer>> activeStats, long starttime) {
int localDuration = duration; int localDuration = getBuffLocalDuration();
localDuration = alchemistModifyVal(target, localDuration, false); localDuration = alchemistModifyVal(target, localDuration, false);
long leftDuration = (starttime + localDuration) - System.currentTimeMillis(); long leftDuration = (starttime + localDuration) - Server.getInstance().getCurrentTime();
if(leftDuration > 0) { if(leftDuration > 0) {
target.announce(MaplePacketCreator.giveBuff((skill ? sourceid : -sourceid), (int)leftDuration, activeStats)); target.announce(MaplePacketCreator.giveBuff((skill ? sourceid : -sourceid), (int)leftDuration, activeStats));
} }
@@ -1043,7 +1047,7 @@ public class MapleStatEffect {
} }
List<Pair<MapleBuffStat, Integer>> localstatups = statups; List<Pair<MapleBuffStat, Integer>> localstatups = statups;
int localDuration = duration; int localDuration = getBuffLocalDuration();
int localsourceid = sourceid; int localsourceid = sourceid;
int seconds = localDuration / 1000; int seconds = localDuration / 1000;
MapleMount givemount = null; MapleMount givemount = null;
@@ -1155,7 +1159,7 @@ public class MapleStatEffect {
} }
} }
long starttime = System.currentTimeMillis(); long starttime = Server.getInstance().getCurrentTime();
//CancelEffectAction cancelAction = new CancelEffectAction(applyto, this, starttime); //CancelEffectAction cancelAction = new CancelEffectAction(applyto, this, starttime);
//ScheduledFuture<?> schedule = TimerManager.getInstance().schedule(cancelAction, localDuration); //ScheduledFuture<?> schedule = TimerManager.getInstance().schedule(cancelAction, localDuration);
applyto.registerEffect(this, starttime, starttime + localDuration, false); applyto.registerEffect(this, starttime, starttime + localDuration, false);
-1
View File
@@ -32,7 +32,6 @@ import client.status.MonsterStatus;
import constants.GameConstants; import constants.GameConstants;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import server.TimerManager;
import tools.Randomizer; import tools.Randomizer;
import server.maps.MapleMapObject; import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType; import server.maps.MapleMapObjectType;
+2 -2
View File
@@ -48,9 +48,9 @@ public class MapleLootInventory {
} }
} }
public boolean hasItem(int itemid, int quantity) { public int hasItem(int itemid, int quantity) {
Integer itemQty = items.get(itemid); Integer itemQty = items.get(itemid);
return itemQty != null && itemQty >= quantity; return itemQty == null ? 0 : itemQty >= quantity ? 2 : itemQty > 0 ? 1 : 0;
} }
} }
+14 -9
View File
@@ -34,25 +34,30 @@ import server.quest.MapleQuest;
*/ */
public class MapleLootManager { public class MapleLootManager {
private static boolean needQuestItem(MonsterDropEntry dropEntry, MapleQuest quest, MapleCharacter chr, MapleLootInventory chrInv) {
if (chr.getQuestStatus(dropEntry.questid) != 1) return false;
return !chrInv.hasItem(dropEntry.itemId, quest.getItemAmountNeeded(dropEntry.itemId));
}
private static boolean isRelevantDrop(MonsterDropEntry dropEntry, List<MapleCharacter> partyMembers, List<MapleLootInventory> partyInv) { private static boolean isRelevantDrop(MonsterDropEntry dropEntry, List<MapleCharacter> partyMembers, List<MapleLootInventory> partyInv) {
MapleQuest quest = MapleQuest.getInstance(dropEntry.questid); MapleQuest quest = MapleQuest.getInstance(dropEntry.questid);
boolean restricted = MapleItemInformationProvider.getInstance().isDropRestricted(dropEntry.itemId); int qItemAmount = quest != null ? quest.getItemAmountNeeded(dropEntry.itemId) : 0;
boolean restricted = MapleItemInformationProvider.getInstance().isLootRestricted(dropEntry.itemId);
for (int i = 0; i < partyMembers.size(); i++) { for (int i = 0; i < partyMembers.size(); i++) {
MapleLootInventory chrInv = partyInv.get(i); MapleLootInventory chrInv = partyInv.get(i);
if(restricted && chrInv.hasItem(dropEntry.itemId, 1)) { if (dropEntry.questid > 0) {
if (partyMembers.get(i).getQuestStatus(dropEntry.questid) != 1) {
continue; continue;
} }
if(dropEntry.questid <= 0 || needQuestItem(dropEntry, quest, partyMembers.get(i), chrInv)) { int qItemStatus = chrInv.hasItem(dropEntry.itemId, qItemAmount);
return true; if (qItemStatus == 2) {
continue;
} else if (restricted && qItemStatus == 1) {
continue;
} }
} else if (restricted && chrInv.hasItem(dropEntry.itemId, 1) > 0) {
continue;
}
return true;
} }
return false; return false;
+1 -1
View File
@@ -242,7 +242,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
if (c.getPlayer().getMeso() >= price) { if (c.getPlayer().getMeso() >= price) {
if (canBuy(c, newItem)) { if (canBuy(c, newItem)) {
c.getPlayer().gainMeso(-price, false); c.getPlayer().gainMeso(-price, false);
if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) announceItemSold(newItem, price); // idea thanks to vcoc if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) announceItemSold(newItem, price); // idea thanks to Vcoc
synchronized (sold) { synchronized (sold) {
sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), newItem.getQuantity(), price)); sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), newItem.getQuantity(), price));
+2 -2
View File
@@ -1144,11 +1144,11 @@ public class MapleMap {
return character; return character;
} }
public List<MapleCharacter> getPlayersInRange(Rectangle box, List<MapleCharacter> chrList) { public List<MapleCharacter> getPlayersInRange(Rectangle box, List<MapleCharacter> targets) {
List<MapleCharacter> character = new LinkedList<>(); List<MapleCharacter> character = new LinkedList<>();
chrRLock.lock(); chrRLock.lock();
try { try {
for (MapleCharacter chr : chrList) { for (MapleCharacter chr : targets) {
if (characters.contains(chr)) { if (characters.contains(chr)) {
if (box.contains(chr.getPosition())) { if (box.contains(chr.getPosition())) {
character.add(chr); character.add(chr);
+1 -2
View File
@@ -227,7 +227,7 @@ public class MapleQuest {
if(!repeatable) return false; if(!repeatable) return false;
IntervalRequirement ir = (IntervalRequirement) startReqs.get(MapleQuestRequirementType.INTERVAL); IntervalRequirement ir = (IntervalRequirement) startReqs.get(MapleQuestRequirementType.INTERVAL);
return ir.getInterval() < ServerConstants.FAME_GAIN_MIN_HOUR_INTERVAL * 60 * 60 * 1000; return ir.getInterval() < ServerConstants.QUEST_POINT_REPEATABLE_INTERVAL * 60 * 60 * 1000;
} }
public boolean canStartWithoutRequirements(MapleCharacter c) { public boolean canStartWithoutRequirements(MapleCharacter c) {
@@ -373,7 +373,6 @@ public class MapleQuest {
return 0; return 0;
ItemRequirement ireq = (ItemRequirement) req; ItemRequirement ireq = (ItemRequirement) req;
return ireq.getItemAmountNeeded(itemid); return ireq.getItemAmountNeeded(itemid);
} }
+1 -3
View File
@@ -50,8 +50,6 @@ public class FameAction extends MapleQuestAction {
@Override @Override
public void run(MapleCharacter chr, Integer extSelection) { public void run(MapleCharacter chr, Integer extSelection) {
chr.addFame(fame); chr.gainFame(fame);
chr.updateSingleStat(MapleStat.FAME, chr.getFame());
chr.announce(MaplePacketCreator.getShowFameGain(fame));
} }
} }
+8
View File
@@ -41,6 +41,14 @@ public class HexTool {
return hexed.substring(0, hexed.length() - 1); return hexed.substring(0, hexed.length() - 1);
} }
public static String toCompressedString(byte[] bytes) {
StringBuilder hexed = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
hexed.append(toString(bytes[i]));
}
return hexed.substring(0, hexed.length());
}
public static byte[] getByteArrayFromHexString(String hex) { public static byte[] getByteArrayFromHexString(String hex) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
int nexti = 0; int nexti = 0;
+17 -23
View File
@@ -758,6 +758,8 @@ public class MaplePacketCreator {
* *
* @param serverId * @param serverId
* @param serverName The name of the server. * @param serverName The name of the server.
* @param flag
* @param eventmsg
* @param channelLoad Load of the channel - 1200 seems to be max. * @param channelLoad Load of the channel - 1200 seems to be max.
* @return The server info packet. * @return The server info packet.
*/ */
@@ -777,8 +779,11 @@ public class MaplePacketCreator {
for (Channel ch : channelLoad) { for (Channel ch : channelLoad) {
mplew.writeMapleAsciiString(serverName + "-" + ch.getId()); mplew.writeMapleAsciiString(serverName + "-" + ch.getId());
mplew.writeInt(ch.getChannelCapacity()); mplew.writeInt(ch.getChannelCapacity());
mplew.write(1);
mplew.writeShort(ch.getId() - 1); // thanks GabrielSin for this channel packet structure part
mplew.write(1);// nWorldID
mplew.write(ch.getId() - 1);// nChannelID
mplew.writeBool(false);// bAdultChannel
} }
mplew.writeShort(0); mplew.writeShort(0);
return mplew.getPacket(); return mplew.getPacket();
@@ -1042,8 +1047,8 @@ public class MaplePacketCreator {
mplew.write(spawnPoint); mplew.write(spawnPoint);
mplew.writeShort(chr.getHp()); mplew.writeShort(chr.getHp());
mplew.writeBool(false); mplew.writeBool(false);
mplew.writeLong(getTime(System.currentTimeMillis())); mplew.writeLong(getTime(Server.getInstance().getCurrentTime()));
mplew.writeShort(0); mplew.writeInt(0);
return mplew.getPacket(); return mplew.getPacket();
} }
@@ -1059,8 +1064,8 @@ public class MaplePacketCreator {
mplew.writeBool(true); mplew.writeBool(true);
mplew.writeInt(spawnPosition.x); // spawn position placement thanks to Arnah (Vertisy) mplew.writeInt(spawnPosition.x); // spawn position placement thanks to Arnah (Vertisy)
mplew.writeInt(spawnPosition.y); mplew.writeInt(spawnPosition.y);
mplew.writeLong(getTime(System.currentTimeMillis())); mplew.writeLong(getTime(Server.getInstance().getCurrentTime()));
mplew.writeShort(0); mplew.writeInt(0);
return mplew.getPacket(); return mplew.getPacket();
} }
@@ -7527,27 +7532,16 @@ public class MaplePacketCreator {
} }
mplew.skip(121); mplew.skip(121);
List<List<Integer>> mostSellers = c.getWorldServer().getMostSellerCashItems();
for (int i = 1; i <= 8; i++) { for (int i = 1; i <= 8; i++) {
List<Integer> mostSellersTab = mostSellers.get(i);
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
for (Integer snid : mostSellersTab) {
mplew.writeInt(i); mplew.writeInt(i);
mplew.writeInt(j); mplew.writeInt(j);
mplew.writeInt(50200004); mplew.writeInt(snid);
}
mplew.writeInt(i);
mplew.writeInt(j);
mplew.writeInt(50200069);
mplew.writeInt(i);
mplew.writeInt(j);
mplew.writeInt(50200117);
mplew.writeInt(i);
mplew.writeInt(j);
mplew.writeInt(50100008);
mplew.writeInt(i);
mplew.writeInt(j);
mplew.writeInt(50000047);
} }
} }
@@ -43,7 +43,7 @@ import java.util.Set;
* *
* This application finds inexistent itemids within the drop data from * This application finds inexistent itemids within the drop data from
* the Maplestory database specified by the URL below. This program * the Maplestory database specified by the URL below. This program
* assumes all itemids haves at most 7 digits. * assumes all itemids uses 7 digits.
* *
* A file is generated listing all the inexistent ids. * A file is generated listing all the inexistent ids.
*/ */
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="MapleInvalidItemWithNoNameFetcher" default="default" basedir=".">
<description>Builds, tests, and runs the project MapleInvalidItemWithNoNameFetcher.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="MapleInvalidItemWithNoNameFetcher-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>
@@ -36,16 +36,16 @@ import provider.MapleDataDirectoryEntry;
import provider.MapleDataFileEntry; import provider.MapleDataFileEntry;
import provider.MapleDataProvider; import provider.MapleDataProvider;
import provider.MapleDataProviderFactory; import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
/** /**
* *
* @author RonanLana * @author RonanLana
* *
* This application finds inexistent itemids within the drop data from * This application finds itemids with inexistent name and description from
* the Maplestory database specified by the URL below. This program * within the server-side XMLs, then identify them on a report file along
* assumes all itemids have at most 7 digits. * with a XML excerpt to be appended on the String.wz xml nodes. This program
* * assumes all equipids are depicted using 8 digits and item using 7 digits.
* A file is generated listing all the inexistent ids.
* *
* Estimated parse time: 2 minutes * Estimated parse time: 2 minutes
*/ */
@@ -65,6 +65,9 @@ public class MapleNoItemNameFetcher {
static Map<Integer, ItemType> itemsWithNoNameProperty = new HashMap<>(); static Map<Integer, ItemType> itemsWithNoNameProperty = new HashMap<>();
static Set<Integer> equipsWithNoCashProperty = new HashSet<>(); static Set<Integer> equipsWithNoCashProperty = new HashSet<>();
static Map<Integer, String> nameContentCache = new HashMap<>();
static Map<Integer, String> descContentCache = new HashMap<>();
static ItemType curType = ItemType.UNDEF; static ItemType curType = ItemType.UNDEF;
private enum ItemType { private enum ItemType {
@@ -78,11 +81,20 @@ public class MapleNoItemNameFetcher {
private static void processStringSubdirectoryData(MapleData subdirData, String subdirPath) { private static void processStringSubdirectoryData(MapleData subdirData, String subdirPath) {
for(MapleData md : subdirData.getChildren()) { for(MapleData md : subdirData.getChildren()) {
try { try {
if(md.getChildByPath("name") != null) { MapleData nameData = md.getChildByPath("name");
MapleData descData = md.getChildByPath("desc");
int itemId = Integer.parseInt(md.getName()); int itemId = Integer.parseInt(md.getName());
if (nameData != null && descData != null) {
itemsWithNoNameProperty.remove(itemId); itemsWithNoNameProperty.remove(itemId);
} else { } else {
System.out.println("Found itemid on String.wz with no name property: " + subdirPath + subdirData.getName() + "/" + md.getName()); if (nameData != null) {
nameContentCache.put(itemId, MapleDataTool.getString("name", md));
} else if (descData != null) {
descContentCache.put(itemId, MapleDataTool.getString("desc", md));
}
System.out.println("Found itemid on String.wz with no full property: " + subdirPath + subdirData.getName() + "/" + md.getName());
} }
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
System.out.println("Error reading string image: " + subdirPath + subdirData.getName() + "/" + md.getName()); System.out.println("Error reading string image: " + subdirPath + subdirData.getName() + "/" + md.getName());
@@ -422,10 +434,33 @@ public class MapleNoItemNameFetcher {
printWriter.println(); printWriter.println();
} }
private static String getMissingEquipName(int itemid) {
String s = nameContentCache.get(itemid);
if (s == null) {
s = "MISSING NAME " + itemid;
}
return s;
}
private static String getMissingEquipDesc(int itemid) {
String s = descContentCache.get(itemid);
if (s == null) {
s = "MISSING INFO " + itemid;
}
return s;
}
private static void writeMissingEquipInfo(Integer itemid) { private static void writeMissingEquipInfo(Integer itemid) {
printWriter.println(" <imgdir name=\"" + itemid + "\">"); printWriter.println(" <imgdir name=\"" + itemid + "\">");
printWriter.println(" <string name=\"name\" value=\"MISSING NAME\"/>");
printWriter.println(" <string name=\"desc\" value=\"MISSING INFO\"/>"); String s;
s = getMissingEquipName(itemid);
printWriter.println(" <string name=\"name\" value=\"" + s + "\"/>");
s = getMissingEquipDesc(itemid);
printWriter.println(" <string name=\"desc\" value=\"" + s + "\"/>");
printWriter.println(" </imgdir>"); printWriter.println(" </imgdir>");
} }
+73
View File
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="MapleQuestlineFetcher" default="default" basedir=".">
<description>Builds, tests, and runs the project MapleQuestlineFetcher.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="MapleQuestlineFetcher-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>
+73
View File
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="MapleReactorDropFetcher" default="default" basedir=".">
<description>Builds, tests, and runs the project MapleReactorDropFetcher.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="MapleReactorDropFetcher-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>
+3 -3
View File
@@ -2389,7 +2389,7 @@
<int name="delay" value="0"/> <int name="delay" value="0"/>
</imgdir> </imgdir>
<imgdir name="8"> <imgdir name="8">
<string name="pn" value="tp"/> <string name="pn" value="np"/>
<int name="pt" value="6"/> <int name="pt" value="6"/>
<int name="x" value="403"/> <int name="x" value="403"/>
<int name="y" value="235"/> <int name="y" value="235"/>
@@ -2397,7 +2397,7 @@
<string name="tn" value=""/> <string name="tn" value=""/>
</imgdir> </imgdir>
<imgdir name="9"> <imgdir name="9">
<string name="pn" value="tp"/> <string name="pn" value="np"/>
<int name="pt" value="6"/> <int name="pt" value="6"/>
<int name="x" value="316"/> <int name="x" value="316"/>
<int name="y" value="234"/> <int name="y" value="234"/>
@@ -2405,7 +2405,7 @@
<string name="tn" value=""/> <string name="tn" value=""/>
</imgdir> </imgdir>
<imgdir name="10"> <imgdir name="10">
<string name="pn" value="tp"/> <string name="pn" value="np"/>
<int name="pt" value="6"/> <int name="pt" value="6"/>
<int name="x" value="487"/> <int name="x" value="487"/>
<int name="y" value="234"/> <int name="y" value="234"/>
+1 -1
View File
@@ -315,7 +315,7 @@
<string name="tn" value=""/> <string name="tn" value=""/>
</imgdir> </imgdir>
<imgdir name="1"> <imgdir name="1">
<string name="pn" value="tp"/> <string name="pn" value="np"/>
<int name="pt" value="0"/> <int name="pt" value="0"/>
<int name="x" value="-313"/> <int name="x" value="-313"/>
<int name="y" value="179"/> <int name="y" value="179"/>
+1 -1
View File
@@ -315,7 +315,7 @@
<string name="tn" value=""/> <string name="tn" value=""/>
</imgdir> </imgdir>
<imgdir name="1"> <imgdir name="1">
<string name="pn" value="tp"/> <string name="pn" value="np"/>
<int name="pt" value="0"/> <int name="pt" value="0"/>
<int name="x" value="-313"/> <int name="x" value="-313"/>
<int name="y" value="179"/> <int name="y" value="179"/>
+2 -2
View File
@@ -5116,7 +5116,7 @@
<string name="tn" value=""/> <string name="tn" value=""/>
</imgdir> </imgdir>
<imgdir name="1"> <imgdir name="1">
<string name="pn" value="tp"/> <string name="pn" value="np"/>
<int name="pt" value="0"/> <int name="pt" value="0"/>
<int name="x" value="607"/> <int name="x" value="607"/>
<int name="y" value="-130"/> <int name="y" value="-130"/>
@@ -5124,7 +5124,7 @@
<string name="tn" value=""/> <string name="tn" value=""/>
</imgdir> </imgdir>
<imgdir name="2"> <imgdir name="2">
<string name="pn" value="tp"/> <string name="pn" value="np"/>
<int name="pt" value="0"/> <int name="pt" value="0"/>
<int name="x" value="-148"/> <int name="x" value="-148"/>
<int name="y" value="-62"/> <int name="y" value="-62"/>
+1 -1
View File
@@ -7555,7 +7555,7 @@
<string name="tn" value=""/> <string name="tn" value=""/>
</imgdir> </imgdir>
<imgdir name="1"> <imgdir name="1">
<string name="pn" value="tp"/> <string name="pn" value="np"/>
<int name="pt" value="0"/> <int name="pt" value="0"/>
<int name="x" value="1675"/> <int name="x" value="1675"/>
<int name="y" value="77"/> <int name="y" value="77"/>
+179 -1
View File
@@ -64,6 +64,36 @@
<int name="19" value="104040002"/> <int name="19" value="104040002"/>
<int name="20" value="106010000"/> <int name="20" value="106010000"/>
<int name="21" value="106010100"/> <int name="21" value="106010100"/>
<int name="33" value="106020600"/>
<int name="31" value="106020500"/>
<int name="30" value="106020403"/>
<int name="29" value="106020402"/>
<int name="28" value="106020401"/>
<int name="27" value="106020400"/>
<int name="26" value="106020300"/>
<int name="25" value="106020200"/>
<int name="24" value="106020100"/>
<int name="23" value="106020000"/>
<int name="32" value="106020501"/>
<int name="40" value="106021100"/>
<int name="51" value="106021700"/>
<int name="49" value="106021600"/>
<int name="48" value="106021501"/>
<int name="47" value="106021500"/>
<int name="46" value="106021402"/>
<int name="41" value="106021200"/>
<int name="38" value="106021000"/>
<int name="37" value="106020900"/>
<int name="36" value="106020800"/>
<int name="35" value="106020700"/>
<int name="34" value="106020601"/>
<int name="39" value="106021001"/>
<int name="45" value="106021401"/>
<int name="44" value="106021400"/>
<int name="43" value="106021300"/>
<int name="42" value="106021201"/>
<int name="50" value="106021601"/>
<int name="22" value="106021800"/>
</imgdir> </imgdir>
<vector name="spot" x="-230" y="-67"/> <vector name="spot" x="-230" y="-67"/>
</imgdir> </imgdir>
@@ -202,6 +232,10 @@
<int name="43" value="103040400"/> <int name="43" value="103040400"/>
</imgdir> </imgdir>
<vector name="spot" x="-273" y="-99"/> <vector name="spot" x="-273" y="-99"/>
<canvas name="path" width="287" height="116">
<vector name="origin" x="284" y="204"/>
<int name="z" value="2"/>
</canvas>
</imgdir> </imgdir>
<imgdir name="5"> <imgdir name="5">
<int name="type" value="3"/> <int name="type" value="3"/>
@@ -527,9 +561,26 @@
<int name="86" value="221024500"/> <int name="86" value="221024500"/>
<int name="87" value="222020200"/> <int name="87" value="222020200"/>
<int name="88" value="222020300"/> <int name="88" value="222020300"/>
<int name="89" value="300000000"/>
<int name="90" value="300000010"/>
<int name="99" value="300010300"/>
<int name="100" value="300010400"/>
<int name="101" value="300020000"/>
<int name="102" value="300020100"/>
<int name="103" value="300020200"/>
<int name="104" value="300030000"/>
<int name="105" value="300030100"/>
<int name="91" value="300000011"/>
<int name="92" value="300000012"/>
<int name="93" value="300000001"/>
<int name="94" value="300000002"/>
<int name="95" value="300000100"/>
<int name="96" value="300010000"/>
<int name="97" value="300010100"/>
<int name="98" value="300010200"/>
</imgdir> </imgdir>
<canvas name="path" width="59" height="119"> <canvas name="path" width="59" height="119">
<vector name="origin" x="61" y="103"/> <vector name="origin" x="-282" y="-199"/>
<int name="z" value="2"/> <int name="z" value="2"/>
</canvas> </canvas>
</imgdir> </imgdir>
@@ -918,6 +969,19 @@
<int name="z" value="2"/> <int name="z" value="2"/>
</canvas> </canvas>
</imgdir> </imgdir>
<imgdir name="23">
<vector name="spot" x="-183" y="-95"/>
<int name="type" value="3"/>
<imgdir name="mapNo">
<int name="0" value="680000000"/>
<int name="1" value="670000200"/>
<int name="2" value="670010000"/>
<int name="3" value="670000100"/>
<int name="4" value="680000001"/>
<int name="5" value="680000002"/>
<int name="6" value="680000003"/>
</imgdir>
</imgdir>
<imgdir name="22"> <imgdir name="22">
<vector name="spot" x="-266" y="10"/> <vector name="spot" x="-266" y="10"/>
<int name="type" value="3"/> <int name="type" value="3"/>
@@ -948,6 +1012,110 @@
<int name="z" value="2"/> <int name="z" value="2"/>
</canvas> </canvas>
</imgdir> </imgdir>
<imgdir name="24">
<int name="type" value="3"/>
<imgdir name="mapNo">
<int name="0" value="540000000"/>
<int name="1" value="540000100"/>
<int name="2" value="540000200"/>
<int name="3" value="540000300"/>
<int name="4" value="540010000"/>
<int name="5" value="540020000"/>
<int name="6" value="540020100"/>
</imgdir>
<vector name="spot" x="-8" y="-152"/>
<canvas name="path" width="287" height="116">
<vector name="origin" x="284" y="204"/>
<int name="z" value="2"/>
</canvas>
</imgdir>
<imgdir name="25">
<vector name="spot" x="3" y="-148"/>
<int name="type" value="3"/>
<imgdir name="mapNo">
<int name="0" value="541000000"/>
<int name="1" value="541000100"/>
<int name="2" value="541000200"/>
<int name="12" value="541010110"/>
<int name="11" value="541010100"/>
<int name="10" value="541010060"/>
<int name="9" value="541010050"/>
<int name="8" value="541010040"/>
<int name="7" value="541010030"/>
<int name="6" value="541010020"/>
<int name="5" value="541010010"/>
<int name="4" value="541010000"/>
<int name="3" value="541000300"/>
</imgdir>
</imgdir>
<imgdir name="26">
<imgdir name="mapNo">
<int name="0" value="550000000"/>
<int name="1" value="550000100"/>
<int name="2" value="550000200"/>
<int name="3" value="550000300"/>
<int name="4" value="550000400"/>
</imgdir>
<vector name="spot" x="-30" y="-162"/>
<int name="type" value="3"/>
</imgdir>
<imgdir name="27">
<vector name="spot" x="-16" y="-159"/>
<int name="type" value="3"/>
<imgdir name="mapNo">
<int name="0" value="551000000"/>
<int name="1" value="551000100"/>
<int name="2" value="551000200"/>
<int name="3" value="551010000"/>
<int name="4" value="551020000"/>
<int name="5" value="551030000"/>
<int name="6" value="551030100"/>
<int name="7" value="551030200"/>
</imgdir>
</imgdir>
<imgdir name="28">
<imgdir name="mapNo">
<int name="0" value="800000000"/>
<int name="1" value="800010000"/>
<int name="2" value="800010001"/>
<int name="3" value="800010100"/>
<int name="4" value="800020000"/>
<int name="5" value="800020100"/>
<int name="6" value="800020101"/>
<int name="7" value="800020110"/>
<int name="8" value="800020120"/>
<int name="9" value="800020130"/>
<int name="10" value="800020200"/>
<int name="11" value="800020300"/>
<int name="12" value="800030000"/>
</imgdir>
<vector name="spot" x="5" y="-170"/>
<int name="type" value="3"/>
</imgdir>
<imgdir name="29">
<imgdir name="mapNo">
<int name="0" value="801000000"/>
<int name="1" value="801000001"/>
<int name="2" value="801000002"/>
<int name="3" value="801000100"/>
<int name="4" value="801000110"/>
<int name="5" value="801000200"/>
<int name="6" value="801000210"/>
<int name="7" value="801000300"/>
<int name="8" value="801010000"/>
<int name="9" value="801020000"/>
<int name="10" value="801030000"/>
<int name="11" value="801040000"/>
<int name="12" value="801040001"/>
<int name="13" value="801040002"/>
<int name="14" value="801040003"/>
<int name="15" value="801040004"/>
<int name="16" value="801040100"/>
<int name="17" value="801040101"/>
</imgdir>
<vector name="spot" x="20" y="-175"/>
<int name="type" value="3"/>
</imgdir>
</imgdir> </imgdir>
<imgdir name="MapLink"> <imgdir name="MapLink">
<imgdir name="0"> <imgdir name="0">
@@ -1061,5 +1229,15 @@
</canvas> </canvas>
</imgdir> </imgdir>
</imgdir> </imgdir>
<imgdir name="11">
<imgdir name="link">
<string name="linkMap" value="WorldMap140"/>
<canvas name="linkImg" width="77" height="48">
<int name="z" value="1"/>
<vector name="origin" x="42" y="185"/>
</canvas>
</imgdir>
<string name="toolTip" value="WorldTour"/>
</imgdir>
</imgdir> </imgdir>
</imgdir> </imgdir>
+125 -9
View File
@@ -51,15 +51,6 @@
<int name="5" value="103000005"/> <int name="5" value="103000005"/>
<int name="6" value="103000006"/> <int name="6" value="103000006"/>
<int name="7" value="193000000"/> <int name="7" value="193000000"/>
<int name="8" value="103000100"/>
<int name="9" value="103000101"/>
<int name="10" value="103000102"/>
<int name="11" value="103000103"/>
<int name="12" value="103000104"/>
<int name="13" value="103000105"/>
<int name="14" value="103000200"/>
<int name="15" value="103000201"/>
<int name="16" value="103000202"/>
</imgdir> </imgdir>
</imgdir> </imgdir>
<imgdir name="4"> <imgdir name="4">
@@ -810,6 +801,121 @@
<int name="13" value="103040400"/> <int name="13" value="103040400"/>
</imgdir> </imgdir>
</imgdir> </imgdir>
<imgdir name="99">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="103000200"/>
</imgdir>
<vector name="spot" x="-266" y="-77"/>
</imgdir>
<imgdir name="98">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="103000105"/>
</imgdir>
<vector name="spot" x="-277" y="-95"/>
</imgdir>
<imgdir name="97">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="103000104"/>
</imgdir>
<vector name="spot" x="-272" y="-91"/>
</imgdir>
<imgdir name="96">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="103000103"/>
</imgdir>
<vector name="spot" x="-268" y="-87"/>
</imgdir>
<imgdir name="95">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="103000102"/>
</imgdir>
<vector name="spot" x="-264" y="-82"/>
</imgdir>
<imgdir name="94">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="103000101"/>
</imgdir>
<vector name="spot" x="-258" y="-78"/>
</imgdir>
<imgdir name="93">
<int name="type" value="3"/>
<imgdir name="mapNo">
<int name="0" value="103000100"/>
</imgdir>
<vector name="spot" x="-250" y="-72"/>
</imgdir>
<imgdir name="100">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="103000201"/>
</imgdir>
<vector name="spot" x="-273" y="-75"/>
</imgdir>
<imgdir name="101">
<vector name="spot" x="-283" y="-76"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="103000202"/>
</imgdir>
</imgdir>
<imgdir name="102">
<vector name="spot" x="228" y="-15"/>
<int name="type" value="0"/>
<imgdir name="mapNo">
<int name="0" value="680000000"/>
<int name="1" value="680000001"/>
<int name="2" value="680000002"/>
<int name="3" value="680000003"/>
<int name="4" value="680000100"/>
<int name="5" value="680000110"/>
<int name="6" value="680000200"/>
<int name="7" value="680000210"/>
<int name="8" value="680000300"/>
<int name="9" value="680000400"/>
<int name="10" value="680000401"/>
</imgdir>
</imgdir>
<imgdir name="103">
<vector name="spot" x="245" y="-12"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="680010000"/>
</imgdir>
</imgdir>
<imgdir name="104">
<vector name="spot" x="260" y="-25"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="680010100"/>
</imgdir>
</imgdir>
<imgdir name="105">
<vector name="spot" x="265" y="-44"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="670000100"/>
</imgdir>
</imgdir>
<imgdir name="106">
<vector name="spot" x="273" y="-56"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="670000200"/>
</imgdir>
</imgdir>
<imgdir name="107">
<vector name="spot" x="284" y="-62"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="670010000"/>
</imgdir>
</imgdir>
</imgdir> </imgdir>
<imgdir name="MapLink"> <imgdir name="MapLink">
<imgdir name="0"> <imgdir name="0">
@@ -842,5 +948,15 @@
</imgdir> </imgdir>
<string name="toolTip" value="Kerning Square"/> <string name="toolTip" value="Kerning Square"/>
</imgdir> </imgdir>
<imgdir name="3">
<imgdir name="link">
<string name="linkMap" value="WorldMap014"/>
<canvas name="linkImg" width="110" height="86">
<vector name="origin" x="202" y="-113"/>
<int name="z" value="1"/>
</canvas>
</imgdir>
<string name="toolTip" value="Mushroom Castle"/>
</imgdir>
</imgdir> </imgdir>
</imgdir> </imgdir>
+163
View File
@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imgdir name="WorldMap014.img">
<imgdir name="info">
<string name="parentMap" value="WorldMap010"/>
</imgdir>
<imgdir name="BaseImg">
<canvas name="0" width="642" height="472">
<vector name="origin" x="321" y="236"/>
<int name="z" value="0"/>
</canvas>
</imgdir>
<imgdir name="MapList">
<imgdir name="0">
<vector name="spot" x="-264" y="160"/>
<int name="type" value="0"/>
<imgdir name="mapNo">
<int name="0" value="106020000"/>
</imgdir>
</imgdir>
<imgdir name="1">
<vector name="spot" x="-226" y="140"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106020100"/>
</imgdir>
</imgdir>
<imgdir name="2">
<vector name="spot" x="-208" y="121"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106020200"/>
</imgdir>
</imgdir>
<imgdir name="3">
<vector name="spot" x="-190" y="97"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106020300"/>
</imgdir>
</imgdir>
<imgdir name="4">
<vector name="spot" x="-156" y="90"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106020400"/>
</imgdir>
</imgdir>
<imgdir name="5">
<vector name="spot" x="-149" y="60"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106020500"/>
<int name="1" value="106020501"/>
</imgdir>
</imgdir>
<imgdir name="6">
<vector name="spot" x="-139" y="116"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106020401"/>
</imgdir>
</imgdir>
<imgdir name="7">
<vector name="spot" x="-118" y="140"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106020402"/>
</imgdir>
</imgdir>
<imgdir name="8">
<vector name="spot" x="-84" y="158"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106020403"/>
</imgdir>
</imgdir>
<imgdir name="9">
<vector name="spot" x="-129" y="-34"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106020600"/>
</imgdir>
</imgdir>
<imgdir name="10">
<vector name="spot" x="-100" y="-24"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106020700"/>
</imgdir>
</imgdir>
<imgdir name="11">
<vector name="spot" x="-82" y="-37"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106020800"/>
</imgdir>
</imgdir>
<imgdir name="12">
<vector name="spot" x="-71" y="-12"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106021000"/>
</imgdir>
</imgdir>
<imgdir name="13">
<vector name="spot" x="-40" y="0"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106021100"/>
</imgdir>
</imgdir>
<imgdir name="14">
<vector name="spot" x="-4" y="15"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106021200"/>
</imgdir>
</imgdir>
<imgdir name="15">
<vector name="spot" x="29" y="28"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106021300"/>
</imgdir>
</imgdir>
<imgdir name="16">
<vector name="spot" x="88" y="30"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106021400"/>
</imgdir>
</imgdir>
<imgdir name="17">
<vector name="spot" x="135" y="-15"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106021401"/>
</imgdir>
</imgdir>
<imgdir name="18">
<vector name="spot" x="131" y="-56"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106021402"/>
</imgdir>
</imgdir>
<imgdir name="19">
<vector name="spot" x="194" y="-97"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106021600"/>
<int name="1" value="106021700"/>
</imgdir>
</imgdir>
<imgdir name="20">
<vector name="spot" x="261" y="-32"/>
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="106021800"/>
</imgdir>
</imgdir>
</imgdir>
</imgdir>
+33
View File
@@ -602,6 +602,29 @@
<int name="2" value="220000111"/> <int name="2" value="220000111"/>
</imgdir> </imgdir>
</imgdir> </imgdir>
<imgdir name="72">
<imgdir name="mapNo">
<int name="0" value="300000000"/>
<int name="1" value="300000010"/>
<int name="2" value="300000011"/>
<int name="3" value="300000012"/>
<int name="4" value="300000001"/>
<int name="5" value="300000002"/>
<int name="6" value="300000100"/>
<int name="7" value="300010000"/>
<int name="8" value="300010100"/>
<int name="9" value="300010200"/>
<int name="10" value="300010300"/>
<int name="11" value="300010400"/>
<int name="12" value="300020000"/>
<int name="13" value="300020100"/>
<int name="14" value="300020200"/>
<int name="15" value="300030000"/>
<int name="16" value="300030100"/>
</imgdir>
<int name="type" value="2"/>
<vector name="spot" x="271" y="-155"/>
</imgdir>
</imgdir> </imgdir>
<imgdir name="MapLink"> <imgdir name="MapLink">
<imgdir name="0"> <imgdir name="0">
@@ -614,5 +637,15 @@
</imgdir> </imgdir>
<string name="toolTip" value="Lowest Floor of Clocktower"/> <string name="toolTip" value="Lowest Floor of Clocktower"/>
</imgdir> </imgdir>
<imgdir name="1">
<imgdir name="link">
<string name="linkMap" value="WorldMap032"/>
<canvas name="linkImg" width="166" height="123">
<vector name="origin" x="-155" y="234"/>
<int name="z" value="1"/>
</canvas>
</imgdir>
<string name="toolTip" value="Ellin Forest"/>
</imgdir>
</imgdir> </imgdir>
</imgdir> </imgdir>
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imgdir name="WorldMap031.img"> <imgdir name="WorldMap031.img">
<imgdir name="info"> <imgdir name="info">
<string name="parentMap" value="WorldMap30"/> <string name="parentMap" value="WorldMap030"/>
</imgdir> </imgdir>
<imgdir name="BaseImg"> <imgdir name="BaseImg">
<canvas name="0" width="640" height="470"> <canvas name="0" width="640" height="470">
+103
View File
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imgdir name="WorldMap032.img">
<imgdir name="BaseImg">
<canvas name="0" width="640" height="470">
<vector name="origin" x="320" y="235"/>
<int name="z" value="0"/>
</canvas>
</imgdir>
<imgdir name="info">
<string name="parentMap" value="WorldMap030"/>
</imgdir>
<imgdir name="MapList">
<imgdir name="0">
<int name="type" value="0"/>
<imgdir name="mapNo">
<int name="0" value="300000000"/>
<int name="1" value="300000001"/>
<int name="2" value="300000002"/>
<int name="3" value="300000010"/>
<int name="4" value="300000011"/>
<int name="5" value="300000012"/>
</imgdir>
<vector name="spot" x="-28" y="19"/>
</imgdir>
<imgdir name="1">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="300000100"/>
</imgdir>
<vector name="spot" x="119" y="-88"/>
</imgdir>
<imgdir name="2">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="300010000"/>
</imgdir>
<vector name="spot" x="-143" y="-43"/>
</imgdir>
<imgdir name="3">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="300010100"/>
</imgdir>
<vector name="spot" x="-136" y="11"/>
</imgdir>
<imgdir name="4">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="300010200"/>
</imgdir>
<vector name="spot" x="-95" y="52"/>
</imgdir>
<imgdir name="5">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="300010300"/>
</imgdir>
<vector name="spot" x="-123" y="96"/>
</imgdir>
<imgdir name="6">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="300010400"/>
</imgdir>
<vector name="spot" x="-191" y="80"/>
</imgdir>
<imgdir name="7">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="300020000"/>
</imgdir>
<vector name="spot" x="-55" y="123"/>
</imgdir>
<imgdir name="8">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="300020100"/>
</imgdir>
<vector name="spot" x="8" y="158"/>
</imgdir>
<imgdir name="9">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="300020200"/>
</imgdir>
<vector name="spot" x="15" y="208"/>
</imgdir>
<imgdir name="10">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="300030000"/>
</imgdir>
<vector name="spot" x="143" y="10"/>
</imgdir>
<imgdir name="11">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="300030100"/>
</imgdir>
<vector name="spot" x="236" y="-71"/>
</imgdir>
</imgdir>
</imgdir>
+111 -28
View File
@@ -2,6 +2,9 @@
<imgdir name="WorldMap140.img"> <imgdir name="WorldMap140.img">
<imgdir name="info"> <imgdir name="info">
<string name="parentMap" value="WorldMap"/> <string name="parentMap" value="WorldMap"/>
<string name="disclaimer" value="HeavenMS rev227:
Added world maps for Mushroom Castle, Zipangu, CBD/Malaysia and Ellin Forest regions. Original artwork content used on files depicted in this topic are rightful property of Nexon Corps., these files thoroughly trying to adhere the &quot;Fair Use&quot; disclaimer policy, their purpose being solely to fulfill gaming experience for the areas that were already present on v83 GMS but still lacked worldmaps. For more info regarding Fair Use, please refer to http://www.dmlp.org/legal-guide/fair-use."/>
</imgdir> </imgdir>
<imgdir name="BaseImg"> <imgdir name="BaseImg">
<canvas name="0" width="640" height="470"> <canvas name="0" width="640" height="470">
@@ -11,56 +14,136 @@
</imgdir> </imgdir>
<imgdir name="MapList"> <imgdir name="MapList">
<imgdir name="0"> <imgdir name="0">
<vector name="spot" x="-118" y="70"/> <int name="type" value="2"/>
<int name="type" value="0"/>
<imgdir name="mapNo"> <imgdir name="mapNo">
<int name="0" value="680000000"/> <int name="0" value="540000000"/>
<int name="1" value="680000001"/> <int name="1" value="540000100"/>
<int name="2" value="680000002"/> <int name="2" value="540000200"/>
<int name="3" value="680000003"/> <int name="3" value="540000300"/>
<int name="4" value="680000100"/> <int name="4" value="540010000"/>
<int name="5" value="680000110"/> <int name="5" value="540020000"/>
<int name="6" value="680000200"/> <int name="6" value="540020100"/>
<int name="7" value="680000210"/>
<int name="8" value="680000300"/>
<int name="9" value="680000400"/>
<int name="10" value="680000401"/>
</imgdir> </imgdir>
<vector name="spot" x="-29" y="142"/>
</imgdir> </imgdir>
<imgdir name="1"> <imgdir name="1">
<vector name="spot" x="-45" y="79"/>
<int name="type" value="1"/>
<imgdir name="mapNo"> <imgdir name="mapNo">
<int name="0" value="680010000"/> <int name="0" value="541000000"/>
<int name="1" value="541000100"/>
<int name="10" value="541010060"/>
<int name="11" value="541010100"/>
<int name="12" value="541010110"/>
<int name="2" value="541000200"/>
<int name="3" value="541000300"/>
<int name="4" value="541010000"/>
<int name="5" value="541010010"/>
<int name="6" value="541010020"/>
<int name="7" value="541010030"/>
<int name="8" value="541010040"/>
<int name="9" value="541010050"/>
</imgdir> </imgdir>
<vector name="spot" x="29" y="162"/>
<int name="type" value="2"/>
</imgdir> </imgdir>
<imgdir name="2"> <imgdir name="2">
<vector name="spot" x="8" y="34"/>
<int name="type" value="1"/>
<imgdir name="mapNo"> <imgdir name="mapNo">
<int name="0" value="680010100"/> <int name="0" value="550000000"/>
<int name="1" value="550000100"/>
<int name="2" value="550000200"/>
<int name="3" value="550000300"/>
<int name="4" value="550000400"/>
</imgdir> </imgdir>
<vector name="spot" x="-210" y="70"/>
<int name="type" value="2"/>
</imgdir> </imgdir>
<imgdir name="3"> <imgdir name="3">
<vector name="spot" x="24" y="-25"/>
<int name="type" value="1"/>
<imgdir name="mapNo"> <imgdir name="mapNo">
<int name="0" value="670000100"/> <int name="0" value="551000000"/>
<int name="1" value="551000100"/>
<int name="2" value="551000200"/>
<int name="3" value="551010000"/>
<int name="4" value="551020000"/>
<int name="5" value="551030000"/>
<int name="6" value="551030100"/>
<int name="7" value="551030200"/>
</imgdir> </imgdir>
<vector name="spot" x="-94" y="74"/>
<int name="type" value="2"/>
</imgdir> </imgdir>
<imgdir name="4"> <imgdir name="4">
<vector name="spot" x="49" y="-87"/>
<int name="type" value="1"/>
<imgdir name="mapNo"> <imgdir name="mapNo">
<int name="0" value="670000200"/> <int name="0" value="800000000"/>
<int name="1" value="800010000"/>
<int name="10" value="800020200"/>
<int name="11" value="800020300"/>
<int name="12" value="800030000"/>
<int name="2" value="800010001"/>
<int name="3" value="800010100"/>
<int name="4" value="800020000"/>
<int name="5" value="800020100"/>
<int name="6" value="800020101"/>
<int name="7" value="800020110"/>
<int name="8" value="800020120"/>
<int name="9" value="800020130"/>
</imgdir> </imgdir>
<vector name="spot" x="24" y="-32"/>
<int name="type" value="2"/>
</imgdir> </imgdir>
<imgdir name="5"> <imgdir name="5">
<vector name="spot" x="107" y="-121"/>
<int name="type" value="1"/>
<imgdir name="mapNo"> <imgdir name="mapNo">
<int name="0" value="670010000"/> <int name="0" value="801000000"/>
<int name="1" value="801000001"/>
<int name="10" value="801030000"/>
<int name="11" value="801040000"/>
<int name="12" value="801040001"/>
<int name="13" value="801040002"/>
<int name="14" value="801040003"/>
<int name="15" value="801040004"/>
<int name="16" value="801040100"/>
<int name="17" value="801040101"/>
<int name="2" value="801000002"/>
<int name="3" value="801000100"/>
<int name="4" value="801000110"/>
<int name="5" value="801000200"/>
<int name="6" value="801000210"/>
<int name="7" value="801000300"/>
<int name="8" value="801010000"/>
<int name="9" value="801020000"/>
</imgdir> </imgdir>
<vector name="spot" x="104" y="-92"/>
<int name="type" value="2"/>
</imgdir>
</imgdir>
<imgdir name="MapLink">
<imgdir name="0">
<imgdir name="link">
<string name="linkMap" value="WorldMap220"/>
<canvas name="linkImg" width="152" height="83">
<int name="z" value="1"/>
<vector name="origin" x="67" y="-117"/>
</canvas>
</imgdir>
<string name="toolTip" value="Singapore"/>
</imgdir>
<imgdir name="1">
<imgdir name="link">
<string name="linkMap" value="WorldMap230"/>
<canvas name="linkImg" width="194" height="132">
<int name="z" value="1"/>
<vector name="origin" x="260" y="-23"/>
</canvas>
</imgdir>
<string name="toolTip" value="Malaysia"/>
</imgdir>
<imgdir name="2">
<imgdir name="link">
<string name="linkMap" value="WorldMap210"/>
<canvas name="linkImg" width="253" height="195">
<int name="z" value="1"/>
<vector name="origin" x="68" y="164"/>
</canvas>
</imgdir>
<string name="toolTip" value="Japan"/>
</imgdir> </imgdir>
</imgdir> </imgdir>
</imgdir> </imgdir>
+133
View File
@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imgdir name="WorldMap210.img">
<imgdir name="BaseImg">
<canvas name="0" width="640" height="470">
<vector name="origin" x="320" y="235"/>
<int name="z" value="0"/>
</canvas>
</imgdir>
<imgdir name="info">
<string name="parentMap" value="WorldMap140"/>
</imgdir>
<imgdir name="MapLink">
<imgdir name="0">
<imgdir name="link">
<canvas name="linkImg" width="103" height="100">
<vector name="origin" x="-2" y="116"/>
<int name="z" value="1"/>
</canvas>
<string name="linkMap" value="WorldMap211"/>
</imgdir>
<string name="toolTip" value="Showa"/>
</imgdir>
</imgdir>
<imgdir name="MapList">
<imgdir name="2">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="800010001"/>
</imgdir>
<vector name="spot" x="-7" y="-7"/>
</imgdir>
<imgdir name="0">
<int name="type" value="0"/>
<imgdir name="mapNo">
<int name="0" value="800000000"/>
</imgdir>
<vector name="spot" x="-68" y="-14"/>
</imgdir>
<imgdir name="3">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="800020000"/>
</imgdir>
<vector name="spot" x="25" y="63"/>
</imgdir>
<imgdir name="4">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="800020100"/>
</imgdir>
<vector name="spot" x="163" y="144"/>
</imgdir>
<imgdir name="5">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="800020101"/>
</imgdir>
<vector name="spot" x="51" y="6"/>
</imgdir>
<imgdir name="6">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="800020110"/>
</imgdir>
<vector name="spot" x="90" y="76"/>
</imgdir>
<imgdir name="7">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="800020120"/>
</imgdir>
<vector name="spot" x="113" y="25"/>
</imgdir>
<imgdir name="8">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="800020130"/>
</imgdir>
<vector name="spot" x="137" y="-41"/>
</imgdir>
<imgdir name="9">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="800020200"/>
</imgdir>
<vector name="spot" x="253" y="81"/>
</imgdir>
<imgdir name="10">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="800020300"/>
</imgdir>
<vector name="spot" x="286" y="43"/>
</imgdir>
<imgdir name="11">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="800030000"/>
</imgdir>
<vector name="spot" x="-14" y="-25"/>
</imgdir>
<imgdir name="12">
<int name="type" value="2"/>
<imgdir name="mapNo">
<int name="0" value="801000000"/>
<int name="1" value="801000001"/>
<int name="2" value="801000002"/>
<int name="3" value="801000100"/>
<int name="4" value="801000110"/>
<int name="5" value="801000200"/>
<int name="6" value="801000210"/>
<int name="7" value="801000300"/>
<int name="8" value="801010000"/>
<int name="9" value="801020000"/>
<int name="10" value="801030000"/>
<int name="11" value="801040000"/>
<int name="12" value="801040001"/>
<int name="13" value="801040002"/>
<int name="14" value="801040003"/>
<int name="15" value="801040004"/>
</imgdir>
<vector name="spot" x="35" y="-55"/>
</imgdir>
<imgdir name="1">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="800010000"/>
<int name="1" value="800010100"/>
</imgdir>
<vector name="spot" x="-23" y="33"/>
</imgdir>
</imgdir>
</imgdir>
+98
View File
@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imgdir name="WorldMap211.img">
<imgdir name="BaseImg">
<canvas name="0" width="640" height="470">
<vector name="origin" x="320" y="235"/>
<int name="z" value="0"/>
</canvas>
</imgdir>
<imgdir name="info">
<string name="parentMap" value="WorldMap210"/>
</imgdir>
<imgdir name="MapList">
<imgdir name="0">
<int name="type" value="0"/>
<imgdir name="mapNo">
<int name="0" value="801000000"/>
<int name="1" value="801000001"/>
<int name="2" value="801000002"/>
<int name="3" value="801000100"/>
<int name="4" value="801000110"/>
<int name="5" value="801000200"/>
<int name="6" value="801000210"/>
</imgdir>
<vector name="spot" x="23" y="26"/>
</imgdir>
<imgdir name="1">
<int name="type" value="3"/>
<imgdir name="mapNo">
<int name="0" value="801000300"/>
</imgdir>
<vector name="spot" x="133" y="51"/>
</imgdir>
<imgdir name="2">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="801010000"/>
</imgdir>
<vector name="spot" x="-51" y="-37"/>
</imgdir>
<imgdir name="3">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="801020000"/>
</imgdir>
<vector name="spot" x="-42" y="-110"/>
</imgdir>
<imgdir name="4">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="801030000"/>
</imgdir>
<vector name="spot" x="-136" y="-173"/>
</imgdir>
<imgdir name="5">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="801040000"/>
<int name="1" value="801040101"/>
</imgdir>
<vector name="spot" x="4" y="179"/>
</imgdir>
<imgdir name="6">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="801040001"/>
</imgdir>
<vector name="spot" x="50" y="179"/>
</imgdir>
<imgdir name="7">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="801040002"/>
</imgdir>
<vector name="spot" x="50" y="153"/>
</imgdir>
<imgdir name="8">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="801040003"/>
</imgdir>
<vector name="spot" x="50" y="127"/>
</imgdir>
<imgdir name="9">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="801040004"/>
</imgdir>
<vector name="spot" x="50" y="101"/>
</imgdir>
<imgdir name="10">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="801040100"/>
</imgdir>
<vector name="spot" x="97" y="74"/>
</imgdir>
</imgdir>
</imgdir>
+100
View File
@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imgdir name="WorldMap220.img">
<imgdir name="BaseImg">
<canvas name="0" width="640" height="470">
<vector name="origin" x="320" y="235"/>
<int name="z" value="0"/>
</canvas>
</imgdir>
<imgdir name="info">
<string name="parentMap" value="WorldMap140"/>
</imgdir>
<imgdir name="MapList">
<imgdir name="1">
<imgdir name="mapNo">
<int name="0" value="540000100"/>
</imgdir>
<int name="type" value="1"/>
<vector name="spot" x="-156" y="50"/>
</imgdir>
<imgdir name="2">
<imgdir name="mapNo">
<int name="0" value="540000200"/>
</imgdir>
<int name="type" value="1"/>
<vector name="spot" x="-90" y="82"/>
</imgdir>
<imgdir name="3">
<imgdir name="mapNo">
<int name="0" value="540000300"/>
</imgdir>
<int name="type" value="1"/>
<vector name="spot" x="-39" y="80"/>
</imgdir>
<imgdir name="4">
<imgdir name="mapNo">
<int name="0" value="540020000"/>
</imgdir>
<int name="type" value="1"/>
<vector name="spot" x="1" y="58"/>
</imgdir>
<imgdir name="5">
<imgdir name="mapNo">
<int name="0" value="540020100"/>
</imgdir>
<int name="type" value="1"/>
<vector name="spot" x="1" y="21"/>
</imgdir>
<imgdir name="6">
<imgdir name="mapNo">
<int name="0" value="541000000"/>
</imgdir>
<int name="type" value="0"/>
<vector name="spot" x="55" y="70"/>
</imgdir>
<imgdir name="7">
<imgdir name="mapNo">
<int name="0" value="541000100"/>
</imgdir>
<int name="type" value="1"/>
<vector name="spot" x="112" y="54"/>
</imgdir>
<imgdir name="8">
<imgdir name="mapNo">
<int name="0" value="541000200"/>
</imgdir>
<int name="type" value="1"/>
<vector name="spot" x="141" y="37"/>
</imgdir>
<imgdir name="9">
<imgdir name="mapNo">
<int name="0" value="541000300"/>
</imgdir>
<int name="type" value="1"/>
<vector name="spot" x="171" y="21"/>
</imgdir>
<imgdir name="10">
<imgdir name="mapNo">
<int name="0" value="541010000"/>
<int name="1" value="541010010"/>
<int name="2" value="541010020"/>
<int name="3" value="541010030"/>
<int name="4" value="541010040"/>
<int name="5" value="541010050"/>
<int name="6" value="541010060"/>
<int name="7" value="541010100"/>
<int name="8" value="541010110"/>
</imgdir>
<int name="type" value="2"/>
<vector name="spot" x="237" y="17"/>
</imgdir>
<imgdir name="0">
<int name="type" value="0"/>
<imgdir name="mapNo">
<int name="0" value="540000000"/>
<int name="1" value="540010000"/>
</imgdir>
<vector name="spot" x="-204" y="-21"/>
</imgdir>
</imgdir>
</imgdir>
+105
View File
@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imgdir name="WorldMap230.img">
<imgdir name="BaseImg">
<canvas name="0" width="640" height="470">
<vector name="origin" x="320" y="235"/>
<int name="z" value="0"/>
</canvas>
</imgdir>
<imgdir name="info">
<string name="parentMap" value="WorldMap140"/>
</imgdir>
<imgdir name="MapList">
<imgdir name="0">
<int name="type" value="0"/>
<imgdir name="mapNo">
<int name="0" value="550000000"/>
</imgdir>
<vector name="spot" x="-258" y="-4"/>
</imgdir>
<imgdir name="1">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="550000100"/>
</imgdir>
<vector name="spot" x="34" y="-104"/>
</imgdir>
<imgdir name="2">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="550000200"/>
</imgdir>
<vector name="spot" x="75" y="-123"/>
</imgdir>
<imgdir name="3">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="550000300"/>
</imgdir>
<vector name="spot" x="123" y="-127"/>
</imgdir>
<imgdir name="6">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="551000100"/>
</imgdir>
<vector name="spot" x="219" y="-72"/>
</imgdir>
<imgdir name="5">
<int name="type" value="0"/>
<imgdir name="mapNo">
<int name="0" value="551000000"/>
</imgdir>
<vector name="spot" x="201" y="-96"/>
</imgdir>
<imgdir name="9">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="551020000"/>
</imgdir>
<vector name="spot" x="69" y="41"/>
</imgdir>
<imgdir name="10">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="551030000"/>
</imgdir>
<vector name="spot" x="21" y="73"/>
</imgdir>
<imgdir name="11">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="551030100"/>
</imgdir>
<vector name="spot" x="80" y="121"/>
</imgdir>
<imgdir name="12">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="551030200"/>
</imgdir>
<vector name="spot" x="194" y="120"/>
</imgdir>
<imgdir name="4">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="550000400"/>
</imgdir>
<vector name="spot" x="158" y="-120"/>
</imgdir>
<imgdir name="8">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="551010000"/>
</imgdir>
<vector name="spot" x="158" y="35"/>
</imgdir>
<imgdir name="7">
<int name="type" value="1"/>
<imgdir name="mapNo">
<int name="0" value="551000200"/>
</imgdir>
<vector name="spot" x="209" y="17"/>
</imgdir>
</imgdir>
</imgdir>
+2 -2
View File
@@ -55982,8 +55982,8 @@
<int name="id" value="4000295"/> <int name="id" value="4000295"/>
</imgdir> </imgdir>
<imgdir name="4"> <imgdir name="4">
<int name="count" value="-30"/> <int name="count" value="-15"/>
<int name="id" value="4001372"/> <int name="id" value="4000030"/>
</imgdir> </imgdir>
<imgdir name="5"> <imgdir name="5">
<int name="count" value="-10"/> <int name="count" value="-10"/>
+2 -2
View File
@@ -76673,8 +76673,8 @@
<int name="count" value="30"/> <int name="count" value="30"/>
</imgdir> </imgdir>
<imgdir name="4"> <imgdir name="4">
<int name="count" value="30"/> <int name="count" value="15"/>
<int name="id" value="4001372"/> <int name="id" value="4000030"/>
</imgdir> </imgdir>
<imgdir name="5"> <imgdir name="5">
<int name="id" value="4032475"/> <int name="id" value="4032475"/>
+1 -1
View File
@@ -10317,7 +10317,7 @@ Able to proceed to &apos;For the peace of Victoria Island...&apos; as next quest
<string name="name" value="Hunter&apos;s Profission"/> <string name="name" value="Hunter&apos;s Profission"/>
<int name="area" value="50"/> <int name="area" value="50"/>
<string name="0" value="Scadur seems up to something big this time. I should move to El Nath and see what he is looking for..."/> <string name="0" value="Scadur seems up to something big this time. I should move to El Nath and see what he is looking for..."/>
<string name="1" value="So Scadur is going to set-up a &quot;Leather Festival&quot; on El Nath... I&apos;m out to find and collect all these items for him. Hope the reward of this mission is really worth it...\n\n#i4000021# #t4000021# #c4000021# / #r50#k\n#i4000264# #t4000264# #c4000264# / #r10#k\n#i4000281# #t4000281# #c4000281# / #r10#k\n#i4000295# #t4000295# #c4000295# / #r10#k\n#i4001372# #t4001372# #c4001372# / #r10#k\n#i4032475# #t4032475# #c4032475# / #r5#k"/> <string name="1" value="So Scadur is going to set-up a &quot;Leather Festival&quot; on El Nath... I&apos;m out to find and collect all these items for him. Hope the reward of this mission is really worth it...\n\n#i4000021# #t4000021# #c4000021# / #r60#k\n#i4000264# #t4000264# #c4000264# / #r30#k\n#i4000281# #t4000281# #c4000281# / #r30#k\n#i4000295# #t4000295# #c4000295# / #r30#k\n#i4000030# #t4000030# #c4000030# / #r15#k\n#i4032475# #t4032475# #c4032475# / #r10#k"/>
<string name="2" value="I helped Scadur in his &quot;Leather Festival&quot;, held on El Nath."/> <string name="2" value="I helped Scadur in his &quot;Leather Festival&quot;, held on El Nath."/>
</imgdir> </imgdir>
<imgdir name="29000"> <imgdir name="29000">
+1 -1
View File
@@ -64772,7 +64772,7 @@
<string name="0" value="The new hunting season has begun! I am planning to set-up a stand here at the El-Nath Market, selling #rleathers#k of the most fine quality. And for this, I am counting on you to bring all these leathers here, obviously for a #bvery nice reward#k. If you miss that season, you&apos;re not a serious hunter. It&apos;s that important. You know, I can give you a special tip on how to hunt effectively. Are you interested?"/> <string name="0" value="The new hunting season has begun! I am planning to set-up a stand here at the El-Nath Market, selling #rleathers#k of the most fine quality. And for this, I am counting on you to bring all these leathers here, obviously for a #bvery nice reward#k. If you miss that season, you&apos;re not a serious hunter. It&apos;s that important. You know, I can give you a special tip on how to hunt effectively. Are you interested?"/>
<imgdir name="yes"> <imgdir name="yes">
<string name="0" value="Oh hoho... Surely you do. As a hunter in training, you should get accostumed with the uncertainties that surround us. So, take this list of leathers I want you to bring, and bring them here, ok? If you pass this test, you will not regret it!"/> <string name="0" value="Oh hoho... Surely you do. As a hunter in training, you should get accostumed with the uncertainties that surround us. So, take this list of leathers I want you to bring, and bring them here, ok? If you pass this test, you will not regret it!"/>
<string name="1" value="You will need firstly regular #b#t4000021##k in a bunch of 60. In #rLeafre#k, hunt for some #b#t4000264##k. In #rMu Lung#k, hunt for some #b#t4000281##k and #b#t4000295##k, these are widely appreciated around here, and quite rare to find. In #rVictoria Island#k, hunt some #b#t4001372##k, despite their pungent smell they are quite the spice."/> <string name="1" value="You will need firstly regular #b#t4000021##k in a bunch of 60. In #rLeafre#k, hunt for some #b#t4000264##k. In #rMu Lung#k, hunt for some #b#t4000281##k and #b#t4000295##k, these are widely appreciated around here, and quite rare to find. In #rVictoria Island#k, hunt some #b#t4000030##k, their tough yet flexible texture form quite the spice."/>
<string name="2" value="Finally, #b#t4032475##k. Normally their leather are not very consistent. However, once we are in open hunt season, they have some utility for adorning things. Said all that, good luck on your hunt and be safe."/> <string name="2" value="Finally, #b#t4032475##k. Normally their leather are not very consistent. However, once we are in open hunt season, they have some utility for adorning things. Said all that, good luck on your hunt and be safe."/>
</imgdir> </imgdir>
<imgdir name="no"> <imgdir name="no">
+6 -2
View File
@@ -1039,12 +1039,16 @@
<vector name="origin" x="90" y="162"/> <vector name="origin" x="90" y="162"/>
<int name="delay" value="70"/> <int name="delay" value="70"/>
</canvas> </canvas>
<canvas name="14" width="127" height="274">
<int name="delay" value="70"/>
<vector name="origin" x="88" y="167"/>
</canvas>
<string name="action" value="shoot1"/>
<int name="time" value="960"/>
<canvas name="13" width="127" height="268"> <canvas name="13" width="127" height="268">
<vector name="origin" x="89" y="164"/> <vector name="origin" x="89" y="164"/>
<int name="delay" value="70"/> <int name="delay" value="70"/>
</canvas> </canvas>
<string name="action" value="shoot1"/>
<int name="time" value="960"/>
</imgdir> </imgdir>
<imgdir name="keydown"> <imgdir name="keydown">
<canvas name="0" width="127" height="276"> <canvas name="0" width="127" height="276">
+8 -8
View File
@@ -1560,20 +1560,20 @@
<string name="mapName" value="Hot Sand"/> <string name="mapName" value="Hot Sand"/>
</imgdir> </imgdir>
<imgdir name="180000000"> <imgdir name="180000000">
<string name="streetName" value="Victoria Road"/> <string name="streetName" value="Hidden Street"/>
<string name="mapName" value="Ellinia"/> <string name="mapName" value="GM Room"/>
</imgdir> </imgdir>
<imgdir name="180000001"> <imgdir name="180000001">
<string name="streetName" value="Victoria Road"/> <string name="streetName" value="Hidden Street"/>
<string name="mapName" value="Ellinia"/> <string name="mapName" value="GM Room"/>
</imgdir> </imgdir>
<imgdir name="180000002"> <imgdir name="180000002">
<string name="streetName" value="Victoria Road"/> <string name="streetName" value="Hidden Street"/>
<string name="mapName" value="Ellinia"/> <string name="mapName" value="GM Room"/>
</imgdir> </imgdir>
<imgdir name="180000003"> <imgdir name="180000003">
<string name="streetName" value="Victoria Road"/> <string name="streetName" value="Hidden Street"/>
<string name="mapName" value="Ellinia"/> <string name="mapName" value="GM Room"/>
</imgdir> </imgdir>
<imgdir name="190000000"> <imgdir name="190000000">
<string name="streetName" value="Premium Road"/> <string name="streetName" value="Premium Road"/>