Implemented Kites, PlayerNPCs and C. Shop Surprise & Tweaked login
Added code support for Kites. Reviewed concurrent access issues with pet autopot. Addressed PlayerStorage issue where characters would not be properly deregistered from channel PlayerStorage in certain situations. Implemented harp quest (questid 3314) because of reasons. Added SFX to signalize KC/NLC subway departing/approaching. Changed traveling time values to work similarly to GMS. Properly developed the PlayerNPC feature in the source. Added autodeployable PlayerNPC system and Hall of Fame. Solved a glitch with NLC mayor's quiz questline that would allow a player to restart the quiz as many times one would see fit. Added a custom server flag that allows overwriting the ToT 999 mobs to a new value (technically it doesn't overwrite, rather sets the player at quest start with 999 - n credited mobs). Fixed permanent pets expiring after a while. Added code support for Cash Shop Surprise item. Reviewed login handler system as a whole, protecting many exposed flaws. Solved a bug with ULTRA_THREE_SNAILS sometimes taking wrong etc shell from inventory.
This commit is contained in:
@@ -30,6 +30,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import tools.locks.MonitoredReentrantLock;
|
||||
|
||||
@@ -92,10 +93,10 @@ public class CashShop {
|
||||
Item item;
|
||||
|
||||
int petid = -1;
|
||||
|
||||
if (ItemConstants.isPet(itemId))
|
||||
if (ItemConstants.isPet(itemId)) {
|
||||
petid = MaplePet.createPet(itemId);
|
||||
|
||||
}
|
||||
|
||||
if (ItemConstants.getInventoryType(itemId).equals(MapleInventoryType.EQUIP)) {
|
||||
item = MapleItemInformationProvider.getInstance().getEquipById(itemId);
|
||||
} else {
|
||||
@@ -119,10 +120,12 @@ public class CashShop {
|
||||
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 24 * period));
|
||||
}
|
||||
}
|
||||
|
||||
item.setSN(sn);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SpecialCashItem {
|
||||
private int sn, modifier;
|
||||
private byte info; //?
|
||||
@@ -151,6 +154,7 @@ public class CashShop {
|
||||
private static final Map<Integer, CashItem> items = new HashMap<>();
|
||||
private static final Map<Integer, List<Integer>> packages = new HashMap<>();
|
||||
private static final List<SpecialCashItem> specialcashitems = new ArrayList<>();
|
||||
private static final List<Integer> randomitemids = new ArrayList<>();
|
||||
|
||||
static {
|
||||
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz"));
|
||||
@@ -174,6 +178,13 @@ public class CashShop {
|
||||
|
||||
packages.put(Integer.parseInt(cashPackage.getName()), cPackage);
|
||||
}
|
||||
|
||||
for(Entry<Integer, CashItem> e : items.entrySet()) {
|
||||
if(e.getValue().isOnSale()) {
|
||||
randomitemids.add(e.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
Connection con = null;
|
||||
@@ -197,6 +208,13 @@ public class CashShop {
|
||||
}
|
||||
}
|
||||
|
||||
public static CashItem getRandomCashItem() {
|
||||
if(randomitemids.isEmpty()) return null;
|
||||
|
||||
int rnd = (int)(Math.random() * randomitemids.size());
|
||||
return items.get(randomitemids.get(rnd));
|
||||
}
|
||||
|
||||
public static CashItem getItem(int sn) {
|
||||
return items.get(sn);
|
||||
}
|
||||
@@ -508,4 +526,48 @@ public class CashShop {
|
||||
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private Item getCashShopItemByItemid(int itemid) {
|
||||
lock.lock();
|
||||
try {
|
||||
for(Item it : inventory) {
|
||||
if(it.getItemId() == itemid) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public synchronized Item openCashShopSurprise() {
|
||||
Item css = getCashShopItemByItemid(5222000);
|
||||
|
||||
if(css != null) {
|
||||
CashItem cItem = CashItemFactory.getRandomCashItem();
|
||||
|
||||
if(cItem != null) {
|
||||
if(css.getQuantity() > 1) {
|
||||
/* if(NOT ENOUGH SPACE) { looks like we're not dealing with cash inventory limit whatsoever, k then
|
||||
return null;
|
||||
} */
|
||||
|
||||
css.setQuantity((short) (css.getQuantity() - 1));
|
||||
} else {
|
||||
removeFromInventory(css);
|
||||
}
|
||||
|
||||
Item item = cItem.toItem();
|
||||
addToInventory(item);
|
||||
|
||||
return item;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,6 @@ public class MapleItemInformationProvider {
|
||||
protected Map<Integer, Pair<String, Integer>> statUpgradeMakerCache = new HashMap<>();
|
||||
protected Map<Integer, MakerItemFactory.MakerItemCreateEntry> makerItemCache = new HashMap<>();
|
||||
|
||||
|
||||
private MapleItemInformationProvider() {
|
||||
loadCardIdData();
|
||||
itemData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
|
||||
|
||||
@@ -0,0 +1,644 @@
|
||||
/*
|
||||
This file is part of the OdinMS Maple Story Server
|
||||
Copyright (C) 2008 Patrick Huy <[email protected]>
|
||||
Matthias Butz <[email protected]>
|
||||
Jan Christian Meyer <[email protected]>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package server.life;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import constants.GameConstants;
|
||||
import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
import net.server.world.World;
|
||||
import server.maps.AbstractMapleMapObject;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MapleMapObject;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author XoticStory
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MaplePlayerNPC extends AbstractMapleMapObject {
|
||||
private static final Map<Byte, AtomicInteger> runningPlayerNpcScriptIds = new HashMap<>();
|
||||
private static final AtomicInteger runningOverallRank = new AtomicInteger();
|
||||
private static final List<AtomicInteger> runningWorldRank = new ArrayList<>();
|
||||
private static final Map<Pair<Integer, Integer>, AtomicInteger> runningWorldJobRank = new HashMap<>();
|
||||
|
||||
private Map<Short, Integer> equips = new HashMap<>();
|
||||
private int scriptId, face, hair, gender, job;
|
||||
private byte skin;
|
||||
private String name = "";
|
||||
private int dir, FH, RX0, RX1, CY;
|
||||
private int worldRank, overallRank, worldJobRank, overallJobRank;
|
||||
|
||||
static {
|
||||
getRunningMetadata();
|
||||
}
|
||||
|
||||
public MaplePlayerNPC(String name, int scriptId, int face, int hair, int gender, byte skin, Map<Short, Integer> equips, int dir, int FH, int RX0, int RX1, int CX, int CY, int oid) {
|
||||
this.equips = equips;
|
||||
this.scriptId = scriptId;
|
||||
this.face = face;
|
||||
this.hair = hair;
|
||||
this.gender = gender;
|
||||
this.skin = skin;
|
||||
this.name = name;
|
||||
this.dir = dir;
|
||||
this.FH = FH;
|
||||
this.RX0 = RX0;
|
||||
this.RX1 = RX1;
|
||||
this.CY = CY;
|
||||
this.job = 7777; // supposed to be developer
|
||||
|
||||
setPosition(new Point(CX, CY));
|
||||
setObjectId(oid);
|
||||
}
|
||||
|
||||
public MaplePlayerNPC(ResultSet rs) {
|
||||
try {
|
||||
CY = rs.getInt("cy");
|
||||
name = rs.getString("name");
|
||||
hair = rs.getInt("hair");
|
||||
face = rs.getInt("face");
|
||||
skin = rs.getByte("skin");
|
||||
gender = rs.getInt("gender");
|
||||
dir = rs.getInt("dir");
|
||||
FH = rs.getInt("fh");
|
||||
RX0 = rs.getInt("rx0");
|
||||
RX1 = rs.getInt("rx1");
|
||||
scriptId = rs.getInt("scriptid");
|
||||
|
||||
worldRank = rs.getInt("worldrank");
|
||||
overallRank = rs.getInt("overallrank");
|
||||
worldJobRank = rs.getInt("worldjobrank");
|
||||
overallJobRank = GameConstants.getOverallJobRankByScriptId(scriptId);
|
||||
job = rs.getInt("job");
|
||||
|
||||
setPosition(new Point(rs.getInt("x"), CY));
|
||||
setObjectId(rs.getInt("id"));
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT equippos, equipid FROM playernpcs_equip WHERE npcid = ?");
|
||||
ps.setInt(1, rs.getInt("id"));
|
||||
ResultSet rs2 = ps.executeQuery();
|
||||
while (rs2.next()) {
|
||||
equips.put(rs2.getShort("equippos"), rs2.getInt("equipid"));
|
||||
}
|
||||
rs2.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<Short, Integer> getEquips() {
|
||||
return equips;
|
||||
}
|
||||
|
||||
public int getScriptId() {
|
||||
return scriptId;
|
||||
}
|
||||
|
||||
public int getJob() {
|
||||
return job;
|
||||
}
|
||||
|
||||
public int getDirection() {
|
||||
return dir;
|
||||
}
|
||||
|
||||
public int getFH() {
|
||||
return FH;
|
||||
}
|
||||
|
||||
public int getRX0() {
|
||||
return RX0;
|
||||
}
|
||||
|
||||
public int getRX1() {
|
||||
return RX1;
|
||||
}
|
||||
|
||||
public int getCY() {
|
||||
return CY;
|
||||
}
|
||||
|
||||
public byte getSkin() {
|
||||
return skin;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getFace() {
|
||||
return face;
|
||||
}
|
||||
|
||||
public int getHair() {
|
||||
return hair;
|
||||
}
|
||||
|
||||
public int getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public int getWorldRank() {
|
||||
return worldRank;
|
||||
}
|
||||
|
||||
public int getOverallRank() {
|
||||
return overallRank;
|
||||
}
|
||||
|
||||
public int getWorldJobRank() {
|
||||
return worldJobRank;
|
||||
}
|
||||
|
||||
public int getOverallJobRank() {
|
||||
return overallJobRank;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapleMapObjectType getType() {
|
||||
return MapleMapObjectType.PLAYER_NPC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(MapleClient client) {
|
||||
client.announce(MaplePacketCreator.spawnPlayerNPC(this));
|
||||
client.announce(MaplePacketCreator.getPlayerNPC(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyData(MapleClient client) {
|
||||
client.announce(MaplePacketCreator.removeNPCController(this.getObjectId()));
|
||||
client.announce(MaplePacketCreator.removePlayerNPC(this.getObjectId()));
|
||||
}
|
||||
|
||||
private static void getRunningMetadata() {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
getRunningPlayerNpcScriptIds(con);
|
||||
getRunningOverallRanks(con);
|
||||
getRunningWorldRanks(con);
|
||||
getRunningWorldJobRanks(con);
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void getRunningPlayerNpcScriptIds(Connection con) throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT floor(scriptid / 100), max(scriptid) FROM playernpcs GROUP BY floor(scriptid / 100) ORDER BY floor(scriptid / 100)");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while(rs.next()) {
|
||||
byte branch = (byte) (rs.getInt(1) % 100);
|
||||
int maxid = rs.getInt(2);
|
||||
|
||||
if(branch < 26) {
|
||||
runningPlayerNpcScriptIds.put(branch, new AtomicInteger(maxid));
|
||||
} else {
|
||||
runningPlayerNpcScriptIds.put((byte)(branch - ((branch - 26) % 4)), new AtomicInteger(maxid));
|
||||
}
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private static void getRunningOverallRanks(Connection con) throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT max(overallrank) FROM playernpcs");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
if(rs.next()) {
|
||||
runningOverallRank.set(rs.getInt(1) + 1);
|
||||
} else {
|
||||
runningOverallRank.set(1);
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private static void getRunningWorldRanks(Connection con) throws SQLException {
|
||||
int numWorlds = Server.getInstance().getWorlds().size();
|
||||
for(int i = 0; i < numWorlds; i++) {
|
||||
runningWorldRank.add(new AtomicInteger(1));
|
||||
}
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("SELECT world, max(worldrank) FROM playernpcs GROUP BY world ORDER BY world");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while(rs.next()) {
|
||||
int wid = rs.getInt(1);
|
||||
if(wid < numWorlds) {
|
||||
runningWorldRank.get(wid).set(rs.getInt(2) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private static void getRunningWorldJobRanks(Connection con) throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT world, job, max(worldjobrank) FROM playernpcs GROUP BY world, job ORDER BY world, job");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while(rs.next()) {
|
||||
runningWorldJobRank.put(new Pair<>(rs.getInt(1), rs.getInt(2)), new AtomicInteger(rs.getInt(3) + 1));
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private static int getAndIncrementRunningWorldJobRanks(int world, int job) {
|
||||
AtomicInteger wjr = runningWorldJobRank.get(new Pair<>(world, job));
|
||||
if(wjr == null) {
|
||||
wjr = new AtomicInteger(1);
|
||||
runningWorldJobRank.put(new Pair<>(world, job), wjr);
|
||||
}
|
||||
|
||||
return wjr.getAndIncrement();
|
||||
}
|
||||
|
||||
public static boolean canSpawnPlayerNpc(String name, int mapid) {
|
||||
boolean ret = true;
|
||||
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT name FROM playernpcs WHERE name LIKE ? AND map = ?");
|
||||
ps.setString(1, name);
|
||||
ps.setInt(2, mapid);
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if(rs.next()) {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void updatePlayerNPCPosition(MapleMap map, Point newPos) {
|
||||
setPosition(newPos);
|
||||
RX0 = newPos.x + 50;
|
||||
RX1 = newPos.x - 50;
|
||||
CY = newPos.y;
|
||||
FH = map.getFootholds().findBelow(newPos).getId();
|
||||
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("UPDATE playernpcs SET x = ?, cy = ?, fh = ?, rx0 = ?, rx1 = ? WHERE id = ?");
|
||||
ps.setInt(1, newPos.x);
|
||||
ps.setInt(2, CY);
|
||||
ps.setInt(3, FH);
|
||||
ps.setInt(4, RX0);
|
||||
ps.setInt(5, RX1);
|
||||
ps.setInt(6, getObjectId());
|
||||
ps.executeUpdate();
|
||||
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static MaplePlayerNPC createPlayerNPCInternal(MapleMap map, Point pos, MapleCharacter chr) {
|
||||
if(pos == null) {
|
||||
pos = map.getNextPlayerNpcPosition();
|
||||
if(pos == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int mapId = map.getId();
|
||||
int worldId = chr.getWorld();
|
||||
int jobId = (chr.getJob().getId() / 100) * 100;
|
||||
|
||||
/*
|
||||
if(!canSpawnPlayerNpc(chr.getName(), mapId)) {
|
||||
return null;
|
||||
}
|
||||
*/
|
||||
|
||||
byte branch = GameConstants.getHallOfFameBranch(chr.getJob(), mapId);
|
||||
|
||||
int scriptId;
|
||||
try {
|
||||
scriptId = runningPlayerNpcScriptIds.get(branch).getAndIncrement();
|
||||
|
||||
if(!GameConstants.canPnpcBranchUseScriptId(branch, scriptId)) {
|
||||
return null;
|
||||
}
|
||||
} catch (NullPointerException npe) {
|
||||
scriptId = 9900000 + (branch * 100);
|
||||
runningPlayerNpcScriptIds.put(branch, new AtomicInteger(scriptId + 1));
|
||||
}
|
||||
|
||||
MaplePlayerNPC ret;
|
||||
int npcId;
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT * FROM playernpcs WHERE scriptid = ?");
|
||||
ps.setInt(1, scriptId);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (!rs.next()) { // creates new playernpc if scriptid doesn't exist
|
||||
rs.close();
|
||||
ps.close();
|
||||
|
||||
ps = con.prepareStatement("INSERT INTO playernpcs (name, hair, face, skin, gender, x, cy, world, map, scriptid, dir, fh, rx0, rx1, worldrank, overallrank, worldjobrank, job) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
|
||||
ps.setString(1, chr.getName());
|
||||
ps.setInt(2, chr.getHair());
|
||||
ps.setInt(3, chr.getFace());
|
||||
ps.setInt(4, chr.getSkinColor().getId());
|
||||
ps.setInt(5, chr.getGender());
|
||||
ps.setInt(6, pos.x);
|
||||
ps.setInt(7, pos.y);
|
||||
ps.setInt(8, worldId);
|
||||
ps.setInt(9, mapId);
|
||||
ps.setInt(10, scriptId);
|
||||
ps.setInt(11, 1); // default direction
|
||||
ps.setInt(12, map.getFootholds().findBelow(pos).getId());
|
||||
ps.setInt(13, pos.x + 50);
|
||||
ps.setInt(14, pos.x - 50);
|
||||
ps.setInt(15, runningWorldRank.get(worldId).getAndIncrement());
|
||||
ps.setInt(16, runningOverallRank.getAndIncrement());
|
||||
ps.setInt(17, getAndIncrementRunningWorldJobRanks(worldId, jobId));
|
||||
ps.setInt(18, jobId);
|
||||
|
||||
ps.executeUpdate();
|
||||
|
||||
rs = ps.getGeneratedKeys();
|
||||
rs.next();
|
||||
npcId = rs.getInt(1);
|
||||
rs.close();
|
||||
ps.close();
|
||||
|
||||
ps = con.prepareStatement("INSERT INTO playernpcs_equip (npcid, equipid, equippos) VALUES (?, ?, ?)");
|
||||
ps.setInt(1, npcId);
|
||||
for (Item equip : chr.getInventory(MapleInventoryType.EQUIPPED)) {
|
||||
int position = Math.abs(equip.getPosition());
|
||||
if ((position < 12 && position > 0) || (position > 100 && position < 112)) {
|
||||
ps.setInt(2, equip.getItemId());
|
||||
ps.setInt(3, equip.getPosition());
|
||||
ps.addBatch();
|
||||
}
|
||||
}
|
||||
ps.executeBatch();
|
||||
ps.close();
|
||||
|
||||
ps = con.prepareStatement("SELECT * FROM playernpcs WHERE id = ?");
|
||||
ps.setInt(1, npcId);
|
||||
rs = ps.executeQuery();
|
||||
|
||||
rs.next();
|
||||
ret = new MaplePlayerNPC(rs);
|
||||
} else {
|
||||
ret = null;
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
|
||||
return ret;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Integer> removePlayerNPCInternal(MapleMap map, MapleCharacter chr) {
|
||||
Set<Integer> updateMapids = new HashSet<>();
|
||||
|
||||
List<Integer> mapids = new LinkedList<>();
|
||||
mapids.add(chr.getWorld());
|
||||
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT id, map FROM playernpcs WHERE name LIKE ?" + (map != null ? " AND map = ?" : ""));
|
||||
ps.setString(1, chr.getName());
|
||||
if(map != null) ps.setInt(2, map.getId());
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while(rs.next()) {
|
||||
updateMapids.add(rs.getInt("map"));
|
||||
int npcId = rs.getInt("id");
|
||||
|
||||
PreparedStatement ps2 = con.prepareStatement("DELETE FROM playernpcs WHERE id = ?");
|
||||
ps2.setInt(1, npcId);
|
||||
ps2.executeUpdate();
|
||||
ps2.close();
|
||||
|
||||
ps2 = con.prepareStatement("DELETE FROM playernpcs_equip WHERE npcid = ?");
|
||||
ps2.setInt(1, npcId);
|
||||
ps2.executeUpdate();
|
||||
ps2.close();
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for(Integer i : updateMapids) {
|
||||
mapids.add(i);
|
||||
}
|
||||
|
||||
return mapids;
|
||||
}
|
||||
|
||||
private static synchronized Pair<MaplePlayerNPC, List<Integer>> processPlayerNPCInternal(MapleMap map, Point pos, MapleCharacter chr, boolean create) {
|
||||
if(create) {
|
||||
return new Pair<>(createPlayerNPCInternal(map, pos, chr), null);
|
||||
} else {
|
||||
return new Pair<>(null, removePlayerNPCInternal(map, chr));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean spawnPlayerNPC(int mapid, MapleCharacter chr) {
|
||||
return spawnPlayerNPC(mapid, null, chr);
|
||||
}
|
||||
|
||||
public static boolean spawnPlayerNPC(int mapid, Point pos, MapleCharacter chr) {
|
||||
MaplePlayerNPC pn = processPlayerNPCInternal(chr.getClient().getChannelServer().getMapFactory().getMap(mapid), pos, chr, true).getLeft();
|
||||
|
||||
if(pn != null) {
|
||||
for (Channel channel : Server.getInstance().getChannelsFromWorld(chr.getWorld())) {
|
||||
MapleMap m = channel.getMapFactory().getMap(mapid);
|
||||
|
||||
m.addPlayerNPCMapObject(pn);
|
||||
m.broadcastMessage(MaplePacketCreator.spawnPlayerNPC(pn));
|
||||
m.broadcastMessage(MaplePacketCreator.getPlayerNPC(pn));
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static MaplePlayerNPC getPlayerNPCFromWorldMap(String name, int world, int map) {
|
||||
World wserv = Server.getInstance().getWorld(world);
|
||||
for(MapleMapObject pnpcObj : wserv.getChannel(1).getMapFactory().getMap(map).getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.PLAYER_NPC))) {
|
||||
MaplePlayerNPC pn = (MaplePlayerNPC) pnpcObj;
|
||||
|
||||
if(name.contentEquals(pn.getName()) && pn.getScriptId() < 9977777) {
|
||||
return pn;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void removePlayerNPC(MapleCharacter chr) {
|
||||
List<Integer> updateMapids = processPlayerNPCInternal(null, null, chr, false).getRight();
|
||||
int worldid = updateMapids.remove(0);
|
||||
|
||||
for (Integer mapid : updateMapids) {
|
||||
MaplePlayerNPC pn = getPlayerNPCFromWorldMap(chr.getName(), worldid, mapid);
|
||||
|
||||
if(pn != null) {
|
||||
for (Channel channel : Server.getInstance().getChannelsFromWorld(worldid)) {
|
||||
MapleMap m = channel.getMapFactory().getMap(mapid);
|
||||
m.removeMapObject(pn);
|
||||
|
||||
m.broadcastMessage(MaplePacketCreator.removeNPCController(pn.getObjectId()));
|
||||
m.broadcastMessage(MaplePacketCreator.removePlayerNPC(pn.getObjectId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Integer> loadCharacteridsFromDB(int world) {
|
||||
List<Integer> list = new LinkedList<>();
|
||||
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT id FROM characters WHERE world = ?");
|
||||
ps.setInt(1, world);
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while(rs.next()) {
|
||||
list.add(rs.getInt(1));
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void multicastSpawnPlayerNPC(int mapid, int world) {
|
||||
MapleClient mockClient = new MapleClient(null, null, null);
|
||||
|
||||
for(Integer cid : loadCharacteridsFromDB(world)) {
|
||||
try {
|
||||
MapleCharacter mc = MapleCharacter.loadCharFromDB(cid, mockClient, false);
|
||||
spawnPlayerNPC(mapid, mc);
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeAllPlayerNPC() {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("SELECT DISTINCT world, map FROM playernpcs");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while(rs.next()) {
|
||||
int world = rs.getInt("world"), map = rs.getInt("map");
|
||||
|
||||
World w = Server.getInstance().getWorld(world);
|
||||
for (Channel channel : w.getChannels()) {
|
||||
MapleMap m = channel.getMapFactory().getMap(map);
|
||||
|
||||
for(MapleMapObject pnpcObj : m.getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.PLAYER_NPC))) {
|
||||
MaplePlayerNPC pn = (MaplePlayerNPC) pnpcObj;
|
||||
|
||||
m.removeMapObject(pn);
|
||||
m.broadcastMessage(MaplePacketCreator.removeNPCController(pn.getObjectId()));
|
||||
m.broadcastMessage(MaplePacketCreator.removePlayerNPC(pn.getObjectId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
|
||||
ps = con.prepareStatement("DELETE FROM playernpcs");
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
|
||||
ps = con.prepareStatement("DELETE FROM playernpcs_equip");
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2018 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package server.life;
|
||||
|
||||
import constants.GameConstants;
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class MaplePlayerNPCFactory {
|
||||
|
||||
private static final Map<Integer, List<MaplePlayerNPC>> dnpcMaps = new HashMap<>();
|
||||
private static Integer runningDeveloperOid = 2147483000; // 647 slots, long enough
|
||||
|
||||
public static void loadDeveloperRoomMetadata(MapleDataProvider npc) {
|
||||
MapleData thisData = npc.getData("9977777.img");
|
||||
if(thisData != null) {
|
||||
MapleDataProvider map = MapleDataProviderFactory.getDataProvider(new File("wz/Map.wz"));
|
||||
|
||||
thisData = map.getData("Map/Map7/777777777.img");
|
||||
if(thisData != null) {
|
||||
MapleDataProvider sound = MapleDataProviderFactory.getDataProvider(new File("wz/Sound.wz"));
|
||||
|
||||
thisData = sound.getData("Field.img");
|
||||
if(thisData != null) {
|
||||
MapleData md = thisData.getChildByPath("anthem/brazil");
|
||||
if(md != null) {
|
||||
GameConstants.setAvailableDeveloperRoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadFactoryMetadata() {
|
||||
MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz"));
|
||||
loadDeveloperRoomMetadata(npc);
|
||||
|
||||
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz"));
|
||||
MapleData dnpcData = etc.getData("DeveloperNpc.img");
|
||||
if(dnpcData != null) {
|
||||
for (MapleData data : dnpcData.getChildren()) {
|
||||
int scriptId = Integer.parseInt(data.getName());
|
||||
|
||||
String name = MapleDataTool.getString("name", data, "");
|
||||
int face = MapleDataTool.getIntConvert("face", data, 20000);
|
||||
int hair = MapleDataTool.getIntConvert("hair", data, 30000);
|
||||
int gender = MapleDataTool.getIntConvert("gender", data, 0);
|
||||
byte skin = (byte) MapleDataTool.getIntConvert("skin", data, 0);
|
||||
int dir = MapleDataTool.getIntConvert("dir", data, 0);
|
||||
int mapid = MapleDataTool.getIntConvert("map", data, 0);
|
||||
int FH = MapleDataTool.getIntConvert("fh", data, 0);
|
||||
int RX0 = MapleDataTool.getIntConvert("rx0", data, 0);
|
||||
int RX1 = MapleDataTool.getIntConvert("rx1", data, 0);
|
||||
int CX = MapleDataTool.getIntConvert("cx", data, 0);
|
||||
int CY = MapleDataTool.getIntConvert("cy", data, 0);
|
||||
|
||||
Map<Short, Integer> equips = new HashMap<>();
|
||||
for (MapleData edata : data.getChildByPath("equips").getChildren()) {
|
||||
short equippos = (short) MapleDataTool.getIntConvert("pos", edata);
|
||||
int equipid = MapleDataTool.getIntConvert("itemid", edata);
|
||||
|
||||
equips.put(equippos, equipid);
|
||||
}
|
||||
|
||||
List<MaplePlayerNPC> dnpcSet = dnpcMaps.get(mapid);
|
||||
if(dnpcSet == null) {
|
||||
dnpcSet = new LinkedList<>();
|
||||
dnpcMaps.put(mapid, dnpcSet);
|
||||
}
|
||||
|
||||
dnpcSet.add(new MaplePlayerNPC(name, scriptId, face, hair, gender, skin, equips, dir, FH, RX0, RX1, CX, CY, runningDeveloperOid));
|
||||
runningDeveloperOid++;
|
||||
}
|
||||
} else {
|
||||
MapleData thisData = npc.getData("9977777.img");
|
||||
|
||||
if(thisData != null) {
|
||||
byte[] encData = {0x52,0x6F,0x6E,0x61,0x6E};
|
||||
String name = new String(encData);
|
||||
int face = 20104, hair = 30215, gender = 0, skin = 0, dir = 0, mapid = 777777777;
|
||||
int FH = 4, RX0 = -143, RX1 = -243, CX = -193, CY = 117, scriptId = 9977777;
|
||||
|
||||
Map<Short, Integer> equips = new HashMap<>();
|
||||
equips.put((short) -1, 1002067);
|
||||
equips.put((short) -11, 1402046);
|
||||
equips.put((short) -8, 1082140);
|
||||
equips.put((short) -6, 1060091);
|
||||
equips.put((short) -7, 1072154);
|
||||
equips.put((short) -5, 1040103);
|
||||
|
||||
List<MaplePlayerNPC> dnpcSet = dnpcMaps.get(mapid);
|
||||
if(dnpcSet == null) {
|
||||
dnpcSet = new LinkedList<>();
|
||||
dnpcMaps.put(mapid, dnpcSet);
|
||||
}
|
||||
|
||||
dnpcSet.add(new MaplePlayerNPC(name, scriptId, face, hair, gender, (byte) skin, equips, dir, FH, RX0, RX1, CX, CY, runningDeveloperOid));
|
||||
runningDeveloperOid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<MaplePlayerNPC> getDeveloperNpcsFromMapid(int mapid) {
|
||||
return dnpcMaps.get(mapid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package server.maps;
|
||||
|
||||
import java.awt.Point;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import constants.ServerConstants;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
public class MapleKite extends AbstractMapleMapObject {
|
||||
|
||||
private Point pos;
|
||||
private MapleCharacter owner;
|
||||
private String text;
|
||||
private int ft;
|
||||
private int itemid;
|
||||
|
||||
public MapleKite(MapleCharacter owner, String text, int itemid) {
|
||||
this.owner = owner;
|
||||
this.pos = owner.getPosition();
|
||||
this.ft = owner.getFh();
|
||||
this.text = text;
|
||||
this.itemid = itemid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapleMapObjectType getType() {
|
||||
return MapleMapObjectType.KITE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getPosition() {
|
||||
return pos.getLocation();
|
||||
}
|
||||
|
||||
public MapleCharacter getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(Point position) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyData(MapleClient client) {
|
||||
client.getSession().write(makeDestroyData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(MapleClient client) {
|
||||
client.getSession().write(makeSpawnData());
|
||||
}
|
||||
|
||||
public final byte[] makeSpawnData() {
|
||||
return MaplePacketCreator.spawnKite(getObjectId(), itemid, owner.getName(), text, pos, ft);
|
||||
}
|
||||
|
||||
public final byte[] makeDestroyData() {
|
||||
return MaplePacketCreator.removeKite(getObjectId(), 0);
|
||||
}
|
||||
}
|
||||
+228
-10
@@ -59,6 +59,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Comparator;
|
||||
import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
import scripting.map.MapScriptManager;
|
||||
@@ -83,6 +84,7 @@ import server.partyquest.MonsterCarnival;
|
||||
import server.partyquest.MonsterCarnivalParty;
|
||||
//import server.partyquest.Pyramid;
|
||||
import scripting.event.EventInstanceManager;
|
||||
import server.life.MaplePlayerNPC;
|
||||
import server.life.MonsterListener;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
@@ -343,6 +345,15 @@ public class MapleMap {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void addPlayerNPCMapObject(MaplePlayerNPC pnpcobject) {
|
||||
objectWLock.lock();
|
||||
try {
|
||||
this.mapobjects.put(pnpcobject.getObjectId(), pnpcobject);
|
||||
} finally {
|
||||
objectWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void addMapObject(MapleMapObject mapobject) {
|
||||
objectWLock.lock();
|
||||
try {
|
||||
@@ -353,7 +364,7 @@ public class MapleMap {
|
||||
objectWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void spawnAndAddRangedMapObject(MapleMapObject mapobject, DelayedPacketCreation packetbakery) {
|
||||
spawnAndAddRangedMapObject(mapobject, packetbakery, null);
|
||||
}
|
||||
@@ -403,7 +414,7 @@ public class MapleMap {
|
||||
Integer curOid;
|
||||
|
||||
do {
|
||||
if ((curOid = runningOid.incrementAndGet()) < 0) {
|
||||
if ((curOid = runningOid.incrementAndGet()) < 0) { // clashes with playernpc on curOid >= 2147000000, developernpc uses >= 2147483000
|
||||
runningOid.set(curOid = 1000000001);
|
||||
}
|
||||
} while (mapobjects.containsKey(curOid));
|
||||
@@ -1995,6 +2006,21 @@ public class MapleMap {
|
||||
}, duration);
|
||||
}
|
||||
|
||||
public void spawnKite(final MapleKite kite) {
|
||||
addMapObject(kite);
|
||||
broadcastMessage(kite.makeSpawnData());
|
||||
|
||||
Runnable expireKite = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
removeMapObject(kite);
|
||||
broadcastMessage(kite.makeDestroyData());
|
||||
}
|
||||
};
|
||||
|
||||
Server.getInstance().getWorld(world).registerTimedMapObject(expireKite, ServerConstants.KITE_EXPIRE_TIME);
|
||||
}
|
||||
|
||||
public final void spawnItemDrop(final MapleMapObject dropper, final MapleCharacter owner, final Item item, Point pos, final boolean ffaDrop, final boolean playerDrop) {
|
||||
spawnItemDrop(dropper, owner, item, pos, (byte)(ffaDrop ? 2 : 0), playerDrop);
|
||||
}
|
||||
@@ -2278,7 +2304,7 @@ public class MapleMap {
|
||||
}
|
||||
}, 60 * 1000);
|
||||
} else if (mapid == 200090030) { // To Ereve (SkyFerry)
|
||||
chr.getClient().announce(MaplePacketCreator.getClock(60));
|
||||
chr.getClient().announce(MaplePacketCreator.getClock(2 * 60));
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
|
||||
@Override
|
||||
@@ -2287,9 +2313,9 @@ public class MapleMap {
|
||||
chr.changeMap(130000210, 0);
|
||||
}
|
||||
}
|
||||
}, 60 * 1000);
|
||||
}, 2 * 60 * 1000);
|
||||
} else if (mapid == 200090031) { // To Victoria Island (SkyFerry)
|
||||
chr.getClient().announce(MaplePacketCreator.getClock(60));
|
||||
chr.getClient().announce(MaplePacketCreator.getClock(2 * 60));
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
|
||||
@Override
|
||||
@@ -2298,9 +2324,9 @@ public class MapleMap {
|
||||
chr.changeMap(101000400, 0);
|
||||
}
|
||||
}
|
||||
}, 60 * 1000);
|
||||
}, 2 * 60 * 1000);
|
||||
} else if (mapid == 200090021) { // To Orbis (SkyFerry)
|
||||
chr.getClient().announce(MaplePacketCreator.getClock(60));
|
||||
chr.getClient().announce(MaplePacketCreator.getClock(8 * 60));
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
|
||||
@Override
|
||||
@@ -2309,9 +2335,9 @@ public class MapleMap {
|
||||
chr.changeMap(200000161, 0);
|
||||
}
|
||||
}
|
||||
}, 60 * 1000);
|
||||
}, 8 * 60 * 1000);
|
||||
} else if (mapid == 200090020) { // To Ereve From Orbis (SkyFerry)
|
||||
chr.getClient().announce(MaplePacketCreator.getClock(60));
|
||||
chr.getClient().announce(MaplePacketCreator.getClock(8 * 60));
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
|
||||
@Override
|
||||
@@ -2320,7 +2346,7 @@ public class MapleMap {
|
||||
chr.changeMap(130000210, 0);
|
||||
}
|
||||
}
|
||||
}, 60 * 1000);
|
||||
}, 8 * 60 * 1000);
|
||||
} else if (mapid == 103040400) {
|
||||
if (chr.getEventInstance() != null) {
|
||||
chr.getEventInstance().movePlayer(chr);
|
||||
@@ -2681,6 +2707,7 @@ public class MapleMap {
|
||||
case PLAYER_NPC:
|
||||
case DRAGON:
|
||||
case MIST:
|
||||
case KITE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -3762,4 +3789,195 @@ public class MapleMap {
|
||||
spawnMonsterOnGroundBelow(m, targetPoint);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPlayerNpcNearby(List<Point> otherPos, Point searchPos, int xLimit, int yLimit) {
|
||||
int xLimit2 = xLimit / 2, yLimit2 = yLimit / 2;
|
||||
|
||||
Rectangle searchRect = new Rectangle(searchPos.x - xLimit2, searchPos.y - yLimit2, xLimit, yLimit);
|
||||
for(Point pos : otherPos) {
|
||||
Rectangle otherRect = new Rectangle(pos.x - xLimit2, pos.y - yLimit2, xLimit, yLimit);
|
||||
|
||||
if(otherRect.intersects(searchRect)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int calcDx(int newStep) {
|
||||
return ServerConstants.PLAYERNPC_AREA_X / (newStep + 1);
|
||||
}
|
||||
|
||||
private static int calcDy(int newStep) {
|
||||
return (ServerConstants.PLAYERNPC_AREA_Y / 2) + (ServerConstants.PLAYERNPC_AREA_Y / (1 << (newStep + 1)));
|
||||
}
|
||||
|
||||
private List<Point> rearrangePlayerNpcPositions(int newStep, int pnpcsSize) {
|
||||
int leftPx = mapArea.x + ServerConstants.PLAYERNPC_INITIAL_X, px, py = mapArea.y + ServerConstants.PLAYERNPC_INITIAL_Y;
|
||||
int outx = mapArea.x + mapArea.width - ServerConstants.PLAYERNPC_INITIAL_X, outy = mapArea.y + mapArea.height;
|
||||
int cx = calcDx(newStep), cy = calcDy(newStep);
|
||||
|
||||
List<Point> otherPlayerNpcs = new LinkedList<>();
|
||||
while(py < outy) {
|
||||
px = leftPx;
|
||||
|
||||
while(px < outx) {
|
||||
Point searchPos = calcPointBelow(new Point(px, py));
|
||||
if(searchPos != null) {
|
||||
if(!isPlayerNpcNearby(otherPlayerNpcs, searchPos, cx, cy)) {
|
||||
otherPlayerNpcs.add(searchPos);
|
||||
|
||||
if(otherPlayerNpcs.size() == pnpcsSize) {
|
||||
return otherPlayerNpcs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
px += cx;
|
||||
}
|
||||
|
||||
py += cy;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Point rearrangePlayerNpcs(int newStep, List<MaplePlayerNPC> pnpcs) {
|
||||
int leftPx = mapArea.x + ServerConstants.PLAYERNPC_INITIAL_X, px, py = mapArea.y + ServerConstants.PLAYERNPC_INITIAL_Y;
|
||||
int outx = mapArea.x + mapArea.width - ServerConstants.PLAYERNPC_INITIAL_X, outy = mapArea.y + mapArea.height;
|
||||
int cx = calcDx(newStep), cy = calcDy(newStep);
|
||||
|
||||
List<Point> otherPlayerNpcs = new LinkedList<>();
|
||||
int i = 0;
|
||||
|
||||
while(py < outy) {
|
||||
px = leftPx;
|
||||
|
||||
while(px < outx) {
|
||||
Point searchPos = calcPointBelow(new Point(px, py));
|
||||
if(searchPos != null) {
|
||||
if(!isPlayerNpcNearby(otherPlayerNpcs, searchPos, cx, cy)) {
|
||||
if(i == pnpcs.size()) {
|
||||
return searchPos;
|
||||
}
|
||||
|
||||
MaplePlayerNPC pn = pnpcs.get(i);
|
||||
i++;
|
||||
|
||||
pn.updatePlayerNPCPosition(this, searchPos);
|
||||
otherPlayerNpcs.add(searchPos);
|
||||
}
|
||||
}
|
||||
|
||||
px += cx;
|
||||
}
|
||||
|
||||
py += cy;
|
||||
}
|
||||
|
||||
return null; // this area should not be reached under any scenario
|
||||
}
|
||||
|
||||
private Point reorganizePlayerNpcs(int newStep, List<MapleMapObject> mmoList) {
|
||||
if(!mmoList.isEmpty()) {
|
||||
List<MaplePlayerNPC> playerNpcs = new ArrayList<>(mmoList.size());
|
||||
for(MapleMapObject mmo : mmoList) {
|
||||
playerNpcs.add((MaplePlayerNPC) mmo);
|
||||
}
|
||||
|
||||
Collections.sort(playerNpcs, new Comparator<MaplePlayerNPC>() {
|
||||
@Override
|
||||
public int compare(MaplePlayerNPC p1, MaplePlayerNPC p2) {
|
||||
return p1.getScriptId() - p2.getScriptId(); // scriptid as playernpc history
|
||||
}
|
||||
});
|
||||
|
||||
for(Channel ch : Server.getInstance().getChannelsFromWorld(world)) {
|
||||
MapleMap map = ch.getMapFactory().getMap(mapid);
|
||||
|
||||
for(MaplePlayerNPC pn : playerNpcs) {
|
||||
map.removeMapObject(pn);
|
||||
map.broadcastMessage(MaplePacketCreator.removeNPCController(pn.getObjectId()));
|
||||
map.broadcastMessage(MaplePacketCreator.removePlayerNPC(pn.getObjectId()));
|
||||
}
|
||||
}
|
||||
|
||||
Point ret = rearrangePlayerNpcs(newStep, playerNpcs);
|
||||
|
||||
for(Channel ch : Server.getInstance().getChannelsFromWorld(world)) {
|
||||
MapleMap map = ch.getMapFactory().getMap(mapid);
|
||||
|
||||
for(MaplePlayerNPC pn : playerNpcs) {
|
||||
map.addPlayerNPCMapObject(pn);
|
||||
map.broadcastMessage(MaplePacketCreator.spawnPlayerNPC(pn));
|
||||
map.broadcastMessage(MaplePacketCreator.getPlayerNPC(pn));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Point getNextPlayerNpcPosition() {
|
||||
return getNextPlayerNpcPosition(Server.getInstance().getWorld(world).getPlayerNpcMapStep(mapid));
|
||||
}
|
||||
|
||||
private Point getNextPlayerNpcPosition(int initStep) { // automated playernpc position thanks to Ronan
|
||||
List<MapleMapObject> mmoList = getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.PLAYER_NPC));
|
||||
List<Point> otherPlayerNpcs = new LinkedList<>();
|
||||
for(MapleMapObject mmo : mmoList) {
|
||||
otherPlayerNpcs.add(mmo.getPosition());
|
||||
}
|
||||
|
||||
int cx = calcDx(initStep), cy = calcDy(initStep);
|
||||
int outx = mapArea.x + mapArea.width - ServerConstants.PLAYERNPC_INITIAL_X, outy = mapArea.y + mapArea.height;
|
||||
boolean reorganize = false;
|
||||
|
||||
int i = initStep;
|
||||
while(i < ServerConstants.PLAYERNPC_AREA_STEPS) {
|
||||
int leftPx = mapArea.x + ServerConstants.PLAYERNPC_INITIAL_X, px, py = mapArea.y + ServerConstants.PLAYERNPC_INITIAL_Y;
|
||||
|
||||
while(py < outy) {
|
||||
px = leftPx;
|
||||
|
||||
while(px < outx) {
|
||||
Point searchPos = calcPointBelow(new Point(px, py));
|
||||
if(searchPos != null) {
|
||||
if(!isPlayerNpcNearby(otherPlayerNpcs, searchPos, cx, cy)) {
|
||||
if(i > initStep) {
|
||||
Server.getInstance().getWorld(world).setPlayerNpcMapStep(mapid, i);
|
||||
}
|
||||
|
||||
if(reorganize && ServerConstants.PLAYERNPC_ORGANIZE_AREA) {
|
||||
return reorganizePlayerNpcs(i, mmoList);
|
||||
}
|
||||
|
||||
return searchPos;
|
||||
}
|
||||
}
|
||||
|
||||
px += cx;
|
||||
}
|
||||
|
||||
py += cy;
|
||||
}
|
||||
|
||||
reorganize = true;
|
||||
i++;
|
||||
|
||||
cx = calcDx(i);
|
||||
cy = calcDy(i);
|
||||
if(ServerConstants.PLAYERNPC_ORGANIZE_AREA) {
|
||||
otherPlayerNpcs = rearrangePlayerNpcPositions(i, mmoList.size());
|
||||
}
|
||||
}
|
||||
|
||||
if(i > initStep) {
|
||||
Server.getInstance().getWorld(world).setPlayerNpcMapStep(mapid, ServerConstants.PLAYERNPC_AREA_STEPS - 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +27,12 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import tools.locks.MonitoredReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
@@ -44,6 +44,8 @@ import server.PortalFactory;
|
||||
import server.life.AbstractLoadedMapleLife;
|
||||
import server.life.MapleLifeFactory;
|
||||
import server.life.MapleMonster;
|
||||
import server.life.MaplePlayerNPC;
|
||||
import server.life.MaplePlayerNPCFactory;
|
||||
import scripting.event.EventInstanceManager;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.StringUtil;
|
||||
@@ -198,22 +200,30 @@ public class MapleMapFactory {
|
||||
map.addMapleArea(new Rectangle(x1, y1, (x2 - x1), (y2 - y1)));
|
||||
}
|
||||
}
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM playernpcs WHERE map = ?")) {
|
||||
ps.setInt(1, omapid);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
map.addMapObject(new PlayerNPCs(rs));
|
||||
if(event == null) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM playernpcs WHERE map = ? AND world = ?")) {
|
||||
ps.setInt(1, omapid);
|
||||
ps.setInt(2, world);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
map.addPlayerNPCMapObject(new MaplePlayerNPC(rs));
|
||||
}
|
||||
}
|
||||
}
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
List<MaplePlayerNPC> dnpcs = MaplePlayerNPCFactory.getDeveloperNpcsFromMapid(mapid);
|
||||
if(dnpcs != null) {
|
||||
for(MaplePlayerNPC dnpc : dnpcs) {
|
||||
map.addPlayerNPCMapObject(dnpc);
|
||||
}
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for (MapleData life : mapData.getChildByPath("life")) {
|
||||
String id = MapleDataTool.getString(life.getChildByPath("id"));
|
||||
String type = MapleDataTool.getString(life.getChildByPath("type"));
|
||||
@@ -319,8 +329,6 @@ public class MapleMapFactory {
|
||||
|
||||
private AbstractLoadedMapleLife loadLife(MapleData life, String id, String type) {
|
||||
AbstractLoadedMapleLife myLife = MapleLifeFactory.getLife(Integer.parseInt(id), type);
|
||||
if(life == null) System.out.println("lf null");
|
||||
if(myLife == null) System.out.println("mlf null");
|
||||
myLife.setCy(MapleDataTool.getInt(life.getChildByPath("cy")));
|
||||
MapleData dF = life.getChildByPath("f");
|
||||
if (dF != null) {
|
||||
|
||||
@@ -22,5 +22,5 @@
|
||||
package server.maps;
|
||||
|
||||
public enum MapleMapObjectType {
|
||||
NPC, MONSTER, ITEM, PLAYER, DOOR, SUMMON, SHOP, MINI_GAME, MIST, REACTOR, HIRED_MERCHANT, PLAYER_NPC, DRAGON;
|
||||
NPC, MONSTER, ITEM, PLAYER, DOOR, SUMMON, SHOP, MINI_GAME, MIST, REACTOR, HIRED_MERCHANT, PLAYER_NPC, DRAGON, KITE;
|
||||
}
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
This file is part of the OdinMS Maple Story Server
|
||||
Copyright (C) 2008 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 as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package server.maps;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import client.MapleClient;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author XoticStory
|
||||
*/
|
||||
public class PlayerNPCs extends AbstractMapleMapObject {
|
||||
private Map<Short, Integer> equips = new HashMap<Short, Integer>();
|
||||
private int npcId, face, hair;
|
||||
private byte skin;
|
||||
private String name = "";
|
||||
private int FH, RX0, RX1, CY;
|
||||
|
||||
public PlayerNPCs(ResultSet rs) {
|
||||
try {
|
||||
CY = rs.getInt("cy");
|
||||
name = rs.getString("name");
|
||||
hair = rs.getInt("hair");
|
||||
face = rs.getInt("face");
|
||||
skin = rs.getByte("skin");
|
||||
FH = rs.getInt("Foothold");
|
||||
RX0 = rs.getInt("rx0");
|
||||
RX1 = rs.getInt("rx1");
|
||||
npcId = rs.getInt("ScriptId");
|
||||
setPosition(new Point(rs.getInt("x"), CY));
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT equippos, equipid FROM playernpcs_equip WHERE NpcId = ?");
|
||||
ps.setInt(1, rs.getInt("id"));
|
||||
ResultSet rs2 = ps.executeQuery();
|
||||
while (rs2.next()) {
|
||||
equips.put(rs2.getShort("equippos"), rs2.getInt("equipid"));
|
||||
}
|
||||
rs2.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<Short, Integer> getEquips() {
|
||||
return equips;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return npcId;
|
||||
}
|
||||
|
||||
public int getFH() {
|
||||
return FH;
|
||||
}
|
||||
|
||||
public int getRX0() {
|
||||
return RX0;
|
||||
}
|
||||
|
||||
public int getRX1() {
|
||||
return RX1;
|
||||
}
|
||||
|
||||
public int getCY() {
|
||||
return CY;
|
||||
}
|
||||
|
||||
public byte getSkin() {
|
||||
return skin;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getFace() {
|
||||
return face;
|
||||
}
|
||||
|
||||
public int getHair() {
|
||||
return hair;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyData(MapleClient client) {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapleMapObjectType getType() {
|
||||
return MapleMapObjectType.PLAYER_NPC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(MapleClient client) {
|
||||
client.announce(MaplePacketCreator.spawnPlayerNPC(this));
|
||||
client.announce(MaplePacketCreator.getPlayerNPC(this));
|
||||
}
|
||||
}
|
||||
@@ -22,15 +22,16 @@
|
||||
package server.maps;
|
||||
|
||||
public enum SavedLocationType {
|
||||
FREE_MARKET,
|
||||
EVENT,
|
||||
WORLDTOUR,
|
||||
FLORINA,
|
||||
INTRO,
|
||||
SUNDAY_MARKET,
|
||||
MIRROR,
|
||||
FREE_MARKET,
|
||||
WORLDTOUR,
|
||||
FLORINA,
|
||||
INTRO,
|
||||
SUNDAY_MARKET,
|
||||
MIRROR,
|
||||
EVENT,
|
||||
BOSSPQ,
|
||||
HAPPYVILLE;
|
||||
HAPPYVILLE,
|
||||
DEVELOPER;
|
||||
|
||||
public static SavedLocationType fromString(String Str) {
|
||||
return valueOf(Str);
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.Map;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleQuestStatus;
|
||||
import client.MapleQuestStatus.Status;
|
||||
import constants.ServerConstants;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Set;
|
||||
import provider.MapleData;
|
||||
@@ -39,6 +40,7 @@ import provider.MapleDataTool;
|
||||
import server.quest.actions.*;
|
||||
import server.quest.requirements.*;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.StringUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -323,6 +325,19 @@ public class MapleQuest {
|
||||
}
|
||||
|
||||
c.updateQuest(newStatus);
|
||||
|
||||
if(id / 100 == 35 && ServerConstants.TOT_MOB_QUEST_REQUIREMENT > 0) {
|
||||
int setProg = 999 - Math.min(999, ServerConstants.TOT_MOB_QUEST_REQUIREMENT);
|
||||
|
||||
for(Integer pid : newStatus.getProgress().keySet()) {
|
||||
if(pid >= 8200000 && pid <= 8200012) {
|
||||
String pr = StringUtil.getLeftPaddedStr(Integer.toString(setProg), '0', 3);
|
||||
newStatus.setProgress(pid, pr);
|
||||
c.announce(MaplePacketCreator.updateQuest(newStatus, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user