Merge branch 'refs/heads/master' into feat/postgresql-database
# Conflicts: # config.yaml # docker-compose.yml # pom.xml # src/main/java/client/Character.java # src/main/java/client/Client.java # src/main/java/client/MonsterBook.java # src/main/java/client/command/commands/gm0/BuyBackCommand.java # src/main/java/client/processor/stat/AssignAPProcessor.java # src/main/java/config/ServerConfig.java # src/main/java/net/server/channel/Channel.java # src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java # src/main/java/net/server/channel/handlers/BuddylistModifyHandler.java # src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java # src/main/java/net/server/channel/handlers/EnterMTSHandler.java # src/main/java/net/server/channel/handlers/NPCTalkHandler.java # src/main/java/net/server/channel/handlers/RangedAttackHandler.java # src/main/java/net/server/channel/handlers/SummonDamageHandler.java # src/main/java/net/server/channel/handlers/UseCashItemHandler.java # src/main/java/net/server/handlers/login/CreateCharHandler.java # src/main/java/net/server/world/World.java # src/main/java/scripting/npc/NPCConversationManager.java # src/main/java/server/ItemInformationProvider.java # src/main/java/server/life/Monster.java # src/main/java/server/life/MonsterInformationProvider.java # src/main/java/server/maps/MapleMap.java # src/main/java/tools/PacketCreator.java # src/test/java/service/NoteServiceTest.java # src/test/java/testutil/Any.java
This commit is contained in:
@@ -21,10 +21,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package server;
|
||||
|
||||
import client.inventory.*;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.InventoryType;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.ItemFactory;
|
||||
import client.inventory.Pet;
|
||||
import config.YamlConfig;
|
||||
import constants.id.ItemId;
|
||||
import constants.inventory.ItemConstants;
|
||||
import net.jcip.annotations.GuardedBy;
|
||||
import net.server.Server;
|
||||
import provider.Data;
|
||||
import provider.DataProvider;
|
||||
@@ -38,7 +43,13 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@@ -47,8 +58,74 @@ import static java.util.concurrent.TimeUnit.HOURS;
|
||||
|
||||
/*
|
||||
* @author Flav
|
||||
* @author Ponk
|
||||
*/
|
||||
public class CashShop {
|
||||
public static final int NX_CREDIT = 1;
|
||||
public static final int MAPLE_POINT = 2;
|
||||
public static final int NX_PREPAID = 4;
|
||||
|
||||
private final int accountId;
|
||||
private final int characterId;
|
||||
private int nxCredit;
|
||||
private int maplePoint;
|
||||
private int nxPrepaid;
|
||||
private boolean opened;
|
||||
private ItemFactory factory;
|
||||
private final List<Item> inventory = new ArrayList<>();
|
||||
private final List<Integer> wishList = new ArrayList<>();
|
||||
private int notes = 0;
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
public CashShop(int accountId, int characterId, int jobType) throws SQLException {
|
||||
this.accountId = accountId;
|
||||
this.characterId = characterId;
|
||||
|
||||
if (!YamlConfig.config.server.USE_JOINT_CASHSHOP_INVENTORY) {
|
||||
switch (jobType) {
|
||||
case 0:
|
||||
factory = ItemFactory.CASH_EXPLORER;
|
||||
break;
|
||||
case 1:
|
||||
factory = ItemFactory.CASH_CYGNUS;
|
||||
break;
|
||||
case 2:
|
||||
factory = ItemFactory.CASH_ARAN;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
factory = ItemFactory.CASH_OVERALL;
|
||||
}
|
||||
|
||||
try (Connection con = DatabaseConnection.getConnection()) {
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT `nxCredit`, `maplePoint`, `nxPrepaid` FROM `accounts` WHERE `id` = ?")) {
|
||||
ps.setInt(1, accountId);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
this.nxCredit = rs.getInt("nxCredit");
|
||||
this.maplePoint = rs.getInt("maplePoint");
|
||||
this.nxPrepaid = rs.getInt("nxPrepaid");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Pair<Item, InventoryType> item : factory.loadItems(accountId, false)) {
|
||||
inventory.add(item.getLeft());
|
||||
}
|
||||
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT `sn` FROM `wishlists` WHERE `charid` = ?")) {
|
||||
ps.setInt(1, characterId);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
wishList.add(rs.getInt("sn"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CashItem {
|
||||
|
||||
private final int sn;
|
||||
@@ -104,19 +181,20 @@ public class CashShop {
|
||||
if (ItemConstants.EXPIRING_ITEMS) {
|
||||
if (period == 1) {
|
||||
switch (itemId) {
|
||||
case ItemId.DROP_COUPON_2X_4H, ItemId.EXP_COUPON_2X_4H: // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day.
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(4));
|
||||
case ItemId.DROP_COUPON_2X_4H,
|
||||
ItemId.EXP_COUPON_2X_4H: // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day.
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(4));
|
||||
/*
|
||||
} else if(itemId == 5211047 || itemId == 5360014) { // 3 Hour 2X coupons, unused as of now
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(3));
|
||||
*/
|
||||
break;
|
||||
case ItemId.EXP_COUPON_3X_2H:
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(2));
|
||||
break;
|
||||
default:
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + DAYS.toMillis(1));
|
||||
break;
|
||||
break;
|
||||
case ItemId.EXP_COUPON_3X_2H:
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(2));
|
||||
break;
|
||||
default:
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + DAYS.toMillis(1));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
item.setExpiration(Server.getInstance().getCurrentTime() + DAYS.toMillis(period));
|
||||
@@ -154,7 +232,6 @@ public class CashShop {
|
||||
|
||||
public static class CashItemFactory {
|
||||
private static volatile Map<Integer, CashItem> items = new HashMap<>();
|
||||
private static volatile List<Integer> randomitemsns = new ArrayList<>();
|
||||
private static volatile Map<Integer, List<Integer>> packages = new HashMap<>();
|
||||
private static volatile List<SpecialCashItem> specialcashitems = new ArrayList<>();
|
||||
|
||||
@@ -162,7 +239,6 @@ public class CashShop {
|
||||
DataProvider etc = DataProviderFactory.getDataProvider(WZFiles.ETC);
|
||||
|
||||
Map<Integer, CashItem> loadedItems = new HashMap<>();
|
||||
List<Integer> onSaleItems = new ArrayList<>();
|
||||
for (Data item : etc.getData("Commodity.img").getChildren()) {
|
||||
int sn = DataTool.getIntConvert("SN", item);
|
||||
int itemId = DataTool.getIntConvert("ItemId", item);
|
||||
@@ -171,13 +247,8 @@ public class CashShop {
|
||||
short count = (short) DataTool.getIntConvert("Count", item, 1);
|
||||
boolean onSale = DataTool.getIntConvert("OnSale", item, 0) == 1;
|
||||
loadedItems.put(sn, new CashItem(sn, itemId, price, period, count, onSale));
|
||||
|
||||
if (onSale) {
|
||||
onSaleItems.add(sn);
|
||||
}
|
||||
}
|
||||
CashItemFactory.items = loadedItems;
|
||||
CashItemFactory.randomitemsns = onSaleItems;
|
||||
|
||||
Map<Integer, List<Integer>> loadedPackages = new HashMap<>();
|
||||
for (Data cashPackage : etc.getData("CashPackage.img").getChildren()) {
|
||||
@@ -204,13 +275,20 @@ public class CashShop {
|
||||
CashItemFactory.specialcashitems = loadedSpecialItems;
|
||||
}
|
||||
|
||||
public static CashItem getRandomCashItem() {
|
||||
if (randomitemsns.isEmpty()) {
|
||||
return null;
|
||||
public static Optional<CashItem> getRandomCashItem() {
|
||||
if (items.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
int rnd = (int) (Math.random() * randomitemsns.size());
|
||||
return items.get(randomitemsns.get(rnd));
|
||||
List<CashItem> itemPool = items.values().stream()
|
||||
.filter(CashItem::isOnSale)
|
||||
.filter(cashItem -> !ItemId.isCashPackage(cashItem.itemId))
|
||||
.toList();
|
||||
return Optional.of(getRandomItem(itemPool));
|
||||
}
|
||||
|
||||
private static CashItem getRandomItem(List<CashItem> items) {
|
||||
return items.get(new Random().nextInt(items.size()));
|
||||
}
|
||||
|
||||
public static CashItem getItem(int sn) {
|
||||
@@ -234,107 +312,26 @@ public class CashShop {
|
||||
public static List<SpecialCashItem> getSpecialCashItems() {
|
||||
return specialcashitems;
|
||||
}
|
||||
|
||||
public static void reloadSpecialCashItems() {//Yay?
|
||||
List<SpecialCashItem> loadedSpecialItems = new ArrayList<>();
|
||||
try (Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT * FROM specialcashitems");
|
||||
ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
loadedSpecialItems.add(new SpecialCashItem(rs.getInt("sn"), rs.getInt("modifier"), rs.getByte("info")));
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
CashItemFactory.specialcashitems = loadedSpecialItems;
|
||||
}
|
||||
}
|
||||
|
||||
private final int accountId;
|
||||
private final int characterId;
|
||||
private int nxCredit;
|
||||
private int maplePoint;
|
||||
private int nxPrepaid;
|
||||
private boolean opened;
|
||||
private ItemFactory factory;
|
||||
private final List<Item> inventory = new ArrayList<>();
|
||||
private final List<Integer> wishList = new ArrayList<>();
|
||||
private int notes = 0;
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
public CashShop(int accountId, int characterId, int jobType) throws SQLException {
|
||||
this.accountId = accountId;
|
||||
this.characterId = characterId;
|
||||
|
||||
if (!YamlConfig.config.server.USE_JOINT_CASHSHOP_INVENTORY) {
|
||||
switch (jobType) {
|
||||
case 0:
|
||||
factory = ItemFactory.CASH_EXPLORER;
|
||||
break;
|
||||
case 1:
|
||||
factory = ItemFactory.CASH_CYGNUS;
|
||||
break;
|
||||
case 2:
|
||||
factory = ItemFactory.CASH_ARAN;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
factory = ItemFactory.CASH_OVERALL;
|
||||
}
|
||||
|
||||
try (Connection con = DatabaseConnection.getConnection()) {
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT `nxCredit`, `maplePoint`, `nxPrepaid` FROM `accounts` WHERE `id` = ?")) {
|
||||
ps.setInt(1, accountId);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
this.nxCredit = rs.getInt("nxCredit");
|
||||
this.maplePoint = rs.getInt("maplePoint");
|
||||
this.nxPrepaid = rs.getInt("nxPrepaid");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Pair<Item, InventoryType> item : factory.loadItems(accountId, false)) {
|
||||
inventory.add(item.getLeft());
|
||||
}
|
||||
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT `sn` FROM `wishlists` WHERE `charid` = ?")) {
|
||||
ps.setInt(1, characterId);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
wishList.add(rs.getInt("sn"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public record CashShopSurpriseResult(Item usedCashShopSurprise, Item reward) {
|
||||
}
|
||||
|
||||
public int getCash(int type) {
|
||||
switch (type) {
|
||||
case 1:
|
||||
return nxCredit;
|
||||
case 2:
|
||||
return maplePoint;
|
||||
case 4:
|
||||
return nxPrepaid;
|
||||
}
|
||||
return switch (type) {
|
||||
case NX_CREDIT -> nxCredit;
|
||||
case MAPLE_POINT -> maplePoint;
|
||||
case NX_PREPAID -> nxPrepaid;
|
||||
default -> 0;
|
||||
};
|
||||
|
||||
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;
|
||||
case NX_CREDIT -> nxCredit += cash;
|
||||
case MAPLE_POINT -> maplePoint += cash;
|
||||
case NX_PREPAID -> nxPrepaid += cash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,47 +514,57 @@ public class CashShop {
|
||||
}
|
||||
}
|
||||
|
||||
private Item getCashShopItemByItemid(int itemid) {
|
||||
public Optional<CashShopSurpriseResult> openCashShopSurprise(long cashId) {
|
||||
lock.lock();
|
||||
try {
|
||||
for (Item it : inventory) {
|
||||
if (it.getItemId() == itemid) {
|
||||
return it;
|
||||
}
|
||||
Optional<Item> maybeCashShopSurprise = getItemByCashId(cashId);
|
||||
if (maybeCashShopSurprise.isEmpty() ||
|
||||
maybeCashShopSurprise.get().getItemId() != ItemId.CASH_SHOP_SURPRISE) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Item cashShopSurprise = maybeCashShopSurprise.get();
|
||||
if (cashShopSurprise.getQuantity() <= 0) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (getItemsSize() >= 100) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Optional<CashItem> cashItemReward = CashItemFactory.getRandomCashItem();
|
||||
if (cashItemReward.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
short newQuantity = (short) (cashShopSurprise.getQuantity() - 1);
|
||||
cashShopSurprise.setQuantity(newQuantity);
|
||||
if (newQuantity <= 0) {
|
||||
removeFromInventory(cashShopSurprise);
|
||||
}
|
||||
|
||||
Item itemReward = cashItemReward.get().toItem();
|
||||
addToInventory(itemReward);
|
||||
|
||||
return Optional.of(new CashShopSurpriseResult(cashShopSurprise, itemReward));
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public synchronized Pair<Item, Item> openCashShopSurprise() {
|
||||
Item css = getCashShopItemByItemid(ItemId.CASH_SHOP_SURPRISE);
|
||||
@GuardedBy("lock")
|
||||
private Optional<Item> getItemByCashId(long cashId) {
|
||||
return inventory.stream()
|
||||
.filter(item -> item.getCashId() == cashId)
|
||||
.findAny();
|
||||
}
|
||||
|
||||
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 int getItemsSize() {
|
||||
lock.lock();
|
||||
try {
|
||||
return inventory.size();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
package server;
|
||||
|
||||
import config.YamlConfig;
|
||||
import tools.DatabaseConnection;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.sql.Timestamp;
|
||||
import static java.util.concurrent.TimeUnit.*;
|
||||
|
||||
import config.YamlConfig;
|
||||
import tools.DatabaseConnection;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
public class ExpLogger {
|
||||
private static final LinkedBlockingQueue<ExpLogRecord> expLoggerQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
@@ -22,9 +22,16 @@
|
||||
package server;
|
||||
|
||||
import client.Character;
|
||||
import client.*;
|
||||
import client.Client;
|
||||
import client.Job;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.autoban.AutobanFactory;
|
||||
import client.inventory.*;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Inventory;
|
||||
import client.inventory.InventoryType;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.WeaponType;
|
||||
import config.YamlConfig;
|
||||
import constants.id.ItemId;
|
||||
import constants.inventory.EquipSlot;
|
||||
@@ -35,7 +42,12 @@ import constants.skills.NightWalker;
|
||||
import net.server.Server;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import provider.*;
|
||||
import provider.Data;
|
||||
import provider.DataDirectoryEntry;
|
||||
import provider.DataFileEntry;
|
||||
import provider.DataProvider;
|
||||
import provider.DataProviderFactory;
|
||||
import provider.DataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import server.life.LifeFactory;
|
||||
import server.life.MonsterInformationProvider;
|
||||
@@ -43,13 +55,22 @@ import tools.DatabaseConnection;
|
||||
import tools.PacketCreator;
|
||||
import tools.Pair;
|
||||
import tools.Randomizer;
|
||||
import tools.StringUtil;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Matze
|
||||
@@ -1024,9 +1045,16 @@ public class ItemInformationProvider {
|
||||
Issue with clean slate found thanks to Masterrulax
|
||||
Vicious added in the clean slate check thanks to Crypter (CrypterDEV)
|
||||
*/
|
||||
public boolean canUseCleanSlate(Equip nEquip) {
|
||||
Map<String, Integer> eqstats = this.getEquipStats(nEquip.getItemId());
|
||||
return YamlConfig.config.server.USE_ENHANCED_CLNSLATE || nEquip.getUpgradeSlots() < (byte) (eqstats.get("tuc") + nEquip.getVicious());
|
||||
public boolean canUseCleanSlate(Equip equip) {
|
||||
Map<String, Integer> eqStats = getEquipStats(equip.getItemId());
|
||||
if (eqStats == null || eqStats.get("tuc") == 0 ) {
|
||||
return false;
|
||||
}
|
||||
int totalUpgradeCount = eqStats.get("tuc");
|
||||
int freeUpgradeCount = equip.getUpgradeSlots();
|
||||
int viciousCount = equip.getVicious();
|
||||
int appliedScrollCount = equip.getLevel();
|
||||
return freeUpgradeCount + appliedScrollCount < totalUpgradeCount + viciousCount;
|
||||
}
|
||||
|
||||
public Item scrollEquipWithId(Item equip, int scrollId, boolean usingWhiteScroll, int vegaItemId, boolean isGM) {
|
||||
|
||||
@@ -21,8 +21,13 @@
|
||||
*/
|
||||
package server;
|
||||
|
||||
import client.BuffStat;
|
||||
import client.Character;
|
||||
import client.*;
|
||||
import client.Disease;
|
||||
import client.Job;
|
||||
import client.Mount;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.inventory.Inventory;
|
||||
import client.inventory.InventoryType;
|
||||
import client.inventory.Item;
|
||||
@@ -33,7 +38,57 @@ import config.YamlConfig;
|
||||
import constants.id.ItemId;
|
||||
import constants.id.MapId;
|
||||
import constants.inventory.ItemConstants;
|
||||
import constants.skills.*;
|
||||
import constants.skills.Aran;
|
||||
import constants.skills.Assassin;
|
||||
import constants.skills.Bandit;
|
||||
import constants.skills.Beginner;
|
||||
import constants.skills.Bishop;
|
||||
import constants.skills.BlazeWizard;
|
||||
import constants.skills.Bowmaster;
|
||||
import constants.skills.Brawler;
|
||||
import constants.skills.Buccaneer;
|
||||
import constants.skills.ChiefBandit;
|
||||
import constants.skills.Cleric;
|
||||
import constants.skills.Corsair;
|
||||
import constants.skills.Crossbowman;
|
||||
import constants.skills.Crusader;
|
||||
import constants.skills.DarkKnight;
|
||||
import constants.skills.DawnWarrior;
|
||||
import constants.skills.DragonKnight;
|
||||
import constants.skills.Evan;
|
||||
import constants.skills.FPArchMage;
|
||||
import constants.skills.FPMage;
|
||||
import constants.skills.FPWizard;
|
||||
import constants.skills.Fighter;
|
||||
import constants.skills.GM;
|
||||
import constants.skills.Gunslinger;
|
||||
import constants.skills.Hermit;
|
||||
import constants.skills.Hero;
|
||||
import constants.skills.Hunter;
|
||||
import constants.skills.ILArchMage;
|
||||
import constants.skills.ILMage;
|
||||
import constants.skills.ILWizard;
|
||||
import constants.skills.Legend;
|
||||
import constants.skills.Magician;
|
||||
import constants.skills.Marauder;
|
||||
import constants.skills.Marksman;
|
||||
import constants.skills.NightLord;
|
||||
import constants.skills.NightWalker;
|
||||
import constants.skills.Noblesse;
|
||||
import constants.skills.Outlaw;
|
||||
import constants.skills.Page;
|
||||
import constants.skills.Paladin;
|
||||
import constants.skills.Pirate;
|
||||
import constants.skills.Priest;
|
||||
import constants.skills.Ranger;
|
||||
import constants.skills.Rogue;
|
||||
import constants.skills.Shadower;
|
||||
import constants.skills.Sniper;
|
||||
import constants.skills.Spearman;
|
||||
import constants.skills.SuperGM;
|
||||
import constants.skills.ThunderBreaker;
|
||||
import constants.skills.WhiteKnight;
|
||||
import constants.skills.WindArcher;
|
||||
import net.packet.Packet;
|
||||
import net.server.Server;
|
||||
import net.server.world.Party;
|
||||
@@ -44,15 +99,27 @@ import server.life.MobSkill;
|
||||
import server.life.MobSkillFactory;
|
||||
import server.life.MobSkillType;
|
||||
import server.life.Monster;
|
||||
import server.maps.*;
|
||||
import server.maps.Door;
|
||||
import server.maps.FieldLimit;
|
||||
import server.maps.MapObject;
|
||||
import server.maps.MapObjectType;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.Mist;
|
||||
import server.maps.Portal;
|
||||
import server.maps.Summon;
|
||||
import server.maps.SummonMovementType;
|
||||
import server.partyquest.CarnivalFactory;
|
||||
import server.partyquest.CarnivalFactory.MCSkill;
|
||||
import tools.PacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Matze
|
||||
@@ -910,18 +977,8 @@ public class StatEffect {
|
||||
Portal pt;
|
||||
|
||||
if (moveTo == MapId.NONE) {
|
||||
if (sourceid != ItemId.ANTI_BANISH_SCROLL) {
|
||||
target = applyto.getMap().getReturnMap();
|
||||
pt = target.getRandomPlayerSpawnpoint();
|
||||
} else {
|
||||
if (!applyto.canRecoverLastBanish()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Pair<Integer, Integer> lastBanishInfo = applyto.getLastBanishData();
|
||||
target = applyto.getWarpMap(lastBanishInfo.getLeft());
|
||||
pt = target.getPortal(lastBanishInfo.getRight());
|
||||
}
|
||||
target = applyto.getMap().getReturnMap();
|
||||
pt = target.getRandomPlayerSpawnpoint();
|
||||
} else {
|
||||
target = applyto.getClient().getWorldServer().getChannel(applyto.getClient().getChannel()).getMapFactory().getMap(moveTo);
|
||||
int targetid = target.getId() / 10000000;
|
||||
|
||||
@@ -38,7 +38,12 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
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 java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@@ -358,4 +363,4 @@ public class Storage {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,11 @@
|
||||
*/
|
||||
package server;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
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 static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
@@ -37,8 +37,14 @@ import server.maps.MapleMap;
|
||||
import tools.PacketCreator;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
@@ -23,7 +23,11 @@ import config.YamlConfig;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.Pair;
|
||||
|
||||
import java.sql.*;
|
||||
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;
|
||||
|
||||
9
src/main/java/server/life/BanishInfo.java
Normal file
9
src/main/java/server/life/BanishInfo.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package server.life;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record BanishInfo(int map, String portal, String msg) {
|
||||
public BanishInfo {
|
||||
Objects.requireNonNull(portal, "BanishInfo portal");
|
||||
}
|
||||
}
|
||||
@@ -33,8 +33,12 @@ import tools.Pair;
|
||||
import tools.StringUtil;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class LifeFactory {
|
||||
private static final Logger log = LoggerFactory.getLogger(LifeFactory.class);
|
||||
@@ -227,7 +231,10 @@ public class LifeFactory {
|
||||
|
||||
Data banishData = monsterInfoData.getChildByPath("ban");
|
||||
if (banishData != null) {
|
||||
stats.setBanishInfo(new BanishInfo(DataTool.getString("banMsg", banishData), DataTool.getInt("banMap/0/field", banishData, -1), DataTool.getString("banMap/0/portal", banishData, "sp")));
|
||||
int map = DataTool.getInt("banMap/0/field", banishData, -1);
|
||||
String portal = DataTool.getString("banMap/0/portal", banishData, "sp");
|
||||
String msg = DataTool.getString("banMsg", banishData);
|
||||
stats.setBanishInfo(new BanishInfo(map, portal, msg));
|
||||
}
|
||||
|
||||
int noFlip = DataTool.getInt("noFlip", monsterInfoData, 0);
|
||||
@@ -292,31 +299,6 @@ public class LifeFactory {
|
||||
return DataTool.getString(nid + "/d0", npcStringData, "(...)");
|
||||
}
|
||||
|
||||
public static class BanishInfo {
|
||||
|
||||
private final int map;
|
||||
private final String portal;
|
||||
private final String 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 final int id;
|
||||
|
||||
@@ -38,8 +38,11 @@ import server.maps.Mist;
|
||||
import tools.Randomizer;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Danny (Leifde)
|
||||
@@ -190,6 +193,11 @@ public class MobSkill {
|
||||
|
||||
// TODO: avoid output argument banishPlayersOutput
|
||||
public void applyEffect(Character player, Monster monster, boolean skill, List<Character> banishPlayersOutput) {
|
||||
// See if the MobSkill is successful before doing anything
|
||||
if (!makeChanceResult()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Disease disease = null;
|
||||
Map<MonsterStatus, Integer> stats = new EnumMap<>(MonsterStatus.class);
|
||||
List<Integer> reflection = new ArrayList<>();
|
||||
@@ -213,12 +221,12 @@ public class MobSkill {
|
||||
case REVERSE_INPUT -> disease = Disease.CONFUSE;
|
||||
case UNDEAD -> disease = Disease.ZOMBIFY;
|
||||
case PHYSICAL_IMMUNE -> {
|
||||
if (makeChanceResult() && !monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) {
|
||||
if (!monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) {
|
||||
stats.put(MonsterStatus.WEAPON_IMMUNITY, x);
|
||||
}
|
||||
}
|
||||
case MAGIC_IMMUNE -> {
|
||||
if (makeChanceResult() && !monster.isBuffed(MonsterStatus.WEAPON_IMMUNITY)) {
|
||||
if (!monster.isBuffed(MonsterStatus.WEAPON_IMMUNITY)) {
|
||||
stats.put(MonsterStatus.MAGIC_IMMUNITY, x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,11 @@ import provider.DataTool;
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
@@ -21,13 +21,26 @@
|
||||
*/
|
||||
package server.life;
|
||||
|
||||
import client.BuffStat;
|
||||
import client.Character;
|
||||
import client.*;
|
||||
import client.Client;
|
||||
import client.FamilyEntry;
|
||||
import client.Job;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.status.MonsterStatus;
|
||||
import client.status.MonsterStatusEffect;
|
||||
import config.YamlConfig;
|
||||
import constants.id.MobId;
|
||||
import constants.skills.*;
|
||||
import constants.skills.Crusader;
|
||||
import constants.skills.FPMage;
|
||||
import constants.skills.Hermit;
|
||||
import constants.skills.ILMage;
|
||||
import constants.skills.NightLord;
|
||||
import constants.skills.NightWalker;
|
||||
import constants.skills.Priest;
|
||||
import constants.skills.Shadower;
|
||||
import constants.skills.WhiteKnight;
|
||||
import database.drop.DropProvider;
|
||||
import net.packet.Packet;
|
||||
import net.server.channel.Channel;
|
||||
@@ -44,7 +57,6 @@ import org.slf4j.LoggerFactory;
|
||||
import scripting.event.EventInstanceManager;
|
||||
import server.StatEffect;
|
||||
import server.TimerManager;
|
||||
import server.life.LifeFactory.BanishInfo;
|
||||
import server.loot.LootManager;
|
||||
import server.maps.AbstractAnimatedMapObject;
|
||||
import server.maps.MapObjectType;
|
||||
@@ -57,9 +69,17 @@ import tools.Randomizer;
|
||||
|
||||
import java.awt.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -807,12 +827,12 @@ public class Monster extends AbstractLoadedLife {
|
||||
}
|
||||
|
||||
if (htKilled) {
|
||||
reviveMap.killMonster(ht, killer, true);
|
||||
reviveMap.killMonster(ht, killer, true, (short) 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = MobId.DEAD_HORNTAIL_MAX; i >= MobId.DEAD_HORNTAIL_MIN; i--) {
|
||||
reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true);
|
||||
reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true, (short) 0);
|
||||
}
|
||||
} else if (controller != null) {
|
||||
mob.aggroSwitchController(controller, aggro);
|
||||
|
||||
@@ -29,7 +29,17 @@ import provider.DataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import tools.Pair;
|
||||
|
||||
import java.util.*;
|
||||
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;
|
||||
|
||||
public class MonsterInformationProvider {
|
||||
private static final Logger log = LoggerFactory.getLogger(MonsterInformationProvider.class);
|
||||
|
||||
@@ -21,13 +21,18 @@
|
||||
*/
|
||||
package server.life;
|
||||
|
||||
import server.life.LifeFactory.BanishInfo;
|
||||
import server.life.LifeFactory.loseItem;
|
||||
import server.life.LifeFactory.selfDestruction;
|
||||
import tools.Pair;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Frz
|
||||
|
||||
@@ -44,9 +44,19 @@ import tools.PacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
import java.awt.*;
|
||||
import java.sql.*;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
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.*;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,11 @@
|
||||
package server.maps;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.packet.*;
|
||||
import net.packet.ByteBufInPacket;
|
||||
import net.packet.ByteBufOutPacket;
|
||||
import net.packet.InPacket;
|
||||
import net.packet.OutPacket;
|
||||
import net.packet.Packet;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
@@ -46,7 +46,12 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@@ -208,7 +208,8 @@ public class MapItem extends AbstractMapObject {
|
||||
if (chr.needQuestItem(questid, getItemId())) {
|
||||
this.lockItem();
|
||||
try {
|
||||
client.sendPacket(PacketCreator.dropItemFromMapObject(chr, this, null, getPosition(), (byte) 2));
|
||||
client.sendPacket(PacketCreator.dropItemFromMapObject(chr, this, null, getPosition(),
|
||||
(byte) 2, (short) 0));
|
||||
} finally {
|
||||
this.unlockItem();
|
||||
}
|
||||
@@ -219,4 +220,4 @@ public class MapItem extends AbstractMapObject {
|
||||
public void sendDestroyData(final Client client) {
|
||||
client.sendPacket(PacketCreator.removeItemFromMap(getObjectId(), 1, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +53,21 @@ import scripting.map.MapScriptManager;
|
||||
import server.ItemInformationProvider;
|
||||
import server.StatEffect;
|
||||
import server.TimerManager;
|
||||
import server.events.gm.*;
|
||||
import server.life.*;
|
||||
import server.events.gm.Coconut;
|
||||
import server.events.gm.Fitness;
|
||||
import server.events.gm.Ola;
|
||||
import server.events.gm.OxQuiz;
|
||||
import server.events.gm.Snowball;
|
||||
import server.life.LifeFactory;
|
||||
import server.life.LifeFactory.selfDestruction;
|
||||
import server.life.Monster;
|
||||
import server.life.MonsterDropEntry;
|
||||
import server.life.MonsterGlobalDropEntry;
|
||||
import server.life.MonsterInformationProvider;
|
||||
import server.life.MonsterListener;
|
||||
import server.life.NPC;
|
||||
import server.life.PlayerNPC;
|
||||
import server.life.SpawnPoint;
|
||||
import server.partyquest.CarnivalFactory;
|
||||
import server.partyquest.CarnivalFactory.MCSkill;
|
||||
import server.partyquest.GuardianSpawnPoint;
|
||||
@@ -65,9 +77,21 @@ import tools.Randomizer;
|
||||
|
||||
import java.awt.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
@@ -97,7 +121,6 @@ public class MapleMap {
|
||||
private final Map<String, Integer> environment = new LinkedHashMap<>();
|
||||
private final Map<MapItem, Long> droppedItems = new LinkedHashMap<>();
|
||||
private final LinkedList<WeakReference<MapObject>> registeredDrops = new LinkedList<>();
|
||||
private final Map<MobLootEntry, Long> mobLootEntries = new HashMap(20);
|
||||
private final List<Runnable> statUpdateRunnables = new ArrayList(50);
|
||||
private final List<Rectangle> areas = new ArrayList<>();
|
||||
private FootholdTree footholds = null;
|
||||
@@ -137,7 +160,6 @@ public class MapleMap {
|
||||
private MonsterAggroCoordinator aggroMonitor = null; // aggroMonitor activity in sync with itemMonitor
|
||||
private ScheduledFuture<?> itemMonitor = null;
|
||||
private ScheduledFuture<?> expireItemsTask = null;
|
||||
private ScheduledFuture<?> mobSpawnLootTask = null;
|
||||
private ScheduledFuture<?> characterStatUpdateTask = null;
|
||||
private short itemMonitorTimeout;
|
||||
private Pair<Integer, String> timeMob = null;
|
||||
@@ -632,27 +654,26 @@ public class MapleMap {
|
||||
}
|
||||
}
|
||||
|
||||
private byte dropItemsFromMonsterOnMap(List<MonsterDropEntry> dropEntries, Point pos, byte d, int chRate,
|
||||
byte droptype, int mobpos, Character chr, Monster mob) {
|
||||
if (dropEntries.isEmpty()) {
|
||||
return d;
|
||||
private byte dropItemsFromMonsterOnMap(List<MonsterDropEntry> dropEntry, Point pos, byte index, int chRate,
|
||||
byte droptype, int mobpos, Character chr, Monster mob, short delay) {
|
||||
if (dropEntry.isEmpty()) {
|
||||
return index;
|
||||
}
|
||||
|
||||
List<MonsterDropEntry> shuffledDropEntries = new ArrayList<>(dropEntries);
|
||||
Collections.shuffle(shuffledDropEntries);
|
||||
Collections.shuffle(dropEntry);
|
||||
|
||||
Item idrop;
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
|
||||
for (final MonsterDropEntry de : shuffledDropEntries) {
|
||||
for (final MonsterDropEntry de : dropEntry) {
|
||||
float cardRate = chr.getCardRate(de.itemId);
|
||||
int dropChance = (int) Math.min((float) de.chance * chRate * cardRate, Integer.MAX_VALUE);
|
||||
|
||||
if (Randomizer.nextInt(999999) < dropChance) {
|
||||
if (droptype == 3) {
|
||||
pos.x = mobpos + ((d % 2 == 0) ? (40 * ((d + 1) / 2)) : -(40 * (d / 2)));
|
||||
pos.x = mobpos + ((index % 2 == 0) ? (40 * ((index + 1) / 2)) : -(40 * (index / 2)));
|
||||
} else {
|
||||
pos.x = mobpos + ((d % 2 == 0) ? (25 * ((d + 1) / 2)) : -(25 * (d / 2)));
|
||||
pos.x = mobpos + ((index % 2 == 0) ? (25 * ((index + 1) / 2)) : -(25 * (index / 2)));
|
||||
}
|
||||
if (de.itemId == 0) { // meso
|
||||
int mesos = Randomizer.nextInt(de.Maximum - de.Minimum) + de.Minimum;
|
||||
@@ -666,7 +687,8 @@ public class MapleMap {
|
||||
mesos = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
spawnMesoDrop(mesos, calcDropPos(pos, mob.getPosition()), mob, chr, false, droptype);
|
||||
spawnMesoDrop(mesos, calcDropPos(pos, mob.getPosition()), mob, chr, false, droptype,
|
||||
delay);
|
||||
}
|
||||
} else {
|
||||
if (ItemConstants.getInventoryType(de.itemId) == InventoryType.EQUIP) {
|
||||
@@ -674,28 +696,23 @@ public class MapleMap {
|
||||
} else {
|
||||
idrop = new Item(de.itemId, (short) 0, (short) (de.Maximum != 1 ? Randomizer.nextInt(de.Maximum - de.Minimum) + de.Minimum : 1));
|
||||
}
|
||||
spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid);
|
||||
spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid, delay);
|
||||
}
|
||||
d++;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
return d;
|
||||
return index;
|
||||
}
|
||||
|
||||
private byte dropGlobalItemsFromMonsterOnMap(List<MonsterGlobalDropEntry> globalDropEntries, Point pos, byte d,
|
||||
byte droptype, int mobpos, Character chr, Monster mob) {
|
||||
if (globalDropEntries.isEmpty()) {
|
||||
return d;
|
||||
}
|
||||
|
||||
List<MonsterGlobalDropEntry> shuffledGlobalDropEntries = new ArrayList<>(globalDropEntries);
|
||||
Collections.shuffle(shuffledGlobalDropEntries);
|
||||
private byte dropGlobalItemsFromMonsterOnMap(List<MonsterGlobalDropEntry> globalEntry, Point pos, byte d,
|
||||
byte droptype, int mobpos, Character chr, Monster mob, short delay) {
|
||||
Collections.shuffle(globalEntry);
|
||||
|
||||
Item idrop;
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
|
||||
for (final MonsterGlobalDropEntry de : shuffledGlobalDropEntries) {
|
||||
for (final MonsterGlobalDropEntry de : globalEntry) {
|
||||
if (Randomizer.nextInt(999999) < de.chance) {
|
||||
if (droptype == 3) {
|
||||
pos.x = mobpos + (d % 2 == 0 ? (40 * (d + 1) / 2) : -(40 * (d / 2)));
|
||||
@@ -708,7 +725,7 @@ public class MapleMap {
|
||||
} else {
|
||||
idrop = new Item(de.itemId, (short) 0, (short) (de.Maximum != 1 ? Randomizer.nextInt(de.Maximum - de.Minimum) + de.Minimum : 1));
|
||||
}
|
||||
spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid);
|
||||
spawnDrop(idrop, calcDropPos(pos, mob.getPosition()), mob, chr, droptype, de.questid, delay);
|
||||
d++;
|
||||
}
|
||||
}
|
||||
@@ -717,7 +734,7 @@ public class MapleMap {
|
||||
return d;
|
||||
}
|
||||
|
||||
private void dropFromMonster(final Character chr, final Monster mob, final boolean useBaseRate) {
|
||||
private void dropFromMonster(final Character chr, final Monster mob, final boolean useBaseRate, short delay) {
|
||||
if (mob.dropsDisabled() || !dropsOn) {
|
||||
return;
|
||||
}
|
||||
@@ -725,7 +742,6 @@ public class MapleMap {
|
||||
final byte droptype = (byte) (mob.getStats().isExplosiveReward() ? 3 : mob.getStats().isFfaLoot() ? 2 : chr.getParty() != null ? 1 : 0);
|
||||
final int mobpos = mob.getPosition().x;
|
||||
int chRate = !mob.isBoss() ? chr.getDropRate() : chr.getBossDropRate();
|
||||
byte d = 1;
|
||||
Point pos = new Point(0, mob.getPosition().y);
|
||||
|
||||
MonsterStatusEffect stati = mob.getStati(MonsterStatus.SHOWDOWN);
|
||||
@@ -751,10 +767,20 @@ public class MapleMap {
|
||||
return;
|
||||
}
|
||||
|
||||
registerMobItemDrops(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, chr, mob);
|
||||
|
||||
byte index = 1;
|
||||
// Normal Drops
|
||||
index = dropItemsFromMonsterOnMap(dropEntry, pos, index, chRate, droptype, mobpos, chr, mob, delay);
|
||||
|
||||
// Global Drops
|
||||
index = dropGlobalItemsFromMonsterOnMap(globalEntry, pos, index, droptype, mobpos, chr, mob, delay);
|
||||
|
||||
// Quest Drops
|
||||
index = dropItemsFromMonsterOnMap(visibleQuestEntry, pos, index, chRate, droptype, mobpos, chr, mob, delay);
|
||||
dropItemsFromMonsterOnMap(otherQuestEntry, pos, index, chRate, droptype, mobpos, chr, mob, delay);
|
||||
}
|
||||
|
||||
public void dropItemsFromMonster(List<MonsterDropEntry> list, final Character chr, final Monster mob) {
|
||||
public void dropItemsFromMonster(List<MonsterDropEntry> list, final Character chr, final Monster mob, short delay) {
|
||||
if (mob.dropsDisabled() || !dropsOn) {
|
||||
return;
|
||||
}
|
||||
@@ -765,15 +791,17 @@ public class MapleMap {
|
||||
byte d = 1;
|
||||
Point pos = new Point(0, mob.getPosition().y);
|
||||
|
||||
dropItemsFromMonsterOnMap(list, pos, d, chRate, droptype, mobpos, chr, mob);
|
||||
dropItemsFromMonsterOnMap(list, pos, d, chRate, droptype, mobpos, chr, mob, delay);
|
||||
}
|
||||
|
||||
public void dropFromFriendlyMonster(final Character chr, final Monster mob) {
|
||||
dropFromMonster(chr, mob, true);
|
||||
dropFromMonster(chr, mob, true, (short) 0);
|
||||
}
|
||||
|
||||
public void dropFromReactor(final Character chr, final Reactor reactor, Item drop, Point dropPos, short questid) {
|
||||
spawnDrop(drop, this.calcDropPos(dropPos, reactor.getPosition()), reactor, chr, (byte) (chr.getParty() != null ? 1 : 0), questid);
|
||||
public void dropFromReactor(final Character chr, final Reactor reactor, Item drop, Point dropPos, short questid,
|
||||
short delay) {
|
||||
spawnDrop(drop, this.calcDropPos(dropPos, reactor.getPosition()), reactor, chr,
|
||||
(byte) (chr.getParty() != null ? 1 : 0), questid, delay);
|
||||
}
|
||||
|
||||
private void stopItemMonitor() {
|
||||
@@ -783,11 +811,6 @@ public class MapleMap {
|
||||
expireItemsTask.cancel(false);
|
||||
expireItemsTask = null;
|
||||
|
||||
if (YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) {
|
||||
mobSpawnLootTask.cancel(false);
|
||||
mobSpawnLootTask = null;
|
||||
}
|
||||
|
||||
characterStatUpdateTask.cancel(false);
|
||||
characterStatUpdateTask = null;
|
||||
}
|
||||
@@ -844,17 +867,6 @@ public class MapleMap {
|
||||
|
||||
expireItemsTask = TimerManager.getInstance().register(() -> makeDisappearExpiredItemDrops(), YamlConfig.config.server.ITEM_EXPIRE_CHECK, YamlConfig.config.server.ITEM_EXPIRE_CHECK);
|
||||
|
||||
if (YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) {
|
||||
lootLock.lock();
|
||||
try {
|
||||
mobLootEntries.clear();
|
||||
} finally {
|
||||
lootLock.unlock();
|
||||
}
|
||||
|
||||
mobSpawnLootTask = TimerManager.getInstance().register(() -> spawnMobItemDrops(), 200, 200);
|
||||
}
|
||||
|
||||
characterStatUpdateTask = TimerManager.getInstance().register(() -> runCharacterStatUpdate(), 200, 200);
|
||||
|
||||
itemMonitorTimeout = 1;
|
||||
@@ -951,63 +963,6 @@ public class MapleMap {
|
||||
}
|
||||
}
|
||||
|
||||
private void registerMobItemDrops(byte droptype, int mobpos, int chRate, Point pos, List<MonsterDropEntry> dropEntry, List<MonsterDropEntry> visibleQuestEntry, List<MonsterDropEntry> otherQuestEntry, List<MonsterGlobalDropEntry> globalEntry, Character chr, Monster mob) {
|
||||
MobLootEntry mle = new MobLootEntry(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, chr, mob);
|
||||
|
||||
if (YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) {
|
||||
int animationTime = mob.getAnimationTime("die1");
|
||||
|
||||
lootLock.lock();
|
||||
try {
|
||||
long timeNow = Server.getInstance().getCurrentTime();
|
||||
mobLootEntries.put(mle, timeNow + ((long) (0.42 * animationTime)));
|
||||
} finally {
|
||||
lootLock.unlock();
|
||||
}
|
||||
} else {
|
||||
mle.run();
|
||||
}
|
||||
}
|
||||
|
||||
private void spawnMobItemDrops() {
|
||||
Set<Entry<MobLootEntry, Long>> mleList;
|
||||
|
||||
lootLock.lock();
|
||||
try {
|
||||
mleList = new HashSet<>(mobLootEntries.entrySet());
|
||||
} finally {
|
||||
lootLock.unlock();
|
||||
}
|
||||
|
||||
long timeNow = Server.getInstance().getCurrentTime();
|
||||
List<MobLootEntry> toRemove = new LinkedList<>();
|
||||
for (Entry<MobLootEntry, Long> mlee : mleList) {
|
||||
if (mlee.getValue() < timeNow) {
|
||||
toRemove.add(mlee.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
if (!toRemove.isEmpty()) {
|
||||
List<MobLootEntry> toSpawnLoot = new LinkedList<>();
|
||||
|
||||
lootLock.lock();
|
||||
try {
|
||||
for (MobLootEntry mle : toRemove) {
|
||||
Long mler = mobLootEntries.remove(mle);
|
||||
if (mler != null) {
|
||||
toSpawnLoot.add(mle);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lootLock.unlock();
|
||||
}
|
||||
|
||||
for (MobLootEntry mle : toSpawnLoot) {
|
||||
mle.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<MapItem> getDroppedItems() {
|
||||
objectRLock.lock();
|
||||
try {
|
||||
@@ -1109,7 +1064,8 @@ public class MapleMap {
|
||||
}
|
||||
}
|
||||
|
||||
private void spawnDrop(final Item idrop, final Point dropPos, final MapObject dropper, final Character chr, final byte droptype, final short questid) {
|
||||
private void spawnDrop(final Item idrop, final Point dropPos, final MapObject dropper, final Character chr,
|
||||
final byte droptype, final short questid, short delay) {
|
||||
final MapItem mdrop = new MapItem(idrop, dropPos, dropper, chr, chr.getClient(), droptype, false, questid);
|
||||
mdrop.setDropTime(Server.getInstance().getCurrentTime());
|
||||
spawnAndAddRangedMapObject(mdrop, c -> {
|
||||
@@ -1118,7 +1074,8 @@ public class MapleMap {
|
||||
if (chr1.needQuestItem(questid, idrop.getItemId())) {
|
||||
mdrop.lockItem();
|
||||
try {
|
||||
c.sendPacket(PacketCreator.dropItemFromMapObject(chr1, mdrop, dropper.getPosition(), dropPos, (byte) 1));
|
||||
c.sendPacket(PacketCreator.dropItemFromMapObject(chr1, mdrop, dropper.getPosition(), dropPos,
|
||||
(byte) 1, delay));
|
||||
} finally {
|
||||
mdrop.unlockItem();
|
||||
}
|
||||
@@ -1129,7 +1086,8 @@ public class MapleMap {
|
||||
activateItemReactors(mdrop, chr.getClient());
|
||||
}
|
||||
|
||||
public final void spawnMesoDrop(final int meso, final Point position, final MapObject dropper, final Character owner, final boolean playerDrop, final byte droptype) {
|
||||
public final void spawnMesoDrop(final int meso, final Point position, final MapObject dropper,
|
||||
final Character owner, final boolean playerDrop, final byte droptype, short delay) {
|
||||
final Point droppos = calcDropPos(position, position);
|
||||
final MapItem mdrop = new MapItem(meso, droppos, dropper, owner, owner.getClient(), droptype, playerDrop);
|
||||
mdrop.setDropTime(Server.getInstance().getCurrentTime());
|
||||
@@ -1137,7 +1095,8 @@ public class MapleMap {
|
||||
spawnAndAddRangedMapObject(mdrop, c -> {
|
||||
mdrop.lockItem();
|
||||
try {
|
||||
c.sendPacket(PacketCreator.dropItemFromMapObject(c.getPlayer(), mdrop, dropper.getPosition(), droppos, (byte) 1));
|
||||
c.sendPacket(PacketCreator.dropItemFromMapObject(c.getPlayer(), mdrop, dropper.getPosition(), droppos,
|
||||
(byte) 1, delay));
|
||||
} finally {
|
||||
mdrop.unlockItem();
|
||||
}
|
||||
@@ -1152,7 +1111,7 @@ public class MapleMap {
|
||||
|
||||
mdrop.lockItem();
|
||||
try {
|
||||
broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 3, mdrop.getPosition());
|
||||
broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 3, (short) 0, mdrop.getPosition());
|
||||
} finally {
|
||||
mdrop.unlockItem();
|
||||
}
|
||||
@@ -1164,7 +1123,7 @@ public class MapleMap {
|
||||
|
||||
mdrop.lockItem();
|
||||
try {
|
||||
broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 3, mdrop.getPosition());
|
||||
broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 3, (short) 0, mdrop.getPosition());
|
||||
} finally {
|
||||
mdrop.unlockItem();
|
||||
}
|
||||
@@ -1312,7 +1271,11 @@ public class MapleMap {
|
||||
return count;
|
||||
}
|
||||
|
||||
public boolean damageMonster(final Character chr, final Monster monster, final int damage) {
|
||||
public boolean damageMonster(Character chr, Monster monster, int damage) {
|
||||
return damageMonster(chr, monster, damage, (short) 0);
|
||||
}
|
||||
|
||||
public boolean damageMonster(final Character chr, final Monster monster, final int damage, short delay) {
|
||||
if (monster.getId() == MobId.ZAKUM_1) {
|
||||
for (MapObject object : chr.getMap().getMapObjects()) {
|
||||
Monster mons = chr.getMap().getMonsterByOid(object.getObjectId());
|
||||
@@ -1323,22 +1286,23 @@ public class MapleMap {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (monster.isAlive()) {
|
||||
boolean killed = monster.damage(chr, damage, false);
|
||||
|
||||
selfDestruction selfDestr = monster.getStats().selfDestruction();
|
||||
if (selfDestr != null && selfDestr.getHp() > -1) {// should work ;p
|
||||
if (monster.getHp() <= selfDestr.getHp()) {
|
||||
killMonster(monster, chr, true, selfDestr.getAction());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (killed) {
|
||||
killMonster(monster, chr, true);
|
||||
}
|
||||
return true;
|
||||
if (!monster.isAlive()) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
boolean killed = monster.damage(chr, damage, false);
|
||||
|
||||
selfDestruction selfDestr = monster.getStats().selfDestruction();
|
||||
if (selfDestr != null && selfDestr.getHp() > -1) {// should work ;p
|
||||
if (monster.getHp() <= selfDestr.getHp()) {
|
||||
killMonster(monster, chr, true, selfDestr.getAction());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (killed) {
|
||||
killMonster(monster, chr, true, delay);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void broadcastBalrogVictory(String leaderName) {
|
||||
@@ -1377,11 +1341,12 @@ public class MapleMap {
|
||||
}
|
||||
}
|
||||
|
||||
public void killMonster(final Monster monster, final Character chr, final boolean withDrops) {
|
||||
killMonster(monster, chr, withDrops, 1);
|
||||
public void killMonster(final Monster monster, final Character chr, final boolean withDrops, short dropDelay) {
|
||||
killMonster(monster, chr, withDrops, 1, dropDelay);
|
||||
}
|
||||
|
||||
public void killMonster(final Monster monster, final Character chr, final boolean withDrops, int animation) {
|
||||
public void killMonster(final Monster monster, final Character chr, final boolean withDrops, int animation,
|
||||
short dropDelay) {
|
||||
if (monster == null) {
|
||||
return;
|
||||
}
|
||||
@@ -1392,12 +1357,17 @@ public class MapleMap {
|
||||
broadcastMessage(PacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition());
|
||||
monster.aggroSwitchController(null, false);
|
||||
}
|
||||
} else {
|
||||
if (removeKilledMonsterObject(monster)) {
|
||||
try {
|
||||
if (monster.getStats().getLevel() >= chr.getLevel() + 30 && !chr.isGM()) {
|
||||
AutobanFactory.GENERAL.alert(chr, " for killing a " + monster.getName() + " which is over 30 levels higher.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!removeKilledMonsterObject(monster)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (monster.getStats().getLevel() >= chr.getLevel() + 30 && !chr.isGM()) {
|
||||
AutobanFactory.GENERAL.alert(chr, " for killing a " + monster.getName() + " which is over 30 levels higher.");
|
||||
}
|
||||
|
||||
/*if (chr.getQuest(Quest.getInstance(29400)).getStatus().equals(QuestStatus.Status.STARTED)) {
|
||||
if (chr.getLevel() >= 120 && monster.getStats().getLevel() >= 120) {
|
||||
@@ -1406,78 +1376,76 @@ public class MapleMap {
|
||||
}
|
||||
}*/
|
||||
|
||||
if (monster.getCP() > 0 && chr.getMap().isCPQMap()) {
|
||||
chr.gainCP(monster.getCP());
|
||||
}
|
||||
if (monster.getCP() > 0 && chr.getMap().isCPQMap()) {
|
||||
chr.gainCP(monster.getCP());
|
||||
}
|
||||
|
||||
int buff = monster.getBuffToGive();
|
||||
if (buff > -1) {
|
||||
ItemInformationProvider mii = ItemInformationProvider.getInstance();
|
||||
for (MapObject mmo : this.getPlayers()) {
|
||||
Character character = (Character) mmo;
|
||||
if (character.isAlive()) {
|
||||
StatEffect statEffect = mii.getItemEffect(buff);
|
||||
character.sendPacket(PacketCreator.showOwnBuffEffect(buff, 1));
|
||||
broadcastMessage(character, PacketCreator.showBuffEffect(character.getId(), buff, 1), false);
|
||||
statEffect.applyTo(character);
|
||||
}
|
||||
}
|
||||
int buff = monster.getBuffToGive();
|
||||
if (buff > -1) {
|
||||
ItemInformationProvider mii = ItemInformationProvider.getInstance();
|
||||
for (MapObject mmo : this.getPlayers()) {
|
||||
Character character = (Character) mmo;
|
||||
if (character.isAlive()) {
|
||||
StatEffect statEffect = mii.getItemEffect(buff);
|
||||
character.sendPacket(PacketCreator.showOwnBuffEffect(buff, 1));
|
||||
broadcastMessage(character, PacketCreator.showBuffEffect(character.getId(), buff, 1), false);
|
||||
statEffect.applyTo(character);
|
||||
}
|
||||
|
||||
if (MobId.isZakumArm(monster.getId())) {
|
||||
boolean makeZakReal = true;
|
||||
Collection<MapObject> objects = getMapObjects();
|
||||
for (MapObject object : objects) {
|
||||
Monster mons = getMonsterByOid(object.getObjectId());
|
||||
if (mons != null) {
|
||||
if (MobId.isZakumArm(mons.getId())) {
|
||||
makeZakReal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (makeZakReal) {
|
||||
MapleMap map = chr.getMap();
|
||||
|
||||
for (MapObject object : objects) {
|
||||
Monster mons = map.getMonsterByOid(object.getObjectId());
|
||||
if (mons != null) {
|
||||
if (mons.getId() == MobId.ZAKUM_1) {
|
||||
makeMonsterReal(mons);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Character dropOwner = monster.killBy(chr);
|
||||
if (withDrops && !monster.dropsDisabled()) {
|
||||
if (dropOwner == null) {
|
||||
dropOwner = chr;
|
||||
}
|
||||
dropFromMonster(dropOwner, monster, false);
|
||||
}
|
||||
|
||||
if (monster.hasBossHPBar()) {
|
||||
for (Character mc : this.getAllPlayers()) {
|
||||
if (mc.getTargetHpBarHash() == monster.hashCode()) {
|
||||
mc.resetPlayerAggro();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally { // thanks resinate for pointing out a memory leak possibly from an exception thrown
|
||||
monster.dispatchMonsterKilled(true);
|
||||
broadcastMessage(PacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
if (MobId.isZakumArm(monster.getId())) {
|
||||
boolean makeZakReal = true;
|
||||
Collection<MapObject> objects = getMapObjects();
|
||||
for (MapObject object : objects) {
|
||||
Monster mons = getMonsterByOid(object.getObjectId());
|
||||
if (mons != null) {
|
||||
if (MobId.isZakumArm(mons.getId())) {
|
||||
makeZakReal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (makeZakReal) {
|
||||
MapleMap map = chr.getMap();
|
||||
|
||||
for (MapObject object : objects) {
|
||||
Monster mons = map.getMonsterByOid(object.getObjectId());
|
||||
if (mons != null) {
|
||||
if (mons.getId() == MobId.ZAKUM_1) {
|
||||
makeMonsterReal(mons);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Character dropOwner = monster.killBy(chr);
|
||||
if (withDrops && !monster.dropsDisabled()) {
|
||||
if (dropOwner == null) {
|
||||
dropOwner = chr;
|
||||
}
|
||||
dropFromMonster(dropOwner, monster, false, dropDelay);
|
||||
}
|
||||
|
||||
if (monster.hasBossHPBar()) {
|
||||
for (Character mc : this.getAllPlayers()) {
|
||||
if (mc.getTargetHpBarHash() == monster.hashCode()) {
|
||||
mc.resetPlayerAggro();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally { // thanks resinate for pointing out a memory leak possibly from an exception thrown
|
||||
monster.dispatchMonsterKilled(true);
|
||||
broadcastMessage(PacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
public void killFriendlies(Monster mob) {
|
||||
this.killMonster(mob, (Character) getPlayers().get(0), false);
|
||||
this.killMonster(mob, (Character) getPlayers().get(0), false, (short) 0);
|
||||
}
|
||||
|
||||
public void killMonster(int mobId) {
|
||||
@@ -1486,7 +1454,7 @@ public class MapleMap {
|
||||
|
||||
for (Monster mob : mobList) {
|
||||
if (mob.getId() == mobId) {
|
||||
this.killMonster(mob, chr, false);
|
||||
this.killMonster(mob, chr, false, (short) 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1505,7 +1473,7 @@ public class MapleMap {
|
||||
chr = defaultChr;
|
||||
}
|
||||
|
||||
this.killMonster(mob, chr, true);
|
||||
this.killMonster(mob, chr, true, (short) 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1535,7 +1503,7 @@ public class MapleMap {
|
||||
continue;
|
||||
}
|
||||
|
||||
killMonster(monster, null, false, 1);
|
||||
killMonster(monster, null, false, 1, (short) 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1545,7 +1513,7 @@ public class MapleMap {
|
||||
for (MapObject monstermo : getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapObjectType.MONSTER))) {
|
||||
Monster monster = (Monster) monstermo;
|
||||
|
||||
killMonster(monster, null, false, 1);
|
||||
killMonster(monster, null, false, 1, (short) 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1892,7 +1860,7 @@ public class MapleMap {
|
||||
Runnable removeAfterAction;
|
||||
|
||||
if (selfDestruction == null) {
|
||||
removeAfterAction = () -> killMonster(monster, null, false);
|
||||
removeAfterAction = () -> killMonster(monster, null, false, (short) 0);
|
||||
|
||||
registerMapSchedule(removeAfterAction, SECONDS.toMillis(monster.getStats().removeAfter()));
|
||||
} else {
|
||||
@@ -2140,11 +2108,13 @@ public class MapleMap {
|
||||
getWorldServer().registerTimedMapObject(expireKite, YamlConfig.config.server.KITE_EXPIRE_TIME);
|
||||
}
|
||||
|
||||
public final void spawnItemDrop(final MapObject dropper, final Character owner, final Item item, Point pos, final boolean ffaDrop, final boolean playerDrop) {
|
||||
public final void spawnItemDrop(final MapObject dropper, final Character owner, final Item item, Point pos,
|
||||
final boolean ffaDrop, final boolean playerDrop) {
|
||||
spawnItemDrop(dropper, owner, item, pos, (byte) (ffaDrop ? 2 : 0), playerDrop);
|
||||
}
|
||||
|
||||
public final void spawnItemDrop(final MapObject dropper, final Character owner, final Item item, Point pos, final byte dropType, final boolean playerDrop) {
|
||||
public final void spawnItemDrop(final MapObject dropper, final Character owner, final Item item, Point pos,
|
||||
final byte dropType, final boolean playerDrop) {
|
||||
if (FieldLimit.DROP_LIMIT.check(this.getFieldLimit())) { // thanks Conrad for noticing some maps shouldn't have loots available
|
||||
this.disappearingItemDrop(dropper, owner, item, pos);
|
||||
return;
|
||||
@@ -2157,7 +2127,8 @@ public class MapleMap {
|
||||
spawnAndAddRangedMapObject(mdrop, c -> {
|
||||
mdrop.lockItem();
|
||||
try {
|
||||
c.sendPacket(PacketCreator.dropItemFromMapObject(c.getPlayer(), mdrop, dropper.getPosition(), droppos, (byte) 1));
|
||||
c.sendPacket(PacketCreator.dropItemFromMapObject(c.getPlayer(), mdrop, dropper.getPosition(), droppos,
|
||||
(byte) 1, (short) 0));
|
||||
} finally {
|
||||
mdrop.unlockItem();
|
||||
}
|
||||
@@ -2165,7 +2136,7 @@ public class MapleMap {
|
||||
|
||||
mdrop.lockItem();
|
||||
try {
|
||||
broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 0);
|
||||
broadcastItemDropMessage(mdrop, dropper.getPosition(), droppos, (byte) 0, (short) 0);
|
||||
} finally {
|
||||
mdrop.unlockItem();
|
||||
}
|
||||
@@ -2174,49 +2145,6 @@ public class MapleMap {
|
||||
activateItemReactors(mdrop, owner.getClient());
|
||||
}
|
||||
|
||||
public final void spawnItemDropList(List<Integer> list, final MapObject dropper, final Character owner, Point pos) {
|
||||
spawnItemDropList(list, 1, 1, dropper, owner, pos, true, false);
|
||||
}
|
||||
|
||||
public final void spawnItemDropList(List<Integer> list, int minCopies, int maxCopies, final MapObject dropper, final Character owner, Point pos) {
|
||||
spawnItemDropList(list, minCopies, maxCopies, dropper, owner, pos, true, false);
|
||||
}
|
||||
|
||||
// spawns item instances of all defined item ids on a list
|
||||
public final void spawnItemDropList(List<Integer> list, int minCopies, int maxCopies, final MapObject dropper, final Character owner, Point pos, final boolean ffaDrop, final boolean playerDrop) {
|
||||
int copies = (maxCopies - minCopies) + 1;
|
||||
if (copies < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Collections.shuffle(list);
|
||||
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
Random rnd = new Random();
|
||||
|
||||
final Point dropPos = new Point(pos);
|
||||
dropPos.x -= (12 * list.size());
|
||||
|
||||
for (Integer integer : list) {
|
||||
if (integer == 0) {
|
||||
spawnMesoDrop(owner != null ? 10 * owner.getMesoRate() : 10, calcDropPos(dropPos, pos), dropper, owner, playerDrop, (byte) (ffaDrop ? 2 : 0));
|
||||
} else {
|
||||
final Item drop;
|
||||
int randomedId = integer;
|
||||
|
||||
if (ItemConstants.getInventoryType(randomedId) != InventoryType.EQUIP) {
|
||||
drop = new Item(randomedId, (short) 0, (short) (rnd.nextInt(copies) + minCopies));
|
||||
} else {
|
||||
drop = ii.randomizeStats((Equip) ii.getEquipById(randomedId));
|
||||
}
|
||||
|
||||
spawnItemDrop(dropper, owner, drop, calcDropPos(dropPos, pos), ffaDrop, playerDrop);
|
||||
}
|
||||
|
||||
dropPos.x += 25;
|
||||
}
|
||||
}
|
||||
|
||||
private void registerMapSchedule(Runnable r, long delay) {
|
||||
OverallService service = (OverallService) this.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
|
||||
service.registerOverallAction(mapid, r, delay);
|
||||
@@ -2847,20 +2775,23 @@ public class MapleMap {
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, Point rangedFrom) {
|
||||
broadcastItemDropMessage(mdrop, dropperPos, dropPos, mod, getRangedDistance(), rangedFrom);
|
||||
private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, short delay,
|
||||
Point rangedFrom) {
|
||||
broadcastItemDropMessage(mdrop, dropperPos, dropPos, mod, delay, getRangedDistance(), rangedFrom);
|
||||
}
|
||||
|
||||
private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod) {
|
||||
broadcastItemDropMessage(mdrop, dropperPos, dropPos, mod, Double.POSITIVE_INFINITY, null);
|
||||
private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, short delay) {
|
||||
broadcastItemDropMessage(mdrop, dropperPos, dropPos, mod, delay, Double.POSITIVE_INFINITY, null);
|
||||
}
|
||||
|
||||
private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, double rangeSq, Point rangedFrom) {
|
||||
private void broadcastItemDropMessage(MapItem mdrop, Point dropperPos, Point dropPos, byte mod, short delay,
|
||||
double rangeSq, Point rangedFrom) {
|
||||
chrRLock.lock();
|
||||
try {
|
||||
for (Character chr : characters) {
|
||||
Packet packet = PacketCreator.dropItemFromMapObject(chr, mdrop, dropperPos, dropPos, mod);
|
||||
Packet packet = PacketCreator.dropItemFromMapObject(chr, mdrop, dropperPos, dropPos, mod, delay);
|
||||
|
||||
// TODO: remove along with USE_MAXRANGE config
|
||||
if (rangeSq < Double.POSITIVE_INFINITY) {
|
||||
if (rangedFrom.distanceSq(chr.getPosition()) <= rangeSq) {
|
||||
chr.sendPacket(packet);
|
||||
@@ -3385,12 +3316,14 @@ public class MapleMap {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: no reason to implement runnable - this is not intended to be submitted to another thread
|
||||
private class MobLootEntry implements Runnable {
|
||||
|
||||
private final byte droptype;
|
||||
private final int mobpos;
|
||||
private final int chRate;
|
||||
private final Point pos;
|
||||
private final short delay;
|
||||
private final List<MonsterDropEntry> dropEntry;
|
||||
private final List<MonsterDropEntry> visibleQuestEntry;
|
||||
private final List<MonsterDropEntry> otherQuestEntry;
|
||||
@@ -3398,11 +3331,15 @@ public class MapleMap {
|
||||
private final Character chr;
|
||||
private final Monster mob;
|
||||
|
||||
protected MobLootEntry(byte droptype, int mobpos, int chRate, Point pos, List<MonsterDropEntry> dropEntry, List<MonsterDropEntry> visibleQuestEntry, List<MonsterDropEntry> otherQuestEntry, List<MonsterGlobalDropEntry> globalEntry, Character chr, Monster mob) {
|
||||
protected MobLootEntry(byte droptype, int mobpos, int chRate, Point pos, short delay,
|
||||
List<MonsterDropEntry> dropEntry, List<MonsterDropEntry> visibleQuestEntry,
|
||||
List<MonsterDropEntry> otherQuestEntry, List<MonsterGlobalDropEntry> globalEntry,
|
||||
Character chr, Monster mob) {
|
||||
this.droptype = droptype;
|
||||
this.mobpos = mobpos;
|
||||
this.chRate = chRate;
|
||||
this.pos = pos;
|
||||
this.delay = delay;
|
||||
this.dropEntry = dropEntry;
|
||||
this.visibleQuestEntry = visibleQuestEntry;
|
||||
this.otherQuestEntry = otherQuestEntry;
|
||||
@@ -3416,14 +3353,14 @@ public class MapleMap {
|
||||
byte d = 1;
|
||||
|
||||
// Normal Drops
|
||||
d = dropItemsFromMonsterOnMap(dropEntry, pos, d, chRate, droptype, mobpos, chr, mob);
|
||||
d = dropItemsFromMonsterOnMap(dropEntry, pos, d, chRate, droptype, mobpos, chr, mob, delay);
|
||||
|
||||
// Global Drops
|
||||
d = dropGlobalItemsFromMonsterOnMap(globalEntry, pos, d, droptype, mobpos, chr, mob);
|
||||
d = dropGlobalItemsFromMonsterOnMap(globalEntry, pos, d, droptype, mobpos, chr, mob, delay);
|
||||
|
||||
// Quest Drops
|
||||
d = dropItemsFromMonsterOnMap(visibleQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob);
|
||||
dropItemsFromMonsterOnMap(otherQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob);
|
||||
d = dropItemsFromMonsterOnMap(visibleQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob, delay);
|
||||
dropItemsFromMonsterOnMap(otherQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob, delay);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4443,11 +4380,6 @@ public class MapleMap {
|
||||
expireItemsTask = null;
|
||||
}
|
||||
|
||||
if (mobSpawnLootTask != null) {
|
||||
mobSpawnLootTask.cancel(false);
|
||||
mobSpawnLootTask = null;
|
||||
}
|
||||
|
||||
if (characterStatUpdateTask != null) {
|
||||
characterStatUpdateTask.cancel(false);
|
||||
characterStatUpdateTask = null;
|
||||
|
||||
@@ -25,7 +25,11 @@ import client.Character;
|
||||
import client.Client;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import constants.skills.*;
|
||||
import constants.skills.BlazeWizard;
|
||||
import constants.skills.Evan;
|
||||
import constants.skills.FPMage;
|
||||
import constants.skills.NightWalker;
|
||||
import constants.skills.Shadower;
|
||||
import net.packet.Packet;
|
||||
import server.StatEffect;
|
||||
import server.life.MobSkill;
|
||||
|
||||
@@ -33,7 +33,12 @@ import server.Trade;
|
||||
import tools.PacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -617,4 +622,4 @@ public class PlayerShop extends AbstractMapObject {
|
||||
return mesos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,13 +32,52 @@ import provider.DataProvider;
|
||||
import provider.DataProviderFactory;
|
||||
import provider.DataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import server.quest.actions.*;
|
||||
import server.quest.requirements.*;
|
||||
import server.quest.actions.AbstractQuestAction;
|
||||
import server.quest.actions.BuffAction;
|
||||
import server.quest.actions.ExpAction;
|
||||
import server.quest.actions.FameAction;
|
||||
import server.quest.actions.InfoAction;
|
||||
import server.quest.actions.ItemAction;
|
||||
import server.quest.actions.MesoAction;
|
||||
import server.quest.actions.NextQuestAction;
|
||||
import server.quest.actions.PetSkillAction;
|
||||
import server.quest.actions.PetSpeedAction;
|
||||
import server.quest.actions.PetTamenessAction;
|
||||
import server.quest.actions.QuestAction;
|
||||
import server.quest.actions.SkillAction;
|
||||
import server.quest.requirements.AbstractQuestRequirement;
|
||||
import server.quest.requirements.BuffExceptRequirement;
|
||||
import server.quest.requirements.BuffRequirement;
|
||||
import server.quest.requirements.CompletedQuestRequirement;
|
||||
import server.quest.requirements.EndDateRequirement;
|
||||
import server.quest.requirements.FieldEnterRequirement;
|
||||
import server.quest.requirements.InfoExRequirement;
|
||||
import server.quest.requirements.InfoNumberRequirement;
|
||||
import server.quest.requirements.IntervalRequirement;
|
||||
import server.quest.requirements.ItemRequirement;
|
||||
import server.quest.requirements.JobRequirement;
|
||||
import server.quest.requirements.MaxLevelRequirement;
|
||||
import server.quest.requirements.MesoRequirement;
|
||||
import server.quest.requirements.MinLevelRequirement;
|
||||
import server.quest.requirements.MinTamenessRequirement;
|
||||
import server.quest.requirements.MobRequirement;
|
||||
import server.quest.requirements.MonsterBookCountRequirement;
|
||||
import server.quest.requirements.NpcRequirement;
|
||||
import server.quest.requirements.PetRequirement;
|
||||
import server.quest.requirements.QuestRequirement;
|
||||
import server.quest.requirements.ScriptRequirement;
|
||||
import tools.PacketCreator;
|
||||
import tools.StringUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
Reference in New Issue
Block a user