Added Puppets on Aggro + Guild/Alliance packet info patch + Pet flags
Added pet flags recognition, such as the "pet speed same as owner" mechanic. Fixed repeatable quest "Remnants of HT..." not rewarding Dragon Stones. Refactored character object's check slots method, now using the same algorithm from the API class. Fixed a vunerability from before reviving/respawning players to towns, on where players would get their HP restored in area map before sending them back to safety. Fixed some NPC gates in Mushking Empire, allowing players to access bosses free of quest requirements. Improved dialog of NPCs for "permission to attempt Zakum expeds". Implemented (or rather improved) item scripts, now using NPC conversation methods as it was intended originally. Rehauled mob-skill "summon mobs" limit, now using the placeholders found in the wz to determine limit of concurrent underlings spawned, and "limit" now refers to total of underlings spawned from a mob. Revised IoSession closing mechanics in several login classes. Implemented automated loggedin account shutdown if another player has gained a pass from the DB to login. Improved the server-side check for teleport rocks. Fixed removeAfter from mobs being procced after they were disposed. Reviewed EIM start references on several scripts, minding the latest EIM setup implementations. Fixed equipments that were reused Scissors of Karma not not being able to sell on pshops/merchants. Fixed a bug where entering alliances would lead a player to see information from other guilds of past alliances until an update takes place. Padronized a few skillbook names. Reviewed player puppets not gaining priority properly on the recent aggro system. Improved "item sold" message of merchants, now displaying also quantity left of an item on store. Fixed a bug with itemid limits of weapons on server.
This commit is contained in:
@@ -66,6 +66,7 @@ import net.server.world.World;
|
||||
import scripting.event.EventInstanceManager;
|
||||
import server.CashShop;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.MapleItemInformationProvider.ScriptedItem;
|
||||
import server.MaplePortal;
|
||||
import server.MapleShop;
|
||||
import server.MapleStatEffect;
|
||||
@@ -287,6 +288,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
private AutobanManager autoban;
|
||||
private boolean isbanned = false;
|
||||
private boolean blockCashShop = false;
|
||||
private boolean allowExpGain = false;
|
||||
private byte pendantExp = 0, lastmobcount = 0, doorSlot = -1;
|
||||
private List<Integer> trockmaps = new ArrayList<>();
|
||||
private List<Integer> viptrockmaps = new ArrayList<>();
|
||||
@@ -649,6 +651,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
|
||||
public void addSummon(int id, MapleSummon summon) {
|
||||
summons.put(id, summon);
|
||||
|
||||
if (summon.isPuppet()) {
|
||||
map.addPlayerPuppet(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void addVisibleMapObject(MapleMapObject mo) {
|
||||
@@ -805,6 +811,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
public void toggleBlockCashShop() {
|
||||
blockCashShop = !blockCashShop;
|
||||
}
|
||||
|
||||
public void toggleExpGain() {
|
||||
allowExpGain = !allowExpGain;
|
||||
}
|
||||
|
||||
public void setClient(MapleClient c) {
|
||||
this.client = c;
|
||||
@@ -1728,6 +1738,15 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<MapleMonster> getControlledMonsters() {
|
||||
cpnLock.lock();
|
||||
try {
|
||||
return new ArrayList<>(controlled);
|
||||
} finally {
|
||||
cpnLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseControlledMonsters() {
|
||||
Collection<MapleMonster> controlledMonsters;
|
||||
|
||||
@@ -1799,7 +1818,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
mpcs = getPartyMembersOnSameMap();
|
||||
}
|
||||
|
||||
String itemScriptName = null;
|
||||
ScriptedItem itemScript = null;
|
||||
mapitem.lockItem();
|
||||
try {
|
||||
if (mapitem.isPickedUp()) {
|
||||
@@ -1872,9 +1891,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
this.gainMeso(mapitem.getMeso(), true, true, false);
|
||||
}
|
||||
} else if (mItem.getItemId() / 10000 == 243) {
|
||||
MapleItemInformationProvider.scriptedItem info = ii.getScriptedItemInfo(mItem.getItemId());
|
||||
if (info.runOnPickup()) {
|
||||
itemScriptName = info.getScript();
|
||||
ScriptedItem info = ii.getScriptedItemInfo(mItem.getItemId());
|
||||
if (info != null && info.runOnPickup()) {
|
||||
itemScript = info;
|
||||
} else {
|
||||
if (!MapleInventoryManipulator.addFromDrop(client, mItem, true)) {
|
||||
client.announce(MaplePacketCreator.enableActions());
|
||||
@@ -1908,11 +1927,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
mapitem.unlockItem();
|
||||
}
|
||||
|
||||
if (itemScriptName != null) {
|
||||
if (itemScript != null) {
|
||||
ItemScriptManager ism = ItemScriptManager.getInstance();
|
||||
if (ism.scriptExists(itemScriptName)) {
|
||||
ism.runItemScript(client, itemScriptName);
|
||||
}
|
||||
ism.runItemScript(client, itemScript);
|
||||
}
|
||||
}
|
||||
client.announce(MaplePacketCreator.enableActions());
|
||||
@@ -1927,14 +1944,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
public boolean canHold(int itemid, int quantity) {
|
||||
int hold = getCleanItemQuantity(itemid, false);
|
||||
|
||||
if(hold > 0) {
|
||||
if(hold + quantity <= ii.getSlotMax(client, itemid))
|
||||
return true;
|
||||
}
|
||||
|
||||
return getInventory(ItemConstants.getInventoryType(itemid)).getNextFreeSlot() > -1;
|
||||
return client.getAbstractPlayerInteraction().canHold(itemid, quantity);
|
||||
}
|
||||
|
||||
public boolean isRidingBattleship() {
|
||||
@@ -2907,7 +2917,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
private synchronized void gainExpInternal(long gain, int equip, int party, boolean show, boolean inChat, boolean white) { // need of method synchonization here detected thanks to MedicOP
|
||||
long total = Math.max(gain, -exp.get());
|
||||
|
||||
if (level < getMaxLevel()) {
|
||||
if (level < getMaxLevel() && (allowExpGain || this.getEventInstance() != null)) {
|
||||
long leftover = 0;
|
||||
long nextExp = exp.get() + total;
|
||||
|
||||
@@ -3464,9 +3474,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
getMap().broadcastMessage(MaplePacketCreator.removeSummon(summon, true), summon.getPosition());
|
||||
getMap().removeMapObject(summon);
|
||||
removeVisibleMapObject(summon);
|
||||
|
||||
summons.remove(summonId);
|
||||
|
||||
if (summon.getSkill() == DarkKnight.BEHOLDER) {
|
||||
if (summon.isPuppet()) {
|
||||
map.removePlayerPuppet(this);
|
||||
} else if (summon.getSkill() == DarkKnight.BEHOLDER) {
|
||||
if (beholderHealingSchedule != null) {
|
||||
beholderHealingSchedule.cancel(false);
|
||||
beholderHealingSchedule = null;
|
||||
@@ -4602,19 +4614,19 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
public int getItemQuantity(int itemid, boolean checkEquipped) {
|
||||
int possesed = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countById(itemid);
|
||||
int count = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countById(itemid);
|
||||
if (checkEquipped) {
|
||||
possesed += inventory[MapleInventoryType.EQUIPPED.ordinal()].countById(itemid);
|
||||
count += inventory[MapleInventoryType.EQUIPPED.ordinal()].countById(itemid);
|
||||
}
|
||||
return possesed;
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getCleanItemQuantity(int itemid, boolean checkEquipped) {
|
||||
int possesed = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countNotOwnedById(itemid);
|
||||
int count = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countNotOwnedById(itemid);
|
||||
if (checkEquipped) {
|
||||
possesed += inventory[MapleInventoryType.EQUIPPED.ordinal()].countNotOwnedById(itemid);
|
||||
count += inventory[MapleInventoryType.EQUIPPED.ordinal()].countNotOwnedById(itemid);
|
||||
}
|
||||
return possesed;
|
||||
return count;
|
||||
}
|
||||
|
||||
public MapleJob getJob() {
|
||||
@@ -6600,13 +6612,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
rs = ps.executeQuery();
|
||||
while (rs.next()) {
|
||||
String name = rs.getString("name");
|
||||
if (rs.getString("name").equals("rescueGaga")) {
|
||||
if (rs.getString("name").contentEquals("rescueGaga")) {
|
||||
ret.events.put(name, new RescueGaga(rs.getInt("info")));
|
||||
}
|
||||
//ret.events = new MapleEvents(new RescueGaga(rs.getInt("rescuegaga")), new ArtifactHunt(rs.getInt("artifacthunt")));
|
||||
}
|
||||
if (!ret.events.containsKey("rescueGaga")) {
|
||||
ret.events.put("rescueGaga", new RescueGaga(0));
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
@@ -6988,13 +6996,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
public void respawn(EventInstanceManager eim, int returnMap) {
|
||||
cancelAllBuffs(false);
|
||||
|
||||
updateHp(50);
|
||||
setStance(0);
|
||||
|
||||
if (eim != null) eim.unregisterPlayer(this); // some event scripts uses this...
|
||||
changeMap(returnMap);
|
||||
|
||||
cancelAllBuffs(false); // thanks Oblivium91 for finding out players still could revive in area and take damage before returning to town
|
||||
updateHp(50);
|
||||
setStance(0);
|
||||
}
|
||||
|
||||
private void prepareDragonBlood(final MapleStatEffect bloodEffect) {
|
||||
@@ -7514,6 +7521,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
this.getInventory(itEntry.getRight()).addItem(itEntry.getLeft());
|
||||
}
|
||||
|
||||
this.events.put("rescueGaga", new RescueGaga(0));
|
||||
|
||||
Connection con = null;
|
||||
PreparedStatement ps = null;
|
||||
|
||||
@@ -9733,7 +9742,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
events = null;
|
||||
mpc = null;
|
||||
mgc = null;
|
||||
events = null;
|
||||
party = null;
|
||||
family = null;
|
||||
|
||||
|
||||
+16
-10
@@ -517,11 +517,15 @@ public class MapleClient {
|
||||
}
|
||||
|
||||
public int login(String login, String pwd, String nibbleHwid) {
|
||||
loginattempt++;
|
||||
if (loginattempt > 4) {
|
||||
MapleSessionCoordinator.getInstance().closeSession(session, false);
|
||||
}
|
||||
int loginok = 5;
|
||||
|
||||
loginattempt++;
|
||||
if (loginattempt > 4) {
|
||||
loggedIn = false;
|
||||
MapleSessionCoordinator.getInstance().closeSession(session, false);
|
||||
return loginok;
|
||||
}
|
||||
|
||||
Connection con = null;
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
@@ -748,7 +752,12 @@ public class MapleClient {
|
||||
return accId;
|
||||
}
|
||||
|
||||
public void updateLoginState(int newstate) {
|
||||
public void updateLoginState(int newstate) {
|
||||
// rules out possibility of multiple account entries
|
||||
if (newstate == LOGIN_LOGGEDIN) {
|
||||
MapleSessionCoordinator.getInstance().updateOnlineSession(this.getSession());
|
||||
}
|
||||
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = ?, lastlogin = ? WHERE id = ?")) {
|
||||
@@ -763,6 +772,7 @@ public class MapleClient {
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (newstate == LOGIN_NOTLOGGEDIN) {
|
||||
loggedIn = false;
|
||||
serverTransition = false;
|
||||
@@ -797,9 +807,6 @@ public class MapleClient {
|
||||
MapleSessionCoordinator.getInstance().closeSession(session, null);
|
||||
updateLoginState(LOGIN_NOTLOGGEDIN);
|
||||
}
|
||||
} else if (state == LOGIN_LOGGEDIN && player == null) {
|
||||
state = LOGIN_LOGGEDIN;
|
||||
updateLoginState(LOGIN_LOGGEDIN);
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
@@ -1006,8 +1013,7 @@ public class MapleClient {
|
||||
|
||||
Server.getInstance().unregisterLoginState(this);
|
||||
|
||||
this.session.setAttribute(MapleClient.CLIENT_KEY, null);
|
||||
this.accountName = null;
|
||||
this.accountName = null;
|
||||
this.macs = null;
|
||||
this.hwid = null;
|
||||
this.birthday = null;
|
||||
|
||||
@@ -193,6 +193,7 @@ public class CommandsExecutor {
|
||||
addCommand("int", StatIntCommand.class);
|
||||
addCommand("luk", StatLukCommand.class);
|
||||
addCommand("enableauth", EnableAuthCommand.class);
|
||||
addCommand("toggleexp", ToggleExpCommand.class);
|
||||
|
||||
commandsNameDesc.add(levelCommandsCursor);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
|
||||
Copyleft (L) 2016 - 2018 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
@Author: Ronan
|
||||
*/
|
||||
package client.command.commands.gm0;
|
||||
|
||||
import client.command.Command;
|
||||
import client.MapleClient;
|
||||
|
||||
public class ToggleExpCommand extends Command {
|
||||
{
|
||||
setDescription("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(MapleClient c, String[] params) {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
c.getPlayer().toggleExpGain(); // Vcoc's idea
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,12 +46,19 @@ public class GotoCommand extends Command {
|
||||
player.yellowMessage("Syntax: @goto <map name>");
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.getEventInstance() != null || MapleMiniDungeonInfo.isDungeonMap(player.getMapId()) || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit()) || !player.isAlive()) {
|
||||
player.dropMessage(1, "This command can not be used in this map.");
|
||||
|
||||
if (!player.isAlive()) {
|
||||
player.dropMessage(1, "This command cannot be used when you're dead.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player.isGM()) {
|
||||
if (player.getEventInstance() != null || MapleMiniDungeonInfo.isDungeonMap(player.getMapId()) || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit())) {
|
||||
player.dropMessage(1, "This command can not be used in this map.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HashMap<String, Integer> gotomaps;
|
||||
if (player.isGM()) {
|
||||
gotomaps = new HashMap<>(GameConstants.GOTO_AREAS); // distinct map registry for GM/users suggested thanks to Vcoc
|
||||
|
||||
@@ -50,11 +50,18 @@ public class WarpCommand extends Command {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.getEventInstance() != null || MapleMiniDungeonInfo.isDungeonMap(player.getMapId()) || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit()) || !player.isAlive()) {
|
||||
player.dropMessage(1, "This command cannot be used in this map.");
|
||||
if (!player.isAlive()) {
|
||||
player.dropMessage(1, "This command cannot be used when you're dead.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player.isGM()) {
|
||||
if (player.getEventInstance() != null || MapleMiniDungeonInfo.isDungeonMap(player.getMapId()) || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit())) {
|
||||
player.dropMessage(1, "This command cannot be used in this map.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// expedition issue with this command detected thanks to Masterrulax
|
||||
player.saveLocationOnWarp();
|
||||
player.changeMap(target, target.getRandomPlayerSpawnpoint());
|
||||
|
||||
@@ -27,6 +27,7 @@ import client.MapleStat;
|
||||
import client.command.Command;
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import constants.ItemConstants;
|
||||
import server.MapleItemInformationProvider;
|
||||
|
||||
public class FaceCommand extends Command {
|
||||
@@ -45,7 +46,7 @@ public class FaceCommand extends Command {
|
||||
try {
|
||||
if (params.length == 1) {
|
||||
int itemId = Integer.parseInt(params[0]);
|
||||
if (!(itemId >= 20000 && itemId < 22000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
|
||||
if (!ItemConstants.isFace(itemId) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
|
||||
player.yellowMessage("Face id '" + params[0] + "' does not exist.");
|
||||
return;
|
||||
}
|
||||
@@ -55,7 +56,7 @@ public class FaceCommand extends Command {
|
||||
player.equipChanged();
|
||||
} else {
|
||||
int itemId = Integer.parseInt(params[1]);
|
||||
if (!(itemId >= 20000 && itemId < 22000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
|
||||
if (!ItemConstants.isFace(itemId) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
|
||||
player.yellowMessage("Face id '" + params[1] + "' does not exist.");
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import client.MapleStat;
|
||||
import client.command.Command;
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import constants.ItemConstants;
|
||||
import server.MapleItemInformationProvider;
|
||||
|
||||
public class HairCommand extends Command {
|
||||
@@ -45,7 +46,7 @@ public class HairCommand extends Command {
|
||||
try {
|
||||
if (params.length == 1) {
|
||||
int itemId = Integer.parseInt(params[0]);
|
||||
if (!(itemId >= 30000 && itemId < 35000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
|
||||
if (!ItemConstants.isHair(itemId) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
|
||||
player.yellowMessage("Hair id '" + params[0] + "' does not exist.");
|
||||
return;
|
||||
}
|
||||
@@ -55,7 +56,7 @@ public class HairCommand extends Command {
|
||||
player.equipChanged();
|
||||
} else {
|
||||
int itemId = Integer.parseInt(params[1]);
|
||||
if (!(itemId >= 30000 && itemId < 35000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
|
||||
if (!ItemConstants.isHair(itemId) || MapleItemInformationProvider.getInstance().getName(itemId) == null) {
|
||||
player.yellowMessage("Hair id '" + params[1] + "' does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,21 @@ public class MaplePet extends Item {
|
||||
private Point pos;
|
||||
private int stance;
|
||||
private boolean summoned;
|
||||
private int petFlag = 0;
|
||||
|
||||
public enum PetFlag {
|
||||
OWNER_SPEED(0x01);
|
||||
|
||||
private int i;
|
||||
|
||||
private PetFlag(int i) {
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
private MaplePet(int id, short position, int uniqueid) {
|
||||
super(id, position, (short) 1);
|
||||
@@ -63,7 +78,7 @@ public class MaplePet extends Item {
|
||||
try {
|
||||
MaplePet ret = new MaplePet(itemid, position, petid);
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT name, level, closeness, fullness, summoned FROM pets WHERE petid = ?"); // Get pet details..
|
||||
PreparedStatement ps = con.prepareStatement("SELECT name, level, closeness, fullness, summoned, flag FROM pets WHERE petid = ?"); // Get pet details..
|
||||
ps.setInt(1, petid);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
rs.next();
|
||||
@@ -72,6 +87,7 @@ public class MaplePet extends Item {
|
||||
ret.setLevel((byte) Math.min(rs.getByte("level"), 30));
|
||||
ret.setFullness(Math.min(rs.getInt("fullness"), 100));
|
||||
ret.setSummoned(rs.getInt("summoned") == 1);
|
||||
ret.setPetFlag(rs.getInt("flag"));
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
@@ -108,13 +124,14 @@ public class MaplePet extends Item {
|
||||
public void saveToDb() {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("UPDATE pets SET name = ?, level = ?, closeness = ?, fullness = ?, summoned = ? WHERE petid = ?");
|
||||
PreparedStatement ps = con.prepareStatement("UPDATE pets SET name = ?, level = ?, closeness = ?, fullness = ?, summoned = ?, flag = ? WHERE petid = ?");
|
||||
ps.setString(1, getName());
|
||||
ps.setInt(2, getLevel());
|
||||
ps.setInt(3, getCloseness());
|
||||
ps.setInt(4, getFullness());
|
||||
ps.setInt(5, isSummoned() ? 1 : 0);
|
||||
ps.setInt(6, getUniqueId());
|
||||
ps.setInt(6, getPetFlag());
|
||||
ps.setInt(7, getUniqueId());
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
con.close();
|
||||
@@ -126,7 +143,7 @@ public class MaplePet extends Item {
|
||||
public static int createPet(int itemid) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned) VALUES (?, ?, 1, 0, 100, 0)");
|
||||
PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned, flag) VALUES (?, ?, 1, 0, 100, 0, 0)");
|
||||
int ret = MapleCashidGenerator.generateCashId();
|
||||
ps.setInt(1, ret);
|
||||
ps.setString(2, MapleItemInformationProvider.getInstance().getName(itemid));
|
||||
@@ -143,7 +160,7 @@ public class MaplePet extends Item {
|
||||
public static int createPet(int itemid, byte level, int closeness, int fullness) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned) VALUES (?, ?, ?, ?, ?, 0)");
|
||||
PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned, flag) VALUES (?, ?, ?, ?, ?, 0, 0)");
|
||||
int ret = MapleCashidGenerator.generateCashId();
|
||||
ps.setInt(1, ret);
|
||||
ps.setString(2, MapleItemInformationProvider.getInstance().getName(itemid));
|
||||
@@ -274,6 +291,32 @@ public class MaplePet extends Item {
|
||||
public void setSummoned(boolean yes) {
|
||||
this.summoned = yes;
|
||||
}
|
||||
|
||||
public int getPetFlag() {
|
||||
return this.petFlag;
|
||||
}
|
||||
|
||||
private void setPetFlag(int flag) {
|
||||
this.petFlag = flag;
|
||||
}
|
||||
|
||||
public void addPetFlag(MapleCharacter owner, PetFlag flag) {
|
||||
this.petFlag |= flag.getValue();
|
||||
saveToDb();
|
||||
|
||||
Item petz = owner.getInventory(MapleInventoryType.CASH).getItem(getPosition());
|
||||
if (petz != null)
|
||||
owner.forceUpdateItem(petz);
|
||||
}
|
||||
|
||||
public void removePetFlag(MapleCharacter owner, PetFlag flag) {
|
||||
this.petFlag &= 0xFFFFFFFF ^ flag.getValue();
|
||||
saveToDb();
|
||||
|
||||
Item petz = owner.getInventory(MapleInventoryType.CASH).getItem(getPosition());
|
||||
if (petz != null)
|
||||
owner.forceUpdateItem(petz);
|
||||
}
|
||||
|
||||
public Pair<Integer, Boolean> canConsume(int itemId) {
|
||||
return MapleItemInformationProvider.getInstance().canPetConsume(this.getItemId(), itemId);
|
||||
|
||||
@@ -53,6 +53,7 @@ public class MapleKarmaManipulator {
|
||||
int flag = item.getFlag();
|
||||
|
||||
flag |= karmaFlag;
|
||||
flag &= (0xFFFFFFFF ^ ItemConstants.UNTRADEABLE);
|
||||
item.setFlag((byte) flag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ public class MakerProcessor {
|
||||
Map<Integer, Integer> reagentType = new LinkedHashMap<>();
|
||||
List<Integer> toRemove = new LinkedList<>();
|
||||
|
||||
boolean isWeapon = ItemConstants.isWeapon(toCreate) || ServerConstants.USE_MAKER_PERMISSIVE_ATKUP;
|
||||
boolean isWeapon = ItemConstants.isWeapon(toCreate) || ServerConstants.USE_MAKER_PERMISSIVE_ATKUP; // thanks Vcoc for finding a case where a weapon wouldn't be counted as such due to a bounding on isWeapon
|
||||
|
||||
for(Map.Entry<Integer, Short> r : reagentids.entrySet()) {
|
||||
int curRid = r.getKey();
|
||||
|
||||
@@ -217,7 +217,7 @@ public final class ItemConstants {
|
||||
}
|
||||
|
||||
public static boolean isWeapon(int itemId) {
|
||||
return itemId >= 1302000 && itemId < 1492024;
|
||||
return itemId >= 1302000 && itemId < 1493000;
|
||||
}
|
||||
|
||||
public static boolean isEquipment(int itemId) {
|
||||
@@ -235,4 +235,12 @@ public final class ItemConstants {
|
||||
public static boolean isWeddingToken(int itemId) {
|
||||
return itemId >= 4031357 && itemId <= 4031364;
|
||||
}
|
||||
|
||||
public static boolean isFace(int itemId) {
|
||||
return itemId >= 20000 && itemId < 22000;
|
||||
}
|
||||
|
||||
public static boolean isHair(int itemId) {
|
||||
return itemId >= 30000 && itemId < 35000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ public class ServerConstants {
|
||||
public static boolean USE_DISPLAY_NUMBERS_WITH_COMMA = true; //Enforce comma on displayed strings (use this when USE_UNITPRICE_WITH_COMMA is active and you still want to display comma-separated values).
|
||||
public static boolean USE_UNITPRICE_WITH_COMMA = true; //Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions.
|
||||
public static final byte MIN_UNDERLEVEL_TO_EXP_GAIN = 20; //Characters are unable to get EXP from a mob if their level are under this threshold, only if "USE_ENFORCE_MOB_LEVEL_RANGE" is enabled. For bosses, this attribute is doubled.
|
||||
public static final byte MIN_UNDERLEVEL_TO_EXP_LEECH = 40; //Characters are unable to leech EXP from party member kills whose level difference are past this limit.
|
||||
public static final byte MIN_RANGELEVEL_TO_EXP_LEECH = 40; //Characters are unable to leech EXP from party member kills whose level difference are past this limit.
|
||||
public static final byte MAX_MONITORED_BUFFSTATS = 5; //Limits accounting for "dormant" buff effects, that should take place when stronger stat buffs expires.
|
||||
public static final int MAX_AP = 32767; //Max AP allotted on the auto-assigner.
|
||||
public static final int MAX_EVENT_LEVELS = 8; //Event has different levels of rewarding system.
|
||||
|
||||
@@ -88,8 +88,8 @@ public class MapleServerHandler extends IoHandlerAdapter {
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(IoSession session, Throwable cause) {
|
||||
if (cause instanceof IOException) { // mina closes session automatically and does not call sessionClosed on IOException
|
||||
session.closeNow(); // calls sessionClosed
|
||||
if (cause instanceof IOException) {
|
||||
closeMapleSession(session);
|
||||
} else {
|
||||
MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
|
||||
|
||||
@@ -137,8 +137,7 @@ public class MapleServerHandler extends IoHandlerAdapter {
|
||||
session.setAttribute(MapleClient.CLIENT_KEY, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionClosed(IoSession session) throws Exception {
|
||||
private void closeMapleSession(IoSession session) {
|
||||
if (isLoginServerHandler()) {
|
||||
MapleSessionCoordinator.getInstance().closeLoginSession(session);
|
||||
} else {
|
||||
@@ -157,10 +156,15 @@ public class MapleServerHandler extends IoHandlerAdapter {
|
||||
FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, t);
|
||||
} finally {
|
||||
session.close();
|
||||
session.removeAttribute(MapleClient.CLIENT_KEY);
|
||||
session.removeAttribute(MapleClient.CLIENT_KEY);
|
||||
//client.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionClosed(IoSession session) throws Exception {
|
||||
closeMapleSession(session);
|
||||
super.sessionClosed(session);
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ import constants.ItemConstants;
|
||||
import constants.GameConstants;
|
||||
import constants.ServerConstants;
|
||||
import java.util.TimeZone;
|
||||
import net.server.coordinator.MapleSessionCoordinator;
|
||||
import server.CashShop.CashItemFactory;
|
||||
import server.MapleSkillbookInformationProvider;
|
||||
import server.ThreadManager;
|
||||
@@ -1693,7 +1694,7 @@ public class Server {
|
||||
if(c.isLoggedIn()) {
|
||||
c.disconnect(false, false);
|
||||
} else {
|
||||
c.getSession().close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1766,8 +1767,13 @@ public class Server {
|
||||
System.out.println("Worlds + Channels are offline.");
|
||||
acceptor.unbind();
|
||||
acceptor = null;
|
||||
if (!restart) {
|
||||
System.exit(0);
|
||||
if (!restart) { // shutdown hook deadlocks if System.exit() method is used within its body chores, thanks MIKE for pointing that out
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.exit(0);
|
||||
}
|
||||
}).start();
|
||||
} else {
|
||||
System.out.println("\r\nRestarting the server....\r\n");
|
||||
try {
|
||||
|
||||
@@ -130,7 +130,7 @@ public final class AllianceOperationHandler extends AbstractMaplePacketHandler {
|
||||
chr.saveGuildStatus();
|
||||
|
||||
Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.addGuildToAlliance(alliance, guildid, c), -1, -1);
|
||||
Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.updateAllianceInfo(alliance, c), -1, -1);
|
||||
Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.updateAllianceInfo(alliance, c.getWorld()), -1, -1);
|
||||
Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.allianceNotice(alliance.getId(), alliance.getNotice()), -1, -1);
|
||||
chr.getGuild().dropMessage("Your guild has joined the [" + alliance.getName() + "] union.");
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
public final class CharInfoRequestHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
slea.skip(4);
|
||||
int cid = slea.readInt();
|
||||
|
||||
@@ -24,7 +24,6 @@ package net.server.channel.handlers;
|
||||
import client.MapleBuffStat;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.SkillFactory;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import server.maps.MapleSummon;
|
||||
import server.maps.MapleMapObject;
|
||||
|
||||
@@ -25,6 +25,7 @@ import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.autoban.AutobanFactory;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import client.inventory.manipulator.MapleKarmaManipulator;
|
||||
@@ -465,22 +466,41 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
} else if (mode == Action.SET_ITEMS.getCode()) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
MapleInventoryType ivType = MapleInventoryType.getByType(slea.readByte());
|
||||
Item item = chr.getInventory(ivType).getItem(slea.readShort());
|
||||
short pos = slea.readShort();
|
||||
Item item = chr.getInventory(ivType).getItem(pos);
|
||||
short quantity = slea.readShort();
|
||||
byte targetSlot = slea.readByte();
|
||||
|
||||
if (targetSlot < 1 || targetSlot > 9) {
|
||||
System.out.println("[h4x] " + chr.getName() + " Trying to dupe on trade slot.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if (item == null) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Invalid item description."));
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(ServerConstants.USE_ENFORCE_UNMERCHABLE_CASH && ii.isCash(item.getItemId())) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Cash items are not allowed to be traded."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ServerConstants.USE_ENFORCE_UNMERCHABLE_PET && ItemConstants.isPet(item.getItemId())) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Pets are not allowed to be traded."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (quantity < 1 || quantity > item.getQuantity()) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough quantity of the item."));
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
if (chr.getTrade() != null) {
|
||||
|
||||
MapleTrade trade = chr.getTrade();
|
||||
if (trade != null) {
|
||||
if ((quantity <= item.getQuantity() && quantity >= 0) || ItemConstants.isRechargeable(item.getItemId())) {
|
||||
if (ii.isDropRestricted(item.getItemId())) { // ensure that undroppable items do not make it to the trade window
|
||||
if (!MapleKarmaManipulator.hasKarmaFlag(item)) {
|
||||
@@ -489,16 +509,38 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Item tradeItem = item.copy();
|
||||
if (ItemConstants.isRechargeable(item.getItemId())) {
|
||||
tradeItem.setQuantity(item.getQuantity());
|
||||
MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), item.getQuantity(), true);
|
||||
} else {
|
||||
|
||||
MapleInventory inv = chr.getInventory(ivType);
|
||||
inv.lockInventory();
|
||||
try {
|
||||
Item checkItem = chr.getInventory(ivType).getItem(pos);
|
||||
if (checkItem != item || checkItem.getPosition() != item.getPosition()) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Invalid item description."));
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
Item tradeItem = item.copy();
|
||||
if (ItemConstants.isRechargeable(item.getItemId())) {
|
||||
quantity = item.getQuantity();
|
||||
}
|
||||
|
||||
tradeItem.setQuantity(quantity);
|
||||
MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), quantity, true);
|
||||
tradeItem.setPosition(targetSlot);
|
||||
|
||||
if (trade.addItem(tradeItem)) {
|
||||
MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), quantity, true);
|
||||
|
||||
trade.getChr().announce(MaplePacketCreator.getTradeItemAdd((byte) 0, tradeItem));
|
||||
if (trade.getPartner() != null) {
|
||||
trade.getPartner().getChr().announce(MaplePacketCreator.getTradeItemAdd((byte) 1, tradeItem));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FilePrinter.printError(FilePrinter.TRADE_EXCEPTION, e, "Player '" + chr + "' tried to add " + ii.getName(item.getItemId()) + " qty. " + item.getQuantity() + " in trade (slot " + targetSlot + ") then exception occurred.");
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
tradeItem.setPosition(targetSlot);
|
||||
chr.getTrade().addItem(tradeItem);
|
||||
}
|
||||
}
|
||||
} else if (mode == Action.CONFIRM.getCode()) {
|
||||
|
||||
@@ -289,7 +289,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
}
|
||||
if (newAlliance != null) {
|
||||
c.announce(MaplePacketCreator.updateAllianceInfo(newAlliance, c));
|
||||
c.announce(MaplePacketCreator.updateAllianceInfo(newAlliance, c.getWorld()));
|
||||
c.announce(MaplePacketCreator.allianceNotice(newAlliance.getId(), newAlliance.getNotice()));
|
||||
|
||||
if (newcomer) {
|
||||
|
||||
@@ -27,8 +27,7 @@ import constants.ItemConstants;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import scripting.item.ItemScriptManager;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.MapleItemInformationProvider.scriptedItem;
|
||||
import tools.MaplePacketCreator;
|
||||
import server.MapleItemInformationProvider.ScriptedItem;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
@@ -38,19 +37,20 @@ import tools.data.input.SeekableLittleEndianAccessor;
|
||||
public final class ScriptedItemHandler extends AbstractMaplePacketHandler {
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
slea.readInt(); // trash stamp, thanks RMZero213
|
||||
short itemSlot = slea.readShort(); // item slot, thanks RMZero213
|
||||
int itemId = slea.readInt();
|
||||
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
slea.readInt(); // trash stamp (thanks rmzero)
|
||||
short itemSlot = slea.readShort(); // item slot (thanks rmzero)
|
||||
int itemId = slea.readInt(); // itemId
|
||||
scriptedItem info = ii.getScriptedItemInfo(itemId);
|
||||
ScriptedItem info = ii.getScriptedItemInfo(itemId);
|
||||
if (info == null) return;
|
||||
ItemScriptManager ism = ItemScriptManager.getInstance();
|
||||
|
||||
Item item = c.getPlayer().getInventory(ItemConstants.getInventoryType(itemId)).getItem(itemSlot);
|
||||
if (item == null || item.getItemId() != itemId || item.getQuantity() < 1 || !ism.scriptExists(info.getScript())) {
|
||||
if (item == null || item.getItemId() != itemId || item.getQuantity() < 1) {
|
||||
return;
|
||||
}
|
||||
ism.runItemScript(c, info.getScript());
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
//NPCScriptManager.getInstance().start(c, info.getNpc(), null, null);
|
||||
|
||||
ItemScriptManager ism = ItemScriptManager.getInstance();
|
||||
ism.runItemScript(c, info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ import tools.data.input.SeekableLittleEndianAccessor;
|
||||
* @author kevintjuh93
|
||||
*/
|
||||
public final class TrockAddMapHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
byte type = slea.readByte();
|
||||
|
||||
@@ -53,6 +53,7 @@ import server.MapleShop;
|
||||
import server.MapleShopFactory;
|
||||
import server.TimerManager;
|
||||
import server.maps.AbstractMapleMapObject;
|
||||
import server.maps.FieldLimit;
|
||||
import server.maps.MaplePlayerShopItem;
|
||||
import server.maps.MapleKite;
|
||||
import server.maps.MapleMap;
|
||||
@@ -106,31 +107,32 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
if (itemType == 504) { // vip teleport rock
|
||||
String error1 = "Either the player could not be found or you were trying to teleport to an illegal location.";
|
||||
boolean vip = slea.readByte() == 1;
|
||||
boolean vip = slea.readByte() == 1 && itemId / 1000 >= 5041;
|
||||
remove(c, position, itemId);
|
||||
boolean success = false;
|
||||
if (!vip) {
|
||||
int mapId = slea.readInt();
|
||||
if (c.getChannelServer().getMapFactory().getMap(mapId).getForcedReturnId() == 999999999) {
|
||||
player.changeMap(c.getChannelServer().getMapFactory().getMap(mapId));
|
||||
if (itemId / 1000 >= 5041 || mapId / 100000000 == player.getMapId() / 100000000) { //check vip or same continent
|
||||
MapleMap targetMap = c.getChannelServer().getMapFactory().getMap(mapId);
|
||||
if (!FieldLimit.CANNOTVIPROCK.check(targetMap.getFieldLimit()) && (targetMap.getForcedReturnId() == 999999999 || mapId < 100000000)) {
|
||||
player.forceChangeMap(targetMap, targetMap.getRandomPlayerSpawnpoint());
|
||||
success = true;
|
||||
} else {
|
||||
player.dropMessage(1, error1);
|
||||
}
|
||||
} else {
|
||||
MapleInventoryManipulator.addById(c, itemId, (short) 1);
|
||||
player.dropMessage(1, error1);
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
player.dropMessage(1, "You cannot teleport between continents with this teleport rock.");
|
||||
}
|
||||
} else {
|
||||
String name = slea.readMapleAsciiString();
|
||||
MapleCharacter victim = c.getChannelServer().getPlayerStorage().getCharacterByName(name);
|
||||
boolean success = false;
|
||||
|
||||
if (victim != null) {
|
||||
MapleMap target = victim.getMap();
|
||||
if (c.getChannelServer().getMapFactory().getMap(victim.getMapId()).getForcedReturnId() == 999999999 || victim.getMapId() < 100000000) {
|
||||
MapleMap targetMap = victim.getMap();
|
||||
if (!FieldLimit.CANNOTVIPROCK.check(targetMap.getFieldLimit()) && (targetMap.getForcedReturnId() == 999999999 || targetMap.getId() < 100000000)) {
|
||||
if (victim.gmLevel() <= player.gmLevel()) {
|
||||
if (itemId == 5041000 || victim.getMapId() / player.getMapId() == 1) { //viprock & same continent
|
||||
player.changeMap(target, target.findClosestPlayerSpawnpoint(victim.getPosition()));
|
||||
success = true;
|
||||
} else {
|
||||
player.dropMessage(1, "You cannot teleport between continents with this teleport rock.");
|
||||
}
|
||||
player.forceChangeMap(targetMap, targetMap.findClosestPlayerSpawnpoint(victim.getPosition()));
|
||||
success = true;
|
||||
} else {
|
||||
player.dropMessage(1, error1);
|
||||
}
|
||||
@@ -140,10 +142,11 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
} else {
|
||||
player.dropMessage(1, "Player could not be found in this channel.");
|
||||
}
|
||||
if (!success) {
|
||||
MapleInventoryManipulator.addById(c, itemId, (short) 1);
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
MapleInventoryManipulator.addById(c, itemId, (short) 1);
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
} else if (itemType == 505) { // AP/SP reset
|
||||
if(!player.isAlive()) {
|
||||
|
||||
@@ -25,8 +25,10 @@ import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import constants.ServerConstants;
|
||||
import client.MapleCharacter;
|
||||
@@ -56,6 +58,8 @@ public class MapleMonsterAggroCoordinator {
|
||||
private Map<MapleMonster, Map<Integer, PlayerAggroEntry>> mobAggroEntries = new HashMap<>();
|
||||
private Map<MapleMonster, List<PlayerAggroEntry>> mobSortedAggros = new HashMap<>();
|
||||
|
||||
private Set<Integer> mapPuppetEntries = new HashSet<>();
|
||||
|
||||
private class PlayerAggroEntry {
|
||||
protected int cid;
|
||||
protected int averageDamage = 0;
|
||||
@@ -291,6 +295,12 @@ public class MapleMonsterAggroCoordinator {
|
||||
}
|
||||
|
||||
public boolean isLeadingCharacterAggro(MapleMonster mob, MapleCharacter player) {
|
||||
if (mob.isLeadingPuppetInVicinity()) {
|
||||
return false;
|
||||
} else if (mob.isCharacterPuppetInVicinity(player)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// by assuming the quasi-sorted nature of "mobAggroList", this method
|
||||
// returns whether the player given as parameter can be elected as next aggro leader
|
||||
|
||||
@@ -342,6 +352,24 @@ public class MapleMonsterAggroCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
public void addPuppetAggro(MapleCharacter player) {
|
||||
synchronized (mapPuppetEntries) {
|
||||
mapPuppetEntries.add(player.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public void removePuppetAggro(Integer cid) {
|
||||
synchronized (mapPuppetEntries) {
|
||||
mapPuppetEntries.remove(cid);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Integer> getPuppetAggroList() {
|
||||
synchronized (mapPuppetEntries) {
|
||||
return new ArrayList<>(mapPuppetEntries);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
stopAggroCoordinator();
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ public class MapleSessionCoordinator {
|
||||
}
|
||||
|
||||
private final LoginStorage loginStorage = new LoginStorage();
|
||||
private final Map<Integer, MapleClient> onlineClients = new HashMap<>();
|
||||
private final Set<String> onlineRemoteHwids = new HashSet<>();
|
||||
private final Map<String, Set<IoSession>> loginRemoteHosts = new HashMap<>();
|
||||
private final Set<String> pooledRemoteHosts = new HashSet<>();
|
||||
@@ -221,6 +222,24 @@ public class MapleSessionCoordinator {
|
||||
return ((InetSocketAddress) session.getRemoteAddress()).getAddress().getHostAddress();
|
||||
}
|
||||
|
||||
private static MapleClient getSessionClient(IoSession session) {
|
||||
return (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
|
||||
}
|
||||
|
||||
public void updateOnlineSession(IoSession session) {
|
||||
MapleClient client = getSessionClient(session);
|
||||
|
||||
if (client != null) {
|
||||
int accountId = client.getAccID();
|
||||
MapleClient ingameClient = onlineClients.get(accountId);
|
||||
if (ingameClient != null) { // thanks MedicOP for finding out a loss of loggedin account uniqueness when using the CMS "Unstuck" feature
|
||||
ingameClient.forceDisconnect();
|
||||
}
|
||||
|
||||
onlineClients.put(accountId, client);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canStartLoginSession(IoSession session) {
|
||||
if (!ServerConstants.DETERRED_MULTICLIENT) return true;
|
||||
|
||||
@@ -296,6 +315,11 @@ public class MapleSessionCoordinator {
|
||||
String nibbleHwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID);
|
||||
if (nibbleHwid != null) {
|
||||
onlineRemoteHwids.remove(nibbleHwid);
|
||||
|
||||
MapleClient client = getSessionClient(session);
|
||||
if (client != null) {
|
||||
onlineClients.remove(client.getAccID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,6 +472,11 @@ public class MapleSessionCoordinator {
|
||||
hwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); // making sure to clean up calls to this function on login phase
|
||||
onlineRemoteHwids.remove(hwid);
|
||||
|
||||
MapleClient client = getSessionClient(session);
|
||||
if (client != null) {
|
||||
onlineClients.remove(client.getAccID());
|
||||
}
|
||||
|
||||
if (immediately != null) {
|
||||
session.close(immediately);
|
||||
}
|
||||
|
||||
@@ -130,7 +130,9 @@ public class MapleAlliance {
|
||||
|
||||
Server.getInstance().addAlliance(id, alliance);
|
||||
|
||||
Server.getInstance().allianceMessage(id, MaplePacketCreator.updateAllianceInfo(alliance, guildMasters.get(0).getClient()), -1, -1);
|
||||
int worldid = guildMasters.get(0).getWorld();
|
||||
Server.getInstance().allianceMessage(id, MaplePacketCreator.updateAllianceInfo(alliance, worldid), -1, -1);
|
||||
Server.getInstance().allianceMessage(id, MaplePacketCreator.getGuildAlliances(alliance, worldid), -1, -1); // thanks Vcoc for noticing guilds from other alliances being visually stacked here due to this not being updated
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
@@ -347,7 +349,7 @@ public class MapleAlliance {
|
||||
|
||||
public void updateAlliancePackets(MapleCharacter chr) {
|
||||
if (allianceId > 0) {
|
||||
this.broadcastMessage(MaplePacketCreator.updateAllianceInfo(this, chr.getClient()));
|
||||
this.broadcastMessage(MaplePacketCreator.updateAllianceInfo(this, chr.getWorld()));
|
||||
this.broadcastMessage(MaplePacketCreator.allianceNotice(this.getId(), this.getNotice()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,13 +78,13 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
|
||||
if (c.hasBannedMac() || c.hasBannedHWID()) {
|
||||
session.close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(session, true);
|
||||
return;
|
||||
}
|
||||
|
||||
Server server = Server.getInstance();
|
||||
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
|
||||
session.close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(session, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,13 +59,13 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
|
||||
if (c.hasBannedMac() || c.hasBannedHWID()) {
|
||||
c.getSession().close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
Server server = Server.getInstance();
|
||||
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
|
||||
c.getSession().close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
|
||||
c.setAccID(rs.getInt(1));
|
||||
rs.close();
|
||||
} catch (SQLException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
|
||||
c.setAccID(-1);
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
disposeSql(con, ps);
|
||||
|
||||
@@ -59,13 +59,13 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
|
||||
if (c.hasBannedMac() || c.hasBannedHWID()) {
|
||||
c.getSession().close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
Server server = Server.getInstance();
|
||||
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
|
||||
c.getSession().close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
c.getSession().close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand
|
||||
c.updateHWID(hwid);
|
||||
|
||||
if (c.hasBannedMac() || c.hasBannedHWID()) {
|
||||
c.getSession().close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand
|
||||
|
||||
Server server = Server.getInstance();
|
||||
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
|
||||
c.getSession().close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler
|
||||
c.updateHWID(hwid);
|
||||
|
||||
if (c.hasBannedMac() || c.hasBannedHWID()) {
|
||||
c.getSession().close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler
|
||||
|
||||
Server server = Server.getInstance();
|
||||
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
|
||||
c.getSession().close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle
|
||||
c.updateHWID(hwid);
|
||||
|
||||
if (c.hasBannedMac() || c.hasBannedHWID()) {
|
||||
c.getSession().close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle
|
||||
|
||||
Server server = Server.getInstance();
|
||||
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
|
||||
c.getSession().close(true);
|
||||
MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -228,7 +228,11 @@ public class AbstractPlayerInteraction {
|
||||
}
|
||||
|
||||
public boolean canHold(int itemid, int quantity) {
|
||||
return getPlayer().canHold(itemid, quantity);
|
||||
return canHoldAll(Collections.singletonList(itemid), Collections.singletonList(quantity), true);
|
||||
}
|
||||
|
||||
public boolean canHold(int itemid, int quantity, int removeItemid, int removeQuantity) {
|
||||
return canHoldAllAfterRemoving(Collections.singletonList(itemid), Collections.singletonList(quantity), Collections.singletonList(removeItemid), Collections.singletonList(removeQuantity));
|
||||
}
|
||||
|
||||
private static List<Integer> convertToIntegerArray(List<Double> list) {
|
||||
@@ -263,10 +267,6 @@ public class AbstractPlayerInteraction {
|
||||
return MapleInventory.checkSpots(c.getPlayer(), addedItems, false);
|
||||
}
|
||||
|
||||
public boolean canHold(int itemid, int quantity, int removeItemid, int removeQuantity) {
|
||||
return canHoldAllAfterRemoving(Collections.singletonList(itemid), Collections.singletonList(quantity), Collections.singletonList(removeItemid), Collections.singletonList(removeQuantity));
|
||||
}
|
||||
|
||||
private static List<Pair<Item, MapleInventoryType>> prepareProofInventoryItems(List<Pair<Integer, Integer>> items) {
|
||||
List<Pair<Item, MapleInventoryType>> addedItems = new LinkedList<>();
|
||||
for(Pair<Integer, Integer> p : items) {
|
||||
|
||||
+11
-11
@@ -1,8 +1,6 @@
|
||||
/*
|
||||
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 file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2018 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
@@ -19,17 +17,19 @@
|
||||
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 scripting.item;
|
||||
package scripting.event;
|
||||
|
||||
import client.MapleClient;
|
||||
import scripting.AbstractPlayerInteraction;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author kevintjuh93
|
||||
* @author Ronan
|
||||
*/
|
||||
public class ItemScriptMethods extends AbstractPlayerInteraction {
|
||||
public ItemScriptMethods(MapleClient c) {
|
||||
super(c);
|
||||
public class EventInstanceInProgressException extends Exception {
|
||||
|
||||
public static String EIIP_KEY = "Event instance ";
|
||||
|
||||
public EventInstanceInProgressException(String eventName, String eventInstance) {
|
||||
super(EIIP_KEY + "already in progress - " + eventName + ", EM: " + eventInstance);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -260,7 +260,7 @@ public class EventManager {
|
||||
}
|
||||
}
|
||||
|
||||
public EventInstanceManager newInstance(String name) {
|
||||
public EventInstanceManager newInstance(String name) throws EventInstanceInProgressException {
|
||||
EventInstanceManager ret = getReadyInstance();
|
||||
|
||||
if(ret == null) {
|
||||
@@ -271,7 +271,7 @@ public class EventManager {
|
||||
|
||||
synchronized (instances) {
|
||||
if (instances.containsKey(name)) {
|
||||
return null;
|
||||
throw new EventInstanceInProgressException(name, this.getName());
|
||||
}
|
||||
|
||||
instances.put(name, ret);
|
||||
@@ -365,6 +365,34 @@ public class EventManager {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private String getInternalScriptExceptionMessage(Throwable a) {
|
||||
if (!(a instanceof ScriptException)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while(true) {
|
||||
Throwable t = a;
|
||||
a = a.getCause();
|
||||
|
||||
if (a == null) {
|
||||
return t.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EventInstanceManager createInstance(String name, Object... args) throws ScriptException, NoSuchMethodException {
|
||||
return (EventInstanceManager) iv.invokeFunction(name, args);
|
||||
}
|
||||
|
||||
private void registerEventInstance(String eventName, int lobbyId) {
|
||||
Integer oldLobby = instanceLocks.get(eventName);
|
||||
if (oldLobby != null) {
|
||||
setLockLobby(oldLobby, false);
|
||||
}
|
||||
|
||||
instanceLocks.put(eventName, lobbyId);
|
||||
}
|
||||
|
||||
public boolean startInstance(MapleExpedition exped) {
|
||||
return startInstance(-1, exped);
|
||||
}
|
||||
@@ -394,9 +422,14 @@ public class EventManager {
|
||||
|
||||
EventInstanceManager eim;
|
||||
try {
|
||||
eim = (EventInstanceManager) (iv.invokeFunction("setup", leader.getClient().getChannel()));
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
} catch (NullPointerException npe) {
|
||||
eim = createInstance("setup", leader.getClient().getChannel());
|
||||
registerEventInstance(eim.getName(), lobbyId);
|
||||
} catch (ScriptException | NullPointerException e) {
|
||||
String message = getInternalScriptExceptionMessage(e);
|
||||
if (message != null && !message.startsWith(EventInstanceInProgressException.EIIP_KEY)) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
if(lobbyId > -1) {
|
||||
setLockLobby(lobbyId, false);
|
||||
}
|
||||
@@ -460,9 +493,14 @@ public class EventManager {
|
||||
|
||||
EventInstanceManager eim;
|
||||
try {
|
||||
eim = (EventInstanceManager) (iv.invokeFunction("setup", difficulty, (lobbyId > -1) ? lobbyId : leader.getId()));
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
} catch (NullPointerException npe) {
|
||||
eim = createInstance("setup", difficulty, (lobbyId > -1) ? lobbyId : leader.getId());
|
||||
registerEventInstance(eim.getName(), lobbyId);
|
||||
} catch (ScriptException | NullPointerException e) {
|
||||
String message = getInternalScriptExceptionMessage(e);
|
||||
if (message != null && !message.startsWith(EventInstanceInProgressException.EIIP_KEY)) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
if(lobbyId > -1) {
|
||||
setLockLobby(lobbyId, false);
|
||||
}
|
||||
@@ -520,9 +558,14 @@ public class EventManager {
|
||||
|
||||
EventInstanceManager eim;
|
||||
try {
|
||||
eim = (EventInstanceManager) (iv.invokeFunction("setup", (Object) null));
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
} catch (NullPointerException npe) {
|
||||
eim = createInstance("setup", (Object) null);
|
||||
registerEventInstance(eim.getName(), lobbyId);
|
||||
} catch (ScriptException | NullPointerException e) {
|
||||
String message = getInternalScriptExceptionMessage(e);
|
||||
if (message != null && !message.startsWith(EventInstanceInProgressException.EIIP_KEY)) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
if(lobbyId > -1) {
|
||||
setLockLobby(lobbyId, false);
|
||||
}
|
||||
@@ -582,9 +625,14 @@ public class EventManager {
|
||||
|
||||
EventInstanceManager eim;
|
||||
try {
|
||||
eim = (EventInstanceManager) (iv.invokeFunction("setup", difficulty, (lobbyId > -1) ? lobbyId : party.getLeaderId()));
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
} catch (NullPointerException npe) {
|
||||
eim = createInstance("setup", difficulty, (lobbyId > -1) ? lobbyId : party.getLeaderId());
|
||||
registerEventInstance(eim.getName(), lobbyId);
|
||||
} catch (ScriptException | NullPointerException e) {
|
||||
String message = getInternalScriptExceptionMessage(e);
|
||||
if (message != null && !message.startsWith(EventInstanceInProgressException.EIIP_KEY)) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
if(lobbyId > -1) {
|
||||
setLockLobby(lobbyId, false);
|
||||
}
|
||||
@@ -652,7 +700,7 @@ public class EventManager {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
instanceLocks.put(eim.getName(), lobbyId);
|
||||
registerEventInstance(eim.getName(), lobbyId);
|
||||
eim.setLeader(leader);
|
||||
|
||||
iv.invokeFunction("setup", eim);
|
||||
|
||||
@@ -22,21 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package scripting.item;
|
||||
|
||||
import client.MapleClient;
|
||||
import constants.ServerConstants;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.script.Compilable;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
import scripting.npc.NPCScriptManager;
|
||||
import server.MapleItemInformationProvider.ScriptedItem;
|
||||
|
||||
public class ItemScriptManager {
|
||||
|
||||
@@ -46,60 +33,7 @@ public class ItemScriptManager {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private Map<String, Invocable> scripts = new HashMap<>();
|
||||
private ScriptEngineFactory sef;
|
||||
|
||||
private ItemScriptManager() {
|
||||
ScriptEngineManager sem = new ScriptEngineManager();
|
||||
sef = sem.getEngineByName("javascript").getFactory();
|
||||
}
|
||||
|
||||
public boolean scriptExists(String scriptName) {
|
||||
File scriptFile = new File("scripts/item/" + scriptName + ".js");
|
||||
return scriptFile.exists();
|
||||
}
|
||||
|
||||
public void runItemScript(MapleClient c, String scriptName) {
|
||||
if (scripts.containsKey(scriptName)) {
|
||||
try {
|
||||
scripts.get(scriptName).invokeFunction("start", new ItemScriptMethods(c));
|
||||
} catch (ScriptException | NoSuchMethodException ex) {
|
||||
FilePrinter.printError(FilePrinter.ITEM + scriptName + ".txt", ex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
File scriptFile = new File("scripts/item/" + scriptName + ".js");
|
||||
if (!scriptFile.exists()) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
FileReader fr = null;
|
||||
ScriptEngine portal = sef.getScriptEngine();
|
||||
try {
|
||||
fr = new FileReader(scriptFile);
|
||||
|
||||
// java 8 support here thanks to Arufonsu
|
||||
if (ServerConstants.JAVA_8){
|
||||
portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator());
|
||||
}
|
||||
|
||||
((Compilable) portal).compile(fr).eval();
|
||||
|
||||
final Invocable script = ((Invocable) portal);
|
||||
scripts.put(scriptName, script);
|
||||
script.invokeFunction("start", new ItemScriptMethods(c));
|
||||
} catch (final UndeclaredThrowableException | ScriptException ute) {
|
||||
FilePrinter.printError(FilePrinter.ITEM + scriptName + ".txt", ute);
|
||||
} catch (final Exception e) {
|
||||
FilePrinter.printError(FilePrinter.ITEM + scriptName + ".txt", e);
|
||||
} finally {
|
||||
if (fr != null) {
|
||||
try {
|
||||
fr.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void runItemScript(MapleClient c, ScriptedItem scriptItem) {
|
||||
NPCScriptManager.getInstance().start(c, scriptItem, null);
|
||||
}
|
||||
}
|
||||
@@ -78,18 +78,18 @@ public class MapScriptManager {
|
||||
return;
|
||||
}
|
||||
FileReader fr = null;
|
||||
ScriptEngine portal = sef.getScriptEngine();
|
||||
ScriptEngine se = sef.getScriptEngine();
|
||||
try {
|
||||
fr = new FileReader(scriptFile);
|
||||
|
||||
// java 8 support here thanks to Arufonsu
|
||||
if (ServerConstants.JAVA_8){
|
||||
portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator());
|
||||
se.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator());
|
||||
}
|
||||
|
||||
((Compilable) portal).compile(fr).eval();
|
||||
((Compilable) se).compile(fr).eval();
|
||||
|
||||
final Invocable script = ((Invocable) portal);
|
||||
final Invocable script = ((Invocable) se);
|
||||
scripts.put(scriptName, script);
|
||||
script.invokeFunction("start", new MapScriptMethods(c));
|
||||
} catch (final UndeclaredThrowableException | ScriptException ute) {
|
||||
|
||||
@@ -74,16 +74,18 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
|
||||
private int npcOid;
|
||||
private String scriptName;
|
||||
private String getText;
|
||||
private boolean itemScript;
|
||||
|
||||
public NPCConversationManager(MapleClient c, int npc, String scriptName) {
|
||||
this(c, npc, -1, scriptName);
|
||||
this(c, npc, -1, scriptName, false);
|
||||
}
|
||||
|
||||
public NPCConversationManager(MapleClient c, int npc, int oid, String scriptName) {
|
||||
public NPCConversationManager(MapleClient c, int npc, int oid, String scriptName, boolean itemScript) {
|
||||
super(c);
|
||||
this.npc = npc;
|
||||
this.npcOid = oid;
|
||||
this.scriptName = scriptName;
|
||||
this.itemScript = itemScript;
|
||||
}
|
||||
|
||||
public int getNpc() {
|
||||
@@ -97,6 +99,14 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
|
||||
public String getScriptName() {
|
||||
return scriptName;
|
||||
}
|
||||
|
||||
public boolean isItemScript() {
|
||||
return itemScript;
|
||||
}
|
||||
|
||||
public void resetItemScript() {
|
||||
this.itemScript = false;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
NPCScriptManager.getInstance().dispose(this);
|
||||
|
||||
@@ -32,6 +32,7 @@ import javax.script.Invocable;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import scripting.AbstractScriptManager;
|
||||
import server.MapleItemInformationProvider.ScriptedItem;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
@@ -60,7 +61,7 @@ public class NPCScriptManager extends AbstractScriptManager {
|
||||
}
|
||||
|
||||
public boolean start(MapleClient c, int npc, MapleCharacter chr) {
|
||||
return start(c, npc, null, chr);
|
||||
return start(c, npc, -1, chr);
|
||||
}
|
||||
|
||||
public boolean start(MapleClient c, int npc, int oid, MapleCharacter chr) {
|
||||
@@ -70,27 +71,42 @@ public class NPCScriptManager extends AbstractScriptManager {
|
||||
public boolean start(MapleClient c, int npc, String fileName, MapleCharacter chr) {
|
||||
return start(c, npc, -1, fileName, chr);
|
||||
}
|
||||
|
||||
|
||||
public boolean start(MapleClient c, int npc, int oid, String fileName, MapleCharacter chr) {
|
||||
return start(c, npc, oid, fileName, chr, false, "cm");
|
||||
}
|
||||
|
||||
public boolean start(MapleClient c, ScriptedItem scriptItem, MapleCharacter chr) {
|
||||
return start(c, scriptItem.getNpc(), -1, scriptItem.getScript(), chr, true, "im");
|
||||
}
|
||||
|
||||
private boolean start(MapleClient c, int npc, int oid, String fileName, MapleCharacter chr, boolean itemScript, String engineName) {
|
||||
try {
|
||||
NPCConversationManager cm = new NPCConversationManager(c, npc, oid, fileName);
|
||||
NPCConversationManager cm = new NPCConversationManager(c, npc, oid, fileName, itemScript);
|
||||
if (cms.containsKey(c)) {
|
||||
dispose(c);
|
||||
}
|
||||
if (c.canClickNPC()) {
|
||||
cms.put(c, cm);
|
||||
Invocable iv = null;
|
||||
if (fileName != null) {
|
||||
iv = getInvocable("npc/" + fileName + ".js", c);
|
||||
if (!itemScript) {
|
||||
if (fileName != null) {
|
||||
iv = getInvocable("npc/" + fileName + ".js", c);
|
||||
}
|
||||
} else {
|
||||
if (fileName != null) { // thanks MiLin for drafting NPC-based item scripts
|
||||
iv = getInvocable("item/" + fileName + ".js", c);
|
||||
}
|
||||
}
|
||||
if (iv == null) {
|
||||
iv = getInvocable("npc/" + npc + ".js", c);
|
||||
cm.resetItemScript();
|
||||
}
|
||||
if (iv == null || NPCScriptManager.getInstance() == null) {
|
||||
dispose(c);
|
||||
return false;
|
||||
}
|
||||
engine.put("cm", cm);
|
||||
engine.put(engineName, cm);
|
||||
scripts.put(c, iv);
|
||||
c.setClickedNPC();
|
||||
try {
|
||||
@@ -142,10 +158,11 @@ public class NPCScriptManager extends AbstractScriptManager {
|
||||
cms.remove(c);
|
||||
scripts.remove(c);
|
||||
|
||||
String scriptFolder = (cm.isItemScript() ? "item" : "npc");
|
||||
if(cm.getScriptName() != null) {
|
||||
resetContext("npc/" + cm.getScriptName() + ".js", c);
|
||||
resetContext(scriptFolder + "/" + cm.getScriptName() + ".js", c);
|
||||
} else {
|
||||
resetContext("npc/" + cm.getNpc() + ".js", c);
|
||||
resetContext(scriptFolder + "/" + cm.getNpc() + ".js", c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ public class MapleItemInformationProvider {
|
||||
protected Map<Integer, Integer> monsterBookID = new HashMap<>();
|
||||
protected Map<Integer, Boolean> untradeableCache = new HashMap<>();
|
||||
protected Map<Integer, Boolean> onEquipUntradeableCache = new HashMap<>();
|
||||
protected Map<Integer, scriptedItem> scriptedItemCache = new HashMap<>();
|
||||
protected Map<Integer, ScriptedItem> scriptedItemCache = new HashMap<>();
|
||||
protected Map<Integer, Boolean> karmaCache = new HashMap<>();
|
||||
protected Map<Integer, Integer> triggerItemCache = new HashMap<>();
|
||||
protected Map<Integer, Integer> expCache = new HashMap<>();
|
||||
@@ -1447,14 +1447,14 @@ public class MapleItemInformationProvider {
|
||||
return untradeableOnEquip;
|
||||
}
|
||||
|
||||
public scriptedItem getScriptedItemInfo(int itemId) {
|
||||
public ScriptedItem getScriptedItemInfo(int itemId) {
|
||||
if (scriptedItemCache.containsKey(itemId)) {
|
||||
return scriptedItemCache.get(itemId);
|
||||
}
|
||||
if ((itemId / 10000) != 243) {
|
||||
return null;
|
||||
}
|
||||
scriptedItem script = new scriptedItem(MapleDataTool.getInt("spec/npc", getItemData(itemId), 0),
|
||||
ScriptedItem script = new ScriptedItem(MapleDataTool.getInt("spec/npc", getItemData(itemId), 0),
|
||||
MapleDataTool.getString("spec/script", getItemData(itemId), ""),
|
||||
MapleDataTool.getInt("spec/runOnPickup", getItemData(itemId), 0) == 1);
|
||||
scriptedItemCache.put(itemId, script);
|
||||
@@ -2100,13 +2100,13 @@ public class MapleItemInformationProvider {
|
||||
return skillbook;
|
||||
}
|
||||
|
||||
public class scriptedItem {
|
||||
public class ScriptedItem {
|
||||
|
||||
private boolean runOnPickup;
|
||||
private int npc;
|
||||
private String script;
|
||||
|
||||
public scriptedItem(int npc, String script, boolean rop) {
|
||||
public ScriptedItem(int npc, String script, boolean rop) {
|
||||
this.npc = npc;
|
||||
this.script = script;
|
||||
this.runOnPickup = rop;
|
||||
|
||||
+88
-23
@@ -82,16 +82,16 @@ public class MapleTrade {
|
||||
partner.getChr().getClient().announce(MaplePacketCreator.getTradeConfirmation());
|
||||
}
|
||||
|
||||
private void complete1() {
|
||||
private void fetchExchangedItems() {
|
||||
exchangeItems = partner.getItems();
|
||||
exchangeMeso = partner.getMeso();
|
||||
}
|
||||
|
||||
private void complete2() {
|
||||
private void completeTrade() {
|
||||
boolean show = ServerConstants.USE_DEBUG;
|
||||
|
||||
items.clear();
|
||||
meso = 0;
|
||||
|
||||
for (Item item : exchangeItems) {
|
||||
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
|
||||
MapleInventoryManipulator.addFromDrop(chr.getClient(), item, show);
|
||||
@@ -164,12 +164,21 @@ public class MapleTrade {
|
||||
}
|
||||
}
|
||||
|
||||
public void addItem(Item item) {
|
||||
items.add(item);
|
||||
chr.getClient().announce(MaplePacketCreator.getTradeItemAdd((byte) 0, item));
|
||||
if (partner != null) {
|
||||
partner.getChr().getClient().announce(MaplePacketCreator.getTradeItemAdd((byte) 1, item));
|
||||
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) {
|
||||
@@ -214,38 +223,65 @@ public class MapleTrade {
|
||||
|
||||
return MapleInventory.checkSpotsAndOwnership(chr, tradeItems);
|
||||
}
|
||||
|
||||
|
||||
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 c) {
|
||||
c.getTrade().lockTrade();
|
||||
MapleTrade local = c.getTrade();
|
||||
MapleTrade partner = local.getPartner();
|
||||
if (partner.isLocked()) {
|
||||
local.complete1();
|
||||
partner.complete1();
|
||||
if (local.checkCompleteHandshake()) {
|
||||
local.fetchExchangedItems();
|
||||
partner.fetchExchangedItems();
|
||||
|
||||
if (!local.fitsMeso()) {
|
||||
cancelTrade(c);
|
||||
c.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()) {
|
||||
} else if (!partner.fitsMeso()) {
|
||||
cancelTrade(c);
|
||||
c.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()) {
|
||||
cancelTrade(c);
|
||||
c.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.");
|
||||
return;
|
||||
}
|
||||
else if (!partner.fitsInInventory()) {
|
||||
} else if (!partner.fitsInInventory()) {
|
||||
cancelTrade(c);
|
||||
c.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.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (local.getChr().getLevel() < 15) {
|
||||
if (local.getChr().getMesosTraded() + local.exchangeMeso > 1000000) {
|
||||
cancelTrade(c);
|
||||
@@ -263,16 +299,18 @@ public class MapleTrade {
|
||||
partner.getChr().addMesosTraded(partner.exchangeMeso);
|
||||
}
|
||||
}
|
||||
|
||||
LogHelper.logTrade(local, partner);
|
||||
local.complete2();
|
||||
partner.complete2();
|
||||
local.completeTrade();
|
||||
partner.completeTrade();
|
||||
|
||||
partner.getChr().setTrade(null);
|
||||
c.setTrade(null);
|
||||
}
|
||||
}
|
||||
|
||||
public static void cancelTrade(MapleCharacter c) {
|
||||
MapleTrade trade = c.getTrade();
|
||||
|
||||
private static void cancelTradeInternal(MapleCharacter chr) {
|
||||
MapleTrade trade = chr.getTrade();
|
||||
if(trade == null) return;
|
||||
|
||||
trade.cancel();
|
||||
@@ -280,9 +318,36 @@ public class MapleTrade {
|
||||
trade.getPartner().cancel();
|
||||
trade.getPartner().getChr().setTrade(null);
|
||||
}
|
||||
c.setTrade(null);
|
||||
chr.setTrade(null);
|
||||
}
|
||||
|
||||
private synchronized void tradeCancelHandshake(boolean updateSelf) {
|
||||
MapleTrade self;
|
||||
|
||||
if (updateSelf) {
|
||||
self = this;
|
||||
} else {
|
||||
self = this.getPartner();
|
||||
}
|
||||
|
||||
cancelTradeInternal(self.getChr());
|
||||
}
|
||||
|
||||
private void cancelHandshake() { // handshake checkout thanks to Ronan
|
||||
if (this.getChr().getId() < this.getPartner().getChr().getId()) {
|
||||
this.tradeCancelHandshake(true);
|
||||
} else {
|
||||
this.getPartner().tradeCancelHandshake(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void cancelTrade(MapleCharacter chr) {
|
||||
MapleTrade trade = chr.getTrade();
|
||||
if(trade == null) return;
|
||||
|
||||
trade.cancelHandshake();
|
||||
}
|
||||
|
||||
public static void startTrade(MapleCharacter c) {
|
||||
if (c.getTrade() == null) {
|
||||
c.setTrade(new MapleTrade((byte) 0, c));
|
||||
|
||||
@@ -63,7 +63,6 @@ import scripting.event.EventInstanceManager;
|
||||
import server.TimerManager;
|
||||
import server.life.MapleLifeFactory.BanishInfo;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MapleMapObject;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
@@ -72,6 +71,8 @@ import net.server.audit.LockCollector;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import net.server.coordinator.MapleMonsterAggroCoordinator;
|
||||
import server.MapleStatEffect;
|
||||
import server.maps.MapleSummon;
|
||||
|
||||
public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
private ChangeableStats ostats = null; //unused, v83 WZs offers no support for changeable stats.
|
||||
@@ -80,7 +81,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
private AtomicLong maxHpPlusHeal = new AtomicLong(1);
|
||||
private int mp;
|
||||
private WeakReference<MapleCharacter> controller = new WeakReference<>(null);
|
||||
private boolean controllerHasAggro, controllerKnowsAboutAggro;
|
||||
private boolean controllerHasAggro, controllerKnowsAboutAggro, controllerHasPuppet;
|
||||
private Collection<MonsterListener> listeners = new LinkedList<>();
|
||||
private EnumMap<MonsterStatus, MonsterStatusEffect> stati = new EnumMap<>(MonsterStatus.class);
|
||||
private ArrayList<MonsterStatus> alreadyBuffed = new ArrayList<>();
|
||||
@@ -91,10 +92,15 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
private List<Pair<Integer, Integer>> usedSkills = new ArrayList<>();
|
||||
private Map<Pair<Integer, Integer>, Integer> skillsUsed = new HashMap<>();
|
||||
private Set<Integer> usedAttacks = new HashSet<>();
|
||||
private Set<Integer> calledMobOids = null;
|
||||
private int calledMobCount = 0;
|
||||
private WeakReference<MapleMonster> callerMob = new WeakReference<>(null);
|
||||
private List<Integer> stolenItems = new ArrayList<>();
|
||||
private int team;
|
||||
private int parentMobOid = 0;
|
||||
private final HashMap<Integer, AtomicInteger> takenDamage = new HashMap<>();
|
||||
private Runnable removeAfterAction = null;
|
||||
private boolean availablePuppetUpdate = true;
|
||||
|
||||
private MonitoredReentrantLock externalLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB_EXT);
|
||||
private MonitoredReentrantLock monsterLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB, true);
|
||||
@@ -152,7 +158,59 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
public void setParentMobOid(int parentMobId) {
|
||||
this.parentMobOid = parentMobId;
|
||||
}
|
||||
|
||||
|
||||
public int countAvailableMobSummons(int limit, int skillLimit) { // limit prop for summons has another conotation, found thanks to MedicOP
|
||||
Set<Integer> calledOids = this.calledMobOids;
|
||||
if(calledOids != null) {
|
||||
limit -= calledOids.size();
|
||||
}
|
||||
|
||||
return Math.min(limit, skillLimit - this.calledMobCount);
|
||||
}
|
||||
|
||||
public void addSummonedMob(MapleMonster mob) {
|
||||
Set<Integer> calledOids = this.calledMobOids;
|
||||
if (calledOids == null) {
|
||||
calledOids = Collections.synchronizedSet(new HashSet<Integer>());
|
||||
this.calledMobOids = calledOids;
|
||||
}
|
||||
|
||||
calledOids.add(mob.getObjectId());
|
||||
mob.setSummonerMob(this);
|
||||
this.calledMobCount += 1;
|
||||
}
|
||||
|
||||
private void removeSummonedMob(int mobOid) {
|
||||
Set<Integer> calledOids = this.calledMobOids;
|
||||
if (calledOids != null) {
|
||||
calledOids.remove(mobOid);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSummonerMob(MapleMonster mob) {
|
||||
this.callerMob = new WeakReference<>(mob);
|
||||
}
|
||||
|
||||
private void dispatchClearSummons() {
|
||||
MapleMonster caller = this.callerMob.get();
|
||||
if (caller != null) {
|
||||
caller.removeSummonedMob(this.getObjectId());
|
||||
}
|
||||
|
||||
this.calledMobOids = null;
|
||||
}
|
||||
|
||||
public void pushRemoveAfterAction(Runnable run) {
|
||||
this.removeAfterAction = run;
|
||||
}
|
||||
|
||||
public Runnable popRemoveAfterAction() {
|
||||
Runnable r = this.removeAfterAction;
|
||||
this.removeAfterAction = null;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public int getHp() {
|
||||
return hp.get();
|
||||
}
|
||||
@@ -421,7 +479,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
int expSharersLevel = 0;
|
||||
for (MapleCharacter mc : members) {
|
||||
if (mc.getLevel() >= minThresholdLevel) { //NO EXP WILL BE GIVEN for those who are underleveled!
|
||||
if (Math.abs(killerLevel - mc.getLevel()) < ServerConstants.MIN_UNDERLEVEL_TO_EXP_LEECH) {
|
||||
if (Math.abs(killerLevel - mc.getLevel()) < ServerConstants.MIN_RANGELEVEL_TO_EXP_LEECH) {
|
||||
// thanks Thora for pointing out leech level limitation
|
||||
|
||||
expSharersLevel += mc.getLevel();
|
||||
@@ -710,6 +768,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
this.aggroClearDamages();
|
||||
this.dispatchClearSummons();
|
||||
|
||||
MonsterListener[] listenersList;
|
||||
statiLock.lock();
|
||||
@@ -809,6 +868,10 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
private void setControllerKnowsAboutAggro(boolean controllerKnowsAboutAggro) {
|
||||
if (!fake) this.controllerKnowsAboutAggro = controllerKnowsAboutAggro;
|
||||
}
|
||||
|
||||
private void setControllerHasPuppet(boolean controllerHasPuppet) {
|
||||
this.controllerHasPuppet = controllerHasPuppet;
|
||||
}
|
||||
|
||||
public byte[] makeBossHPBarPacket() {
|
||||
return MaplePacketCreator.showBossHP(getId(), getHp(), getMaxHp(), getTagColor(), getTagBgColor());
|
||||
@@ -1233,39 +1296,13 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
return false;
|
||||
}
|
||||
|
||||
int useLimit = toUse.getLimit();
|
||||
int useSkillid = toUse.getSkillId();
|
||||
if (useSkillid == 200) {
|
||||
int i = 0;
|
||||
for (MapleMapObject mo : getMap().getMapObjects()) {
|
||||
if (mo.getType() == MapleMapObjectType.MONSTER) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (i > 100) {
|
||||
return false;
|
||||
}
|
||||
if (map.isDojoMap()) { // spawns in dojo should be unlimited
|
||||
useLimit = 0;
|
||||
}
|
||||
} else if (useSkillid >= 143 && useSkillid <= 145) {
|
||||
if (useSkillid >= 143 && useSkillid <= 145) {
|
||||
if (this.isBuffed(MonsterStatus.WEAPON_REFLECT) || this.isBuffed(MonsterStatus.MAGIC_REFLECT)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (useLimit > 0) {
|
||||
monsterLock.lock();
|
||||
try {
|
||||
Integer times = this.skillsUsed.get(new Pair<>(useSkillid, toUse.getSkillLevel()));
|
||||
if (times != null && times >= useLimit) {
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
monsterLock.lock();
|
||||
try {
|
||||
/*
|
||||
@@ -1303,13 +1340,14 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
try {
|
||||
mp -= skill.getMpCon();
|
||||
|
||||
this.usedSkills.add(new Pair<>(skillId, level));
|
||||
if (this.skillsUsed.containsKey(new Pair<>(skillId, level))) {
|
||||
int times = this.skillsUsed.get(new Pair<>(skillId, level)) + 1;
|
||||
this.skillsUsed.remove(new Pair<>(skillId, level));
|
||||
this.skillsUsed.put(new Pair<>(skillId, level), times);
|
||||
Pair<Integer, Integer> skillKey = new Pair<>(skillId, level);
|
||||
this.usedSkills.add(skillKey);
|
||||
|
||||
Integer useCount = this.skillsUsed.remove(skillKey);
|
||||
if (useCount != null) {
|
||||
this.skillsUsed.put(skillKey, useCount + 1);
|
||||
} else {
|
||||
this.skillsUsed.put(new Pair<>(skillId, level), 1);
|
||||
this.skillsUsed.put(skillKey, 1);
|
||||
}
|
||||
} finally {
|
||||
monsterLock.unlock();
|
||||
@@ -1605,6 +1643,40 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
changeLevelByDifficulty(difficulty, pqMob);
|
||||
}
|
||||
|
||||
private boolean isPuppetInVicinity(MapleSummon summon) {
|
||||
return summon.getPosition().distanceSq(this.getPosition()) < 177777;
|
||||
}
|
||||
|
||||
public boolean isCharacterPuppetInVicinity(MapleCharacter chr) {
|
||||
MapleStatEffect mse = chr.getBuffEffect(MapleBuffStat.PUPPET);
|
||||
if (mse != null) {
|
||||
MapleSummon summon = chr.getSummonByKey(mse.getSourceId());
|
||||
|
||||
// check whether mob is currently under a puppet's field of action or not
|
||||
if (summon != null) {
|
||||
if (isPuppetInVicinity(summon)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
map.getAggroCoordinator().removePuppetAggro(chr.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isLeadingPuppetInVicinity() {
|
||||
MapleCharacter chrController = this.getActiveController();
|
||||
|
||||
if (chrController != null) {
|
||||
if (this.isCharacterPuppetInVicinity(chrController)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private MapleCharacter getNextControllerCandidate() {
|
||||
int mincontrolled = Integer.MAX_VALUE;
|
||||
MapleCharacter newController = null;
|
||||
@@ -1612,11 +1684,16 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
int mincontrolleddead = Integer.MAX_VALUE;
|
||||
MapleCharacter newControllerDead = null;
|
||||
|
||||
MapleCharacter newControllerWithPuppet = null;
|
||||
|
||||
for (MapleCharacter chr : getMap().getAllPlayers()) {
|
||||
if (!chr.isHidden()) {
|
||||
int ctrlMonsSize = chr.getNumControlledMonsters();
|
||||
|
||||
if (chr.isAlive()) {
|
||||
if (isCharacterPuppetInVicinity(chr)) {
|
||||
newControllerWithPuppet = chr;
|
||||
break;
|
||||
} else if (chr.isAlive()) {
|
||||
if (ctrlMonsSize < mincontrolled) {
|
||||
mincontrolled = ctrlMonsSize;
|
||||
newController = chr;
|
||||
@@ -1630,7 +1707,13 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
return newController != null ? newController : newControllerDead;
|
||||
if (newControllerWithPuppet != null) {
|
||||
return newControllerWithPuppet;
|
||||
} else if (newController != null) {
|
||||
return newController;
|
||||
} else {
|
||||
return newControllerDead;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1682,15 +1765,39 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
this.setController(newController);
|
||||
this.setControllerHasAggro(immediateAggro);
|
||||
this.setControllerKnowsAboutAggro(false);
|
||||
this.setControllerHasPuppet(false);
|
||||
} finally {
|
||||
aggroUpdateLock.unlock();
|
||||
}
|
||||
|
||||
this.aggroUpdatePuppetVisibility();
|
||||
newController.announce(MaplePacketCreator.controlMonster(this, false, immediateAggro));
|
||||
newController.controlMonster(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void aggroAddPuppet(MapleCharacter player) {
|
||||
MapleMonsterAggroCoordinator mmac = map.getAggroCoordinator();
|
||||
mmac.addPuppetAggro(player);
|
||||
|
||||
aggroUpdatePuppetController(player);
|
||||
|
||||
if (this.isControllerHasAggro()) {
|
||||
this.aggroUpdatePuppetVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
public void aggroRemovePuppet(MapleCharacter player) {
|
||||
MapleMonsterAggroCoordinator mmac = map.getAggroCoordinator();
|
||||
mmac.removePuppetAggro(player.getId());
|
||||
|
||||
aggroUpdatePuppetController(null);
|
||||
|
||||
if (this.isControllerHasAggro()) {
|
||||
this.aggroUpdatePuppetVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Automagically finds a new controller for the given monster from the chars
|
||||
* on the map it is from...
|
||||
@@ -1710,6 +1817,60 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
this.aggroSwitchController(newController, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a new controller for the given monster from the chars with deployed puppet
|
||||
* nearby on the map it is from...
|
||||
*
|
||||
*/
|
||||
private void aggroUpdatePuppetController(MapleCharacter newController) {
|
||||
MapleCharacter chrController = this.getActiveController();
|
||||
boolean updateController = false;
|
||||
|
||||
if (chrController != null && chrController.isAlive()) {
|
||||
if (isCharacterPuppetInVicinity(chrController)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
updateController = true;
|
||||
}
|
||||
|
||||
if (newController == null || !isCharacterPuppetInVicinity(newController)) {
|
||||
MapleMonsterAggroCoordinator mmac = map.getAggroCoordinator();
|
||||
|
||||
List<Integer> puppetOwners = mmac.getPuppetAggroList();
|
||||
List<Integer> toRemovePuppets = new LinkedList<>();
|
||||
|
||||
for (Integer cid : puppetOwners) {
|
||||
MapleCharacter chr = map.getCharacterById(cid);
|
||||
|
||||
if (chr != null) {
|
||||
if (isCharacterPuppetInVicinity(chr)) {
|
||||
newController = chr;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
toRemovePuppets.add(cid);
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer cid : toRemovePuppets) {
|
||||
mmac.removePuppetAggro(cid);
|
||||
}
|
||||
|
||||
if (newController == null) { // was a new controller found? (if not there's no puppet nearby)
|
||||
if (updateController) {
|
||||
aggroUpdateController();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
} else if (chrController == newController) {
|
||||
this.aggroUpdatePuppetVisibility();
|
||||
}
|
||||
|
||||
this.aggroSwitchController(newController, this.isControllerHasAggro());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures controllability removal of the current player controller, and
|
||||
* fetches for any player on the map to start controlling in place.
|
||||
@@ -1767,6 +1928,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
if (chrController != attacker) {
|
||||
if (this.getMapAggroCoordinator().isLeadingCharacterAggro(this, attacker)) {
|
||||
this.aggroSwitchController(attacker, true);
|
||||
} else {
|
||||
this.setControllerHasAggro(true);
|
||||
this.aggroUpdatePuppetVisibility();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1780,9 +1944,69 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
*/
|
||||
} else {
|
||||
this.setControllerHasAggro(true);
|
||||
this.aggroUpdatePuppetVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
private void aggroRefreshPuppetVisibility(MapleCharacter chrController, MapleSummon puppet) {
|
||||
// lame patch for client to redirect all aggro to the puppet
|
||||
|
||||
List<MapleMonster> puppetControlled = new LinkedList<>();
|
||||
for (MapleMonster mob : chrController.getControlledMonsters()) {
|
||||
if (mob.isPuppetInVicinity(puppet)) {
|
||||
puppetControlled.add(mob);
|
||||
}
|
||||
}
|
||||
|
||||
for (MapleMonster mob : puppetControlled) {
|
||||
chrController.announce(MaplePacketCreator.stopControllingMonster(mob.getObjectId()));
|
||||
}
|
||||
chrController.announce(MaplePacketCreator.removeSummon(puppet, false));
|
||||
|
||||
for (MapleMonster mob : puppetControlled) {
|
||||
chrController.announce(MaplePacketCreator.controlMonster(mob, false, mob.isControllerHasAggro()));
|
||||
}
|
||||
chrController.announce(MaplePacketCreator.spawnSummon(puppet, false));
|
||||
}
|
||||
|
||||
public void aggroUpdatePuppetVisibility() {
|
||||
if (!availablePuppetUpdate) return;
|
||||
|
||||
availablePuppetUpdate = false;
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
MapleCharacter chrController = MapleMonster.this.getActiveController();
|
||||
if (chrController == null) return;
|
||||
|
||||
MapleStatEffect puppetEffect = chrController.getBuffEffect(MapleBuffStat.PUPPET);
|
||||
if (puppetEffect != null) {
|
||||
MapleSummon puppet = chrController.getSummonByKey(puppetEffect.getSourceId());
|
||||
|
||||
if (puppet != null && isPuppetInVicinity(puppet)) {
|
||||
controllerHasPuppet = true;
|
||||
aggroRefreshPuppetVisibility(chrController, puppet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (controllerHasPuppet) {
|
||||
controllerHasPuppet = false;
|
||||
|
||||
chrController.announce(MaplePacketCreator.stopControllingMonster(MapleMonster.this.getObjectId()));
|
||||
chrController.announce(MaplePacketCreator.controlMonster(MapleMonster.this, false, MapleMonster.this.isControllerHasAggro()));
|
||||
}
|
||||
} finally {
|
||||
availablePuppetUpdate = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// had to schedule this since mob wouldn't stick to puppet aggro who knows why
|
||||
this.getMap().getChannelServer().registerOverallAction(this.getMap().getId(), r, ServerConstants.UPDATE_INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all applied damage input for this mob, doesn't refresh target aggro.
|
||||
*
|
||||
@@ -1805,7 +2029,12 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
public final void disposeLocks() {
|
||||
public void dispose() {
|
||||
this.getMap().dismissRemoveAfter(this);
|
||||
disposeLocks();
|
||||
}
|
||||
|
||||
private void disposeLocks() {
|
||||
LockCollector.getInstance().registerDisposeAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -33,6 +33,7 @@ import constants.GameConstants;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import tools.Randomizer;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MapleMapObject;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import server.maps.MapleMist;
|
||||
@@ -235,57 +236,72 @@ public class MobSkill {
|
||||
case 156: // speed up
|
||||
break;
|
||||
case 200: // summon
|
||||
if (monster.getMap().getSpawnedMonstersOnMap() < 80) {
|
||||
for (Integer mobId : getSummons()) {
|
||||
MapleMonster toSpawn = MapleLifeFactory.getMonster(mobId);
|
||||
if(toSpawn != null) {
|
||||
if(GameConstants.isBossRush(monster.getMap().getId())) 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 {
|
||||
int skillLimit = this.getLimit();
|
||||
MapleMap map = monster.getMap();
|
||||
|
||||
if (map.isDojoMap()) { // 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) {
|
||||
Collections.shuffle(summons);
|
||||
boolean bossRushMap = GameConstants.isBossRush(map.getId());
|
||||
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (monster.getMap().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) {
|
||||
monster.getMap().spawnFakeMonster(toSpawn);
|
||||
} else {
|
||||
monster.getMap().spawnMonsterWithEffect(toSpawn, getSpawnEffect(), toSpawn.getPosition());
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -342,7 +358,7 @@ public class MobSkill {
|
||||
}
|
||||
|
||||
public List<Integer> getSummons() {
|
||||
return Collections.unmodifiableList(toSummon);
|
||||
return new ArrayList<>(toSummon);
|
||||
}
|
||||
|
||||
public int getSpawnEffect() {
|
||||
|
||||
@@ -44,12 +44,19 @@ public class MapMonitor {
|
||||
}
|
||||
|
||||
private void cancelAction() {
|
||||
monitorSchedule.cancel(false);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,6 +227,18 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
|
||||
return MapleInventoryManipulator.checkSpace(c, newItem.getItemId(), newItem.getQuantity(), newItem.getOwner()) && MapleInventoryManipulator.addFromDrop(c, newItem, false);
|
||||
}
|
||||
|
||||
private int getQuantityLeft(int itemid) {
|
||||
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);
|
||||
@@ -247,7 +259,6 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
|
||||
if (c.getPlayer().getMeso() >= price) {
|
||||
if (canBuy(c, newItem)) {
|
||||
c.getPlayer().gainMeso(-price, false);
|
||||
if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) announceItemSold(newItem, price); // idea thanks to Vcoc
|
||||
|
||||
synchronized (sold) {
|
||||
sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), newItem.getQuantity(), price));
|
||||
@@ -257,6 +268,11 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
|
||||
if (pItem.getBundles() < 1) {
|
||||
pItem.setDoesExist(false);
|
||||
}
|
||||
|
||||
if(ServerConstants.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);
|
||||
@@ -288,12 +304,12 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
|
||||
}
|
||||
}
|
||||
|
||||
private void announceItemSold(Item item, int mesos) {
|
||||
String qtyStr = (item.getQuantity() > 1) ? " (qty. " + item.getQuantity() + ")" : "";
|
||||
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.");
|
||||
player.dropMessage(6, "[Hired Merchant] Item '" + MapleItemInformationProvider.getInstance().getName(item.getItemId()) + "'" + qtyStr + " has been sold for " + mesos + " mesos. (" + inStore + " left)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1796,21 +1796,36 @@ public class MapleMap {
|
||||
private void applyRemoveAfter(final MapleMonster monster) {
|
||||
final selfDestruction selfDestruction = monster.getStats().selfDestruction();
|
||||
if (monster.getStats().removeAfter() > 0 || selfDestruction != null && selfDestruction.getHp() < 0) {
|
||||
Runnable removeAfterAction;
|
||||
|
||||
if (selfDestruction == null) {
|
||||
registerMapSchedule(new Runnable() {
|
||||
removeAfterAction = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
killMonster(monster, null, false);
|
||||
}
|
||||
}, monster.getStats().removeAfter() * 1000);
|
||||
};
|
||||
|
||||
registerMapSchedule(removeAfterAction, monster.getStats().removeAfter() * 1000);
|
||||
} else {
|
||||
registerMapSchedule(new Runnable() {
|
||||
removeAfterAction = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
killMonster(monster, null, false, selfDestruction.getAction());
|
||||
}
|
||||
}, selfDestruction.removeAfter() * 1000);
|
||||
};
|
||||
|
||||
registerMapSchedule(removeAfterAction, selfDestruction.removeAfter() * 1000);
|
||||
}
|
||||
|
||||
monster.pushRemoveAfterAction(removeAfterAction);
|
||||
}
|
||||
}
|
||||
|
||||
public void dismissRemoveAfter(final MapleMonster monster) {
|
||||
Runnable removeAfterAction = monster.popRemoveAfterAction();
|
||||
if (removeAfterAction != null) {
|
||||
this.getChannelServer().forceRunOverallAction(mapid, removeAfterAction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2583,6 +2598,18 @@ public class MapleMap {
|
||||
return Collections.unmodifiableCollection(portals.values());
|
||||
}
|
||||
*/
|
||||
|
||||
public void addPlayerPuppet(MapleCharacter player) {
|
||||
for (MapleMonster mm : this.getMonsters()) {
|
||||
mm.aggroAddPuppet(player);
|
||||
}
|
||||
}
|
||||
|
||||
public void removePlayerPuppet(MapleCharacter player) {
|
||||
for (MapleMonster mm : this.getMonsters()) {
|
||||
mm.aggroRemovePuppet(player);
|
||||
}
|
||||
}
|
||||
|
||||
public void removePlayer(MapleCharacter chr) {
|
||||
Channel cserv = chr.getClient().getChannelServer();
|
||||
@@ -3699,8 +3726,11 @@ public class MapleMap {
|
||||
if (mapid >= 922240100 && mapid <= 922240119) {
|
||||
toggleHiddenNPC(9001108);
|
||||
}
|
||||
mapMonitor.cancel(true);
|
||||
mapMonitor = null;
|
||||
|
||||
if (mapMonitor != null) {
|
||||
mapMonitor.cancel(true);
|
||||
mapMonitor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
@@ -3993,7 +4023,7 @@ public class MapleMap {
|
||||
|
||||
public void dispose() {
|
||||
for(MapleMonster mm : this.getMonsters()) {
|
||||
mm.disposeLocks();
|
||||
mm.dispose();
|
||||
}
|
||||
|
||||
clearMapObjects();
|
||||
|
||||
@@ -536,6 +536,9 @@ public class MapleQuest {
|
||||
case PETTAMENESS:
|
||||
ret = new PetTamenessAction(this, data);
|
||||
break;
|
||||
case PETSPEED:
|
||||
ret = new PetSpeedAction(this, data);
|
||||
break;
|
||||
default:
|
||||
//FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Unhandled Action Type: " + type.toString() + " QuestID: " + this.getId());
|
||||
break;
|
||||
|
||||
@@ -26,7 +26,7 @@ package server.quest;
|
||||
* @author Matze
|
||||
*/
|
||||
public enum MapleQuestActionType {
|
||||
UNDEFINED(-1), EXP(0), ITEM(1), NEXTQUEST(2), MESO(3), QUEST(4), SKILL(5), FAME(6), BUFF(7), PETSKILL(8), YES(9), NO(10), NPC(11), MIN_LEVEL(12), NORMAL_AUTO_START(13), PETTAMENESS(14), ZERO(15);
|
||||
UNDEFINED(-1), EXP(0), ITEM(1), NEXTQUEST(2), MESO(3), QUEST(4), SKILL(5), FAME(6), BUFF(7), PETSKILL(8), YES(9), NO(10), NPC(11), MIN_LEVEL(12), NORMAL_AUTO_START(13), PETTAMENESS(14), PETSPEED(15), ZERO(16);
|
||||
final byte type;
|
||||
|
||||
private MapleQuestActionType(int type) {
|
||||
@@ -62,6 +62,8 @@ public enum MapleQuestActionType {
|
||||
return NORMAL_AUTO_START;
|
||||
} else if (name.equals("pettameness")) {
|
||||
return PETTAMENESS;
|
||||
} else if (name.equals("petspeed")) {
|
||||
return PETSPEED;
|
||||
} else if (name.equals("0")) {
|
||||
return ZERO;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2018 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package server.quest.actions;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import client.inventory.MaplePet;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataTool;
|
||||
import server.quest.MapleQuest;
|
||||
import server.quest.MapleQuestActionType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class PetSpeedAction extends MapleQuestAction {
|
||||
|
||||
public PetSpeedAction(MapleQuest quest, MapleData data) {
|
||||
super(MapleQuestActionType.PETTAMENESS, quest);
|
||||
questID = quest.getId();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void processData(MapleData data) {}
|
||||
|
||||
@Override
|
||||
public void run(MapleCharacter chr, Integer extSelection) {
|
||||
MapleClient c = chr.getClient();
|
||||
|
||||
MaplePet pet = chr.getPet(0); // assuming here only the pet leader will gain owner speed
|
||||
if(pet == null) return;
|
||||
|
||||
c.lockClient();
|
||||
try {
|
||||
pet.addPetFlag(c.getPlayer(), MaplePet.PetFlag.OWNER_SPEED);
|
||||
} finally {
|
||||
c.unlockClient();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ public class FilePrinter {
|
||||
PACKET_LOG = "game/Log.txt",
|
||||
CASHITEM_BOUGHT = "interactions/CashLog.txt",
|
||||
EXCEPTION = "game/Exceptions.txt",
|
||||
TRADE_EXCEPTION = "game/TradeExceptions.txt",
|
||||
SQL_EXCEPTION = "game/SqlExceptions.txt",
|
||||
PACKET_HANDLER = "game/packethandler/",
|
||||
PORTAL = "game/portals/",
|
||||
|
||||
@@ -406,7 +406,7 @@ public class MaplePacketCreator {
|
||||
mplew.writeShort(pet.getCloseness());
|
||||
mplew.write(pet.getFullness());
|
||||
addExpirationTime(mplew, item.getExpiration());
|
||||
mplew.writeInt(0);
|
||||
mplew.writeInt(pet.getPetFlag()); /* pet flags found by -- Irenex & Spoon */
|
||||
mplew.write(new byte[]{(byte) 0x50, (byte) 0x46}); //wonder what this is
|
||||
mplew.writeInt(0);
|
||||
return;
|
||||
@@ -1065,7 +1065,7 @@ public class MaplePacketCreator {
|
||||
mplew.writeShort(chr.getHp());
|
||||
mplew.writeBool(false);
|
||||
mplew.writeLong(getTime(Server.getInstance().getCurrentTime()));
|
||||
mplew.skip(10);
|
||||
mplew.skip(14);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
@@ -1082,7 +1082,7 @@ public class MaplePacketCreator {
|
||||
mplew.writeInt(spawnPosition.x); // spawn position placement thanks to Arnah (Vertisy)
|
||||
mplew.writeInt(spawnPosition.y);
|
||||
mplew.writeLong(getTime(Server.getInstance().getCurrentTime()));
|
||||
mplew.skip(10);
|
||||
mplew.skip(14);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
@@ -1158,7 +1158,8 @@ public class MaplePacketCreator {
|
||||
mplew.write(0x0A); //v83
|
||||
mplew.write(summon.getSkillLevel());
|
||||
mplew.writePos(summon.getPosition());
|
||||
mplew.skip(3);
|
||||
mplew.write(summon.getStance()); //bMoveAction & foothold, found thanks to Rien dev team
|
||||
mplew.writeShort(0);
|
||||
mplew.write(summon.getMovementType().getValue()); // 0 = don't move, 1 = follow (4th mage summons?), 2/4 = only tele follow, 3 = bird follow
|
||||
mplew.write(summon.isPuppet() ? 0 : 1); // 0 and the summon can't attack - but puppets don't attack with 1 either ^.-
|
||||
mplew.write(animated ? 0 : 1);
|
||||
@@ -1828,8 +1829,7 @@ public class MaplePacketCreator {
|
||||
mplew.writeInt(!drop.isFFADrop() ? (recvrInParty ? drop.getPartyOwnerId() : drop.getOwnerId()) : 0); // owner charid/partyid :)
|
||||
mplew.write(drop.getDropType()); // 0 = timeout for non-owner, 1 = timeout for non-owner's party, 2 = FFA, 3 = explosive/FFA
|
||||
mplew.writePos(dropto);
|
||||
// its not charId, but dropper's oid, this error will occur only if a monster's oid in map equals charId, (the item will drop from the error monster)
|
||||
mplew.writeInt(drop.getDropper().getObjectId());
|
||||
mplew.writeInt(drop.getDropper().getObjectId()); // dropper oid, found thanks to Li Jixue
|
||||
|
||||
if (mod != 2) {
|
||||
mplew.writePos(dropfrom);
|
||||
@@ -2736,9 +2736,9 @@ public class MaplePacketCreator {
|
||||
}
|
||||
}
|
||||
mplew.writeMapleAsciiString(guildName);
|
||||
mplew.writeMapleAsciiString(allianceName); // does not seems to work
|
||||
mplew.writeMapleAsciiString(allianceName); // does not seem to work
|
||||
mplew.write(0); // pMedalInfo, thanks to Arnah (Vertisy)
|
||||
|
||||
mplew.write(0);
|
||||
MaplePet[] pets = chr.getPets();
|
||||
Item inv = chr.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -114);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
@@ -3778,11 +3778,16 @@ public class MaplePacketCreator {
|
||||
}
|
||||
|
||||
/**
|
||||
* 10: A beginner can't create a party. 1/11/14/19: Your request for a party
|
||||
* didn't work due to an unexpected error. 13: You have yet to join a party.
|
||||
* 10: A beginner can't create a party. 1/5/6/11/14/19: Your request for a
|
||||
* party didn't work due to an unexpected error. 12: Quit as leader of the
|
||||
* party. 13: You have yet to join a party.
|
||||
* 16: Already have joined a party. 17: The party you're trying to join is
|
||||
* already in full capacity. 19: Unable to find the requested character in
|
||||
* this channel.
|
||||
* this channel. 21: Player is blocking any party invitations. 22: Player
|
||||
* is taking care of another invitation. 23: Player denied request.
|
||||
* 25: Cannot kick another user in this map. 28/29: Leadership can only be
|
||||
* given to a party member in the vicinity. 30: Change leadership only on
|
||||
* same channel.
|
||||
*
|
||||
* @param message
|
||||
* @return
|
||||
@@ -4065,7 +4070,7 @@ public class MaplePacketCreator {
|
||||
mplew.writeInt(cid);
|
||||
mplew.writeInt(oid);
|
||||
mplew.write(12);
|
||||
mplew.writeInt(damage); // damage display doesn't seems to work...
|
||||
mplew.writeInt(damage); // damage display doesn't seem to work...
|
||||
mplew.writeInt(monsterIdFrom);
|
||||
mplew.write(0);
|
||||
return mplew.getPacket();
|
||||
@@ -6746,7 +6751,7 @@ public class MaplePacketCreator {
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] updateAllianceInfo(MapleAlliance alliance, MapleClient c) {
|
||||
public static byte[] updateAllianceInfo(MapleAlliance alliance, int world) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.ALLIANCE_OPERATION.getValue());
|
||||
mplew.write(0x0F);
|
||||
@@ -6761,8 +6766,8 @@ public class MaplePacketCreator {
|
||||
}
|
||||
mplew.writeInt(alliance.getCapacity()); // probably capacity
|
||||
mplew.writeShort(0);
|
||||
for (Integer guildd : alliance.getGuilds()) {
|
||||
getGuildInfo(mplew, Server.getInstance().getGuild(guildd, c.getWorld()));
|
||||
for (Integer guildid : alliance.getGuilds()) {
|
||||
getGuildInfo(mplew, Server.getInstance().getGuild(guildid, world));
|
||||
}
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user