/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 ~ 2010 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License version 3
as published by the Free Software Foundation. You may not use, modify
or distribute this program under any other version of the
GNU Affero General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package server.maps;
import com.mysql.jdbc.Connection;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.io.File;
import constants.GameConstants;
import custom.CustomConstants;
import database.DatabaseConnection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import server.MaplePortal;
import server.Randomizer;
import server.life.AbstractLoadedMapleLife;
import server.life.MapleLifeFactory;
import server.life.MapleMonster;
import server.life.MapleNPC;
import server.maps.MapleNodes.DirectionInfo;
import server.maps.MapleNodes.MapleNodeInfo;
import server.maps.MapleNodes.MaplePlatform;
import tools.Pair;
import tools.StringUtil;
public class MapleMapFactory {
private final MapleDataProvider source = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz"));
private final MapleData nameData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz")).getData("Map.img");
private final HashMap<Integer, MapleMap> maps = new HashMap<>();
private final HashMap<Integer, MapleMap> instanceMap = new HashMap<>();
private final ReentrantLock lock = new ReentrantLock();
private static final Map<Integer, List<AbstractLoadedMapleLife>> customLife = new HashMap<>();
private int channel;
public static int loadCustomLife() {
customLife.clear(); // init
try {
Connection con = (Connection) DatabaseConnection.getConnection();
try (java.sql.PreparedStatement ps = con.prepareStatement("SELECT * FROM `wz_customlife`"); ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
final int mapid = rs.getInt("mid");
final AbstractLoadedMapleLife myLife = loadLife(rs.getInt("dataid"), rs.getInt("f"), rs.getByte("hide") > 0, rs.getInt("fh"), rs.getInt("cy"), rs.getInt("rx0"), rs.getInt("rx1"), rs.getInt("x"), rs.getInt("y"), rs.getString("type"), rs.getInt("mobtime"));
if (myLife == null) {
continue;
}
final List<AbstractLoadedMapleLife> entries = customLife.get(mapid);
final List<AbstractLoadedMapleLife> collections = new ArrayList<>();
if (entries == null) {
collections.add(myLife);
customLife.put(mapid, collections);
} else {
collections.addAll(entries); //re-add
collections.add(myLife);
customLife.put(mapid, collections);
}
}
}
loadCustomNPC();
return customLife.size();
//System.out.println("Successfully loaded " + customLife.size() + " maps with custom life.");
} catch (SQLException e) {
System.out.println("Error loading custom life..." + e);
}
return -1;
}
public static void loadCustomNPC() {
List<Pair<Integer, AbstractLoadedMapleLife>> customNPC = new LinkedList();
customNPC.add(new Pair<>(910000000, loadLife(9010038, 0, false, 78, 34, 312, 312, 312, 34, "n", 0)));
customNPC.add(new Pair<>(910000000, loadLife(9010037, 0, false, 46, -266, 932, 932, 932, -266, "n", 0)));
// customNPC.add(new Pair<>(910000000, loadLife(9000019, 0, true, 47, -266, 650, 650, 650, -266, "n", 0)));//cuz RPS no works..
customNPC.add(new Pair<>(910000000, loadLife(9000031, 0, false, 108, -326, 72, 170, 120, -329, "n", 0)));
customNPC.add(new Pair<>(910000000, loadLife(9010029, 0, false, 107, -326, -729, -729, -729, -329, "n", 0)));
// customNPC.add(new Pair<>(910000000, loadLife(1033225, 0, true, 42, -266, 638, 638, 638, -266, "n", 0)));//Shadow Knight cuz not coded..
customNPC.add(new Pair<>(910000000, loadLife(9072000, 0, false, 40, -266, 1098, 1098, 1098, -266, "n", 0)));
customNPC.add(new Pair<>(910000000, loadLife(9010034, 0, false, 59, 4, 749, 749, 749, 4, "n", 0)));
customNPC.add(new Pair<>(910000000, loadLife(9010035, 0, false, 61, 4, 1116, 1116, 1116, 4, "n", 0)));
customNPC.add(new Pair<>(910000000, loadLife(9010036, 0, false, 62, 4, 935, 935, 935, 4, "n", 0)));
customNPC.add(new Pair<>(910000000, loadLife(9201117, 0, false, 45, -266, 1092, 1092, 1092, -266, "n", 0)));
//customNPC.add(new Pair<>(910000000, loadLife(9900002, 0, false, 57, 4, 603, 603, 603, 4, "n", 0)));
//id, face, hide, fh, y, x, x, x, y, "n", 1000
for (int i = 0; i < customNPC.size(); i++) {
final int mapid = customNPC.get(i).getLeft();
final AbstractLoadedMapleLife myLife = customNPC.get(i).getRight();
if (myLife == null) {
continue;
}
final List<AbstractLoadedMapleLife> entries = customLife.get(mapid);
final List<AbstractLoadedMapleLife> collections = new ArrayList<>();
if (entries == null) {
collections.add(myLife);
customLife.put(mapid, collections);
} else {
collections.addAll(entries); //re-add
collections.add(myLife);
customLife.put(mapid, collections);
}
}
}
public MapleMapFactory(int channel) {
this.channel = channel;
}
public final MapleMap getMap(final int mapid) {
return getMap(mapid, true, true, true);
}
private static AbstractLoadedMapleLife loadLife(int id, int f, boolean hide, int fh, int cy, int rx0, int rx1, int x, int y, String type, int mtime) {
final AbstractLoadedMapleLife myLife = MapleLifeFactory.getLife(id, type);
if (myLife == null) {
System.out.println("Custom npc " + id + " is null...");
return null;
}
myLife.setCy(cy);
myLife.setF(f);
myLife.setFh(fh);
myLife.setRx0(rx0);
myLife.setRx1(rx1);
myLife.setPosition(new Point(x, y));
myLife.setHide(hide);
myLife.setMTime(mtime);
myLife.setCType(type);
return myLife;
}
//backwards-compatible
public final MapleMap getMap(final int mapid, final boolean respawns, final boolean npcs) {
return getMap(mapid, respawns, npcs, true);
}
public final MapleMap getMap(final int mapid, final boolean respawns, final boolean npcs, final boolean reactors) {
Integer omapid = Integer.valueOf(mapid);
MapleMap map = maps.get(omapid);
if (map == null) {
lock.lock();
try {
map = maps.get(omapid);
if (map != null) {
return map;
}
MapleData mapData = null;
try {
mapData = source.getData(getMapName(mapid));
} catch (Exception e) {
return null;
}
if (mapData == null) {
return null;
}
MapleData link = mapData.getChildByPath("info/link");
if (link != null) {
mapData = source.getData(getMapName(MapleDataTool.getIntConvert("info/link", mapData)));
}
float monsterRate = 0;
if (respawns) {
MapleData mobRate = mapData.getChildByPath("info/mobRate");
if (mobRate != null) {
monsterRate = ((Float) mobRate.getData()).floatValue();
}
}
map = new MapleMap(mapid, channel, MapleDataTool.getInt("info/returnMap", mapData), monsterRate);
loadPortals(map, mapData.getChildByPath("portal"));
map.setTop(MapleDataTool.getInt(mapData.getChildByPath("info/VRTop"), 0));
map.setLeft(MapleDataTool.getInt(mapData.getChildByPath("info/VRLeft"), 0));
map.setBottom(MapleDataTool.getInt(mapData.getChildByPath("info/VRBottom"), 0));
map.setRight(MapleDataTool.getInt(mapData.getChildByPath("info/VRRight"), 0));
List<MapleFoothold> allFootholds = new LinkedList<>();
Point lBound = new Point();
Point uBound = new Point();
MapleFoothold fh;
for (MapleData footRoot : mapData.getChildByPath("foothold")) {
for (MapleData footCat : footRoot) {
for (MapleData footHold : footCat) {
fh = new MapleFoothold(new Point(
MapleDataTool.getInt(footHold.getChildByPath("x1"), 0), MapleDataTool.getInt(footHold.getChildByPath("y1"), 0)), new Point(
MapleDataTool.getInt(footHold.getChildByPath("x2"), 0), MapleDataTool.getInt(footHold.getChildByPath("y2"), 0)), Integer.parseInt(footHold.getName()));
fh.setPrev((short) MapleDataTool.getInt(footHold.getChildByPath("prev"), 0));
fh.setNext((short) MapleDataTool.getInt(footHold.getChildByPath("next"), 0));
if (fh.getX1() < lBound.x) {
lBound.x = fh.getX1();
}
if (fh.getX2() > uBound.x) {
uBound.x = fh.getX2();
}
if (fh.getY1() < lBound.y) {
lBound.y = fh.getY1();
}
if (fh.getY2() > uBound.y) {
uBound.y = fh.getY2();
}
allFootholds.add(fh);
}
}
}
MapleFootholdTree fTree = new MapleFootholdTree(lBound, uBound);
for (MapleFoothold foothold : allFootholds) {
fTree.insert(foothold);
}
map.setFootholds(fTree);
if (map.getTop() == 0) {
map.setTop(lBound.y);
}
if (map.getBottom() == 0) {
map.setBottom(uBound.y);
}
if (map.getLeft() == 0) {
map.setLeft(lBound.x);
}
if (map.getRight() == 0) {
map.setRight(uBound.x);
}
int bossid = -1;
String msg = null;
if (mapData.getChildByPath("info/timeMob") != null) {
bossid = MapleDataTool.getInt(mapData.getChildByPath("info/timeMob/id"), 0);
msg = MapleDataTool.getString(mapData.getChildByPath("info/timeMob/message"), null);
}
// load life data (npc, monsters)
List<Point> herbRocks = new ArrayList<>();
int lowestLevel = 200, highestLevel = 0;
String type, limited;
AbstractLoadedMapleLife myLife;
for (MapleData life : mapData.getChildByPath("life")) {
type = MapleDataTool.getString(life.getChildByPath("type"));
limited = MapleDataTool.getString("limitedname", life, "");
if ((npcs || !type.equals("n")) && !limited.equals("Stage0")) { //alien pq stuff
myLife = loadLife(life, MapleDataTool.getString(life.getChildByPath("id")), type);
if (myLife instanceof MapleMonster && !GameConstants.isNoSpawn(mapid)) {
final MapleMonster mob = (MapleMonster) myLife;
herbRocks.add(map.addMonsterSpawn(mob,
MapleDataTool.getInt("mobTime", life, 0),
(byte) MapleDataTool.getInt("team", life, -1),
mob.getId() == bossid ? msg : null).getPosition());
if (mob.getStats().getLevel() > highestLevel && !mob.getStats().isBoss()) {
highestLevel = mob.getStats().getLevel();
}
if (mob.getStats().getLevel() < lowestLevel && !mob.getStats().isBoss()) {
lowestLevel = mob.getStats().getLevel();
}
} else if (myLife instanceof MapleNPC) {
boolean spawn = true;
for (int id : CustomConstants.noSpawnNPC) {
if (myLife.getId() == id) {
spawn = false;
}
}
for (int id : GameConstants.unusedNpcs) {
if (myLife.getId() == id) {
spawn = false;
}
}
if (spawn) {
map.addMapObject(myLife);
}
}
}
final List<AbstractLoadedMapleLife> custom = customLife.get(mapid);
if (custom != null) {
for (AbstractLoadedMapleLife n : custom) {
switch (n.getCType()) {
case "n":
map.addMapObject(n);
break;
case "m":
final MapleMonster monster = (MapleMonster) n;
map.addMonsterSpawn(monster, n.getMTime(), (byte) -1, null);
break;
}
}
}
}
addAreaBossSpawn(map);
map.setCreateMobInterval((short) MapleDataTool.getInt(mapData.getChildByPath("info/createMobInterval"), 9000));
map.setFixedMob(MapleDataTool.getInt(mapData.getChildByPath("info/fixedMobCapacity"), 0));
map.setPartyBonusRate(GameConstants.getPartyPlay(mapid, MapleDataTool.getInt(mapData.getChildByPath("info/partyBonusR"), 0)));
map.loadMonsterRate(true);
map.setNodes(loadNodes(mapid, mapData));
//load reactor data
String id;
if (reactors && mapData.getChildByPath("reactor") != null) {
for (MapleData reactor : mapData.getChildByPath("reactor")) {
id = MapleDataTool.getString(reactor.getChildByPath("id"));
if (id != null) {
map.spawnReactor(loadReactor(reactor, id, (byte) MapleDataTool.getInt(reactor.getChildByPath("f"), 0)));
}
}
}
map.setFirstUserEnter(MapleDataTool.getString(mapData.getChildByPath("info/onFirstUserEnter"), ""));
map.setUserEnter(mapid == GameConstants.JAIL ? "jail" : MapleDataTool.getString(mapData.getChildByPath("info/onUserEnter"), ""));
if (reactors && herbRocks.size() > 0 && highestLevel >= 30 && map.getFirstUserEnter().equals("") && map.getUserEnter().equals("")) {
final List<Integer> allowedSpawn = new ArrayList<>(24);
allowedSpawn.add(100011);
allowedSpawn.add(200011);
if (highestLevel >= 100) {
for (int i = 0; i < 10; i++) {
for (int x = 0; x < 4; x++) { //to make heartstones rare
allowedSpawn.add(100000 + i);
allowedSpawn.add(200000 + i);
}
}
} else {
for (int i = (lowestLevel % 10 > highestLevel % 10 ? 0 : (lowestLevel % 10)); i < (highestLevel % 10); i++) {
for (int x = 0; x < 4; x++) { //to make heartstones rare
allowedSpawn.add(100000 + i);
allowedSpawn.add(200000 + i);
}
}
}
final int numSpawn = Randomizer.nextInt(allowedSpawn.size()) / 6; //0-7
for (int i = 0; i < numSpawn && !herbRocks.isEmpty(); i++) {
final int idd = allowedSpawn.get(Randomizer.nextInt(allowedSpawn.size()));
final int theSpawn = Randomizer.nextInt(herbRocks.size());
final MapleReactor myReactor = new MapleReactor(MapleReactorFactory.getReactor(idd), idd);
myReactor.setPosition(herbRocks.get(theSpawn));
myReactor.setDelay(idd % 100 == 11 ? 60000 : 5000); //in the reactor's wz
map.spawnReactor(myReactor);
herbRocks.remove(theSpawn);
}
}
try {
map.setMapName(MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(omapid)), ""));
map.setStreetName(MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(omapid)), ""));
} catch (Exception e) {
map.setMapName("");
map.setStreetName("");
}
map.setClock(mapData.getChildByPath("clock") != null); //clock was changed in wz to have x,y,width,height
map.setEverlast(MapleDataTool.getInt(mapData.getChildByPath("info/everlast"), 0) > 0);
map.setTown(MapleDataTool.getInt(mapData.getChildByPath("info/town"), 0) > 0);
map.setSoaring(MapleDataTool.getInt(mapData.getChildByPath("info/needSkillForFly"), 0) > 0);
map.setPersonalShop(MapleDataTool.getInt(mapData.getChildByPath("info/personalShop"), 0) > 0);
map.setForceMove(MapleDataTool.getInt(mapData.getChildByPath("info/lvForceMove"), 0));
map.setHPDec(MapleDataTool.getInt(mapData.getChildByPath("info/decHP"), 0));
map.setHPDecInterval(MapleDataTool.getInt(mapData.getChildByPath("info/decHPInterval"), 10000));
map.setHPDecProtect(MapleDataTool.getInt(mapData.getChildByPath("info/protectItem"), 0));
map.setForcedReturnMap(mapid == 0 ? 999999999 : MapleDataTool.getInt(mapData.getChildByPath("info/forcedReturn"), 999999999));
map.setTimeLimit(MapleDataTool.getInt(mapData.getChildByPath("info/timeLimit"), -1));
map.setFieldLimit(MapleDataTool.getInt(mapData.getChildByPath("info/fieldLimit"), 0));
map.setRecoveryRate(MapleDataTool.getFloat(mapData.getChildByPath("info/recovery"), 1));
map.setFixedMob(MapleDataTool.getInt(mapData.getChildByPath("info/fixedMobCapacity"), 0));
map.setFieldType(MapleDataTool.getString(mapData.getChildByPath("info/fieldType"), ""));
map.setPartyBonusRate(GameConstants.getPartyPlay(mapid, MapleDataTool.getInt(mapData.getChildByPath("info/partyBonusR"), 0)));
map.setConsumeItemCoolTime(MapleDataTool.getInt(mapData.getChildByPath("info/consumeItemCoolTime"), 0));
maps.put(omapid, map);
} finally {
lock.unlock();
}
}
return map;
}
public MapleMap getInstanceMap(final int instanceid) {
return instanceMap.get(instanceid);
}
public void removeInstanceMap(final int instanceid) {
lock.lock();
try {
if (isInstanceMapLoaded(instanceid)) {
getInstanceMap(instanceid).checkStates("");
instanceMap.remove(instanceid);
}
} finally {
lock.unlock();
}
}
public void removeMap(final int instanceid) {
lock.lock();
try {
if (isMapLoaded(instanceid)) {
getMap(instanceid).checkStates("");
maps.remove(instanceid);
}
} finally {
lock.unlock();
}
}
public MapleMap CreateInstanceMap(int mapid, boolean respawns, boolean npcs, boolean reactors, int instanceid) {
lock.lock();
try {
if (isInstanceMapLoaded(instanceid)) {
return getInstanceMap(instanceid);
}
} finally {
lock.unlock();
}
MapleData mapData;
try {
mapData = source.getData(getMapName(mapid));
} catch (Exception e) {
return null;
}
if (mapData == null) {
return null;
}
MapleData link = mapData.getChildByPath("info/link");
if (link != null) {
mapData = source.getData(getMapName(MapleDataTool.getIntConvert("info/link", mapData)));
}
float monsterRate = 0;
if (respawns) {
MapleData mobRate = mapData.getChildByPath("info/mobRate");
if (mobRate != null) {
monsterRate = ((Float) mobRate.getData()).floatValue();
}
}
MapleMap map = new MapleMap(mapid, channel, MapleDataTool.getInt("info/returnMap", mapData), monsterRate);
loadPortals(map, mapData.getChildByPath("portal"));
map.setTop(MapleDataTool.getInt(mapData.getChildByPath("info/VRTop"), 0));
map.setLeft(MapleDataTool.getInt(mapData.getChildByPath("info/VRLeft"), 0));
map.setBottom(MapleDataTool.getInt(mapData.getChildByPath("info/VRBottom"), 0));
map.setRight(MapleDataTool.getInt(mapData.getChildByPath("info/VRRight"), 0));
List<MapleFoothold> allFootholds = new LinkedList<>();
Point lBound = new Point();
Point uBound = new Point();
for (MapleData footRoot : mapData.getChildByPath("foothold")) {
for (MapleData footCat : footRoot) {
for (MapleData footHold : footCat) {
MapleFoothold fh = new MapleFoothold(new Point(
MapleDataTool.getInt(footHold.getChildByPath("x1")), MapleDataTool.getInt(footHold.getChildByPath("y1"))), new Point(
MapleDataTool.getInt(footHold.getChildByPath("x2")), MapleDataTool.getInt(footHold.getChildByPath("y2"))), Integer.parseInt(footHold.getName()));
fh.setPrev((short) MapleDataTool.getInt(footHold.getChildByPath("prev")));
fh.setNext((short) MapleDataTool.getInt(footHold.getChildByPath("next")));
if (fh.getX1() < lBound.x) {
lBound.x = fh.getX1();
}
if (fh.getX2() > uBound.x) {
uBound.x = fh.getX2();
}
if (fh.getY1() < lBound.y) {
lBound.y = fh.getY1();
}
if (fh.getY2() > uBound.y) {
uBound.y = fh.getY2();
}
allFootholds.add(fh);
}
}
}
MapleFootholdTree fTree = new MapleFootholdTree(lBound, uBound);
for (MapleFoothold fh : allFootholds) {
fTree.insert(fh);
}
map.setFootholds(fTree);
if (map.getTop() == 0) {
map.setTop(lBound.y);
}
if (map.getBottom() == 0) {
map.setBottom(uBound.y);
}
if (map.getLeft() == 0) {
map.setLeft(lBound.x);
}
if (map.getRight() == 0) {
map.setRight(uBound.x);
}
int bossid = -1;
String msg = null;
if (mapData.getChildByPath("info/timeMob") != null) {
bossid = MapleDataTool.getInt(mapData.getChildByPath("info/timeMob/id"), 0);
msg = MapleDataTool.getString(mapData.getChildByPath("info/timeMob/message"), null);
}
// load life data (npc, monsters)
String type, limited;
AbstractLoadedMapleLife myLife;
for (MapleData life : mapData.getChildByPath("life")) {
type = MapleDataTool.getString(life.getChildByPath("type"));
limited = MapleDataTool.getString("limitedname", life, "");
if ((npcs || !type.equals("n")) && limited.equals("")) {
myLife = loadLife(life, MapleDataTool.getString(life.getChildByPath("id")), type);
if (myLife instanceof MapleMonster && !GameConstants.isNoSpawn(mapid)) {
final MapleMonster mob = (MapleMonster) myLife;
map.addMonsterSpawn(mob,
MapleDataTool.getInt("mobTime", life, 0),
(byte) MapleDataTool.getInt("team", life, -1),
mob.getId() == bossid ? msg : null);
} else if (myLife instanceof MapleNPC) {
map.addMapObject(myLife);
}
}
}
addAreaBossSpawn(map);
map.setCreateMobInterval((short) MapleDataTool.getInt(mapData.getChildByPath("info/createMobInterval"), 9000));
map.setFixedMob(MapleDataTool.getInt(mapData.getChildByPath("info/fixedMobCapacity"), 0));
map.setPartyBonusRate(GameConstants.getPartyPlay(mapid, MapleDataTool.getInt(mapData.getChildByPath("info/partyBonusR"), 0)));
map.loadMonsterRate(true);
map.setNodes(loadNodes(mapid, mapData));
//load reactor data
String id;
if (reactors && mapData.getChildByPath("reactor") != null) {
for (MapleData reactor : mapData.getChildByPath("reactor")) {
id = MapleDataTool.getString(reactor.getChildByPath("id"));
if (id != null) {
map.spawnReactor(loadReactor(reactor, id, (byte) MapleDataTool.getInt(reactor.getChildByPath("f"), 0)));
}
}
}
try {
map.setMapName(MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(mapid)), ""));
map.setStreetName(MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(mapid)), ""));
} catch (Exception e) {
map.setMapName("");
map.setStreetName("");
}
map.setClock(MapleDataTool.getInt(mapData.getChildByPath("info/clock"), 0) > 0);
map.setEverlast(MapleDataTool.getInt(mapData.getChildByPath("info/everlast"), 0) > 0);
map.setTown(MapleDataTool.getInt(mapData.getChildByPath("info/town"), 0) > 0);
map.setSoaring(MapleDataTool.getInt(mapData.getChildByPath("info/needSkillForFly"), 0) > 0);
map.setForceMove(MapleDataTool.getInt(mapData.getChildByPath("info/lvForceMove"), 0));
map.setHPDec(MapleDataTool.getInt(mapData.getChildByPath("info/decHP"), 0));
map.setHPDecInterval(MapleDataTool.getInt(mapData.getChildByPath("info/decHPInterval"), 10000));
map.setHPDecProtect(MapleDataTool.getInt(mapData.getChildByPath("info/protectItem"), 0));
map.setForcedReturnMap(MapleDataTool.getInt(mapData.getChildByPath("info/forcedReturn"), 999999999));
map.setTimeLimit(MapleDataTool.getInt(mapData.getChildByPath("info/timeLimit"), -1));
map.setFieldLimit(MapleDataTool.getInt(mapData.getChildByPath("info/fieldLimit"), 0));
map.setFirstUserEnter(MapleDataTool.getString(mapData.getChildByPath("info/onFirstUserEnter"), ""));
map.setUserEnter(MapleDataTool.getString(mapData.getChildByPath("info/onUserEnter"), ""));
map.setRecoveryRate(MapleDataTool.getFloat(mapData.getChildByPath("info/recovery"), 1));
map.setFieldType(MapleDataTool.getString(mapData.getChildByPath("info/fieldType"), ""));
map.setConsumeItemCoolTime(MapleDataTool.getInt(mapData.getChildByPath("info/consumeItemCoolTime"), 0));
map.setInstanceId(instanceid);
lock.lock();
try {
instanceMap.put(instanceid, map);
} finally {
lock.unlock();
}
return map;
}
public int getLoadedMaps() {
return maps.size();
}
public boolean isMapLoaded(int mapId) {
return maps.containsKey(mapId);
}
public boolean isInstanceMapLoaded(int instanceid) {
return instanceMap.containsKey(instanceid);
}
public void clearLoadedMap() {
lock.lock();
try {
maps.clear();
} finally {
lock.unlock();
}
}
public List<MapleMap> getAllLoadedMaps() {
List<MapleMap> ret = new ArrayList<>();
lock.lock();
try {
ret.addAll(maps.values());
ret.addAll(instanceMap.values());
} finally {
lock.unlock();
}
return ret;
}
public Collection<MapleMap> getAllMaps() {
return maps.values();
}
private AbstractLoadedMapleLife loadLife(MapleData life, String id, String type) {
AbstractLoadedMapleLife myLife = MapleLifeFactory.getLife(Integer.parseInt(id), type);
if (myLife == null) {
return null;
}
myLife.setCy(MapleDataTool.getInt(life.getChildByPath("cy")));
MapleData dF = life.getChildByPath("f");
if (dF != null) {
myLife.setF(MapleDataTool.getInt(dF));
}
myLife.setFh(MapleDataTool.getInt(life.getChildByPath("fh")));
myLife.setRx0(MapleDataTool.getInt(life.getChildByPath("rx0")));
myLife.setRx1(MapleDataTool.getInt(life.getChildByPath("rx1")));
myLife.setPosition(new Point(MapleDataTool.getInt(life.getChildByPath("x")), MapleDataTool.getInt(life.getChildByPath("y"))));
if (MapleDataTool.getInt("hide", life, 0) == 1 && myLife instanceof MapleNPC) {
myLife.setHide(true);
// } else if (hide > 1) {
// System.err.println("Hide > 1 ("+ hide +")");
}
return myLife;
}
private MapleReactor loadReactor(final MapleData reactor, final String id, final byte FacingDirection) {
final MapleReactor myReactor = new MapleReactor(MapleReactorFactory.getReactor(Integer.parseInt(id)), Integer.parseInt(id));
myReactor.setFacingDirection(FacingDirection);
myReactor.setPosition(new Point(MapleDataTool.getInt(reactor.getChildByPath("x")), MapleDataTool.getInt(reactor.getChildByPath("y"))));
myReactor.setDelay(MapleDataTool.getInt(reactor.getChildByPath("reactorTime")) * 1000);
myReactor.setName(MapleDataTool.getString(reactor.getChildByPath("name"), ""));
return myReactor;
}
private String getMapName(int mapid) {
String mapName = StringUtil.getLeftPaddedStr(Integer.toString(mapid), '0', 9);
StringBuilder builder = new StringBuilder("Map/Map");
builder.append(mapid / 100000000);
builder.append("/");
builder.append(mapName);
builder.append(".img");
mapName = builder.toString();
return mapName;
}
private String getMapStringName(int mapid) {
StringBuilder builder = new StringBuilder();
if (mapid < 100000000) {
builder.append("maple");
} else if ((mapid >= 100000000 && mapid < 200000000) || mapid / 100000 == 5540) {
builder.append("victoria");
} else if (mapid >= 200000000 && mapid < 300000000) {
builder.append("ossyria");
} else if (mapid >= 300000000 && mapid < 400000000) {
builder.append("3rd");
} else if (mapid >= 500000000 && mapid < 510000000) {
builder.append(GameConstants.GMS ? "thai" : "TH");
} else if (mapid >= 555000000 && mapid < 556000000) {
builder.append("SG");
} else if (mapid >= 540000000 && mapid < 600000000) {
builder.append(GameConstants.GMS ? "singapore" : "SG");
} else if (mapid >= 682000000 && mapid < 683000000) {
builder.append(GameConstants.GMS ? "HalloweenGL" : "GL");
} else if (mapid >= 600000000 && mapid < 670000000) {
builder.append(GameConstants.GMS ? "MasteriaGL" : "GL");
} else if (mapid >= 677000000 && mapid < 678000000) {
builder.append(GameConstants.GMS ? "Episode1GL" : "GL");
} else if (mapid >= 670000000 && mapid < 682000000) {
builder.append(GameConstants.GMS ? "weddingGL" : "GL");
} else if (mapid >= 687000000 && mapid < 688000000) {
builder.append("Gacha_GL");
} else if (mapid >= 689000000 && mapid < 690000000) {
builder.append("CTF_GL");
} else if (mapid >= 683000000 && mapid < 684000000) {
builder.append("event");
} else if (mapid >= 684000000 && mapid < 685000000) {
builder.append("event_5th");
} else if (mapid >= 700000000 && mapid < 700000300) {
builder.append("wedding");
} else if (mapid >= 701000000 && mapid < 701020000) {
builder.append("china");
} else if ((mapid >= 702090000 && mapid <= 702100000) || (mapid >= 740000000 && mapid < 741000000)) {
builder.append("TW");
} else if (mapid >= 702000000 && mapid < 742000000) {
builder.append("CN");
} else if (mapid >= 800000000 && mapid < 900000000) {
builder.append(GameConstants.GMS ? "jp" : "JP");
} else {
builder.append("etc");
}
builder.append("/");
builder.append(mapid);
return builder.toString();
}
public void setChannel(int channel) {
this.channel = channel;
}
private void addAreaBossSpawn(final MapleMap map) {
int monsterid = -1;
int mobtime = -1;
String msg = null;
boolean shouldSpawn = true;
Point pos1 = null, pos2 = null, pos3 = null;
switch (map.getId()) {
case 104010200: // Mano
mobtime = 1200;
monsterid = 2220000;
msg = "A cool breeze was felt when Mano appeared.";
pos1 = new Point(189, 2);
pos2 = new Point(478, 250);
pos3 = new Point(611, 489);
break;
case 102020500: // Stumpy
mobtime = 1200;
monsterid = 3220000;
msg = "Stumpy has appeared with a stumping sound that rings the Stone Mountain.";
pos1 = new Point(1121, 2130);
pos2 = new Point(483, 2171);
pos3 = new Point(1474, 1706);
break;
case 100020101: //Mushmom
mobtime = 1200;
monsterid = 6130101;
msg = "Mushmom has appeared.";
pos1 = new Point(-311, 201);
pos2 = new Point(-903, 197);
pos3 = new Point(-568, 196);
break;
case 100020301: //Blue Mushmom
mobtime = 1200;
monsterid = 8220007;
msg = "Blue Mushmom has appeared.";
pos1 = new Point(-188, -657);
pos2 = new Point(625, -660);
pos3 = new Point(508, -648);
break;
case 100020401: //Zombie Mushmom
mobtime = 1200;
monsterid = 6300005;
msg = "Zombie Mushmom has appeared.";
pos1 = new Point(-130, -773);
pos2 = new Point(504, -760);
pos3 = new Point(608, -641);
break;
case 120030500: // King Clang
mobtime = 1200;
monsterid = 5220001;
msg = "A strange turban shell has appeared on the beach.";
pos1 = new Point(-355, 179);
pos2 = new Point(-1283, -113);
pos3 = new Point(-571, -593);
break;
case 250010304: // Tae Roon
mobtime = 2100;
monsterid = 7220000;
msg = "Tae Roon appeared with a loud growl.";
pos1 = new Point(-210, 33);
pos2 = new Point(-234, 393);
pos3 = new Point(-654, 33);
break;
case 200010300: // Eliza
mobtime = 1200;
monsterid = 8220000;
msg = "Eliza has appeared with a black whirlwind.";
pos1 = new Point(665, 83);
pos2 = new Point(672, -217);
pos3 = new Point(-123, -217);
break;
case 250010503: // Ghost Priest
mobtime = 1800;
monsterid = 7220002;
msg = "The area fills with an unpleasant force of evil.. even the occasional ones of the cats sound disturbing";
pos1 = new Point(-303, 543);
pos2 = new Point(227, 543);
pos3 = new Point(719, 543);
break;
case 222010310: // Old Fox
mobtime = 2700;
monsterid = 7220001;
msg = "As the moon light dims, a long fox cry can be heard and the presence of the old fox can be felt.";
pos1 = new Point(-169, -147);
pos2 = new Point(-517, 93);
pos3 = new Point(247, 93);
break;
case 103030400: // Dale
mobtime = 1800;
monsterid = 6220000;
msg = "The huge crocodile Dyle has come out from the swamp.";
pos1 = new Point(-831, 109);
pos2 = new Point(1525, -75);
pos3 = new Point(-511, 107);
break;
case 101040300: // Faust
mobtime = 1800;
monsterid = 5220002;
msg = "The blue fog became darker when Faust appeared.";
pos1 = new Point(600, -600);
pos2 = new Point(600, -800);
pos3 = new Point(600, -300);
break;
case 220050100: // Timer
mobtime = 1500;
monsterid = 5220003;
msg = "Click clock! Timer has appeared with an irregular clock sound.";
pos1 = new Point(-467, 1032);
pos2 = new Point(532, 1032);
pos3 = new Point(-47, 1032);
break;
case 221040301: // Zeno
mobtime = 2400;
monsterid = 6220001;
msg = "Zeno has appeared with a heavy sound of machinery.";
pos1 = new Point(-4134, 416);
pos2 = new Point(-4283, 776);
pos3 = new Point(-3292, 776);
break;
case 240040401: // Lev
mobtime = 7200;
monsterid = 8220003;
msg = "Leviathan has appeared with a cold wind from over the gorge.";
pos1 = new Point(-15, 2481);
pos2 = new Point(127, 1634);
pos3 = new Point(159, 1142);
break;
case 260010201: // Deo
mobtime = 3600;
monsterid = 3220001;
msg = "Deo slowly appeared out of the sand dust.";
pos1 = new Point(-215, 275);
pos2 = new Point(298, 275);
pos3 = new Point(592, 275);
break;
case 251010102: // Centipede
mobtime = 3600;
monsterid = 5220004;
msg = "A giant centipede appeared.";
pos1 = new Point(-41, 124);
pos2 = new Point(-173, 126);
pos3 = new Point(79, 118);
break;
case 261030000: // Chimera
mobtime = 2700;
monsterid = 8220002;
msg = "Chimera has appeared out of the darkness of the underground with a glitter in her eyes.";
pos1 = new Point(-1094, -405);
pos2 = new Point(-772, -116);
pos3 = new Point(-108, 181);
break;
case 230020100: // Sherp
mobtime = 2700;
monsterid = 4220000;
msg = "A strange shell has appeared from a grove of seaweed.";
pos1 = new Point(-291, -20);
pos2 = new Point(-272, -500);
pos3 = new Point(-462, 640);
break;
case 103020320: // Shade
mobtime = 1800;
monsterid = 5090000;
msg = "Shade has appeared.";
pos1 = new Point(79, 174);
pos2 = new Point(-223, 296);
pos3 = new Point(80, 275);
break;
case 103020420: // Shade
mobtime = 1800;
monsterid = 5090000;
msg = "Shade has appeared.";
pos1 = new Point(2241, 301);
pos2 = new Point(1990, 301);
pos3 = new Point(1684, 307);
break;
case 261020300: // Camera
mobtime = 2700;
monsterid = 7090000;
msg = "A camera has appeared.";
pos1 = new Point(312, 157);
pos2 = new Point(539, 136);
pos3 = new Point(760, 141);
break;
case 261020401: // Deet and Roi
mobtime = 2700;
monsterid = 8090000;
msg = "Deet and Roi has appeared.";
pos1 = new Point(-263, 155);
pos2 = new Point(-436, 122);
pos3 = new Point(22, 144);
break;
case 250020300: // Master Dummy
mobtime = 2700;
monsterid = 5090001;
msg = "Master Dummy has appeared.";
pos1 = new Point(1208, 27);
pos2 = new Point(1654, 40);
pos3 = new Point(927, -502);
break;
case 211050000: // Snow Witch
mobtime = 2700;
monsterid = 6090001;
msg = "The witch of snow has appeared.";
pos1 = new Point(-233, -431);
pos2 = new Point(-370, -426);
pos3 = new Point(-526, -420);
break;
case 261010003: // Rurumo
mobtime = 2700;
monsterid = 6090004;
msg = "Rurumo has appeared.";
pos1 = new Point(-861, 301);
pos2 = new Point(-703, 301);
pos3 = new Point(-426, 287);
break;
case 222010300: // Scholar Ghost
mobtime = 2700;
monsterid = 6090003;
msg = "A wise ghost has appeared.";
pos1 = new Point(1300, -400);
pos2 = new Point(1100, -100);
pos3 = new Point(1100, 100);
break;
case 251010101: // Bamboo Warrior
mobtime = 2700;
monsterid = 6090002;
msg = "A warrior with bamboo has appeared.";
pos1 = new Point(-15, -449);
pos2 = new Point(-114, -442);
pos3 = new Point(-255, -446);
break;
case 211041400: // Riche
mobtime = 2700;
monsterid = 6090000;
msg = "Riche has appeared.";
pos1 = new Point(1672, 82);
pos2 = new Point(2071, 10);
pos3 = new Point(1417, 57);
break;
case 105030500: // Rog
mobtime = 2700;
monsterid = 8130100;
msg = "Jr. Balrog has appeared.";
pos1 = new Point(1275, -399);
pos2 = new Point(1254, -412);
pos3 = new Point(1058, -427);
break;
case 105020400: // Snack Bar
mobtime = 2700;
monsterid = 8220008;
msg = "A mysterious shop appeared.";
pos1 = new Point(-163, 82);
pos2 = new Point(958, 107);
pos3 = new Point(706, -206);
break;
case 211040101: // Snowman
mobtime = 3600;
monsterid = 8220001;
msg = "A snowman covered in ice has appeared.";
pos1 = new Point(485, 244);
pos2 = new Point(-60, 249);
pos3 = new Point(208, 255);
break;
case 910000000: // FM
if (channel == 7) {
mobtime = 3600;
monsterid = 9420015;
msg = "NooNoo has appeared out of anger.";
pos1 = new Point(498, 4);
pos2 = new Point(498, 4);
pos3 = new Point(498, 4);
} else if (channel == 8) {
mobtime = 3600;
monsterid = 9400700;
msg = "Giant Tomato has appeared.";
pos1 = new Point(498, 4);
pos2 = new Point(498, 4);
pos3 = new Point(498, 4);
} else if (channel == 9) {
mobtime = 3600;
monsterid = 9400734;
msg = "Giant Tomato has appeared.";
pos1 = new Point(498, 4);
pos2 = new Point(498, 4);
pos3 = new Point(498, 4);
}
break;
case 209000000: // Happyville
mobtime = 300;
monsterid = 9500318;
msg = "Giant Snowman has appeared!";
pos1 = new Point(-115, 154);
pos2 = new Point(-115, 154);
pos3 = new Point(-115, 154);
break;
case 677000001:
mobtime = 60;
monsterid = 9400612;
msg = "Marbas has appeared.";
pos1 = new Point(99, 60);
pos2 = new Point(99, 60);
pos3 = new Point(99, 60);
break;
case 677000003:
mobtime = 60;
monsterid = 9400610;
msg = "Amdusias has appeared.";
pos1 = new Point(6, 35);
pos2 = new Point(6, 35);
pos3 = new Point(6, 35);
break;
case 677000005:
mobtime = 60;
monsterid = 9400609;
msg = "Andras has appeared.";
pos1 = new Point(-277, 78); //on the spawnpoint
pos2 = new Point(547, 86); //bottom of right ladder
pos3 = new Point(-347, 80); //bottom of left ladder
break;
case 677000007:
mobtime = 60;
monsterid = 9400611;
msg = "Crocell has appeared.";
pos1 = new Point(117, 73);
pos2 = new Point(117, 73);
pos3 = new Point(117, 73);
break;
case 677000009:
mobtime = 60;
monsterid = 9400613;
msg = "Valefor has appeared.";
pos1 = new Point(85, 66);
pos2 = new Point(85, 66);
pos3 = new Point(85, 66);
break;
case 931000500:
mobtime = 30 * 3600; //30 hours
monsterid = 9304005;
msg = "Jaira has appeared.";
pos1 = new Point(-872, -332);
pos2 = new Point(409, -572);
pos3 = new Point(-131, 0);
shouldSpawn = false;
break;
}
if (monsterid > 0) {
map.addAreaMonsterSpawn(MapleLifeFactory.getMonster(monsterid), pos1, pos2, pos3, mobtime, msg, shouldSpawn);
}
}
private void loadPortals(MapleMap map, MapleData port) {
if (port == null) {
return;
}
int nextDoorPortal = 0x80;
for (MapleData portal : port.getChildren()) {
MaplePortal myPortal = new MaplePortal(MapleDataTool.getInt(portal.getChildByPath("pt")));
myPortal.setName(MapleDataTool.getString(portal.getChildByPath("pn")));
myPortal.setTarget(MapleDataTool.getString(portal.getChildByPath("tn")));
myPortal.setTargetMapId(MapleDataTool.getInt(portal.getChildByPath("tm")));
myPortal.setPosition(new Point(MapleDataTool.getInt(portal.getChildByPath("x")), MapleDataTool.getInt(portal.getChildByPath("y"))));
String script = MapleDataTool.getString("script", portal, null);
if (script != null && script.equals("")) {
script = null;
}
myPortal.setScriptName(script);
if (myPortal.getType() == MaplePortal.DOOR_PORTAL) {
myPortal.setId(nextDoorPortal);
nextDoorPortal++;
} else {
myPortal.setId(Integer.parseInt(portal.getName()));
}
map.addPortal(myPortal);
}
}
private MapleNodes loadNodes(final int mapid, final MapleData mapData) {
MapleNodes nodeInfo = new MapleNodes(mapid);
if (mapData.getChildByPath("nodeInfo") != null) {
for (MapleData node : mapData.getChildByPath("nodeInfo")) {
try {
if (node.getName().equals("start")) {
nodeInfo.setNodeStart(MapleDataTool.getInt(node, 0));
continue;
}
List<Integer> edges = new ArrayList<>();
if (node.getChildByPath("edge") != null) {
for (MapleData edge : node.getChildByPath("edge")) {
edges.add(MapleDataTool.getInt(edge, -1));
}
}
final MapleNodeInfo mni = new MapleNodeInfo(
Integer.parseInt(node.getName()),
MapleDataTool.getIntConvert("key", node, 0),
MapleDataTool.getIntConvert("x", node, 0),
MapleDataTool.getIntConvert("y", node, 0),
MapleDataTool.getIntConvert("attr", node, 0), edges);
nodeInfo.addNode(mni);
} catch (NumberFormatException e) {
} //start, end, edgeInfo = we dont need it
}
nodeInfo.sortNodes();
}
for (int i = 1; i <= 7; i++) {
if (mapData.getChildByPath(String.valueOf(i)) != null && mapData.getChildByPath(i + "/obj") != null) {
for (MapleData node : mapData.getChildByPath(i + "/obj")) {
if (node.getChildByPath("SN_count") != null && node.getChildByPath("speed") != null) {
int sn_count = MapleDataTool.getIntConvert("SN_count", node, 0);
String name = MapleDataTool.getString("name", node, "");
int speed = MapleDataTool.getIntConvert("speed", node, 0);
if (sn_count <= 0 || speed <= 0 || name.equals("")) {
continue;
}
final List<Integer> SN = new ArrayList<>();
for (int x = 0; x < sn_count; x++) {
SN.add(MapleDataTool.getIntConvert("SN" + x, node, 0));
}
final MaplePlatform mni = new MaplePlatform(
name, MapleDataTool.getIntConvert("start", node, 2), speed,
MapleDataTool.getIntConvert("x1", node, 0),
MapleDataTool.getIntConvert("y1", node, 0),
MapleDataTool.getIntConvert("x2", node, 0),
MapleDataTool.getIntConvert("y2", node, 0),
MapleDataTool.getIntConvert("r", node, 0), SN);
nodeInfo.addPlatform(mni);
} else if (node.getChildByPath("tags") != null) {
String name = MapleDataTool.getString("tags", node, "");
nodeInfo.addFlag(new Pair<>(name, name.endsWith("3") ? 1 : 0)); //idk, no indication in wz
}
}
}
}
// load areas (EG PQ platforms)
if (mapData.getChildByPath("area") != null) {
int x1, y1, x2, y2;
Rectangle mapArea;
for (MapleData area : mapData.getChildByPath("area")) {
x1 = MapleDataTool.getInt(area.getChildByPath("x1"));
y1 = MapleDataTool.getInt(area.getChildByPath("y1"));
x2 = MapleDataTool.getInt(area.getChildByPath("x2"));
y2 = MapleDataTool.getInt(area.getChildByPath("y2"));
mapArea = new Rectangle(x1, y1, (x2 - x1), (y2 - y1));
nodeInfo.addMapleArea(mapArea);
}
}
if (mapData.getChildByPath("CaptureTheFlag") != null) {
final MapleData mc = mapData.getChildByPath("CaptureTheFlag");
for (MapleData area : mc) {
nodeInfo.addGuardianSpawn(new Point(MapleDataTool.getInt(area.getChildByPath("FlagPositionX")), MapleDataTool.getInt(area.getChildByPath("FlagPositionY"))), area.getName().startsWith("Red") ? 0 : 1);
}
}
if (mapData.getChildByPath("directionInfo") != null) {
final MapleData mc = mapData.getChildByPath("directionInfo");
for (MapleData area : mc) {
DirectionInfo di = new DirectionInfo(Integer.parseInt(area.getName()), MapleDataTool.getInt("x", area, 0), MapleDataTool.getInt("y", area, 0), MapleDataTool.getInt("forcedInput", area, 0) > 0);
final MapleData mc2 = area.getChildByPath("eventQ");
if (mc2 != null) {
for (MapleData event : mc2) {
di.eventQ.add(MapleDataTool.getString(event));
}
}
nodeInfo.addDirection(Integer.parseInt(area.getName()), di);
}
}
if (mapData.getChildByPath("monsterCarnival") != null) {
final MapleData mc = mapData.getChildByPath("monsterCarnival");
if (mc.getChildByPath("mobGenPos") != null) {
for (MapleData area : mc.getChildByPath("mobGenPos")) {
nodeInfo.addMonsterPoint(MapleDataTool.getInt(area.getChildByPath("x")),
MapleDataTool.getInt(area.getChildByPath("y")),
MapleDataTool.getInt(area.getChildByPath("fh")),
MapleDataTool.getInt(area.getChildByPath("cy")),
MapleDataTool.getInt("team", area, -1));
}
}
if (mc.getChildByPath("mob") != null) {
for (MapleData area : mc.getChildByPath("mob")) {
nodeInfo.addMobSpawn(MapleDataTool.getInt(area.getChildByPath("id")), MapleDataTool.getInt(area.getChildByPath("spendCP")));
}
}
if (mc.getChildByPath("guardianGenPos") != null) {
for (MapleData area : mc.getChildByPath("guardianGenPos")) {
nodeInfo.addGuardianSpawn(new Point(MapleDataTool.getInt(area.getChildByPath("x")), MapleDataTool.getInt(area.getChildByPath("y"))), MapleDataTool.getInt("team", area, -1));
}
}
if (mc.getChildByPath("skill") != null) {
for (MapleData area : mc.getChildByPath("skill")) {
nodeInfo.addSkillId(MapleDataTool.getInt(area));
}
}
}
return nodeInfo;
}
}