Switch to Maven file structure
This commit is contained in:
593
src/main/java/server/CashShop.java
Normal file
593
src/main/java/server/CashShop.java
Normal file
@@ -0,0 +1,593 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
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 config.YamlConfig;
|
||||
import net.server.Server;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.Pair;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.ItemFactory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.MaplePet;
|
||||
import constants.inventory.ItemConstants;
|
||||
import java.util.Collections;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
|
||||
/*
|
||||
* @author Flav
|
||||
*/
|
||||
public class CashShop {
|
||||
public static class CashItem {
|
||||
|
||||
private int sn, itemId, price;
|
||||
private long period;
|
||||
private short count;
|
||||
private boolean onSale;
|
||||
|
||||
private CashItem(int sn, int itemId, int price, long period, short count, boolean onSale) {
|
||||
this.sn = sn;
|
||||
this.itemId = itemId;
|
||||
this.price = price;
|
||||
this.period = (period == 0 ? 90 : period);
|
||||
this.count = count;
|
||||
this.onSale = onSale;
|
||||
}
|
||||
|
||||
public int getSN() {
|
||||
return sn;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public int getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public short getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public boolean isOnSale() {
|
||||
return onSale;
|
||||
}
|
||||
|
||||
public Item toItem() {
|
||||
Item item;
|
||||
|
||||
int petid = -1;
|
||||
if (ItemConstants.isPet(itemId)) {
|
||||
petid = MaplePet.createPet(itemId);
|
||||
}
|
||||
|
||||
if (ItemConstants.getInventoryType(itemId).equals(MapleInventoryType.EQUIP)) {
|
||||
item = MapleItemInformationProvider.getInstance().getEquipById(itemId);
|
||||
} else {
|
||||
item = new Item(itemId, (byte) 0, count, petid);
|
||||
}
|
||||
|
||||
if (ItemConstants.EXPIRING_ITEMS) {
|
||||
if(period == 1) {
|
||||
if(itemId == 5211048 || itemId == 5360042) { // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day.
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 4));
|
||||
/*
|
||||
} else if(itemId == 5211047 || itemId == 5360014) { // 3 Hour 2X coupons, unused as of now
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 3));
|
||||
*/
|
||||
} else if(itemId == 5211060) { // 2 Hour 3X coupons.
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 2));
|
||||
} else {
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 24));
|
||||
}
|
||||
} else {
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 24 * period));
|
||||
}
|
||||
}
|
||||
|
||||
item.setSN(sn);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SpecialCashItem {
|
||||
private int sn, modifier;
|
||||
private byte info; //?
|
||||
|
||||
public SpecialCashItem(int sn, int modifier, byte info) {
|
||||
this.sn = sn;
|
||||
this.modifier = modifier;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public int getSN() {
|
||||
return sn;
|
||||
}
|
||||
|
||||
public int getModifier() {
|
||||
return modifier;
|
||||
}
|
||||
|
||||
public byte getInfo() {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CashItemFactory {
|
||||
|
||||
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> randomitemsns = new ArrayList<>();
|
||||
|
||||
static {
|
||||
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz"));
|
||||
|
||||
for (MapleData item : etc.getData("Commodity.img").getChildren()) {
|
||||
int sn = MapleDataTool.getIntConvert("SN", item);
|
||||
int itemId = MapleDataTool.getIntConvert("ItemId", item);
|
||||
int price = MapleDataTool.getIntConvert("Price", item, 0);
|
||||
long period = MapleDataTool.getIntConvert("Period", item, 1);
|
||||
short count = (short) MapleDataTool.getIntConvert("Count", item, 1);
|
||||
boolean onSale = MapleDataTool.getIntConvert("OnSale", item, 0) == 1;
|
||||
items.put(sn, new CashItem(sn, itemId, price, period, count, onSale));
|
||||
}
|
||||
|
||||
for (MapleData cashPackage : etc.getData("CashPackage.img").getChildren()) {
|
||||
List<Integer> cPackage = new ArrayList<>();
|
||||
|
||||
for (MapleData item : cashPackage.getChildByPath("SN").getChildren()) {
|
||||
cPackage.add(Integer.parseInt(item.getData().toString()));
|
||||
}
|
||||
|
||||
packages.put(Integer.parseInt(cashPackage.getName()), cPackage);
|
||||
}
|
||||
|
||||
for(Entry<Integer, CashItem> e : items.entrySet()) {
|
||||
if(e.getValue().isOnSale()) {
|
||||
randomitemsns.add(e.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
Connection con = null;
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
ps = con.prepareStatement("SELECT * FROM specialcashitems");
|
||||
rs = ps.executeQuery();
|
||||
while (rs.next()) {
|
||||
specialcashitems.add(new SpecialCashItem(rs.getInt("sn"), rs.getInt("modifier"), rs.getByte("info")));
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (rs != null && !rs.isClosed()) rs.close();
|
||||
if (ps != null && !ps.isClosed()) ps.close();
|
||||
if (con != null && !con.isClosed()) con.close();
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static CashItem getRandomCashItem() {
|
||||
if(randomitemsns.isEmpty()) return null;
|
||||
|
||||
int rnd = (int)(Math.random() * randomitemsns.size());
|
||||
return items.get(randomitemsns.get(rnd));
|
||||
}
|
||||
|
||||
public static CashItem getItem(int sn) {
|
||||
return items.get(sn);
|
||||
}
|
||||
|
||||
public static List<Item> getPackage(int itemId) {
|
||||
List<Item> cashPackage = new ArrayList<>();
|
||||
|
||||
for (int sn : packages.get(itemId)) {
|
||||
cashPackage.add(getItem(sn).toItem());
|
||||
}
|
||||
|
||||
return cashPackage;
|
||||
}
|
||||
|
||||
public static boolean isPackage(int itemId) {
|
||||
return packages.containsKey(itemId);
|
||||
}
|
||||
|
||||
public static List<SpecialCashItem> getSpecialCashItems() {
|
||||
return specialcashitems;
|
||||
}
|
||||
|
||||
public static void reloadSpecialCashItems() {//Yay?
|
||||
specialcashitems.clear();
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
Connection con = null;
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
ps = con.prepareStatement("SELECT * FROM specialcashitems");
|
||||
rs = ps.executeQuery();
|
||||
while (rs.next()) {
|
||||
specialcashitems.add(new SpecialCashItem(rs.getInt("sn"), rs.getInt("modifier"), rs.getByte("info")));
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (rs != null && !rs.isClosed()) rs.close();
|
||||
if (ps != null && !ps.isClosed()) ps.close();
|
||||
if (con != null && !con.isClosed()) con.close();
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int accountId, characterId, nxCredit, maplePoint, nxPrepaid;
|
||||
private boolean opened;
|
||||
private ItemFactory factory;
|
||||
private List<Item> inventory = new ArrayList<>();
|
||||
private List<Integer> wishList = new ArrayList<>();
|
||||
private int notes = 0;
|
||||
private Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CASHSHOP);
|
||||
|
||||
public CashShop(int accountId, int characterId, int jobType) throws SQLException {
|
||||
this.accountId = accountId;
|
||||
this.characterId = characterId;
|
||||
|
||||
if (!YamlConfig.config.server.USE_JOINT_CASHSHOP_INVENTORY) {
|
||||
if (jobType == 0) {
|
||||
factory = ItemFactory.CASH_EXPLORER;
|
||||
} else if (jobType == 1) {
|
||||
factory = ItemFactory.CASH_CYGNUS;
|
||||
} else if (jobType == 2) {
|
||||
factory = ItemFactory.CASH_ARAN;
|
||||
}
|
||||
} else {
|
||||
factory = ItemFactory.CASH_OVERALL;
|
||||
}
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
ps = con.prepareStatement("SELECT `nxCredit`, `maplePoint`, `nxPrepaid` FROM `accounts` WHERE `id` = ?");
|
||||
ps.setInt(1, accountId);
|
||||
rs = ps.executeQuery();
|
||||
|
||||
if (rs.next()) {
|
||||
this.nxCredit = rs.getInt("nxCredit");
|
||||
this.maplePoint = rs.getInt("maplePoint");
|
||||
this.nxPrepaid = rs.getInt("nxPrepaid");
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
|
||||
for (Pair<Item, MapleInventoryType> item : factory.loadItems(accountId, false)) {
|
||||
inventory.add(item.getLeft());
|
||||
}
|
||||
|
||||
ps = con.prepareStatement("SELECT `sn` FROM `wishlists` WHERE `charid` = ?");
|
||||
ps.setInt(1, characterId);
|
||||
rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
wishList.add(rs.getInt("sn"));
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} finally {
|
||||
if (ps != null && !ps.isClosed()) ps.close();
|
||||
if (rs != null && !rs.isClosed()) rs.close();
|
||||
if (con != null && !con.isClosed()) con.close();
|
||||
}
|
||||
}
|
||||
|
||||
public int getCash(int type) {
|
||||
switch (type) {
|
||||
case 1:
|
||||
return nxCredit;
|
||||
case 2:
|
||||
return maplePoint;
|
||||
case 4:
|
||||
return nxPrepaid;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void gainCash(int type, int cash) {
|
||||
switch (type) {
|
||||
case 1:
|
||||
nxCredit += cash;
|
||||
break;
|
||||
case 2:
|
||||
maplePoint += cash;
|
||||
break;
|
||||
case 4:
|
||||
nxPrepaid += cash;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void gainCash(int type, CashItem buyItem, int world) {
|
||||
gainCash(type, -buyItem.getPrice());
|
||||
if(!YamlConfig.config.server.USE_ENFORCE_ITEM_SUGGESTION) Server.getInstance().getWorld(world).addCashItemBought(buyItem.getSN());
|
||||
}
|
||||
|
||||
public boolean isOpened() {
|
||||
return opened;
|
||||
}
|
||||
|
||||
public void open(boolean b) {
|
||||
opened = b;
|
||||
}
|
||||
|
||||
public List<Item> getInventory() {
|
||||
lock.lock();
|
||||
try {
|
||||
return Collections.unmodifiableList(inventory);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Item findByCashId(int cashId) {
|
||||
boolean isRing;
|
||||
Equip equip = null;
|
||||
for (Item item : getInventory()) {
|
||||
if (item.getInventoryType().equals(MapleInventoryType.EQUIP)) {
|
||||
equip = (Equip) item;
|
||||
isRing = equip.getRingId() > -1;
|
||||
} else {
|
||||
isRing = false;
|
||||
}
|
||||
|
||||
if ((item.getPetId() > -1 ? item.getPetId() : isRing ? equip.getRingId() : item.getCashId()) == cashId) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addToInventory(Item item) {
|
||||
lock.lock();
|
||||
try {
|
||||
inventory.add(item);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeFromInventory(Item item) {
|
||||
lock.lock();
|
||||
try {
|
||||
inventory.remove(item);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Integer> getWishList() {
|
||||
return wishList;
|
||||
}
|
||||
|
||||
public void clearWishList() {
|
||||
wishList.clear();
|
||||
}
|
||||
|
||||
public void addToWishList(int sn) {
|
||||
wishList.add(sn);
|
||||
}
|
||||
|
||||
public void gift(int recipient, String from, String message, int sn) {
|
||||
gift(recipient, from, message, sn, -1);
|
||||
}
|
||||
|
||||
public void gift(int recipient, String from, String message, int sn, int ringid) {
|
||||
PreparedStatement ps = null;
|
||||
Connection con = null;
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
ps = con.prepareStatement("INSERT INTO `gifts` VALUES (DEFAULT, ?, ?, ?, ?, ?)");
|
||||
ps.setInt(1, recipient);
|
||||
ps.setString(2, from);
|
||||
ps.setString(3, message);
|
||||
ps.setInt(4, sn);
|
||||
ps.setInt(5, ringid);
|
||||
ps.executeUpdate();
|
||||
con.close();
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (ps != null && !ps.isClosed()) ps.close();
|
||||
if (con != null && !con.isClosed()) con.close();
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Pair<Item, String>> loadGifts() {
|
||||
List<Pair<Item, String>> gifts = new ArrayList<>();
|
||||
Connection con = null;
|
||||
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT * FROM `gifts` WHERE `to` = ?");
|
||||
ps.setInt(1, characterId);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
notes++;
|
||||
CashItem cItem = CashItemFactory.getItem(rs.getInt("sn"));
|
||||
Item item = cItem.toItem();
|
||||
Equip equip = null;
|
||||
item.setGiftFrom(rs.getString("from"));
|
||||
if (item.getInventoryType().equals(MapleInventoryType.EQUIP)) {
|
||||
equip = (Equip) item;
|
||||
equip.setRingId(rs.getInt("ringid"));
|
||||
gifts.add(new Pair<Item, String>(equip, rs.getString("message")));
|
||||
} else
|
||||
gifts.add(new Pair<>(item, rs.getString("message")));
|
||||
|
||||
if (CashItemFactory.isPackage(cItem.getItemId())) { //Packages never contains a ring
|
||||
for (Item packageItem : CashItemFactory.getPackage(cItem.getItemId())) {
|
||||
packageItem.setGiftFrom(rs.getString("from"));
|
||||
addToInventory(packageItem);
|
||||
}
|
||||
} else {
|
||||
addToInventory(equip == null ? item : equip);
|
||||
}
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
ps = con.prepareStatement("DELETE FROM `gifts` WHERE `to` = ?");
|
||||
ps.setInt(1, characterId);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
|
||||
return gifts;
|
||||
}
|
||||
|
||||
public int getAvailableNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public void decreaseNotes() {
|
||||
notes--;
|
||||
}
|
||||
|
||||
public void save(Connection con) throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement("UPDATE `accounts` SET `nxCredit` = ?, `maplePoint` = ?, `nxPrepaid` = ? WHERE `id` = ?");
|
||||
ps.setInt(1, nxCredit);
|
||||
ps.setInt(2, maplePoint);
|
||||
ps.setInt(3, nxPrepaid);
|
||||
ps.setInt(4, accountId);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
List<Pair<Item, MapleInventoryType>> itemsWithType = new ArrayList<>();
|
||||
|
||||
List<Item> inv = getInventory();
|
||||
for (Item item : inv) {
|
||||
itemsWithType.add(new Pair<>(item, item.getInventoryType()));
|
||||
}
|
||||
|
||||
factory.saveItems(itemsWithType, accountId, con);
|
||||
ps = con.prepareStatement("DELETE FROM `wishlists` WHERE `charid` = ?");
|
||||
ps.setInt(1, characterId);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
ps = con.prepareStatement("INSERT INTO `wishlists` VALUES (DEFAULT, ?, ?)");
|
||||
ps.setInt(1, characterId);
|
||||
|
||||
for (int sn : wishList) {
|
||||
ps.setInt(2, sn);
|
||||
ps.executeUpdate();
|
||||
}
|
||||
|
||||
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 Pair<Item, 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 new Pair<>(item, css);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Item generateCouponItem(int itemId, short quantity) {
|
||||
CashItem it = new CashItem(77777777, itemId, 7777, ItemConstants.isPet(itemId) ? 30 : 0, quantity, true);
|
||||
return it.toItem();
|
||||
}
|
||||
}
|
||||
111
src/main/java/server/DueyPackage.java
Normal file
111
src/main/java/server/DueyPackage.java
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import client.inventory.Item;
|
||||
import java.util.Calendar;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
public class DueyPackage {
|
||||
private String sender = null;
|
||||
private Item item = null;
|
||||
private int mesos = 0;
|
||||
private String message = null;
|
||||
private Calendar timestamp;
|
||||
private int packageId = 0;
|
||||
|
||||
public DueyPackage(int pId, Item item) {
|
||||
this.item = item;
|
||||
packageId = pId;
|
||||
}
|
||||
|
||||
public DueyPackage(int pId) { // Meso only package.
|
||||
this.packageId = pId;
|
||||
}
|
||||
|
||||
public String getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public void setSender(String name) {
|
||||
sender = name;
|
||||
}
|
||||
|
||||
public Item getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public int getMesos() {
|
||||
return mesos;
|
||||
}
|
||||
|
||||
public void setMesos(int set) {
|
||||
mesos = set;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String m) {
|
||||
message = m;
|
||||
}
|
||||
|
||||
public int getPackageId() {
|
||||
return packageId;
|
||||
}
|
||||
|
||||
public long sentTimeInMilliseconds() {
|
||||
Calendar ts = timestamp;
|
||||
if (ts != null) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(ts.getTime());
|
||||
cal.add(Calendar.MONTH, 1); // duey representation is in an array of months.
|
||||
|
||||
return cal.getTimeInMillis();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDeliveringTime() {
|
||||
Calendar ts = timestamp;
|
||||
if (ts != null) {
|
||||
return ts.getTimeInMillis() >= System.currentTimeMillis();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSentTime(Timestamp ts, boolean quick) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(ts.getTime());
|
||||
|
||||
if (quick) {
|
||||
if (System.currentTimeMillis() - ts.getTime() < 24 * 60 * 60 * 1000) { // thanks inhyuk for noticing quick delivery packages unavailable to retrieve from the get-go
|
||||
cal.add(Calendar.DATE, -1);
|
||||
}
|
||||
}
|
||||
|
||||
this.timestamp = cal;
|
||||
}
|
||||
}
|
||||
73
src/main/java/server/MTSItemInfo.java
Normal file
73
src/main/java/server/MTSItemInfo.java
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import client.inventory.Item;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Traitor
|
||||
*/
|
||||
public class MTSItemInfo {
|
||||
private int price;
|
||||
private Item item;
|
||||
private String seller;
|
||||
private int id;
|
||||
private int year, month, day = 1;
|
||||
|
||||
public MTSItemInfo(Item item, int price, int id, int cid, String seller, String date) {
|
||||
this.item = item;
|
||||
this.price = price;
|
||||
this.seller = seller;
|
||||
this.id = id;
|
||||
this.year = Integer.parseInt(date.substring(0, 4));
|
||||
this.month = Integer.parseInt(date.substring(5, 7));
|
||||
this.day = Integer.parseInt(date.substring(8, 10));
|
||||
}
|
||||
|
||||
public Item getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public int getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public int getTaxes() {
|
||||
return 100 + price / 10;
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public long getEndingDate() {
|
||||
Calendar now = Calendar.getInstance();
|
||||
now.set(year, month - 1, day);
|
||||
return now.getTimeInMillis();
|
||||
}
|
||||
|
||||
public String getSeller() {
|
||||
return seller;
|
||||
}
|
||||
}
|
||||
214
src/main/java/server/MakerItemFactory.java
Normal file
214
src/main/java/server/MakerItemFactory.java
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import config.YamlConfig;
|
||||
import constants.inventory.EquipType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jay Estrella, Ronan
|
||||
*/
|
||||
public class MakerItemFactory {
|
||||
private static MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
|
||||
public static MakerItemCreateEntry getItemCreateEntry(int toCreate, int stimulantid, Map<Integer, Short> reagentids) {
|
||||
MakerItemCreateEntry makerEntry = ii.getMakerItemEntry(toCreate);
|
||||
if(makerEntry.isInvalid()) {
|
||||
return makerEntry;
|
||||
}
|
||||
|
||||
// THEY DECIDED FOR SOME BIZARRE PATTERN ON THE FEE THING, ALMOST RANDOMIZED.
|
||||
if(stimulantid != -1) {
|
||||
makerEntry.addCost(getMakerStimulantFee(toCreate));
|
||||
}
|
||||
|
||||
if(!reagentids.isEmpty()) {
|
||||
for(Entry<Integer, Short> r : reagentids.entrySet()) {
|
||||
makerEntry.addCost((getMakerReagentFee(toCreate, ((r.getKey() % 10) + 1))) * r.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
makerEntry.trimCost(); // "commit" the real cost of the recipe.
|
||||
return makerEntry;
|
||||
}
|
||||
|
||||
public static MakerItemCreateEntry generateLeftoverCrystalEntry(int fromLeftoverid, int crystalId) {
|
||||
MakerItemCreateEntry ret = new MakerItemCreateEntry(0, 0, 1);
|
||||
ret.addReqItem(fromLeftoverid, 100);
|
||||
ret.addGainItem(crystalId, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static MakerItemCreateEntry generateDisassemblyCrystalEntry(int fromEquipid, int cost, List<Pair<Integer, Integer>> gains) { // equipment at specific position already taken
|
||||
MakerItemCreateEntry ret = new MakerItemCreateEntry(cost, 0, 1);
|
||||
ret.addReqItem(fromEquipid, 1);
|
||||
for (Pair<Integer, Integer> p : gains) {
|
||||
ret.addGainItem(p.getLeft(), p.getRight());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static double getMakerStimulantFee(int itemid) {
|
||||
if(YamlConfig.config.server.USE_MAKER_FEE_HEURISTICS) {
|
||||
EquipType et = EquipType.getEquipTypeById(itemid);
|
||||
int eqpLevel = ii.getEquipLevelReq(itemid);
|
||||
|
||||
switch(et) {
|
||||
case CAP:
|
||||
return 1145.736246 * Math.exp(0.03336832546 * eqpLevel);
|
||||
|
||||
case LONGCOAT:
|
||||
return 2117.469118 * Math.exp(0.03355349137 * eqpLevel);
|
||||
|
||||
case SHOES:
|
||||
return 1218.624674 * Math.exp(0.0342266043 * eqpLevel);
|
||||
|
||||
case GLOVES:
|
||||
return 2129.531152 * Math.exp(0.03421778102 * eqpLevel);
|
||||
|
||||
case COAT:
|
||||
return 1770.630579 * Math.exp(0.03359768677 * eqpLevel);
|
||||
|
||||
case PANTS:
|
||||
return 1442.98837 * Math.exp(0.03444783295 * eqpLevel);
|
||||
|
||||
case SHIELD:
|
||||
return 6312.40136 * Math.exp(0.0237929527 * eqpLevel);
|
||||
|
||||
default: // weapons
|
||||
return 4313.581428 * Math.exp(0.03147837094 * eqpLevel);
|
||||
}
|
||||
} else {
|
||||
return 14000;
|
||||
}
|
||||
}
|
||||
|
||||
private static double getMakerReagentFee(int itemid, int reagentLevel) {
|
||||
if(YamlConfig.config.server.USE_MAKER_FEE_HEURISTICS) {
|
||||
EquipType et = EquipType.getEquipTypeById(itemid);
|
||||
int eqpLevel = ii.getEquipLevelReq(itemid);
|
||||
|
||||
switch(et) {
|
||||
case CAP:
|
||||
return 5592.01613 * Math.exp(0.02914576018 * eqpLevel) * reagentLevel;
|
||||
|
||||
case LONGCOAT:
|
||||
return 3405.23441 * Math.exp(0.03413001038 * eqpLevel) * reagentLevel;
|
||||
|
||||
case SHOES:
|
||||
return 2115.697484 * Math.exp(0.0354881705 * eqpLevel) * reagentLevel;
|
||||
|
||||
case GLOVES:
|
||||
return 4684.040894 * Math.exp(0.03166500585 * eqpLevel) * reagentLevel;
|
||||
|
||||
case COAT:
|
||||
return 2955.89017 * Math.exp(0.0339948456 * eqpLevel) * reagentLevel;
|
||||
|
||||
case PANTS:
|
||||
return 1774.722181 * Math.exp(0.03854321409 * eqpLevel) * reagentLevel;
|
||||
|
||||
case SHIELD:
|
||||
return 12014.11296 * Math.exp(0.02185471162 * eqpLevel) * reagentLevel;
|
||||
|
||||
default: // weapons
|
||||
return 4538.650247 * Math.exp(0.0371980303 * eqpLevel) * reagentLevel;
|
||||
}
|
||||
} else {
|
||||
return 8000 * reagentLevel;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MakerItemCreateEntry {
|
||||
private int reqLevel, reqMakerLevel;
|
||||
private double cost;
|
||||
private int reqCost;
|
||||
private List<Pair<Integer, Integer>> reqItems = new ArrayList<>(); // itemId / amount
|
||||
private List<Pair<Integer, Integer>> gainItems = new ArrayList<>(); // itemId / amount
|
||||
|
||||
public MakerItemCreateEntry(int cost, int reqLevel, int reqMakerLevel) {
|
||||
this.cost = cost;
|
||||
this.reqLevel = reqLevel;
|
||||
this.reqMakerLevel = reqMakerLevel;
|
||||
}
|
||||
|
||||
public MakerItemCreateEntry(MakerItemCreateEntry mi) {
|
||||
this.cost = mi.cost;
|
||||
this.reqLevel = mi.reqLevel;
|
||||
this.reqMakerLevel = mi.reqMakerLevel;
|
||||
|
||||
for(Pair<Integer, Integer> p : mi.reqItems) {
|
||||
reqItems.add(p);
|
||||
}
|
||||
|
||||
for(Pair<Integer, Integer> p : mi.gainItems) {
|
||||
gainItems.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Pair<Integer, Integer>> getReqItems() {
|
||||
return reqItems;
|
||||
}
|
||||
|
||||
public List<Pair<Integer, Integer>> getGainItems() {
|
||||
return gainItems;
|
||||
}
|
||||
|
||||
public int getReqLevel() {
|
||||
return reqLevel;
|
||||
}
|
||||
|
||||
public int getReqSkillLevel() {
|
||||
return reqMakerLevel;
|
||||
}
|
||||
|
||||
public int getCost() {
|
||||
return reqCost;
|
||||
}
|
||||
|
||||
public void addCost(double amount) {
|
||||
cost += amount;
|
||||
}
|
||||
|
||||
protected void addReqItem(int itemId, int amount) {
|
||||
reqItems.add(new Pair<>(itemId, amount));
|
||||
}
|
||||
|
||||
protected void addGainItem(int itemId, int amount) {
|
||||
gainItems.add(new Pair<>(itemId, amount));
|
||||
}
|
||||
|
||||
public void trimCost() {
|
||||
reqCost = (int) (cost / 1000);
|
||||
reqCost *= 1000;
|
||||
}
|
||||
|
||||
public boolean isInvalid() { // thanks Rohenn, Wh1SK3Y for noticing some items not getting checked properly
|
||||
return reqLevel < 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
2236
src/main/java/server/MapleItemInformationProvider.java
Normal file
2236
src/main/java/server/MapleItemInformationProvider.java
Normal file
File diff suppressed because it is too large
Load Diff
171
src/main/java/server/MapleMarriage.java
Normal file
171
src/main/java/server/MapleMarriage.java
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.ItemFactory;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import scripting.event.EventInstanceManager;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import scripting.event.EventManager;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleMarriage extends EventInstanceManager {
|
||||
|
||||
public MapleMarriage(EventManager em, String name) {
|
||||
super(em, name);
|
||||
}
|
||||
|
||||
public boolean giftItemToSpouse(int cid) {
|
||||
return this.getIntProperty("wishlistSelection") == 0;
|
||||
}
|
||||
|
||||
public List<String> getWishlistItems(boolean groom) {
|
||||
String strItems = this.getProperty(groom ? "groomWishlist" : "brideWishlist");
|
||||
if (strItems != null) {
|
||||
return Arrays.asList(strItems.split("\r\n"));
|
||||
}
|
||||
|
||||
return new LinkedList<>();
|
||||
}
|
||||
|
||||
public void initializeGiftItems() {
|
||||
List<Item> groomGifts = new ArrayList<>();
|
||||
this.setObjectProperty("groomGiftlist", groomGifts);
|
||||
|
||||
List<Item> brideGifts = new ArrayList<>();
|
||||
this.setObjectProperty("brideGiftlist", brideGifts);
|
||||
}
|
||||
|
||||
public List<Item> getGiftItems(MapleClient c, boolean groom) {
|
||||
List<Item> gifts = getGiftItemsList(groom);
|
||||
synchronized (gifts) {
|
||||
return new LinkedList<>(gifts);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Item> getGiftItemsList(boolean groom) {
|
||||
return (List<Item>) this.getObjectProperty(groom ? "groomGiftlist" : "brideGiftlist");
|
||||
}
|
||||
|
||||
public Item getGiftItem(MapleClient c, boolean groom, int idx) {
|
||||
try {
|
||||
return getGiftItems(c, groom).get(idx);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void addGiftItem(boolean groom, Item item) {
|
||||
List<Item> gifts = getGiftItemsList(groom);
|
||||
synchronized (gifts) {
|
||||
gifts.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeGiftItem(boolean groom, Item item) {
|
||||
List<Item> gifts = getGiftItemsList(groom);
|
||||
synchronized (gifts) {
|
||||
gifts.remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean isMarriageGroom(MapleCharacter chr) {
|
||||
Boolean groom = null;
|
||||
try {
|
||||
int groomid = this.getIntProperty("groomId"), brideid = this.getIntProperty("brideId");
|
||||
if (chr.getId() == groomid) {
|
||||
groom = true;
|
||||
} else if (chr.getId() == brideid) {
|
||||
groom = false;
|
||||
}
|
||||
} catch (NumberFormatException nfe) {}
|
||||
|
||||
return groom;
|
||||
}
|
||||
|
||||
public static boolean claimGiftItems(MapleClient c, MapleCharacter chr) {
|
||||
List<Item> gifts = loadGiftItemsFromDb(c, chr.getId());
|
||||
if (MapleInventory.checkSpot(chr, gifts)) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
ItemFactory.MARRIAGE_GIFTS.saveItems(new LinkedList<Pair<Item, MapleInventoryType>>(), chr.getId(), con);
|
||||
con.close();
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
|
||||
for (Item item : gifts) {
|
||||
MapleInventoryManipulator.addFromDrop(chr.getClient(), item, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<Item> loadGiftItemsFromDb(MapleClient c, int cid) {
|
||||
List<Item> items = new LinkedList<>();
|
||||
|
||||
try {
|
||||
for (Pair<Item, MapleInventoryType> it : ItemFactory.MARRIAGE_GIFTS.loadItems(cid, false)) {
|
||||
items.add(it.getLeft());
|
||||
}
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public void saveGiftItemsToDb(MapleClient c, boolean groom, int cid) {
|
||||
MapleMarriage.saveGiftItemsToDb(c, getGiftItems(c, groom), cid);
|
||||
}
|
||||
|
||||
public static void saveGiftItemsToDb(MapleClient c, List<Item> giftItems, int cid) {
|
||||
List<Pair<Item, MapleInventoryType>> items = new LinkedList<>();
|
||||
for (Item it : giftItems) {
|
||||
items.add(new Pair<>(it, it.getInventoryType()));
|
||||
}
|
||||
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
ItemFactory.MARRIAGE_GIFTS.saveItems(items, cid, con);
|
||||
con.close();
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
296
src/main/java/server/MapleShop.java
Normal file
296
src/main/java/server/MapleShop.java
Normal file
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import client.MapleClient;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.MaplePet;
|
||||
import constants.inventory.ItemConstants;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
*/
|
||||
public class MapleShop {
|
||||
private static final Set<Integer> rechargeableItems = new LinkedHashSet<>();
|
||||
private int id;
|
||||
private int npcId;
|
||||
private List<MapleShopItem> items;
|
||||
private int tokenvalue = 1000000000;
|
||||
private int token = 4000313;
|
||||
|
||||
static {
|
||||
for (int i = 2070000; i < 2070017; i++) {
|
||||
rechargeableItems.add(i);
|
||||
}
|
||||
rechargeableItems.add(2331000);//Blaze Capsule
|
||||
rechargeableItems.add(2332000);//Glaze Capsule
|
||||
rechargeableItems.add(2070018);
|
||||
rechargeableItems.remove(2070014); // doesn't exist
|
||||
for (int i = 2330000; i <= 2330005; i++) {
|
||||
rechargeableItems.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
private MapleShop(int id, int npcId) {
|
||||
this.id = id;
|
||||
this.npcId = npcId;
|
||||
items = new ArrayList<>();
|
||||
}
|
||||
|
||||
private void addItem(MapleShopItem item) {
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
public void sendShop(MapleClient c) {
|
||||
c.getPlayer().setShop(this);
|
||||
c.announce(MaplePacketCreator.getNPCShop(c, getNpcId(), items));
|
||||
}
|
||||
|
||||
public void buy(MapleClient c, short slot, int itemId, short quantity) {
|
||||
MapleShopItem item = findBySlot(slot);
|
||||
if (item != null) {
|
||||
if (item.getItemId() != itemId) {
|
||||
System.out.println("Wrong slot number in shop " + id);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
if (item.getPrice() > 0) {
|
||||
int amount = (int)Math.min((float) item.getPrice() * quantity, Integer.MAX_VALUE);
|
||||
if (c.getPlayer().getMeso() >= amount) {
|
||||
if (MapleInventoryManipulator.checkSpace(c, itemId, quantity, "")) {
|
||||
if (!ItemConstants.isRechargeable(itemId)) { //Pets can't be bought from shops
|
||||
MapleInventoryManipulator.addById(c, itemId, quantity, "", -1);
|
||||
c.getPlayer().gainMeso(-amount, false);
|
||||
} else {
|
||||
short slotMax = ii.getSlotMax(c, item.getItemId());
|
||||
quantity = slotMax;
|
||||
MapleInventoryManipulator.addById(c, itemId, quantity, "", -1);
|
||||
c.getPlayer().gainMeso(-item.getPrice(), false);
|
||||
}
|
||||
c.announce(MaplePacketCreator.shopTransaction((byte) 0));
|
||||
} else
|
||||
c.announce(MaplePacketCreator.shopTransaction((byte) 3));
|
||||
|
||||
} else
|
||||
c.announce(MaplePacketCreator.shopTransaction((byte) 2));
|
||||
|
||||
} else if (item.getPitch() > 0) {
|
||||
int amount = (int)Math.min((float) item.getPitch() * quantity, Integer.MAX_VALUE);
|
||||
|
||||
if (c.getPlayer().getInventory(MapleInventoryType.ETC).countById(4310000) >= amount) {
|
||||
if (MapleInventoryManipulator.checkSpace(c, itemId, quantity, "")) {
|
||||
if (!ItemConstants.isRechargeable(itemId)) {
|
||||
MapleInventoryManipulator.addById(c, itemId, quantity, "", -1);
|
||||
MapleInventoryManipulator.removeById(c, MapleInventoryType.ETC, 4310000, amount, false, false);
|
||||
} else {
|
||||
short slotMax = ii.getSlotMax(c, item.getItemId());
|
||||
quantity = slotMax;
|
||||
MapleInventoryManipulator.addById(c, itemId, quantity, "", -1);
|
||||
MapleInventoryManipulator.removeById(c, MapleInventoryType.ETC, 4310000, amount, false, false);
|
||||
}
|
||||
c.announce(MaplePacketCreator.shopTransaction((byte) 0));
|
||||
} else
|
||||
c.announce(MaplePacketCreator.shopTransaction((byte) 3));
|
||||
}
|
||||
|
||||
} else if (c.getPlayer().getInventory(MapleInventoryType.CASH).countById(token) != 0) {
|
||||
int amount = c.getPlayer().getInventory(MapleInventoryType.CASH).countById(token);
|
||||
int value = amount * tokenvalue;
|
||||
int cost = item.getPrice() * quantity;
|
||||
if (c.getPlayer().getMeso() + value >= cost) {
|
||||
int cardreduce = value - cost;
|
||||
int diff = cardreduce + c.getPlayer().getMeso();
|
||||
if (MapleInventoryManipulator.checkSpace(c, itemId, quantity, "")) {
|
||||
if (ItemConstants.isPet(itemId)) {
|
||||
int petid = MaplePet.createPet(itemId);
|
||||
MapleInventoryManipulator.addById(c, itemId, quantity, "", petid, -1);
|
||||
} else {
|
||||
MapleInventoryManipulator.addById(c, itemId, quantity, "", -1, -1);
|
||||
}
|
||||
c.getPlayer().gainMeso(diff, false);
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.shopTransaction((byte) 3));
|
||||
}
|
||||
c.announce(MaplePacketCreator.shopTransaction((byte) 0));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.shopTransaction((byte) 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canSell(Item item, short quantity) {
|
||||
if (item == null) { //Basic check
|
||||
return false;
|
||||
}
|
||||
|
||||
short iQuant = item.getQuantity();
|
||||
if (iQuant == 0xFFFF) {
|
||||
iQuant = 1;
|
||||
} else if(iQuant < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ItemConstants.isRechargeable(item.getItemId())) {
|
||||
if (iQuant == 0 || quantity > iQuant) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static short getSellingQuantity(Item item, short quantity) {
|
||||
if (ItemConstants.isRechargeable(item.getItemId())) {
|
||||
quantity = item.getQuantity();
|
||||
if (quantity == 0xFFFF) {
|
||||
quantity = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void sell(MapleClient c, MapleInventoryType type, short slot, short quantity) {
|
||||
if (quantity == 0xFFFF || quantity == 0) {
|
||||
quantity = 1;
|
||||
} else if (quantity < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Item item = c.getPlayer().getInventory(type).getItem((short) slot);
|
||||
if(canSell(item, quantity)) {
|
||||
quantity = getSellingQuantity(item, quantity);
|
||||
MapleInventoryManipulator.removeFromSlot(c, type, (byte) slot, quantity, false);
|
||||
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
int recvMesos = ii.getPrice(item.getItemId(), quantity);
|
||||
if (recvMesos > 0) {
|
||||
c.getPlayer().gainMeso(recvMesos, false);
|
||||
}
|
||||
c.announce(MaplePacketCreator.shopTransaction((byte) 0x8));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.shopTransaction((byte) 0x5));
|
||||
}
|
||||
}
|
||||
|
||||
public void recharge(MapleClient c, short slot) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
Item item = c.getPlayer().getInventory(MapleInventoryType.USE).getItem(slot);
|
||||
if (item == null || !ItemConstants.isRechargeable(item.getItemId())) {
|
||||
return;
|
||||
}
|
||||
short slotMax = ii.getSlotMax(c, item.getItemId());
|
||||
if (item.getQuantity() < 0) {
|
||||
return;
|
||||
}
|
||||
if (item.getQuantity() < slotMax) {
|
||||
int price = (int) Math.ceil(ii.getUnitPrice(item.getItemId()) * (slotMax - item.getQuantity()));
|
||||
if (c.getPlayer().getMeso() >= price) {
|
||||
item.setQuantity(slotMax);
|
||||
c.getPlayer().forceUpdateItem(item);
|
||||
c.getPlayer().gainMeso(-price, false, true, false);
|
||||
c.announce(MaplePacketCreator.shopTransaction((byte) 0x8));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.shopTransaction((byte) 0x2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MapleShopItem findBySlot(short slot) {
|
||||
return items.get(slot);
|
||||
}
|
||||
|
||||
public static MapleShop createFromDB(int id, boolean isShopId) {
|
||||
MapleShop ret = null;
|
||||
int shopId;
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps;
|
||||
if (isShopId) {
|
||||
ps = con.prepareStatement("SELECT * FROM shops WHERE shopid = ?");
|
||||
} else {
|
||||
ps = con.prepareStatement("SELECT * FROM shops WHERE npcid = ?");
|
||||
}
|
||||
ps.setInt(1, id);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
shopId = rs.getInt("shopid");
|
||||
ret = new MapleShop(shopId, rs.getInt("npcid"));
|
||||
rs.close();
|
||||
ps.close();
|
||||
} else {
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
return null;
|
||||
}
|
||||
ps = con.prepareStatement("SELECT itemid, price, pitch FROM shopitems WHERE shopid = ? ORDER BY position DESC");
|
||||
ps.setInt(1, shopId);
|
||||
rs = ps.executeQuery();
|
||||
List<Integer> recharges = new ArrayList<>(rechargeableItems);
|
||||
while (rs.next()) {
|
||||
if (ItemConstants.isRechargeable(rs.getInt("itemid"))) {
|
||||
MapleShopItem starItem = new MapleShopItem((short) 1, rs.getInt("itemid"), rs.getInt("price"), rs.getInt("pitch"));
|
||||
ret.addItem(starItem);
|
||||
if (rechargeableItems.contains(starItem.getItemId())) {
|
||||
recharges.remove(Integer.valueOf(starItem.getItemId()));
|
||||
}
|
||||
} else {
|
||||
ret.addItem(new MapleShopItem((short) 1000, rs.getInt("itemid"), rs.getInt("price"), rs.getInt("pitch")));
|
||||
}
|
||||
}
|
||||
for (Integer recharge : recharges) {
|
||||
ret.addItem(new MapleShopItem((short) 1000, recharge.intValue(), 0, 0));
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int getNpcId() {
|
||||
return npcId;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
73
src/main/java/server/MapleShopFactory.java
Normal file
73
src/main/java/server/MapleShopFactory.java
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
*/
|
||||
public class MapleShopFactory {
|
||||
|
||||
private static MapleShopFactory instance = new MapleShopFactory();
|
||||
|
||||
public static MapleShopFactory getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private Map<Integer, MapleShop> shops = new HashMap<Integer, MapleShop>();
|
||||
private Map<Integer, MapleShop> npcShops = new HashMap<Integer, MapleShop>();
|
||||
|
||||
private MapleShop loadShop(int id, boolean isShopId) {
|
||||
MapleShop ret = MapleShop.createFromDB(id, isShopId);
|
||||
if (ret != null) {
|
||||
shops.put(ret.getId(), ret);
|
||||
npcShops.put(ret.getNpcId(), ret);
|
||||
} else if (isShopId) {
|
||||
shops.put(id, null);
|
||||
} else {
|
||||
npcShops.put(id, null);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public MapleShop getShop(int shopId) {
|
||||
if (shops.containsKey(shopId)) {
|
||||
return shops.get(shopId);
|
||||
}
|
||||
return loadShop(shopId, true);
|
||||
}
|
||||
|
||||
public MapleShop getShopForNPC(int npcId) {
|
||||
if (npcShops.containsKey(npcId)) {
|
||||
return npcShops.get(npcId);
|
||||
}
|
||||
return loadShop(npcId, false);
|
||||
}
|
||||
|
||||
public void reloadShops() {
|
||||
shops.clear();
|
||||
npcShops.clear();
|
||||
}
|
||||
}
|
||||
56
src/main/java/server/MapleShopItem.java
Normal file
56
src/main/java/server/MapleShopItem.java
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
*/
|
||||
public class MapleShopItem {
|
||||
private short buyable;
|
||||
private int itemId;
|
||||
private int price;
|
||||
private int pitch;
|
||||
|
||||
public MapleShopItem(short buyable, int itemId, int price, int pitch) {
|
||||
this.buyable = buyable;
|
||||
this.itemId = itemId;
|
||||
this.price = price;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public short getBuyable() {
|
||||
return buyable;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public int getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public int getPitch() {
|
||||
return pitch;
|
||||
}
|
||||
}
|
||||
296
src/main/java/server/MapleSkillbookInformationProvider.java
Normal file
296
src/main/java/server/MapleSkillbookInformationProvider.java
Normal file
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import client.MapleCharacter;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import tools.DatabaseConnection;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class MapleSkillbookInformationProvider {
|
||||
|
||||
private final static MapleSkillbookInformationProvider instance = new MapleSkillbookInformationProvider();
|
||||
|
||||
public static MapleSkillbookInformationProvider getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected static Map<Integer, SkillBookEntry> foundSkillbooks = new HashMap<>();
|
||||
|
||||
public enum SkillBookEntry {
|
||||
UNAVAILABLE,
|
||||
QUEST,
|
||||
QUEST_BOOK,
|
||||
QUEST_REWARD,
|
||||
REACTOR,
|
||||
SCRIPT
|
||||
}
|
||||
|
||||
private static String rootDirectory = ".";
|
||||
|
||||
private static int skillbookMinItemid = 2280000;
|
||||
private static int skillbookMaxItemid = 2300000; // exclusively
|
||||
|
||||
static {
|
||||
loadSkillbooks();
|
||||
}
|
||||
|
||||
private static boolean is4thJobSkill(int itemid) {
|
||||
return itemid / 10000 % 10 == 2;
|
||||
}
|
||||
|
||||
private static boolean isSkillBook(int itemid) {
|
||||
return itemid >= skillbookMinItemid && itemid < skillbookMaxItemid;
|
||||
}
|
||||
|
||||
private static boolean isQuestBook(int itemid) {
|
||||
return itemid >= 4001107 && itemid <= 4001114 || itemid >= 4161015 && itemid <= 4161023;
|
||||
}
|
||||
|
||||
private static int fetchQuestbook(MapleData checkData, String quest) {
|
||||
MapleData questStartData = checkData.getChildByPath(quest).getChildByPath("0");
|
||||
|
||||
MapleData startReqItemData = questStartData.getChildByPath("item");
|
||||
if (startReqItemData != null) {
|
||||
for (MapleData itemData : startReqItemData.getChildren()) {
|
||||
int itemid = MapleDataTool.getInt("id", itemData, 0);
|
||||
if (isQuestBook(itemid)) {
|
||||
return itemid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapleData startReqQuestData = questStartData.getChildByPath("quest");
|
||||
if (startReqQuestData != null) {
|
||||
Set<Integer> reqQuests = new HashSet<>();
|
||||
|
||||
for (MapleData questStatusData : startReqQuestData.getChildren()) {
|
||||
int reqQuest = MapleDataTool.getInt("id", questStatusData, 0);
|
||||
if (reqQuest > 0) {
|
||||
reqQuests.add(reqQuest);
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer reqQuest : reqQuests) {
|
||||
int book = fetchQuestbook(checkData, Integer.toString(reqQuest));
|
||||
if (book > -1) {
|
||||
return book;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static void fetchSkillbooksFromQuests() {
|
||||
MapleDataProvider questDataProvider = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Quest.wz"));
|
||||
MapleData actData = questDataProvider.getData("Act.img");
|
||||
MapleData checkData = questDataProvider.getData("Check.img");
|
||||
|
||||
for (MapleData questData : actData.getChildren()) {
|
||||
for (MapleData questStatusData : questData.getChildren()) {
|
||||
for (MapleData questNodeData : questStatusData.getChildren()) {
|
||||
String actNodeName = questNodeData.getName();
|
||||
if (actNodeName.contentEquals("item")) {
|
||||
for (MapleData questItemData : questNodeData.getChildren()) {
|
||||
int itemid = MapleDataTool.getInt("id", questItemData, 0);
|
||||
int itemcount = MapleDataTool.getInt("count", questItemData, 0);
|
||||
|
||||
if (isSkillBook(itemid) && itemcount > 0) {
|
||||
int questbook = fetchQuestbook(checkData, questData.getName());
|
||||
if (questbook < 0) {
|
||||
foundSkillbooks.put(itemid, SkillBookEntry.QUEST);
|
||||
} else {
|
||||
foundSkillbooks.put(itemid, SkillBookEntry.QUEST_BOOK);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (actNodeName.contentEquals("skill")) {
|
||||
for (MapleData questSkillData : questNodeData.getChildren()) {
|
||||
int skillid = MapleDataTool.getInt("id", questSkillData, 0);
|
||||
if (is4thJobSkill(skillid)) {
|
||||
// negative itemids are skill rewards
|
||||
|
||||
int questbook = fetchQuestbook(checkData, questData.getName());
|
||||
if (questbook < 0) {
|
||||
foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_REWARD);
|
||||
} else {
|
||||
foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_BOOK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void fetchSkillbooksFromReactors() {
|
||||
Connection con = null;
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("SELECT itemid FROM reactordrops WHERE itemid >= ? AND itemid < ?;");
|
||||
ps.setInt(1, skillbookMinItemid);
|
||||
ps.setInt(2, skillbookMaxItemid);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
if (rs.isBeforeFirst()) {
|
||||
while(rs.next()) {
|
||||
foundSkillbooks.put(rs.getInt("itemid"), SkillBookEntry.REACTOR);
|
||||
}
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
|
||||
con.close();
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void listFiles(String directoryName, ArrayList<File> files) {
|
||||
File directory = new File(directoryName);
|
||||
|
||||
// get all the files from a directory
|
||||
File[] fList = directory.listFiles();
|
||||
for (File file : fList) {
|
||||
if (file.isFile()) {
|
||||
files.add(file);
|
||||
} else if (file.isDirectory()) {
|
||||
listFiles(file.getAbsolutePath(), files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<File> listFilesFromDirectoryRecursively(String directory) {
|
||||
ArrayList<File> files = new ArrayList<>();
|
||||
listFiles(directory, files);
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
private static void filterScriptDirectorySearchMatchingData(String path) {
|
||||
for (File file : listFilesFromDirectoryRecursively(rootDirectory + "/" + path)) {
|
||||
if (file.getName().endsWith(".js")) {
|
||||
fileSearchMatchingData(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<Integer> foundMatchingDataOnFile(String fileContent) {
|
||||
Set<Integer> matches = new HashSet<>(4);
|
||||
|
||||
Matcher searchM = Pattern.compile("22(8|9)[0-9]{4}").matcher(fileContent);
|
||||
int idx = 0;
|
||||
while (searchM.find(idx)) {
|
||||
idx = searchM.end();
|
||||
matches.add(Integer.valueOf(fileContent.substring(searchM.start(), idx)));
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
static String readFileToString(File file, String encoding) throws IOException {
|
||||
Scanner scanner = new Scanner(file, encoding);
|
||||
String text = "";
|
||||
try {
|
||||
try {
|
||||
text = scanner.useDelimiter("\\A").next();
|
||||
} finally {
|
||||
scanner.close();
|
||||
}
|
||||
} catch (NoSuchElementException e) {}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private static void fileSearchMatchingData(File file) {
|
||||
try {
|
||||
String fileContent = readFileToString(file, "UTF-8");
|
||||
|
||||
Set<Integer> books = foundMatchingDataOnFile(fileContent);
|
||||
for (Integer i : books) {
|
||||
foundSkillbooks.put(i, SkillBookEntry.SCRIPT);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("Failed to read " + file.getName() + ".");
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void fetchSkillbooksFromScripts() {
|
||||
filterScriptDirectorySearchMatchingData("scripts");
|
||||
}
|
||||
|
||||
private static void loadSkillbooks() {
|
||||
fetchSkillbooksFromQuests();
|
||||
fetchSkillbooksFromReactors();
|
||||
fetchSkillbooksFromScripts();
|
||||
}
|
||||
|
||||
public SkillBookEntry getSkillbookAvailability(int itemid) {
|
||||
SkillBookEntry sbe = foundSkillbooks.get(itemid);
|
||||
return sbe != null ? sbe : SkillBookEntry.UNAVAILABLE;
|
||||
}
|
||||
|
||||
public List<Integer> getTeachableSkills(MapleCharacter chr) {
|
||||
List<Integer> list = new ArrayList<>();
|
||||
|
||||
for (Integer book : foundSkillbooks.keySet()) {
|
||||
if (book >= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int skillid = -book;
|
||||
if (skillid / 10000 == chr.getJob().getId()) {
|
||||
if (chr.getMasterLevel(skillid) == 0) {
|
||||
list.add(-skillid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
1975
src/main/java/server/MapleStatEffect.java
Normal file
1975
src/main/java/server/MapleStatEffect.java
Normal file
File diff suppressed because it is too large
Load Diff
374
src/main/java/server/MapleStorage.java
Normal file
374
src/main/java/server/MapleStorage.java
Normal file
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.ItemFactory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import constants.game.GameConstants;
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import tools.FilePrinter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
*/
|
||||
public class MapleStorage {
|
||||
private static Map<Integer, Integer> trunkGetCache = new HashMap<>();
|
||||
private static Map<Integer, Integer> trunkPutCache = new HashMap<>();
|
||||
|
||||
private int id;
|
||||
private int currentNpcid;
|
||||
private int meso;
|
||||
private byte slots;
|
||||
private Map<MapleInventoryType, List<Item>> typeItems = new HashMap<>();
|
||||
private List<Item> items = new LinkedList<>();
|
||||
private Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.STORAGE, true);
|
||||
|
||||
private MapleStorage(int id, byte slots, int meso) {
|
||||
this.id = id;
|
||||
this.slots = slots;
|
||||
this.meso = meso;
|
||||
}
|
||||
|
||||
private static MapleStorage create(int id, int world) throws SQLException {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("INSERT INTO storages (accountid, world, slots, meso) VALUES (?, ?, 4, 0)")) {
|
||||
ps.setInt(1, id);
|
||||
ps.setInt(2, world);
|
||||
ps.executeUpdate();
|
||||
}
|
||||
con.close();
|
||||
|
||||
return loadOrCreateFromDB(id, world);
|
||||
}
|
||||
|
||||
public static MapleStorage loadOrCreateFromDB(int id, int world) {
|
||||
try {
|
||||
MapleStorage ret;
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT storageid, slots, meso FROM storages WHERE accountid = ? AND world = ?");
|
||||
ps.setInt(1, id);
|
||||
ps.setInt(2, world);
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
ret = new MapleStorage(rs.getInt("storageid"), (byte) rs.getInt("slots"), rs.getInt("meso"));
|
||||
for (Pair<Item, MapleInventoryType> item : ItemFactory.STORAGE.loadItems(ret.id, false)) {
|
||||
ret.items.add(item.getLeft());
|
||||
}
|
||||
} else {
|
||||
ret = create(id, world);
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
|
||||
return ret;
|
||||
} catch (SQLException ex) { // exceptions leading to deploy null storages found thanks to Jefe
|
||||
FilePrinter.printError(FilePrinter.STORAGE, ex, "SQL error occurred when trying to load storage for accountid " + id + ", world " + GameConstants.WORLD_NAMES[world]);
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public byte getSlots() {
|
||||
return slots;
|
||||
}
|
||||
|
||||
public boolean canGainSlots(int slots) {
|
||||
slots += this.slots;
|
||||
return slots <= 48;
|
||||
}
|
||||
|
||||
public boolean gainSlots(int slots) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (canGainSlots(slots)) {
|
||||
slots += this.slots;
|
||||
this.slots = (byte) slots;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void saveToDB(Connection con) {
|
||||
try {
|
||||
try (PreparedStatement ps = con.prepareStatement("UPDATE storages SET slots = ?, meso = ? WHERE storageid = ?")) {
|
||||
ps.setInt(1, slots);
|
||||
ps.setInt(2, meso);
|
||||
ps.setInt(3, id);
|
||||
ps.executeUpdate();
|
||||
}
|
||||
List<Pair<Item, MapleInventoryType>> itemsWithType = new ArrayList<>();
|
||||
|
||||
List<Item> list = getItems();
|
||||
for (Item item : list) {
|
||||
itemsWithType.add(new Pair<>(item, item.getInventoryType()));
|
||||
}
|
||||
|
||||
ItemFactory.STORAGE.saveItems(itemsWithType, id, con);
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Item getItem(byte slot) {
|
||||
lock.lock();
|
||||
try {
|
||||
return items.get(slot);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean takeOut(Item item) {
|
||||
lock.lock();
|
||||
try {
|
||||
boolean ret = items.remove(item);
|
||||
|
||||
MapleInventoryType type = item.getInventoryType();
|
||||
typeItems.put(type, new ArrayList<>(filterItems(type)));
|
||||
|
||||
return ret;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean store(Item item) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (isFull()) { // thanks Optimist for noticing unrestricted amount of insertions here
|
||||
return false;
|
||||
}
|
||||
|
||||
items.add(item);
|
||||
|
||||
MapleInventoryType type = item.getInventoryType();
|
||||
typeItems.put(type, new ArrayList<>(filterItems(type)));
|
||||
|
||||
return true;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Item> getItems() {
|
||||
lock.lock();
|
||||
try {
|
||||
return Collections.unmodifiableList(items);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Item> filterItems(MapleInventoryType type) {
|
||||
List<Item> storageItems = getItems();
|
||||
List<Item> ret = new LinkedList<>();
|
||||
|
||||
for (Item item : storageItems) {
|
||||
if (item.getInventoryType() == type) {
|
||||
ret.add(item);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public byte getSlot(MapleInventoryType type, byte slot) {
|
||||
lock.lock();
|
||||
try {
|
||||
byte ret = 0;
|
||||
List<Item> storageItems = getItems();
|
||||
for (Item item : storageItems) {
|
||||
if (item == typeItems.get(type).get(slot)) {
|
||||
return ret;
|
||||
}
|
||||
ret++;
|
||||
}
|
||||
return -1;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void sendStorage(MapleClient c, int npcId) {
|
||||
if (c.getPlayer().getLevel() < 15){
|
||||
c.getPlayer().dropMessage(1, "You may only use the storage once you have reached level 15.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
Collections.sort(items, new Comparator<Item>() {
|
||||
@Override
|
||||
public int compare(Item o1, Item o2) {
|
||||
if (o1.getInventoryType().getType() < o2.getInventoryType().getType()) {
|
||||
return -1;
|
||||
} else if (o1.getInventoryType() == o2.getInventoryType()) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
List<Item> storageItems = getItems();
|
||||
for (MapleInventoryType type : MapleInventoryType.values()) {
|
||||
typeItems.put(type, new ArrayList<>(storageItems));
|
||||
}
|
||||
|
||||
currentNpcid = npcId;
|
||||
c.announce(MaplePacketCreator.getStorage(npcId, slots, storageItems, meso));
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void sendStored(MapleClient c, MapleInventoryType type) {
|
||||
lock.lock();
|
||||
try {
|
||||
c.announce(MaplePacketCreator.storeStorage(slots, type, typeItems.get(type)));
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void sendTakenOut(MapleClient c, MapleInventoryType type) {
|
||||
lock.lock();
|
||||
try {
|
||||
c.announce(MaplePacketCreator.takeOutStorage(slots, type, typeItems.get(type)));
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void arrangeItems(MapleClient c) {
|
||||
lock.lock();
|
||||
try {
|
||||
MapleStorageInventory msi = new MapleStorageInventory(c, items);
|
||||
msi.mergeItems();
|
||||
items = msi.sortItems();
|
||||
|
||||
for (MapleInventoryType type : MapleInventoryType.values()) {
|
||||
typeItems.put(type, new ArrayList<>(items));
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.arrangeStorage(slots, items));
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int getMeso() {
|
||||
return meso;
|
||||
}
|
||||
|
||||
public void setMeso(int meso) {
|
||||
if (meso < 0) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
this.meso = meso;
|
||||
}
|
||||
|
||||
public void sendMeso(MapleClient c) {
|
||||
c.announce(MaplePacketCreator.mesoStorage(slots, meso));
|
||||
}
|
||||
|
||||
public int getStoreFee() { // thanks to GabrielSin
|
||||
int npcId = currentNpcid;
|
||||
Integer fee = trunkPutCache.get(npcId);
|
||||
if(fee == null) {
|
||||
fee = 100;
|
||||
|
||||
MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz"));
|
||||
MapleData npcData = npc.getData(npcId + ".img");
|
||||
if(npcData != null) {
|
||||
fee = MapleDataTool.getIntConvert("info/trunkPut", npcData, 100);
|
||||
}
|
||||
|
||||
trunkPutCache.put(npcId, fee);
|
||||
}
|
||||
|
||||
return fee;
|
||||
}
|
||||
|
||||
public int getTakeOutFee() {
|
||||
int npcId = currentNpcid;
|
||||
Integer fee = trunkGetCache.get(npcId);
|
||||
if(fee == null) {
|
||||
fee = 0;
|
||||
|
||||
MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz"));
|
||||
MapleData npcData = npc.getData(npcId + ".img");
|
||||
if(npcData != null) {
|
||||
fee = MapleDataTool.getIntConvert("info/trunkGet", npcData, 0);
|
||||
}
|
||||
|
||||
trunkGetCache.put(npcId, fee);
|
||||
}
|
||||
|
||||
return fee;
|
||||
}
|
||||
|
||||
public boolean isFull() {
|
||||
lock.lock();
|
||||
try {
|
||||
return items.size() >= slots;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
lock.lock();
|
||||
try {
|
||||
typeItems.clear();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
368
src/main/java/server/MapleStorageInventory.java
Normal file
368
src/main/java/server/MapleStorageInventory.java
Normal file
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import config.YamlConfig;
|
||||
import constants.inventory.ItemConstants;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Item;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
*/
|
||||
|
||||
class PairedQuicksort {
|
||||
private int i = 0;
|
||||
private int j = 0;
|
||||
private final ArrayList<Integer> intersect;
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
|
||||
private void PartitionByItemId(int Esq, int Dir, ArrayList<Item> A) {
|
||||
Item x, w;
|
||||
|
||||
i = Esq;
|
||||
j = Dir;
|
||||
|
||||
x = A.get((i + j) / 2);
|
||||
do {
|
||||
while (x.getItemId() > A.get(i).getItemId()) i++;
|
||||
while (x.getItemId() < A.get(j).getItemId()) j--;
|
||||
|
||||
if (i <= j) {
|
||||
w = A.get(i);
|
||||
A.set(i, A.get(j));
|
||||
A.set(j, w);
|
||||
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
} while (i <= j);
|
||||
}
|
||||
|
||||
private void PartitionByName(int Esq, int Dir, ArrayList<Item> A) {
|
||||
Item x, w;
|
||||
|
||||
i = Esq;
|
||||
j = Dir;
|
||||
|
||||
x = A.get((i + j) / 2);
|
||||
do {
|
||||
while (ii.getName(x.getItemId()).compareTo(ii.getName(A.get(i).getItemId())) > 0) i++;
|
||||
while (ii.getName(x.getItemId()).compareTo(ii.getName(A.get(j).getItemId())) < 0) j--;
|
||||
|
||||
if (i <= j) {
|
||||
w = A.get(i);
|
||||
A.set(i, A.get(j));
|
||||
A.set(j, w);
|
||||
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
} while (i <= j);
|
||||
}
|
||||
|
||||
private void PartitionByQuantity(int Esq, int Dir, ArrayList<Item> A) {
|
||||
Item x, w;
|
||||
|
||||
i = Esq;
|
||||
j = Dir;
|
||||
|
||||
x = A.get((i + j) / 2);
|
||||
do {
|
||||
while (x.getQuantity() > A.get(i).getQuantity()) i++;
|
||||
while (x.getQuantity() < A.get(j).getQuantity()) j--;
|
||||
|
||||
if (i <= j) {
|
||||
w = A.get(i);
|
||||
A.set(i, A.get(j));
|
||||
A.set(j, w);
|
||||
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
} while (i <= j);
|
||||
}
|
||||
|
||||
private void PartitionByLevel(int Esq, int Dir, ArrayList<Item> A) {
|
||||
Equip x, w, eqpI, eqpJ;
|
||||
|
||||
i = Esq;
|
||||
j = Dir;
|
||||
|
||||
x = (Equip)(A.get((i + j) / 2));
|
||||
|
||||
do {
|
||||
eqpI = (Equip)A.get(i);
|
||||
eqpJ = (Equip)A.get(j);
|
||||
|
||||
while (x.getLevel() > eqpI.getLevel()) i++;
|
||||
while (x.getLevel() < eqpJ.getLevel()) j--;
|
||||
|
||||
if (i <= j) {
|
||||
w = (Equip)A.get(i);
|
||||
A.set(i, A.get(j));
|
||||
A.set(j, (Item)w);
|
||||
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
} while (i <= j);
|
||||
}
|
||||
|
||||
void MapleQuicksort(int Esq, int Dir, ArrayList<Item> A, int sort) {
|
||||
switch(sort) {
|
||||
case 3:
|
||||
PartitionByLevel(Esq, Dir, A);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
PartitionByName(Esq, Dir, A);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
PartitionByQuantity(Esq, Dir, A);
|
||||
break;
|
||||
|
||||
default:
|
||||
PartitionByItemId(Esq, Dir, A);
|
||||
}
|
||||
|
||||
|
||||
if (Esq < j) MapleQuicksort(Esq, j, A, sort);
|
||||
if (i < Dir) MapleQuicksort(i, Dir, A, sort);
|
||||
}
|
||||
|
||||
public PairedQuicksort(ArrayList<Item> A, int primarySort, int secondarySort) {
|
||||
intersect = new ArrayList<>();
|
||||
|
||||
if(A.size() > 0) MapleQuicksort(0, A.size() - 1, A, primarySort);
|
||||
|
||||
intersect.add(0);
|
||||
for(int ind = 1; ind < A.size(); ind++) {
|
||||
if(A.get(ind - 1).getItemId() != A.get(ind).getItemId()) {
|
||||
intersect.add(ind);
|
||||
}
|
||||
}
|
||||
intersect.add(A.size());
|
||||
|
||||
for(int ind = 0; ind < intersect.size() - 1; ind++) {
|
||||
if(intersect.get(ind + 1) > intersect.get(ind)) MapleQuicksort(intersect.get(ind), intersect.get(ind + 1) - 1, A, secondarySort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MapleStorageInventory {
|
||||
private MapleClient c;
|
||||
private Map<Short, Item> inventory = new LinkedHashMap<>();
|
||||
private byte slotLimit;
|
||||
|
||||
public MapleStorageInventory(MapleClient c, List<Item> toSort) {
|
||||
this.inventory = new LinkedHashMap<>();
|
||||
this.slotLimit = (byte)toSort.size();
|
||||
this.c = c;
|
||||
|
||||
for(Item item : toSort) {
|
||||
this.addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
private byte getSlotLimit() {
|
||||
return slotLimit;
|
||||
}
|
||||
|
||||
private Collection<Item> list() {
|
||||
return Collections.unmodifiableCollection(inventory.values());
|
||||
}
|
||||
|
||||
private short addItem(Item item) {
|
||||
short slotId = getNextFreeSlot();
|
||||
if (slotId < 0 || item == null) {
|
||||
return -1;
|
||||
}
|
||||
addSlot(slotId, item);
|
||||
item.setPosition(slotId);
|
||||
return slotId;
|
||||
}
|
||||
|
||||
private static boolean isEquipOrCash(Item item) {
|
||||
int type = item.getItemId() / 1000000;
|
||||
return type == 1 || type == 5;
|
||||
}
|
||||
|
||||
private static boolean isSameOwner(Item source, Item target) {
|
||||
return source.getOwner().equals(target.getOwner());
|
||||
}
|
||||
|
||||
private void move(short sSlot, short dSlot, short slotMax) {
|
||||
Item source = (Item) inventory.get(sSlot);
|
||||
Item target = (Item) inventory.get(dSlot);
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
if (target == null) {
|
||||
source.setPosition(dSlot);
|
||||
inventory.put(dSlot, source);
|
||||
inventory.remove(sSlot);
|
||||
} else if (target.getItemId() == source.getItemId() && !ItemConstants.isRechargeable(source.getItemId()) && !MapleItemInformationProvider.getInstance().isPickupRestricted(source.getItemId()) && isSameOwner(source, target)) {
|
||||
if (isEquipOrCash(source)) {
|
||||
swap(target, source);
|
||||
} else if (source.getQuantity() + target.getQuantity() > slotMax) {
|
||||
short rest = (short) ((source.getQuantity() + target.getQuantity()) - slotMax);
|
||||
source.setQuantity(rest);
|
||||
target.setQuantity(slotMax);
|
||||
} else {
|
||||
target.setQuantity((short) (source.getQuantity() + target.getQuantity()));
|
||||
inventory.remove(sSlot);
|
||||
}
|
||||
} else {
|
||||
swap(target, source);
|
||||
}
|
||||
}
|
||||
|
||||
private void moveItem(short src, short dst) {
|
||||
if (src < 0 || dst < 0) {
|
||||
return;
|
||||
}
|
||||
if(dst > this.getSlotLimit()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Item source = this.getItem(src);
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
short slotMax = MapleItemInformationProvider.getInstance().getSlotMax(c, source.getItemId());
|
||||
this.move(src, dst, slotMax);
|
||||
}
|
||||
|
||||
private void swap(Item source, Item target) {
|
||||
inventory.remove(source.getPosition());
|
||||
inventory.remove(target.getPosition());
|
||||
short swapPos = source.getPosition();
|
||||
source.setPosition(target.getPosition());
|
||||
target.setPosition(swapPos);
|
||||
inventory.put(source.getPosition(), source);
|
||||
inventory.put(target.getPosition(), target);
|
||||
}
|
||||
|
||||
private Item getItem(short slot) {
|
||||
return inventory.get(slot);
|
||||
}
|
||||
|
||||
private void addSlot(short slot, Item item) {
|
||||
inventory.put(slot, item);
|
||||
}
|
||||
|
||||
private void removeSlot(short slot) {
|
||||
inventory.remove(slot);
|
||||
}
|
||||
|
||||
private boolean isFull() {
|
||||
return inventory.size() >= slotLimit;
|
||||
}
|
||||
|
||||
private short getNextFreeSlot() {
|
||||
if (isFull()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (short i = 1; i <= slotLimit; i++) {
|
||||
if (!inventory.containsKey(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void mergeItems() {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
Item srcItem, dstItem;
|
||||
|
||||
for(short dst = 1; dst <= this.getSlotLimit(); dst++) {
|
||||
dstItem = this.getItem(dst);
|
||||
if(dstItem == null) continue;
|
||||
|
||||
for(short src = (short)(dst + 1); src <= this.getSlotLimit(); src++) {
|
||||
srcItem = this.getItem(src);
|
||||
if(srcItem == null) continue;
|
||||
|
||||
if(dstItem.getItemId() != srcItem.getItemId()) continue;
|
||||
if(dstItem.getQuantity() == ii.getSlotMax(c, this.getItem(dst).getItemId())) break;
|
||||
|
||||
moveItem(src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
boolean sorted = false;
|
||||
|
||||
while (!sorted) {
|
||||
short freeSlot = this.getNextFreeSlot();
|
||||
|
||||
if (freeSlot != -1) {
|
||||
short itemSlot = -1;
|
||||
for (short i = (short) (freeSlot + 1); i <= this.getSlotLimit(); i = (short) (i + 1)) {
|
||||
if (this.getItem(i) != null) {
|
||||
itemSlot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (itemSlot > 0) {
|
||||
moveItem(itemSlot, freeSlot);
|
||||
} else {
|
||||
sorted = true;
|
||||
}
|
||||
} else {
|
||||
sorted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Item> sortItems() {
|
||||
ArrayList<Item> itemarray = new ArrayList<>();
|
||||
|
||||
for (short i = 1; i <= this.getSlotLimit(); i++) {
|
||||
Item item = this.getItem(i);
|
||||
if (item != null) {
|
||||
itemarray.add((Item) item.copy());
|
||||
}
|
||||
}
|
||||
|
||||
for (Item item : itemarray) {
|
||||
this.removeSlot(item.getPosition());
|
||||
}
|
||||
|
||||
int invTypeCriteria = 1;
|
||||
int sortCriteria = (YamlConfig.config.server.USE_ITEM_SORT_BY_NAME == true) ? 2 : 0;
|
||||
PairedQuicksort pq = new PairedQuicksort(itemarray, sortCriteria, invTypeCriteria);
|
||||
|
||||
inventory.clear();
|
||||
return itemarray;
|
||||
}
|
||||
}
|
||||
523
src/main/java/server/MapleTrade.java
Normal file
523
src/main/java/server/MapleTrade.java
Normal file
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import config.YamlConfig;
|
||||
import tools.LogHelper;
|
||||
import tools.MaplePacketCreator;
|
||||
import client.MapleCharacter;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import client.inventory.manipulator.MapleKarmaManipulator;
|
||||
import constants.game.GameConstants;
|
||||
import net.server.coordinator.world.MapleInviteCoordinator;
|
||||
import net.server.coordinator.world.MapleInviteCoordinator.InviteResult;
|
||||
import net.server.coordinator.world.MapleInviteCoordinator.InviteType;
|
||||
import net.server.coordinator.world.MapleInviteCoordinator.MapleInviteResult;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
* @author Ronan - concurrency safety + check available slots + trade results
|
||||
*/
|
||||
public class MapleTrade {
|
||||
|
||||
public enum TradeResult {
|
||||
NO_RESPONSE(1),
|
||||
PARTNER_CANCEL(2),
|
||||
SUCCESSFUL(7),
|
||||
UNSUCCESSFUL(8),
|
||||
UNSUCCESSFUL_UNIQUE_ITEM_LIMIT(9),
|
||||
UNSUCCESSFUL_ANOTHER_MAP(12),
|
||||
UNSUCCESSFUL_DAMAGED_FILES(13);
|
||||
|
||||
private final int res;
|
||||
|
||||
private TradeResult(int res) {
|
||||
this.res = res;
|
||||
}
|
||||
|
||||
private byte getValue() {
|
||||
return (byte) res;
|
||||
}
|
||||
}
|
||||
|
||||
private MapleTrade partner = null;
|
||||
private List<Item> items = new ArrayList<>();
|
||||
private List<Item> exchangeItems;
|
||||
private int meso = 0;
|
||||
private int exchangeMeso;
|
||||
private AtomicBoolean locked = new AtomicBoolean(false);
|
||||
private MapleCharacter chr;
|
||||
private byte number;
|
||||
private boolean fullTrade = false;
|
||||
|
||||
public MapleTrade(byte number, MapleCharacter chr) {
|
||||
this.chr = chr;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public static int getFee(long meso) {
|
||||
long fee = 0;
|
||||
if (meso >= 100000000) {
|
||||
fee = (meso * 6) / 100;
|
||||
} else if (meso >= 25000000) {
|
||||
fee = (meso * 5) / 100;
|
||||
} else if (meso >= 10000000) {
|
||||
fee = (meso * 4) / 100;
|
||||
} else if (meso >= 5000000) {
|
||||
fee = (meso * 3) / 100;
|
||||
} else if (meso >= 1000000) {
|
||||
fee = (meso * 18) / 1000;
|
||||
} else if (meso >= 100000) {
|
||||
fee = (meso * 8) / 1000;
|
||||
}
|
||||
return (int) fee;
|
||||
}
|
||||
|
||||
private void lockTrade() {
|
||||
locked.set(true);
|
||||
partner.getChr().getClient().announce(MaplePacketCreator.getTradeConfirmation());
|
||||
}
|
||||
|
||||
private void fetchExchangedItems() {
|
||||
exchangeItems = partner.getItems();
|
||||
exchangeMeso = partner.getMeso();
|
||||
}
|
||||
|
||||
private void completeTrade() {
|
||||
byte result;
|
||||
boolean show = YamlConfig.config.server.USE_DEBUG;
|
||||
items.clear();
|
||||
meso = 0;
|
||||
|
||||
for (Item item : exchangeItems) {
|
||||
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
|
||||
MapleInventoryManipulator.addFromDrop(chr.getClient(), item, show);
|
||||
}
|
||||
|
||||
if (exchangeMeso > 0) {
|
||||
int fee = getFee(exchangeMeso);
|
||||
|
||||
chr.gainMeso(exchangeMeso - fee, show, true, show);
|
||||
if(fee > 0) {
|
||||
chr.dropMessage(1, "Transaction completed. You received " + GameConstants.numberWithCommas(exchangeMeso - fee) + " mesos due to trade fees.");
|
||||
} else {
|
||||
chr.dropMessage(1, "Transaction completed. You received " + GameConstants.numberWithCommas(exchangeMeso) + " mesos.");
|
||||
}
|
||||
|
||||
result = TradeResult.NO_RESPONSE.getValue();
|
||||
} else {
|
||||
result = TradeResult.SUCCESSFUL.getValue();
|
||||
}
|
||||
|
||||
exchangeMeso = 0;
|
||||
if (exchangeItems != null) {
|
||||
exchangeItems.clear();
|
||||
}
|
||||
|
||||
chr.getClient().announce(MaplePacketCreator.getTradeResult(number, result));
|
||||
}
|
||||
|
||||
private void cancel(byte result) {
|
||||
boolean show = YamlConfig.config.server.USE_DEBUG;
|
||||
|
||||
for (Item item : items) {
|
||||
MapleInventoryManipulator.addFromDrop(chr.getClient(), item, show);
|
||||
}
|
||||
if (meso > 0) {
|
||||
chr.gainMeso(meso, show, true, show);
|
||||
}
|
||||
meso = 0;
|
||||
if (items != null) {
|
||||
items.clear();
|
||||
}
|
||||
exchangeMeso = 0;
|
||||
if (exchangeItems != null) {
|
||||
exchangeItems.clear();
|
||||
}
|
||||
|
||||
chr.getClient().announce(MaplePacketCreator.getTradeResult(number, result));
|
||||
}
|
||||
|
||||
private boolean isLocked() {
|
||||
return locked.get();
|
||||
}
|
||||
|
||||
private int getMeso() {
|
||||
return meso;
|
||||
}
|
||||
|
||||
public void setMeso(int meso) {
|
||||
if (locked.get()) {
|
||||
throw new RuntimeException("Trade is locked.");
|
||||
}
|
||||
if (meso < 0) {
|
||||
System.out.println("[Hack] " + chr.getName() + " Trying to trade < 0 mesos");
|
||||
return;
|
||||
}
|
||||
if (chr.getMeso() >= meso) {
|
||||
chr.gainMeso(-meso, false, true, false);
|
||||
this.meso += meso;
|
||||
chr.getClient().announce(MaplePacketCreator.getTradeMesoSet((byte) 0, this.meso));
|
||||
if (partner != null) {
|
||||
partner.getChr().getClient().announce(MaplePacketCreator.getTradeMesoSet((byte) 1, this.meso));
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean addItem(Item item) {
|
||||
synchronized (items) {
|
||||
if (items.size() > 9) {
|
||||
return false;
|
||||
}
|
||||
for (Item it : items) {
|
||||
if (it.getPosition() == item.getPosition()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void chat(String message) {
|
||||
chr.getClient().announce(MaplePacketCreator.getTradeChat(chr, message, true));
|
||||
if (partner != null) {
|
||||
partner.getChr().getClient().announce(MaplePacketCreator.getTradeChat(chr, message, false));
|
||||
}
|
||||
}
|
||||
|
||||
public MapleTrade getPartner() {
|
||||
return partner;
|
||||
}
|
||||
|
||||
public void setPartner(MapleTrade partner) {
|
||||
if (locked.get()) {
|
||||
return;
|
||||
}
|
||||
this.partner = partner;
|
||||
}
|
||||
|
||||
public MapleCharacter getChr() {
|
||||
return chr;
|
||||
}
|
||||
|
||||
public List<Item> getItems() {
|
||||
return new LinkedList<>(items);
|
||||
}
|
||||
|
||||
public int getExchangeMesos(){
|
||||
return exchangeMeso;
|
||||
}
|
||||
|
||||
private boolean fitsMeso() {
|
||||
return chr.canHoldMeso(exchangeMeso - getFee(exchangeMeso));
|
||||
}
|
||||
|
||||
private boolean fitsInInventory() {
|
||||
List<Pair<Item, MapleInventoryType>> tradeItems = new LinkedList<>();
|
||||
for (Item item : exchangeItems) {
|
||||
tradeItems.add(new Pair<>(item, item.getInventoryType()));
|
||||
}
|
||||
|
||||
return MapleInventory.checkSpotsAndOwnership(chr, tradeItems);
|
||||
}
|
||||
|
||||
private boolean fitsUniquesInInventory() {
|
||||
List<Integer> exchangeItemids = new LinkedList<>();
|
||||
for (Item item : exchangeItems) {
|
||||
exchangeItemids.add(item.getItemId());
|
||||
}
|
||||
|
||||
return chr.canHoldUniques(exchangeItemids);
|
||||
}
|
||||
|
||||
private synchronized boolean checkTradeCompleteHandshake(boolean updateSelf) {
|
||||
MapleTrade self, other;
|
||||
|
||||
if (updateSelf) {
|
||||
self = this;
|
||||
other = this.getPartner();
|
||||
} else {
|
||||
self = this.getPartner();
|
||||
other = this;
|
||||
}
|
||||
|
||||
if (self.isLocked()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.lockTrade();
|
||||
return other.isLocked();
|
||||
}
|
||||
|
||||
private boolean checkCompleteHandshake() { // handshake checkout thanks to Ronan
|
||||
if (this.getChr().getId() < this.getPartner().getChr().getId()) {
|
||||
return this.checkTradeCompleteHandshake(true);
|
||||
} else {
|
||||
return this.getPartner().checkTradeCompleteHandshake(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void completeTrade(MapleCharacter chr) {
|
||||
MapleTrade local = chr.getTrade();
|
||||
MapleTrade partner = local.getPartner();
|
||||
if (local.checkCompleteHandshake()) {
|
||||
local.fetchExchangedItems();
|
||||
partner.fetchExchangedItems();
|
||||
|
||||
if (!local.fitsMeso()) {
|
||||
cancelTrade(local.getChr(), TradeResult.UNSUCCESSFUL);
|
||||
chr.message("There is not enough meso inventory space to complete the trade.");
|
||||
partner.getChr().message("Partner does not have enough meso inventory space to complete the trade.");
|
||||
return;
|
||||
} else if (!partner.fitsMeso()) {
|
||||
cancelTrade(partner.getChr(), TradeResult.UNSUCCESSFUL);
|
||||
chr.message("Partner does not have enough meso inventory space to complete the trade.");
|
||||
partner.getChr().message("There is not enough meso inventory space to complete the trade.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!local.fitsInInventory()) {
|
||||
if (local.fitsUniquesInInventory()) {
|
||||
cancelTrade(local.getChr(), TradeResult.UNSUCCESSFUL);
|
||||
chr.message("There is not enough inventory space to complete the trade.");
|
||||
partner.getChr().message("Partner does not have enough inventory space to complete the trade.");
|
||||
} else {
|
||||
cancelTrade(local.getChr(), TradeResult.UNSUCCESSFUL_UNIQUE_ITEM_LIMIT);
|
||||
partner.getChr().message("Partner cannot hold more than one one-of-a-kind item at a time.");
|
||||
}
|
||||
return;
|
||||
} else if (!partner.fitsInInventory()) {
|
||||
if (partner.fitsUniquesInInventory()) {
|
||||
cancelTrade(partner.getChr(), TradeResult.UNSUCCESSFUL);
|
||||
chr.message("Partner does not have enough inventory space to complete the trade.");
|
||||
partner.getChr().message("There is not enough inventory space to complete the trade.");
|
||||
} else {
|
||||
cancelTrade(partner.getChr(), TradeResult.UNSUCCESSFUL_UNIQUE_ITEM_LIMIT);
|
||||
chr.message("Partner cannot hold more than one one-of-a-kind item at a time.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (local.getChr().getLevel() < 15) {
|
||||
if (local.getChr().getMesosTraded() + local.exchangeMeso > 1000000) {
|
||||
cancelTrade(local.getChr(), TradeResult.NO_RESPONSE);
|
||||
local.getChr().getClient().announce(MaplePacketCreator.serverNotice(1, "Characters under level 15 may not trade more than 1 million mesos per day."));
|
||||
return;
|
||||
} else {
|
||||
local.getChr().addMesosTraded(local.exchangeMeso);
|
||||
}
|
||||
} else if (partner.getChr().getLevel() < 15) {
|
||||
if (partner.getChr().getMesosTraded() + partner.exchangeMeso > 1000000) {
|
||||
cancelTrade(partner.getChr(), TradeResult.NO_RESPONSE);
|
||||
partner.getChr().getClient().announce(MaplePacketCreator.serverNotice(1, "Characters under level 15 may not trade more than 1 million mesos per day."));
|
||||
return;
|
||||
} else {
|
||||
partner.getChr().addMesosTraded(partner.exchangeMeso);
|
||||
}
|
||||
}
|
||||
|
||||
LogHelper.logTrade(local, partner);
|
||||
local.completeTrade();
|
||||
partner.completeTrade();
|
||||
|
||||
partner.getChr().setTrade(null);
|
||||
chr.setTrade(null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void cancelTradeInternal(MapleCharacter chr, byte selfResult, byte partnerResult) {
|
||||
MapleTrade trade = chr.getTrade();
|
||||
if(trade == null) return;
|
||||
|
||||
trade.cancel(selfResult);
|
||||
if (trade.getPartner() != null) {
|
||||
trade.getPartner().cancel(partnerResult);
|
||||
trade.getPartner().getChr().setTrade(null);
|
||||
|
||||
MapleInviteCoordinator.answerInvite(InviteType.TRADE, trade.getChr().getId(), trade.getPartner().getChr().getId(), false);
|
||||
MapleInviteCoordinator.answerInvite(InviteType.TRADE, trade.getPartner().getChr().getId(), trade.getChr().getId(), false);
|
||||
}
|
||||
chr.setTrade(null);
|
||||
}
|
||||
|
||||
private static byte[] tradeResultsPair(byte result) {
|
||||
byte selfResult, partnerResult;
|
||||
|
||||
if (result == TradeResult.PARTNER_CANCEL.getValue()) {
|
||||
partnerResult = result;
|
||||
selfResult = TradeResult.NO_RESPONSE.getValue();
|
||||
} else if (result == TradeResult.UNSUCCESSFUL_UNIQUE_ITEM_LIMIT.getValue()) {
|
||||
partnerResult = TradeResult.UNSUCCESSFUL.getValue();
|
||||
selfResult = result;
|
||||
} else {
|
||||
partnerResult = result;
|
||||
selfResult = result;
|
||||
}
|
||||
|
||||
return new byte[]{selfResult, partnerResult};
|
||||
}
|
||||
|
||||
private synchronized void tradeCancelHandshake(boolean updateSelf, byte result) {
|
||||
byte selfResult, partnerResult;
|
||||
MapleTrade self;
|
||||
|
||||
byte[] pairedResult = tradeResultsPair(result);
|
||||
selfResult = pairedResult[0];
|
||||
partnerResult = pairedResult[1];
|
||||
|
||||
if (updateSelf) {
|
||||
self = this;
|
||||
} else {
|
||||
self = this.getPartner();
|
||||
}
|
||||
|
||||
cancelTradeInternal(self.getChr(), selfResult, partnerResult);
|
||||
}
|
||||
|
||||
private void cancelHandshake(byte result) { // handshake checkout thanks to Ronan
|
||||
MapleTrade partner = this.getPartner();
|
||||
if (partner == null || this.getChr().getId() < partner.getChr().getId()) {
|
||||
this.tradeCancelHandshake(true, result);
|
||||
} else {
|
||||
partner.tradeCancelHandshake(false, result);
|
||||
}
|
||||
}
|
||||
|
||||
public static void cancelTrade(MapleCharacter chr, TradeResult result) {
|
||||
MapleTrade trade = chr.getTrade();
|
||||
if(trade == null) return;
|
||||
|
||||
trade.cancelHandshake(result.getValue());
|
||||
}
|
||||
|
||||
public static void startTrade(MapleCharacter chr) {
|
||||
if (chr.getTrade() == null) {
|
||||
chr.setTrade(new MapleTrade((byte) 0, chr));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasTradeInviteBack(MapleCharacter c1, MapleCharacter c2) {
|
||||
MapleTrade other = c2.getTrade();
|
||||
if (other != null) {
|
||||
MapleTrade otherPartner = other.getPartner();
|
||||
if (otherPartner != null) {
|
||||
if (otherPartner.getChr().getId() == c1.getId()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void inviteTrade(MapleCharacter c1, MapleCharacter c2) {
|
||||
if (MapleInviteCoordinator.hasInvite(InviteType.TRADE, c1.getId())) {
|
||||
if (hasTradeInviteBack(c1, c2)) {
|
||||
c1.message("You are already managing this player's trade invitation.");
|
||||
} else {
|
||||
c1.message("You are already managing someone's trade invitation.");
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (c1.getTrade().isFullTrade()) {
|
||||
c1.message("You are already in a trade.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (MapleInviteCoordinator.createInvite(InviteType.TRADE, c1, c1.getId(), c2.getId())) {
|
||||
if (c2.getTrade() == null) {
|
||||
c2.setTrade(new MapleTrade((byte) 1, c2));
|
||||
c2.getTrade().setPartner(c1.getTrade());
|
||||
c1.getTrade().setPartner(c2.getTrade());
|
||||
|
||||
c1.getClient().announce(MaplePacketCreator.getTradeStart(c1.getClient(), c1.getTrade(), (byte) 0));
|
||||
c2.getClient().announce(MaplePacketCreator.tradeInvite(c1));
|
||||
} else {
|
||||
c1.message("The other player is already trading with someone else.");
|
||||
cancelTrade(c1, TradeResult.NO_RESPONSE);
|
||||
MapleInviteCoordinator.answerInvite(InviteType.TRADE, c2.getId(), c1.getId(), false);
|
||||
}
|
||||
} else {
|
||||
c1.message("The other player is already managing someone else's trade invitation.");
|
||||
cancelTrade(c1, TradeResult.NO_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
public static void visitTrade(MapleCharacter c1, MapleCharacter c2) {
|
||||
MapleInviteResult inviteRes = MapleInviteCoordinator.answerInvite(InviteType.TRADE, c1.getId(), c2.getId(), true);
|
||||
|
||||
InviteResult res = inviteRes.result;
|
||||
if (res == InviteResult.ACCEPTED) {
|
||||
if (c1.getTrade() != null && c1.getTrade().getPartner() == c2.getTrade() && c2.getTrade() != null && c2.getTrade().getPartner() == c1.getTrade()) {
|
||||
c2.getClient().announce(MaplePacketCreator.getTradePartnerAdd(c1));
|
||||
c1.getClient().announce(MaplePacketCreator.getTradeStart(c1.getClient(), c1.getTrade(), (byte) 1));
|
||||
c1.getTrade().setFullTrade(true);
|
||||
c2.getTrade().setFullTrade(true);
|
||||
} else {
|
||||
c1.message("The other player has already closed the trade.");
|
||||
}
|
||||
} else {
|
||||
c1.message("This trade invitation already rescinded.");
|
||||
cancelTrade(c1, TradeResult.NO_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
public static void declineTrade(MapleCharacter chr) {
|
||||
MapleTrade trade = chr.getTrade();
|
||||
if (trade != null) {
|
||||
if (trade.getPartner() != null) {
|
||||
MapleCharacter other = trade.getPartner().getChr();
|
||||
if (MapleInviteCoordinator.answerInvite(InviteType.TRADE, chr.getId(), other.getId(), false).result == InviteResult.DENIED) {
|
||||
other.message(chr.getName() + " has declined your trade request.");
|
||||
}
|
||||
|
||||
other.getTrade().cancel(TradeResult.PARTNER_CANCEL.getValue());
|
||||
other.setTrade(null);
|
||||
|
||||
}
|
||||
trade.cancel(TradeResult.NO_RESPONSE.getValue());
|
||||
chr.setTrade(null);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFullTrade() {
|
||||
return fullTrade;
|
||||
}
|
||||
|
||||
public void setFullTrade(boolean fullTrade) {
|
||||
this.fullTrade = fullTrade;
|
||||
}
|
||||
}
|
||||
72
src/main/java/server/ThreadManager.java
Normal file
72
src/main/java/server/ThreadManager.java
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class ThreadManager {
|
||||
private static ThreadManager instance = new ThreadManager();
|
||||
|
||||
public static ThreadManager getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ThreadPoolExecutor tpe;
|
||||
|
||||
private ThreadManager() {}
|
||||
|
||||
private class RejectedExecutionHandlerImpl implements RejectedExecutionHandler {
|
||||
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
|
||||
Thread t = new Thread(r);
|
||||
t.start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void newTask(Runnable r) {
|
||||
tpe.execute(r);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
RejectedExecutionHandler reh = new RejectedExecutionHandlerImpl();
|
||||
ThreadFactory tf = Executors.defaultThreadFactory();
|
||||
|
||||
tpe = new ThreadPoolExecutor(20, 1000, 77, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50), tf, reh);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
tpe.shutdown();
|
||||
try {
|
||||
tpe.awaitTermination(5, TimeUnit.MINUTES);
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
|
||||
}
|
||||
154
src/main/java/server/TimerManager.java
Normal file
154
src/main/java/server/TimerManager.java
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import net.server.Server;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
import tools.FilePrinter;
|
||||
|
||||
public class TimerManager implements TimerManagerMBean {
|
||||
private static TimerManager instance = new TimerManager();
|
||||
|
||||
public static TimerManager getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ScheduledThreadPoolExecutor ses;
|
||||
|
||||
private TimerManager() {
|
||||
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
|
||||
try {
|
||||
mBeanServer.registerMBean(this, new ObjectName("server:type=TimerManger"));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (ses != null && !ses.isShutdown() && !ses.isTerminated()) {
|
||||
return;
|
||||
}
|
||||
ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(4, new ThreadFactory() {
|
||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r);
|
||||
t.setName("TimerManager-Worker-" + threadNumber.getAndIncrement());
|
||||
return t;
|
||||
}
|
||||
});
|
||||
//this is a no-no, it actually does nothing..then why the fuck are you doing it?
|
||||
stpe.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
|
||||
stpe.setRemoveOnCancelPolicy(true);
|
||||
|
||||
stpe.setKeepAliveTime(5, TimeUnit.MINUTES);
|
||||
stpe.allowCoreThreadTimeOut(true);
|
||||
|
||||
ses = stpe;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
ses.shutdownNow();
|
||||
}
|
||||
|
||||
public Runnable purge() {//Yay?
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Server.getInstance().forceUpdateCurrentTime();
|
||||
ses.purge();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public ScheduledFuture<?> register(Runnable r, long repeatTime, long delay) {
|
||||
return ses.scheduleAtFixedRate(new LoggingSaveRunnable(r), delay, repeatTime, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public ScheduledFuture<?> register(Runnable r, long repeatTime) {
|
||||
return ses.scheduleAtFixedRate(new LoggingSaveRunnable(r), 0, repeatTime, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public ScheduledFuture<?> schedule(Runnable r, long delay) {
|
||||
return ses.schedule(new LoggingSaveRunnable(r), delay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public ScheduledFuture<?> scheduleAtTimestamp(Runnable r, long timestamp) {
|
||||
return schedule(r, timestamp - System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getActiveCount() {
|
||||
return ses.getActiveCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCompletedTaskCount() {
|
||||
return ses.getCompletedTaskCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getQueuedTasks() {
|
||||
return ses.getQueue().toArray().length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTaskCount() {
|
||||
return ses.getTaskCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return ses.isShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
return ses.isTerminated();
|
||||
}
|
||||
|
||||
|
||||
private static class LoggingSaveRunnable implements Runnable {
|
||||
Runnable r;
|
||||
|
||||
public LoggingSaveRunnable(Runnable r) {
|
||||
this.r = r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
r.run();
|
||||
} catch (Throwable t) {
|
||||
FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/main/java/server/TimerManagerMBean.java
Normal file
31
src/main/java/server/TimerManagerMBean.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
public interface TimerManagerMBean {
|
||||
public boolean isTerminated();
|
||||
public boolean isShutdown();
|
||||
public long getCompletedTaskCount();
|
||||
public long getActiveCount();
|
||||
public long getTaskCount();
|
||||
public int getQueuedTasks();
|
||||
}
|
||||
34
src/main/java/server/events/MapleEvents.java
Normal file
34
src/main/java/server/events/MapleEvents.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
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.events;
|
||||
|
||||
/**
|
||||
* @author kevintjuh93
|
||||
*/
|
||||
public abstract class MapleEvents {
|
||||
public MapleEvents() {
|
||||
|
||||
}
|
||||
|
||||
public abstract int getInfo();
|
||||
}
|
||||
58
src/main/java/server/events/RescueGaga.java
Normal file
58
src/main/java/server/events/RescueGaga.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package server.events;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import client.SkillFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author kevintjuh93
|
||||
*/
|
||||
public class RescueGaga extends MapleEvents {
|
||||
|
||||
private int completed;
|
||||
|
||||
public RescueGaga(int completed) {
|
||||
super();
|
||||
this.completed = completed;
|
||||
}
|
||||
|
||||
public int getCompleted() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
public void complete() {
|
||||
completed++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInfo() {
|
||||
return getCompleted();
|
||||
}
|
||||
|
||||
public void giveSkill(MapleCharacter chr) {
|
||||
int skillid = 0;
|
||||
switch (chr.getJobType()) {
|
||||
case 0:
|
||||
skillid = 1013;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
skillid = 10001014;
|
||||
}
|
||||
|
||||
long expiration = (System.currentTimeMillis() + 3600 * 24 * 20 * 1000);//20 days
|
||||
if (completed < 20) {
|
||||
chr.changeSkillLevel(SkillFactory.getSkill(skillid), (byte) 1, 1, expiration);
|
||||
chr.changeSkillLevel(SkillFactory.getSkill(skillid + 1), (byte) 1, 1, expiration);
|
||||
chr.changeSkillLevel(SkillFactory.getSkill(skillid + 2), (byte) 1, 1, expiration);
|
||||
} else {
|
||||
chr.changeSkillLevel(SkillFactory.getSkill(skillid), (byte) 2, 2, chr.getSkillExpiration(skillid));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
206
src/main/java/server/events/gm/MapleCoconut.java
Normal file
206
src/main/java/server/events/gm/MapleCoconut.java
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
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.events.gm;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import server.TimerManager;
|
||||
import server.maps.MapleMap;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author kevintjuh93
|
||||
*/
|
||||
//Make them better :)
|
||||
public class MapleCoconut extends MapleEvent {
|
||||
private MapleMap map = null;
|
||||
private int MapleScore = 0;
|
||||
private int StoryScore = 0;
|
||||
private int countBombing = 80;
|
||||
private int countFalling = 401;
|
||||
private int countStopped = 20;
|
||||
private List<MapleCoconuts> coconuts = new LinkedList<MapleCoconuts>();
|
||||
|
||||
public MapleCoconut(MapleMap map) {
|
||||
super(1, 50);
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public void startEvent() {
|
||||
map.startEvent();
|
||||
for (int i = 0; i < 506; i++) {
|
||||
coconuts.add(new MapleCoconuts(i));
|
||||
}
|
||||
map.broadcastMessage(MaplePacketCreator.hitCoconut(true, 0, 0));
|
||||
setCoconutsHittable(true);
|
||||
map.broadcastMessage(MaplePacketCreator.getClock(300));
|
||||
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (map.getId() == 109080000) {
|
||||
if (getMapleScore() == getStoryScore()) {
|
||||
bonusTime();
|
||||
} else if (getMapleScore() > getStoryScore()) {
|
||||
for (MapleCharacter chr : map.getCharacters()) {
|
||||
if (chr.getTeam() == 0) {
|
||||
chr.getClient().announce(MaplePacketCreator.showEffect("event/coconut/victory"));
|
||||
chr.getClient().announce(MaplePacketCreator.playSound("Coconut/Victory"));
|
||||
} else {
|
||||
chr.getClient().announce(MaplePacketCreator.showEffect("event/coconut/lose"));
|
||||
chr.getClient().announce(MaplePacketCreator.playSound("Coconut/Failed"));
|
||||
}
|
||||
}
|
||||
warpOut();
|
||||
} else {
|
||||
for (MapleCharacter chr : map.getCharacters()) {
|
||||
if (chr.getTeam() == 1) {
|
||||
chr.getClient().announce(MaplePacketCreator.showEffect("event/coconut/victory"));
|
||||
chr.getClient().announce(MaplePacketCreator.playSound("Coconut/Victory"));
|
||||
} else {
|
||||
chr.getClient().announce(MaplePacketCreator.showEffect("event/coconut/lose"));
|
||||
chr.getClient().announce(MaplePacketCreator.playSound("Coconut/Failed"));
|
||||
}
|
||||
}
|
||||
warpOut();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 300000);
|
||||
}
|
||||
|
||||
public void bonusTime() {
|
||||
map.broadcastMessage(MaplePacketCreator.getClock(120));
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (getMapleScore() == getStoryScore()) {
|
||||
for (MapleCharacter chr : map.getCharacters()) {
|
||||
chr.getClient().announce(MaplePacketCreator.showEffect("event/coconut/lose"));
|
||||
chr.getClient().announce(MaplePacketCreator.playSound("Coconut/Failed"));
|
||||
}
|
||||
warpOut();
|
||||
} else if (getMapleScore() > getStoryScore()) {
|
||||
for (MapleCharacter chr : map.getCharacters()) {
|
||||
if (chr.getTeam() == 0) {
|
||||
chr.getClient().announce(MaplePacketCreator.showEffect("event/coconut/victory"));
|
||||
chr.getClient().announce(MaplePacketCreator.playSound("Coconut/Victory"));
|
||||
} else {
|
||||
chr.getClient().announce(MaplePacketCreator.showEffect("event/coconut/lose"));
|
||||
chr.getClient().announce(MaplePacketCreator.playSound("Coconut/Failed"));
|
||||
}
|
||||
}
|
||||
warpOut();
|
||||
} else {
|
||||
for (MapleCharacter chr : map.getCharacters()) {
|
||||
if (chr.getTeam() == 1) {
|
||||
chr.getClient().announce(MaplePacketCreator.showEffect("event/coconut/victory"));
|
||||
chr.getClient().announce(MaplePacketCreator.playSound("Coconut/Victory"));
|
||||
} else {
|
||||
chr.getClient().announce(MaplePacketCreator.showEffect("event/coconut/lose"));
|
||||
chr.getClient().announce(MaplePacketCreator.playSound("Coconut/Failed"));
|
||||
}
|
||||
}
|
||||
warpOut();
|
||||
}
|
||||
}
|
||||
}, 120000);
|
||||
|
||||
}
|
||||
|
||||
public void warpOut() {
|
||||
setCoconutsHittable(false);
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<MapleCharacter> chars = new ArrayList<>(map.getCharacters());
|
||||
|
||||
for (MapleCharacter chr : chars) {
|
||||
if ((getMapleScore() > getStoryScore() && chr.getTeam() == 0) || (getStoryScore() > getMapleScore() && chr.getTeam() == 1)) {
|
||||
chr.changeMap(109050000);
|
||||
} else {
|
||||
chr.changeMap(109050001);
|
||||
}
|
||||
}
|
||||
map.setCoconut(null);
|
||||
}
|
||||
}, 12000);
|
||||
}
|
||||
|
||||
public int getMapleScore() {
|
||||
return MapleScore;
|
||||
}
|
||||
|
||||
public int getStoryScore() {
|
||||
return StoryScore;
|
||||
}
|
||||
|
||||
public void addMapleScore() {
|
||||
this.MapleScore += 1;
|
||||
}
|
||||
|
||||
public void addStoryScore() {
|
||||
this.StoryScore += 1;
|
||||
}
|
||||
|
||||
public int getBombings() {
|
||||
return countBombing;
|
||||
}
|
||||
|
||||
public void bombCoconut() {
|
||||
countBombing--;
|
||||
}
|
||||
|
||||
public int getFalling() {
|
||||
return countFalling;
|
||||
}
|
||||
|
||||
public void fallCoconut() {
|
||||
countFalling--;
|
||||
}
|
||||
|
||||
public int getStopped() {
|
||||
return countStopped;
|
||||
}
|
||||
|
||||
public void stopCoconut() {
|
||||
countStopped--;
|
||||
}
|
||||
|
||||
public MapleCoconuts getCoconut(int id) {
|
||||
return coconuts.get(id);
|
||||
}
|
||||
|
||||
public List<MapleCoconuts> getAllCoconuts() {
|
||||
return coconuts;
|
||||
}
|
||||
|
||||
public void setCoconutsHittable(boolean hittable) {
|
||||
for (MapleCoconuts nut : coconuts) {
|
||||
nut.setHittable(hittable);
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/main/java/server/events/gm/MapleCoconuts.java
Normal file
63
src/main/java/server/events/gm/MapleCoconuts.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
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.events.gm;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author kevintjuh93
|
||||
*/
|
||||
public class MapleCoconuts {
|
||||
|
||||
private int id;
|
||||
private int hits = 0;
|
||||
private boolean hittable = false;
|
||||
private long hittime = System.currentTimeMillis();
|
||||
|
||||
public MapleCoconuts(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void hit() {
|
||||
this.hittime = System.currentTimeMillis() + 750;
|
||||
hits++;
|
||||
}
|
||||
|
||||
public int getHits() {
|
||||
return hits;
|
||||
}
|
||||
|
||||
public void resetHits() {
|
||||
hits = 0;
|
||||
}
|
||||
|
||||
public boolean isHittable() {
|
||||
return hittable;
|
||||
}
|
||||
|
||||
public void setHittable(boolean hittable) {
|
||||
this.hittable = hittable;
|
||||
}
|
||||
|
||||
public long getHitTime() {
|
||||
return hittime;
|
||||
}
|
||||
}
|
||||
53
src/main/java/server/events/gm/MapleEvent.java
Normal file
53
src/main/java/server/events/gm/MapleEvent.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
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.events.gm;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author kevintjuh93
|
||||
*/
|
||||
public class MapleEvent {
|
||||
private int mapid;
|
||||
private int limit;
|
||||
|
||||
public MapleEvent(int mapid, int limit) {
|
||||
this.mapid = mapid;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public int getMapId() {
|
||||
return mapid;
|
||||
}
|
||||
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public void minusLimit() {
|
||||
this.limit--;
|
||||
}
|
||||
|
||||
public void addLimit() {
|
||||
this.limit++;
|
||||
}
|
||||
}
|
||||
133
src/main/java/server/events/gm/MapleFitness.java
Normal file
133
src/main/java/server/events/gm/MapleFitness.java
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
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.events.gm;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import server.TimerManager;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author kevintjuh93
|
||||
*/
|
||||
public class MapleFitness {
|
||||
private MapleCharacter chr;
|
||||
private long time = 0;
|
||||
private long timeStarted = 0;
|
||||
private ScheduledFuture<?> schedule = null;
|
||||
private ScheduledFuture<?> schedulemsg = null;
|
||||
|
||||
public MapleFitness(final MapleCharacter chr) {
|
||||
this.chr = chr;
|
||||
this.schedule = TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (chr.getMapId() >= 109040000 && chr.getMapId() <= 109040004)
|
||||
chr.changeMap(chr.getMap().getReturnMap());
|
||||
}
|
||||
}, 900000);
|
||||
}
|
||||
|
||||
public void startFitness() {
|
||||
chr.getMap().startEvent();
|
||||
chr.getClient().announce(MaplePacketCreator.getClock(900));
|
||||
this.timeStarted = System.currentTimeMillis();
|
||||
this.time = 900000;
|
||||
checkAndMessage();
|
||||
|
||||
chr.getMap().getPortal("join00").setPortalStatus(true);
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "The portal has now opened. Press the up arrow key at the portal to enter."));
|
||||
}
|
||||
|
||||
public boolean isTimerStarted() {
|
||||
return time > 0 && timeStarted > 0;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void resetTimes() {
|
||||
this.time = 0;
|
||||
this.timeStarted = 0;
|
||||
schedule.cancel(false);
|
||||
schedulemsg.cancel(false);
|
||||
}
|
||||
|
||||
public long getTimeLeft() {
|
||||
return time - (System.currentTimeMillis() - timeStarted);
|
||||
}
|
||||
|
||||
public void checkAndMessage() {
|
||||
this.schedulemsg = TimerManager.getInstance().register(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (chr.getFitness() == null) {
|
||||
resetTimes();
|
||||
}
|
||||
if (chr.getMap().getId() >= 109040000 && chr.getMap().getId() <= 109040004) {
|
||||
if (getTimeLeft() > 9000 && getTimeLeft() < 11000) {
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "You have 10 sec left. Those of you unable to beat the game, we hope you beat it next time! Great job everyone!! See you later~"));
|
||||
} else if (getTimeLeft() > 99000 && getTimeLeft() < 101000) {
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "Alright, you don't have much time remaining. Please hurry up a little!"));
|
||||
} else if (getTimeLeft() > 239000 && getTimeLeft() < 241000) {
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "The 4th stage is the last one for [The Maple Physical Fitness Test]. Please don't give up at the last minute and try your best. The reward is waiting for you at the very top!"));
|
||||
} else if (getTimeLeft() > 299000 && getTimeLeft() < 301000) {
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "The 3rd stage offers traps where you may see them, but you won't be able to step on them. Please be careful of them as you make your way up."));
|
||||
} else if (getTimeLeft() > 359000 && getTimeLeft() < 361000) {
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "For those who have heavy lags, please make sure to move slowly to avoid falling all the way down because of lags."));
|
||||
} else if (getTimeLeft() > 499000 && getTimeLeft() < 501000) {
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "Please remember that if you die during the event, you'll be eliminated from the game. If you're running out of HP, either take a potion or recover HP first before moving on."));
|
||||
} else if (getTimeLeft() > 599000 && getTimeLeft() < 601000) {
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "The most important thing you'll need to know to avoid the bananas thrown by the monkeys is *Timing* Timing is everything in this!"));
|
||||
} else if (getTimeLeft() > 659000 && getTimeLeft() < 661000) {
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "The 2nd stage offers monkeys throwing bananas. Please make sure to avoid them by moving along at just the right timing."));
|
||||
} else if (getTimeLeft() > 699000 && getTimeLeft() < 701000) {
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "Please remember that if you die during the event, you'll be eliminated from the game. You still have plenty of time left, so either take a potion or recover HP first before moving on."));
|
||||
} else if (getTimeLeft() > 779000 && getTimeLeft() < 781000) {
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "Everyone that clears [The Maple Physical Fitness Test] on time will be given an item, regardless of the order of finish, so just relax, take your time, and clear the 4 stages."));
|
||||
} else if (getTimeLeft() > 839000 && getTimeLeft() < 841000) {
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "There may be a heavy lag due to many users at stage 1 all at once. It won't be difficult, so please make sure not to fall down because of heavy lag."));
|
||||
} else if (getTimeLeft() > 869000 && getTimeLeft() < 871000) {
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "[MapleStory Physical Fitness Test] consists of 4 stages, and if you happen to die during the game, you'll be eliminated from the game, so please be careful of that."));
|
||||
}
|
||||
} else {
|
||||
resetTimes();
|
||||
}
|
||||
}
|
||||
}, 5000, 29500);
|
||||
}
|
||||
// 14:30 [Notice][MapleStory Physical Fitness Test] consists of 4 stages, and if you happen to die during the game, you'll be eliminated from the game, so please be careful of that.
|
||||
// 14:00 [Notice]There may be a heavy lag due to many users at stage 1 all at once. It won't be difficult, so please make sure not to fall down because of heavy lag.
|
||||
// 13:00 [Notice]Everyone that clears [The Maple Physical Fitness Test] on time will be given an item, regardless of the order of finish, so just relax, take your time, and clear the 4 stages.
|
||||
// 11:40 [Notice]Please remember that if you die during the event, you'll be eliminated from the game. You still have plenty of time left, so either take a potion or recover HP first before moving on.
|
||||
// 11:00 [Notice]The 2nd stage offers monkeys throwing bananas. Please make sure to avoid them by moving along at just the right timing.
|
||||
// 10:00 [Notice]The most important thing you'll need to know to avoid the bananas thrown by the monkeys is *Timing* Timing is everything in this!
|
||||
// 8:20 [Notice]Please remember that if you die during the event, you'll be eliminated from the game. If you're running out of HP, either take a potion or recover HP first before moving on.
|
||||
// 6:00 [Notice]For those who have heavy lags, please make sure to move slowly to avoid falling all the way down because of lags.
|
||||
// 5:00 [Notice]The 3rd stage offers traps where you may see them, but you won't be able to step on them. Please be careful of them as you make your way up.
|
||||
// 4:00 [Notice]The 4th stage is the last one for [The Maple Physical Fitness Test]. Please don't give up at the last minute and try your best. The reward is waiting for you at the very top!
|
||||
// 1:40 [Notice]Alright, you don't have much time remaining. Please hurry up a little!
|
||||
// 0:10 [Notice]You have 10 sec left. Those of you unable to beat the game, we hope you beat it next time! Great job everyone!! See you later~
|
||||
}
|
||||
78
src/main/java/server/events/gm/MapleOla.java
Normal file
78
src/main/java/server/events/gm/MapleOla.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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.events.gm;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import server.TimerManager;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author kevintjuh93
|
||||
*/
|
||||
public class MapleOla {
|
||||
private MapleCharacter chr;
|
||||
private long time = 0;
|
||||
private long timeStarted = 0;
|
||||
private ScheduledFuture<?> schedule = null;
|
||||
|
||||
public MapleOla(final MapleCharacter chr) {
|
||||
this.chr = chr;
|
||||
this.schedule = TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (chr.getMapId() >= 109030001 && chr.getMapId() <= 109030303)
|
||||
chr.changeMap(chr.getMap().getReturnMap());
|
||||
resetTimes();
|
||||
}
|
||||
}, 360000);
|
||||
}
|
||||
|
||||
public void startOla() { // TODO: Messages
|
||||
chr.getMap().startEvent();
|
||||
chr.getClient().announce(MaplePacketCreator.getClock(360));
|
||||
this.timeStarted = System.currentTimeMillis();
|
||||
this.time = 360000;
|
||||
|
||||
chr.getMap().getPortal("join00").setPortalStatus(true);
|
||||
chr.getClient().announce(MaplePacketCreator.serverNotice(0, "The portal has now opened. Press the up arrow key at the portal to enter."));
|
||||
}
|
||||
|
||||
public boolean isTimerStarted() {
|
||||
return time > 0 && timeStarted > 0;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void resetTimes() {
|
||||
this.time = 0;
|
||||
this.timeStarted = 0;
|
||||
schedule.cancel(false);
|
||||
}
|
||||
|
||||
public long getTimeLeft() {
|
||||
return time - (System.currentTimeMillis() - timeStarted);
|
||||
}
|
||||
}
|
||||
111
src/main/java/server/events/gm/MapleOxQuiz.java
Normal file
111
src/main/java/server/events/gm/MapleOxQuiz.java
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
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.events.gm;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import tools.Randomizer;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import server.TimerManager;
|
||||
import server.maps.MapleMap;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author FloppyDisk
|
||||
*/
|
||||
public final class MapleOxQuiz {
|
||||
private int round = 1;
|
||||
private int question = 1;
|
||||
private MapleMap map = null;
|
||||
private int expGain = 200;
|
||||
private static MapleDataProvider stringData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Etc.wz"));
|
||||
|
||||
public MapleOxQuiz(MapleMap map) {
|
||||
this.map = map;
|
||||
this.round = Randomizer.nextInt(9);
|
||||
this.question = 1;
|
||||
}
|
||||
|
||||
private boolean isCorrectAnswer(MapleCharacter chr, int answer) {
|
||||
double x = chr.getPosition().getX();
|
||||
double y = chr.getPosition().getY();
|
||||
if ((x > -234 && y > -26 && answer == 0) || (x < -234 && y > -26 && answer == 1)) {
|
||||
chr.dropMessage("Correct!");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void sendQuestion() {
|
||||
int gm = 0;
|
||||
for (MapleCharacter mc : map.getCharacters()) {
|
||||
if (mc.gmLevel() > 1) {
|
||||
gm++;
|
||||
}
|
||||
}
|
||||
final int number = gm;
|
||||
map.broadcastMessage(MaplePacketCreator.showOXQuiz(round, question, true));
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
map.broadcastMessage(MaplePacketCreator.showOXQuiz(round, question, true));
|
||||
List<MapleCharacter> chars = new ArrayList<>(map.getCharacters());
|
||||
|
||||
for (MapleCharacter chr : chars) {
|
||||
if (chr != null) // make sure they aren't null... maybe something can happen in 12 seconds.
|
||||
{
|
||||
if (!isCorrectAnswer(chr, getOXAnswer(round, question)) && !chr.isGM()) {
|
||||
chr.changeMap(chr.getMap().getReturnMap());
|
||||
} else {
|
||||
chr.gainExp(expGain, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
//do question
|
||||
if ((round == 1 && question == 29) || ((round == 2 || round == 3) && question == 17) || ((round == 4 || round == 8) && question == 12) || (round == 5 && question == 26) || (round == 9 && question == 44) || ((round == 6 || round == 7) && question == 16)) {
|
||||
question = 100;
|
||||
} else {
|
||||
question++;
|
||||
}
|
||||
//send question
|
||||
if (map.getCharacters().size() - number <= 2) {
|
||||
map.broadcastMessage(MaplePacketCreator.serverNotice(6, "The event has ended"));
|
||||
map.getPortal("join00").setPortalStatus(true);
|
||||
map.setOx(null);
|
||||
map.setOxQuiz(false);
|
||||
//prizes here
|
||||
return;
|
||||
}
|
||||
sendQuestion();
|
||||
}
|
||||
}, 30000); // Time to answer = 30 seconds ( Ox Quiz packet shows a 30 second timer.
|
||||
}
|
||||
|
||||
private static int getOXAnswer(int imgdir, int id) {
|
||||
return MapleDataTool.getInt(stringData.getData("OXQuiz.img").getChildByPath("" + imgdir + "").getChildByPath("" + id + "").getChildByPath("a"));
|
||||
}
|
||||
}
|
||||
165
src/main/java/server/events/gm/MapleSnowball.java
Normal file
165
src/main/java/server/events/gm/MapleSnowball.java
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
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.events.gm;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import server.TimerManager;
|
||||
import server.maps.MapleMap;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author kevintjuh93
|
||||
*/
|
||||
public class MapleSnowball {
|
||||
private MapleMap map;
|
||||
private int position = 0;
|
||||
private int hits = 3;
|
||||
private int snowmanhp = 1000;
|
||||
private boolean hittable = false;
|
||||
private int team;
|
||||
private boolean winner = false;
|
||||
List<MapleCharacter> characters = new LinkedList<MapleCharacter>();
|
||||
|
||||
public MapleSnowball(int team, MapleMap map) {
|
||||
this.map = map;
|
||||
this.team = team;
|
||||
|
||||
for (MapleCharacter chr : map.getCharacters()) {
|
||||
if (chr.getTeam() == team)
|
||||
characters.add(chr);
|
||||
}
|
||||
}
|
||||
|
||||
public void startEvent() {
|
||||
if (hittable == true) return;
|
||||
|
||||
for (MapleCharacter chr : characters) {
|
||||
if (chr != null) {
|
||||
chr.announce(MaplePacketCreator.rollSnowBall(false, 1, map.getSnowball(0), map.getSnowball(1)));
|
||||
chr.announce(MaplePacketCreator.getClock(600));
|
||||
}
|
||||
}
|
||||
hittable = true;
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (map.getSnowball(team).getPosition() > map.getSnowball(team == 0 ? 1 : 0).getPosition()) {
|
||||
for (MapleCharacter chr : characters) {
|
||||
if (chr != null)
|
||||
chr.announce(MaplePacketCreator.rollSnowBall(false, 3, map.getSnowball(0), map.getSnowball(0)));
|
||||
}
|
||||
winner = true;
|
||||
} else if (map.getSnowball(team == 0 ? 1 : 0).getPosition() > map.getSnowball(team).getPosition()) {
|
||||
for (MapleCharacter chr : characters) {
|
||||
if (chr != null)
|
||||
chr.announce(MaplePacketCreator.rollSnowBall(false, 4, map.getSnowball(0), map.getSnowball(0)));
|
||||
}
|
||||
winner = true;
|
||||
} //Else
|
||||
warpOut();
|
||||
}
|
||||
}, 600000);
|
||||
|
||||
}
|
||||
|
||||
public boolean isHittable() {
|
||||
return hittable;
|
||||
}
|
||||
|
||||
public void setHittable(boolean hit) {
|
||||
this.hittable = hit;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public int getSnowmanHP() {
|
||||
return snowmanhp;
|
||||
}
|
||||
|
||||
public void setSnowmanHP(int hp) {
|
||||
this.snowmanhp = hp;
|
||||
}
|
||||
|
||||
public void hit(int what, int damage) {
|
||||
if (what < 2)
|
||||
if (damage > 0)
|
||||
this.hits--;
|
||||
else {
|
||||
if (this.snowmanhp - damage < 0) {
|
||||
this.snowmanhp = 0;
|
||||
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
setSnowmanHP(7500);
|
||||
message(5);
|
||||
}
|
||||
}, 10000);
|
||||
} else
|
||||
this.snowmanhp -= damage;
|
||||
map.broadcastMessage(MaplePacketCreator.rollSnowBall(false, 1, map.getSnowball(0), map.getSnowball(1)));
|
||||
}
|
||||
|
||||
if (this.hits == 0) {
|
||||
this.position += 1;
|
||||
if (this.position == 45)
|
||||
map.getSnowball(team == 0 ? 1 : 0).message(1);
|
||||
else if (this.position == 290)
|
||||
map.getSnowball(team == 0 ? 1 : 0).message(2);
|
||||
else if (this.position == 560)
|
||||
map.getSnowball(team == 0 ? 1 : 0).message(3);
|
||||
|
||||
this.hits = 3;
|
||||
map.broadcastMessage(MaplePacketCreator.rollSnowBall(false, 0, map.getSnowball(0), map.getSnowball(1)));
|
||||
map.broadcastMessage(MaplePacketCreator.rollSnowBall(false, 1, map.getSnowball(0), map.getSnowball(1)));
|
||||
}
|
||||
map.broadcastMessage(MaplePacketCreator.hitSnowBall(what, damage));
|
||||
}
|
||||
|
||||
public void message(int message) {
|
||||
for (MapleCharacter chr : characters) {
|
||||
if (chr != null)
|
||||
chr.announce(MaplePacketCreator.snowballMessage(team, message));
|
||||
}
|
||||
}
|
||||
|
||||
public void warpOut() {
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (winner == true)
|
||||
map.warpOutByTeam(team, 109050000);
|
||||
else
|
||||
map.warpOutByTeam(team, 109050001);
|
||||
|
||||
map.setSnowball(team, null);
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
}
|
||||
420
src/main/java/server/expeditions/MapleExpedition.java
Normal file
420
src/main/java/server/expeditions/MapleExpedition.java
Normal file
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
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.expeditions;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
import net.server.PlayerStorage;
|
||||
import net.server.Server;
|
||||
import server.TimerManager;
|
||||
import server.life.MapleMonster;
|
||||
import server.maps.MapleMap;
|
||||
import tools.LogHelper;
|
||||
import tools.MaplePacketCreator;
|
||||
import client.MapleCharacter;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import net.server.channel.Channel;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX)
|
||||
*/
|
||||
public class MapleExpedition {
|
||||
|
||||
private static final int [] EXPEDITION_BOSSES = {
|
||||
8800000,// - Zakum's first body
|
||||
8800001,// - Zakum's second body
|
||||
8800002,// - Zakum's third body
|
||||
8800003,// - Zakum's Arm 1
|
||||
8800004,// - Zakum's Arm 2
|
||||
8800005,// - Zakum's Arm 3
|
||||
8800006,// - Zakum's Arm 4
|
||||
8800007,// - Zakum's Arm 5
|
||||
8800008,// - Zakum's Arm 6
|
||||
8800009,// - Zakum's Arm 7
|
||||
8800010,// - Zakum's Arm 8
|
||||
8810000,// - Horntail's Left Head
|
||||
8810001,// - Horntail's Right Head
|
||||
8810002,// - Horntail's Head A
|
||||
8810003,// - Horntail's Head B
|
||||
8810004,// - Horntail's Head C
|
||||
8810005,// - Horntail's Left Hand
|
||||
8810006,// - Horntail's Right Hand
|
||||
8810007,// - Horntail's Wings
|
||||
8810008,// - Horntail's Legs
|
||||
8810009,// - Horntail's Tails
|
||||
9420546,// - Scarlion Boss
|
||||
9420547,// - Scarlion Boss
|
||||
9420548,// - Angry Scarlion Boss
|
||||
9420549,// - Furious Scarlion Boss
|
||||
9420541,// - Targa
|
||||
9420542,// - Targa
|
||||
9420543,// - Angry Targa
|
||||
9420544,// - Furious Targa
|
||||
};
|
||||
|
||||
private MapleCharacter leader;
|
||||
private MapleExpeditionType type;
|
||||
private boolean registering;
|
||||
private MapleMap startMap;
|
||||
private List<String> bossLogs;
|
||||
private ScheduledFuture<?> schedule;
|
||||
private Map<Integer, String> members = new ConcurrentHashMap<>();
|
||||
private List<Integer> banned = new CopyOnWriteArrayList<>();
|
||||
private long startTime;
|
||||
private Properties props = new Properties();
|
||||
private boolean silent;
|
||||
private int minSize, maxSize;
|
||||
private MonitoredReentrantLock pL = MonitoredReentrantLockFactory.createLock(MonitoredLockType.EIM_PARTY, true);
|
||||
|
||||
public MapleExpedition(MapleCharacter player, MapleExpeditionType met, boolean sil, int minPlayers, int maxPlayers) {
|
||||
leader = player;
|
||||
members.put(player.getId(), player.getName());
|
||||
startMap = player.getMap();
|
||||
type = met;
|
||||
silent = sil;
|
||||
minSize = (minPlayers != 0) ? minPlayers : type.getMinSize();
|
||||
maxSize = (maxPlayers != 0) ? maxPlayers : type.getMaxSize();
|
||||
bossLogs = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
public int getMinSize() {
|
||||
return minSize;
|
||||
}
|
||||
|
||||
public int getMaxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
public void beginRegistration() {
|
||||
registering = true;
|
||||
leader.announce(MaplePacketCreator.getClock(type.getRegistrationTime() * 60));
|
||||
if (!silent) {
|
||||
startMap.broadcastMessage(leader, MaplePacketCreator.serverNotice(6, "[Expedition] " + leader.getName() + " has been declared the expedition captain. Please register for the expedition."), false);
|
||||
leader.announce(MaplePacketCreator.serverNotice(6, "[Expedition] You have become the expedition captain. Gather enough people for your team then talk to the NPC to start."));
|
||||
}
|
||||
scheduleRegistrationEnd();
|
||||
}
|
||||
|
||||
private void scheduleRegistrationEnd() {
|
||||
final MapleExpedition exped = this;
|
||||
startTime = System.currentTimeMillis() + type.getRegistrationTime() * 60 * 1000;
|
||||
|
||||
schedule = TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (registering){
|
||||
exped.removeChannelExpedition(startMap.getChannelServer());
|
||||
if (!silent) startMap.broadcastMessage(MaplePacketCreator.serverNotice(6, "[Expedition] The time limit has been reached. Expedition has been disbanded."));
|
||||
|
||||
dispose(false);
|
||||
}
|
||||
}
|
||||
}, type.getRegistrationTime() * 60 * 1000);
|
||||
}
|
||||
|
||||
public void dispose(boolean log){
|
||||
broadcastExped(MaplePacketCreator.removeClock());
|
||||
|
||||
if (schedule != null){
|
||||
schedule.cancel(false);
|
||||
}
|
||||
if (log && !registering){
|
||||
LogHelper.logExpedition(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void finishRegistration() {
|
||||
registering = false;
|
||||
}
|
||||
|
||||
public void start(){
|
||||
finishRegistration();
|
||||
registerExpeditionAttempt();
|
||||
broadcastExped(MaplePacketCreator.removeClock());
|
||||
if (!silent) broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] The expedition has started! Good luck, brave heroes!"));
|
||||
startTime = System.currentTimeMillis();
|
||||
Server.getInstance().broadcastGMMessage(startMap.getWorld(), MaplePacketCreator.serverNotice(6, "[Expedition] " + type.toString() + " Expedition started with leader: " + leader.getName()));
|
||||
}
|
||||
|
||||
public String addMember(MapleCharacter player) {
|
||||
if (!registering){
|
||||
return "Sorry, this expedition is already underway. Registration is closed!";
|
||||
}
|
||||
if (banned.contains(player.getId())){
|
||||
return "Sorry, you've been banned from this expedition by #b" + leader.getName() + "#k.";
|
||||
}
|
||||
if (members.size() >= this.getMaxSize()){ //Would be a miracle if anybody ever saw this
|
||||
return "Sorry, this expedition is full!";
|
||||
}
|
||||
|
||||
int channel = this.getRecruitingMap().getChannelServer().getId();
|
||||
if (!MapleExpeditionBossLog.attemptBoss(player.getId(), channel, this, false)) { // thanks Conrad, Cato for noticing some expeditions have entry limit
|
||||
return "Sorry, you've already reached the quota of attempts for this expedition! Try again another day...";
|
||||
}
|
||||
|
||||
members.put(player.getId(), player.getName());
|
||||
player.announce(MaplePacketCreator.getClock((int)(startTime - System.currentTimeMillis()) / 1000));
|
||||
if (!silent) broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + player.getName() + " has joined the expedition!"));
|
||||
return "You have registered for the expedition successfully!";
|
||||
}
|
||||
|
||||
public int addMemberInt(MapleCharacter player) {
|
||||
if (!registering) {
|
||||
return 1; //"Sorry, this expedition is already underway. Registration is closed!";
|
||||
}
|
||||
if (banned.contains(player.getId())) {
|
||||
return 2; //"Sorry, you've been banned from this expedition by #b" + leader.getName() + "#k.";
|
||||
}
|
||||
if (members.size() >= this.getMaxSize()) { //Would be a miracle if anybody ever saw this
|
||||
return 3; //"Sorry, this expedition is full!";
|
||||
}
|
||||
|
||||
members.put(player.getId(), player.getName());
|
||||
player.announce(MaplePacketCreator.getClock((int) (startTime - System.currentTimeMillis()) / 1000));
|
||||
if (!silent) broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + player.getName() + " has joined the expedition!"));
|
||||
return 0; //"You have registered for the expedition successfully!";
|
||||
}
|
||||
|
||||
private void registerExpeditionAttempt(){
|
||||
int channel = this.getRecruitingMap().getChannelServer().getId();
|
||||
|
||||
for (MapleCharacter chr : getActiveMembers()){
|
||||
MapleExpeditionBossLog.attemptBoss(chr.getId(), channel, this, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastExped(byte[] packet){
|
||||
for (MapleCharacter chr : getActiveMembers()){
|
||||
chr.announce(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeMember(MapleCharacter chr) {
|
||||
if(members.remove(chr.getId()) != null) {
|
||||
chr.announce(MaplePacketCreator.removeClock());
|
||||
if (!silent) {
|
||||
broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + chr.getName() + " has left the expedition."));
|
||||
chr.dropMessage(6, "[Expedition] You have left this expedition.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ban(Entry<Integer, String> chr) {
|
||||
int cid = chr.getKey();
|
||||
if (!banned.contains(cid)) {
|
||||
banned.add(cid);
|
||||
members.remove(cid);
|
||||
|
||||
if (!silent) broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + chr.getValue() + " has been banned from the expedition."));
|
||||
|
||||
MapleCharacter player = startMap.getWorldServer().getPlayerStorage().getCharacterById(cid);
|
||||
if (player != null && player.isLoggedinWorld()) {
|
||||
player.announce(MaplePacketCreator.removeClock());
|
||||
if (!silent) player.dropMessage(6, "[Expedition] You have been banned from this expedition.");
|
||||
if (MapleExpeditionType.ARIANT.equals(type) || MapleExpeditionType.ARIANT1.equals(type) || MapleExpeditionType.ARIANT2.equals(type)) {
|
||||
player.changeMap(980010000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void monsterKilled(MapleCharacter chr, MapleMonster mob) {
|
||||
for (int i = 0; i < EXPEDITION_BOSSES.length; i++) {
|
||||
if (mob.getId() == EXPEDITION_BOSSES[i]) { //If the monster killed was a boss
|
||||
String timeStamp = new SimpleDateFormat("HH:mm:ss").format(new Date());
|
||||
bossLogs.add(">" + mob.getName() + " was killed after " + LogHelper.getTimeString(startTime) + " - " + timeStamp + "\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setProperty(String key, String value) {
|
||||
pL.lock();
|
||||
try {
|
||||
props.setProperty(key, value);
|
||||
} finally {
|
||||
pL.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public String getProperty(String key) {
|
||||
pL.lock();
|
||||
try {
|
||||
return props.getProperty(key);
|
||||
} finally {
|
||||
pL.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public MapleExpeditionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public List<MapleCharacter> getActiveMembers() { // thanks MedicOP for figuring out an issue with broadcasting packets to offline members
|
||||
PlayerStorage ps = startMap.getWorldServer().getPlayerStorage();
|
||||
|
||||
List<MapleCharacter> activeMembers = new LinkedList<>();
|
||||
for (Integer chrid : getMembers().keySet()){
|
||||
MapleCharacter chr = ps.getCharacterById(chrid);
|
||||
if (chr != null && chr.isLoggedinWorld()) {
|
||||
activeMembers.add(chr);
|
||||
}
|
||||
}
|
||||
|
||||
return activeMembers;
|
||||
}
|
||||
|
||||
public Map<Integer, String> getMembers() {
|
||||
return new HashMap<>(members);
|
||||
}
|
||||
|
||||
public List<Entry<Integer, String>> getMemberList() {
|
||||
List<Entry<Integer, String>> memberList = new LinkedList<>();
|
||||
Entry<Integer, String> leaderEntry = null;
|
||||
|
||||
for (Entry<Integer, String> e : getMembers().entrySet()) {
|
||||
if (!isLeader(e.getKey())) {
|
||||
memberList.add(e);
|
||||
} else {
|
||||
leaderEntry = e;
|
||||
}
|
||||
}
|
||||
|
||||
if (leaderEntry != null) {
|
||||
memberList.add(0, leaderEntry);
|
||||
}
|
||||
|
||||
return memberList;
|
||||
}
|
||||
|
||||
public final boolean isExpeditionTeamTogether() {
|
||||
List<MapleCharacter> chars = getActiveMembers();
|
||||
if(chars.size() <= 1) return true;
|
||||
|
||||
Iterator<MapleCharacter> iterator = chars.iterator();
|
||||
MapleCharacter mc = iterator.next();
|
||||
int mapId = mc.getMapId();
|
||||
|
||||
for (; iterator.hasNext();) {
|
||||
mc = iterator.next();
|
||||
if(mc.getMapId() != mapId) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public final void warpExpeditionTeam(int warpFrom, int warpTo) {
|
||||
List<MapleCharacter> players = getActiveMembers();
|
||||
|
||||
for (MapleCharacter chr : players) {
|
||||
if(chr.getMapId() == warpFrom)
|
||||
chr.changeMap(warpTo);
|
||||
}
|
||||
}
|
||||
|
||||
public final void warpExpeditionTeam(int warpTo) {
|
||||
List<MapleCharacter> players = getActiveMembers();
|
||||
|
||||
for (MapleCharacter chr : players) {
|
||||
chr.changeMap(warpTo);
|
||||
}
|
||||
}
|
||||
|
||||
public final void warpExpeditionTeamToMapSpawnPoint(int warpFrom, int warpTo, int toSp) {
|
||||
List<MapleCharacter> players = getActiveMembers();
|
||||
|
||||
for (MapleCharacter chr : players) {
|
||||
if(chr.getMapId() == warpFrom)
|
||||
chr.changeMap(warpTo, toSp);
|
||||
}
|
||||
}
|
||||
|
||||
public final void warpExpeditionTeamToMapSpawnPoint(int warpTo, int toSp) {
|
||||
List<MapleCharacter> players = getActiveMembers();
|
||||
|
||||
for (MapleCharacter chr : players) {
|
||||
chr.changeMap(warpTo, toSp);
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean addChannelExpedition(Channel ch) {
|
||||
return ch.addExpedition(this);
|
||||
}
|
||||
|
||||
public final void removeChannelExpedition(Channel ch) {
|
||||
ch.removeExpedition(this);
|
||||
}
|
||||
|
||||
public MapleCharacter getLeader(){
|
||||
return leader;
|
||||
}
|
||||
|
||||
public MapleMap getRecruitingMap() {
|
||||
return startMap;
|
||||
}
|
||||
|
||||
public boolean contains(MapleCharacter player) {
|
||||
return members.containsKey(player.getId()) || isLeader(player);
|
||||
}
|
||||
|
||||
public boolean isLeader(MapleCharacter player) {
|
||||
return isLeader(player.getId());
|
||||
}
|
||||
|
||||
public boolean isLeader(int playerid) {
|
||||
return leader.getId() == playerid;
|
||||
}
|
||||
|
||||
public boolean isRegistering(){
|
||||
return registering;
|
||||
}
|
||||
|
||||
public boolean isInProgress(){
|
||||
return !registering;
|
||||
}
|
||||
|
||||
public long getStartTime(){
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public List<String> getBossLogs(){
|
||||
return bossLogs;
|
||||
}
|
||||
}
|
||||
208
src/main/java/server/expeditions/MapleExpeditionBossLog.java
Normal file
208
src/main/java/server/expeditions/MapleExpeditionBossLog.java
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package server.expeditions;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Calendar;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import config.YamlConfig;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Conrad
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleExpeditionBossLog {
|
||||
|
||||
public enum BossLogEntry {
|
||||
ZAKUM(2, 1, false),
|
||||
HORNTAIL(2, 1, false),
|
||||
PINKBEAN(1, 1, false),
|
||||
SCARGA(1, 1, false),
|
||||
PAPULATUS(2, 1, false);
|
||||
|
||||
private int entries;
|
||||
private int timeLength;
|
||||
private int minChannel, maxChannel;
|
||||
private boolean week;
|
||||
|
||||
private BossLogEntry(int entries, int timeLength, boolean week) {
|
||||
this(entries, 0, Integer.MAX_VALUE, timeLength, week);
|
||||
}
|
||||
|
||||
private BossLogEntry(int entries, int minChannel, int maxChannel, int timeLength, boolean week) {
|
||||
this.entries = entries;
|
||||
this.minChannel = minChannel;
|
||||
this.maxChannel = maxChannel;
|
||||
this.timeLength = timeLength;
|
||||
this.week = week;
|
||||
}
|
||||
|
||||
private static List<Pair<Timestamp, BossLogEntry>> getBossLogResetTimestamps(Calendar timeNow, boolean week) {
|
||||
List<Pair<Timestamp, BossLogEntry>> resetTimestamps = new LinkedList<>();
|
||||
|
||||
Timestamp ts = new Timestamp(timeNow.getTime().getTime()); // reset all table entries actually, thanks Conrad
|
||||
for (BossLogEntry b : BossLogEntry.values()) {
|
||||
if (b.week == week) {
|
||||
resetTimestamps.add(new Pair<>(ts, b));
|
||||
}
|
||||
}
|
||||
|
||||
return resetTimestamps;
|
||||
}
|
||||
|
||||
private static BossLogEntry getBossEntryByName(String name) {
|
||||
for (BossLogEntry b : BossLogEntry.values()) {
|
||||
if (name.contentEquals(b.name())) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void resetBossLogTable() {
|
||||
/*
|
||||
Boss logs resets 12am, weekly thursday 12AM - thanks Smitty Werbenjagermanjensen (superadlez) - https://www.reddit.com/r/Maplestory/comments/61tiup/about_reset_time/
|
||||
*/
|
||||
|
||||
Calendar thursday = Calendar.getInstance();
|
||||
thursday.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
|
||||
thursday.set(Calendar.HOUR, 0);
|
||||
thursday.set(Calendar.MINUTE, 0);
|
||||
thursday.set(Calendar.SECOND, 0);
|
||||
|
||||
Calendar now = Calendar.getInstance();
|
||||
|
||||
long weekLength = 7 * 24 * 60 * 60 * 1000;
|
||||
long halfDayLength = 12 * 60 * 60 * 1000;
|
||||
|
||||
long deltaTime = now.getTime().getTime() - thursday.getTime().getTime(); // 2x time: get Date into millis
|
||||
deltaTime += halfDayLength;
|
||||
deltaTime %= weekLength;
|
||||
deltaTime -= halfDayLength;
|
||||
|
||||
if (deltaTime < halfDayLength) {
|
||||
MapleExpeditionBossLog.resetBossLogTable(true, thursday);
|
||||
}
|
||||
|
||||
now.set(Calendar.HOUR, 0);
|
||||
now.set(Calendar.MINUTE, 0);
|
||||
now.set(Calendar.SECOND, 0);
|
||||
|
||||
MapleExpeditionBossLog.resetBossLogTable(false, now);
|
||||
}
|
||||
|
||||
private static void resetBossLogTable(boolean week, Calendar c) {
|
||||
List<Pair<Timestamp, BossLogEntry>> resetTimestamps = BossLogEntry.getBossLogResetTimestamps(c, week);
|
||||
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
for (Pair<Timestamp, BossLogEntry> p : resetTimestamps) {
|
||||
PreparedStatement ps = con.prepareStatement("DELETE FROM " + getBossLogTable(week) + " WHERE attempttime <= ? AND bosstype LIKE ?");
|
||||
ps.setTimestamp(1, p.getLeft());
|
||||
ps.setString(2, p.getRight().name());
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static String getBossLogTable(boolean week) {
|
||||
return week ? "bosslog_weekly" : "bosslog_daily";
|
||||
}
|
||||
|
||||
private static int countPlayerEntries(int cid, BossLogEntry boss) {
|
||||
int ret_count = 0;
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps;
|
||||
ps = con.prepareStatement("SELECT COUNT(*) FROM " + getBossLogTable(boss.week) + " WHERE characterid = ? AND bosstype LIKE ?");
|
||||
ps.setInt(1, cid);
|
||||
ps.setString(2, boss.name());
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
ret_count = rs.getInt(1);
|
||||
} else {
|
||||
ret_count = -1;
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
return ret_count;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void insertPlayerEntry(int cid, BossLogEntry boss) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("INSERT INTO " + getBossLogTable(boss.week) + " (characterid, bosstype) VALUES (?,?)");
|
||||
ps.setInt(1, cid);
|
||||
ps.setString(2, boss.name());
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean attemptBoss(int cid, int channel, MapleExpedition exped, boolean log) {
|
||||
if (!YamlConfig.config.server.USE_ENABLE_DAILY_EXPEDITIONS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BossLogEntry boss = BossLogEntry.getBossEntryByName(exped.getType().name());
|
||||
if (boss == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (channel < boss.minChannel || channel > boss.maxChannel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (countPlayerEntries(cid, boss) >= boss.entries) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (log) {
|
||||
insertPlayerEntry(cid, boss);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
81
src/main/java/server/expeditions/MapleExpeditionType.java
Normal file
81
src/main/java/server/expeditions/MapleExpeditionType.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
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.expeditions;
|
||||
|
||||
import config.YamlConfig;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX)
|
||||
*/
|
||||
|
||||
public enum MapleExpeditionType {
|
||||
|
||||
BALROG_EASY(3, 30, 50, 255, 5),
|
||||
BALROG_NORMAL(6, 30, 50, 255, 5),
|
||||
SCARGA(6, 30, 100, 255, 5),
|
||||
SHOWA(3, 30, 100, 255, 5),
|
||||
ZAKUM(6, 30, 50, 255, 5),
|
||||
HORNTAIL(6, 30, 100, 255, 5),
|
||||
CHAOS_ZAKUM(6, 30, 120, 255, 5),
|
||||
CHAOS_HORNTAIL(6, 30, 120, 255, 5),
|
||||
ARIANT(2, 7, 20, 30, 5),
|
||||
ARIANT1(2, 7, 20, 30, 5),
|
||||
ARIANT2(2, 7, 20, 30, 5),
|
||||
PINKBEAN(6, 30, 120, 255, 5),
|
||||
CWKPQ(6, 30, 90, 255, 5); // CWKPQ min-level 90, found thanks to Cato
|
||||
|
||||
private int minSize;
|
||||
private int maxSize;
|
||||
private int minLevel;
|
||||
private int maxLevel;
|
||||
private int registrationTime;
|
||||
|
||||
private MapleExpeditionType(int minSize, int maxSize, int minLevel, int maxLevel, int minutes) {
|
||||
this.minSize = minSize;
|
||||
this.maxSize = maxSize;
|
||||
this.minLevel = minLevel;
|
||||
this.maxLevel = maxLevel;
|
||||
this.registrationTime = minutes;
|
||||
}
|
||||
|
||||
public int getMinSize() {
|
||||
return !YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS ? minSize : 1;
|
||||
}
|
||||
|
||||
public int getMaxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
public int getMinLevel() {
|
||||
return minLevel;
|
||||
}
|
||||
|
||||
public int getMaxLevel() {
|
||||
return maxLevel;
|
||||
}
|
||||
|
||||
public int getRegistrationTime(){
|
||||
return registrationTime;
|
||||
}
|
||||
}
|
||||
65
src/main/java/server/gachapon/ElNath.java
Normal file
65
src/main/java/server/gachapon/ElNath.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan - parsed MapleSEA loots
|
||||
*
|
||||
* MapleSEA-like loots thanks to AyumiLove - src: https://ayumilovemaple.wordpress.com/maplestory-gachapon-guide/
|
||||
*/
|
||||
|
||||
public class ElNath extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int [] {
|
||||
|
||||
/* Scroll */
|
||||
2041012, 2048003, 2043800, 2043301, 2040301, 2043101, 2043201, 2043001, 2044301, 2043801, 2044201, 2043701, 2044502,
|
||||
2041011, 2041014, 2044602, 2043302, 2043202, 2043002, 2048005, 2044402, 2044302, 2043802, 2044102, 2044202, 2043702,
|
||||
2044812,
|
||||
|
||||
/* Useable drop */
|
||||
2000004, 2000005,
|
||||
|
||||
/* Common equipment */
|
||||
1402010, 1032003, 1442013, 1432009, 1302022, 1302029, 1322021, 1302026, 1442017, 1322023, 1102011, 1032008, 1322026,
|
||||
1442016, 1312000, 1032007, 1322025, 1322027, 1032020, 1442015, 1432017, 1302027, 1302049, 1372006, 1032022, 1032021,
|
||||
1372004, 1332020, 1322007, 1032006, 1302028, 1322003, 1302007, 1092030, 1302021, 1322024, 1322012, 1032005, 1322022,
|
||||
1032013, 1302025, 1302013, 1032017, 1032002, 1032001, 1302017, 1432018, 1442012, 1302000, 1032000, 1102013, 1442022,
|
||||
1372005, 1442021, 1032009, 1302016,
|
||||
|
||||
/* Warrior equipment */
|
||||
1442003, 1312007, 1402008, 1312008, 1412008, 1442009, 1302004, 1312006, 1442016, 1402012, 1302003, 1312005, 1432002,
|
||||
1432001, 1302008, 1040030, 1402015, 1322015, 1432006, 1322002, 1302010, 1322017, 1402003, 1402006, 1322000, 1422001,
|
||||
1442001, 1422004, 1412004, 1322009, 1322011, 1442000, 1412005, 1402002, 1432004, 1442010, 1422008, 1442007, 1422009,
|
||||
1322019, 1412003, 1412007, 1302009, 1412000, 1322014, 1402001, 1402007, 1432005,
|
||||
|
||||
/* Magician equipment */
|
||||
1382001, 1372007, 1382010, 1382007, 1372000, 1372003, 1382011, 1382006, 1382000,
|
||||
|
||||
/* Bowman equipment */
|
||||
1452004, 1452000, 1452010, 1452015, 1452014, 1462012, 1462010, 1452017, 1462000, 1452008, 1452006, 1462006, 1452007,
|
||||
1452002, 1402001,
|
||||
|
||||
/* Thief equipment */
|
||||
1472006, 1472010, 1332022, 1332011, 1472015, 1472016, 1472023, 1472028, 1472022, 1472011, 1472026, 1332024, 1332009,
|
||||
1472017, 1472013, 1472029, 1472021, 1332015, 1332031, 1332023, 1332004, 1472000, 1332019, 1472027, 1332018, 1472007,
|
||||
1332012, 1332016, 1472024, 1332017, 1332003, 1472012, 1472014, 1472005, 1472018, 1472001,
|
||||
|
||||
/* Pirate equipment */
|
||||
1072294, 1492009
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int [] {2022439, 2040804, 2040805, 2340000};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int [] {2043803, 1102085};
|
||||
}
|
||||
|
||||
}
|
||||
66
src/main/java/server/gachapon/Ellinia.java
Normal file
66
src/main/java/server/gachapon/Ellinia.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX) - gachapon source classes stub & pirate equipment
|
||||
* @author Ronan - parsed MapleSEA loots
|
||||
*
|
||||
* MapleSEA-like loots thanks to AyumiLove - src: https://ayumilovemaple.wordpress.com/maplestory-gachapon-guide/
|
||||
*/
|
||||
|
||||
public class Ellinia extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int[] {
|
||||
|
||||
/* Scroll */
|
||||
2043302, 2040002, 2043102, 2043002, 2044402, 2044302, 2043802, 2044002, 2041017, 2044902,
|
||||
|
||||
/* Useable drop */
|
||||
2000004, 2000005, 2022025, 2022026,
|
||||
|
||||
/* Common equipment */
|
||||
1402010, 1442013, 1432009, 1002060, 1002063, 1322023, 1002042, 1050018, 1082147, 1002026, 1002392, 1062024, 1442016, 1322025,
|
||||
1322027, 1302027, 1372006, 1302019, 1092022, 1302021, 1041004, 1002395, 1322024, 1082148, 1002012, 1322012, 1032028, 1102012,
|
||||
1322022, 1051017, 1302013, 1082146, 1442014, 1302017, 1102013, 1102003, 1002041, 1002097, 1302016, 1082145,
|
||||
|
||||
/* Warrior equipment */
|
||||
1412006, 1040029, 1040086, 1050005, 1060028, 1002059, 1060008, 1061088, 1402012, 1302003, 1432002, 1312011, 1302008, 1040030,
|
||||
1002004, 1402015, 1322028, 1322015, 1432006, 1442006, 1322000, 1002085, 1002056, 1092013, 1002058, 1002050, 1060011, 1322009,
|
||||
1322011, 1442000, 1051011, 1061016, 1060018, 1041024, 1061020, 1302005, 1402002, 1002030, 1092004, 1041023, 1422008, 1060009,
|
||||
1051000, 1002021, 1442005, 1412003, 1412007, 1422007, 1302009, 1402000, 1402001, 1402007, 1432005,
|
||||
|
||||
/* Magician equipment */
|
||||
1382001, 1002037, 1060014, 1040018, 1061027, 1050002, 1002152, 1051027, 1050035, 1050056, 1051047, 1051030, 1002274, 1050074,
|
||||
1002218, 1002254, 1082088, 1382007, 1002013, 1082087, 1372008, 1382008, 1372002, 1372003, 1382011, 1382004, 1050047, 1040019,
|
||||
1041041, 1061034, 1041051, 1051045, 1051024, 1082081, 1041030, 1040018, 1002073, 1382003, 1082086, 1382014, 1050055, 1050025,
|
||||
1002155, 1060015,
|
||||
|
||||
/* Bowman equipment */
|
||||
1452004, 1462003, 1060070, 1002118, 1061058, 1040003, 1002160, 1002121, 1040068, 1061063, 1040080, 1462004, 1041008, 1061006,
|
||||
1061009, 1040022, 1002168, 1040067, 1060056, 1041054, 1041067, 1060063, 1002213, 1002119, 1462005, 1452001, 1462000, 1040025,
|
||||
1002166, 1002161, 1040069, 1051039, 1452006, 1462006, 1452007, 1402001, 1041062,
|
||||
|
||||
/* Thief equipment */
|
||||
1472010, 1472006, 1332011, 1472031, 1041048, 1472019, 1041095, 1040095, 1002128, 1061077, 1060025, 1041040, 1061033, 1472028,
|
||||
1472022, 1472011, 1040096, 1062002, 1002129, 1472026, 1332009, 1060043, 1002249, 1472021, 1040084, 1332015, 1002173, 1002148,
|
||||
1332004, 1332018, 1472009, 1061069, 1002176, 1041044, 1061037, 1060032, 1472020, 1040060, 1472018, 1332013, 1332002, 1402001,
|
||||
|
||||
/* Pirate equipment */
|
||||
1002625, 1002616, 1482005, 1052098, 1482003,
|
||||
1482001, 1492004, 1002622, 1492005, 1082195
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int [] {1082149, 1002391, 1002419};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int [] {};
|
||||
}
|
||||
|
||||
}
|
||||
57
src/main/java/server/gachapon/GachaponItems.java
Normal file
57
src/main/java/server/gachapon/GachaponItems.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX)
|
||||
*/
|
||||
|
||||
public abstract class GachaponItems {
|
||||
|
||||
public abstract int [] getCommonItems();
|
||||
public abstract int [] getUncommonItems();
|
||||
public abstract int [] getRareItems();
|
||||
|
||||
private final int [] commonItems;
|
||||
private final int [] uncommonItems;
|
||||
private final int [] rareItems;
|
||||
|
||||
public GachaponItems() {
|
||||
this.commonItems = getCommonItems();
|
||||
this.uncommonItems = getUncommonItems();
|
||||
this.rareItems = getRareItems();
|
||||
}
|
||||
|
||||
public final int[] getItems(int tier) {
|
||||
if (tier == 0) {
|
||||
return commonItems;
|
||||
} else if (tier == 1) {
|
||||
return uncommonItems;
|
||||
} else if (tier == 2) {
|
||||
return rareItems;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
47
src/main/java/server/gachapon/Global.java
Normal file
47
src/main/java/server/gachapon/Global.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX)
|
||||
* @author Ronan - added ores and reworked global loots
|
||||
*/
|
||||
|
||||
public class Global extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int[] {
|
||||
/* Potions */
|
||||
2000004, 2000005, 2001002, 2001001, 2020012, 2020013, 2020014, 2020015,
|
||||
|
||||
/* Ores */
|
||||
4004000, 4004001, 4004002, 4004003, 4004004,
|
||||
4006000, 4006001,
|
||||
4010000, 4010001, 4010002, 4010003, 4010004, 4010005, 4010006, 4010007,
|
||||
4020000, 4020001, 4020002, 4020003, 4020004, 4020005, 4020006, 4020007, 4020008
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int[] {
|
||||
/* Potions */
|
||||
2022179, 2022273, 2022282, 2022283, 2022285, 2022245, 2022182,
|
||||
|
||||
/* Scrolls */
|
||||
2049003
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int[] {
|
||||
/* Scrolls */
|
||||
2049100, 2340000,
|
||||
|
||||
/* Chairs */
|
||||
3010063, 3010064
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
61
src/main/java/server/gachapon/Henesys.java
Normal file
61
src/main/java/server/gachapon/Henesys.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX) - gachapon source classes stub
|
||||
* @author Ronan - parsed MapleSEA loots
|
||||
*
|
||||
* MapleSEA-like loots thanks to AyumiLove - src: https://ayumilovemaple.wordpress.com/maplestory-gachapon-guide/
|
||||
*/
|
||||
|
||||
public class Henesys extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int [] {
|
||||
|
||||
/* Scroll */
|
||||
2040001, 2041002, 2040702, 2043802, 2040402, 2043702, 2044813,
|
||||
|
||||
/* Useable Drops */
|
||||
2000004, 2000005, 2020012, 2030007,
|
||||
|
||||
/* Common equipment */
|
||||
1432009, 1302022, 1322021, 1302026, 1442017, 1082147, 1102043, 1322026, 1442016, 1402012, 1322025, 1322027, 1302027,
|
||||
1312012, 1062000, 1332020, 1302028, 1372002, 1002033, 1092022, 1302021, 1322009, 1322024, 1082148, 1002012, 1322012,
|
||||
1322022, 1002020, 1302013, 1082146, 1442014, 1002096, 1302017, 1442012,
|
||||
|
||||
/* Warrior equipment */
|
||||
1092011, 1092014, 1302003, 1432001, 1312011, 1002088, 1041020, 1322015, 1442004, 1422008, 1302056, 1432000, 1442005,
|
||||
|
||||
/* Magician equipment */
|
||||
1382001, 1041053, 1041029, 1050053, 1051032, 1050073, 1061036, 1002253, 1002034, 1051025, 1050067, 1051052, 1002072,
|
||||
1002144, 1051054, 1050069, 1372007, 1050056, 1050074, 1002254, 1002274, 1002218, 1051055, 1382010, 1002246, 1050039,
|
||||
1382007, 1372000, 1002013, 1050072, 1002036, 1002244, 1372008, 1382008, 1382011, 1092021, 1051034, 1050047, 1040019,
|
||||
1041031, 1051033, 1002153, 1002252, 1051024, 1051053, 1050068, 1382003, 1382006, 1050055, 1051031, 1050025, 1002155,
|
||||
1002245, 1372001,
|
||||
|
||||
/* Bowman equipment */
|
||||
1452004, 1452023, 1060057, 1432001, 1040071, 1002137, 1462009, 1452017, 1040025, 1041027, 1452005, 1452007, 1061057,
|
||||
|
||||
/* Thief equipment */
|
||||
1472006, 1472019, 1060084, 1472028, 1472004, 1002179, 1082074, 1472029, 1040100, 1332015, 1432001, 1040097, 1060071,
|
||||
1472007, 1472002, 1051009, 1041044, 1041003, 1332016, 1472020, 1332003,
|
||||
|
||||
/* Pirate equipment */
|
||||
1002622, 1082204, 1082213, 1082198, 1002631, 1052122, 1482012, 1052131, 1482007, 1482004, 1072318, 1492007
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int[] {2040805, 1102041, 1102042, 1442018};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int[] {};
|
||||
}
|
||||
|
||||
}
|
||||
59
src/main/java/server/gachapon/KerningCity.java
Normal file
59
src/main/java/server/gachapon/KerningCity.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX) - gachapon source classes stub & pirate equipment
|
||||
* @author Ronan - parsed MapleSEA loots
|
||||
*
|
||||
* MapleSEA-like loots thanks to AyumiLove - src: https://ayumilovemaple.wordpress.com/maplestory-gachapon-guide/
|
||||
*/
|
||||
|
||||
public class KerningCity extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int [] {
|
||||
|
||||
/* Scroll */
|
||||
2041016, 2043302, 2040902, 2044804, 2044906,
|
||||
|
||||
/* Useable drop */
|
||||
2000004, 2000005, 2022025, 2022027,
|
||||
|
||||
/* Common equipment */
|
||||
1442013, 1432009, 1322021, 1050018, 1002392, 1002394, 1442004, 1372002, 1002418, 1002033, 1092008,
|
||||
1082148, 1062001, 1302017, 1032023, 1102013, 1102040, 1002041, 1002097,
|
||||
|
||||
/* Warrior equipment */
|
||||
1332026, 1051010, 1432001, 1422005, 1332019, 1302010, 1002056, 1060011, 1322011, 1432004, 1002028,
|
||||
1051000, 1442007, 1302002,
|
||||
|
||||
/* Magician equipment */
|
||||
1002037, 1002034, 1082020, 1050039, 1372000, 1002215, 1051034, 1040019, 1061034, 1382003, 1382006,
|
||||
1050025,
|
||||
|
||||
/* Bowman equipment */
|
||||
1002118, 1061081, 1452011, 1462012, 1452006, 1452007,
|
||||
|
||||
/* Thief equipment */
|
||||
1472010, 1472029, 1041048, 1041095, 1060031, 1061033, 1041049, 1472011, 1040096, 1472033, 1332026,
|
||||
1051006, 1082074, 1472025, 1061106, 1040084, 1332015, 1472000, 1332019, 1002183, 1002209, 1092020,
|
||||
1332029, 1092019, 1061099, 1060106, 1040032, 1040059, 1332003, 1040060, 1060046, 1472005, 1332027,
|
||||
|
||||
/* Pirate equipment */
|
||||
1082192, 1072288, 1492003, 1052113, 1052104,
|
||||
1492002, 1052095, 1492001, 1002613, 1492004
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int[] {2040805, 1082149, 1102041};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int[] {};
|
||||
}
|
||||
|
||||
}
|
||||
56
src/main/java/server/gachapon/Ludibrium.java
Normal file
56
src/main/java/server/gachapon/Ludibrium.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan - parsed MapleSEA loots
|
||||
*
|
||||
* MapleSEA-like loots thanks to AyumiLove - src: https://ayumilovemaple.wordpress.com/maplestory-gachapon-guide/
|
||||
*/
|
||||
|
||||
public class Ludibrium extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int [] {
|
||||
|
||||
/* Scroll */
|
||||
2048000, 2040601, 2041019, 2041007, 2041016, 2041022, 2041001, 2041010, 2041013, 2041004, 2044701, 2043301, 2040301, 2048004, 2048001, 2040901, 2040701, 2040704, 2040707, 2040602, 2041020, 2041008, 2041017, 2041023, 2041002, 2041011, 2041014, 2041005, 2044702, 2043302, 2040302, 2040002, 2044402, 2048005, 2048002, 2040702, 2040705, 2040708, 2044302, 2043802, 2040402, 2043702, 2044811,
|
||||
|
||||
/* Useable drop */
|
||||
2000004, 2000005, 4006000, 4006001,
|
||||
|
||||
/* Common equipment */
|
||||
1032003, 1432009, 1302022, 1302029, 1102014, 1102018, 1312014, 1302026, 1102015, 1032011, 1312013, 1032008, 1032019, 1032007, 1332030, 1032020, 1032004, 1302027, 1032022, 1312012, 1032021, 1032006, 1302028, 1322003, 1032016, 1032015, 1302024, 1092008, 1032018, 1302021, 1032014, 1332021, 1322012, 1032005, 1032013, 1102012, 1302025, 1302013, 1032002, 1032001, 1032012, 1302017, 1032010, 1402014, 1102017, 1102013, 1442021, 1032009,
|
||||
|
||||
/* Beginner equipment */
|
||||
1332021, 1422011,
|
||||
|
||||
/* Warrior equipment */
|
||||
1402017, 1422005, 1002023, 1332016, 1432005,
|
||||
|
||||
/* Magician equipment */
|
||||
1002037, 1002034, 1002064, 1002038, 1002013, 1002036, 1382011, 1002035, 1002065, 1382014, 1372001,
|
||||
|
||||
/* Bowman equipment */
|
||||
1452026, 1002162, 1002164, 1462018, 1002165, 1452014, 1002163, 1452012, 1002161, 1452009, 1462007,
|
||||
|
||||
/* Thief Equipment */
|
||||
1332022, 1002175, 1002172, 1002174, 1040096, 1472033, 1002173, 1332054, 1472054, 1002171, 1332016,
|
||||
|
||||
/* Pirate equipment */
|
||||
1002646
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int [] {2040805, 1002419, 1442018};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int [] {};
|
||||
}
|
||||
|
||||
}
|
||||
165
src/main/java/server/gachapon/MapleGachapon.java
Normal file
165
src/main/java/server/gachapon/MapleGachapon.java
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
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.gachapon;
|
||||
|
||||
import server.MapleItemInformationProvider;
|
||||
import tools.Randomizer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX)
|
||||
*/
|
||||
public class MapleGachapon {
|
||||
|
||||
private static final MapleGachapon instance = new MapleGachapon();
|
||||
|
||||
public static MapleGachapon getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public enum Gachapon {
|
||||
|
||||
GLOBAL(-1, -1, -1, -1, new Global()),
|
||||
HENESYS(9100100, 90, 8, 2, new Henesys()),
|
||||
ELLINIA(9100101, 90, 8, 2, new Ellinia()),
|
||||
PERION(9100102, 90, 8, 2, new Perion()),
|
||||
KERNING_CITY(9100103, 90, 8, 2, new KerningCity()),
|
||||
SLEEPYWOOD(9100104, 90, 8, 2, new Sleepywood()),
|
||||
MUSHROOM_SHRINE(9100105, 90, 8, 2, new MushroomShrine()),
|
||||
SHOWA_SPA_MALE(9100106, 90, 8, 2, new ShowaSpaMale()),
|
||||
SHOWA_SPA_FEMALE(9100107, 90, 8, 2, new ShowaSpaFemale()),
|
||||
LUDIBRIUM(9100108, 90, 8, 2, new Ludibrium()),
|
||||
NEW_LEAF_CITY(9100109, 90, 8, 2, new NewLeafCity()),
|
||||
EL_NATH(9100110, 90, 8, 2, new ElNath()),
|
||||
NAUTILUS_HARBOR(9100117, 90, 8, 2, new NautilusHarbor());
|
||||
|
||||
private static final Gachapon[] values = Gachapon.values();
|
||||
|
||||
private GachaponItems gachapon;
|
||||
private int npcId;
|
||||
private int common;
|
||||
private int uncommon;
|
||||
private int rare;
|
||||
|
||||
private Gachapon(int npcid, int c, int u, int r, GachaponItems g) {
|
||||
this.npcId = npcid;
|
||||
this.gachapon = g;
|
||||
this.common = c;
|
||||
this.uncommon = u;
|
||||
this.rare = r;
|
||||
}
|
||||
|
||||
private int getTier() {
|
||||
int chance = Randomizer.nextInt(common + uncommon + rare) + 1;
|
||||
if (chance > common + uncommon) {
|
||||
return 2; //Rare
|
||||
} else if (chance > common) {
|
||||
return 1; //Uncommon
|
||||
} else {
|
||||
return 0; //Common
|
||||
}
|
||||
}
|
||||
|
||||
public int [] getItems(int tier){
|
||||
return gachapon.getItems(tier);
|
||||
}
|
||||
|
||||
public int getItem(int tier) {
|
||||
int[] gacha = getItems(tier);
|
||||
int[] global = GLOBAL.getItems(tier);
|
||||
int chance = Randomizer.nextInt(gacha.length + global.length);
|
||||
return chance < gacha.length ? gacha[chance] : global[chance - gacha.length];
|
||||
}
|
||||
|
||||
public static Gachapon getByNpcId(int npcId) {
|
||||
for (Gachapon gacha : values) {
|
||||
if (npcId == gacha.npcId) {
|
||||
return gacha;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] getLootInfo() {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
|
||||
String[] strList = new String[values.length + 1];
|
||||
|
||||
String menuStr = "";
|
||||
int j = 0;
|
||||
for (Gachapon gacha : values) {
|
||||
menuStr += "#L" + j + "#" + gacha.name() + "#l\r\n";
|
||||
j++;
|
||||
|
||||
String str = "";
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int[] gachaItems = gacha.getItems(i);
|
||||
|
||||
if (gachaItems.length > 0) {
|
||||
str += (" #rTier " + i + "#k:\r\n");
|
||||
for (int itemid : gachaItems) {
|
||||
String itemName = ii.getName(itemid);
|
||||
if (itemName == null) {
|
||||
itemName = "MISSING NAME #" + itemid;
|
||||
}
|
||||
|
||||
str += (" " + itemName + "\r\n");
|
||||
}
|
||||
|
||||
str += "\r\n";
|
||||
}
|
||||
}
|
||||
str += "\r\n";
|
||||
|
||||
strList[j] = str;
|
||||
}
|
||||
strList[0] = menuStr;
|
||||
|
||||
return strList;
|
||||
}
|
||||
}
|
||||
|
||||
public MapleGachaponItem process(int npcId) {
|
||||
Gachapon gacha = Gachapon.getByNpcId(npcId);
|
||||
int tier = gacha.getTier();
|
||||
int item = gacha.getItem(tier);
|
||||
return new MapleGachaponItem(tier, item);
|
||||
}
|
||||
|
||||
public class MapleGachaponItem {
|
||||
private int id;
|
||||
private int tier;
|
||||
|
||||
public MapleGachaponItem(int t, int i) {
|
||||
id = i;
|
||||
tier = t;
|
||||
}
|
||||
|
||||
public int getTier() {
|
||||
return tier;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/main/java/server/gachapon/MushroomShrine.java
Normal file
58
src/main/java/server/gachapon/MushroomShrine.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX) - gachapon source classes stub
|
||||
* @author Ronan - parsed MapleSEA loots
|
||||
*
|
||||
* MapleSEA-like loots thanks to AyumiLove - src: https://ayumilovemaple.wordpress.com/maplestory-gachapon-guide/
|
||||
*/
|
||||
|
||||
public class MushroomShrine extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int [] {
|
||||
|
||||
/* Scroll */
|
||||
2040305, 2040306, 2040308, 2044604, 2041039, 2041037, 2041035, 2041034, 2041041, 2040608, 2040605, 2040604, 2040611,
|
||||
2040610, 2040813, 2040808, 2043004, 2040017, 2040015, 2040011, 2040013, 2040405, 2040406, 2040410, 2040511, 2040509,
|
||||
2040508, 2040519, 2040521, 2040108, 2040904, 2040908, 2043104, 2044104, 2043005, 2043004, 2043006, 2044004, 2044205,
|
||||
2043304, 2040607, 2040715, 2040713, 2044305, 2044904,
|
||||
|
||||
/* Common equipment */
|
||||
1102040, 1002392, 1432009, 1002393, 1002394, 1082147, 1082148, 1032028, 1002585, 1002586, 1432013, 1022047, 1322027,
|
||||
1012056, 1432018,
|
||||
|
||||
/* Beginner equipment */
|
||||
1072264, 1072262, 1072263,
|
||||
|
||||
/* Warrior equipment */
|
||||
1060074, 1322002, 1002340, 1442004, 1402037, 1422008, 1050022,
|
||||
|
||||
/* Mage equipment */
|
||||
1382037, 1060014, 1051026, 1050056, 1050029, 1051030, 1382036, 1372032, 1041015, 1382015, 1372008, 1382008,
|
||||
|
||||
/* Bowman equipment */
|
||||
1452018, 1041068, 1462007,
|
||||
|
||||
/* Thief equipment */
|
||||
1060052, 1472013, 1002180, 1002170, 1060073, 1060099,
|
||||
|
||||
/* Pirate equipment */
|
||||
1492004, 1492012, 1482009, 1072303, 1002637, 1052107, 1082189, 1052116, 1072309
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int [] {2040811, 2040810, 2040815, 1102041, 1102042, 1082149};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int [] {1102084, 3010019};
|
||||
}
|
||||
|
||||
}
|
||||
66
src/main/java/server/gachapon/NautilusHarbor.java
Normal file
66
src/main/java/server/gachapon/NautilusHarbor.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX) - gachapon source classes stub & pirate items
|
||||
* @author Ronan - parsed MapleSEA loots
|
||||
*
|
||||
* MapleSEA-like loots thanks to AyumiLove - src: https://ayumilovemaple.wordpress.com/maplestory-gachapon-guide/
|
||||
*/
|
||||
|
||||
public class NautilusHarbor extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int[] {
|
||||
/* Scroll */
|
||||
2040605, 2040626, 2040609, 2040607, 2041029, 2041027, 2041031, 2041037, 2041033, 2041039, 2041041, 2041035,
|
||||
2040809, 2040813, 2040015, 2040009, 2040011, 2040013, 2040509, 2040521, 2040519, 2040507, 2040905, 2040909,
|
||||
2040907, 2040713, 2040715, 2040717, 2040405, 2040409, 2040407, 2040426, 2040303, 2040307, 2040309, 2044505,
|
||||
2044705, 2044605, 2043305, 2043105, 2043205, 2043005, 2043007, 2044405, 2044305, 2043805, 2044105, 2044205,
|
||||
2044005, 2043705, 2044901,
|
||||
|
||||
/* Useable drop */
|
||||
2012000, 2000004, 2020008, 2000005, 2012002, 2101004, 2101005, 2101002, 2101003, 4006000,
|
||||
|
||||
/* Warrior equipment */
|
||||
1092014, 1402017,
|
||||
|
||||
/* Magician equipment */
|
||||
1002037, 1002034, 1002064, 1002038, 1382037, 1372000, 1002013, 1002035, 1002065, 1382000,
|
||||
|
||||
/* Bowman equipment */
|
||||
1452018,
|
||||
|
||||
/* Thief equipment */
|
||||
1472010, 1002175, 1472017, 1472025,
|
||||
|
||||
/* Pirate equipment */
|
||||
1002610, 1002616, 1002622, 1002628, 1002634, 1002640, 1002646, 1052095, 1052101, 1052107,
|
||||
1052113, 1052119, 1052125, 1052131, 1072285, 1072291, 1072297, 1072303, 1072309, 1072315,
|
||||
1082180, 1082186, 1082192, 1082198, 1082204, 1082210, 1482001, 1482003, 1482005, 1482007,
|
||||
1482009, 1482011, 1492000, 1492002, 1492004, 1492006, 1492008, 1492010, 1492012, 1002613,
|
||||
1002619, 1002625, 1002631, 1002637, 1002643, 1052098, 1052104, 1052110, 1052116, 1052122,
|
||||
1052128, 1072288, 1072294, 1072300, 1072306, 1072312, 1072318, 1072338, 1082183, 1082189,
|
||||
1082195, 1082201, 1082207, 1082213, 1482000, 1482002, 1482004, 1482006, 1482008, 1482010,
|
||||
1482012, 1492001, 1492003, 1492005, 1492007, 1492009, 1492011,
|
||||
|
||||
/* Knuckler Scrolls */
|
||||
2044800, 2044801, 2044802, 2044803, 2044804, 2044805, 2044806, 2044807, 2044808, 2044809,
|
||||
|
||||
/* Gun Scrolls */
|
||||
2044900, 2044901, 2044902, 2044903, 2044904
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int[] {2040811, 2040815, 2101001};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int[] {};
|
||||
}
|
||||
|
||||
}
|
||||
55
src/main/java/server/gachapon/NewLeafCity.java
Normal file
55
src/main/java/server/gachapon/NewLeafCity.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX) - gachapon source classes stub & pirate equipment
|
||||
* @author Ronan - parsed MapleSEA loots, thanks Vcoc for noticing somewhat unbalanced loots in NLC
|
||||
*
|
||||
* MapleSEA-like loots thanks to AyumiLove - src: https://ayumilovemaple.wordpress.com/maplestory-gachapon-guide/
|
||||
*/
|
||||
|
||||
public class NewLeafCity extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int[] {
|
||||
|
||||
/* Scroll */
|
||||
2040406, 2040408, 2040404, 2040411, 2040409, 2044405, 2040610, 2040607, 2040812, 2041039, 2041040, 2041034,
|
||||
2041030, 2041037, 2043105, 2043304, 2040103, 2040605, 2040611, 2043004, 2043204, 2044204, 2044005, 2040521,
|
||||
2040510, 2043304, 2040908, 2040904, 2040907, 2040809, 2040812, 2040014, 2040714, 2040712, 2044004, 2043705,
|
||||
2044505, 2040519, 2040204, 2040104, 2040109, 2044704, 2040906, 2044304, 2043007, 2040307, 2040304, 2040309,
|
||||
2040208, 2040209, 2044803,
|
||||
|
||||
/* Common equipment */
|
||||
1102040, 1102086, 1082145, 1032027, 1082146, 1002395, 1002083, 1002392, 1002587, 1022047,
|
||||
|
||||
/* Warrior equipment */
|
||||
1312002, 1432013, 1060030, 1422008, 1050022, 1050011, 1402013, 1402017, 1302012,
|
||||
|
||||
/* Mage equipment */
|
||||
1002074, 1050029, 1040093, 1050056, 1050039, 1382008,
|
||||
|
||||
/* Bowman equipment */
|
||||
1002159, 1061051, 1040023,
|
||||
|
||||
/* Thief equipment */
|
||||
1061054, 1061106, 1002249, 1040084, 1060052, 1472054,
|
||||
|
||||
/* Pirate equipment */
|
||||
1002640, 1002643, 1002646, 1052125, 1052128, 1052131, 1072312, 1072315, 1072318, 1082207, 1082210, 1082213,
|
||||
1482010, 1482011, 1002640, 1482012, 1492010, 1492011, 1492012
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int[] {2022284, 2040811, 2040815, 2040811, 1102041, 1102042, 1082149};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int[] {};
|
||||
}
|
||||
|
||||
}
|
||||
57
src/main/java/server/gachapon/Perion.java
Normal file
57
src/main/java/server/gachapon/Perion.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX) - gachapon source classes stub & pirate equipment
|
||||
* @author Ronan - parsed MapleSEA loots
|
||||
*
|
||||
* MapleSEA-like loots thanks to AyumiLove - src: https://ayumilovemaple.wordpress.com/maplestory-gachapon-guide/
|
||||
*/
|
||||
|
||||
public class Perion extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int [] {
|
||||
|
||||
/* Scrolls */
|
||||
2044907, 2044802,
|
||||
|
||||
/* Useable drop */
|
||||
2000004, 2000005,
|
||||
|
||||
/* Common equipment */
|
||||
1402010, 1302022, 1002060, 1322021, 1082147, 1002006, 1002026, 1002392, 1322025, 1322027, 1102000, 1082150,
|
||||
1332020, 1322007, 1302021, 1002395, 1082148, 1322012, 1302017, 1322010, 1032000, 1102013, 1002097,
|
||||
|
||||
/* Warrior equipment */
|
||||
1322020, 1312007, 1312008, 1302004, 1312006, 1082036, 1082117, 1061088, 1302008, 1422005, 1002048, 1061087,
|
||||
1302018, 1322017, 1422001, 1040103, 1060077, 1002022, 1002050, 1442000, 1432030, 1402037, 1092002, 1041092,
|
||||
1050006, 1432004, 1061019, 1432000, 1060009, 1051000, 1002021, 1322014, 1432005,
|
||||
|
||||
/* Magician equipment */
|
||||
1051032, 1040018, 1051027, 1372007, 1050049, 1002036, 1382012, 1002217, 1051033, 1382006, 1050048,
|
||||
|
||||
/* Bowman equipment */
|
||||
1061061, 1060062, 1040075, 1462013, 1041065, 1452006,
|
||||
|
||||
/* Thief equipment */
|
||||
1040095, 1060084, 1002182, 1041049, 1002247, 1332024, 1332009, 1060024, 1332015, 1041060, 1061032, 1041074,
|
||||
1041003, 1332016, 1472020, 1332003, 1041059,
|
||||
|
||||
/* Pirate equipment */
|
||||
1482001, 1492002, 1052113, 1002616, 1072294, 1492004, 1482006, 1082192, 1082189, 1082195
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int [] {1082149, 1002391, 1002419, 1102041};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int [] {};
|
||||
}
|
||||
|
||||
}
|
||||
58
src/main/java/server/gachapon/ShowaSpaFemale.java
Normal file
58
src/main/java/server/gachapon/ShowaSpaFemale.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX) - gachapon source classes stub
|
||||
* @author Ronan - parsed MapleSEA loots
|
||||
*
|
||||
* MapleSEA-like loots src: http://maplesecrets.blogspot.com/2011/06/gachapon-showa-towns-sauna-female-spa.html
|
||||
*/
|
||||
|
||||
public class ShowaSpaFemale extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int [] {
|
||||
|
||||
/* Scroll */
|
||||
2048005, 2048002, 2043202, 2044602, 2043214, 2041307, 2041035, 2044104, 2044505, 2044305, 2043304, 2041309,
|
||||
2044010, 2044803, 2044814, 2044904, 2044902, 2044901,
|
||||
|
||||
/* Useable drop */
|
||||
2022016, 2000005, 2022025, 2022027,
|
||||
|
||||
/* Common equipment */
|
||||
1402000, 1402013, 1002418, 1022047, 1082145, 1082147, 1082146, 1082178, 1082175,
|
||||
|
||||
/* Common setup */
|
||||
3010073, 3010099,
|
||||
|
||||
/* Warrior equipment */
|
||||
1422013, 1432030,
|
||||
|
||||
/* Magician equipment */
|
||||
1372002, 1382003,
|
||||
|
||||
/* Bowman equipment */
|
||||
1040023,
|
||||
|
||||
/* Thief equipment */
|
||||
1332003, 1002209,
|
||||
|
||||
/* Pirate equipment */
|
||||
1082198, 1082213, 1482007, 1492004, 1002646
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int [] {2040916, 1102042};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int [] {};
|
||||
}
|
||||
|
||||
}
|
||||
60
src/main/java/server/gachapon/ShowaSpaMale.java
Normal file
60
src/main/java/server/gachapon/ShowaSpaMale.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX) - gachapon source classes stub
|
||||
* @author Ronan - parsed MapleSEA loots
|
||||
*
|
||||
* MapleSEA-like loots src: http://maplesecrets.blogspot.com/2011/05/gachapon-showa-towns-sauna.html
|
||||
*/
|
||||
|
||||
public class ShowaSpaMale extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int [] {
|
||||
|
||||
/* Scroll */
|
||||
2048005, 2048002, 2043202, 2044602, 2043214, 2041307, 2041035, 2044104, 2044505, 2044305, 2043304, 2044902,
|
||||
2044901, 2044811, 2044903, 2044804,
|
||||
|
||||
/* Useable drop */
|
||||
2022016, 2000005, 2022025, 2022027,
|
||||
|
||||
/* Common equipment */
|
||||
1332020, 1312004, 1332032, 1322023, 1322026, 1322022, 1322012, 1302014, 1302049, 1302017, 1332007, 1432009,
|
||||
1432016, 1432017, 1432009, 1402013, 1402044, 1442014, 1442017, 1442016, 1442025, 1002418, 1082178, 1082179,
|
||||
1082148, 1032027, 1032032, 1102028, 1102086,
|
||||
|
||||
/* Common setup */
|
||||
3010073, 3010111,
|
||||
|
||||
/* Warrior equipment */
|
||||
1412005, 1402048, 1402049, 1322011, 1302003, 1302004, 1302008,
|
||||
|
||||
/* Magician equipment */
|
||||
1372000, 1372009, 1372001, 1372011, 1382006, 1382014,
|
||||
|
||||
/* Bowman equipment */
|
||||
1452018, 1452006, 1452008, 1452005, 1462002, 1462007, 1462003, 1002169,
|
||||
|
||||
/* Thief equipment */
|
||||
1472023, 1332012, 1332017, 1332022, 1332006, 1332029, 1040097,
|
||||
|
||||
/* Pirate equipment */
|
||||
1052107, 1082204, 1072318, 1002637, 1482009, 1492007
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int [] {2040916, 1102042};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int [] {};
|
||||
}
|
||||
|
||||
}
|
||||
59
src/main/java/server/gachapon/Sleepywood.java
Normal file
59
src/main/java/server/gachapon/Sleepywood.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package server.gachapon;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX) - gachapon source classes stub & pirate equipment
|
||||
* @author Ronan - parsed MapleSEA loots
|
||||
*
|
||||
* MapleSEA-like loots thanks to AyumiLove - src: https://ayumilovemaple.wordpress.com/maplestory-gachapon-guide/
|
||||
*/
|
||||
|
||||
public class Sleepywood extends GachaponItems {
|
||||
|
||||
@Override
|
||||
public int[] getCommonItems() {
|
||||
return new int [] {
|
||||
|
||||
/* Scroll */
|
||||
2048003, 2048000, 2040601, 2044501, 2041019, 2041016, 2041022, 2041010, 2041013, 2043301, 2040301, 2040801, 2040001,
|
||||
2040004, 2043101, 2043201, 2043001, 2040504, 2040501, 2048004, 2048001, 2044401, 2040901, 2040701, 2040704, 2040707,
|
||||
2044301, 2043801, 2044101, 2044201, 2044001, 2040602, 2044502, 2041020, 2041017, 2041023, 2041014, 2041005, 2044702,
|
||||
2044602, 2043302, 2040302, 2040802, 2040005, 2043202, 2043002, 2040505, 2040502, 2048005, 2048002, 2044402, 2040902,
|
||||
2040702, 2040705, 2040708, 2044302, 2043802, 2044202, 2044002, 2044801, 2044903, 2044814,
|
||||
|
||||
/* Useable drop */
|
||||
2012000, 2012003, 2020007, 2000004, 2012001, 2020008, 2070006, 2020012, 2000005, 2030007, 2012002, 2002001, 2070005,
|
||||
|
||||
/* Common equipment */
|
||||
1032003, 1432009, 1102014, 1102018, 1002392, 1322026, 1032022, 1312012, 1332020, 1092030, 1032016, 1032015, 1032014,
|
||||
1322024, 1032013, 1322022, 1102016, 1032012, 1032023, 1402014, 1032000, 1102017,
|
||||
|
||||
/* Warrior equipment */
|
||||
1402017, 1051010, 1432011, 1442006, 1322002, 1422004, 1432010, 1051011, 1060018, 1432000, 1422003, 1412003, 1422000,
|
||||
|
||||
/* Magician equipment */
|
||||
1002034, 1002142, 1382010, 1002013, 1382008, 1382011, 1050047, 1002065,
|
||||
|
||||
/* Bowman equipment */
|
||||
1452003, 1002165, 1040068, 1462013, 1462011, 1462012, 1061050, 1462010, 1002161,
|
||||
|
||||
/* Thief equipment */
|
||||
1332022, 1002175, 1040042, 1472004, 1040057, 1332031, 1332023, 1332010, 1002171, 1060046,
|
||||
|
||||
/* Pirate equipment */
|
||||
1002631, 1002634, 1002637, 1052116, 1052119, 1052122, 1072303, 1072306, 1072309, 1082198, 1082201, 1082204, 1482007,
|
||||
1482008, 1482009
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getUncommonItems() {
|
||||
return new int [] {2040804, 2040817, 2040805, 2340000, 1082149, 1442018};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getRareItems() {
|
||||
return new int [] {};
|
||||
}
|
||||
|
||||
}
|
||||
106
src/main/java/server/life/AbstractLoadedMapleLife.java
Normal file
106
src/main/java/server/life/AbstractLoadedMapleLife.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
import server.maps.AbstractAnimatedMapleMapObject;
|
||||
|
||||
public abstract class AbstractLoadedMapleLife extends AbstractAnimatedMapleMapObject {
|
||||
private final int id;
|
||||
private int f;
|
||||
private boolean hide;
|
||||
private int fh;
|
||||
private int start_fh;
|
||||
private int cy;
|
||||
private int rx0;
|
||||
private int rx1;
|
||||
|
||||
public AbstractLoadedMapleLife(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AbstractLoadedMapleLife(AbstractLoadedMapleLife life) {
|
||||
this(life.getId());
|
||||
this.f = life.f;
|
||||
this.hide = life.hide;
|
||||
this.fh = life.fh;
|
||||
this.start_fh = life.fh;
|
||||
this.cy = life.cy;
|
||||
this.rx0 = life.rx0;
|
||||
this.rx1 = life.rx1;
|
||||
}
|
||||
|
||||
public int getF() {
|
||||
return f;
|
||||
}
|
||||
|
||||
public void setF(int f) {
|
||||
this.f = f;
|
||||
}
|
||||
|
||||
public boolean isHidden() {
|
||||
return hide;
|
||||
}
|
||||
|
||||
public void setHide(boolean hide) {
|
||||
this.hide = hide;
|
||||
}
|
||||
|
||||
public int getFh() {
|
||||
return fh;
|
||||
}
|
||||
|
||||
public void setFh(int fh) {
|
||||
this.fh = fh;
|
||||
}
|
||||
|
||||
public int getStartFh() {
|
||||
return start_fh;
|
||||
}
|
||||
|
||||
public int getCy() {
|
||||
return cy;
|
||||
}
|
||||
|
||||
public void setCy(int cy) {
|
||||
this.cy = cy;
|
||||
}
|
||||
|
||||
public int getRx0() {
|
||||
return rx0;
|
||||
}
|
||||
|
||||
public void setRx0(int rx0) {
|
||||
this.rx0 = rx0;
|
||||
}
|
||||
|
||||
public int getRx1() {
|
||||
return rx1;
|
||||
}
|
||||
|
||||
public void setRx1(int rx1) {
|
||||
this.rx1 = rx1;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
54
src/main/java/server/life/ChangeableStats.java
Normal file
54
src/main/java/server/life/ChangeableStats.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
import constants.game.GameConstants;
|
||||
|
||||
public class ChangeableStats extends OverrideMonsterStats {
|
||||
|
||||
public int watk, matk, wdef, mdef, level;
|
||||
|
||||
public ChangeableStats(MapleMonsterStats stats, OverrideMonsterStats ostats) {
|
||||
hp = ostats.getHp();
|
||||
exp = ostats.getExp();
|
||||
mp = ostats.getMp();
|
||||
watk = stats.getPADamage();
|
||||
matk = stats.getMADamage();
|
||||
wdef = stats.getPDDamage();
|
||||
mdef = stats.getMDDamage();
|
||||
level = stats.getLevel();
|
||||
}
|
||||
|
||||
public ChangeableStats(MapleMonsterStats stats, int newLevel, boolean pqMob) { // here we go i think
|
||||
final double mod = (double) newLevel / (double) stats.getLevel();
|
||||
final double hpRatio = (double) stats.getHp() / (double) stats.getExp();
|
||||
final double pqMod = (pqMob ? 1.5 : 1.0); // god damn
|
||||
hp = Math.min((int) Math.round((!stats.isBoss() ? GameConstants.getMonsterHP(newLevel) : (stats.getHp() * mod)) * pqMod), Integer.MAX_VALUE); // right here lol
|
||||
exp = Math.min((int) Math.round((!stats.isBoss() ? (GameConstants.getMonsterHP(newLevel) / hpRatio) : (stats.getExp())) * pqMod), Integer.MAX_VALUE);
|
||||
mp = Math.min((int) Math.round(stats.getMp() * mod * pqMod), Integer.MAX_VALUE);
|
||||
watk = Math.min((int) Math.round(stats.getPADamage() * mod), Integer.MAX_VALUE);
|
||||
matk = Math.min((int) Math.round(stats.getMADamage() * mod), Integer.MAX_VALUE);
|
||||
wdef = Math.min(Math.min(stats.isBoss() ? 30 : 20, (int) Math.round(stats.getPDDamage() * mod)), Integer.MAX_VALUE);
|
||||
mdef = Math.min(Math.min(stats.isBoss() ? 30 : 20, (int) Math.round(stats.getMDDamage() * mod)), Integer.MAX_VALUE);
|
||||
level = newLevel;
|
||||
}
|
||||
|
||||
public ChangeableStats(MapleMonsterStats stats, float statModifier, boolean pqMob) {
|
||||
this(stats, (int)(statModifier * stats.getLevel()), pqMob);
|
||||
}
|
||||
}
|
||||
65
src/main/java/server/life/Element.java
Normal file
65
src/main/java/server/life/Element.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
public enum Element {
|
||||
NEUTRAL(0), PHYSICAL(1), FIRE(2, true), ICE(3, true), LIGHTING(4), POISON(5), HOLY(6, true), DARKNESS(7);
|
||||
|
||||
private int value;
|
||||
private boolean special = false;
|
||||
private Element(int v) {
|
||||
this.value = v;
|
||||
}
|
||||
|
||||
private Element(int v, boolean special) {
|
||||
this.value = v;
|
||||
this.special = special;
|
||||
}
|
||||
|
||||
public boolean isSpecial() {
|
||||
return special;
|
||||
}
|
||||
|
||||
public static Element getFromChar(char c) {
|
||||
switch (Character.toUpperCase(c)) {
|
||||
case 'F':
|
||||
return FIRE;
|
||||
case 'I':
|
||||
return ICE;
|
||||
case 'L':
|
||||
return LIGHTING;
|
||||
case 'S':
|
||||
return POISON;
|
||||
case 'H':
|
||||
return HOLY;
|
||||
case 'D':
|
||||
return DARKNESS;
|
||||
case 'P':
|
||||
return NEUTRAL;
|
||||
}
|
||||
throw new IllegalArgumentException("unknown elemnt char " + c);
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
41
src/main/java/server/life/ElementalEffectiveness.java
Normal file
41
src/main/java/server/life/ElementalEffectiveness.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
public enum ElementalEffectiveness {
|
||||
NORMAL, IMMUNE, STRONG, WEAK, NEUTRAL;
|
||||
|
||||
public static ElementalEffectiveness getByNumber(int num) {
|
||||
switch (num) {
|
||||
case 1:
|
||||
return IMMUNE;
|
||||
case 2:
|
||||
return STRONG;
|
||||
case 3:
|
||||
return WEAK;
|
||||
case 4:
|
||||
return NEUTRAL;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unkown effectiveness: " + num);
|
||||
}
|
||||
}
|
||||
}
|
||||
372
src/main/java/server/life/MapleLifeFactory.java
Normal file
372
src/main/java/server/life/MapleLifeFactory.java
Normal file
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import provider.wz.MapleDataType;
|
||||
import tools.Pair;
|
||||
import tools.StringUtil;
|
||||
|
||||
public class MapleLifeFactory {
|
||||
|
||||
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Mob.wz"));
|
||||
private final static MapleDataProvider stringDataWZ = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz"));
|
||||
private static MapleData mobStringData = stringDataWZ.getData("Mob.img");
|
||||
private static MapleData npcStringData = stringDataWZ.getData("Npc.img");
|
||||
private static Map<Integer, MapleMonsterStats> monsterStats = new HashMap<>();
|
||||
private static Set<Integer> hpbarBosses = getHpBarBosses();
|
||||
|
||||
private static Set<Integer> getHpBarBosses() {
|
||||
Set<Integer> ret = new HashSet<>();
|
||||
|
||||
MapleDataProvider uiDataWZ = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/UI.wz"));
|
||||
for (MapleData bossData : uiDataWZ.getData("UIWindow.img").getChildByPath("MobGage/Mob").getChildren()) {
|
||||
ret.add(Integer.valueOf(bossData.getName()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static AbstractLoadedMapleLife getLife(int id, String type) {
|
||||
if (type.equalsIgnoreCase("n")) {
|
||||
return getNPC(id);
|
||||
} else if (type.equalsIgnoreCase("m")) {
|
||||
return getMonster(id);
|
||||
} else {
|
||||
System.out.println("Unknown Life type: " + type);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MobAttackInfoHolder {
|
||||
protected int attackPos;
|
||||
protected int mpCon;
|
||||
protected int coolTime;
|
||||
protected int animationTime;
|
||||
|
||||
protected MobAttackInfoHolder(int attackPos, int mpCon, int coolTime, int animationTime) {
|
||||
this.attackPos = attackPos;
|
||||
this.mpCon = mpCon;
|
||||
this.coolTime = coolTime;
|
||||
this.animationTime = animationTime;
|
||||
}
|
||||
}
|
||||
|
||||
private static void setMonsterAttackInfo(int mid, List<MobAttackInfoHolder> attackInfos) {
|
||||
if (!attackInfos.isEmpty()) {
|
||||
MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance();
|
||||
|
||||
for (MobAttackInfoHolder attackInfo : attackInfos) {
|
||||
mi.setMobAttackInfo(mid, attackInfo.attackPos, attackInfo.mpCon, attackInfo.coolTime);
|
||||
mi.setMobAttackAnimationTime(mid, attackInfo.attackPos, attackInfo.animationTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Pair<MapleMonsterStats, List<MobAttackInfoHolder>> getMonsterStats(int mid) {
|
||||
MapleData monsterData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(mid) + ".img", '0', 11));
|
||||
if (monsterData == null) {
|
||||
return null;
|
||||
}
|
||||
MapleData monsterInfoData = monsterData.getChildByPath("info");
|
||||
|
||||
List<MobAttackInfoHolder> attackInfos = new LinkedList<>();
|
||||
MapleMonsterStats stats = new MapleMonsterStats();
|
||||
|
||||
int linkMid = MapleDataTool.getIntConvert("link", monsterInfoData, 0);
|
||||
if (linkMid != 0) {
|
||||
Pair<MapleMonsterStats, List<MobAttackInfoHolder>> linkStats = getMonsterStats(linkMid);
|
||||
if (linkStats == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// thanks resinate for noticing non-propagable infos such as revives getting retrieved
|
||||
attackInfos.addAll(linkStats.getRight());
|
||||
}
|
||||
|
||||
stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData));
|
||||
stats.setFriendly(MapleDataTool.getIntConvert("damagedByMob", monsterInfoData, stats.isFriendly() ? 1 : 0) == 1);
|
||||
stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData));
|
||||
stats.setPDDamage(MapleDataTool.getIntConvert("PDDamage", monsterInfoData));
|
||||
stats.setMADamage(MapleDataTool.getIntConvert("MADamage", monsterInfoData));
|
||||
stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData));
|
||||
stats.setMp(MapleDataTool.getIntConvert("maxMP", monsterInfoData, stats.getMp()));
|
||||
stats.setExp(MapleDataTool.getIntConvert("exp", monsterInfoData, stats.getExp()));
|
||||
stats.setLevel(MapleDataTool.getIntConvert("level", monsterInfoData));
|
||||
stats.setRemoveAfter(MapleDataTool.getIntConvert("removeAfter", monsterInfoData, stats.removeAfter()));
|
||||
stats.setBoss(MapleDataTool.getIntConvert("boss", monsterInfoData, stats.isBoss() ? 1 : 0) > 0);
|
||||
stats.setExplosiveReward(MapleDataTool.getIntConvert("explosiveReward", monsterInfoData, stats.isExplosiveReward() ? 1 : 0) > 0);
|
||||
stats.setFfaLoot(MapleDataTool.getIntConvert("publicReward", monsterInfoData, stats.isFfaLoot() ? 1 : 0) > 0);
|
||||
stats.setUndead(MapleDataTool.getIntConvert("undead", monsterInfoData, stats.isUndead() ? 1 : 0) > 0);
|
||||
stats.setName(MapleDataTool.getString(mid + "/name", mobStringData, "MISSINGNO"));
|
||||
stats.setBuffToGive(MapleDataTool.getIntConvert("buff", monsterInfoData, stats.getBuffToGive()));
|
||||
stats.setCP(MapleDataTool.getIntConvert("getCP", monsterInfoData, stats.getCP()));
|
||||
stats.setRemoveOnMiss(MapleDataTool.getIntConvert("removeOnMiss", monsterInfoData, stats.removeOnMiss() ? 1 : 0) > 0);
|
||||
|
||||
MapleData special = monsterInfoData.getChildByPath("coolDamage");
|
||||
if (special != null) {
|
||||
int coolDmg = MapleDataTool.getIntConvert("coolDamage", monsterInfoData);
|
||||
int coolProb = MapleDataTool.getIntConvert("coolDamageProb", monsterInfoData, 0);
|
||||
stats.setCool(new Pair<>(coolDmg, coolProb));
|
||||
}
|
||||
special = monsterInfoData.getChildByPath("loseItem");
|
||||
if (special != null) {
|
||||
for (MapleData liData : special.getChildren()) {
|
||||
stats.addLoseItem(new loseItem(MapleDataTool.getInt(liData.getChildByPath("id")), (byte) MapleDataTool.getInt(liData.getChildByPath("prop")), (byte) MapleDataTool.getInt(liData.getChildByPath("x"))));
|
||||
}
|
||||
}
|
||||
special = monsterInfoData.getChildByPath("selfDestruction");
|
||||
if (special != null) {
|
||||
stats.setSelfDestruction(new selfDestruction((byte) MapleDataTool.getInt(special.getChildByPath("action")), MapleDataTool.getIntConvert("removeAfter", special, -1), MapleDataTool.getIntConvert("hp", special, -1)));
|
||||
}
|
||||
MapleData firstAttackData = monsterInfoData.getChildByPath("firstAttack");
|
||||
int firstAttack = 0;
|
||||
if (firstAttackData != null) {
|
||||
if (firstAttackData.getType() == MapleDataType.FLOAT) {
|
||||
firstAttack = Math.round(MapleDataTool.getFloat(firstAttackData));
|
||||
} else {
|
||||
firstAttack = MapleDataTool.getInt(firstAttackData);
|
||||
}
|
||||
}
|
||||
stats.setFirstAttack(firstAttack > 0);
|
||||
stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, stats.getDropPeriod() / 10000) * 10000);
|
||||
|
||||
// thanks yuxaij, Riizade, Z1peR, Anesthetic for noticing some bosses crashing players due to missing requirements
|
||||
boolean hpbarBoss = stats.isBoss() && hpbarBosses.contains(mid);
|
||||
stats.setTagColor(hpbarBoss ? MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0) : 0);
|
||||
stats.setTagBgColor(hpbarBoss ? MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0) : 0);
|
||||
|
||||
for (MapleData idata : monsterData) {
|
||||
if (!idata.getName().equals("info")) {
|
||||
int delay = 0;
|
||||
for (MapleData pic : idata.getChildren()) {
|
||||
delay += MapleDataTool.getIntConvert("delay", pic, 0);
|
||||
}
|
||||
stats.setAnimationTime(idata.getName(), delay);
|
||||
}
|
||||
}
|
||||
MapleData reviveInfo = monsterInfoData.getChildByPath("revive");
|
||||
if (reviveInfo != null) {
|
||||
List<Integer> revives = new LinkedList<>();
|
||||
for (MapleData data_ : reviveInfo) {
|
||||
revives.add(MapleDataTool.getInt(data_));
|
||||
}
|
||||
stats.setRevives(revives);
|
||||
}
|
||||
decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, ""));
|
||||
|
||||
MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance();
|
||||
MapleData monsterSkillInfoData = monsterInfoData.getChildByPath("skill");
|
||||
if (monsterSkillInfoData != null) {
|
||||
int i = 0;
|
||||
List<Pair<Integer, Integer>> skills = new ArrayList<>();
|
||||
while (monsterSkillInfoData.getChildByPath(Integer.toString(i)) != null) {
|
||||
int skillId = MapleDataTool.getInt(i + "/skill", monsterSkillInfoData, 0);
|
||||
int skillLv = MapleDataTool.getInt(i + "/level", monsterSkillInfoData, 0);
|
||||
skills.add(new Pair<>(skillId, skillLv));
|
||||
|
||||
MapleData monsterSkillData = monsterData.getChildByPath("skill" + (i + 1));
|
||||
if (monsterSkillData != null) {
|
||||
int animationTime = 0;
|
||||
for (MapleData effectEntry : monsterSkillData.getChildren()) {
|
||||
animationTime += MapleDataTool.getIntConvert("delay", effectEntry, 0);
|
||||
}
|
||||
|
||||
MobSkill skill = MobSkillFactory.getMobSkill(skillId, skillLv);
|
||||
mi.setMobSkillAnimationTime(skill, animationTime);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
stats.setSkills(skills);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
MapleData monsterAttackData;
|
||||
while ((monsterAttackData = monsterData.getChildByPath("attack" + (i + 1))) != null) {
|
||||
int animationTime = 0;
|
||||
for (MapleData effectEntry : monsterAttackData.getChildren()) {
|
||||
animationTime += MapleDataTool.getIntConvert("delay", effectEntry, 0);
|
||||
}
|
||||
|
||||
int mpCon = MapleDataTool.getIntConvert("info/conMP", monsterAttackData, 0);
|
||||
int coolTime = MapleDataTool.getIntConvert("info/attackAfter", monsterAttackData, 0);
|
||||
attackInfos.add(new MobAttackInfoHolder(i, mpCon, coolTime, animationTime));
|
||||
i++;
|
||||
}
|
||||
|
||||
MapleData banishData = monsterInfoData.getChildByPath("ban");
|
||||
if (banishData != null) {
|
||||
stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp")));
|
||||
}
|
||||
|
||||
int noFlip = MapleDataTool.getInt("noFlip", monsterInfoData, 0);
|
||||
if (noFlip > 0) {
|
||||
Point origin = MapleDataTool.getPoint("stand/0/origin", monsterData, null);
|
||||
if (origin != null) {
|
||||
stats.setFixedStance(origin.getX() < 1 ? 5 : 4); // fixed left/right
|
||||
}
|
||||
}
|
||||
|
||||
return new Pair<>(stats, attackInfos);
|
||||
}
|
||||
|
||||
public static MapleMonster getMonster(int mid) {
|
||||
try {
|
||||
MapleMonsterStats stats = monsterStats.get(Integer.valueOf(mid));
|
||||
if (stats == null) {
|
||||
Pair<MapleMonsterStats, List<MobAttackInfoHolder>> mobStats = getMonsterStats(mid);
|
||||
stats = mobStats.getLeft();
|
||||
setMonsterAttackInfo(mid, mobStats.getRight());
|
||||
|
||||
monsterStats.put(Integer.valueOf(mid), stats);
|
||||
}
|
||||
MapleMonster ret = new MapleMonster(mid, stats);
|
||||
return ret;
|
||||
} catch(NullPointerException npe) {
|
||||
System.out.println("[SEVERE] MOB " + mid + " failed to load. Issue: " + npe.getMessage() + "\n\n");
|
||||
npe.printStackTrace();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getMonsterLevel(int mid) {
|
||||
try {
|
||||
MapleMonsterStats stats = monsterStats.get(Integer.valueOf(mid));
|
||||
if (stats == null) {
|
||||
MapleData monsterData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(mid) + ".img", '0', 11));
|
||||
if (monsterData == null) {
|
||||
return -1;
|
||||
}
|
||||
MapleData monsterInfoData = monsterData.getChildByPath("info");
|
||||
return MapleDataTool.getIntConvert("level", monsterInfoData);
|
||||
} else {
|
||||
return stats.getLevel();
|
||||
}
|
||||
} catch(NullPointerException npe) {
|
||||
System.out.println("[SEVERE] MOB " + mid + " failed to load. Issue: " + npe.getMessage() + "\n\n");
|
||||
npe.printStackTrace();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static void decodeElementalString(MapleMonsterStats stats, String elemAttr) {
|
||||
for (int i = 0; i < elemAttr.length(); i += 2) {
|
||||
stats.setEffectiveness(Element.getFromChar(elemAttr.charAt(i)), ElementalEffectiveness.getByNumber(Integer.valueOf(String.valueOf(elemAttr.charAt(i + 1)))));
|
||||
}
|
||||
}
|
||||
|
||||
public static MapleNPC getNPC(int nid) {
|
||||
return new MapleNPC(nid, new MapleNPCStats(MapleDataTool.getString(nid + "/name", npcStringData, "MISSINGNO")));
|
||||
}
|
||||
|
||||
public static String getNPCDefaultTalk(int nid) {
|
||||
return MapleDataTool.getString(nid + "/d0", npcStringData, "(...)");
|
||||
}
|
||||
|
||||
public static class BanishInfo {
|
||||
|
||||
private int map;
|
||||
private String portal, msg;
|
||||
|
||||
public BanishInfo(String msg, int map, String portal) {
|
||||
this.msg = msg;
|
||||
this.map = map;
|
||||
this.portal = portal;
|
||||
}
|
||||
|
||||
public int getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public String getPortal() {
|
||||
return portal;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
public static class loseItem {
|
||||
|
||||
private int id;
|
||||
private byte chance, x;
|
||||
|
||||
private loseItem(int id, byte chance, byte x) {
|
||||
this.id = id;
|
||||
this.chance = chance;
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public byte getChance() {
|
||||
return chance;
|
||||
}
|
||||
|
||||
public byte getX() {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
public static class selfDestruction {
|
||||
|
||||
private byte action;
|
||||
private int removeAfter;
|
||||
private int hp;
|
||||
|
||||
private selfDestruction(byte action, int removeAfter, int hp) {
|
||||
this.action = action;
|
||||
this.removeAfter = removeAfter;
|
||||
this.hp = hp;
|
||||
}
|
||||
|
||||
public int getHp() {
|
||||
return hp;
|
||||
}
|
||||
|
||||
public byte getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public int removeAfter() {
|
||||
return removeAfter;
|
||||
}
|
||||
}
|
||||
}
|
||||
2269
src/main/java/server/life/MapleMonster.java
Normal file
2269
src/main/java/server/life/MapleMonster.java
Normal file
File diff suppressed because it is too large
Load Diff
338
src/main/java/server/life/MapleMonsterInformationProvider.java
Normal file
338
src/main/java/server/life/MapleMonsterInformationProvider.java
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
import config.YamlConfig;
|
||||
import constants.inventory.ItemConstants;
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import server.MapleItemInformationProvider;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.Pair;
|
||||
import tools.Randomizer;
|
||||
|
||||
public class MapleMonsterInformationProvider {
|
||||
// Author : LightPepsi
|
||||
|
||||
private static final MapleMonsterInformationProvider instance = new MapleMonsterInformationProvider();
|
||||
|
||||
public static MapleMonsterInformationProvider getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private final Map<Integer, List<MonsterDropEntry>> drops = new HashMap<>();
|
||||
private final List<MonsterGlobalDropEntry> globaldrops = new ArrayList<>();
|
||||
private final Map<Integer, List<MonsterGlobalDropEntry>> continentdrops = new HashMap<>();
|
||||
|
||||
private final Map<Integer, List<Integer>> dropsChancePool = new HashMap<>(); // thanks to ronan
|
||||
private final Set<Integer> hasNoMultiEquipDrops = new HashSet<>();
|
||||
private final Map<Integer, List<MonsterDropEntry>> extraMultiEquipDrops = new HashMap<>();
|
||||
|
||||
private final Map<Pair<Integer, Integer>, Integer> mobAttackAnimationTime = new HashMap<>();
|
||||
private final Map<MobSkill, Integer> mobSkillAnimationTime = new HashMap<>();
|
||||
|
||||
private final Map<Integer, Pair<Integer, Integer>> mobAttackInfo = new HashMap<>();
|
||||
|
||||
private final Map<Integer, Boolean> mobBossCache = new HashMap<>();
|
||||
private final Map<Integer, String> mobNameCache = new HashMap<>();
|
||||
|
||||
protected MapleMonsterInformationProvider() {
|
||||
retrieveGlobal();
|
||||
}
|
||||
|
||||
public final List<MonsterGlobalDropEntry> getRelevantGlobalDrops(int mapid) {
|
||||
int continentid = mapid / 100000000;
|
||||
|
||||
List<MonsterGlobalDropEntry> contiItems = continentdrops.get(continentid);
|
||||
if (contiItems == null) { // continent separated global drops found thanks to marcuswoon
|
||||
contiItems = new ArrayList<>();
|
||||
|
||||
for (MonsterGlobalDropEntry e : globaldrops) {
|
||||
if (e.continentid < 0 || e.continentid == continentid) {
|
||||
contiItems.add(e);
|
||||
}
|
||||
}
|
||||
|
||||
continentdrops.put(continentid, contiItems);
|
||||
}
|
||||
|
||||
return contiItems;
|
||||
}
|
||||
|
||||
private void retrieveGlobal() {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
Connection con = null;
|
||||
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
ps = con.prepareStatement("SELECT * FROM drop_data_global WHERE chance > 0");
|
||||
rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
globaldrops.add(
|
||||
new MonsterGlobalDropEntry(
|
||||
rs.getInt("itemid"),
|
||||
rs.getInt("chance"),
|
||||
rs.getByte("continent"),
|
||||
rs.getInt("minimum_quantity"),
|
||||
rs.getInt("maximum_quantity"),
|
||||
rs.getShort("questid")));
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
System.err.println("Error retrieving drop" + e);
|
||||
} finally {
|
||||
try {
|
||||
if (ps != null && !ps.isClosed()) {
|
||||
ps.close();
|
||||
}
|
||||
if (rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
if (con != null && !con.isClosed()) {
|
||||
con.close();
|
||||
}
|
||||
} catch (SQLException ignore) {
|
||||
ignore.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<MonsterDropEntry> retrieveEffectiveDrop(final int monsterId) {
|
||||
// this reads the drop entries searching for multi-equip, properly processing them
|
||||
|
||||
List<MonsterDropEntry> list = retrieveDrop(monsterId);
|
||||
if (hasNoMultiEquipDrops.contains(monsterId) || !YamlConfig.config.server.USE_MULTIPLE_SAME_EQUIP_DROP) {
|
||||
return list;
|
||||
}
|
||||
|
||||
List<MonsterDropEntry> multiDrops = extraMultiEquipDrops.get(monsterId), extra = new LinkedList<>();
|
||||
if (multiDrops == null) {
|
||||
multiDrops = new LinkedList<>();
|
||||
|
||||
for (MonsterDropEntry mde : list) {
|
||||
if (ItemConstants.isEquipment(mde.itemId) && mde.Maximum > 1) {
|
||||
multiDrops.add(mde);
|
||||
|
||||
int rnd = Randomizer.rand(mde.Minimum, mde.Maximum);
|
||||
for (int i = 0; i < rnd - 1; i++) {
|
||||
extra.add(mde); // this passes copies of the equips' MDE with min/max quantity > 1, but idc on equips they are unused anyways
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!multiDrops.isEmpty()) {
|
||||
extraMultiEquipDrops.put(monsterId, multiDrops);
|
||||
} else {
|
||||
hasNoMultiEquipDrops.add(monsterId);
|
||||
}
|
||||
} else {
|
||||
for (MonsterDropEntry mde : multiDrops) {
|
||||
int rnd = Randomizer.rand(mde.Minimum, mde.Maximum);
|
||||
for (int i = 0; i < rnd - 1; i++) {
|
||||
extra.add(mde);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<MonsterDropEntry> ret = new LinkedList<>(list);
|
||||
ret.addAll(extra);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public final List<MonsterDropEntry> retrieveDrop(final int monsterId) {
|
||||
if (drops.containsKey(monsterId)) {
|
||||
return drops.get(monsterId);
|
||||
}
|
||||
final List<MonsterDropEntry> ret = new LinkedList<>();
|
||||
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
Connection con = null;
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
ps = con.prepareStatement("SELECT itemid, chance, minimum_quantity, maximum_quantity, questid FROM drop_data WHERE dropperid = ?");
|
||||
ps.setInt(1, monsterId);
|
||||
rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
ret.add(new MonsterDropEntry(rs.getInt("itemid"), rs.getInt("chance"), rs.getInt("minimum_quantity"), rs.getInt("maximum_quantity"), rs.getShort("questid")));
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return ret;
|
||||
} finally {
|
||||
try {
|
||||
if (ps != null && !ps.isClosed()) {
|
||||
ps.close();
|
||||
}
|
||||
if (rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
if (con != null && !con.isClosed()) {
|
||||
con.close();
|
||||
}
|
||||
} catch (SQLException ignore) {
|
||||
ignore.printStackTrace();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
drops.put(monsterId, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public final List<Integer> retrieveDropPool(final int monsterId) { // ignores Quest and Party Quest items
|
||||
if (dropsChancePool.containsKey(monsterId)) {
|
||||
return dropsChancePool.get(monsterId);
|
||||
}
|
||||
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
|
||||
List<MonsterDropEntry> dropList = retrieveDrop(monsterId);
|
||||
List<Integer> ret = new ArrayList<>();
|
||||
|
||||
int accProp = 0;
|
||||
for (MonsterDropEntry mde : dropList) {
|
||||
if (!ii.isQuestItem(mde.itemId) && !ii.isPartyQuestItem(mde.itemId)) {
|
||||
accProp += mde.chance;
|
||||
}
|
||||
|
||||
ret.add(accProp);
|
||||
}
|
||||
|
||||
if (accProp == 0) {
|
||||
ret.clear(); // don't accept mobs dropping no relevant items
|
||||
}
|
||||
dropsChancePool.put(monsterId, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public final void setMobAttackAnimationTime(int monsterId, int attackPos, int animationTime) {
|
||||
mobAttackAnimationTime.put(new Pair<>(monsterId, attackPos), animationTime);
|
||||
}
|
||||
|
||||
public final Integer getMobAttackAnimationTime(int monsterId, int attackPos) {
|
||||
Integer time = mobAttackAnimationTime.get(new Pair<>(monsterId, attackPos));
|
||||
return time == null ? 0 : time;
|
||||
}
|
||||
|
||||
public final void setMobSkillAnimationTime(MobSkill skill, int animationTime) {
|
||||
mobSkillAnimationTime.put(skill, animationTime);
|
||||
}
|
||||
|
||||
public final Integer getMobSkillAnimationTime(MobSkill skill) {
|
||||
Integer time = mobSkillAnimationTime.get(skill);
|
||||
return time == null ? 0 : time;
|
||||
}
|
||||
|
||||
public final void setMobAttackInfo(int monsterId, int attackPos, int mpCon, int coolTime) {
|
||||
mobAttackInfo.put((monsterId << 3) + attackPos, new Pair<>(mpCon, coolTime));
|
||||
}
|
||||
|
||||
public final Pair<Integer, Integer> getMobAttackInfo(int monsterId, int attackPos) {
|
||||
if (attackPos < 0 || attackPos > 7) {
|
||||
return null;
|
||||
}
|
||||
return mobAttackInfo.get((monsterId << 3) + attackPos);
|
||||
}
|
||||
|
||||
public static ArrayList<Pair<Integer, String>> getMobsIDsFromName(String search) {
|
||||
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
|
||||
ArrayList<Pair<Integer, String>> retMobs = new ArrayList<Pair<Integer, String>>();
|
||||
MapleData data = dataProvider.getData("Mob.img");
|
||||
List<Pair<Integer, String>> mobPairList = new LinkedList<Pair<Integer, String>>();
|
||||
for (MapleData mobIdData : data.getChildren()) {
|
||||
int mobIdFromData = Integer.parseInt(mobIdData.getName());
|
||||
String mobNameFromData = MapleDataTool.getString(mobIdData.getChildByPath("name"), "NO-NAME");
|
||||
mobPairList.add(new Pair<Integer, String>(mobIdFromData, mobNameFromData));
|
||||
}
|
||||
for (Pair<Integer, String> mobPair : mobPairList) {
|
||||
if (mobPair.getRight().toLowerCase().contains(search.toLowerCase())) {
|
||||
retMobs.add(mobPair);
|
||||
}
|
||||
}
|
||||
return retMobs;
|
||||
}
|
||||
|
||||
public boolean isBoss(int id) {
|
||||
Boolean boss = mobBossCache.get(id);
|
||||
if (boss == null) {
|
||||
try {
|
||||
boss = MapleLifeFactory.getMonster(id).isBoss();
|
||||
} catch (NullPointerException npe) {
|
||||
boss = false;
|
||||
} catch (Exception e) { //nonexistant mob
|
||||
boss = false;
|
||||
|
||||
e.printStackTrace();
|
||||
System.err.println("Nonexistant mob id " + id);
|
||||
}
|
||||
|
||||
mobBossCache.put(id, boss);
|
||||
}
|
||||
|
||||
return boss;
|
||||
}
|
||||
|
||||
public String getMobNameFromId(int id) {
|
||||
String mobName = mobNameCache.get(id);
|
||||
if (mobName == null) {
|
||||
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
|
||||
MapleData mobData = dataProvider.getData("Mob.img");
|
||||
|
||||
mobName = MapleDataTool.getString(mobData.getChildByPath(id + "/name"), "");
|
||||
mobNameCache.put(id, mobName);
|
||||
}
|
||||
|
||||
return mobName;
|
||||
}
|
||||
|
||||
public final void clearDrops() {
|
||||
drops.clear();
|
||||
hasNoMultiEquipDrops.clear();
|
||||
extraMultiEquipDrops.clear();
|
||||
dropsChancePool.clear();
|
||||
globaldrops.clear();
|
||||
continentdrops.clear();
|
||||
retrieveGlobal();
|
||||
}
|
||||
}
|
||||
386
src/main/java/server/life/MapleMonsterStats.java
Normal file
386
src/main/java/server/life/MapleMonsterStats.java
Normal file
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import server.life.MapleLifeFactory.BanishInfo;
|
||||
import server.life.MapleLifeFactory.loseItem;
|
||||
import server.life.MapleLifeFactory.selfDestruction;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
* @author Frz
|
||||
*/
|
||||
public class MapleMonsterStats {
|
||||
public boolean changeable;
|
||||
public int exp, hp, mp, level, PADamage, PDDamage, MADamage, MDDamage, dropPeriod, cp, buffToGive = -1, removeAfter;
|
||||
public boolean boss, undead, ffaLoot, isExplosiveReward, firstAttack, removeOnMiss;
|
||||
public String name;
|
||||
public Map<String, Integer> animationTimes = new HashMap<String, Integer>();
|
||||
public Map<Element, ElementalEffectiveness> resistance = new HashMap<Element, ElementalEffectiveness>();
|
||||
public List<Integer> revives = Collections.emptyList();
|
||||
public byte tagColor, tagBgColor;
|
||||
public List<Pair<Integer, Integer>> skills = new ArrayList<Pair<Integer, Integer>>();
|
||||
public Pair<Integer, Integer> cool = null;
|
||||
public BanishInfo banish = null;
|
||||
public List<loseItem> loseItem = null;
|
||||
public selfDestruction selfDestruction = null;
|
||||
public int fixedStance = 0;
|
||||
public boolean friendly;
|
||||
|
||||
public void setChange(boolean change) {
|
||||
this.changeable = change;
|
||||
}
|
||||
|
||||
public boolean isChangeable() {
|
||||
return changeable;
|
||||
}
|
||||
|
||||
public int getExp() {
|
||||
return exp;
|
||||
}
|
||||
|
||||
public void setExp(int exp) {
|
||||
this.exp = exp;
|
||||
}
|
||||
|
||||
public int getHp() {
|
||||
return hp;
|
||||
}
|
||||
|
||||
public void setHp(int hp) {
|
||||
this.hp = hp;
|
||||
}
|
||||
|
||||
public int getMp() {
|
||||
return mp;
|
||||
}
|
||||
|
||||
public void setMp(int mp) {
|
||||
this.mp = mp;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public int removeAfter() {
|
||||
return removeAfter;
|
||||
}
|
||||
|
||||
public void setRemoveAfter(int removeAfter) {
|
||||
this.removeAfter = removeAfter;
|
||||
}
|
||||
|
||||
public int getDropPeriod() {
|
||||
return dropPeriod;
|
||||
}
|
||||
|
||||
public void setDropPeriod(int dropPeriod) {
|
||||
this.dropPeriod = dropPeriod;
|
||||
}
|
||||
|
||||
public void setBoss(boolean boss) {
|
||||
this.boss = boss;
|
||||
}
|
||||
|
||||
public boolean isBoss() {
|
||||
return boss;
|
||||
}
|
||||
|
||||
public void setFfaLoot(boolean ffaLoot) {
|
||||
this.ffaLoot = ffaLoot;
|
||||
}
|
||||
|
||||
public boolean isFfaLoot() {
|
||||
return ffaLoot;
|
||||
}
|
||||
|
||||
public void setAnimationTime(String name, int delay) {
|
||||
animationTimes.put(name, delay);
|
||||
}
|
||||
|
||||
public int getAnimationTime(String name) {
|
||||
Integer ret = animationTimes.get(name);
|
||||
if (ret == null) {
|
||||
return 500;
|
||||
}
|
||||
return ret.intValue();
|
||||
}
|
||||
|
||||
public boolean isMobile() {
|
||||
return animationTimes.containsKey("move") || animationTimes.containsKey("fly");
|
||||
}
|
||||
|
||||
public List<Integer> getRevives() {
|
||||
return revives;
|
||||
}
|
||||
|
||||
public void setRevives(List<Integer> revives) {
|
||||
this.revives = revives;
|
||||
}
|
||||
|
||||
public void setUndead(boolean undead) {
|
||||
this.undead = undead;
|
||||
}
|
||||
|
||||
public boolean isUndead() {
|
||||
return undead;
|
||||
}
|
||||
|
||||
public void setEffectiveness(Element e, ElementalEffectiveness ee) {
|
||||
resistance.put(e, ee);
|
||||
}
|
||||
|
||||
public ElementalEffectiveness getEffectiveness(Element e) {
|
||||
ElementalEffectiveness elementalEffectiveness = resistance.get(e);
|
||||
if (elementalEffectiveness == null) {
|
||||
return ElementalEffectiveness.NORMAL;
|
||||
} else {
|
||||
return elementalEffectiveness;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public byte getTagColor() {
|
||||
return tagColor;
|
||||
}
|
||||
|
||||
public void setTagColor(int tagColor) {
|
||||
this.tagColor = (byte) tagColor;
|
||||
}
|
||||
|
||||
public byte getTagBgColor() {
|
||||
return tagBgColor;
|
||||
}
|
||||
|
||||
public void setTagBgColor(int tagBgColor) {
|
||||
this.tagBgColor = (byte) tagBgColor;
|
||||
}
|
||||
|
||||
public void setSkills(List<Pair<Integer, Integer>> skills) {
|
||||
for (int i = this.skills.size(); i < skills.size(); i++) {
|
||||
this.skills.add(null);
|
||||
}
|
||||
|
||||
for (int i = 0; i < skills.size(); i++) {
|
||||
this.skills.set(i, skills.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public List<Pair<Integer, Integer>> getSkills() {
|
||||
return Collections.unmodifiableList(this.skills);
|
||||
}
|
||||
|
||||
public int getNoSkills() {
|
||||
return this.skills.size();
|
||||
}
|
||||
|
||||
public boolean hasSkill(int skillId, int level) {
|
||||
for (Pair<Integer, Integer> skill : skills) {
|
||||
if (skill.getLeft() == skillId && skill.getRight() == level) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setFirstAttack(boolean firstAttack) {
|
||||
this.firstAttack = firstAttack;
|
||||
}
|
||||
|
||||
public boolean isFirstAttack() {
|
||||
return firstAttack;
|
||||
}
|
||||
|
||||
public void setBuffToGive(int buff) {
|
||||
this.buffToGive = buff;
|
||||
}
|
||||
|
||||
public int getBuffToGive() {
|
||||
return buffToGive;
|
||||
}
|
||||
|
||||
void removeEffectiveness(Element e) {
|
||||
resistance.remove(e);
|
||||
}
|
||||
|
||||
public BanishInfo getBanishInfo() {
|
||||
return banish;
|
||||
}
|
||||
|
||||
public void setBanishInfo(BanishInfo banish) {
|
||||
this.banish = banish;
|
||||
}
|
||||
|
||||
public int getPADamage() {
|
||||
return PADamage;
|
||||
}
|
||||
|
||||
public void setPADamage(int PADamage) {
|
||||
this.PADamage = PADamage;
|
||||
}
|
||||
|
||||
public int getCP() {
|
||||
return cp;
|
||||
}
|
||||
|
||||
public void setCP(int cp) {
|
||||
this.cp = cp;
|
||||
}
|
||||
|
||||
public List<loseItem> loseItem() {
|
||||
return loseItem;
|
||||
}
|
||||
|
||||
public void addLoseItem(loseItem li) {
|
||||
if (loseItem == null) {
|
||||
loseItem = new LinkedList<loseItem>();
|
||||
}
|
||||
loseItem.add(li);
|
||||
}
|
||||
|
||||
public selfDestruction selfDestruction() {
|
||||
return selfDestruction;
|
||||
}
|
||||
|
||||
public void setSelfDestruction(selfDestruction sd) {
|
||||
this.selfDestruction = sd;
|
||||
}
|
||||
|
||||
public void setExplosiveReward(boolean isExplosiveReward) {
|
||||
this.isExplosiveReward = isExplosiveReward;
|
||||
}
|
||||
|
||||
public boolean isExplosiveReward() {
|
||||
return isExplosiveReward;
|
||||
}
|
||||
|
||||
public void setRemoveOnMiss(boolean removeOnMiss) {
|
||||
this.removeOnMiss = removeOnMiss;
|
||||
}
|
||||
|
||||
public boolean removeOnMiss() {
|
||||
return removeOnMiss;
|
||||
}
|
||||
|
||||
public void setCool(Pair<Integer, Integer> cool) {
|
||||
this.cool = cool;
|
||||
}
|
||||
|
||||
public Pair<Integer, Integer> getCool() {
|
||||
return cool;
|
||||
}
|
||||
|
||||
public int getPDDamage() {
|
||||
return PDDamage;
|
||||
}
|
||||
|
||||
public int getMADamage() {
|
||||
return MADamage;
|
||||
}
|
||||
|
||||
public int getMDDamage() {
|
||||
return MDDamage;
|
||||
}
|
||||
|
||||
public boolean isFriendly() {
|
||||
return friendly;
|
||||
}
|
||||
|
||||
public void setFriendly(boolean value) {
|
||||
this.friendly = value;
|
||||
}
|
||||
|
||||
public void setPDDamage(int PDDamage) {
|
||||
this.PDDamage = PDDamage;
|
||||
}
|
||||
|
||||
public void setMADamage(int MADamage) {
|
||||
this.MADamage = MADamage;
|
||||
}
|
||||
|
||||
public void setMDDamage(int MDDamage) {
|
||||
this.MDDamage = MDDamage;
|
||||
}
|
||||
|
||||
public int getFixedStance() {
|
||||
return this.fixedStance;
|
||||
}
|
||||
|
||||
public void setFixedStance(int stance) {
|
||||
this.fixedStance = stance;
|
||||
}
|
||||
|
||||
public MapleMonsterStats copy() {
|
||||
MapleMonsterStats copy = new MapleMonsterStats();
|
||||
try {
|
||||
FieldCopyUtil.setFields(this, copy);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (Exception ex) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
// FieldCopyUtil src: http://www.codesenior.com/en/tutorial/Java-Copy-Fields-From-One-Object-to-Another-Object-with-Reflection
|
||||
private static class FieldCopyUtil { // thanks to Codesenior dev team
|
||||
private static void setFields(Object from, Object to) {
|
||||
Field[] fields = from.getClass().getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
try {
|
||||
Field fieldFrom = from.getClass().getDeclaredField(field.getName());
|
||||
Object value = fieldFrom.get(from);
|
||||
to.getClass().getDeclaredField(field.getName()).set(to, value);
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/main/java/server/life/MapleNPC.java
Normal file
65
src/main/java/server/life/MapleNPC.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
import client.MapleClient;
|
||||
import server.MapleShopFactory;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
public class MapleNPC extends AbstractLoadedMapleLife {
|
||||
private MapleNPCStats stats;
|
||||
|
||||
public MapleNPC(int id, MapleNPCStats stats) {
|
||||
super(id);
|
||||
this.stats = stats;
|
||||
}
|
||||
|
||||
public boolean hasShop() {
|
||||
return MapleShopFactory.getInstance().getShopForNPC(getId()) != null;
|
||||
}
|
||||
|
||||
public void sendShop(MapleClient c) {
|
||||
MapleShopFactory.getInstance().getShopForNPC(getId()).sendShop(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(MapleClient client) {
|
||||
client.announce(MaplePacketCreator.spawnNPC(this));
|
||||
client.announce(MaplePacketCreator.spawnNPCRequestController(this, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyData(MapleClient client) {
|
||||
client.announce(MaplePacketCreator.removeNPCController(getObjectId()));
|
||||
client.announce(MaplePacketCreator.removeNPC(getObjectId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapleMapObjectType getType() {
|
||||
return MapleMapObjectType.NPC;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return stats.getName();
|
||||
}
|
||||
}
|
||||
42
src/main/java/server/life/MapleNPCStats.java
Normal file
42
src/main/java/server/life/MapleNPCStats.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
*/
|
||||
public class MapleNPCStats {
|
||||
private String name;
|
||||
|
||||
public MapleNPCStats(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
680
src/main/java/server/life/MaplePlayerNPC.java
Normal file
680
src/main/java/server/life/MaplePlayerNPC.java
Normal file
@@ -0,0 +1,680 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
import config.YamlConfig;
|
||||
import server.life.positioner.MaplePlayerNPCPositioner;
|
||||
import server.life.positioner.MaplePlayerNPCPodium;
|
||||
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.game.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, List<Integer>> availablePlayerNpcScriptIds = 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();
|
||||
|
||||
getRunningOverallRanks(con);
|
||||
getRunningWorldRanks(con);
|
||||
getRunningWorldJobRanks(con);
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
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().getWorldsSize();
|
||||
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 void fetchAvailableScriptIdsFromDb(byte branch, List<Integer> list) {
|
||||
try {
|
||||
int branchLen = (branch < 26) ? 100 : 400;
|
||||
int branchSid = 9900000 + (branch * 100);
|
||||
int nextBranchSid = branchSid + branchLen;
|
||||
Set<Integer> usedScriptIds = new HashSet<>();
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT scriptid FROM playernpcs WHERE scriptid >= ? AND scriptid < ? ORDER BY scriptid");
|
||||
ps.setInt(1, branchSid);
|
||||
ps.setInt(2, nextBranchSid);
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while(rs.next()) {
|
||||
usedScriptIds.add(rs.getInt(1));
|
||||
}
|
||||
|
||||
List<Integer> availables = new ArrayList<>(20);
|
||||
int j = 0;
|
||||
for(int i = branchSid; i < nextBranchSid; i++) {
|
||||
if(!usedScriptIds.contains(i)) {
|
||||
if (MaplePlayerNPCFactory.isExistentScriptid(i)) { // thanks Ark, Zein, geno, Ariel, JrCl0wn for noticing client crashes due to use of missing scriptids
|
||||
availables.add(i);
|
||||
j++;
|
||||
|
||||
if(j == 20) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break; // after this point no more scriptids expected...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
|
||||
for(int i = availables.size() - 1; i >= 0; i--) {
|
||||
list.add(availables.get(i));
|
||||
}
|
||||
} catch(SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNextScriptId(byte branch) {
|
||||
List<Integer> availablesBranch = availablePlayerNpcScriptIds.get(branch);
|
||||
|
||||
if(availablesBranch == null) {
|
||||
availablesBranch = new ArrayList<>(20);
|
||||
availablePlayerNpcScriptIds.put(branch, availablesBranch);
|
||||
}
|
||||
|
||||
if(availablesBranch.isEmpty()) {
|
||||
fetchAvailableScriptIdsFromDb(branch, availablesBranch);
|
||||
|
||||
if(availablesBranch.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return availablesBranch.remove(availablesBranch.size() - 1);
|
||||
}
|
||||
|
||||
private static MaplePlayerNPC createPlayerNPCInternal(MapleMap map, Point pos, MapleCharacter chr) {
|
||||
int mapId = map.getId();
|
||||
|
||||
if(!canSpawnPlayerNpc(chr.getName(), mapId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte branch = GameConstants.getHallOfFameBranch(chr.getJob(), mapId);
|
||||
|
||||
int scriptId = getNextScriptId(branch);
|
||||
if (scriptId == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(pos == null) {
|
||||
if(GameConstants.isPodiumHallOfFameMap(map.getId())) {
|
||||
pos = MaplePlayerNPCPodium.getNextPlayerNpcPosition(map);
|
||||
} else {
|
||||
pos = MaplePlayerNPCPositioner.getNextPlayerNpcPosition(map);
|
||||
}
|
||||
|
||||
if(pos == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if(YamlConfig.config.server.USE_DEBUG) System.out.println("GOT SID " + scriptId + " POS " + pos);
|
||||
|
||||
int worldId = chr.getWorld();
|
||||
int jobId = (chr.getJob().getId() / 100) * 100;
|
||||
|
||||
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) {
|
||||
if(chr == null) return false;
|
||||
|
||||
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) {
|
||||
if(chr == null) return;
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void multicastSpawnPlayerNPC(int mapid, int world) {
|
||||
World wserv = Server.getInstance().getWorld(world);
|
||||
if (wserv == null) return;
|
||||
|
||||
MapleClient c = new MapleClient(null, null, null); // mock client
|
||||
c.setWorld(world);
|
||||
c.setChannel(1);
|
||||
|
||||
for(MapleCharacter mc : wserv.loadAndGetAllCharactersView()) {
|
||||
mc.setClient(c);
|
||||
spawnPlayerNPC(mapid, mc);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeAllPlayerNPC() {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("SELECT DISTINCT world, map FROM playernpcs");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
int wsize = Server.getInstance().getWorldsSize();
|
||||
while(rs.next()) {
|
||||
int world = rs.getInt("world"), map = rs.getInt("map");
|
||||
if(world >= wsize) continue;
|
||||
|
||||
for (Channel channel : Server.getInstance().getChannelsFromWorld(world)) {
|
||||
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(pnpcObj);
|
||||
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();
|
||||
|
||||
ps = con.prepareStatement("DELETE FROM playernpcs_field");
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
|
||||
for(World w : Server.getInstance().getWorlds()) {
|
||||
w.resetPlayerNpcMapData();
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
140
src/main/java/server/life/MaplePlayerNPCFactory.java
Normal file
140
src/main/java/server/life/MaplePlayerNPCFactory.java
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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 java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
import net.server.Server;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class MaplePlayerNPCFactory {
|
||||
|
||||
private static MapleDataProvider npcData = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz"));
|
||||
|
||||
private static final Map<Integer, List<MaplePlayerNPC>> dnpcMaps = new HashMap<>();
|
||||
private static Integer runningDeveloperOid = 2147483000; // 647 slots, long enough
|
||||
|
||||
public static boolean isExistentScriptid(int scriptid) {
|
||||
return npcData.getData(scriptid + ".img") != null;
|
||||
}
|
||||
|
||||
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) {
|
||||
Server.getInstance().setAvailableDeveloperRoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadFactoryMetadata() {
|
||||
MapleDataProvider npc = npcData;
|
||||
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);
|
||||
}
|
||||
}
|
||||
77
src/main/java/server/life/MobAttackInfo.java
Normal file
77
src/main/java/server/life/MobAttackInfo.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Danny (Leifde)
|
||||
*/
|
||||
public class MobAttackInfo {
|
||||
private boolean isDeadlyAttack;
|
||||
private int mpBurn;
|
||||
private int diseaseSkill;
|
||||
private int diseaseLevel;
|
||||
private int mpCon;
|
||||
|
||||
public MobAttackInfo(int mobId, int attackId) {
|
||||
}
|
||||
|
||||
public void setDeadlyAttack(boolean isDeadlyAttack) {
|
||||
this.isDeadlyAttack = isDeadlyAttack;
|
||||
}
|
||||
|
||||
public boolean isDeadlyAttack() {
|
||||
return isDeadlyAttack;
|
||||
}
|
||||
|
||||
public void setMpBurn(int mpBurn) {
|
||||
this.mpBurn = mpBurn;
|
||||
}
|
||||
|
||||
public int getMpBurn() {
|
||||
return mpBurn;
|
||||
}
|
||||
|
||||
public void setDiseaseSkill(int diseaseSkill) {
|
||||
this.diseaseSkill = diseaseSkill;
|
||||
}
|
||||
|
||||
public int getDiseaseSkill() {
|
||||
return diseaseSkill;
|
||||
}
|
||||
|
||||
public void setDiseaseLevel(int diseaseLevel) {
|
||||
this.diseaseLevel = diseaseLevel;
|
||||
}
|
||||
|
||||
public int getDiseaseLevel() {
|
||||
return diseaseLevel;
|
||||
}
|
||||
|
||||
public void setMpCon(int mpCon) {
|
||||
this.mpCon = mpCon;
|
||||
}
|
||||
|
||||
public int getMpCon() {
|
||||
return mpCon;
|
||||
}
|
||||
}
|
||||
79
src/main/java/server/life/MobAttackInfoFactory.java
Normal file
79
src/main/java/server/life/MobAttackInfoFactory.java
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import tools.StringUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Danny (Leifde)
|
||||
*/
|
||||
public class MobAttackInfoFactory {
|
||||
private static Map<String, MobAttackInfo> mobAttacks = new HashMap<String, MobAttackInfo>();
|
||||
private static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Mob.wz"));
|
||||
|
||||
public static MobAttackInfo getMobAttackInfo(MapleMonster mob, int attack) {
|
||||
MobAttackInfo ret = mobAttacks.get(mob.getId() + "" + attack);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
synchronized (mobAttacks) {
|
||||
ret = mobAttacks.get(mob.getId() + "" + attack);
|
||||
if (ret == null) {
|
||||
MapleData mobData = dataSource.getData(StringUtil.getLeftPaddedStr(Integer.toString(mob.getId()) + ".img", '0', 11));
|
||||
if (mobData != null) {
|
||||
// MapleData infoData = mobData.getChildByPath("info");
|
||||
String linkedmob = MapleDataTool.getString("link", mobData, "");
|
||||
if (!linkedmob.equals("")) {
|
||||
mobData = dataSource.getData(StringUtil.getLeftPaddedStr(linkedmob + ".img", '0', 11));
|
||||
}
|
||||
MapleData attackData = mobData.getChildByPath("attack" + (attack + 1) + "/info");
|
||||
|
||||
if (attackData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MapleData deadlyAttack = attackData.getChildByPath("deadlyAttack");
|
||||
int mpBurn = MapleDataTool.getInt("mpBurn", attackData, 0);
|
||||
int disease = MapleDataTool.getInt("disease", attackData, 0);
|
||||
int level = MapleDataTool.getInt("level", attackData, 0);
|
||||
int mpCon = MapleDataTool.getInt("conMP", attackData, 0);
|
||||
ret = new MobAttackInfo(mob.getId(), attack);
|
||||
ret.setDeadlyAttack(deadlyAttack != null);
|
||||
ret.setMpBurn(mpBurn);
|
||||
ret.setDiseaseSkill(disease);
|
||||
ret.setDiseaseLevel(level);
|
||||
ret.setMpCon(mpCon);
|
||||
}
|
||||
mobAttacks.put(mob.getId() + "" + attack, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
424
src/main/java/server/life/MobSkill.java
Normal file
424
src/main/java/server/life/MobSkill.java
Normal file
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleDisease;
|
||||
import client.status.MonsterStatus;
|
||||
import constants.game.GameConstants;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import net.server.services.type.ChannelServices;
|
||||
import net.server.services.task.channel.OverallService;
|
||||
import tools.Randomizer;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MapleMapObject;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import server.maps.MapleMist;
|
||||
import tools.ArrayMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Danny (Leifde)
|
||||
*/
|
||||
public class MobSkill {
|
||||
|
||||
private int skillId, skillLevel, mpCon;
|
||||
private List<Integer> toSummon = new ArrayList<Integer>();
|
||||
private int spawnEffect, hp, x, y;
|
||||
private long duration, cooltime;
|
||||
private float prop;
|
||||
private Point lt, rb;
|
||||
private int limit;
|
||||
|
||||
public MobSkill(int skillId, int level) {
|
||||
this.skillId = skillId;
|
||||
this.skillLevel = level;
|
||||
}
|
||||
|
||||
public void setMpCon(int mpCon) {
|
||||
this.mpCon = mpCon;
|
||||
}
|
||||
|
||||
public void addSummons(List<Integer> toSummon) {
|
||||
for (Integer summon : toSummon) {
|
||||
this.toSummon.add(summon);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSpawnEffect(int spawnEffect) {
|
||||
this.spawnEffect = spawnEffect;
|
||||
}
|
||||
|
||||
public void setHp(int hp) {
|
||||
this.hp = hp;
|
||||
}
|
||||
|
||||
public void setX(int x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public void setY(int y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public void setDuration(long duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public void setCoolTime(long cooltime) {
|
||||
this.cooltime = cooltime;
|
||||
}
|
||||
|
||||
public void setProp(float prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public void setLtRb(Point lt, Point rb) {
|
||||
this.lt = lt;
|
||||
this.rb = rb;
|
||||
}
|
||||
|
||||
public void setLimit(int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public void applyDelayedEffect(final MapleCharacter player, final MapleMonster monster, final boolean skill, int animationTime) {
|
||||
Runnable toRun = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (monster.isAlive()) {
|
||||
applyEffect(player, monster, skill, null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OverallService service = (OverallService) monster.getMap().getChannelServer().getServiceAccess(ChannelServices.OVERALL);
|
||||
service.registerOverallAction(monster.getMap().getId(), toRun, animationTime);
|
||||
}
|
||||
|
||||
public void applyEffect(MapleCharacter player, MapleMonster monster, boolean skill, List<MapleCharacter> banishPlayers) {
|
||||
MapleDisease disease = null;
|
||||
Map<MonsterStatus, Integer> stats = new ArrayMap<MonsterStatus, Integer>();
|
||||
List<Integer> reflection = new LinkedList<Integer>();
|
||||
switch (skillId) {
|
||||
case 100:
|
||||
case 110:
|
||||
case 150:
|
||||
stats.put(MonsterStatus.WEAPON_ATTACK_UP, Integer.valueOf(x));
|
||||
break;
|
||||
case 101:
|
||||
case 111:
|
||||
case 151:
|
||||
stats.put(MonsterStatus.MAGIC_ATTACK_UP, Integer.valueOf(x));
|
||||
break;
|
||||
case 102:
|
||||
case 112:
|
||||
case 152:
|
||||
stats.put(MonsterStatus.WEAPON_DEFENSE_UP, Integer.valueOf(x));
|
||||
break;
|
||||
case 103:
|
||||
case 113:
|
||||
case 153:
|
||||
stats.put(MonsterStatus.MAGIC_DEFENSE_UP, Integer.valueOf(x));
|
||||
break;
|
||||
case 114:
|
||||
if (lt != null && rb != null && skill) {
|
||||
List<MapleMapObject> objects = getObjectsInRange(monster, MapleMapObjectType.MONSTER);
|
||||
final int hps = (getX() / 1000) * (int) (950 + 1050 * Math.random());
|
||||
for (MapleMapObject mons : objects) {
|
||||
((MapleMonster) mons).heal(hps, getY());
|
||||
}
|
||||
} else {
|
||||
monster.heal(getX(), getY());
|
||||
}
|
||||
break;
|
||||
case 120:
|
||||
disease = MapleDisease.SEAL;
|
||||
break;
|
||||
case 121:
|
||||
disease = MapleDisease.DARKNESS;
|
||||
break;
|
||||
case 122:
|
||||
disease = MapleDisease.WEAKEN;
|
||||
break;
|
||||
case 123:
|
||||
disease = MapleDisease.STUN;
|
||||
break;
|
||||
case 124:
|
||||
disease = MapleDisease.CURSE;
|
||||
break;
|
||||
case 125:
|
||||
disease = MapleDisease.POISON;
|
||||
break;
|
||||
case 126: // Slow
|
||||
disease = MapleDisease.SLOW;
|
||||
break;
|
||||
case 127:
|
||||
if (lt != null && rb != null && skill) {
|
||||
for (MapleCharacter character : getPlayersInRange(monster)) {
|
||||
character.dispel();
|
||||
}
|
||||
} else {
|
||||
player.dispel();
|
||||
}
|
||||
break;
|
||||
case 128: // Seduce
|
||||
disease = MapleDisease.SEDUCE;
|
||||
break;
|
||||
case 129: // Banish
|
||||
if (lt != null && rb != null && skill) {
|
||||
for (MapleCharacter chr : getPlayersInRange(monster)) {
|
||||
banishPlayers.add(chr);
|
||||
}
|
||||
} else {
|
||||
banishPlayers.add(player);
|
||||
}
|
||||
break;
|
||||
case 131: // Mist
|
||||
monster.getMap().spawnMist(new MapleMist(calculateBoundingBox(monster.getPosition()), monster, this), x * 100, false, false, false);
|
||||
break;
|
||||
case 132:
|
||||
disease = MapleDisease.CONFUSE;
|
||||
break;
|
||||
case 133: // zombify
|
||||
disease = MapleDisease.ZOMBIFY;
|
||||
break;
|
||||
case 140:
|
||||
if (makeChanceResult() && !monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) {
|
||||
stats.put(MonsterStatus.WEAPON_IMMUNITY, Integer.valueOf(x));
|
||||
}
|
||||
break;
|
||||
case 141:
|
||||
if (makeChanceResult() && !monster.isBuffed(MonsterStatus.WEAPON_IMMUNITY)) {
|
||||
stats.put(MonsterStatus.MAGIC_IMMUNITY, Integer.valueOf(x));
|
||||
}
|
||||
break;
|
||||
case 143: // Weapon Reflect
|
||||
stats.put(MonsterStatus.WEAPON_REFLECT, 10);
|
||||
stats.put(MonsterStatus.WEAPON_IMMUNITY, 10);
|
||||
reflection.add(x);
|
||||
break;
|
||||
case 144: // Magic Reflect
|
||||
stats.put(MonsterStatus.MAGIC_REFLECT, 10);
|
||||
stats.put(MonsterStatus.MAGIC_IMMUNITY, 10);
|
||||
reflection.add(x);
|
||||
break;
|
||||
case 145: // Weapon / Magic reflect
|
||||
stats.put(MonsterStatus.WEAPON_REFLECT, 10);
|
||||
stats.put(MonsterStatus.WEAPON_IMMUNITY, 10);
|
||||
stats.put(MonsterStatus.MAGIC_REFLECT, 10);
|
||||
stats.put(MonsterStatus.MAGIC_IMMUNITY, 10);
|
||||
reflection.add(x);
|
||||
break;
|
||||
case 154:
|
||||
stats.put(MonsterStatus.ACC, Integer.valueOf(x));
|
||||
break;
|
||||
case 155:
|
||||
stats.put(MonsterStatus.AVOID, Integer.valueOf(x));
|
||||
break;
|
||||
case 156:
|
||||
stats.put(MonsterStatus.SPEED, Integer.valueOf(x));
|
||||
break;
|
||||
case 200: // summon
|
||||
int skillLimit = this.getLimit();
|
||||
MapleMap map = monster.getMap();
|
||||
|
||||
if (GameConstants.isDojo(map.getId())) { // spawns in dojo should be unlimited
|
||||
skillLimit = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
if (map.getSpawnedMonstersOnMap() < 80) {
|
||||
List<Integer> summons = getSummons();
|
||||
int summonLimit = monster.countAvailableMobSummons(summons.size(), skillLimit);
|
||||
if (summonLimit >= 1) {
|
||||
boolean bossRushMap = GameConstants.isBossRush(map.getId());
|
||||
|
||||
Collections.shuffle(summons);
|
||||
for (Integer mobId : summons.subList(0, summonLimit)) {
|
||||
MapleMonster toSpawn = MapleLifeFactory.getMonster(mobId);
|
||||
if (toSpawn != null) {
|
||||
if (bossRushMap) {
|
||||
toSpawn.disableDrops(); // no littering on BRPQ pls
|
||||
}
|
||||
toSpawn.setPosition(monster.getPosition());
|
||||
int ypos, xpos;
|
||||
xpos = (int) monster.getPosition().getX();
|
||||
ypos = (int) monster.getPosition().getY();
|
||||
switch (mobId) {
|
||||
case 8500003: // Pap bomb high
|
||||
toSpawn.setFh((int) Math.ceil(Math.random() * 19.0));
|
||||
ypos = -590;
|
||||
break;
|
||||
case 8500004: // Pap bomb
|
||||
xpos = (int) (monster.getPosition().getX() + Randomizer.nextInt(1000) - 500);
|
||||
if (ypos != -590) {
|
||||
ypos = (int) monster.getPosition().getY();
|
||||
}
|
||||
break;
|
||||
case 8510100: //Pianus bomb
|
||||
if (Math.ceil(Math.random() * 5) == 1) {
|
||||
ypos = 78;
|
||||
xpos = (int) Randomizer.nextInt(5) + (Randomizer.nextInt(2) == 1 ? 180 : 0);
|
||||
} else {
|
||||
xpos = (int) (monster.getPosition().getX() + Randomizer.nextInt(1000) - 500);
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (map.getId()) {
|
||||
case 220080001: //Pap map
|
||||
if (xpos < -890) {
|
||||
xpos = (int) (Math.ceil(Math.random() * 150) - 890);
|
||||
} else if (xpos > 230) {
|
||||
xpos = (int) (230 - Math.ceil(Math.random() * 150));
|
||||
}
|
||||
break;
|
||||
case 230040420: // Pianus map
|
||||
if (xpos < -239) {
|
||||
xpos = (int) (Math.ceil(Math.random() * 150) - 239);
|
||||
} else if (xpos > 371) {
|
||||
xpos = (int) (371 - Math.ceil(Math.random() * 150));
|
||||
}
|
||||
break;
|
||||
}
|
||||
toSpawn.setPosition(new Point(xpos, ypos));
|
||||
if (toSpawn.getId() == 8500004) {
|
||||
map.spawnFakeMonster(toSpawn);
|
||||
} else {
|
||||
map.spawnMonsterWithEffect(toSpawn, getSpawnEffect(), toSpawn.getPosition());
|
||||
}
|
||||
monster.addSummonedMob(toSpawn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
System.out.println("Unhandled Mob skill: " + skillId);
|
||||
break;
|
||||
}
|
||||
if (stats.size() > 0) {
|
||||
if (lt != null && rb != null && skill) {
|
||||
for (MapleMapObject mons : getObjectsInRange(monster, MapleMapObjectType.MONSTER)) {
|
||||
((MapleMonster) mons).applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection);
|
||||
}
|
||||
} else {
|
||||
monster.applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection);
|
||||
}
|
||||
}
|
||||
if (disease != null) {
|
||||
if (lt != null && rb != null && skill) {
|
||||
int i = 0;
|
||||
for (MapleCharacter character : getPlayersInRange(monster)) {
|
||||
if (!character.hasActiveBuff(2321005)) { // holy shield
|
||||
if (disease.equals(MapleDisease.SEDUCE)) {
|
||||
if (i < 10) {
|
||||
character.giveDebuff(MapleDisease.SEDUCE, this);
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
character.giveDebuff(disease, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
player.giveDebuff(disease, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<MapleCharacter> getPlayersInRange(MapleMonster monster) {
|
||||
return monster.getMap().getPlayersInRange(calculateBoundingBox(monster.getPosition()));
|
||||
}
|
||||
|
||||
public int getSkillId() {
|
||||
return skillId;
|
||||
}
|
||||
|
||||
public int getSkillLevel() {
|
||||
return skillLevel;
|
||||
}
|
||||
|
||||
public int getMpCon() {
|
||||
return mpCon;
|
||||
}
|
||||
|
||||
public List<Integer> getSummons() {
|
||||
return new ArrayList<>(toSummon);
|
||||
}
|
||||
|
||||
public int getSpawnEffect() {
|
||||
return spawnEffect;
|
||||
}
|
||||
|
||||
public int getHP() {
|
||||
return hp;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public long getCoolTime() {
|
||||
return cooltime;
|
||||
}
|
||||
|
||||
public Point getLt() {
|
||||
return lt;
|
||||
}
|
||||
|
||||
public Point getRb() {
|
||||
return rb;
|
||||
}
|
||||
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public boolean makeChanceResult() {
|
||||
return prop == 1.0 || Math.random() < prop;
|
||||
}
|
||||
|
||||
private Rectangle calculateBoundingBox(Point posFrom) {
|
||||
Point mylt = new Point(lt.x + posFrom.x, lt.y + posFrom.y);
|
||||
Point myrb = new Point(rb.x + posFrom.x, rb.y + posFrom.y);
|
||||
Rectangle bounds = new Rectangle(mylt.x, mylt.y, myrb.x - mylt.x, myrb.y - mylt.y);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
private List<MapleMapObject> getObjectsInRange(MapleMonster monster, MapleMapObjectType objectType) {
|
||||
return monster.getMap().getMapObjectsInBox(calculateBoundingBox(monster.getPosition()), Collections.singletonList(objectType));
|
||||
}
|
||||
}
|
||||
116
src/main/java/server/life/MobSkillFactory.java
Normal file
116
src/main/java/server/life/MobSkillFactory.java
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReadLock;
|
||||
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
|
||||
import net.server.audit.locks.MonitoredWriteLock;
|
||||
import net.server.audit.locks.factory.MonitoredReadLockFactory;
|
||||
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Danny (Leifde)
|
||||
*/
|
||||
public class MobSkillFactory {
|
||||
|
||||
private static Map<String, MobSkill> mobSkills = new HashMap<String, MobSkill>();
|
||||
private final static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Skill.wz"));
|
||||
private static MapleData skillRoot = dataSource.getData("MobSkill.img");
|
||||
private final static MonitoredReentrantReadWriteLock dataLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MOBSKILL_FACTORY);
|
||||
private final static MonitoredReadLock rL = MonitoredReadLockFactory.createLock(dataLock);
|
||||
private final static MonitoredWriteLock wL = MonitoredWriteLockFactory.createLock(dataLock);
|
||||
|
||||
public static MobSkill getMobSkill(final int skillId, final int level) {
|
||||
final String key = skillId + "" + level;
|
||||
rL.lock();
|
||||
try {
|
||||
MobSkill ret = mobSkills.get(key);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
} finally {
|
||||
rL.unlock();
|
||||
}
|
||||
wL.lock();
|
||||
try {
|
||||
MobSkill ret;
|
||||
ret = mobSkills.get(key);
|
||||
if (ret == null) {
|
||||
MapleData skillData = skillRoot.getChildByPath(skillId + "/level/" + level);
|
||||
if (skillData != null) {
|
||||
int mpCon = MapleDataTool.getInt(skillData.getChildByPath("mpCon"), 0);
|
||||
List<Integer> toSummon = new ArrayList<Integer>();
|
||||
for (int i = 0; i > -1; i++) {
|
||||
if (skillData.getChildByPath(String.valueOf(i)) == null) {
|
||||
break;
|
||||
}
|
||||
toSummon.add(Integer.valueOf(MapleDataTool.getInt(skillData.getChildByPath(String.valueOf(i)), 0)));
|
||||
}
|
||||
int effect = MapleDataTool.getInt("summonEffect", skillData, 0);
|
||||
int hp = MapleDataTool.getInt("hp", skillData, 100);
|
||||
int x = MapleDataTool.getInt("x", skillData, 1);
|
||||
int y = MapleDataTool.getInt("y", skillData, 1);
|
||||
long duration = MapleDataTool.getInt("time", skillData, 0) * 1000;
|
||||
long cooltime = MapleDataTool.getInt("interval", skillData, 0) * 1000;
|
||||
int iprop = MapleDataTool.getInt("prop", skillData, 100);
|
||||
float prop = iprop / 100;
|
||||
int limit = MapleDataTool.getInt("limit", skillData, 0);
|
||||
MapleData ltd = skillData.getChildByPath("lt");
|
||||
Point lt = null;
|
||||
Point rb = null;
|
||||
if (ltd != null) {
|
||||
lt = (Point) ltd.getData();
|
||||
rb = (Point) skillData.getChildByPath("rb").getData();
|
||||
}
|
||||
ret = new MobSkill(skillId, level);
|
||||
ret.addSummons(toSummon);
|
||||
ret.setCoolTime(cooltime);
|
||||
ret.setDuration(duration);
|
||||
ret.setHp(hp);
|
||||
ret.setMpCon(mpCon);
|
||||
ret.setSpawnEffect(effect);
|
||||
ret.setX(x);
|
||||
ret.setY(y);
|
||||
ret.setProp(prop);
|
||||
ret.setLimit(limit);
|
||||
ret.setLtRb(lt, rb);
|
||||
}
|
||||
mobSkills.put(skillId + "" + level, ret);
|
||||
}
|
||||
return ret;
|
||||
} finally {
|
||||
wL.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/main/java/server/life/MonsterDropEntry.java
Normal file
38
src/main/java/server/life/MonsterDropEntry.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LightPepsi
|
||||
*/
|
||||
|
||||
public class MonsterDropEntry {
|
||||
public MonsterDropEntry(int itemId, int chance, int Minimum, int Maximum, short questid) {
|
||||
this.itemId = itemId;
|
||||
this.chance = chance;
|
||||
this.questid = questid;
|
||||
this.Minimum = Minimum;
|
||||
this.Maximum = Maximum;
|
||||
}
|
||||
public short questid;
|
||||
public int itemId, chance, Minimum, Maximum;
|
||||
}
|
||||
38
src/main/java/server/life/MonsterGlobalDropEntry.java
Normal file
38
src/main/java/server/life/MonsterGlobalDropEntry.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LightPepsi
|
||||
*/
|
||||
public class MonsterGlobalDropEntry {
|
||||
public MonsterGlobalDropEntry(int itemId, int chance, int continent, int Minimum, int Maximum, short questid) {
|
||||
this.itemId = itemId;
|
||||
this.chance = chance;
|
||||
this.questid = questid;
|
||||
this.continentid = continent;
|
||||
this.Minimum = Minimum;
|
||||
this.Maximum = Maximum;
|
||||
}
|
||||
public int itemId, chance, Minimum, Maximum, continentid;
|
||||
public short questid;
|
||||
}
|
||||
9
src/main/java/server/life/MonsterListener.java
Normal file
9
src/main/java/server/life/MonsterListener.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package server.life;
|
||||
import client.MapleCharacter;
|
||||
|
||||
public interface MonsterListener {
|
||||
|
||||
public void monsterKilled(int aniTime);
|
||||
public void monsterDamaged(MapleCharacter from, int trueDmg);
|
||||
public void monsterHealed(int trueHeal);
|
||||
}
|
||||
64
src/main/java/server/life/OverrideMonsterStats.java
Normal file
64
src/main/java/server/life/OverrideMonsterStats.java
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
public class OverrideMonsterStats {
|
||||
|
||||
public int hp;
|
||||
public int exp, mp;
|
||||
|
||||
public OverrideMonsterStats() {
|
||||
hp = 1;
|
||||
exp = 0;
|
||||
mp = 0;
|
||||
}
|
||||
|
||||
public OverrideMonsterStats(int hp, int mp, int exp, boolean change) {
|
||||
this.hp = /*change ? (hp * 3L / 2L) : */ hp;
|
||||
this.mp = mp;
|
||||
this.exp = exp;
|
||||
}
|
||||
|
||||
public OverrideMonsterStats(int hp, int mp, int exp) {
|
||||
this(hp, mp, exp, true);
|
||||
}
|
||||
|
||||
public int getExp() {
|
||||
return exp;
|
||||
}
|
||||
|
||||
public void setOExp(int exp) {
|
||||
this.exp = exp;
|
||||
}
|
||||
|
||||
public int getHp() {
|
||||
return hp;
|
||||
}
|
||||
|
||||
public void setOHp(int hp) {
|
||||
this.hp = hp;
|
||||
}
|
||||
|
||||
public int getMp() {
|
||||
return mp;
|
||||
}
|
||||
|
||||
public void setOMp(int mp) {
|
||||
this.mp = mp;
|
||||
}
|
||||
}
|
||||
130
src/main/java/server/life/SpawnPoint.java
Normal file
130
src/main/java/server/life/SpawnPoint.java
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import java.awt.Point;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import net.server.Server;
|
||||
|
||||
public class SpawnPoint {
|
||||
private int monster, mobTime, team, fh, f;
|
||||
private Point pos;
|
||||
private long nextPossibleSpawn;
|
||||
private int mobInterval = 5000;
|
||||
private AtomicInteger spawnedMonsters = new AtomicInteger(0);
|
||||
private boolean immobile, denySpawn = false;
|
||||
|
||||
public SpawnPoint(final MapleMonster monster, Point pos, boolean immobile, int mobTime, int mobInterval, int team) {
|
||||
this.monster = monster.getId();
|
||||
this.pos = new Point(pos);
|
||||
this.mobTime = mobTime;
|
||||
this.team = team;
|
||||
this.fh = monster.getFh();
|
||||
this.f = monster.getF();
|
||||
this.immobile = immobile;
|
||||
this.mobInterval = mobInterval;
|
||||
this.nextPossibleSpawn = Server.getInstance().getCurrentTime();
|
||||
}
|
||||
|
||||
public int getSpawned() {
|
||||
return spawnedMonsters.intValue();
|
||||
}
|
||||
|
||||
public void setDenySpawn(boolean val) {
|
||||
denySpawn = val;
|
||||
}
|
||||
|
||||
public boolean getDenySpawn() {
|
||||
return denySpawn;
|
||||
}
|
||||
|
||||
public boolean shouldSpawn() {
|
||||
if (denySpawn || mobTime < 0 || spawnedMonsters.get() > 0) {
|
||||
return false;
|
||||
}
|
||||
return nextPossibleSpawn <= Server.getInstance().getCurrentTime();
|
||||
}
|
||||
|
||||
public boolean shouldForceSpawn() {
|
||||
if (mobTime < 0 || spawnedMonsters.get() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public MapleMonster getMonster() {
|
||||
MapleMonster mob = new MapleMonster(MapleLifeFactory.getMonster(monster));
|
||||
mob.setPosition(new Point(pos));
|
||||
mob.setTeam(team);
|
||||
mob.setFh(fh);
|
||||
mob.setF(f);
|
||||
spawnedMonsters.incrementAndGet();
|
||||
mob.addListener(new MonsterListener() {
|
||||
@Override
|
||||
public void monsterKilled(int aniTime) {
|
||||
nextPossibleSpawn = Server.getInstance().getCurrentTime();
|
||||
if (mobTime > 0) {
|
||||
nextPossibleSpawn += mobTime * 1000;
|
||||
} else {
|
||||
nextPossibleSpawn += aniTime;
|
||||
}
|
||||
spawnedMonsters.decrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void monsterDamaged(MapleCharacter from, int trueDmg) {}
|
||||
|
||||
@Override
|
||||
public void monsterHealed(int trueHeal) {}
|
||||
});
|
||||
if (mobTime == 0) {
|
||||
nextPossibleSpawn = Server.getInstance().getCurrentTime() + mobInterval;
|
||||
}
|
||||
return mob;
|
||||
}
|
||||
|
||||
public int getMonsterId() {
|
||||
return monster;
|
||||
}
|
||||
|
||||
public Point getPosition() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public final int getF() {
|
||||
return f;
|
||||
}
|
||||
|
||||
public final int getFh() {
|
||||
return fh;
|
||||
}
|
||||
|
||||
public int getMobTime() {
|
||||
return mobTime;
|
||||
}
|
||||
|
||||
public int getTeam() {
|
||||
return team;
|
||||
}
|
||||
}
|
||||
155
src/main/java/server/life/positioner/MaplePlayerNPCPodium.java
Normal file
155
src/main/java/server/life/positioner/MaplePlayerNPCPodium.java
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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.positioner;
|
||||
|
||||
import config.YamlConfig;
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
import server.life.MaplePlayerNPC;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MapleMapObject;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
*
|
||||
* Note: the podium uses getGroundBelow that in its turn uses inputted posY minus 7.
|
||||
* Podium system will implement increase-by-7 to negate that behaviour.
|
||||
*/
|
||||
public class MaplePlayerNPCPodium {
|
||||
private static int getPlatformPosX(int platform) {
|
||||
switch(platform) {
|
||||
case 0:
|
||||
return -50;
|
||||
|
||||
case 1:
|
||||
return -170;
|
||||
|
||||
default:
|
||||
return 70;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getPlatformPosY(int platform) {
|
||||
switch(platform) {
|
||||
case 0:
|
||||
return -47;
|
||||
|
||||
default:
|
||||
return 40;
|
||||
}
|
||||
}
|
||||
|
||||
private static Point calcNextPos(int rank, int step) {
|
||||
int podiumPlatform = rank / step;
|
||||
int relativePos = (rank % step) + 1;
|
||||
|
||||
Point pos = new Point(getPlatformPosX(podiumPlatform) + ((100 * relativePos) / (step + 1)), getPlatformPosY(podiumPlatform));
|
||||
return pos;
|
||||
}
|
||||
|
||||
private static Point rearrangePlayerNpcs(MapleMap map, int newStep, List<MaplePlayerNPC> pnpcs) {
|
||||
int i = 0;
|
||||
for(MaplePlayerNPC pn : pnpcs) {
|
||||
pn.updatePlayerNPCPosition(map, calcNextPos(i, newStep));
|
||||
i++;
|
||||
}
|
||||
|
||||
return calcNextPos(i, newStep);
|
||||
}
|
||||
|
||||
private static Point reorganizePlayerNpcs(MapleMap map, int newStep, List<MapleMapObject> mmoList) {
|
||||
if(!mmoList.isEmpty()) {
|
||||
if(YamlConfig.config.server.USE_DEBUG) System.out.println("Reorganizing pnpc map, step " + newStep);
|
||||
|
||||
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(map.getWorld())) {
|
||||
MapleMap m = ch.getMapFactory().getMap(map.getId());
|
||||
|
||||
for(MaplePlayerNPC pn : playerNpcs) {
|
||||
m.removeMapObject(pn);
|
||||
m.broadcastMessage(MaplePacketCreator.removeNPCController(pn.getObjectId()));
|
||||
m.broadcastMessage(MaplePacketCreator.removePlayerNPC(pn.getObjectId()));
|
||||
}
|
||||
}
|
||||
|
||||
Point ret = rearrangePlayerNpcs(map, newStep, playerNpcs);
|
||||
|
||||
for(Channel ch : Server.getInstance().getChannelsFromWorld(map.getWorld())) {
|
||||
MapleMap m = ch.getMapFactory().getMap(map.getId());
|
||||
|
||||
for(MaplePlayerNPC pn : playerNpcs) {
|
||||
m.addPlayerNPCMapObject(pn);
|
||||
m.broadcastMessage(MaplePacketCreator.spawnPlayerNPC(pn));
|
||||
m.broadcastMessage(MaplePacketCreator.getPlayerNPC(pn));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int encodePodiumData(int podiumStep, int podiumCount) {
|
||||
return (podiumCount * (1 << 5)) + podiumStep;
|
||||
}
|
||||
|
||||
private static Point getNextPlayerNpcPosition(MapleMap map, int podiumData) { // automated playernpc position thanks to Ronan
|
||||
int podiumStep = podiumData % (1 << 5), podiumCount = (podiumData / (1 << 5));
|
||||
|
||||
if(podiumCount >= 3 * podiumStep) {
|
||||
if(podiumStep >= YamlConfig.config.server.PLAYERNPC_AREA_STEPS) return null;
|
||||
|
||||
List<MapleMapObject> mmoList = map.getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.PLAYER_NPC));
|
||||
map.getWorldServer().setPlayerNpcMapPodiumData(map.getId(), encodePodiumData(podiumStep + 1, podiumCount + 1));
|
||||
return reorganizePlayerNpcs(map, podiumStep + 1, mmoList);
|
||||
} else {
|
||||
map.getWorldServer().setPlayerNpcMapPodiumData(map.getId(), encodePodiumData(podiumStep, podiumCount + 1));
|
||||
return calcNextPos(podiumCount, podiumStep);
|
||||
}
|
||||
}
|
||||
|
||||
public static Point getNextPlayerNpcPosition(MapleMap map) {
|
||||
Point pos = getNextPlayerNpcPosition(map, map.getWorldServer().getPlayerNpcMapPodiumData(map.getId()));
|
||||
if(pos == null) return null;
|
||||
|
||||
return map.getGroundBelow(pos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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.positioner;
|
||||
|
||||
import config.YamlConfig;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
import server.life.MaplePlayerNPC;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MapleMapObject;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
*
|
||||
*/
|
||||
public class MaplePlayerNPCPositioner {
|
||||
|
||||
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 YamlConfig.config.server.PLAYERNPC_AREA_X / (newStep + 1);
|
||||
}
|
||||
|
||||
private static int calcDy(int newStep) {
|
||||
return (YamlConfig.config.server.PLAYERNPC_AREA_Y / 2) + (YamlConfig.config.server.PLAYERNPC_AREA_Y / (1 << (newStep + 1)));
|
||||
}
|
||||
|
||||
private static List<Point> rearrangePlayerNpcPositions(MapleMap map, int newStep, int pnpcsSize) {
|
||||
Rectangle mapArea = map.getMapArea();
|
||||
|
||||
int leftPx = mapArea.x + YamlConfig.config.server.PLAYERNPC_INITIAL_X, px, py = mapArea.y + YamlConfig.config.server.PLAYERNPC_INITIAL_Y;
|
||||
int outx = mapArea.x + mapArea.width - YamlConfig.config.server.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 = map.getPointBelow(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 static Point rearrangePlayerNpcs(MapleMap map, int newStep, List<MaplePlayerNPC> pnpcs) {
|
||||
Rectangle mapArea = map.getMapArea();
|
||||
|
||||
int leftPx = mapArea.x + YamlConfig.config.server.PLAYERNPC_INITIAL_X, px, py = mapArea.y + YamlConfig.config.server.PLAYERNPC_INITIAL_Y;
|
||||
int outx = mapArea.x + mapArea.width - YamlConfig.config.server.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 = map.getPointBelow(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(map, searchPos);
|
||||
otherPlayerNpcs.add(searchPos);
|
||||
}
|
||||
}
|
||||
|
||||
px += cx;
|
||||
}
|
||||
|
||||
py += cy;
|
||||
}
|
||||
|
||||
return null; // this area should not be reached under any scenario
|
||||
}
|
||||
|
||||
private static Point reorganizePlayerNpcs(MapleMap map, int newStep, List<MapleMapObject> mmoList) {
|
||||
if(!mmoList.isEmpty()) {
|
||||
if(YamlConfig.config.server.USE_DEBUG) System.out.println("Reorganizing pnpc map, step " + newStep);
|
||||
|
||||
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(map.getWorld())) {
|
||||
MapleMap m = ch.getMapFactory().getMap(map.getId());
|
||||
|
||||
for(MaplePlayerNPC pn : playerNpcs) {
|
||||
m.removeMapObject(pn);
|
||||
m.broadcastMessage(MaplePacketCreator.removeNPCController(pn.getObjectId()));
|
||||
m.broadcastMessage(MaplePacketCreator.removePlayerNPC(pn.getObjectId()));
|
||||
}
|
||||
}
|
||||
|
||||
Point ret = rearrangePlayerNpcs(map, newStep, playerNpcs);
|
||||
|
||||
for(Channel ch : Server.getInstance().getChannelsFromWorld(map.getWorld())) {
|
||||
MapleMap m = ch.getMapFactory().getMap(map.getId());
|
||||
|
||||
for(MaplePlayerNPC pn : playerNpcs) {
|
||||
m.addPlayerNPCMapObject(pn);
|
||||
m.broadcastMessage(MaplePacketCreator.spawnPlayerNPC(pn));
|
||||
m.broadcastMessage(MaplePacketCreator.getPlayerNPC(pn));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Point getNextPlayerNpcPosition(MapleMap map, int initStep) { // automated playernpc position thanks to Ronan
|
||||
List<MapleMapObject> mmoList = map.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);
|
||||
Rectangle mapArea = map.getMapArea();
|
||||
int outx = mapArea.x + mapArea.width - YamlConfig.config.server.PLAYERNPC_INITIAL_X, outy = mapArea.y + mapArea.height;
|
||||
boolean reorganize = false;
|
||||
|
||||
int i = initStep;
|
||||
while(i < YamlConfig.config.server.PLAYERNPC_AREA_STEPS) {
|
||||
int leftPx = mapArea.x + YamlConfig.config.server.PLAYERNPC_INITIAL_X, px, py = mapArea.y + YamlConfig.config.server.PLAYERNPC_INITIAL_Y;
|
||||
|
||||
while(py < outy) {
|
||||
px = leftPx;
|
||||
|
||||
while(px < outx) {
|
||||
Point searchPos = map.getPointBelow(new Point(px, py));
|
||||
if(searchPos != null) {
|
||||
if(!isPlayerNpcNearby(otherPlayerNpcs, searchPos, cx, cy)) {
|
||||
if(i > initStep) {
|
||||
map.getWorldServer().setPlayerNpcMapStep(map.getId(), i);
|
||||
}
|
||||
|
||||
if(reorganize && YamlConfig.config.server.PLAYERNPC_ORGANIZE_AREA) {
|
||||
return reorganizePlayerNpcs(map, i, mmoList);
|
||||
}
|
||||
|
||||
return searchPos;
|
||||
}
|
||||
}
|
||||
|
||||
px += cx;
|
||||
}
|
||||
|
||||
py += cy;
|
||||
}
|
||||
|
||||
reorganize = true;
|
||||
i++;
|
||||
|
||||
cx = calcDx(i);
|
||||
cy = calcDy(i);
|
||||
if(YamlConfig.config.server.PLAYERNPC_ORGANIZE_AREA) {
|
||||
otherPlayerNpcs = rearrangePlayerNpcPositions(map, i, mmoList.size());
|
||||
}
|
||||
}
|
||||
|
||||
if(i > initStep) {
|
||||
map.getWorldServer().setPlayerNpcMapStep(map.getId(), YamlConfig.config.server.PLAYERNPC_AREA_STEPS - 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Point getNextPlayerNpcPosition(MapleMap map) {
|
||||
return getNextPlayerNpcPosition(map, map.getWorldServer().getPlayerNpcMapStep(map.getId()));
|
||||
}
|
||||
}
|
||||
56
src/main/java/server/loot/MapleLootInventory.java
Normal file
56
src/main/java/server/loot/MapleLootInventory.java
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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.loot;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventoryType;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleLootInventory {
|
||||
Map<Integer, Integer> items = new HashMap<>(50);
|
||||
|
||||
public MapleLootInventory(MapleCharacter from) {
|
||||
for (MapleInventoryType values : MapleInventoryType.values()) {
|
||||
|
||||
for(Item it : from.getInventory(values).list()) {
|
||||
Integer itemQty = items.get(it.getItemId());
|
||||
|
||||
if(itemQty == null) {
|
||||
items.put(it.getItemId(), (int) it.getQuantity());
|
||||
} else {
|
||||
items.put(it.getItemId(), itemQty + it.getQuantity());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int hasItem(int itemid, int quantity) {
|
||||
Integer itemQty = items.get(itemid);
|
||||
return itemQty == null ? 0 : itemQty >= quantity ? 2 : itemQty > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
}
|
||||
97
src/main/java/server/loot/MapleLootManager.java
Normal file
97
src/main/java/server/loot/MapleLootManager.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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.loot;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
//import server.MapleItemInformationProvider;
|
||||
import server.life.MapleMonsterInformationProvider;
|
||||
import server.life.MonsterDropEntry;
|
||||
import server.quest.MapleQuest;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleLootManager {
|
||||
|
||||
private static boolean isRelevantDrop(MonsterDropEntry dropEntry, List<MapleCharacter> players, List<MapleLootInventory> playersInv) {
|
||||
int qStartAmount = 0, qCompleteAmount = 0;
|
||||
MapleQuest quest = MapleQuest.getInstance(dropEntry.questid);
|
||||
if (quest != null) {
|
||||
qStartAmount = quest.getStartItemAmountNeeded(dropEntry.itemId);
|
||||
qCompleteAmount = quest.getCompleteItemAmountNeeded(dropEntry.itemId);
|
||||
}
|
||||
|
||||
//boolean restricted = MapleItemInformationProvider.getInstance().isPickupRestricted(dropEntry.itemId);
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
MapleLootInventory chrInv = playersInv.get(i);
|
||||
|
||||
if (dropEntry.questid > 0) {
|
||||
int qItemAmount, chrQuestStatus = players.get(i).getQuestStatus(dropEntry.questid);
|
||||
if (chrQuestStatus == 0) {
|
||||
qItemAmount = qStartAmount;
|
||||
} else if (chrQuestStatus != 1) {
|
||||
continue;
|
||||
} else {
|
||||
qItemAmount = qCompleteAmount;
|
||||
}
|
||||
|
||||
// thanks kvmba for noticing quest items with no required amount failing to be detected as such
|
||||
|
||||
int qItemStatus = chrInv.hasItem(dropEntry.itemId, qItemAmount);
|
||||
if (qItemStatus == 2) {
|
||||
continue;
|
||||
} /*else if (restricted && qItemStatus == 1) { // one-of-a-kind loots should be available everytime, thanks onechord for noticing
|
||||
continue;
|
||||
}*/
|
||||
} /*else if (restricted && chrInv.hasItem(dropEntry.itemId, 1) > 0) { // thanks Conrad, Legalize for noticing eligible loots not being available to drop for non-killer parties
|
||||
continue;
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<MonsterDropEntry> retrieveRelevantDrops(int monsterId, List<MapleCharacter> players) {
|
||||
List<MonsterDropEntry> loots = MapleMonsterInformationProvider.getInstance().retrieveEffectiveDrop(monsterId);
|
||||
if(loots.isEmpty()) return loots;
|
||||
|
||||
List<MapleLootInventory> playersInv = new LinkedList<>();
|
||||
for(MapleCharacter chr : players) {
|
||||
MapleLootInventory lootInv = new MapleLootInventory(chr);
|
||||
playersInv.add(lootInv);
|
||||
}
|
||||
|
||||
List<MonsterDropEntry> effectiveLoot = new LinkedList<>();
|
||||
for(MonsterDropEntry mde : loots) {
|
||||
if(isRelevantDrop(mde, players, playersInv)) {
|
||||
effectiveLoot.add(mde);
|
||||
}
|
||||
}
|
||||
|
||||
return effectiveLoot;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
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.util.Arrays;
|
||||
|
||||
import tools.data.input.ByteArrayByteStream;
|
||||
import tools.data.input.GenericSeekableLittleEndianAccessor;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
import tools.data.output.MaplePacketLittleEndianWriter;
|
||||
|
||||
public abstract class AbstractAnimatedMapleMapObject extends AbstractMapleMapObject implements AnimatedMapleMapObject {
|
||||
|
||||
static {
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter((int) getIdleMovementDataLength());
|
||||
mplew.write(1); //movement command count
|
||||
mplew.write(0);
|
||||
mplew.writeShort(-1); //x
|
||||
mplew.writeShort(-1); //y
|
||||
mplew.writeShort(0); //xwobble
|
||||
mplew.writeShort(0); //ywobble
|
||||
mplew.writeShort(0); //fh
|
||||
mplew.write(-1); //stance
|
||||
mplew.writeShort(0); //duration
|
||||
idleMovementPacketData = mplew.getPacket();
|
||||
}
|
||||
|
||||
private static final byte[] idleMovementPacketData;
|
||||
|
||||
private int stance;
|
||||
|
||||
@Override
|
||||
public int getStance() {
|
||||
return stance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStance(int stance) {
|
||||
this.stance = stance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFacingLeft() {
|
||||
return Math.abs(stance) % 2 == 1;
|
||||
}
|
||||
|
||||
public SeekableLittleEndianAccessor getIdleMovement() {
|
||||
byte[] movementData = Arrays.copyOf(idleMovementPacketData, idleMovementPacketData.length);
|
||||
//seems wasteful to create a whole packet writer when only a few values are changed
|
||||
int x = getPosition().x;
|
||||
int y = getPosition().y;
|
||||
movementData[2] = (byte) (x & 0xFF); //x
|
||||
movementData[3] = (byte) (x >> 8 & 0xFF);
|
||||
movementData[4] = (byte) (y & 0xFF); //y
|
||||
movementData[5] = (byte) (y >> 8 & 0xFF);
|
||||
movementData[12] = (byte) (getStance() & 0xFF);
|
||||
return new GenericSeekableLittleEndianAccessor(new ByteArrayByteStream(movementData));
|
||||
}
|
||||
|
||||
public static long getIdleMovementDataLength() {
|
||||
return 15;
|
||||
}
|
||||
}
|
||||
57
src/main/java/server/maps/AbstractMapleMapObject.java
Normal file
57
src/main/java/server/maps/AbstractMapleMapObject.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
public abstract class AbstractMapleMapObject implements MapleMapObject {
|
||||
private Point position = new Point();
|
||||
private int objectId;
|
||||
|
||||
@Override
|
||||
public abstract MapleMapObjectType getType();
|
||||
|
||||
@Override
|
||||
public Point getPosition() {
|
||||
return new Point(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(Point position) {
|
||||
this.position.move(position.x, position.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getObjectId() {
|
||||
return objectId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObjectId(int id) {
|
||||
this.objectId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nullifyPosition() {
|
||||
this.position = null;
|
||||
}
|
||||
}
|
||||
28
src/main/java/server/maps/AnimatedMapleMapObject.java
Normal file
28
src/main/java/server/maps/AnimatedMapleMapObject.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
public interface AnimatedMapleMapObject extends MapleMapObject {
|
||||
int getStance();
|
||||
void setStance(int stance);
|
||||
boolean isFacingLeft();
|
||||
}
|
||||
69
src/main/java/server/maps/FieldLimit.java
Normal file
69
src/main/java/server/maps/FieldLimit.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
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 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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author AngelSL
|
||||
*/
|
||||
public enum FieldLimit {
|
||||
JUMP(0x01),
|
||||
MOVEMENTSKILLS(0x02),
|
||||
SUMMON(0x04),
|
||||
DOOR(0x08),
|
||||
CANNOTMIGRATE(0x10), //change channel, town portal scroll, access cash shop, etc etc
|
||||
//NO_NOTES(0x20),
|
||||
CANNOTVIPROCK(0x40),
|
||||
CANNOTMINIGAME(0x80),
|
||||
//SPECIFIC_PORTAL_SCROLL_LIMIT(0x100), // APQ and a couple quest maps have this
|
||||
CANNOTUSEMOUNTS(0x200),
|
||||
//STAT_CHANGE_ITEM_CONSUME_LIMIT(0x400), // Monster carnival?
|
||||
//PARTY_BOSS_CHANGE_LIMIT(0x800), // Monster carnival?
|
||||
CANNOTUSEPOTION(0x1000),
|
||||
//WEDDING_INVITATION_LIMIT(0x2000), // No notes
|
||||
//CASH_WEATHER_CONSUME_LIMIT(0x4000),
|
||||
//NO_PET(0x8000), // Ariant colosseum-related?
|
||||
//ANTI_MACRO_LIMIT(0x10000), // No notes
|
||||
CANNOTJUMPDOWN(0x20000),
|
||||
//SUMMON_NPC_LIMIT(0x40000); // Seems to .. disable Rush if 0x2 is set
|
||||
|
||||
//......... EVEN MORE LIMITS ............
|
||||
//SUMMON_NPC_LIMIT(0x40000),
|
||||
NO_EXP_DECREASE(0x80000),
|
||||
//NO_DAMAGE_ON_FALLING(0x100000),
|
||||
//PARCEL_OPEN_LIMIT(0x200000),
|
||||
DROP_LIMIT(0x400000);
|
||||
//ROCKETBOOSTER_LIMIT(0x800000) //lol we don't even have mechanics <3
|
||||
|
||||
private long i;
|
||||
|
||||
private FieldLimit(long i) {
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return i;
|
||||
}
|
||||
|
||||
public boolean check(int fieldlimit) {
|
||||
return (fieldlimit & i) == i;
|
||||
}
|
||||
}
|
||||
61
src/main/java/server/maps/MapMonitor.java
Normal file
61
src/main/java/server/maps/MapMonitor.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
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.util.concurrent.ScheduledFuture;
|
||||
import server.TimerManager;
|
||||
|
||||
public class MapMonitor {
|
||||
private ScheduledFuture<?> monitorSchedule;
|
||||
private MapleMap map;
|
||||
private MaplePortal portal;
|
||||
|
||||
public MapMonitor(final MapleMap map, String portal) {
|
||||
this.map = map;
|
||||
this.portal = map.getPortal(portal);
|
||||
this.monitorSchedule = TimerManager.getInstance().register(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (map.getCharacters().size() < 1) {
|
||||
cancelAction();
|
||||
}
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
private void cancelAction() {
|
||||
if (monitorSchedule != null) { // thanks Thora for pointing a NPE occurring here
|
||||
monitorSchedule.cancel(false);
|
||||
monitorSchedule = null;
|
||||
}
|
||||
|
||||
map.killAllMonsters();
|
||||
map.clearDrops();
|
||||
if (portal != null) {
|
||||
portal.setPortalStatus(MaplePortal.OPEN);
|
||||
}
|
||||
map.resetReactors();
|
||||
|
||||
map = null;
|
||||
portal = null;
|
||||
}
|
||||
}
|
||||
197
src/main/java/server/maps/MapleDoor.java
Normal file
197
src/main/java/server/maps/MapleDoor.java
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
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.util.Collection;
|
||||
|
||||
import config.YamlConfig;
|
||||
import tools.Pair;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import net.server.services.type.ChannelServices;
|
||||
import net.server.services.task.channel.OverallService;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleDoor {
|
||||
private int ownerId;
|
||||
private MapleMap town;
|
||||
private MaplePortal townPortal;
|
||||
private MapleMap target;
|
||||
private Pair<String, Integer> posStatus = null;
|
||||
private long deployTime;
|
||||
private boolean active;
|
||||
|
||||
private MapleDoorObject townDoor;
|
||||
private MapleDoorObject areaDoor;
|
||||
|
||||
public MapleDoor(MapleCharacter owner, Point targetPosition) {
|
||||
this.ownerId = owner.getId();
|
||||
this.target = owner.getMap();
|
||||
|
||||
if(target.canDeployDoor(targetPosition)) {
|
||||
if(YamlConfig.config.server.USE_ENFORCE_MDOOR_POSITION) {
|
||||
posStatus = target.getDoorPositionStatus(targetPosition);
|
||||
}
|
||||
|
||||
if(posStatus == null) {
|
||||
this.town = this.target.getReturnMap();
|
||||
this.townPortal = getTownDoorPortal(owner.getDoorSlot());
|
||||
this.deployTime = System.currentTimeMillis();
|
||||
this.active = true;
|
||||
|
||||
if(townPortal != null) {
|
||||
this.areaDoor = new MapleDoorObject(ownerId, town, target, townPortal.getId(), targetPosition, townPortal.getPosition());
|
||||
this.townDoor = new MapleDoorObject(ownerId, target, town, -1, townPortal.getPosition(), targetPosition);
|
||||
|
||||
this.areaDoor.setPairOid(this.townDoor.getObjectId());
|
||||
this.townDoor.setPairOid(this.areaDoor.getObjectId());
|
||||
} else {
|
||||
this.ownerId = -1;
|
||||
}
|
||||
} else {
|
||||
this.ownerId = -3;
|
||||
}
|
||||
} else {
|
||||
this.ownerId = -2;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateDoorPortal(MapleCharacter owner) {
|
||||
int slot = owner.fetchDoorSlot();
|
||||
|
||||
MaplePortal nextTownPortal = getTownDoorPortal(slot);
|
||||
if(nextTownPortal != null) {
|
||||
townPortal = nextTownPortal;
|
||||
areaDoor.update(nextTownPortal.getId(), nextTownPortal.getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastRemoveDoor(MapleCharacter owner) {
|
||||
MapleDoorObject areaDoor = this.getAreaDoor();
|
||||
MapleDoorObject townDoor = this.getTownDoor();
|
||||
|
||||
MapleMap target = this.getTarget();
|
||||
MapleMap town = this.getTown();
|
||||
|
||||
Collection<MapleCharacter> targetChars = target.getCharacters();
|
||||
Collection<MapleCharacter> townChars = town.getCharacters();
|
||||
|
||||
target.removeMapObject(areaDoor);
|
||||
town.removeMapObject(townDoor);
|
||||
|
||||
for (MapleCharacter chr : targetChars) {
|
||||
areaDoor.sendDestroyData(chr.getClient());
|
||||
chr.removeVisibleMapObject(areaDoor);
|
||||
}
|
||||
|
||||
for (MapleCharacter chr : townChars) {
|
||||
townDoor.sendDestroyData(chr.getClient());
|
||||
chr.removeVisibleMapObject(townDoor);
|
||||
}
|
||||
|
||||
owner.removePartyDoor(false);
|
||||
|
||||
if (this.getTownPortal().getId() == 0x80) {
|
||||
for (MapleCharacter chr : townChars) {
|
||||
MapleDoor door = chr.getMainTownDoor();
|
||||
if (door != null) {
|
||||
townDoor.sendSpawnData(chr.getClient());
|
||||
chr.addVisibleMapObject(townDoor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void attemptRemoveDoor(final MapleCharacter owner) {
|
||||
final MapleDoor destroyDoor = owner.getPlayerDoor();
|
||||
if (destroyDoor != null && destroyDoor.dispose()) {
|
||||
long effectTimeLeft = 3000 - destroyDoor.getElapsedDeployTime(); // portal deployment effect duration
|
||||
if (effectTimeLeft > 0) {
|
||||
MapleMap town = destroyDoor.getTown();
|
||||
|
||||
OverallService service = (OverallService) town.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
|
||||
service.registerOverallAction(town.getId(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
destroyDoor.broadcastRemoveDoor(owner); // thanks BHB88 for noticing doors crashing players when instantly cancelling buff
|
||||
}
|
||||
}, effectTimeLeft);
|
||||
} else {
|
||||
destroyDoor.broadcastRemoveDoor(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MaplePortal getTownDoorPortal(int doorid) {
|
||||
return town.getDoorPortal(doorid);
|
||||
}
|
||||
|
||||
public int getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public MapleDoorObject getTownDoor() {
|
||||
return townDoor;
|
||||
}
|
||||
|
||||
public MapleDoorObject getAreaDoor() {
|
||||
return areaDoor;
|
||||
}
|
||||
|
||||
public MapleMap getTown() {
|
||||
return town;
|
||||
}
|
||||
|
||||
public MaplePortal getTownPortal() {
|
||||
return townPortal;
|
||||
}
|
||||
|
||||
public MapleMap getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public Pair<String, Integer> getDoorStatus() {
|
||||
return posStatus;
|
||||
}
|
||||
|
||||
public long getElapsedDeployTime() {
|
||||
return System.currentTimeMillis() - deployTime;
|
||||
}
|
||||
|
||||
private boolean dispose() {
|
||||
if (active) {
|
||||
active = false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
}
|
||||
188
src/main/java/server/maps/MapleDoorObject.java
Normal file
188
src/main/java/server/maps/MapleDoorObject.java
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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.maps;
|
||||
|
||||
import java.awt.Point;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReadLock;
|
||||
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
|
||||
import net.server.audit.locks.MonitoredWriteLock;
|
||||
import net.server.audit.locks.factory.MonitoredReadLockFactory;
|
||||
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
|
||||
import net.server.world.MapleParty;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleDoorObject extends AbstractMapleMapObject {
|
||||
private final int ownerId;
|
||||
private int pairOid;
|
||||
|
||||
private final MapleMap from;
|
||||
private final MapleMap to;
|
||||
private int linkedPortalId;
|
||||
private Point linkedPos;
|
||||
|
||||
private final MonitoredReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.PLAYER_DOOR, true);
|
||||
private MonitoredReadLock rlock = MonitoredReadLockFactory.createLock(locks);
|
||||
private MonitoredWriteLock wlock = MonitoredWriteLockFactory.createLock(locks);
|
||||
|
||||
public MapleDoorObject(int owner, MapleMap destination, MapleMap origin, int townPortalId, Point targetPosition, Point toPosition) {
|
||||
super();
|
||||
setPosition(targetPosition);
|
||||
|
||||
ownerId = owner;
|
||||
linkedPortalId = townPortalId;
|
||||
from = origin;
|
||||
to = destination;
|
||||
linkedPos = toPosition;
|
||||
}
|
||||
|
||||
public void update(int townPortalId, Point toPosition) {
|
||||
wlock.lock();
|
||||
try {
|
||||
linkedPortalId = townPortalId;
|
||||
linkedPos = toPosition;
|
||||
} finally {
|
||||
wlock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private int getLinkedPortalId() {
|
||||
rlock.lock();
|
||||
try {
|
||||
return linkedPortalId;
|
||||
} finally {
|
||||
rlock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private Point getLinkedPortalPosition() {
|
||||
rlock.lock();
|
||||
try {
|
||||
return linkedPos;
|
||||
} finally {
|
||||
rlock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void warp(final MapleCharacter chr) {
|
||||
MapleParty party = chr.getParty();
|
||||
if (chr.getId() == ownerId || (party != null && party.getMemberById(ownerId) != null)) {
|
||||
chr.announce(MaplePacketCreator.playPortalSound());
|
||||
|
||||
if(!inTown() && party == null) {
|
||||
chr.changeMap(to, getLinkedPortalId());
|
||||
} else {
|
||||
chr.changeMap(to, getLinkedPortalPosition());
|
||||
}
|
||||
} else {
|
||||
chr.getClient().announce(MaplePacketCreator.blockedMessage(6));
|
||||
chr.getClient().announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(MapleClient client) {
|
||||
sendSpawnData(client, true);
|
||||
}
|
||||
|
||||
public void sendSpawnData(MapleClient client, boolean launched) {
|
||||
MapleCharacter chr = client.getPlayer();
|
||||
if (this.getFrom().getId() == chr.getMapId()) {
|
||||
if (chr.getParty() != null && (this.getOwnerId() == chr.getId() || chr.getParty().getMemberById(this.getOwnerId()) != null)) {
|
||||
chr.announce(MaplePacketCreator.partyPortal(this.getFrom().getId(), this.getTo().getId(), this.toPosition()));
|
||||
}
|
||||
|
||||
chr.announce(MaplePacketCreator.spawnPortal(this.getFrom().getId(), this.getTo().getId(), this.toPosition()));
|
||||
if (!this.inTown()) {
|
||||
chr.announce(MaplePacketCreator.spawnDoor(this.getOwnerId(), this.getPosition(), launched));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyData(MapleClient client) {
|
||||
MapleCharacter chr = client.getPlayer();
|
||||
if (from.getId() == chr.getMapId()) {
|
||||
MapleParty party = chr.getParty();
|
||||
if (party != null && (ownerId == chr.getId() || party.getMemberById(ownerId) != null)) {
|
||||
client.announce(MaplePacketCreator.partyPortal(999999999, 999999999, new Point(-1, -1)));
|
||||
}
|
||||
client.announce(MaplePacketCreator.removeDoor(ownerId, inTown()));
|
||||
}
|
||||
}
|
||||
|
||||
public void sendDestroyData(MapleClient client, boolean partyUpdate) {
|
||||
if (client != null && from.getId() == client.getPlayer().getMapId()) {
|
||||
client.announce(MaplePacketCreator.partyPortal(999999999, 999999999, new Point(-1, -1)));
|
||||
client.announce(MaplePacketCreator.removeDoor(ownerId, inTown()));
|
||||
}
|
||||
}
|
||||
|
||||
public int getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public void setPairOid(int oid) {
|
||||
this.pairOid = oid;
|
||||
}
|
||||
|
||||
public int getPairOid() {
|
||||
return pairOid;
|
||||
}
|
||||
|
||||
public boolean inTown() {
|
||||
return getLinkedPortalId() == -1;
|
||||
}
|
||||
|
||||
public MapleMap getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public MapleMap getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public MapleMap getTown() {
|
||||
return inTown() ? from : to;
|
||||
}
|
||||
|
||||
public MapleMap getArea() {
|
||||
return !inTown() ? from : to;
|
||||
}
|
||||
|
||||
public Point getAreaPosition() {
|
||||
return !inTown() ? getPosition() : getLinkedPortalPosition();
|
||||
}
|
||||
|
||||
public Point toPosition() {
|
||||
return getLinkedPortalPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapleMapObjectType getType() {
|
||||
return MapleMapObjectType.DOOR;
|
||||
}
|
||||
}
|
||||
65
src/main/java/server/maps/MapleDragon.java
Normal file
65
src/main/java/server/maps/MapleDragon.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
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 tools.MaplePacketCreator;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
|
||||
|
||||
|
||||
public class MapleDragon extends AbstractAnimatedMapleMapObject {
|
||||
|
||||
private MapleCharacter owner;
|
||||
|
||||
public MapleDragon(MapleCharacter chr) {
|
||||
super();
|
||||
this.owner = chr;
|
||||
this.setPosition(chr.getPosition());
|
||||
this.setStance(chr.getStance());
|
||||
this.sendSpawnData(chr.getClient());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapleMapObjectType getType() {
|
||||
return MapleMapObjectType.DRAGON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(MapleClient client) {
|
||||
client.announce(MaplePacketCreator.spawnDragon(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getObjectId() {
|
||||
return owner.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyData(MapleClient c) {
|
||||
c.announce(MaplePacketCreator.removeDragon(owner.getId()));
|
||||
}
|
||||
|
||||
public MapleCharacter getOwner() {
|
||||
return owner;
|
||||
}
|
||||
}
|
||||
103
src/main/java/server/maps/MapleFoothold.java
Normal file
103
src/main/java/server/maps/MapleFoothold.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
*/
|
||||
public class MapleFoothold implements Comparable<MapleFoothold> {
|
||||
private Point p1;
|
||||
private Point p2;
|
||||
private int id;
|
||||
private int next, prev;
|
||||
|
||||
public MapleFoothold(Point p1, Point p2, int id) {
|
||||
this.p1 = p1;
|
||||
this.p2 = p2;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean isWall() {
|
||||
return p1.x == p2.x;
|
||||
}
|
||||
|
||||
public int getX1() {
|
||||
return p1.x;
|
||||
}
|
||||
|
||||
public int getX2() {
|
||||
return p2.x;
|
||||
}
|
||||
|
||||
public int getY1() {
|
||||
return p1.y;
|
||||
}
|
||||
|
||||
public int getY2() {
|
||||
return p2.y;
|
||||
}
|
||||
|
||||
// XXX may need more precision
|
||||
public int calculateFooting(int x) {
|
||||
if (p1.y == p2.y) {
|
||||
return p2.y; // y at both ends is the same
|
||||
}
|
||||
int slope = (p1.y - p2.y) / (p1.x - p2.x);
|
||||
int intercept = p1.y - (slope * p1.x);
|
||||
return (slope * x) + intercept;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(MapleFoothold o) {
|
||||
MapleFoothold other = o;
|
||||
if (p2.y < other.getY1()) {
|
||||
return -1;
|
||||
} else if (p1.y > other.getY2()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
public void setNext(int next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public int getPrev() {
|
||||
return prev;
|
||||
}
|
||||
|
||||
public void setPrev(int prev) {
|
||||
this.prev = prev;
|
||||
}
|
||||
}
|
||||
222
src/main/java/server/maps/MapleFootholdTree.java
Normal file
222
src/main/java/server/maps/MapleFootholdTree.java
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
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.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
*/
|
||||
public class MapleFootholdTree {
|
||||
private MapleFootholdTree nw = null;
|
||||
private MapleFootholdTree ne = null;
|
||||
private MapleFootholdTree sw = null;
|
||||
private MapleFootholdTree se = null;
|
||||
private List<MapleFoothold> footholds = new LinkedList<MapleFoothold>();
|
||||
private Point p1;
|
||||
private Point p2;
|
||||
private Point center;
|
||||
private int depth = 0;
|
||||
private static int maxDepth = 8;
|
||||
private int maxDropX;
|
||||
private int minDropX;
|
||||
|
||||
public MapleFootholdTree(Point p1, Point p2) {
|
||||
this.p1 = p1;
|
||||
this.p2 = p2;
|
||||
center = new Point((p2.x - p1.x) / 2, (p2.y - p1.y) / 2);
|
||||
}
|
||||
|
||||
public MapleFootholdTree(Point p1, Point p2, int depth) {
|
||||
this.p1 = p1;
|
||||
this.p2 = p2;
|
||||
this.depth = depth;
|
||||
center = new Point((p2.x - p1.x) / 2, (p2.y - p1.y) / 2);
|
||||
}
|
||||
|
||||
public void insert(MapleFoothold f) {
|
||||
if (depth == 0) {
|
||||
if (f.getX1() > maxDropX) {
|
||||
maxDropX = f.getX1();
|
||||
}
|
||||
if (f.getX1() < minDropX) {
|
||||
minDropX = f.getX1();
|
||||
}
|
||||
if (f.getX2() > maxDropX) {
|
||||
maxDropX = f.getX2();
|
||||
}
|
||||
if (f.getX2() < minDropX) {
|
||||
minDropX = f.getX2();
|
||||
}
|
||||
}
|
||||
if (depth == maxDepth ||
|
||||
(f.getX1() >= p1.x && f.getX2() <= p2.x &&
|
||||
f.getY1() >= p1.y && f.getY2() <= p2.y)) {
|
||||
footholds.add(f);
|
||||
} else {
|
||||
if (nw == null) {
|
||||
nw = new MapleFootholdTree(p1, center, depth + 1);
|
||||
ne = new MapleFootholdTree(new Point(center.x, p1.y), new Point(p2.x, center.y), depth + 1);
|
||||
sw = new MapleFootholdTree(new Point(p1.x, center.y), new Point(center.x, p2.y), depth + 1);
|
||||
se = new MapleFootholdTree(center, p2, depth + 1);
|
||||
}
|
||||
if (f.getX2() <= center.x && f.getY2() <= center.y) {
|
||||
nw.insert(f);
|
||||
} else if (f.getX1() > center.x && f.getY2() <= center.y) {
|
||||
ne.insert(f);
|
||||
} else if (f.getX2() <= center.x && f.getY1() > center.y) {
|
||||
sw.insert(f);
|
||||
} else {
|
||||
se.insert(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<MapleFoothold> getRelevants(Point p) {
|
||||
return getRelevants(p, new LinkedList<MapleFoothold>());
|
||||
}
|
||||
|
||||
private List<MapleFoothold> getRelevants(Point p, List<MapleFoothold> list) {
|
||||
list.addAll(footholds);
|
||||
if (nw != null) {
|
||||
if (p.x <= center.x && p.y <= center.y) {
|
||||
nw.getRelevants(p, list);
|
||||
} else if (p.x > center.x && p.y <= center.y) {
|
||||
ne.getRelevants(p, list);
|
||||
} else if (p.x <= center.x && p.y > center.y) {
|
||||
sw.getRelevants(p, list);
|
||||
} else {
|
||||
se.getRelevants(p, list);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private MapleFoothold findWallR(Point p1, Point p2) {
|
||||
MapleFoothold ret;
|
||||
for (MapleFoothold f : footholds) {
|
||||
if (f.isWall() && f.getX1() >= p1.x && f.getX1() <= p2.x &&
|
||||
f.getY1() >= p1.y && f.getY2() <= p1.y) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
if (nw != null) {
|
||||
if (p1.x <= center.x && p1.y <= center.y) {
|
||||
ret = nw.findWallR(p1, p2);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if ((p1.x > center.x || p2.x > center.x) && p1.y <= center.y) {
|
||||
ret = ne.findWallR(p1, p2);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (p1.x <= center.x && p1.y > center.y) {
|
||||
ret = sw.findWallR(p1, p2);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if ((p1.x > center.x || p2.x > center.x) && p1.y > center.y) {
|
||||
ret = se.findWallR(p1, p2);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public MapleFoothold findWall(Point p1, Point p2) {
|
||||
if (p1.y != p2.y) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return findWallR(p1, p2);
|
||||
}
|
||||
|
||||
public MapleFoothold findBelow(Point p) {
|
||||
List<MapleFoothold> relevants = getRelevants(p);
|
||||
List<MapleFoothold> xMatches = new LinkedList<MapleFoothold>();
|
||||
for (MapleFoothold fh : relevants) {
|
||||
if (fh.getX1() <= p.x && fh.getX2() >= p.x) {
|
||||
xMatches.add(fh);
|
||||
}
|
||||
}
|
||||
Collections.sort(xMatches);
|
||||
for (MapleFoothold fh : xMatches) {
|
||||
if (!fh.isWall()) {
|
||||
if (fh.getY1() != fh.getY2()) {
|
||||
int calcY;
|
||||
double s1 = Math.abs(fh.getY2() - fh.getY1());
|
||||
double s2 = Math.abs(fh.getX2() - fh.getX1());
|
||||
double s4 = Math.abs(p.x - fh.getX1());
|
||||
double alpha = Math.atan(s2 / s1);
|
||||
double beta = Math.atan(s1 / s2);
|
||||
double s5 = Math.cos(alpha) * (s4 / Math.cos(beta));
|
||||
if (fh.getY2() < fh.getY1()) {
|
||||
calcY = fh.getY1() - (int) s5;
|
||||
} else {
|
||||
calcY = fh.getY1() + (int) s5;
|
||||
}
|
||||
if (calcY >= p.y) {
|
||||
return fh;
|
||||
}
|
||||
} else {
|
||||
if (fh.getY1() >= p.y) {
|
||||
return fh;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getX1() {
|
||||
return p1.x;
|
||||
}
|
||||
|
||||
public int getX2() {
|
||||
return p2.x;
|
||||
}
|
||||
|
||||
public int getY1() {
|
||||
return p1.y;
|
||||
}
|
||||
|
||||
public int getY2() {
|
||||
return p2.y;
|
||||
}
|
||||
|
||||
public int getMaxDropX() {
|
||||
return maxDropX;
|
||||
}
|
||||
|
||||
public int getMinDropX() {
|
||||
return minDropX;
|
||||
}
|
||||
}
|
||||
171
src/main/java/server/maps/MapleGenericPortal.java
Normal file
171
src/main/java/server/maps/MapleGenericPortal.java
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
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 client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import constants.game.GameConstants;
|
||||
import java.awt.Point;
|
||||
import scripting.portal.PortalScriptManager;
|
||||
import tools.MaplePacketCreator;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
|
||||
public class MapleGenericPortal implements MaplePortal {
|
||||
|
||||
private String name;
|
||||
private String target;
|
||||
private Point position;
|
||||
private int targetmap;
|
||||
private int type;
|
||||
private boolean status = true;
|
||||
private int id;
|
||||
private String scriptName;
|
||||
private boolean portalState;
|
||||
private MonitoredReentrantLock scriptLock = null;
|
||||
|
||||
public MapleGenericPortal(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPortalStatus(boolean newStatus) {
|
||||
this.status = newStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPortalStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTargetMapId() {
|
||||
return targetmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScriptName() {
|
||||
return scriptName;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setPosition(Point position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public void setTarget(String target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public void setTargetMapId(int targetmapid) {
|
||||
this.targetmap = targetmapid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScriptName(String scriptName) {
|
||||
this.scriptName = scriptName;
|
||||
|
||||
if(scriptName != null) {
|
||||
if(scriptLock == null) {
|
||||
scriptLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.PORTAL, true);
|
||||
}
|
||||
} else {
|
||||
scriptLock = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterPortal(MapleClient c) {
|
||||
boolean changed = false;
|
||||
if (getScriptName() != null) {
|
||||
try {
|
||||
scriptLock.lock();
|
||||
try {
|
||||
changed = PortalScriptManager.getInstance().executePortalScript(this, c);
|
||||
} finally {
|
||||
scriptLock.unlock();
|
||||
}
|
||||
} catch(NullPointerException npe) {
|
||||
npe.printStackTrace();
|
||||
}
|
||||
} else if (getTargetMapId() != 999999999) {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
if (!(chr.getChalkboard() != null && GameConstants.isFreeMarketRoom(getTargetMapId()))) {
|
||||
MapleMap to = chr.getEventInstance() == null ? c.getChannelServer().getMapFactory().getMap(getTargetMapId()) : chr.getEventInstance().getMapInstance(getTargetMapId());
|
||||
MaplePortal pto = to.getPortal(getTarget());
|
||||
if (pto == null) {// fallback for missing portals - no real life case anymore - interesting for not implemented areas
|
||||
pto = to.getPortal(0);
|
||||
}
|
||||
chr.changeMap(to, pto); //late resolving makes this harder but prevents us from loading the whole world at once
|
||||
changed = true;
|
||||
} else {
|
||||
chr.dropMessage(5, "You cannot enter this map with the chalkboard opened.");
|
||||
}
|
||||
}
|
||||
if (!changed) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPortalState(boolean state) {
|
||||
this.portalState = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPortalState() {
|
||||
return portalState;
|
||||
}
|
||||
}
|
||||
759
src/main/java/server/maps/MapleHiredMerchant.java
Normal file
759
src/main/java/server/maps/MapleHiredMerchant.java
Normal file
@@ -0,0 +1,759 @@
|
||||
/*
|
||||
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 client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.ItemFactory;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import client.inventory.manipulator.MapleKarmaManipulator;
|
||||
import client.processor.npc.FredrickProcessor;
|
||||
import com.mysql.jdbc.Statement;
|
||||
import config.YamlConfig;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import net.server.Server;
|
||||
import server.MapleItemInformationProvider;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import server.MapleTrade;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author XoticStory
|
||||
* @author Ronan - concurrency protection
|
||||
*/
|
||||
public class MapleHiredMerchant extends AbstractMapleMapObject {
|
||||
private int ownerId, itemId, mesos = 0;
|
||||
private int channel, world;
|
||||
private long start;
|
||||
private String ownerName = "";
|
||||
private String description = "";
|
||||
private MapleCharacter[] visitors = new MapleCharacter[3];
|
||||
private final List<MaplePlayerShopItem> items = new LinkedList<>();
|
||||
private List<Pair<String, Byte>> messages = new LinkedList<>();
|
||||
private List<SoldItem> sold = new LinkedList<>();
|
||||
private AtomicBoolean open = new AtomicBoolean();
|
||||
private boolean published = false;
|
||||
private MapleMap map;
|
||||
private Lock visitorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.VISITOR_MERCH, true);
|
||||
|
||||
public MapleHiredMerchant(final MapleCharacter owner, String desc, int itemId) {
|
||||
this.setPosition(owner.getPosition());
|
||||
this.start = System.currentTimeMillis();
|
||||
this.ownerId = owner.getId();
|
||||
this.channel = owner.getClient().getChannel();
|
||||
this.world = owner.getWorld();
|
||||
this.itemId = itemId;
|
||||
this.ownerName = owner.getName();
|
||||
this.description = desc;
|
||||
this.map = owner.getMap();
|
||||
}
|
||||
|
||||
public void broadcastToVisitorsThreadsafe(final byte[] packet) {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
broadcastToVisitors(packet);
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastToVisitors(final byte[] packet) {
|
||||
for (MapleCharacter visitor : visitors) {
|
||||
if (visitor != null) {
|
||||
visitor.getClient().announce(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getShopRoomInfo() {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
byte count = 0;
|
||||
if (this.isOpen()) {
|
||||
for (MapleCharacter visitor : visitors) {
|
||||
if (visitor != null) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
count = (byte) (visitors.length + 1);
|
||||
}
|
||||
|
||||
return new byte[]{count, (byte) (visitors.length + 1)};
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean addVisitor(MapleCharacter visitor) {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
int i = this.getFreeSlot();
|
||||
if (i > -1) {
|
||||
visitors[i] = visitor;
|
||||
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorAdd(visitor, i + 1));
|
||||
this.getMap().broadcastMessage(MaplePacketCreator.updateHiredMerchantBox(this));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeVisitor(MapleCharacter visitor) {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
int slot = getVisitorSlot(visitor);
|
||||
if (slot < 0) { //Not found
|
||||
return;
|
||||
}
|
||||
if (visitors[slot] != null && visitors[slot].getId() == visitor.getId()) {
|
||||
visitors[slot] = null;
|
||||
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorLeave(slot + 1));
|
||||
this.getMap().broadcastMessage(MaplePacketCreator.updateHiredMerchantBox(this));
|
||||
}
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int getVisitorSlotThreadsafe(MapleCharacter visitor) {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
return getVisitorSlot(visitor);
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private int getVisitorSlot(MapleCharacter visitor) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] != null && visitors[i].getId() == visitor.getId()){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1; //Actually 0 because of the +1's.
|
||||
}
|
||||
|
||||
private void removeAllVisitors() {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
MapleCharacter visitor = visitors[i];
|
||||
|
||||
if (visitor != null) {
|
||||
visitor.setHiredMerchant(null);
|
||||
|
||||
visitor.getClient().announce(MaplePacketCreator.leaveHiredMerchant(i + 1, 0x11));
|
||||
visitor.getClient().announce(MaplePacketCreator.hiredMerchantMaintenanceMessage());
|
||||
|
||||
visitors[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.getMap().broadcastMessage(MaplePacketCreator.updateHiredMerchantBox(this));
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeOwner(MapleCharacter owner) {
|
||||
if (owner.getHiredMerchant() == this) {
|
||||
owner.announce(MaplePacketCreator.hiredMerchantOwnerLeave());
|
||||
owner.announce(MaplePacketCreator.leaveHiredMerchant(0x00, 0x03));
|
||||
owner.setHiredMerchant(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void withdrawMesos(MapleCharacter chr) {
|
||||
if (isOwner(chr)) {
|
||||
synchronized (items) {
|
||||
chr.withdrawMerchantMesos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void takeItemBack(int slot, MapleCharacter chr) {
|
||||
synchronized (items) {
|
||||
MaplePlayerShopItem shopItem = items.get(slot);
|
||||
if(shopItem.isExist()) {
|
||||
if (shopItem.getBundles() > 0) {
|
||||
Item iitem = shopItem.getItem().copy();
|
||||
iitem.setQuantity((short) (shopItem.getItem().getQuantity() * shopItem.getBundles()));
|
||||
|
||||
if (!MapleInventory.checkSpot(chr, iitem)) {
|
||||
chr.announce(MaplePacketCreator.serverNotice(1, "Have a slot available on your inventory to claim back the item."));
|
||||
chr.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
MapleInventoryManipulator.addFromDrop(chr.getClient(), iitem, true);
|
||||
}
|
||||
|
||||
removeFromSlot(slot);
|
||||
chr.announce(MaplePacketCreator.updateHiredMerchant(this, chr));
|
||||
}
|
||||
|
||||
if (YamlConfig.config.server.USE_ENFORCE_MERCHANT_SAVE) {
|
||||
chr.saveCharToDB(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canBuy(MapleClient c, Item newItem) { // thanks xiaokelvin (Conrad) for noticing a leaked test code here
|
||||
return MapleInventoryManipulator.checkSpace(c, newItem.getItemId(), newItem.getQuantity(), newItem.getOwner()) && MapleInventoryManipulator.addFromDrop(c, newItem, false);
|
||||
}
|
||||
|
||||
private int getQuantityLeft(int itemid) {
|
||||
synchronized (items) {
|
||||
int count = 0;
|
||||
|
||||
for (MaplePlayerShopItem mpsi : items) {
|
||||
if (mpsi.getItem().getItemId() == itemid) {
|
||||
count += (mpsi.getBundles() * mpsi.getItem().getQuantity());
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public void buy(MapleClient c, int item, short quantity) {
|
||||
synchronized (items) {
|
||||
MaplePlayerShopItem pItem = items.get(item);
|
||||
Item newItem = pItem.getItem().copy();
|
||||
|
||||
newItem.setQuantity((short) ((pItem.getItem().getQuantity() * quantity)));
|
||||
if (quantity < 1 || !pItem.isExist() || pItem.getBundles() < quantity) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
} else if (newItem.getInventoryType().equals(MapleInventoryType.EQUIP) && newItem.getQuantity() > 1) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(newItem);
|
||||
|
||||
int price = (int) Math.min((float) pItem.getPrice() * quantity, Integer.MAX_VALUE);
|
||||
if (c.getPlayer().getMeso() >= price) {
|
||||
if (canBuy(c, newItem)) {
|
||||
c.getPlayer().gainMeso(-price, false);
|
||||
price -= MapleTrade.getFee(price); // thanks BHB for pointing out trade fees not applying here
|
||||
|
||||
synchronized (sold) {
|
||||
sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), newItem.getQuantity(), price));
|
||||
}
|
||||
|
||||
pItem.setBundles((short) (pItem.getBundles() - quantity));
|
||||
if (pItem.getBundles() < 1) {
|
||||
pItem.setDoesExist(false);
|
||||
}
|
||||
|
||||
if(YamlConfig.config.server.USE_ANNOUNCE_SHOPITEMSOLD) { // idea thanks to Vcoc
|
||||
announceItemSold(newItem, price, getQuantityLeft(pItem.getItem().getItemId()));
|
||||
}
|
||||
|
||||
MapleCharacter owner = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterByName(ownerName);
|
||||
if (owner != null) {
|
||||
owner.addMerchantMesos(price);
|
||||
} else {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
long merchantMesos = 0;
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT MerchantMesos FROM characters WHERE id = ?")) {
|
||||
ps.setInt(1, ownerId);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
merchantMesos = rs.getInt(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
merchantMesos += price;
|
||||
|
||||
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET MerchantMesos = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
|
||||
ps.setInt(1, (int) Math.min(merchantMesos, Integer.MAX_VALUE));
|
||||
ps.setInt(2, ownerId);
|
||||
ps.executeUpdate();
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.getPlayer().dropMessage(1, "Your inventory is full. Please clear a slot before buying this item.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
c.getPlayer().dropMessage(1, "You don't have enough mesos to purchase this item.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.saveItems(false);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void announceItemSold(Item item, int mesos, int inStore) {
|
||||
String qtyStr = (item.getQuantity() > 1) ? " x " + item.getQuantity() : "";
|
||||
|
||||
MapleCharacter player = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterById(ownerId);
|
||||
if(player != null && player.isLoggedinWorld()) {
|
||||
player.dropMessage(6, "[Hired Merchant] Item '" + MapleItemInformationProvider.getInstance().getName(item.getItemId()) + "'" + qtyStr + " has been sold for " + mesos + " mesos. (" + inStore + " left)");
|
||||
}
|
||||
}
|
||||
|
||||
public void forceClose() {
|
||||
//Server.getInstance().getChannel(world, channel).removeHiredMerchant(ownerId);
|
||||
map.broadcastMessage(MaplePacketCreator.removeHiredMerchantBox(getOwnerId()));
|
||||
map.removeMapObject(this);
|
||||
|
||||
MapleCharacter owner = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterById(ownerId);
|
||||
|
||||
visitorLock.lock();
|
||||
try {
|
||||
setOpen(false);
|
||||
removeAllVisitors();
|
||||
|
||||
if(owner != null && owner.isLoggedinWorld() && this == owner.getHiredMerchant()) {
|
||||
closeOwnerMerchant(owner);
|
||||
}
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
|
||||
Server.getInstance().getWorld(world).unregisterHiredMerchant(this);
|
||||
|
||||
try {
|
||||
saveItems(true);
|
||||
synchronized (items) {
|
||||
items.clear();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
MapleCharacter player = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterById(ownerId);
|
||||
if(player != null) {
|
||||
player.setHasMerchant(false);
|
||||
} else {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("UPDATE characters SET HasMerchant = 0 WHERE id = ?", Statement.RETURN_GENERATED_KEYS);
|
||||
ps.setInt(1, ownerId);
|
||||
ps.executeUpdate();
|
||||
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
map = null;
|
||||
}
|
||||
|
||||
public void closeOwnerMerchant(MapleCharacter chr) {
|
||||
if(this.isOwner(chr)) {
|
||||
this.closeShop(chr.getClient(), false);
|
||||
chr.setHasMerchant(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeShop(MapleClient c, boolean timeout) {
|
||||
map.removeMapObject(this);
|
||||
map.broadcastMessage(MaplePacketCreator.removeHiredMerchantBox(ownerId));
|
||||
c.getChannelServer().removeHiredMerchant(ownerId);
|
||||
|
||||
this.removeAllVisitors();
|
||||
this.removeOwner(c.getPlayer());
|
||||
|
||||
try {
|
||||
List<MaplePlayerShopItem> copyItems = getItems();
|
||||
if (check(c.getPlayer(), copyItems) && !timeout) {
|
||||
for (MaplePlayerShopItem mpsi : copyItems) {
|
||||
if(mpsi.isExist()) {
|
||||
if (mpsi.getItem().getInventoryType().equals(MapleInventoryType.EQUIP)) {
|
||||
MapleInventoryManipulator.addFromDrop(c, mpsi.getItem(), false);
|
||||
} else {
|
||||
MapleInventoryManipulator.addById(c, mpsi.getItem().getItemId(), (short) (mpsi.getBundles() * mpsi.getItem().getQuantity()), mpsi.getItem().getOwner(), -1, mpsi.getItem().getFlag(), mpsi.getItem().getExpiration());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (items) {
|
||||
items.clear();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.saveItems(timeout);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// thanks Rohenn for noticing a possible dupe scenario on closing shop
|
||||
MapleCharacter player = c.getWorldServer().getPlayerStorage().getCharacterById(ownerId);
|
||||
if(player != null) {
|
||||
player.setHasMerchant(false);
|
||||
} else {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET HasMerchant = 0 WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
|
||||
ps.setInt(1, ownerId);
|
||||
ps.executeUpdate();
|
||||
}
|
||||
con.close();
|
||||
}
|
||||
|
||||
if (YamlConfig.config.server.USE_ENFORCE_MERCHANT_SAVE) {
|
||||
c.getPlayer().saveCharToDB(false);
|
||||
}
|
||||
|
||||
synchronized (items) {
|
||||
items.clear();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Server.getInstance().getWorld(world).unregisterHiredMerchant(this);
|
||||
}
|
||||
|
||||
public synchronized void visitShop(MapleCharacter chr) {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
if (this.isOwner(chr)) {
|
||||
this.setOpen(false);
|
||||
this.removeAllVisitors();
|
||||
|
||||
chr.announce(MaplePacketCreator.getHiredMerchant(chr, this, false));
|
||||
} else if (!this.isOpen()) {
|
||||
chr.announce(MaplePacketCreator.getMiniRoomError(18));
|
||||
return;
|
||||
} else if (!this.addVisitor(chr)) {
|
||||
chr.announce(MaplePacketCreator.getMiniRoomError(2));
|
||||
return;
|
||||
} else {
|
||||
chr.announce(MaplePacketCreator.getHiredMerchant(chr, this, false));
|
||||
}
|
||||
chr.setHiredMerchant(this);
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return ownerName;
|
||||
}
|
||||
|
||||
public void clearItems() {
|
||||
synchronized (items) {
|
||||
items.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public int getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public MapleCharacter[] getVisitors() {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
MapleCharacter[] copy = new MapleCharacter[3];
|
||||
for(int i = 0; i < visitors.length; i++) copy[i] = visitors[i];
|
||||
|
||||
return copy;
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public List<MaplePlayerShopItem> getItems() {
|
||||
synchronized (items) {
|
||||
return Collections.unmodifiableList(items);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasItem(int itemid) {
|
||||
for(MaplePlayerShopItem mpsi : getItems()) {
|
||||
if(mpsi.getItem().getItemId() == itemid && mpsi.isExist() && mpsi.getBundles() > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean addItem(MaplePlayerShopItem item) {
|
||||
synchronized (items) {
|
||||
if (items.size() >= 16) return false;
|
||||
|
||||
items.add(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void clearInexistentItems() {
|
||||
synchronized(items) {
|
||||
for (int i = items.size() - 1; i >= 0; i--) {
|
||||
if (!items.get(i).isExist()) {
|
||||
items.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.saveItems(false);
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeFromSlot(int slot) {
|
||||
items.remove(slot);
|
||||
|
||||
try {
|
||||
this.saveItems(false);
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private int getFreeSlot() {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] == null) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public boolean isPublished() {
|
||||
return published;
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return open.get();
|
||||
}
|
||||
|
||||
public void setOpen(boolean set) {
|
||||
open.getAndSet(set);
|
||||
published = true;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public boolean isOwner(MapleCharacter chr) {
|
||||
return chr.getId() == ownerId;
|
||||
}
|
||||
|
||||
public void sendMessage(MapleCharacter chr, String msg) {
|
||||
String message = chr.getName() + " : " + msg;
|
||||
byte slot = (byte) (getVisitorSlot(chr) + 1);
|
||||
|
||||
synchronized (messages) {
|
||||
messages.add(new Pair<>(message, slot));
|
||||
}
|
||||
broadcastToVisitorsThreadsafe(MaplePacketCreator.hiredMerchantChat(message, slot));
|
||||
}
|
||||
|
||||
public List<MaplePlayerShopItem> sendAvailableBundles(int itemid) {
|
||||
List<MaplePlayerShopItem> list = new LinkedList<>();
|
||||
List<MaplePlayerShopItem> all = new ArrayList<>();
|
||||
|
||||
if(!open.get()) return list;
|
||||
|
||||
synchronized (items) {
|
||||
for(MaplePlayerShopItem mpsi : items) all.add(mpsi);
|
||||
}
|
||||
|
||||
for(MaplePlayerShopItem mpsi : all) {
|
||||
if(mpsi.getItem().getItemId() == itemid && mpsi.getBundles() > 0 && mpsi.isExist()) {
|
||||
list.add(mpsi);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public void saveItems(boolean shutdown) throws SQLException {
|
||||
List<Pair<Item, MapleInventoryType>> itemsWithType = new ArrayList<>();
|
||||
List<Short> bundles = new ArrayList<>();
|
||||
|
||||
for (MaplePlayerShopItem pItems : getItems()) {
|
||||
Item newItem = pItems.getItem();
|
||||
short newBundle = pItems.getBundles();
|
||||
|
||||
if (shutdown) { //is "shutdown" really necessary?
|
||||
newItem.setQuantity((short) (pItems.getItem().getQuantity()));
|
||||
} else {
|
||||
newItem.setQuantity((short) (pItems.getItem().getQuantity()));
|
||||
}
|
||||
if (newBundle > 0) {
|
||||
itemsWithType.add(new Pair<>(newItem, newItem.getInventoryType()));
|
||||
bundles.add(newBundle);
|
||||
}
|
||||
}
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
ItemFactory.MERCHANT.saveItems(itemsWithType, bundles, this.ownerId, con);
|
||||
con.close();
|
||||
|
||||
FredrickProcessor.insertFredrickLog(this.ownerId);
|
||||
}
|
||||
|
||||
private static boolean check(MapleCharacter chr, List<MaplePlayerShopItem> items) {
|
||||
List<Pair<Item, MapleInventoryType>> li = new ArrayList<>();
|
||||
for (MaplePlayerShopItem item : items) {
|
||||
Item it = item.getItem().copy();
|
||||
it.setQuantity((short)(it.getQuantity() * item.getBundles()));
|
||||
|
||||
li.add(new Pair<>(it, it.getInventoryType()));
|
||||
}
|
||||
|
||||
return MapleInventory.checkSpotsAndOwnership(chr, li);
|
||||
}
|
||||
|
||||
public int getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public int getTimeOpen() {
|
||||
double openTime = (System.currentTimeMillis() - start) / 60000;
|
||||
openTime /= 1440; // heuristics since engineered method to count time here is unknown
|
||||
openTime *= 1318;
|
||||
|
||||
return (int) Math.ceil(openTime);
|
||||
}
|
||||
|
||||
public void clearMessages() {
|
||||
synchronized (messages) {
|
||||
messages.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Pair<String, Byte>> getMessages() {
|
||||
synchronized (messages) {
|
||||
List<Pair<String, Byte>> msgList = new LinkedList<>();
|
||||
for(Pair<String, Byte> m : messages) {
|
||||
msgList.add(m);
|
||||
}
|
||||
|
||||
return msgList;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMapId() {
|
||||
return map.getId();
|
||||
}
|
||||
|
||||
public MapleMap getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public List<SoldItem> getSold() {
|
||||
synchronized (sold) {
|
||||
return Collections.unmodifiableList(sold);
|
||||
}
|
||||
}
|
||||
|
||||
public int getMesos() {
|
||||
return mesos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapleMapObjectType getType() {
|
||||
return MapleMapObjectType.HIRED_MERCHANT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyData(MapleClient client) {}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(MapleClient client) {
|
||||
client.announce(MaplePacketCreator.spawnHiredMerchantBox(this));
|
||||
}
|
||||
|
||||
public class SoldItem {
|
||||
|
||||
int itemid, mesos;
|
||||
short quantity;
|
||||
String buyer;
|
||||
|
||||
public SoldItem(String buyer, int itemid, short quantity, int mesos) {
|
||||
this.buyer = buyer;
|
||||
this.itemid = itemid;
|
||||
this.quantity = quantity;
|
||||
this.mesos = mesos;
|
||||
}
|
||||
|
||||
public String getBuyer() {
|
||||
return buyer;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return itemid;
|
||||
}
|
||||
|
||||
public short getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public int getMesos() {
|
||||
return mesos;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/main/java/server/maps/MapleKite.java
Normal file
60
src/main/java/server/maps/MapleKite.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package server.maps;
|
||||
|
||||
import java.awt.Point;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
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.announce(makeDestroyData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(MapleClient client) {
|
||||
client.announce(makeSpawnData());
|
||||
}
|
||||
|
||||
public final byte[] makeSpawnData() {
|
||||
return MaplePacketCreator.spawnKite(getObjectId(), itemid, owner.getName(), text, pos, ft);
|
||||
}
|
||||
|
||||
public final byte[] makeDestroyData() {
|
||||
return MaplePacketCreator.removeKite(getObjectId(), 0);
|
||||
}
|
||||
}
|
||||
4624
src/main/java/server/maps/MapleMap.java
Normal file
4624
src/main/java/server/maps/MapleMap.java
Normal file
File diff suppressed because it is too large
Load Diff
48
src/main/java/server/maps/MapleMapEffect.java
Normal file
48
src/main/java/server/maps/MapleMapEffect.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
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 client.MapleClient;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
public class MapleMapEffect {
|
||||
private String msg;
|
||||
private int itemId;
|
||||
private boolean active = true;
|
||||
|
||||
public MapleMapEffect(String msg, int itemId) {
|
||||
this.msg = msg;
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
public final byte[] makeDestroyData() {
|
||||
return MaplePacketCreator.removeMapEffect();
|
||||
}
|
||||
|
||||
public final byte[] makeStartData() {
|
||||
return MaplePacketCreator.startMapEffect(msg, itemId, active);
|
||||
}
|
||||
|
||||
public void sendStartData(MapleClient client) {
|
||||
client.announce(makeStartData());
|
||||
}
|
||||
}
|
||||
444
src/main/java/server/maps/MapleMapFactory.java
Normal file
444
src/main/java/server/maps/MapleMapFactory.java
Normal file
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
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.awt.Rectangle;
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
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 server.partyquest.GuardianSpawnPoint;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.StringUtil;
|
||||
|
||||
public class MapleMapFactory {
|
||||
|
||||
private static MapleData nameData;
|
||||
private static MapleDataProvider mapSource;
|
||||
|
||||
static {
|
||||
nameData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz")).getData("Map.img");
|
||||
mapSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz"));
|
||||
}
|
||||
|
||||
private static void loadLifeFromWz(MapleMap map, MapleData mapData) {
|
||||
for (MapleData life : mapData.getChildByPath("life")) {
|
||||
life.getName();
|
||||
String id = MapleDataTool.getString(life.getChildByPath("id"));
|
||||
String type = MapleDataTool.getString(life.getChildByPath("type"));
|
||||
int team = MapleDataTool.getInt("team", life, -1);
|
||||
if (map.isCPQMap2() && type.equals("m")) {
|
||||
if ((Integer.parseInt(life.getName()) % 2) == 0) {
|
||||
team = 0;
|
||||
} else {
|
||||
team = 1;
|
||||
}
|
||||
}
|
||||
int cy = MapleDataTool.getInt(life.getChildByPath("cy"));
|
||||
MapleData dF = life.getChildByPath("f");
|
||||
int f = (dF != null) ? MapleDataTool.getInt(dF) : 0;
|
||||
int fh = MapleDataTool.getInt(life.getChildByPath("fh"));
|
||||
int rx0 = MapleDataTool.getInt(life.getChildByPath("rx0"));
|
||||
int rx1 = MapleDataTool.getInt(life.getChildByPath("rx1"));
|
||||
int x = MapleDataTool.getInt(life.getChildByPath("x"));
|
||||
int y = MapleDataTool.getInt(life.getChildByPath("y"));
|
||||
int hide = MapleDataTool.getInt("hide", life, 0);
|
||||
int mobTime = MapleDataTool.getInt("mobTime", life, 0);
|
||||
|
||||
loadLifeRaw(map, Integer.parseInt(id), type, cy, f, fh, rx0, rx1, x, y, hide, mobTime, team);
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadLifeFromDb(MapleMap map) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT * FROM plife WHERE map = ? and world = ?");
|
||||
ps.setInt(1, map.getId());
|
||||
ps.setInt(2, map.getWorld());
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) {
|
||||
int id = rs.getInt("life");
|
||||
String type = rs.getString("type");
|
||||
int cy = rs.getInt("cy");
|
||||
int f = rs.getInt("f");
|
||||
int fh = rs.getInt("fh");
|
||||
int rx0 = rs.getInt("rx0");
|
||||
int rx1 = rs.getInt("rx1");
|
||||
int x = rs.getInt("x");
|
||||
int y = rs.getInt("y");
|
||||
int hide = rs.getInt("hide");
|
||||
int mobTime = rs.getInt("mobtime");
|
||||
int team = rs.getInt("team");
|
||||
|
||||
loadLifeRaw(map, id, type, cy, f, fh, rx0, rx1, x, y, hide, mobTime, team);
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadLifeRaw(MapleMap map, int id, String type, int cy, int f, int fh, int rx0, int rx1, int x, int y, int hide, int mobTime, int team) {
|
||||
AbstractLoadedMapleLife myLife = loadLife(id, type, cy, f, fh, rx0, rx1, x, y, hide);
|
||||
if (myLife instanceof MapleMonster) {
|
||||
MapleMonster monster = (MapleMonster) myLife;
|
||||
|
||||
if (mobTime == -1) { //does not respawn, force spawn once
|
||||
map.spawnMonster(monster);
|
||||
} else {
|
||||
map.addMonsterSpawn(monster, mobTime, team);
|
||||
}
|
||||
|
||||
//should the map be reseted, use allMonsterSpawn list of monsters to spawn them again
|
||||
map.addAllMonsterSpawn(monster, mobTime, team);
|
||||
} else {
|
||||
map.addMapObject(myLife);
|
||||
}
|
||||
}
|
||||
|
||||
public static MapleMap loadMapFromWz(int mapid, int world, int channel, EventInstanceManager event) {
|
||||
MapleMap map;
|
||||
|
||||
String mapName = getMapName(mapid);
|
||||
MapleData mapData = mapSource.getData(mapName); // source.getData issue with giving nulls in rare ocasions found thanks to MedicOP
|
||||
MapleData infoData = mapData.getChildByPath("info");
|
||||
|
||||
String link = MapleDataTool.getString(infoData.getChildByPath("link"), "");
|
||||
if (!link.equals("")) { //nexon made hundreds of dojo maps so to reduce the size they added links.
|
||||
mapName = getMapName(Integer.parseInt(link));
|
||||
mapData = mapSource.getData(mapName);
|
||||
}
|
||||
float monsterRate = 0;
|
||||
MapleData mobRate = infoData.getChildByPath("mobRate");
|
||||
if (mobRate != null) {
|
||||
monsterRate = ((Float) mobRate.getData()).floatValue();
|
||||
}
|
||||
map = new MapleMap(mapid, world, channel, MapleDataTool.getInt("returnMap", infoData), monsterRate);
|
||||
map.setEventInstance(event);
|
||||
|
||||
String onFirstEnter = MapleDataTool.getString(infoData.getChildByPath("onFirstUserEnter"), String.valueOf(mapid));
|
||||
map.setOnFirstUserEnter(onFirstEnter.equals("") ? String.valueOf(mapid) : onFirstEnter);
|
||||
|
||||
String onEnter = MapleDataTool.getString(infoData.getChildByPath("onUserEnter"), String.valueOf(mapid));
|
||||
map.setOnUserEnter(onEnter.equals("") ? String.valueOf(mapid) : onEnter);
|
||||
|
||||
map.setFieldLimit(MapleDataTool.getInt(infoData.getChildByPath("fieldLimit"), 0));
|
||||
map.setMobInterval((short) MapleDataTool.getInt(infoData.getChildByPath("createMobInterval"), 5000));
|
||||
MaplePortalFactory portalFactory = new MaplePortalFactory();
|
||||
for (MapleData portal : mapData.getChildByPath("portal")) {
|
||||
map.addPortal(portalFactory.makePortal(MapleDataTool.getInt(portal.getChildByPath("pt")), portal));
|
||||
}
|
||||
MapleData timeMob = infoData.getChildByPath("timeMob");
|
||||
if (timeMob != null) {
|
||||
map.setTimeMob(MapleDataTool.getInt(timeMob.getChildByPath("id")), MapleDataTool.getString(timeMob.getChildByPath("message")));
|
||||
}
|
||||
|
||||
int bounds[] = new int[4];
|
||||
bounds[0] = MapleDataTool.getInt(infoData.getChildByPath("VRTop"));
|
||||
bounds[1] = MapleDataTool.getInt(infoData.getChildByPath("VRBottom"));
|
||||
|
||||
if (bounds[0] == bounds[1]) { // old-style baked map
|
||||
MapleData minimapData = mapData.getChildByPath("miniMap");
|
||||
if (minimapData != null) {
|
||||
bounds[0] = MapleDataTool.getInt(minimapData.getChildByPath("centerX")) * -1;
|
||||
bounds[1] = MapleDataTool.getInt(minimapData.getChildByPath("centerY")) * -1;
|
||||
bounds[2] = MapleDataTool.getInt(minimapData.getChildByPath("height"));
|
||||
bounds[3] = MapleDataTool.getInt(minimapData.getChildByPath("width"));
|
||||
|
||||
map.setMapPointBoundings(bounds[0], bounds[1], bounds[2], bounds[3]);
|
||||
} else {
|
||||
int dist = (1 << 18);
|
||||
map.setMapPointBoundings(-dist / 2, -dist / 2, dist, dist);
|
||||
}
|
||||
} else {
|
||||
bounds[2] = MapleDataTool.getInt(infoData.getChildByPath("VRLeft"));
|
||||
bounds[3] = MapleDataTool.getInt(infoData.getChildByPath("VRRight"));
|
||||
|
||||
map.setMapLineBoundings(bounds[0], bounds[1], bounds[2], bounds[3]);
|
||||
}
|
||||
|
||||
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) {
|
||||
int x1 = MapleDataTool.getInt(footHold.getChildByPath("x1"));
|
||||
int y1 = MapleDataTool.getInt(footHold.getChildByPath("y1"));
|
||||
int x2 = MapleDataTool.getInt(footHold.getChildByPath("x2"));
|
||||
int y2 = MapleDataTool.getInt(footHold.getChildByPath("y2"));
|
||||
MapleFoothold fh = new MapleFoothold(new Point(x1, y1), new Point(x2, y2), Integer.parseInt(footHold.getName()));
|
||||
fh.setPrev(MapleDataTool.getInt(footHold.getChildByPath("prev")));
|
||||
fh.setNext(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 (mapData.getChildByPath("area") != null) {
|
||||
for (MapleData area : mapData.getChildByPath("area")) {
|
||||
int x1 = MapleDataTool.getInt(area.getChildByPath("x1"));
|
||||
int y1 = MapleDataTool.getInt(area.getChildByPath("y1"));
|
||||
int x2 = MapleDataTool.getInt(area.getChildByPath("x2"));
|
||||
int y2 = MapleDataTool.getInt(area.getChildByPath("y2"));
|
||||
map.addMapleArea(new Rectangle(x1, y1, (x2 - x1), (y2 - y1)));
|
||||
}
|
||||
}
|
||||
if (mapData.getChildByPath("seat") != null) {
|
||||
int seats = mapData.getChildByPath("seat").getChildren().size();
|
||||
map.setSeats(seats);
|
||||
}
|
||||
if (event == null) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM playernpcs WHERE map = ? AND world = ?")) {
|
||||
ps.setInt(1, mapid);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadLifeFromWz(map, mapData);
|
||||
loadLifeFromDb(map);
|
||||
|
||||
if (map.isCPQMap()) {
|
||||
MapleData mcData = mapData.getChildByPath("monsterCarnival");
|
||||
if (mcData != null) {
|
||||
map.setDeathCP(MapleDataTool.getIntConvert("deathCP", mcData, 0));
|
||||
map.setMaxMobs(MapleDataTool.getIntConvert("mobGenMax", mcData, 20)); // thanks Atoot for noticing CPQ1 bf. 3 and 4 not accepting spawns due to undefined limits, Lame for noticing a need to cap mob spawns even on such undefined limits
|
||||
map.setTimeDefault(MapleDataTool.getIntConvert("timeDefault", mcData, 0));
|
||||
map.setTimeExpand(MapleDataTool.getIntConvert("timeExpand", mcData, 0));
|
||||
map.setMaxReactors(MapleDataTool.getIntConvert("guardianGenMax", mcData, 16));
|
||||
MapleData guardianGenData = mcData.getChildByPath("guardianGenPos");
|
||||
for (MapleData node : guardianGenData.getChildren()) {
|
||||
GuardianSpawnPoint pt = new GuardianSpawnPoint(new Point(MapleDataTool.getIntConvert("x", node), MapleDataTool.getIntConvert("y", node)));
|
||||
pt.setTeam(MapleDataTool.getIntConvert("team", node, -1));
|
||||
pt.setTaken(false);
|
||||
map.addGuardianSpawnPoint(pt);
|
||||
}
|
||||
if (mcData.getChildByPath("skill") != null) {
|
||||
for (MapleData area : mcData.getChildByPath("skill")) {
|
||||
map.addSkillId(MapleDataTool.getInt(area));
|
||||
}
|
||||
}
|
||||
|
||||
if (mcData.getChildByPath("mob") != null) {
|
||||
for (MapleData area : mcData.getChildByPath("mob")) {
|
||||
map.addMobSpawn(MapleDataTool.getInt(area.getChildByPath("id")), MapleDataTool.getInt(area.getChildByPath("spendCP")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (mapData.getChildByPath("reactor") != null) {
|
||||
for (MapleData reactor : mapData.getChildByPath("reactor")) {
|
||||
String id = MapleDataTool.getString(reactor.getChildByPath("id"));
|
||||
if (id != null) {
|
||||
MapleReactor newReactor = loadReactor(reactor, id, (byte) MapleDataTool.getInt(reactor.getChildByPath("f"), 0));
|
||||
map.spawnReactor(newReactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
map.setMapName(loadPlaceName(mapid));
|
||||
map.setStreetName(loadStreetName(mapid));
|
||||
|
||||
map.setClock(mapData.getChildByPath("clock") != null);
|
||||
map.setEverlast(MapleDataTool.getIntConvert("everlast", infoData, 0) != 0); // thanks davidlafriniere for noticing value 0 accounting as true
|
||||
map.setTown(MapleDataTool.getIntConvert("town", infoData, 0) != 0);
|
||||
map.setHPDec(MapleDataTool.getIntConvert("decHP", infoData, 0));
|
||||
map.setHPDecProtect(MapleDataTool.getIntConvert("protectItem", infoData, 0));
|
||||
map.setForcedReturnMap(MapleDataTool.getInt(infoData.getChildByPath("forcedReturn"), 999999999));
|
||||
map.setBoat(mapData.getChildByPath("shipObj") != null);
|
||||
map.setTimeLimit(MapleDataTool.getIntConvert("timeLimit", infoData, -1));
|
||||
map.setFieldType(MapleDataTool.getIntConvert("fieldType", infoData, 0));
|
||||
map.setMobCapacity(MapleDataTool.getIntConvert("fixedMobCapacity", infoData, 500));//Is there a map that contains more than 500 mobs?
|
||||
|
||||
MapleData recData = infoData.getChildByPath("recovery");
|
||||
if (recData != null) {
|
||||
map.setRecovery(MapleDataTool.getFloat(recData));
|
||||
}
|
||||
|
||||
HashMap<Integer, Integer> backTypes = new HashMap<>();
|
||||
try {
|
||||
for (MapleData layer : mapData.getChildByPath("back")) { // yolo
|
||||
int layerNum = Integer.parseInt(layer.getName());
|
||||
int btype = MapleDataTool.getInt(layer.getChildByPath("type"), 0);
|
||||
|
||||
backTypes.put(layerNum, btype);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// swallow cause I'm cool
|
||||
}
|
||||
|
||||
map.setBackgroundTypes(backTypes);
|
||||
map.generateMapDropRangeCache();
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static AbstractLoadedMapleLife loadLife(int id, String type, int cy, int f, int fh, int rx0, int rx1, int x, int y, int hide) {
|
||||
AbstractLoadedMapleLife myLife = MapleLifeFactory.getLife(id, type);
|
||||
myLife.setCy(cy);
|
||||
myLife.setF(f);
|
||||
myLife.setFh(fh);
|
||||
myLife.setRx0(rx0);
|
||||
myLife.setRx1(rx1);
|
||||
myLife.setPosition(new Point(x, y));
|
||||
if (hide == 1) {
|
||||
myLife.setHide(true);
|
||||
}
|
||||
return myLife;
|
||||
}
|
||||
|
||||
private static MapleReactor loadReactor(MapleData reactor, String id, final byte FacingDirection) {
|
||||
MapleReactor myReactor = new MapleReactor(MapleReactorFactory.getReactor(Integer.parseInt(id)), Integer.parseInt(id));
|
||||
int x = MapleDataTool.getInt(reactor.getChildByPath("x"));
|
||||
int y = MapleDataTool.getInt(reactor.getChildByPath("y"));
|
||||
myReactor.setFacingDirection(FacingDirection);
|
||||
myReactor.setPosition(new Point(x, y));
|
||||
myReactor.setDelay(MapleDataTool.getInt(reactor.getChildByPath("reactorTime")) * 1000);
|
||||
myReactor.setName(MapleDataTool.getString(reactor.getChildByPath("name"), ""));
|
||||
myReactor.resetReactorActions(0);
|
||||
return myReactor;
|
||||
}
|
||||
|
||||
private static String getMapName(int mapid) {
|
||||
String mapName = StringUtil.getLeftPaddedStr(Integer.toString(mapid), '0', 9);
|
||||
StringBuilder builder = new StringBuilder("Map/Map");
|
||||
int area = mapid / 100000000;
|
||||
builder.append(area);
|
||||
builder.append("/");
|
||||
builder.append(mapName);
|
||||
builder.append(".img");
|
||||
mapName = builder.toString();
|
||||
return mapName;
|
||||
}
|
||||
|
||||
private static String getMapStringName(int mapid) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (mapid < 100000000) {
|
||||
builder.append("maple");
|
||||
} else if (mapid >= 100000000 && mapid < 200000000) {
|
||||
builder.append("victoria");
|
||||
} else if (mapid >= 200000000 && mapid < 300000000) {
|
||||
builder.append("ossyria");
|
||||
} else if (mapid >= 300000000 && mapid < 400000000) {
|
||||
builder.append("elin");
|
||||
} else if (mapid >= 540000000 && mapid < 560000000) {
|
||||
builder.append("singapore");
|
||||
} else if (mapid >= 600000000 && mapid < 620000000) {
|
||||
builder.append("MasteriaGL");
|
||||
} else if (mapid >= 677000000 && mapid < 677100000) {
|
||||
builder.append("Episode1GL");
|
||||
} else if (mapid >= 670000000 && mapid < 682000000) {
|
||||
if ((mapid >= 674030000 && mapid < 674040000) || (mapid >= 680100000 && mapid < 680200000)) {
|
||||
builder.append("etc");
|
||||
} else {
|
||||
builder.append("weddingGL");
|
||||
}
|
||||
} else if (mapid >= 682000000 && mapid < 683000000) {
|
||||
builder.append("HalloweenGL");
|
||||
} else if (mapid >= 683000000 && mapid < 684000000) {
|
||||
builder.append("event");
|
||||
} else if (mapid >= 800000000 && mapid < 900000000) {
|
||||
if ((mapid >= 889100000 && mapid < 889200000)) {
|
||||
builder.append("etc");
|
||||
} else {
|
||||
builder.append("jp");
|
||||
}
|
||||
} else {
|
||||
builder.append("etc");
|
||||
}
|
||||
builder.append("/").append(mapid);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String loadPlaceName(int mapid) {
|
||||
try {
|
||||
return MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(mapid)), "");
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String loadStreetName(int mapid) {
|
||||
try {
|
||||
return MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(mapid)), "");
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
216
src/main/java/server/maps/MapleMapItem.java
Normal file
216
src/main/java/server/maps/MapleMapItem.java
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
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 client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.inventory.Item;
|
||||
import java.awt.Point;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import tools.MaplePacketCreator;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
|
||||
public class MapleMapItem extends AbstractMapleMapObject {
|
||||
protected MapleClient ownerClient;
|
||||
protected Item item;
|
||||
protected MapleMapObject dropper;
|
||||
protected int character_ownerid, party_ownerid, meso, questid = -1;
|
||||
protected byte type;
|
||||
protected boolean pickedUp = false, playerDrop, partyDrop;
|
||||
protected long dropTime;
|
||||
private Lock itemLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MAP_ITEM);
|
||||
|
||||
public MapleMapItem(Item item, Point position, MapleMapObject dropper, MapleCharacter owner, MapleClient ownerClient, byte type, boolean playerDrop) {
|
||||
setPosition(position);
|
||||
this.item = item;
|
||||
this.dropper = dropper;
|
||||
this.character_ownerid = owner.getId();
|
||||
this.party_ownerid = owner.getPartyId();
|
||||
this.partyDrop = this.party_ownerid != -1;
|
||||
this.ownerClient = owner.getClient();
|
||||
this.meso = 0;
|
||||
this.type = type;
|
||||
this.playerDrop = playerDrop;
|
||||
}
|
||||
|
||||
public MapleMapItem(Item item, Point position, MapleMapObject dropper, MapleCharacter owner, MapleClient ownerClient, byte type, boolean playerDrop, int questid) {
|
||||
setPosition(position);
|
||||
this.item = item;
|
||||
this.dropper = dropper;
|
||||
this.character_ownerid = owner.getId();
|
||||
this.party_ownerid = owner.getPartyId();
|
||||
this.partyDrop = this.party_ownerid != -1;
|
||||
this.ownerClient = owner.getClient();
|
||||
this.meso = 0;
|
||||
this.type = type;
|
||||
this.playerDrop = playerDrop;
|
||||
this.questid = questid;
|
||||
}
|
||||
|
||||
public MapleMapItem(int meso, Point position, MapleMapObject dropper, MapleCharacter owner, MapleClient ownerClient, byte type, boolean playerDrop) {
|
||||
setPosition(position);
|
||||
this.item = null;
|
||||
this.dropper = dropper;
|
||||
this.character_ownerid = owner.getId();
|
||||
this.party_ownerid = owner.getPartyId();
|
||||
this.partyDrop = this.party_ownerid != -1;
|
||||
this.ownerClient = owner.getClient();
|
||||
this.meso = meso;
|
||||
this.type = type;
|
||||
this.playerDrop = playerDrop;
|
||||
}
|
||||
|
||||
public final Item getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public final int getQuest() {
|
||||
return questid;
|
||||
}
|
||||
|
||||
public final int getItemId() {
|
||||
if (meso > 0) return meso;
|
||||
return item.getItemId();
|
||||
}
|
||||
|
||||
public final MapleMapObject getDropper() {
|
||||
return dropper;
|
||||
}
|
||||
|
||||
public final int getOwnerId() {
|
||||
return character_ownerid;
|
||||
}
|
||||
|
||||
public final int getPartyOwnerId() {
|
||||
return party_ownerid;
|
||||
}
|
||||
|
||||
public final void setPartyOwnerId(int partyid) {
|
||||
party_ownerid = partyid;
|
||||
}
|
||||
|
||||
public final int getClientsideOwnerId() { // thanks nozphex (RedHat) for noting an issue with collecting party items
|
||||
if (this.party_ownerid == -1) {
|
||||
return this.character_ownerid;
|
||||
} else {
|
||||
return this.party_ownerid;
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean hasClientsideOwnership(MapleCharacter player) {
|
||||
return this.character_ownerid == player.getId() || this.party_ownerid == player.getPartyId() || hasExpiredOwnershipTime();
|
||||
}
|
||||
|
||||
public final boolean isFFADrop() {
|
||||
return type == 2 || type == 3 || hasExpiredOwnershipTime();
|
||||
}
|
||||
|
||||
public final boolean hasExpiredOwnershipTime() {
|
||||
return System.currentTimeMillis() - dropTime >= 15 * 1000;
|
||||
}
|
||||
|
||||
public final boolean canBePickedBy(MapleCharacter chr) {
|
||||
if (character_ownerid <= 0 || isFFADrop()) return true;
|
||||
|
||||
if (party_ownerid == -1) {
|
||||
if (chr.getId() == character_ownerid) {
|
||||
return true;
|
||||
} else if (chr.isPartyMember(character_ownerid)) {
|
||||
party_ownerid = chr.getPartyId();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (chr.getPartyId() == party_ownerid) {
|
||||
return true;
|
||||
} else if (chr.getId() == character_ownerid) {
|
||||
party_ownerid = chr.getPartyId();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return hasExpiredOwnershipTime();
|
||||
}
|
||||
|
||||
public final MapleClient getOwnerClient() {
|
||||
return (ownerClient.isLoggedIn() && !ownerClient.getPlayer().isAwayFromWorld()) ? ownerClient : null;
|
||||
}
|
||||
|
||||
public final int getMeso() {
|
||||
return meso;
|
||||
}
|
||||
|
||||
public final boolean isPlayerDrop() {
|
||||
return playerDrop;
|
||||
}
|
||||
|
||||
public final boolean isPickedUp() {
|
||||
return pickedUp;
|
||||
}
|
||||
|
||||
public void setPickedUp(final boolean pickedUp) {
|
||||
this.pickedUp = pickedUp;
|
||||
}
|
||||
|
||||
public long getDropTime() {
|
||||
return dropTime;
|
||||
}
|
||||
|
||||
public void setDropTime(long time) {
|
||||
this.dropTime = time;
|
||||
}
|
||||
|
||||
public byte getDropType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void lockItem() {
|
||||
itemLock.lock();
|
||||
}
|
||||
|
||||
public void unlockItem() {
|
||||
itemLock.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final MapleMapObjectType getType() {
|
||||
return MapleMapObjectType.ITEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(final MapleClient client) {
|
||||
MapleCharacter chr = client.getPlayer();
|
||||
|
||||
if (chr.needQuestItem(questid, getItemId())) {
|
||||
this.lockItem();
|
||||
try {
|
||||
client.announce(MaplePacketCreator.dropItemFromMapObject(chr, this, null, getPosition(), (byte) 2));
|
||||
} finally {
|
||||
this.unlockItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyData(final MapleClient client) {
|
||||
client.announce(MaplePacketCreator.removeItemFromMap(getObjectId(), 1, 0));
|
||||
}
|
||||
}
|
||||
143
src/main/java/server/maps/MapleMapManager.java
Normal file
143
src/main/java/server/maps/MapleMapManager.java
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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.maps;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReadLock;
|
||||
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
|
||||
import net.server.audit.locks.MonitoredWriteLock;
|
||||
import net.server.audit.locks.factory.MonitoredReadLockFactory;
|
||||
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
|
||||
import scripting.event.EventInstanceManager;
|
||||
|
||||
public class MapleMapManager {
|
||||
|
||||
private int channel, world;
|
||||
private EventInstanceManager event;
|
||||
|
||||
private Map<Integer, MapleMap> maps = new HashMap<>();
|
||||
|
||||
private MonitoredReadLock mapsRLock;
|
||||
private MonitoredWriteLock mapsWLock;
|
||||
|
||||
public MapleMapManager(EventInstanceManager eim, int world, int channel) {
|
||||
this.world = world;
|
||||
this.channel = channel;
|
||||
this.event = eim;
|
||||
|
||||
MonitoredReentrantReadWriteLock rrwl = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_MANAGER);
|
||||
this.mapsRLock = MonitoredReadLockFactory.createLock(rrwl);
|
||||
this.mapsWLock = MonitoredWriteLockFactory.createLock(rrwl);
|
||||
}
|
||||
|
||||
public MapleMap resetMap(int mapid) {
|
||||
mapsWLock.lock();
|
||||
try {
|
||||
maps.remove(mapid);
|
||||
} finally {
|
||||
mapsWLock.unlock();
|
||||
}
|
||||
|
||||
return getMap(mapid);
|
||||
}
|
||||
|
||||
private synchronized MapleMap loadMapFromWz(int mapid, boolean cache) {
|
||||
MapleMap map;
|
||||
|
||||
if (cache) {
|
||||
mapsRLock.lock();
|
||||
try {
|
||||
map = maps.get(mapid);
|
||||
} finally {
|
||||
mapsRLock.unlock();
|
||||
}
|
||||
|
||||
if (map != null) {
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
map = MapleMapFactory.loadMapFromWz(mapid, world, channel, event);
|
||||
|
||||
if (cache) {
|
||||
mapsWLock.lock();
|
||||
try {
|
||||
maps.put(mapid, map);
|
||||
} finally {
|
||||
mapsWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public MapleMap getMap(int mapid) {
|
||||
MapleMap map;
|
||||
|
||||
mapsRLock.lock();
|
||||
try {
|
||||
map = maps.get(mapid);
|
||||
} finally {
|
||||
mapsRLock.unlock();
|
||||
}
|
||||
|
||||
return (map != null) ? map : loadMapFromWz(mapid, true);
|
||||
}
|
||||
|
||||
public MapleMap getDisposableMap(int mapid) {
|
||||
return loadMapFromWz(mapid, false);
|
||||
}
|
||||
|
||||
public boolean isMapLoaded(int mapId) {
|
||||
mapsRLock.lock();
|
||||
try {
|
||||
return maps.containsKey(mapId);
|
||||
} finally {
|
||||
mapsRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<Integer, MapleMap> getMaps() {
|
||||
mapsRLock.lock();
|
||||
try {
|
||||
return new HashMap<>(maps);
|
||||
} finally {
|
||||
mapsRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMaps() {
|
||||
for (MapleMap map : getMaps().values()) {
|
||||
map.respawn();
|
||||
map.mobMpRecovery();
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
for (MapleMap map : getMaps().values()) {
|
||||
map.dispose();
|
||||
}
|
||||
|
||||
this.event = null;
|
||||
}
|
||||
|
||||
}
|
||||
36
src/main/java/server/maps/MapleMapObject.java
Normal file
36
src/main/java/server/maps/MapleMapObject.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
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 client.MapleClient;
|
||||
|
||||
public interface MapleMapObject {
|
||||
public int getObjectId();
|
||||
public void setObjectId(int id);
|
||||
public MapleMapObjectType getType();
|
||||
public Point getPosition();
|
||||
public void setPosition(Point position);
|
||||
public void sendSpawnData(MapleClient client);
|
||||
public void sendDestroyData(MapleClient client);
|
||||
public void nullifyPosition();
|
||||
}
|
||||
26
src/main/java/server/maps/MapleMapObjectType.java
Normal file
26
src/main/java/server/maps/MapleMapObjectType.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
public enum MapleMapObjectType {
|
||||
NPC, MONSTER, ITEM, PLAYER, DOOR, SUMMON, SHOP, MINI_GAME, MIST, REACTOR, HIRED_MERCHANT, PLAYER_NPC, DRAGON, KITE;
|
||||
}
|
||||
28
src/main/java/server/maps/MapleMapPortal.java
Normal file
28
src/main/java/server/maps/MapleMapPortal.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
public class MapleMapPortal extends MapleGenericPortal {
|
||||
public MapleMapPortal() {
|
||||
super(MaplePortal.MAP_PORTAL);
|
||||
}
|
||||
}
|
||||
126
src/main/java/server/maps/MapleMiniDungeon.java
Normal file
126
src/main/java/server/maps/MapleMiniDungeon.java
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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.maps;
|
||||
|
||||
import server.TimerManager;
|
||||
import client.MapleCharacter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import tools.MaplePacketCreator;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleMiniDungeon {
|
||||
List<MapleCharacter> players = new ArrayList<>();
|
||||
ScheduledFuture<?> timeoutTask = null;
|
||||
Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MINIDUNGEON, true);
|
||||
|
||||
int baseMap;
|
||||
long expireTime;
|
||||
|
||||
public MapleMiniDungeon(int base, long timeLimit) {
|
||||
baseMap = base;
|
||||
expireTime = timeLimit * 1000;
|
||||
|
||||
timeoutTask = TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
close();
|
||||
}
|
||||
}, expireTime);
|
||||
|
||||
expireTime += System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public boolean registerPlayer(MapleCharacter chr) {
|
||||
int time = (int)((expireTime - System.currentTimeMillis()) / 1000);
|
||||
if(time > 0) chr.getClient().announce(MaplePacketCreator.getClock(time));
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
if(timeoutTask == null) return false;
|
||||
|
||||
players.add(chr);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean unregisterPlayer(MapleCharacter chr) {
|
||||
chr.getClient().announce(MaplePacketCreator.removeClock());
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
players.remove(chr);
|
||||
|
||||
if(players.isEmpty()) {
|
||||
dispose();
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
if (chr.isPartyLeader()) { // thanks Conrad for noticing party is not sent out of the MD as soon as leader leaves it
|
||||
close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
lock.lock();
|
||||
try {
|
||||
List<MapleCharacter> lchr = new ArrayList<>(players);
|
||||
|
||||
for(MapleCharacter chr : lchr) {
|
||||
chr.changeMap(baseMap);
|
||||
}
|
||||
|
||||
dispose();
|
||||
timeoutTask = null;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
lock.lock();
|
||||
try {
|
||||
players.clear();
|
||||
|
||||
if(timeoutTask != null) {
|
||||
timeoutTask.cancel(false);
|
||||
timeoutTask = null;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
85
src/main/java/server/maps/MapleMiniDungeonInfo.java
Normal file
85
src/main/java/server/maps/MapleMiniDungeonInfo.java
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alan (SharpAceX)
|
||||
*/
|
||||
|
||||
public enum MapleMiniDungeonInfo {
|
||||
|
||||
//http://bbb.hidden-street.net/search_finder/mini%20dungeon
|
||||
|
||||
CAVE_OF_MUSHROOMS(105050100, 105050101, 30),
|
||||
GOLEM_CASTLE_RUINS(105040304, 105040320, 34),
|
||||
HILL_OF_SANDSTORMS(260020600, 260020630, 30),
|
||||
HENESYS_PIG_FARM(100020000, 100020100, 30),
|
||||
DRAKES_BLUE_CAVE(105090311, 105090320, 30),
|
||||
DRUMMER_BUNNYS_LAIR(221023400, 221023401, 30),
|
||||
THE_ROUND_TABLE_OF_KENTARUS(240020500, 240020512, 30),
|
||||
THE_RESTORING_MEMORY(240040511, 240040800, 19),
|
||||
NEWT_SECURED_ZONE(240040520, 240040900, 19),
|
||||
PILLAGE_OF_TREASURE_ISLAND(251010402, 251010410, 30),
|
||||
CRITICAL_ERROR(261020300, 261020301, 30),
|
||||
LONGEST_RIDE_ON_BYEBYE_STATION(551030000, 551030001, 19);
|
||||
|
||||
private int baseId;
|
||||
private int dungeonId;
|
||||
private int dungeons;
|
||||
|
||||
private MapleMiniDungeonInfo(int baseId, int dungeonId, int dungeons) {
|
||||
this.baseId = baseId;
|
||||
this.dungeonId = dungeonId;
|
||||
this.dungeons = dungeons;
|
||||
}
|
||||
|
||||
public int getBase() {
|
||||
return baseId;
|
||||
}
|
||||
|
||||
public int getDungeonId() {
|
||||
return dungeonId;
|
||||
}
|
||||
|
||||
public int getDungeons() {
|
||||
return dungeons;
|
||||
}
|
||||
|
||||
public static boolean isDungeonMap(int map){
|
||||
for (MapleMiniDungeonInfo dungeon : MapleMiniDungeonInfo.values()){
|
||||
if (map >= dungeon.getDungeonId() && map <= dungeon.getDungeonId() + dungeon.getDungeons()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static MapleMiniDungeonInfo getDungeon(int map){
|
||||
for (MapleMiniDungeonInfo dungeon : MapleMiniDungeonInfo.values()){
|
||||
if (map >= dungeon.getDungeonId() && map <= dungeon.getDungeonId() + dungeon.getDungeons()){
|
||||
return dungeon;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
520
src/main/java/server/maps/MapleMiniGame.java
Normal file
520
src/main/java/server/maps/MapleMiniGame.java
Normal file
@@ -0,0 +1,520 @@
|
||||
/*
|
||||
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 client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import net.server.Server;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
* @author Ronan (HeavenMS)
|
||||
*/
|
||||
public class MapleMiniGame extends AbstractMapleMapObject {
|
||||
private MapleCharacter owner;
|
||||
private MapleCharacter visitor;
|
||||
private String password;
|
||||
private MiniGameType GameType = MiniGameType.UNDEFINED;
|
||||
private int piecetype;
|
||||
private int inprogress = 0;
|
||||
private int[] piece = new int[250];
|
||||
private List<Integer> list4x3 = new ArrayList<>();
|
||||
private List<Integer> list5x4 = new ArrayList<>();
|
||||
private List<Integer> list6x5 = new ArrayList<>();
|
||||
private String description;
|
||||
private int loser = 1;
|
||||
private int firstslot = 0;
|
||||
private int visitorpoints = 0, visitorscore = 0, visitorforfeits = 0, lastvisitor = -1;
|
||||
private int ownerpoints = 0, ownerscore = 0, ownerforfeits = 0;
|
||||
private boolean visitorquit, ownerquit;
|
||||
private long nextavailabletie = 0;
|
||||
private int matchestowin = 0;
|
||||
|
||||
public static enum MiniGameType {
|
||||
UNDEFINED(0), OMOK(1), MATCH_CARD(2);
|
||||
private int value = 0;
|
||||
|
||||
private MiniGameType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum MiniGameResult {
|
||||
WIN, LOSS, TIE;
|
||||
}
|
||||
|
||||
public MapleMiniGame(MapleCharacter owner, String description, String password) {
|
||||
this.owner = owner;
|
||||
this.description = description;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
public boolean checkPassword(String sentPw) {
|
||||
return this.password.length() == 0 || sentPw.toLowerCase().contentEquals(this.password.toLowerCase());
|
||||
}
|
||||
|
||||
public boolean hasFreeSlot() {
|
||||
return visitor == null;
|
||||
}
|
||||
|
||||
public boolean isOwner(MapleCharacter chr) {
|
||||
return owner.equals(chr);
|
||||
}
|
||||
|
||||
public void addVisitor(MapleCharacter challenger) {
|
||||
visitor = challenger;
|
||||
if (lastvisitor != challenger.getId()) {
|
||||
ownerscore = 0;
|
||||
ownerforfeits = 0;
|
||||
|
||||
visitorscore = 0;
|
||||
visitorforfeits = 0;
|
||||
lastvisitor = challenger.getId();
|
||||
}
|
||||
|
||||
MapleCharacter owner = this.getOwner();
|
||||
if (GameType == MiniGameType.OMOK) {
|
||||
owner.announce(MaplePacketCreator.getMiniGameNewVisitor(this, challenger, 1));
|
||||
owner.getMap().broadcastMessage(MaplePacketCreator.addOmokBox(owner, 2, 0));
|
||||
} else if (GameType == MiniGameType.MATCH_CARD) {
|
||||
owner.announce(MaplePacketCreator.getMatchCardNewVisitor(this, challenger, 1));
|
||||
owner.getMap().broadcastMessage(MaplePacketCreator.addMatchCardBox(owner, 2, 0));
|
||||
}
|
||||
}
|
||||
|
||||
public void closeRoom(boolean forceClose) {
|
||||
owner.getMap().broadcastMessage(MaplePacketCreator.removeMinigameBox(owner));
|
||||
|
||||
if (forceClose) {
|
||||
this.broadcastToOwner(MaplePacketCreator.getMiniGameClose(false, 4));
|
||||
}
|
||||
this.broadcastToVisitor(MaplePacketCreator.getMiniGameClose(true, 3));
|
||||
|
||||
if (visitor != null) {
|
||||
visitor.setMiniGame(null);
|
||||
visitor = null;
|
||||
}
|
||||
|
||||
owner.setMiniGame(null);
|
||||
owner = null;
|
||||
}
|
||||
|
||||
public void removeVisitor(boolean forceClose, MapleCharacter challenger) {
|
||||
if (visitor == challenger) {
|
||||
if (forceClose) {
|
||||
visitor.announce(MaplePacketCreator.getMiniGameClose(true, 4));
|
||||
}
|
||||
|
||||
challenger.setMiniGame(null);
|
||||
visitor = null;
|
||||
|
||||
this.getOwner().getClient().announce(MaplePacketCreator.getMiniGameRemoveVisitor());
|
||||
if (GameType == MiniGameType.OMOK) {
|
||||
this.getOwner().getMap().broadcastMessage(MaplePacketCreator.addOmokBox(owner, 1, 0));
|
||||
} else if (GameType == MiniGameType.MATCH_CARD) {
|
||||
this.getOwner().getMap().broadcastMessage(MaplePacketCreator.addMatchCardBox(owner, 1, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isVisitor(MapleCharacter challenger) {
|
||||
return visitor == challenger;
|
||||
}
|
||||
|
||||
public void broadcastToOwner(final byte[] packet) {
|
||||
MapleClient c = owner.getClient();
|
||||
if (c != null && c.getSession() != null) {
|
||||
c.announce(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastToVisitor(final byte[] packet) {
|
||||
if (visitor != null) {
|
||||
visitor.getClient().announce(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFirstSlot(int type) {
|
||||
firstslot = type;
|
||||
}
|
||||
|
||||
public int getFirstSlot() {
|
||||
return firstslot;
|
||||
}
|
||||
|
||||
private void updateMiniGameBox() {
|
||||
this.getOwner().getMap().broadcastMessage(MaplePacketCreator.addOmokBox(owner, visitor != null ? 2 : 1, inprogress));
|
||||
}
|
||||
|
||||
private synchronized boolean minigameMatchFinish() {
|
||||
if (isMatchInProgress()) {
|
||||
inprogress = 0;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void minigameMatchFinished() {
|
||||
updateMiniGameBox();
|
||||
|
||||
if (ownerquit) {
|
||||
owner.closeMiniGame(true);
|
||||
} else if (visitorquit) {
|
||||
visitor.closeMiniGame(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void minigameMatchStarted() {
|
||||
inprogress = 1;
|
||||
ownerquit = false;
|
||||
visitorquit = false;
|
||||
}
|
||||
|
||||
public void setQuitAfterGame(MapleCharacter player, boolean quit) {
|
||||
if (isOwner(player)) {
|
||||
ownerquit = quit;
|
||||
} else {
|
||||
visitorquit = quit;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMatchInProgress() {
|
||||
return inprogress != 0;
|
||||
}
|
||||
|
||||
public void denyTie(MapleCharacter chr) {
|
||||
if (this.isOwner(chr)) {
|
||||
inprogress |= (1 << 1);
|
||||
} else {
|
||||
inprogress |= (1 << 2);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTieDenied(MapleCharacter chr) {
|
||||
if (this.isOwner(chr)) {
|
||||
return ((inprogress >> 2) % 2) == 1;
|
||||
} else {
|
||||
return ((inprogress >> 1) % 2) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void minigameMatchOwnerWins(boolean forfeit) {
|
||||
if (!minigameMatchFinish()) return;
|
||||
|
||||
owner.setMiniGamePoints(visitor, 1, this.isOmok());
|
||||
|
||||
if (visitorforfeits < 4 || !forfeit) ownerscore += 50;
|
||||
visitorscore += (15 * (forfeit ? -1 : 1));
|
||||
if (forfeit) visitorforfeits++;
|
||||
|
||||
this.broadcast(MaplePacketCreator.getMiniGameOwnerWin(this, forfeit));
|
||||
|
||||
minigameMatchFinished();
|
||||
}
|
||||
|
||||
public void minigameMatchVisitorWins(boolean forfeit) {
|
||||
if (!minigameMatchFinish()) return;
|
||||
|
||||
owner.setMiniGamePoints(visitor, 2, this.isOmok());
|
||||
|
||||
if (ownerforfeits < 4 || !forfeit) visitorscore += 50;
|
||||
ownerscore += (15 * (forfeit ? -1 : 1));
|
||||
if (forfeit) ownerforfeits++;
|
||||
|
||||
this.broadcast(MaplePacketCreator.getMiniGameVisitorWin(this, forfeit));
|
||||
|
||||
minigameMatchFinished();
|
||||
}
|
||||
|
||||
public void minigameMatchDraw() {
|
||||
if (!minigameMatchFinish()) return;
|
||||
|
||||
owner.setMiniGamePoints(visitor, 3, this.isOmok());
|
||||
|
||||
long timeNow = Server.getInstance().getCurrentTime();
|
||||
if (nextavailabletie <= timeNow) {
|
||||
visitorscore += 10;
|
||||
ownerscore += 10;
|
||||
|
||||
nextavailabletie = timeNow + 5 * 60 * 1000;
|
||||
}
|
||||
|
||||
this.broadcast(MaplePacketCreator.getMiniGameTie(this));
|
||||
|
||||
minigameMatchFinished();
|
||||
}
|
||||
|
||||
public void setOwnerPoints() {
|
||||
ownerpoints++;
|
||||
if (ownerpoints + visitorpoints == matchestowin) {
|
||||
if (ownerpoints == visitorpoints) {
|
||||
minigameMatchDraw();
|
||||
} else if (ownerpoints > visitorpoints) {
|
||||
minigameMatchOwnerWins(false);
|
||||
} else {
|
||||
minigameMatchVisitorWins(false);
|
||||
}
|
||||
ownerpoints = 0;
|
||||
visitorpoints = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void setVisitorPoints() {
|
||||
visitorpoints++;
|
||||
if (ownerpoints + visitorpoints == matchestowin) {
|
||||
if (ownerpoints > visitorpoints) {
|
||||
minigameMatchOwnerWins(false);
|
||||
} else if (visitorpoints > ownerpoints) {
|
||||
minigameMatchVisitorWins(false);
|
||||
} else {
|
||||
minigameMatchDraw();
|
||||
}
|
||||
ownerpoints = 0;
|
||||
visitorpoints = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMatchesToWin(int type) {
|
||||
matchestowin = type;
|
||||
}
|
||||
|
||||
public void setPieceType(int type) {
|
||||
piecetype = type;
|
||||
}
|
||||
|
||||
public int getPieceType() {
|
||||
return piecetype;
|
||||
}
|
||||
|
||||
public void setGameType(MiniGameType game) {
|
||||
GameType = game;
|
||||
if (GameType == MiniGameType.MATCH_CARD) {
|
||||
if (matchestowin == 6) {
|
||||
for (int i = 0; i < 6; i++) {
|
||||
list4x3.add(i);
|
||||
list4x3.add(i);
|
||||
}
|
||||
} else if (matchestowin == 10) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
list5x4.add(i);
|
||||
list5x4.add(i);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 15; i++) {
|
||||
list6x5.add(i);
|
||||
list6x5.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MiniGameType getGameType() {
|
||||
return GameType;
|
||||
}
|
||||
|
||||
public boolean isOmok() {
|
||||
return GameType.equals(MiniGameType.OMOK);
|
||||
}
|
||||
|
||||
public void shuffleList() {
|
||||
if (matchestowin == 6) {
|
||||
Collections.shuffle(list4x3);
|
||||
} else if (matchestowin == 10) {
|
||||
Collections.shuffle(list5x4);
|
||||
} else {
|
||||
Collections.shuffle(list6x5);
|
||||
}
|
||||
}
|
||||
|
||||
public int getCardId(int slot) {
|
||||
int cardid;
|
||||
if (matchestowin == 6) {
|
||||
cardid = list4x3.get(slot);
|
||||
} else if (matchestowin == 10) {
|
||||
cardid = list5x4.get(slot);
|
||||
} else {
|
||||
cardid = list6x5.get(slot);
|
||||
}
|
||||
return cardid;
|
||||
}
|
||||
|
||||
public int getMatchesToWin() {
|
||||
return matchestowin;
|
||||
}
|
||||
|
||||
public void setLoser(int type) {
|
||||
loser = type;
|
||||
}
|
||||
|
||||
public int getLoser() {
|
||||
return loser;
|
||||
}
|
||||
|
||||
public void broadcast(final byte[] packet) {
|
||||
broadcastToOwner(packet);
|
||||
broadcastToVisitor(packet);
|
||||
}
|
||||
|
||||
public void chat(MapleClient c, String chat) {
|
||||
broadcast(MaplePacketCreator.getPlayerShopChat(c.getPlayer(), chat, isOwner(c.getPlayer())));
|
||||
}
|
||||
|
||||
public void sendOmok(MapleClient c, int type) {
|
||||
c.announce(MaplePacketCreator.getMiniGame(c, this, isOwner(c.getPlayer()), type));
|
||||
}
|
||||
|
||||
public void sendMatchCard(MapleClient c, int type) {
|
||||
c.announce(MaplePacketCreator.getMatchCard(c, this, isOwner(c.getPlayer()), type));
|
||||
}
|
||||
|
||||
public MapleCharacter getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public MapleCharacter getVisitor() {
|
||||
return visitor;
|
||||
}
|
||||
|
||||
public void setPiece(int move1, int move2, int type, MapleCharacter chr) {
|
||||
int slot = move2 * 15 + move1 + 1;
|
||||
if (piece[slot] == 0) {
|
||||
piece[slot] = type;
|
||||
this.broadcast(MaplePacketCreator.getMiniGameMoveOmok(this, move1, move2, type));
|
||||
for (int y = 0; y < 15; y++) {
|
||||
for (int x = 0; x < 11; x++) {
|
||||
if (searchCombo(x, y, type)) {
|
||||
if (this.isOwner(chr)) {
|
||||
this.minigameMatchOwnerWins(false);
|
||||
this.setLoser(0);
|
||||
} else {
|
||||
this.minigameMatchVisitorWins(false);
|
||||
this.setLoser(1);
|
||||
}
|
||||
for (int y2 = 0; y2 < 15; y2++) {
|
||||
for (int x2 = 0; x2 < 15; x2++) {
|
||||
int slot2 = (y2 * 15 + x2 + 1);
|
||||
piece[slot2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int y = 0; y < 15; y++) {
|
||||
for (int x = 4; x < 15; x++) {
|
||||
if (searchCombo2(x, y, type)) {
|
||||
if (this.isOwner(chr)) {
|
||||
this.minigameMatchOwnerWins(false);
|
||||
this.setLoser(0);
|
||||
} else {
|
||||
this.minigameMatchVisitorWins(false);
|
||||
this.setLoser(1);
|
||||
}
|
||||
for (int y2 = 0; y2 < 15; y2++) {
|
||||
for (int x2 = 0; x2 < 15; x2++) {
|
||||
int slot2 = (y2 * 15 + x2 + 1);
|
||||
piece[slot2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean searchCombo(int x, int y, int type) {
|
||||
int slot = y * 15 + x + 1;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (piece[slot + i] == type) {
|
||||
if (i == 4) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int j = 15; j < 17; j++) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (piece[slot + i * j] == type) {
|
||||
if (i == 4) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean searchCombo2(int x, int y, int type) {
|
||||
int slot = y * 15 + x + 1;
|
||||
for (int j = 14; j < 15; j++) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (piece[slot + i * j] == type) {
|
||||
if (i == 4) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public int getOwnerScore() {
|
||||
return ownerscore;
|
||||
}
|
||||
|
||||
public int getVisitorScore() {
|
||||
return visitorscore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyData(MapleClient client) {}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(MapleClient client) {}
|
||||
|
||||
@Override
|
||||
public MapleMapObjectType getType() {
|
||||
return MapleMapObjectType.MINI_GAME;
|
||||
}
|
||||
}
|
||||
168
src/main/java/server/maps/MapleMist.java
Normal file
168
src/main/java/server/maps/MapleMist.java
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
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 client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import constants.skills.BlazeWizard;
|
||||
import constants.skills.Evan;
|
||||
import constants.skills.FPMage;
|
||||
import constants.skills.NightWalker;
|
||||
import constants.skills.Shadower;
|
||||
import server.MapleStatEffect;
|
||||
import server.life.MapleMonster;
|
||||
import server.life.MobSkill;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LaiLaiNoob
|
||||
*/
|
||||
public class MapleMist extends AbstractMapleMapObject {
|
||||
private Rectangle mistPosition;
|
||||
private MapleCharacter owner = null;
|
||||
private MapleMonster mob = null;
|
||||
private MapleStatEffect source;
|
||||
private MobSkill skill;
|
||||
private boolean isMobMist, isPoisonMist, isRecoveryMist;
|
||||
private int skillDelay;
|
||||
|
||||
public MapleMist(Rectangle mistPosition, MapleMonster mob, MobSkill skill) {
|
||||
this.mistPosition = mistPosition;
|
||||
this.mob = mob;
|
||||
this.skill = skill;
|
||||
isMobMist = true;
|
||||
isPoisonMist = true;
|
||||
isRecoveryMist = false;
|
||||
skillDelay = 0;
|
||||
}
|
||||
|
||||
public MapleMist(Rectangle mistPosition, MapleCharacter owner, MapleStatEffect source) {
|
||||
this.mistPosition = mistPosition;
|
||||
this.owner = owner;
|
||||
this.source = source;
|
||||
this.skillDelay = 8;
|
||||
this.isMobMist = false;
|
||||
this.isRecoveryMist = false;
|
||||
this.isPoisonMist = false;
|
||||
switch (source.getSourceId()) {
|
||||
case Evan.RECOVERY_AURA:
|
||||
isRecoveryMist = true;
|
||||
break;
|
||||
|
||||
case Shadower.SMOKE_SCREEN: // Smoke Screen
|
||||
isPoisonMist = false;
|
||||
break;
|
||||
|
||||
case FPMage.POISON_MIST: // FP mist
|
||||
case BlazeWizard.FLAME_GEAR: // Flame Gear
|
||||
case NightWalker.POISON_BOMB: // Poison Bomb
|
||||
isPoisonMist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapleMapObjectType getType() {
|
||||
return MapleMapObjectType.MIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getPosition() {
|
||||
return mistPosition.getLocation();
|
||||
}
|
||||
|
||||
public Skill getSourceSkill() {
|
||||
return SkillFactory.getSkill(source.getSourceId());
|
||||
}
|
||||
|
||||
public boolean isMobMist() {
|
||||
return isMobMist;
|
||||
}
|
||||
|
||||
public boolean isPoisonMist() {
|
||||
return isPoisonMist;
|
||||
}
|
||||
|
||||
public boolean isRecoveryMist() {
|
||||
return isRecoveryMist;
|
||||
}
|
||||
|
||||
public int getSkillDelay() {
|
||||
return skillDelay;
|
||||
}
|
||||
|
||||
public MapleMonster getMobOwner() {
|
||||
return mob;
|
||||
}
|
||||
|
||||
public MapleCharacter getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public Rectangle getBox() {
|
||||
return mistPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(Point position) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public final byte[] makeDestroyData() {
|
||||
return MaplePacketCreator.removeMist(getObjectId());
|
||||
}
|
||||
|
||||
public final byte[] makeSpawnData() {
|
||||
if (owner != null) {
|
||||
return MaplePacketCreator.spawnMist(getObjectId(), owner.getId(), getSourceSkill().getId(), owner.getSkillLevel(SkillFactory.getSkill(source.getSourceId())), this);
|
||||
}
|
||||
return MaplePacketCreator.spawnMist(getObjectId(), mob.getId(), skill.getSkillId(), skill.getSkillLevel(), this);
|
||||
}
|
||||
|
||||
public final byte[] makeFakeSpawnData(int level) {
|
||||
if (owner != null) {
|
||||
return MaplePacketCreator.spawnMist(getObjectId(), owner.getId(), getSourceSkill().getId(), level, this);
|
||||
}
|
||||
return MaplePacketCreator.spawnMist(getObjectId(), mob.getId(), skill.getSkillId(), skill.getSkillLevel(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(MapleClient client) {
|
||||
client.announce(makeSpawnData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyData(MapleClient client) {
|
||||
client.announce(makeDestroyData());
|
||||
}
|
||||
|
||||
public boolean makeChanceResult() {
|
||||
return source.makeChanceResult();
|
||||
}
|
||||
}
|
||||
620
src/main/java/server/maps/MaplePlayerShop.java
Normal file
620
src/main/java/server/maps/MaplePlayerShop.java
Normal file
@@ -0,0 +1,620 @@
|
||||
/*
|
||||
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 client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import client.inventory.manipulator.MapleKarmaManipulator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.opcodes.SendOpcode;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
import tools.data.output.MaplePacketLittleEndianWriter;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import server.MapleTrade;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
* @author Ronan - concurrency protection
|
||||
*/
|
||||
public class MaplePlayerShop extends AbstractMapleMapObject {
|
||||
private AtomicBoolean open = new AtomicBoolean(false);
|
||||
private MapleCharacter owner;
|
||||
private int itemid;
|
||||
|
||||
private MapleCharacter[] visitors = new MapleCharacter[3];
|
||||
private List<MaplePlayerShopItem> items = new ArrayList<>();
|
||||
private List<SoldItem> sold = new LinkedList<>();
|
||||
private String description;
|
||||
private int boughtnumber = 0;
|
||||
private List<String> bannedList = new ArrayList<>();
|
||||
private List<Pair<MapleCharacter, String>> chatLog = new LinkedList<>();
|
||||
private Map<Integer, Byte> chatSlot = new LinkedHashMap<>();
|
||||
private Lock visitorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.VISITOR_PSHOP, true);
|
||||
|
||||
public MaplePlayerShop(MapleCharacter owner, String description, int itemid) {
|
||||
this.setPosition(owner.getPosition());
|
||||
this.owner = owner;
|
||||
this.description = description;
|
||||
this.itemid = itemid;
|
||||
}
|
||||
|
||||
public int getChannel() {
|
||||
return owner.getClient().getChannel();
|
||||
}
|
||||
|
||||
public int getMapId() {
|
||||
return owner.getMapId();
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return itemid;
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return open.get();
|
||||
}
|
||||
|
||||
public void setOpen(boolean openShop) {
|
||||
open.set(openShop);
|
||||
}
|
||||
|
||||
public boolean hasFreeSlot() {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
return visitors[0] == null || visitors[1] == null || visitors[2] == null;
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getShopRoomInfo() {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
byte count = 0;
|
||||
//if (this.isOpen()) {
|
||||
for (MapleCharacter visitor : visitors) {
|
||||
if (visitor != null) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
//} else { shouldn't happen since there isn't a "closed" state for player shops.
|
||||
// count = (byte) (visitors.length + 1);
|
||||
//}
|
||||
|
||||
return new byte[]{count, (byte) visitors.length};
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isOwner(MapleCharacter chr) {
|
||||
return owner.equals(chr);
|
||||
}
|
||||
|
||||
private void addVisitor(MapleCharacter visitor) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] == null) {
|
||||
visitors[i] = visitor;
|
||||
visitor.setSlot(i);
|
||||
|
||||
this.broadcast(MaplePacketCreator.getPlayerShopNewVisitor(visitor, i + 1));
|
||||
owner.getMap().broadcastMessage(MaplePacketCreator.updatePlayerShopBox(this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void forceRemoveVisitor(MapleCharacter visitor) {
|
||||
if (visitor == owner) {
|
||||
owner.getMap().removeMapObject(this);
|
||||
owner.setPlayerShop(null);
|
||||
}
|
||||
|
||||
visitorLock.lock();
|
||||
try {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] != null && visitors[i].getId() == visitor.getId()) {
|
||||
visitors[i].setPlayerShop(null);
|
||||
visitors[i] = null;
|
||||
visitor.setSlot(-1);
|
||||
|
||||
this.broadcast(MaplePacketCreator.getPlayerShopRemoveVisitor(i + 1));
|
||||
owner.getMap().broadcastMessage(MaplePacketCreator.updatePlayerShopBox(this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeVisitor(MapleCharacter visitor) {
|
||||
if (visitor == owner) {
|
||||
owner.getMap().removeMapObject(this);
|
||||
owner.setPlayerShop(null);
|
||||
} else {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] != null && visitors[i].getId() == visitor.getId()) {
|
||||
visitor.setSlot(-1); //absolutely cant remove player slot for late players without dc'ing them... heh
|
||||
|
||||
for(int j = i; j < 2; j++) {
|
||||
if(visitors[j] != null) owner.announce(MaplePacketCreator.getPlayerShopRemoveVisitor(j + 1));
|
||||
visitors[j] = visitors[j + 1];
|
||||
if(visitors[j] != null) visitors[j].setSlot(j);
|
||||
}
|
||||
visitors[2] = null;
|
||||
for(int j = i; j < 2; j++) {
|
||||
if(visitors[j] != null) owner.announce(MaplePacketCreator.getPlayerShopNewVisitor(visitors[j], j + 1));
|
||||
}
|
||||
|
||||
this.broadcastRestoreToVisitors();
|
||||
owner.getMap().broadcastMessage(MaplePacketCreator.updatePlayerShopBox(this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
|
||||
owner.getMap().broadcastMessage(MaplePacketCreator.updatePlayerShopBox(this));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isVisitor(MapleCharacter visitor) {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
return visitors[0] == visitor || visitors[1] == visitor || visitors[2] == visitor;
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean addItem(MaplePlayerShopItem item) {
|
||||
synchronized (items) {
|
||||
if (items.size() >= 16) return false;
|
||||
|
||||
items.add(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeFromSlot(int slot) {
|
||||
items.remove(slot);
|
||||
}
|
||||
|
||||
private static boolean canBuy(MapleClient c, Item newItem) {
|
||||
return MapleInventoryManipulator.checkSpace(c, newItem.getItemId(), newItem.getQuantity(), newItem.getOwner()) && MapleInventoryManipulator.addFromDrop(c, newItem, false);
|
||||
}
|
||||
|
||||
public void takeItemBack(int slot, MapleCharacter chr) {
|
||||
synchronized (items) {
|
||||
MaplePlayerShopItem shopItem = items.get(slot);
|
||||
if(shopItem.isExist()) {
|
||||
if (shopItem.getBundles() > 0) {
|
||||
Item iitem = shopItem.getItem().copy();
|
||||
iitem.setQuantity((short) (shopItem.getItem().getQuantity() * shopItem.getBundles()));
|
||||
|
||||
if (!MapleInventory.checkSpot(chr, iitem)) {
|
||||
chr.announce(MaplePacketCreator.serverNotice(1, "Have a slot available on your inventory to claim back the item."));
|
||||
chr.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
MapleInventoryManipulator.addFromDrop(chr.getClient(), iitem, true);
|
||||
}
|
||||
|
||||
removeFromSlot(slot);
|
||||
chr.announce(MaplePacketCreator.getPlayerShopItemUpdate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* no warnings for now o.o
|
||||
* @param c
|
||||
* @param item
|
||||
* @param quantity
|
||||
*/
|
||||
public boolean buy(MapleClient c, int item, short quantity) {
|
||||
synchronized (items) {
|
||||
if (isVisitor(c.getPlayer())) {
|
||||
MaplePlayerShopItem pItem = items.get(item);
|
||||
Item newItem = pItem.getItem().copy();
|
||||
|
||||
newItem.setQuantity((short) ((pItem.getItem().getQuantity() * quantity)));
|
||||
if (quantity < 1 || !pItem.isExist() || pItem.getBundles() < quantity) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
} else if (newItem.getInventoryType().equals(MapleInventoryType.EQUIP) && newItem.getQuantity() > 1) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(newItem);
|
||||
|
||||
visitorLock.lock();
|
||||
try {
|
||||
int price = (int) Math.min((float)pItem.getPrice() * quantity, Integer.MAX_VALUE);
|
||||
|
||||
if (c.getPlayer().getMeso() >= price) {
|
||||
if (!owner.canHoldMeso(price)) { // thanks Rohenn for noticing owner hold check misplaced
|
||||
c.getPlayer().dropMessage(1, "Transaction failed since the shop owner can't hold any more mesos.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (canBuy(c, newItem)) {
|
||||
c.getPlayer().gainMeso(-price, false);
|
||||
price -= MapleTrade.getFee(price); // thanks BHB for pointing out trade fees not applying here
|
||||
owner.gainMeso(price, true);
|
||||
|
||||
SoldItem soldItem = new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), quantity, price);
|
||||
owner.announce(MaplePacketCreator.getPlayerShopOwnerUpdate(soldItem, item));
|
||||
|
||||
synchronized (sold) {
|
||||
sold.add(soldItem);
|
||||
}
|
||||
|
||||
pItem.setBundles((short) (pItem.getBundles() - quantity));
|
||||
if (pItem.getBundles() < 1) {
|
||||
pItem.setDoesExist(false);
|
||||
if (++boughtnumber == items.size()) {
|
||||
owner.setPlayerShop(null);
|
||||
this.setOpen(false);
|
||||
this.closeShop();
|
||||
owner.dropMessage(1, "Your items are sold out, and therefore your shop is closed.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.getPlayer().dropMessage(1, "Your inventory is full. Please clear a slot before buying this item.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
c.getPlayer().dropMessage(1, "You don't have enough mesos to purchase this item.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastToVisitors(final byte[] packet) {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] != null) {
|
||||
visitors[i].getClient().announce(packet);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastRestoreToVisitors() {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] != null) {
|
||||
visitors[i].getClient().announce(MaplePacketCreator.getPlayerShopRemoveVisitor(i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] != null) {
|
||||
visitors[i].getClient().announce(MaplePacketCreator.getPlayerShop(this, false));
|
||||
}
|
||||
}
|
||||
|
||||
recoverChatLog();
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeVisitors() {
|
||||
List<MapleCharacter> visitorList = new ArrayList<>(3);
|
||||
|
||||
visitorLock.lock();
|
||||
try {
|
||||
try {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] != null) {
|
||||
visitors[i].getClient().announce(MaplePacketCreator.shopErrorMessage(10, 1));
|
||||
visitorList.add(visitors[i]);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
|
||||
for(MapleCharacter mc : visitorList) forceRemoveVisitor(mc);
|
||||
if (owner != null) {
|
||||
forceRemoveVisitor(owner);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] shopErrorMessage(int error, int type) {
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
|
||||
mplew.write(0x0A);
|
||||
mplew.write(type);
|
||||
mplew.write(error);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public void broadcast(final byte[] packet) {
|
||||
if (owner.getClient() != null && owner.getClient().getSession() != null) {
|
||||
owner.getClient().announce(packet);
|
||||
}
|
||||
broadcastToVisitors(packet);
|
||||
}
|
||||
|
||||
private byte getVisitorSlot(MapleCharacter chr) {
|
||||
byte s = 0;
|
||||
for (MapleCharacter mc : getVisitors()) {
|
||||
s++;
|
||||
if (mc != null) {
|
||||
if (mc.getName().equalsIgnoreCase(chr.getName())) {
|
||||
break;
|
||||
}
|
||||
} else if (s == 3) {
|
||||
s = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public void chat(MapleClient c, String chat) {
|
||||
byte s = getVisitorSlot(c.getPlayer());
|
||||
|
||||
synchronized(chatLog) {
|
||||
chatLog.add(new Pair<>(c.getPlayer(), chat));
|
||||
if(chatLog.size() > 25) chatLog.remove(0);
|
||||
chatSlot.put(c.getPlayer().getId(), s);
|
||||
}
|
||||
|
||||
broadcast(MaplePacketCreator.getPlayerShopChat(c.getPlayer(), chat, s));
|
||||
}
|
||||
|
||||
private void recoverChatLog() {
|
||||
synchronized(chatLog) {
|
||||
for(Pair<MapleCharacter, String> it : chatLog) {
|
||||
MapleCharacter chr = it.getLeft();
|
||||
Byte pos = chatSlot.get(chr.getId());
|
||||
|
||||
broadcastToVisitors(MaplePacketCreator.getPlayerShopChat(chr, it.getRight(), pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clearChatLog() {
|
||||
synchronized(chatLog) {
|
||||
chatLog.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void closeShop() {
|
||||
clearChatLog();
|
||||
removeVisitors();
|
||||
owner.getMap().broadcastMessage(MaplePacketCreator.removePlayerShopBox(this));
|
||||
}
|
||||
|
||||
public void sendShop(MapleClient c) {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
c.announce(MaplePacketCreator.getPlayerShop(this, isOwner(c.getPlayer())));
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public MapleCharacter getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public MapleCharacter[] getVisitors() {
|
||||
visitorLock.lock();
|
||||
try {
|
||||
MapleCharacter[] copy = new MapleCharacter[3];
|
||||
for(int i = 0; i < visitors.length; i++) copy[i] = visitors[i];
|
||||
|
||||
return copy;
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public List<MaplePlayerShopItem> getItems() {
|
||||
synchronized (items) {
|
||||
return Collections.unmodifiableList(items);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasItem(int itemid) {
|
||||
for(MaplePlayerShopItem mpsi : getItems()) {
|
||||
if(mpsi.getItem().getItemId() == itemid && mpsi.isExist() && mpsi.getBundles() > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public void banPlayer(String name) {
|
||||
if (!bannedList.contains(name)) {
|
||||
bannedList.add(name);
|
||||
}
|
||||
|
||||
MapleCharacter target = null;
|
||||
visitorLock.lock();
|
||||
try {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] != null && visitors[i].getName().equals(name)) {
|
||||
target = visitors[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
|
||||
if(target != null) {
|
||||
target.getClient().announce(MaplePacketCreator.shopErrorMessage(5, 1));
|
||||
removeVisitor(target);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBanned(String name) {
|
||||
return bannedList.contains(name);
|
||||
}
|
||||
|
||||
public synchronized boolean visitShop(MapleCharacter chr) {
|
||||
if (this.isBanned(chr.getName())) {
|
||||
chr.dropMessage(1, "You have been banned from this store.");
|
||||
return false;
|
||||
}
|
||||
|
||||
visitorLock.lock();
|
||||
try {
|
||||
if(!open.get()) {
|
||||
chr.dropMessage(1, "This store is not yet open.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.hasFreeSlot() && !this.isVisitor(chr)) {
|
||||
this.addVisitor(chr);
|
||||
chr.setPlayerShop(this);
|
||||
this.sendShop(chr.getClient());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
visitorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public List<MaplePlayerShopItem> sendAvailableBundles(int itemid) {
|
||||
List<MaplePlayerShopItem> list = new LinkedList<>();
|
||||
List<MaplePlayerShopItem> all = new ArrayList<>();
|
||||
|
||||
synchronized (items) {
|
||||
for(MaplePlayerShopItem mpsi : items) all.add(mpsi);
|
||||
}
|
||||
|
||||
for(MaplePlayerShopItem mpsi : all) {
|
||||
if(mpsi.getItem().getItemId() == itemid && mpsi.getBundles() > 0 && mpsi.isExist()) {
|
||||
list.add(mpsi);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<SoldItem> getSold() {
|
||||
synchronized (sold) {
|
||||
return Collections.unmodifiableList(sold);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyData(MapleClient client) {
|
||||
client.announce(MaplePacketCreator.removePlayerShopBox(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(MapleClient client) {
|
||||
client.announce(MaplePacketCreator.updatePlayerShopBox(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapleMapObjectType getType() {
|
||||
return MapleMapObjectType.SHOP;
|
||||
}
|
||||
|
||||
public class SoldItem {
|
||||
|
||||
int itemid, mesos;
|
||||
short quantity;
|
||||
String buyer;
|
||||
|
||||
public SoldItem(String buyer, int itemid, short quantity, int mesos) {
|
||||
this.buyer = buyer;
|
||||
this.itemid = itemid;
|
||||
this.quantity = quantity;
|
||||
this.mesos = mesos;
|
||||
}
|
||||
|
||||
public String getBuyer() {
|
||||
return buyer;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return itemid;
|
||||
}
|
||||
|
||||
public short getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public int getMesos() {
|
||||
return mesos;
|
||||
}
|
||||
}
|
||||
}
|
||||
66
src/main/java/server/maps/MaplePlayerShopItem.java
Normal file
66
src/main/java/server/maps/MaplePlayerShopItem.java
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
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 client.inventory.Item;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
*/
|
||||
public class MaplePlayerShopItem {
|
||||
private Item item;
|
||||
private short bundles;
|
||||
private int price;
|
||||
private boolean doesExist;
|
||||
|
||||
public MaplePlayerShopItem(Item item, short bundles, int price) {
|
||||
this.item = item;
|
||||
this.bundles = bundles;
|
||||
this.price = price;
|
||||
this.doesExist = true;
|
||||
}
|
||||
|
||||
public void setDoesExist(boolean tf) {
|
||||
this.doesExist = tf;
|
||||
}
|
||||
|
||||
public boolean isExist() {
|
||||
return doesExist;
|
||||
}
|
||||
|
||||
public Item getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public short getBundles() {
|
||||
return bundles;
|
||||
}
|
||||
|
||||
public int getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setBundles(short bundles) {
|
||||
this.bundles = bundles;
|
||||
}
|
||||
}
|
||||
46
src/main/java/server/maps/MaplePortal.java
Normal file
46
src/main/java/server/maps/MaplePortal.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
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 client.MapleClient;
|
||||
|
||||
public interface MaplePortal {
|
||||
public final int TELEPORT_PORTAL = 1;
|
||||
public final int MAP_PORTAL = 2;
|
||||
public final int DOOR_PORTAL = 6;
|
||||
public static boolean OPEN = true;
|
||||
public static boolean CLOSED = false;
|
||||
int getType();
|
||||
int getId();
|
||||
Point getPosition();
|
||||
String getName();
|
||||
String getTarget();
|
||||
String getScriptName();
|
||||
void setScriptName(String newName);
|
||||
void setPortalStatus(boolean newStatus);
|
||||
boolean getPortalStatus();
|
||||
int getTargetMapId();
|
||||
void enterPortal(MapleClient c);
|
||||
void setPortalState(boolean state);
|
||||
boolean getPortalState();
|
||||
}
|
||||
68
src/main/java/server/maps/MaplePortalFactory.java
Normal file
68
src/main/java/server/maps/MaplePortalFactory.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
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 provider.MapleData;
|
||||
import provider.MapleDataTool;
|
||||
import server.maps.MapleGenericPortal;
|
||||
import server.maps.MapleMapPortal;
|
||||
|
||||
public class MaplePortalFactory {
|
||||
private int nextDoorPortal;
|
||||
|
||||
public MaplePortalFactory() {
|
||||
nextDoorPortal = 0x80;
|
||||
}
|
||||
|
||||
public MaplePortal makePortal(int type, MapleData portal) {
|
||||
MapleGenericPortal ret = null;
|
||||
if (type == MaplePortal.MAP_PORTAL) {
|
||||
ret = new MapleMapPortal();
|
||||
} else {
|
||||
ret = new MapleGenericPortal(type);
|
||||
}
|
||||
loadPortal(ret, portal);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void loadPortal(MapleGenericPortal myPortal, MapleData portal) {
|
||||
myPortal.setName(MapleDataTool.getString(portal.getChildByPath("pn")));
|
||||
myPortal.setTarget(MapleDataTool.getString(portal.getChildByPath("tn")));
|
||||
myPortal.setTargetMapId(MapleDataTool.getInt(portal.getChildByPath("tm")));
|
||||
int x = MapleDataTool.getInt(portal.getChildByPath("x"));
|
||||
int y = MapleDataTool.getInt(portal.getChildByPath("y"));
|
||||
myPortal.setPosition(new Point(x, 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
431
src/main/java/server/maps/MapleReactor.java
Normal file
431
src/main/java/server/maps/MapleReactor.java
Normal file
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
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 client.MapleClient;
|
||||
import config.YamlConfig;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
|
||||
import scripting.reactor.ReactorScriptManager;
|
||||
import server.TimerManager;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.services.type.ChannelServices;
|
||||
import net.server.services.task.channel.OverallService;
|
||||
import server.partyquest.GuardianSpawnPoint;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Lerk
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleReactor extends AbstractMapleMapObject {
|
||||
|
||||
private int rid;
|
||||
private MapleReactorStats stats;
|
||||
private byte state;
|
||||
private byte evstate;
|
||||
private int delay;
|
||||
private MapleMap map;
|
||||
private String name;
|
||||
private boolean alive;
|
||||
private boolean shouldCollect;
|
||||
private boolean attackHit;
|
||||
private ScheduledFuture<?> timeoutTask = null;
|
||||
private Runnable delayedRespawnRun = null;
|
||||
private GuardianSpawnPoint guardian = null;
|
||||
private byte facingDirection = 0;
|
||||
private Lock reactorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.REACTOR, true);
|
||||
private Lock hitLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.REACTOR_HIT, true);
|
||||
|
||||
public MapleReactor(MapleReactorStats stats, int rid) {
|
||||
this.evstate = (byte) 0;
|
||||
this.stats = stats;
|
||||
this.rid = rid;
|
||||
this.alive = true;
|
||||
}
|
||||
|
||||
public void setShouldCollect(boolean collect) {
|
||||
this.shouldCollect = collect;
|
||||
}
|
||||
|
||||
public boolean getShouldCollect() {
|
||||
return shouldCollect;
|
||||
}
|
||||
|
||||
public void lockReactor() {
|
||||
reactorLock.lock();
|
||||
}
|
||||
|
||||
public void unlockReactor() {
|
||||
reactorLock.unlock();
|
||||
}
|
||||
|
||||
public void hitLockReactor() {
|
||||
hitLock.lock();
|
||||
reactorLock.lock();
|
||||
}
|
||||
|
||||
public void hitUnlockReactor() {
|
||||
reactorLock.unlock();
|
||||
hitLock.unlock();
|
||||
}
|
||||
|
||||
public void setState(byte state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public byte getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setEventState(byte substate) {
|
||||
this.evstate = substate;
|
||||
}
|
||||
|
||||
public byte getEventState() {
|
||||
return evstate;
|
||||
}
|
||||
|
||||
public MapleReactorStats getStats() {
|
||||
return stats;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return rid;
|
||||
}
|
||||
|
||||
public void setDelay(int delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public int getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapleMapObjectType getType() {
|
||||
return MapleMapObjectType.REACTOR;
|
||||
}
|
||||
|
||||
public int getReactorType() {
|
||||
return stats.getType(state);
|
||||
}
|
||||
|
||||
public boolean isRecentHitFromAttack() {
|
||||
return attackHit;
|
||||
}
|
||||
|
||||
public void setMap(MapleMap map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public MapleMap getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public Pair<Integer, Integer> getReactItem(byte index) {
|
||||
return stats.getReactItem(state, index);
|
||||
}
|
||||
|
||||
public boolean isAlive() {
|
||||
return alive;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return alive && stats.getType(state) != -1;
|
||||
}
|
||||
|
||||
public void setAlive(boolean alive) {
|
||||
this.alive = alive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyData(MapleClient client) {
|
||||
client.announce(makeDestroyData());
|
||||
}
|
||||
|
||||
public final byte[] makeDestroyData() {
|
||||
return MaplePacketCreator.destroyReactor(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSpawnData(MapleClient client) {
|
||||
if (this.isAlive()) {
|
||||
client.announce(makeSpawnData());
|
||||
}
|
||||
}
|
||||
|
||||
public final byte[] makeSpawnData() {
|
||||
return MaplePacketCreator.spawnReactor(this);
|
||||
}
|
||||
|
||||
public void resetReactorActions(int newState) {
|
||||
setState((byte) newState);
|
||||
cancelReactorTimeout();
|
||||
setShouldCollect(true);
|
||||
refreshReactorTimeout();
|
||||
|
||||
if (map != null) {
|
||||
map.searchItemReactors(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void forceHitReactor(final byte newState) {
|
||||
this.lockReactor();
|
||||
try {
|
||||
this.resetReactorActions(newState);
|
||||
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, (short) 0));
|
||||
} finally {
|
||||
this.unlockReactor();
|
||||
}
|
||||
}
|
||||
|
||||
private void tryForceHitReactor(final byte newState) { // weak hit state signal, if already changed reactor state before timeout then drop this
|
||||
if (!reactorLock.tryLock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.resetReactorActions(newState);
|
||||
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, (short) 0));
|
||||
} finally {
|
||||
reactorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelReactorTimeout() {
|
||||
if (timeoutTask != null) {
|
||||
timeoutTask.cancel(false);
|
||||
timeoutTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshReactorTimeout() {
|
||||
int timeOut = stats.getTimeout(state);
|
||||
if (timeOut > -1) {
|
||||
final byte nextState = stats.getTimeoutState(state);
|
||||
|
||||
timeoutTask = TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
timeoutTask = null;
|
||||
tryForceHitReactor(nextState);
|
||||
}
|
||||
}, timeOut);
|
||||
}
|
||||
}
|
||||
|
||||
public void delayedHitReactor(final MapleClient c, long delay) {
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hitReactor(c);
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
|
||||
public void hitReactor(MapleClient c) {
|
||||
hitReactor(false, 0, (short) 0, 0, c);
|
||||
}
|
||||
|
||||
public void hitReactor(boolean wHit, int charPos, short stance, int skillid, MapleClient c) {
|
||||
try {
|
||||
if (!this.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hitLock.tryLock()) {
|
||||
this.lockReactor();
|
||||
try {
|
||||
cancelReactorTimeout();
|
||||
attackHit = wHit;
|
||||
|
||||
if (YamlConfig.config.server.USE_DEBUG == true) {
|
||||
c.getPlayer().dropMessage(5, "Hitted REACTOR " + this.getId() + " with POS " + charPos + " , STANCE " + stance + " , SkillID " + skillid + " , STATE " + stats.getType(state) + " STATESIZE " + stats.getStateSize(state));
|
||||
}
|
||||
ReactorScriptManager.getInstance().onHit(c, this);
|
||||
|
||||
int reactorType = stats.getType(state);
|
||||
if (reactorType < 999 && reactorType != -1) {//type 2 = only hit from right (kerning swamp plants), 00 is air left 02 is ground left
|
||||
if (!(reactorType == 2 && (stance == 0 || stance == 2))) { //get next state
|
||||
for (byte b = 0; b < stats.getStateSize(state); b++) {//YAY?
|
||||
List<Integer> activeSkills = stats.getActiveSkills(state, b);
|
||||
if (activeSkills != null) {
|
||||
if (!activeSkills.contains(skillid)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
state = stats.getNextState(state, b);
|
||||
if (stats.getNextState(state, b) == -1) {//end of reactor
|
||||
if (reactorType < 100) {//reactor broken
|
||||
if (delay > 0) {
|
||||
map.destroyReactor(getObjectId());
|
||||
} else {//trigger as normal
|
||||
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, stance));
|
||||
}
|
||||
} else {//item-triggered on final step
|
||||
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, stance));
|
||||
}
|
||||
|
||||
ReactorScriptManager.getInstance().act(c, this);
|
||||
} else { //reactor not broken yet
|
||||
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, stance));
|
||||
if (state == stats.getNextState(state, b)) {//current state = next state, looping reactor
|
||||
ReactorScriptManager.getInstance().act(c, this);
|
||||
}
|
||||
|
||||
setShouldCollect(true); // refresh collectability on item drop-based reactors
|
||||
refreshReactorTimeout();
|
||||
if (stats.getType(state) == 100) {
|
||||
map.searchItemReactors(this);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state++;
|
||||
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, stance));
|
||||
if (this.getId() != 9980000 && this.getId() != 9980001) {
|
||||
ReactorScriptManager.getInstance().act(c, this);
|
||||
}
|
||||
|
||||
setShouldCollect(true);
|
||||
refreshReactorTimeout();
|
||||
if (stats.getType(state) == 100) {
|
||||
map.searchItemReactors(this);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.unlockReactor();
|
||||
hitLock.unlock(); // non-encapsulated unlock found thanks to MiLin
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean destroy() {
|
||||
if (reactorLock.tryLock()) {
|
||||
try {
|
||||
boolean alive = this.isAlive();
|
||||
if (alive) {
|
||||
this.setAlive(false);
|
||||
this.cancelReactorTimeout();
|
||||
|
||||
if (this.getDelay() > 0) {
|
||||
this.delayedRespawn();
|
||||
}
|
||||
} else if (this.inDelayedRespawn()) {
|
||||
return false;
|
||||
} else {
|
||||
return true; // reactor neither alive nor in delayed respawn, remove map object allowed
|
||||
}
|
||||
} finally {
|
||||
reactorLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
map.broadcastMessage(MaplePacketCreator.destroyReactor(this));
|
||||
return false;
|
||||
}
|
||||
|
||||
private void respawn() {
|
||||
this.lockReactor();
|
||||
try {
|
||||
this.resetReactorActions(0);
|
||||
this.setAlive(true);
|
||||
} finally {
|
||||
this.unlockReactor();
|
||||
}
|
||||
|
||||
map.broadcastMessage(this.makeSpawnData());
|
||||
}
|
||||
|
||||
public void delayedRespawn() {
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
delayedRespawnRun = null;
|
||||
respawn();
|
||||
}
|
||||
};
|
||||
|
||||
delayedRespawnRun = r;
|
||||
|
||||
OverallService service = (OverallService) map.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
|
||||
service.registerOverallAction(map.getId(), r, this.getDelay());
|
||||
}
|
||||
|
||||
public boolean forceDelayedRespawn() {
|
||||
Runnable r = delayedRespawnRun;
|
||||
|
||||
if (r != null) {
|
||||
OverallService service = (OverallService) map.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
|
||||
service.forceRunOverallAction(map.getId(), r);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean inDelayedRespawn() {
|
||||
return delayedRespawnRun != null;
|
||||
}
|
||||
|
||||
public Rectangle getArea() {
|
||||
return new Rectangle(getPosition().x + stats.getTL().x, getPosition().y + stats.getTL().y, stats.getBR().x - stats.getTL().x, stats.getBR().y - stats.getTL().y);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public GuardianSpawnPoint getGuardian() {
|
||||
return guardian;
|
||||
}
|
||||
|
||||
public void setGuardian(GuardianSpawnPoint guardian) {
|
||||
this.guardian = guardian;
|
||||
}
|
||||
|
||||
public final void setFacingDirection(final byte facingDirection) {
|
||||
this.facingDirection = facingDirection;
|
||||
}
|
||||
|
||||
public final byte getFacingDirection() {
|
||||
return facingDirection;
|
||||
}
|
||||
}
|
||||
172
src/main/java/server/maps/MapleReactorFactory.java
Normal file
172
src/main/java/server/maps/MapleReactorFactory.java
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
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.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import server.maps.MapleReactorStats.StateData;
|
||||
import tools.Pair;
|
||||
import tools.StringUtil;
|
||||
|
||||
public class MapleReactorFactory {
|
||||
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Reactor.wz"));
|
||||
private static Map<Integer, MapleReactorStats> reactorStats = new HashMap<Integer, MapleReactorStats>();
|
||||
|
||||
|
||||
public static final MapleReactorStats getReactorS(int rid) {
|
||||
MapleReactorStats stats = reactorStats.get(Integer.valueOf(rid));
|
||||
if (stats == null) {
|
||||
int infoId = rid;
|
||||
MapleData reactorData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(infoId) + ".img", '0', 11));
|
||||
MapleData link = reactorData.getChildByPath("info/link");
|
||||
if (link != null) {
|
||||
infoId = MapleDataTool.getIntConvert("info/link", reactorData);
|
||||
stats = reactorStats.get(Integer.valueOf(infoId));
|
||||
}
|
||||
if (stats == null) {
|
||||
stats = new MapleReactorStats();
|
||||
reactorData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(infoId) + ".img", '0', 11));
|
||||
if (reactorData == null) {
|
||||
return stats;
|
||||
}
|
||||
boolean canTouch = MapleDataTool.getInt("info/activateByTouch", reactorData, 0) > 0;
|
||||
boolean areaSet = false;
|
||||
boolean foundState = false;
|
||||
for (byte i = 0; true; i++) {
|
||||
MapleData reactorD = reactorData.getChildByPath(String.valueOf(i));
|
||||
if (reactorD == null) {
|
||||
break;
|
||||
}
|
||||
MapleData reactorInfoData_ = reactorD.getChildByPath("event");
|
||||
if (reactorInfoData_ != null && reactorInfoData_.getChildByPath("0") != null) {
|
||||
MapleData reactorInfoData = reactorInfoData_.getChildByPath("0");
|
||||
Pair<Integer, Integer> reactItem = null;
|
||||
int type = MapleDataTool.getIntConvert("type", reactorInfoData);
|
||||
if (type == 100) { //reactor waits for item
|
||||
reactItem = new Pair<Integer, Integer>(MapleDataTool.getIntConvert("0", reactorInfoData), MapleDataTool.getIntConvert("1", reactorInfoData, 1));
|
||||
if (!areaSet) { //only set area of effect for item-triggered reactors once
|
||||
stats.setTL(MapleDataTool.getPoint("lt", reactorInfoData));
|
||||
stats.setBR(MapleDataTool.getPoint("rb", reactorInfoData));
|
||||
areaSet = true;
|
||||
}
|
||||
}
|
||||
foundState = true;
|
||||
stats.addState(i, type, reactItem, (byte) MapleDataTool.getIntConvert("state", reactorInfoData), MapleDataTool.getIntConvert("timeOut", reactorInfoData_, -1), (byte) (canTouch ? 2 : (MapleDataTool.getIntConvert("2", reactorInfoData, 0) > 0 || reactorInfoData.getChildByPath("clickArea") != null || type == 9 ? 1 : 0)));
|
||||
} else {
|
||||
stats.addState(i, 999, null, (byte) (foundState ? -1 : (i + 1)), 0, (byte) 0);
|
||||
}
|
||||
}
|
||||
reactorStats.put(Integer.valueOf(infoId), stats);
|
||||
if (rid != infoId) {
|
||||
reactorStats.put(Integer.valueOf(rid), stats);
|
||||
}
|
||||
} else { // stats exist at infoId but not rid; add to map
|
||||
reactorStats.put(Integer.valueOf(rid), stats);
|
||||
}
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
public static MapleReactorStats getReactor(int rid) {
|
||||
MapleReactorStats stats = reactorStats.get(Integer.valueOf(rid));
|
||||
if (stats == null) {
|
||||
int infoId = rid;
|
||||
MapleData reactorData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(infoId) + ".img", '0', 11));
|
||||
MapleData link = reactorData.getChildByPath("info/link");
|
||||
if (link != null) {
|
||||
infoId = MapleDataTool.getIntConvert("info/link", reactorData);
|
||||
stats = reactorStats.get(Integer.valueOf(infoId));
|
||||
}
|
||||
MapleData activateOnTouch = reactorData.getChildByPath("info/activateByTouch");
|
||||
boolean loadArea = false;
|
||||
if (activateOnTouch != null) {
|
||||
loadArea = MapleDataTool.getInt("info/activateByTouch", reactorData, 0) != 0;
|
||||
}
|
||||
if (stats == null) {
|
||||
reactorData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(infoId) + ".img", '0', 11));
|
||||
MapleData reactorInfoData = reactorData.getChildByPath("0");
|
||||
stats = new MapleReactorStats();
|
||||
List<StateData> statedatas = new ArrayList<>();
|
||||
if (reactorInfoData != null) {
|
||||
boolean areaSet = false;
|
||||
byte i = 0;
|
||||
while (reactorInfoData != null) {
|
||||
MapleData eventData = reactorInfoData.getChildByPath("event");
|
||||
if (eventData != null) {
|
||||
int timeOut = -1;
|
||||
|
||||
for (MapleData fknexon : eventData.getChildren()) {
|
||||
if (fknexon.getName().equalsIgnoreCase("timeOut")) {
|
||||
timeOut = MapleDataTool.getInt(fknexon);
|
||||
} else {
|
||||
Pair<Integer, Integer> reactItem = null;
|
||||
int type = MapleDataTool.getIntConvert("type", fknexon);
|
||||
if (type == 100) { //reactor waits for item
|
||||
reactItem = new Pair<Integer, Integer>(MapleDataTool.getIntConvert("0", fknexon), MapleDataTool.getIntConvert("1", fknexon));
|
||||
if (!areaSet || loadArea) { //only set area of effect for item-triggered reactors once
|
||||
stats.setTL(MapleDataTool.getPoint("lt", fknexon));
|
||||
stats.setBR(MapleDataTool.getPoint("rb", fknexon));
|
||||
areaSet = true;
|
||||
}
|
||||
}
|
||||
MapleData activeSkillID = fknexon.getChildByPath("activeSkillID");
|
||||
List<Integer> skillids = null;
|
||||
if (activeSkillID != null) {
|
||||
skillids = new ArrayList<Integer>();
|
||||
for (MapleData skill : activeSkillID.getChildren()) {
|
||||
skillids.add(MapleDataTool.getInt(skill));
|
||||
}
|
||||
}
|
||||
byte nextState = (byte) MapleDataTool.getIntConvert("state", fknexon);
|
||||
statedatas.add(new StateData(type, reactItem, skillids, nextState));
|
||||
}
|
||||
}
|
||||
stats.addState(i, statedatas, timeOut);
|
||||
}
|
||||
i++;
|
||||
reactorInfoData = reactorData.getChildByPath(Byte.toString(i));
|
||||
statedatas = new ArrayList<>();
|
||||
}
|
||||
} else //sit there and look pretty; likely a reactor such as Zakum/Papulatus doors that shows if player can enter
|
||||
{
|
||||
statedatas.add(new StateData(999, null, null, (byte) 0));
|
||||
stats.addState((byte) 0, statedatas, -1);
|
||||
}
|
||||
reactorStats.put(Integer.valueOf(infoId), stats);
|
||||
if (rid != infoId) {
|
||||
reactorStats.put(Integer.valueOf(rid), stats);
|
||||
}
|
||||
} else // stats exist at infoId but not rid; add to map
|
||||
{
|
||||
reactorStats.put(Integer.valueOf(rid), stats);
|
||||
}
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user