10264 lines
352 KiB
Java
10264 lines
352 KiB
Java
package client;
|
|
|
|
import java.awt.Point;
|
|
import java.lang.ref.WeakReference;
|
|
import java.sql.Connection;
|
|
import java.sql.PreparedStatement;
|
|
import java.sql.ResultSet;
|
|
import java.sql.SQLException;
|
|
import java.sql.Statement;
|
|
import java.sql.Timestamp;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Calendar;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.EnumMap;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.LinkedHashSet;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.Set;
|
|
import java.util.Comparator;
|
|
import java.util.concurrent.locks.Lock;
|
|
import java.util.concurrent.ScheduledFuture;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.regex.Pattern;
|
|
|
|
import net.server.PlayerBuffValueHolder;
|
|
import net.server.PlayerCoolDownValueHolder;
|
|
import net.server.Server;
|
|
import net.server.guild.MapleAlliance;
|
|
import net.server.guild.MapleGuild;
|
|
import net.server.guild.MapleGuildCharacter;
|
|
import net.server.world.MapleMessenger;
|
|
import net.server.world.MapleMessengerCharacter;
|
|
import net.server.world.MapleParty;
|
|
import net.server.world.MaplePartyCharacter;
|
|
import net.server.world.PartyOperation;
|
|
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;
|
|
import server.MapleStorage;
|
|
import server.MapleTrade;
|
|
import server.TimerManager;
|
|
import server.ThreadManager;
|
|
import server.events.MapleEvents;
|
|
import server.events.RescueGaga;
|
|
import server.events.gm.MapleFitness;
|
|
import server.events.gm.MapleOla;
|
|
import server.life.MapleMonster;
|
|
import server.life.MobSkill;
|
|
import server.loot.MapleLootManager;
|
|
import server.maps.MapleHiredMerchant;
|
|
import server.maps.MapleDoor;
|
|
import server.maps.MapleDragon;
|
|
import server.maps.MapleMap;
|
|
import server.maps.MapleMapEffect;
|
|
import server.maps.MapleMapFactory;
|
|
import server.maps.MapleMapObject;
|
|
import server.maps.MapleMapObjectType;
|
|
import server.maps.MapleMiniGame;
|
|
import server.maps.MapleMiniGame.MiniGameResult;
|
|
import server.maps.MaplePlayerShop;
|
|
import server.maps.MaplePlayerShopItem;
|
|
import server.maps.MapleSummon;
|
|
import server.life.MaplePlayerNPC;
|
|
import server.life.MonsterDropEntry;
|
|
import server.maps.SavedLocation;
|
|
import server.maps.SavedLocationType;
|
|
import server.partyquest.PartyQuest;
|
|
import server.quest.MapleQuest;
|
|
import tools.DatabaseConnection;
|
|
import tools.FilePrinter;
|
|
import tools.MaplePacketCreator;
|
|
import tools.Pair;
|
|
import tools.Randomizer;
|
|
import tools.exceptions.NotEnabledException;
|
|
import tools.packets.Wedding;
|
|
import client.autoban.AutobanManager;
|
|
import client.creator.CharacterFactoryRecipe;
|
|
import client.inventory.Equip;
|
|
import client.inventory.Equip.StatUpgrade;
|
|
import client.inventory.Item;
|
|
import client.inventory.ItemFactory;
|
|
import client.inventory.MapleInventory;
|
|
import client.inventory.MapleInventoryProof;
|
|
import client.inventory.MapleInventoryType;
|
|
import client.inventory.MaplePet;
|
|
import client.inventory.MapleWeaponType;
|
|
import client.inventory.ModifyInventory;
|
|
import client.inventory.PetDataFactory;
|
|
import client.inventory.manipulator.MapleCashidGenerator;
|
|
import client.inventory.manipulator.MapleInventoryManipulator;
|
|
import client.newyear.NewYearCardRecord;
|
|
import constants.ExpTable;
|
|
import constants.GameConstants;
|
|
import constants.ItemConstants;
|
|
import constants.ServerConstants;
|
|
import constants.skills.Aran;
|
|
import constants.skills.Beginner;
|
|
import constants.skills.Bishop;
|
|
import constants.skills.BlazeWizard;
|
|
import constants.skills.Bowmaster;
|
|
import constants.skills.Buccaneer;
|
|
import constants.skills.Corsair;
|
|
import constants.skills.Crusader;
|
|
import constants.skills.DarkKnight;
|
|
import constants.skills.DawnWarrior;
|
|
import constants.skills.Evan;
|
|
import constants.skills.FPArchMage;
|
|
import constants.skills.Hermit;
|
|
import constants.skills.Hero;
|
|
import constants.skills.ILArchMage;
|
|
import constants.skills.Legend;
|
|
import constants.skills.Magician;
|
|
import constants.skills.Marauder;
|
|
import constants.skills.Marksman;
|
|
import constants.skills.NightLord;
|
|
import constants.skills.Noblesse;
|
|
import constants.skills.Paladin;
|
|
import constants.skills.Priest;
|
|
import constants.skills.Ranger;
|
|
import constants.skills.Shadower;
|
|
import constants.skills.Sniper;
|
|
import constants.skills.Swordsman;
|
|
import constants.skills.ThunderBreaker;
|
|
import net.server.channel.handlers.PartyOperationHandler;
|
|
import scripting.item.ItemScriptManager;
|
|
import server.life.MobSkillFactory;
|
|
import server.maps.MapleMapItem;
|
|
import net.server.audit.locks.MonitoredLockType;
|
|
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
|
import server.partyquest.MonsterCarnival;
|
|
|
|
public class MapleCharacter extends AbstractMapleCharacterObject {
|
|
|
|
private static final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
|
private static final String LEVEL_200 = "[Congrats] %s has reached Level %d! Congratulate %s on such an amazing achievement!";
|
|
private static final String[] BLOCKED_NAMES = {"admin", "owner", "moderator", "intern", "donor", "administrator", "help", "helper", "alert", "notice", "maplestory", "fuck", "wizet", "fucking", "negro", "fuk", "fuc", "penis", "pussy", "asshole", "gay",
|
|
"nigger", "homo", "suck", "cum", "shit", "shitty", "condom", "security", "official", "rape", "nigga", "sex", "tit", "boner", "orgy", "clit", "asshole", "fatass", "bitch", "support", "gamemaster", "cock", "gaay", "gm",
|
|
"operate", "master", "sysop", "party", "GameMaster", "community", "message", "event", "test", "meso", "Scania", "yata", "AsiaSoft", "henesys"};
|
|
|
|
private int world;
|
|
private int accountid, id, level;
|
|
private int rank, rankMove, jobRank, jobRankMove;
|
|
private int gender, hair, face;
|
|
private int fame, quest_fame;
|
|
private int initialSpawnPoint;
|
|
private int mapid;
|
|
private int currentPage, currentType = 0, currentTab = 1;
|
|
private int itemEffect;
|
|
private int guildid, guildRank, allianceRank;
|
|
private int messengerposition = 4;
|
|
private int slots = 0;
|
|
private int energybar;
|
|
private int gmLevel;
|
|
private int ci = 0;
|
|
private MapleFamily family;
|
|
private int familyId;
|
|
private int bookCover;
|
|
private int battleshipHp = 0;
|
|
private int mesosTraded = 0;
|
|
private int possibleReports = 10;
|
|
private int dojoPoints, vanquisherStage, dojoStage, dojoEnergy, vanquisherKills;
|
|
private int expRate = 1, mesoRate = 1, dropRate = 1, expCoupon = 1, mesoCoupon = 1, dropCoupon = 1;
|
|
private int omokwins, omokties, omoklosses, matchcardwins, matchcardties, matchcardlosses;
|
|
private int owlSearch;
|
|
private long lastfametime, lastUsedCashItem, lastExpression = 0, lastHealed, lastBuyback = 0, lastDeathtime, lastMesoDrop = -1, jailExpiration = -1;
|
|
private transient int localstr, localdex, localluk, localint_, localmagic, localwatk;
|
|
private transient int equipmaxhp, equipmaxmp, equipstr, equipdex, equipluk, equipint_, equipmagic, equipwatk, localchairhp, localchairmp;
|
|
private int localchairrate;
|
|
private boolean hidden, equipchanged = true, canDoor = true, berserk, hasMerchant, hasSandboxItem = false, whiteChat = false;
|
|
private boolean equippedMesoMagnet = false, equippedItemPouch = false, equippedPetItemIgnore = false;
|
|
private int linkedLevel = 0;
|
|
private String linkedName = null;
|
|
private boolean finishedDojoTutorial;
|
|
private boolean usedStorage = false;
|
|
private String name;
|
|
private String chalktext;
|
|
private String dataString;
|
|
private String search = null;
|
|
private AtomicBoolean mapTransitioning = new AtomicBoolean(true); // player client is currently trying to change maps or log in the game map
|
|
private AtomicBoolean awayFromWorld = new AtomicBoolean(true); // player is online, but on cash shop or mts
|
|
private AtomicInteger exp = new AtomicInteger();
|
|
private AtomicInteger gachaexp = new AtomicInteger();
|
|
private AtomicInteger meso = new AtomicInteger();
|
|
private AtomicInteger chair = new AtomicInteger();
|
|
private int merchantmeso;
|
|
private BuddyList buddylist;
|
|
private EventInstanceManager eventInstance = null;
|
|
private MapleHiredMerchant hiredMerchant = null;
|
|
private MapleClient client;
|
|
private MapleGuildCharacter mgc = null;
|
|
private MaplePartyCharacter mpc = null;
|
|
private MapleInventory[] inventory;
|
|
private MapleJob job = MapleJob.BEGINNER;
|
|
private MapleMap map;
|
|
private MapleMessenger messenger = null;
|
|
private MapleMiniGame miniGame;
|
|
private MapleMount maplemount;
|
|
private MapleParty party;
|
|
private MaplePet[] pets = new MaplePet[3];
|
|
private MaplePlayerShop playerShop = null;
|
|
private MapleShop shop = null;
|
|
private MapleSkinColor skinColor = MapleSkinColor.NORMAL;
|
|
private MapleStorage storage = null;
|
|
private MapleTrade trade = null;
|
|
private MonsterBook monsterbook;
|
|
private CashShop cashshop;
|
|
private Set<NewYearCardRecord> newyears = new LinkedHashSet<>();
|
|
private SavedLocation savedLocations[];
|
|
private SkillMacro[] skillMacros = new SkillMacro[5];
|
|
private List<Integer> lastmonthfameids;
|
|
private List<WeakReference<MapleMap>> lastVisitedMaps = new LinkedList<>();
|
|
private final Map<Short, MapleQuestStatus> quests;
|
|
private Set<MapleMonster> controlled = new LinkedHashSet<>();
|
|
private Map<Integer, String> entered = new LinkedHashMap<>();
|
|
private Set<MapleMapObject> visibleMapObjects = new LinkedHashSet<>();
|
|
private Map<Skill, SkillEntry> skills = new LinkedHashMap<>();
|
|
private Map<Integer, Integer> activeCoupons = new LinkedHashMap<>();
|
|
private Map<Integer, Integer> activeCouponRates = new LinkedHashMap<>();
|
|
private EnumMap<MapleBuffStat, MapleBuffStatValueHolder> effects = new EnumMap<>(MapleBuffStat.class);
|
|
private Map<MapleBuffStat, Byte> buffEffectsCount = new LinkedHashMap<>();
|
|
private Map<MapleDisease, Long> diseaseExpires = new LinkedHashMap<>();
|
|
private Map<Integer, Map<MapleBuffStat, MapleBuffStatValueHolder>> buffEffects = new LinkedHashMap<>(); // non-overriding buffs thanks to Ronan
|
|
private Map<Integer, Long> buffExpires = new LinkedHashMap<>();
|
|
private Map<Integer, MapleKeyBinding> keymap = new LinkedHashMap<>();
|
|
private Map<Integer, MapleSummon> summons = new LinkedHashMap<>();
|
|
private Map<Integer, MapleCoolDownValueHolder> coolDowns = new LinkedHashMap<>();
|
|
private EnumMap<MapleDisease, Pair<MapleDiseaseValueHolder, MobSkill>> diseases = new EnumMap<>(MapleDisease.class);
|
|
private MapleDoor pdoor = null;
|
|
private Map<MapleQuest, Long> questExpirations = new LinkedHashMap<>();
|
|
private ScheduledFuture<?> dragonBloodSchedule;
|
|
private ScheduledFuture<?> hpDecreaseTask;
|
|
private ScheduledFuture<?> beholderHealingSchedule, beholderBuffSchedule, berserkSchedule;
|
|
private ScheduledFuture<?> skillCooldownTask = null;
|
|
private ScheduledFuture<?> buffExpireTask = null;
|
|
private ScheduledFuture<?> itemExpireTask = null;
|
|
private ScheduledFuture<?> diseaseExpireTask = null;
|
|
private ScheduledFuture<?> questExpireTask = null;
|
|
private ScheduledFuture<?> recoveryTask = null;
|
|
private ScheduledFuture<?> extraRecoveryTask = null;
|
|
private ScheduledFuture<?> chairRecoveryTask = null;
|
|
private ScheduledFuture<?> pendantOfSpirit = null; //1122017
|
|
public ScheduledFuture<?> timer;
|
|
private Lock chrLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_CHR, true);
|
|
private Lock evtLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_EVT, true);
|
|
private Lock petLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_PET, true);
|
|
private Lock prtLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_PRT);
|
|
private Lock cpnLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_CPN);
|
|
private Map<Integer, Set<Integer>> excluded = new LinkedHashMap<>();
|
|
private Set<Integer> excludedItems = new LinkedHashSet<>();
|
|
private static String[] ariantroomleader = new String[3];
|
|
private static int[] ariantroomslot = new int[3];
|
|
private long portaldelay = 0, lastcombo = 0;
|
|
private short combocounter = 0;
|
|
private List<String> blockedPortals = new ArrayList<>();
|
|
private Map<Short, String> area_info = new LinkedHashMap<>();
|
|
private AutobanManager autoban;
|
|
private boolean isbanned = false;
|
|
private boolean blockCashShop = false;
|
|
private boolean allowExpGain = true;
|
|
private byte pendantExp = 0, lastmobcount = 0, doorSlot = -1;
|
|
private List<Integer> trockmaps = new ArrayList<>();
|
|
private List<Integer> viptrockmaps = new ArrayList<>();
|
|
private Map<String, MapleEvents> events = new LinkedHashMap<>();
|
|
private PartyQuest partyQuest = null;
|
|
private MapleDragon dragon = null;
|
|
private MapleRing marriageRing;
|
|
private int marriageItemid = -1;
|
|
private int partnerId = -1;
|
|
private List<MapleRing> crushRings = new ArrayList<>();
|
|
private List<MapleRing> friendshipRings = new ArrayList<>();
|
|
private boolean loggedIn = false;
|
|
private boolean useCS; //chaos scroll upon crafting item.
|
|
private long npcCd;
|
|
private long petLootCd;
|
|
private long lastHpDec = 0;
|
|
private int newWarpMap = -1;
|
|
private boolean canWarpMap = true; //only one "warp" must be used per call, and this will define the right one.
|
|
private int canWarpCounter = 0; //counts how many times "inner warps" have been called.
|
|
private byte extraHpRec = 0, extraMpRec = 0;
|
|
private short extraRecInterval;
|
|
private int targetHpBarHash = 0;
|
|
private long targetHpBarTime = 0;
|
|
private long nextUnderlevelTime = 0;
|
|
private int banishMap = -1;
|
|
private int banishSp = -1;
|
|
private long banishTime = 0;
|
|
private long lastExpGainTime;
|
|
public ScheduledFuture<?> pqMapleMap;
|
|
public ScheduledFuture<?> ariantScore;
|
|
|
|
private MapleCharacter() {
|
|
super.setListener(new AbstractCharacterListener() {
|
|
@Override
|
|
public void onHpChanged(int oldHp) {
|
|
hpChangeAction(oldHp);
|
|
}
|
|
|
|
@Override
|
|
public void onHpmpPoolUpdate() {
|
|
List<Pair<MapleStat, Integer>> hpmpupdate = recalcLocalStats();
|
|
for (Pair<MapleStat, Integer> p : hpmpupdate) {
|
|
statUpdates.put(p.getLeft(), p.getRight());
|
|
}
|
|
|
|
if (hp > localmaxhp) {
|
|
setHp(localmaxhp);
|
|
statUpdates.put(MapleStat.HP, hp);
|
|
}
|
|
|
|
if (mp > localmaxmp) {
|
|
setMp(localmaxmp);
|
|
statUpdates.put(MapleStat.MP, mp);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onAnnounceStatPoolUpdate() {
|
|
List<Pair<MapleStat, Integer>> statup = new ArrayList<>(8);
|
|
for (Map.Entry<MapleStat, Integer> s : statUpdates.entrySet()) {
|
|
statup.add(new Pair<>(s.getKey(), s.getValue()));
|
|
}
|
|
|
|
announce(MaplePacketCreator.updatePlayerStats(statup, true, MapleCharacter.this));
|
|
}
|
|
});
|
|
|
|
useCS = false;
|
|
|
|
setStance(0);
|
|
inventory = new MapleInventory[MapleInventoryType.values().length];
|
|
savedLocations = new SavedLocation[SavedLocationType.values().length];
|
|
|
|
for (MapleInventoryType type : MapleInventoryType.values()) {
|
|
byte b = 24;
|
|
if (type == MapleInventoryType.CASH) {
|
|
b = 96;
|
|
}
|
|
inventory[type.ordinal()] = new MapleInventory(this, type, (byte) b);
|
|
}
|
|
inventory[MapleInventoryType.CANHOLD.ordinal()] = new MapleInventoryProof(this);
|
|
|
|
for (int i = 0; i < SavedLocationType.values().length; i++) {
|
|
savedLocations[i] = null;
|
|
}
|
|
quests = new LinkedHashMap<>();
|
|
setPosition(new Point(0, 0));
|
|
|
|
petLootCd = Server.getInstance().getCurrentTime();
|
|
}
|
|
|
|
private static MapleJob getJobStyleInternal(int jobid, byte opt) {
|
|
int jobtype = jobid / 100;
|
|
|
|
if (jobtype == MapleJob.WARRIOR.getId() / 100 || jobtype == MapleJob.DAWNWARRIOR1.getId() / 100 || jobtype == MapleJob.ARAN1.getId() / 100) {
|
|
return (MapleJob.WARRIOR);
|
|
} else if (jobtype == MapleJob.MAGICIAN.getId() / 100 || jobtype == MapleJob.BLAZEWIZARD1.getId() / 100 || jobtype == MapleJob.EVAN1.getId() / 100) {
|
|
return (MapleJob.MAGICIAN);
|
|
} else if (jobtype == MapleJob.BOWMAN.getId() / 100 || jobtype == MapleJob.WINDARCHER1.getId() / 100) {
|
|
if (jobid / 10 == MapleJob.CROSSBOWMAN.getId() / 10) {
|
|
return (MapleJob.CROSSBOWMAN);
|
|
} else {
|
|
return (MapleJob.BOWMAN);
|
|
}
|
|
} else if (jobtype == MapleJob.THIEF.getId() / 100 || jobtype == MapleJob.NIGHTWALKER1.getId() / 100) {
|
|
return (MapleJob.THIEF);
|
|
} else if (jobtype == MapleJob.PIRATE.getId() / 100 || jobtype == MapleJob.THUNDERBREAKER1.getId() / 100) {
|
|
if (opt == (byte) 0x80) {
|
|
return (MapleJob.BRAWLER);
|
|
} else {
|
|
return (MapleJob.GUNSLINGER);
|
|
}
|
|
}
|
|
|
|
return (MapleJob.BEGINNER);
|
|
}
|
|
|
|
public MapleJob getJobStyle(byte opt) {
|
|
return getJobStyleInternal(this.getJob().getId(), opt);
|
|
}
|
|
|
|
public MapleJob getJobStyle() {
|
|
return getJobStyle((byte) ((this.getStr() > this.getDex()) ? 0x80 : 0x40));
|
|
}
|
|
|
|
public static MapleCharacter getDefault(MapleClient c) {
|
|
MapleCharacter ret = new MapleCharacter();
|
|
ret.client = c;
|
|
ret.gmLevel = 0;
|
|
ret.hp = 50;
|
|
ret.setMaxHp(50);
|
|
ret.mp = 5;
|
|
ret.setMaxMp(5);
|
|
ret.str = 12;
|
|
ret.dex = 5;
|
|
ret.int_ = 4;
|
|
ret.luk = 4;
|
|
ret.map = null;
|
|
ret.job = MapleJob.BEGINNER;
|
|
ret.level = 1;
|
|
ret.accountid = c.getAccID();
|
|
ret.buddylist = new BuddyList(20);
|
|
ret.maplemount = null;
|
|
ret.getInventory(MapleInventoryType.EQUIP).setSlotLimit(24);
|
|
ret.getInventory(MapleInventoryType.USE).setSlotLimit(24);
|
|
ret.getInventory(MapleInventoryType.SETUP).setSlotLimit(24);
|
|
ret.getInventory(MapleInventoryType.ETC).setSlotLimit(24);
|
|
|
|
// Select a keybinding method
|
|
int[] selectedKey;
|
|
int[] selectedType;
|
|
int[] selectedAction;
|
|
|
|
if (ServerConstants.USE_CUSTOM_KEYSET) {
|
|
selectedKey = GameConstants.getCustomKey(true);
|
|
selectedType = GameConstants.getCustomType(true);
|
|
selectedAction = GameConstants.getCustomAction(true);
|
|
} else {
|
|
selectedKey = GameConstants.getCustomKey(false);
|
|
selectedType = GameConstants.getCustomType(false);
|
|
selectedAction = GameConstants.getCustomAction(false);
|
|
}
|
|
|
|
for (int i = 0; i < selectedKey.length; i++) {
|
|
ret.keymap.put(selectedKey[i], new MapleKeyBinding(selectedType[i], selectedAction[i]));
|
|
}
|
|
|
|
//to fix the map 0 lol
|
|
for (int i = 0; i < 5; i++) {
|
|
ret.trockmaps.add(999999999);
|
|
}
|
|
for (int i = 0; i < 10; i++) {
|
|
ret.viptrockmaps.add(999999999);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
public boolean isLoggedinWorld() {
|
|
return this.isLoggedin() && !this.isAwayFromWorld();
|
|
}
|
|
|
|
public boolean isAwayFromWorld() {
|
|
return awayFromWorld.get();
|
|
}
|
|
|
|
public void setEnteredChannelWorld() {
|
|
awayFromWorld.set(false);
|
|
client.getChannelServer().removePlayerAway(id);
|
|
}
|
|
|
|
public void setAwayFromChannelWorld() {
|
|
setAwayFromChannelWorld(false);
|
|
}
|
|
|
|
public void setDisconnectedFromChannelWorld() {
|
|
setAwayFromChannelWorld(true);
|
|
}
|
|
|
|
private void setAwayFromChannelWorld(boolean disconnect) {
|
|
awayFromWorld.set(true);
|
|
|
|
if (!disconnect) {
|
|
client.getChannelServer().insertPlayerAway(id);
|
|
} else {
|
|
client.getChannelServer().removePlayerAway(id);
|
|
}
|
|
}
|
|
|
|
public long getPetLootCd() {
|
|
return petLootCd;
|
|
}
|
|
|
|
public void setPetLootCd(long cd) {
|
|
petLootCd = cd;
|
|
}
|
|
|
|
public boolean getCS() {
|
|
return useCS;
|
|
}
|
|
|
|
public void setCS(boolean cs) {
|
|
useCS = cs;
|
|
}
|
|
|
|
public long getNpcCooldown() {
|
|
return npcCd;
|
|
}
|
|
|
|
public void setNpcCooldown(long d) {
|
|
npcCd = d;
|
|
}
|
|
|
|
public void setOwlSearch(int id) {
|
|
owlSearch = id;
|
|
}
|
|
|
|
public int getOwlSearch() {
|
|
return owlSearch;
|
|
}
|
|
|
|
public void addCooldown(int skillId, long startTime, long length) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
this.coolDowns.put(Integer.valueOf(skillId), new MapleCoolDownValueHolder(skillId, startTime, length));
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void addCrushRing(MapleRing r) {
|
|
crushRings.add(r);
|
|
}
|
|
|
|
public MapleRing getRingById(int id) {
|
|
for (MapleRing ring : getCrushRings()) {
|
|
if (ring.getRingId() == id) {
|
|
return ring;
|
|
}
|
|
}
|
|
for (MapleRing ring : getFriendshipRings()) {
|
|
if (ring.getRingId() == id) {
|
|
return ring;
|
|
}
|
|
}
|
|
|
|
if (marriageRing != null) {
|
|
if (marriageRing.getRingId() == id) {
|
|
return marriageRing;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public int getMarriageItemId() {
|
|
return marriageItemid;
|
|
}
|
|
|
|
public void setMarriageItemId(int itemid) {
|
|
marriageItemid = itemid;
|
|
}
|
|
|
|
public int getPartnerId() {
|
|
return partnerId;
|
|
}
|
|
|
|
public void setPartnerId(int partnerid) {
|
|
partnerId = partnerid;
|
|
}
|
|
|
|
public int getRelationshipId() {
|
|
return getWorldServer().getRelationshipId(id);
|
|
}
|
|
|
|
public boolean isMarried() {
|
|
return marriageRing != null && partnerId > 0;
|
|
}
|
|
|
|
public boolean hasJustMarried() {
|
|
EventInstanceManager eim = getEventInstance();
|
|
if (eim != null) {
|
|
String prop = eim.getProperty("groomId");
|
|
|
|
if (prop != null) {
|
|
if ((Integer.parseInt(prop) == id || eim.getIntProperty("brideId") == id) && (mapid == 680000110 || mapid == 680000210)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public int addDojoPointsByMap(int mapid) {
|
|
int pts = 0;
|
|
if (dojoPoints < 17000) {
|
|
pts = 1 + ((mapid - 1) / 100 % 100) / 6;
|
|
if (!getDojoParty()) {
|
|
pts++;
|
|
}
|
|
this.dojoPoints += pts;
|
|
}
|
|
return pts;
|
|
}
|
|
|
|
public void addFame(int famechange) {
|
|
this.fame += famechange;
|
|
}
|
|
|
|
public void addFriendshipRing(MapleRing r) {
|
|
friendshipRings.add(r);
|
|
}
|
|
|
|
public void addMarriageRing(MapleRing r) {
|
|
marriageRing = r;
|
|
}
|
|
|
|
public void addMesosTraded(int gain) {
|
|
this.mesosTraded += gain;
|
|
}
|
|
|
|
public void addPet(MaplePet pet) {
|
|
petLock.lock();
|
|
try {
|
|
for (int i = 0; i < 3; i++) {
|
|
if (pets[i] == null) {
|
|
pets[i] = pet;
|
|
return;
|
|
}
|
|
}
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void addSummon(int id, MapleSummon summon) {
|
|
summons.put(id, summon);
|
|
|
|
if (summon.isPuppet()) {
|
|
map.addPlayerPuppet(this);
|
|
}
|
|
}
|
|
|
|
public void addVisibleMapObject(MapleMapObject mo) {
|
|
visibleMapObjects.add(mo);
|
|
}
|
|
|
|
public void ban(String reason) {
|
|
this.isbanned = true;
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET banned = 1, banreason = ? WHERE id = ?")) {
|
|
ps.setString(1, reason);
|
|
ps.setInt(2, accountid);
|
|
ps.executeUpdate();
|
|
}
|
|
con.close();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
}
|
|
|
|
public static boolean ban(String id, String reason, boolean accountId) {
|
|
PreparedStatement ps = null;
|
|
ResultSet rs = null;
|
|
Connection con = null;
|
|
try {
|
|
con = DatabaseConnection.getConnection();
|
|
|
|
if (id.matches("/[0-9]{1,3}\\..*")) {
|
|
ps = con.prepareStatement("INSERT INTO ipbans VALUES (DEFAULT, ?)");
|
|
ps.setString(1, id);
|
|
ps.executeUpdate();
|
|
ps.close();
|
|
return true;
|
|
}
|
|
if (accountId) {
|
|
ps = con.prepareStatement("SELECT id FROM accounts WHERE name = ?");
|
|
} else {
|
|
ps = con.prepareStatement("SELECT accountid FROM characters WHERE name = ?");
|
|
}
|
|
|
|
boolean ret = false;
|
|
ps.setString(1, id);
|
|
rs = ps.executeQuery();
|
|
if (rs.next()) {
|
|
Connection con2 = DatabaseConnection.getConnection();
|
|
|
|
try (PreparedStatement psb = con2.prepareStatement("UPDATE accounts SET banned = 1, banreason = ? WHERE id = ?")) {
|
|
psb.setString(1, reason);
|
|
psb.setInt(2, rs.getInt(1));
|
|
psb.executeUpdate();
|
|
} finally {
|
|
con2.close();
|
|
}
|
|
ret = true;
|
|
}
|
|
|
|
rs.close();
|
|
ps.close();
|
|
con.close();
|
|
return ret;
|
|
} catch (SQLException ex) {
|
|
ex.printStackTrace();
|
|
} finally {
|
|
try {
|
|
if (ps != null && !ps.isClosed()) {
|
|
ps.close();
|
|
}
|
|
if (rs != null && !rs.isClosed()) {
|
|
rs.close();
|
|
}
|
|
if (con != null && !con.isClosed()) {
|
|
con.close();
|
|
}
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public int calculateMaxBaseDamage(int watk) {
|
|
int maxbasedamage;
|
|
Item weapon_item = getInventory(MapleInventoryType.EQUIPPED).getItem((short) -11);
|
|
if (weapon_item != null) {
|
|
MapleWeaponType weapon = ii.getWeaponType(weapon_item.getItemId());
|
|
int mainstat, secondarystat;
|
|
if (getJob().isA(MapleJob.THIEF) && weapon == MapleWeaponType.DAGGER_OTHER) {
|
|
weapon = MapleWeaponType.DAGGER_THIEVES;
|
|
}
|
|
|
|
if (weapon == MapleWeaponType.BOW || weapon == MapleWeaponType.CROSSBOW || weapon == MapleWeaponType.GUN) {
|
|
mainstat = localdex;
|
|
secondarystat = localstr;
|
|
} else if (weapon == MapleWeaponType.CLAW || weapon == MapleWeaponType.DAGGER_THIEVES) {
|
|
mainstat = localluk;
|
|
secondarystat = localdex + localstr;
|
|
} else {
|
|
mainstat = localstr;
|
|
secondarystat = localdex;
|
|
}
|
|
maxbasedamage = (int) (((weapon.getMaxDamageMultiplier() * mainstat + secondarystat) / 100.0) * watk);
|
|
} else {
|
|
if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
|
|
double weapMulti = 3;
|
|
if (job.getId() % 100 != 0) {
|
|
weapMulti = 4.2;
|
|
}
|
|
|
|
int attack = (int) Math.min(Math.floor((2 * getLevel() + 31) / 3), 31);
|
|
maxbasedamage = (int) (localstr * weapMulti + localdex) * attack / 100;
|
|
} else {
|
|
maxbasedamage = 1;
|
|
}
|
|
}
|
|
return maxbasedamage;
|
|
}
|
|
|
|
public void setCombo(short count) {
|
|
if (count < combocounter) {
|
|
cancelEffectFromBuffStat(MapleBuffStat.ARAN_COMBO);
|
|
}
|
|
combocounter = (short) Math.min(30000, count);
|
|
if (count > 0) {
|
|
announce(MaplePacketCreator.showCombo(combocounter));
|
|
}
|
|
}
|
|
|
|
public void setLastCombo(long time) {
|
|
lastcombo = time;
|
|
}
|
|
|
|
public short getCombo() {
|
|
return combocounter;
|
|
}
|
|
|
|
public long getLastCombo() {
|
|
return lastcombo;
|
|
}
|
|
|
|
public int getLastMobCount() { //Used for skills that have mobCount at 1. (a/b)
|
|
return lastmobcount;
|
|
}
|
|
|
|
public void setLastMobCount(byte count) {
|
|
lastmobcount = count;
|
|
}
|
|
|
|
public boolean cannotEnterCashShop() {
|
|
return blockCashShop;
|
|
}
|
|
|
|
public void toggleBlockCashShop() {
|
|
blockCashShop = !blockCashShop;
|
|
}
|
|
|
|
public void toggleExpGain() {
|
|
allowExpGain = !allowExpGain;
|
|
}
|
|
|
|
public void setClient(MapleClient c) {
|
|
this.client = c;
|
|
}
|
|
|
|
public void newClient(MapleClient c) {
|
|
this.loggedIn = true;
|
|
c.setAccountName(this.client.getAccountName());//No null's for accountName
|
|
this.setClient(c);
|
|
this.map = c.getChannelServer().getMapFactory().getMap(getMapId());
|
|
MaplePortal portal = map.findClosestPlayerSpawnpoint(getPosition());
|
|
if (portal == null) {
|
|
portal = map.getPortal(0);
|
|
}
|
|
this.setPosition(portal.getPosition());
|
|
this.initialSpawnPoint = portal.getId();
|
|
}
|
|
|
|
public String getMedalText() {
|
|
String medal = "";
|
|
final Item medalItem = getInventory(MapleInventoryType.EQUIPPED).getItem((short) -49);
|
|
if (medalItem != null) {
|
|
medal = "<" + ii.getName(medalItem.getItemId()) + "> ";
|
|
}
|
|
return medal;
|
|
}
|
|
|
|
public void Hide(boolean hide, boolean login) {
|
|
if (isGM() && hide != this.hidden) {
|
|
if (!hide) {
|
|
this.hidden = false;
|
|
announce(MaplePacketCreator.getGMEffect(0x10, (byte) 0));
|
|
List<MapleBuffStat> dsstat = Collections.singletonList(MapleBuffStat.DARKSIGHT);
|
|
getMap().broadcastGMMessage(this, MaplePacketCreator.cancelForeignBuff(id, dsstat), false);
|
|
getMap().broadcastSpawnPlayerMapObjectMessage(this, this, false);
|
|
|
|
for (MapleSummon ms : this.getSummonsValues()) {
|
|
getMap().broadcastNONGMMessage(this, MaplePacketCreator.spawnSummon(ms, false), false);
|
|
}
|
|
} else {
|
|
this.hidden = true;
|
|
announce(MaplePacketCreator.getGMEffect(0x10, (byte) 1));
|
|
if (!login) {
|
|
getMap().broadcastNONGMMessage(this, MaplePacketCreator.removePlayerFromMap(getId()), false);
|
|
}
|
|
List<Pair<MapleBuffStat, Integer>> ldsstat = Collections.singletonList(new Pair<MapleBuffStat, Integer>(MapleBuffStat.DARKSIGHT, 0));
|
|
getMap().broadcastGMMessage(this, MaplePacketCreator.giveForeignBuff(id, ldsstat), false);
|
|
this.releaseControlledMonsters();
|
|
}
|
|
announce(MaplePacketCreator.enableActions());
|
|
}
|
|
}
|
|
|
|
public void Hide(boolean hide) {
|
|
Hide(hide, false);
|
|
}
|
|
|
|
public void toggleHide(boolean login) {
|
|
Hide(!hidden);
|
|
}
|
|
|
|
public void cancelMagicDoor() {
|
|
List<MapleBuffStatValueHolder> mbsvhList = getAllStatups();
|
|
for (MapleBuffStatValueHolder mbsvh : mbsvhList) {
|
|
if (mbsvh.effect.isMagicDoor()) {
|
|
cancelEffect(mbsvh.effect, false, mbsvh.startTime);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void cancelPlayerBuffs(List<MapleBuffStat> buffstats) {
|
|
if (client.getChannelServer().getPlayerStorage().getCharacterById(getId()) != null) {
|
|
updateLocalStats();
|
|
client.announce(MaplePacketCreator.cancelBuff(buffstats));
|
|
if (buffstats.size() > 0) {
|
|
getMap().broadcastMessage(this, MaplePacketCreator.cancelForeignBuff(getId(), buffstats), false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static boolean canCreateChar(String name) {
|
|
String lname = name.toLowerCase();
|
|
for (String nameTest : BLOCKED_NAMES) {
|
|
if (lname.contains(nameTest)) {
|
|
return false;
|
|
}
|
|
}
|
|
return getIdByName(name) < 0 && Pattern.compile("[a-zA-Z0-9]{3,12}").matcher(name).matches();
|
|
}
|
|
|
|
public boolean canDoor() {
|
|
return canDoor;
|
|
}
|
|
|
|
public void setHasSandboxItem() {
|
|
hasSandboxItem = true;
|
|
}
|
|
|
|
public void removeSandboxItems() { // sandbox idea thanks to Morty
|
|
if (!hasSandboxItem) {
|
|
return;
|
|
}
|
|
|
|
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
|
for (MapleInventoryType invType : MapleInventoryType.values()) {
|
|
MapleInventory inv = this.getInventory(invType);
|
|
|
|
inv.lockInventory();
|
|
try {
|
|
for (Item item : new ArrayList<>(inv.list())) {
|
|
if (MapleInventoryManipulator.isSandboxItem(item)) {
|
|
MapleInventoryManipulator.removeFromSlot(client, invType, item.getPosition(), item.getQuantity(), false);
|
|
dropMessage(5, "[" + ii.getName(item.getItemId()) + "] has passed its trial conditions and will be removed from your inventory.");
|
|
}
|
|
}
|
|
} finally {
|
|
inv.unlockInventory();
|
|
}
|
|
}
|
|
|
|
hasSandboxItem = false;
|
|
}
|
|
|
|
public FameStatus canGiveFame(MapleCharacter from) {
|
|
if (this.isGM()) {
|
|
return FameStatus.OK;
|
|
} else if (lastfametime >= System.currentTimeMillis() - 3600000 * 24) {
|
|
return FameStatus.NOT_TODAY;
|
|
} else if (lastmonthfameids.contains(Integer.valueOf(from.getId()))) {
|
|
return FameStatus.NOT_THIS_MONTH;
|
|
} else {
|
|
return FameStatus.OK;
|
|
}
|
|
}
|
|
|
|
public void changeCI(int type) {
|
|
this.ci = type;
|
|
}
|
|
|
|
public void setMasteries(int jobId) {
|
|
int[] skills = new int[4];
|
|
for (int i = 0; i > skills.length; i++) {
|
|
skills[i] = 0; //that initialization meng
|
|
}
|
|
if (jobId == 112) {
|
|
skills[0] = Hero.ACHILLES;
|
|
skills[1] = Hero.MONSTER_MAGNET;
|
|
skills[2] = Hero.BRANDISH;
|
|
} else if (jobId == 122) {
|
|
skills[0] = Paladin.ACHILLES;
|
|
skills[1] = Paladin.MONSTER_MAGNET;
|
|
skills[2] = Paladin.BLAST;
|
|
} else if (jobId == 132) {
|
|
skills[0] = DarkKnight.BEHOLDER;
|
|
skills[1] = DarkKnight.ACHILLES;
|
|
skills[2] = DarkKnight.MONSTER_MAGNET;
|
|
} else if (jobId == 212) {
|
|
skills[0] = FPArchMage.BIG_BANG;
|
|
skills[1] = FPArchMage.MANA_REFLECTION;
|
|
skills[2] = FPArchMage.PARALYZE;
|
|
} else if (jobId == 222) {
|
|
skills[0] = ILArchMage.BIG_BANG;
|
|
skills[1] = ILArchMage.MANA_REFLECTION;
|
|
skills[2] = ILArchMage.CHAIN_LIGHTNING;
|
|
} else if (jobId == 232) {
|
|
skills[0] = Bishop.BIG_BANG;
|
|
skills[1] = Bishop.MANA_REFLECTION;
|
|
skills[2] = Bishop.HOLY_SHIELD;
|
|
} else if (jobId == 312) {
|
|
skills[0] = Bowmaster.BOW_EXPERT;
|
|
skills[1] = Bowmaster.HAMSTRING;
|
|
skills[2] = Bowmaster.SHARP_EYES;
|
|
} else if (jobId == 322) {
|
|
skills[0] = Marksman.MARKSMAN_BOOST;
|
|
skills[1] = Marksman.BLIND;
|
|
skills[2] = Marksman.SHARP_EYES;
|
|
} else if (jobId == 412) {
|
|
skills[0] = NightLord.SHADOW_STARS;
|
|
skills[1] = NightLord.SHADOW_SHIFTER;
|
|
skills[2] = NightLord.VENOMOUS_STAR;
|
|
} else if (jobId == 422) {
|
|
skills[0] = Shadower.SHADOW_SHIFTER;
|
|
skills[1] = Shadower.VENOMOUS_STAB;
|
|
skills[2] = Shadower.BOOMERANG_STEP;
|
|
} else if (jobId == 512) {
|
|
skills[0] = Buccaneer.BARRAGE;
|
|
skills[1] = Buccaneer.ENERGY_ORB;
|
|
skills[2] = Buccaneer.SPEED_INFUSION;
|
|
skills[3] = Buccaneer.DRAGON_STRIKE;
|
|
} else if (jobId == 522) {
|
|
skills[0] = Corsair.ELEMENTAL_BOOST;
|
|
skills[1] = Corsair.BULLSEYE;
|
|
skills[2] = Corsair.WRATH_OF_THE_OCTOPI;
|
|
skills[3] = Corsair.RAPID_FIRE;
|
|
} else if (jobId == 2112) {
|
|
skills[0] = Aran.OVER_SWING;
|
|
skills[1] = Aran.HIGH_MASTERY;
|
|
skills[2] = Aran.FREEZE_STANDING;
|
|
} else if (jobId == 2217) {
|
|
skills[0] = Evan.MAPLE_WARRIOR;
|
|
skills[1] = Evan.ILLUSION;
|
|
} else if (jobId == 2218) {
|
|
skills[0] = Evan.BLESSING_OF_THE_ONYX;
|
|
skills[1] = Evan.BLAZE;
|
|
}
|
|
for (Integer skillId : skills) {
|
|
if (skillId != 0) {
|
|
Skill skill = SkillFactory.getSkill(skillId);
|
|
final int skilllevel = getSkillLevel(skill);
|
|
if (skilllevel > 0) {
|
|
continue;
|
|
}
|
|
|
|
changeSkillLevel(skill, (byte) 0, 10, -1);
|
|
}
|
|
}
|
|
}
|
|
|
|
public synchronized void changeJob(MapleJob newJob) {
|
|
if (newJob == null) {
|
|
return;//the fuck you doing idiot!
|
|
}
|
|
|
|
this.job = newJob;
|
|
|
|
int spGain = 1;
|
|
if (GameConstants.hasSPTable(newJob)) {
|
|
spGain += 2;
|
|
} else {
|
|
if (newJob.getId() % 10 == 2) {
|
|
spGain += 2;
|
|
}
|
|
|
|
if (ServerConstants.USE_ENFORCE_JOB_SP_RANGE) {
|
|
spGain = getChangedJobSp(newJob);
|
|
}
|
|
}
|
|
|
|
if (spGain > 0) {
|
|
gainSp(spGain, GameConstants.getSkillBook(newJob.getId()), true);
|
|
}
|
|
|
|
if (newJob.getId() % 10 > 1) {
|
|
gainAp(5, true);
|
|
}
|
|
|
|
if (!isGM()) {
|
|
for (byte i = 1; i < 5; i++) {
|
|
gainSlots(i, 4, true);
|
|
}
|
|
}
|
|
|
|
int addhp = 0, addmp = 0;
|
|
int job_ = job.getId() % 1000; // lame temp "fix"
|
|
if (job_ == 100) { // 1st warrior
|
|
addhp += Randomizer.rand(200, 250);
|
|
} else if (job_ == 200) { // 1st mage
|
|
addmp += Randomizer.rand(100, 150);
|
|
} else if (job_ % 100 == 0) { // 1st others
|
|
addhp += Randomizer.rand(100, 150);
|
|
addhp += Randomizer.rand(25, 50);
|
|
} else if (job_ > 0 && job_ < 200) { // 2nd~4th warrior
|
|
addhp += Randomizer.rand(300, 350);
|
|
} else if (job_ < 300) { // 2nd~4th mage
|
|
addmp += Randomizer.rand(450, 500);
|
|
} else if (job_ > 0) { // 2nd~4th others
|
|
addhp += Randomizer.rand(300, 350);
|
|
addmp += Randomizer.rand(150, 200);
|
|
}
|
|
|
|
/*
|
|
//aran perks?
|
|
int newJobId = newJob.getId();
|
|
if(newJobId == 2100) { // become aran1
|
|
addhp += 275;
|
|
addmp += 15;
|
|
} else if(newJobId == 2110) { // become aran2
|
|
addmp += 275;
|
|
} else if(newJobId == 2111) { // become aran3
|
|
addhp += 275;
|
|
addmp += 275;
|
|
}
|
|
*/
|
|
effLock.lock();
|
|
statWlock.lock();
|
|
try {
|
|
addMaxMPMaxHP(addhp, addmp, true);
|
|
recalcLocalStats();
|
|
|
|
List<Pair<MapleStat, Integer>> statup = new ArrayList<>(7);
|
|
statup.add(new Pair<>(MapleStat.HP, hp));
|
|
statup.add(new Pair<>(MapleStat.MP, mp));
|
|
statup.add(new Pair<>(MapleStat.MAXHP, clientmaxhp));
|
|
statup.add(new Pair<>(MapleStat.MAXMP, clientmaxmp));
|
|
statup.add(new Pair<>(MapleStat.AVAILABLEAP, remainingAp));
|
|
statup.add(new Pair<>(MapleStat.AVAILABLESP, remainingSp[GameConstants.getSkillBook(job.getId())]));
|
|
statup.add(new Pair<>(MapleStat.JOB, job.getId()));
|
|
client.announce(MaplePacketCreator.updatePlayerStats(statup, true, this));
|
|
} finally {
|
|
statWlock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
|
|
silentPartyUpdate();
|
|
|
|
if (dragon != null) {
|
|
getMap().broadcastMessage(MaplePacketCreator.removeDragon(dragon.getObjectId()));
|
|
dragon = null;
|
|
}
|
|
|
|
if (this.guildid > 0) {
|
|
getGuild().broadcast(MaplePacketCreator.jobMessage(0, job.getId(), name), this.getId());
|
|
}
|
|
setMasteries(this.job.getId());
|
|
guildUpdate();
|
|
|
|
getMap().broadcastMessage(this, MaplePacketCreator.showForeignEffect(this.getId(), 8), false);
|
|
|
|
if (GameConstants.hasSPTable(newJob) && newJob.getId() != 2001) {
|
|
if (getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null) {
|
|
cancelBuffStats(MapleBuffStat.MONSTER_RIDING);
|
|
}
|
|
createDragon();
|
|
}
|
|
|
|
if (ServerConstants.USE_ANNOUNCE_CHANGEJOB) {
|
|
if (!this.isGM()) {
|
|
broadcastAcquaintances(6, "[" + GameConstants.ordinal(GameConstants.getJobBranch(newJob)) + " Job] " + name + " has just become a " + newJob.name() + ".");
|
|
}
|
|
}
|
|
}
|
|
|
|
public void broadcastAcquaintances(int type, String message) {
|
|
broadcastAcquaintances(MaplePacketCreator.serverNotice(type, message));
|
|
}
|
|
|
|
public void broadcastAcquaintances(byte[] packet) {
|
|
buddylist.broadcast(packet, getWorldServer().getPlayerStorage());
|
|
|
|
if (family != null) {
|
|
//family.broadcast(packet, id); not yet implemented
|
|
}
|
|
|
|
MapleGuild guild = getGuild();
|
|
if (guild != null) {
|
|
guild.broadcast(packet, id);
|
|
}
|
|
|
|
/*
|
|
if(partnerid > 0) {
|
|
partner.announce(packet); not yet implemented
|
|
}
|
|
*/
|
|
announce(packet);
|
|
}
|
|
|
|
public void changeKeybinding(int key, MapleKeyBinding keybinding) {
|
|
if (keybinding.getType() != 0) {
|
|
keymap.put(Integer.valueOf(key), keybinding);
|
|
} else {
|
|
keymap.remove(Integer.valueOf(key));
|
|
}
|
|
}
|
|
|
|
public void broadcastStance(int newStance) {
|
|
setStance(newStance);
|
|
broadcastStance();
|
|
}
|
|
|
|
public void broadcastStance() {
|
|
map.broadcastMessage(this, MaplePacketCreator.movePlayer(id, this.getIdleMovement()), false);
|
|
}
|
|
|
|
public MapleMap getWarpMap(int map) {
|
|
MapleMap target;
|
|
EventInstanceManager eim = getEventInstance();
|
|
if (eim == null) {
|
|
target = client.getChannelServer().getMapFactory().getMap(map);
|
|
} else {
|
|
target = eim.getMapInstance(map);
|
|
}
|
|
return target;
|
|
}
|
|
|
|
// for use ONLY inside OnUserEnter map scripts that requires a player to change map while still moving between maps.
|
|
public void warpAhead(int map) {
|
|
newWarpMap = map;
|
|
}
|
|
|
|
private void eventChangedMap(int map) {
|
|
EventInstanceManager eim = getEventInstance();
|
|
if (eim != null) {
|
|
eim.changedMap(this, map);
|
|
}
|
|
}
|
|
|
|
private void eventAfterChangedMap(int map) {
|
|
EventInstanceManager eim = getEventInstance();
|
|
if (eim != null) {
|
|
eim.afterChangedMap(this, map);
|
|
}
|
|
}
|
|
|
|
public boolean canRecoverLastBanish() {
|
|
return System.currentTimeMillis() - this.banishTime < 5 * 60 * 1000;
|
|
}
|
|
|
|
public Pair<Integer, Integer> getLastBanishData() {
|
|
return new Pair<>(this.banishMap, this.banishSp);
|
|
}
|
|
|
|
public void clearBanishPlayerData() {
|
|
this.banishMap = -1;
|
|
this.banishSp = -1;
|
|
this.banishTime = 0;
|
|
}
|
|
|
|
public void setBanishPlayerData(int banishMap, int banishSp, long banishTime) {
|
|
this.banishMap = banishMap;
|
|
this.banishSp = banishSp;
|
|
this.banishTime = banishTime;
|
|
}
|
|
|
|
public void changeMapBanish(int mapid, String portal, String msg) {
|
|
if (ServerConstants.USE_SPIKES_AVOID_BANISH) {
|
|
for (Item it : this.getInventory(MapleInventoryType.EQUIPPED).list()) {
|
|
if ((it.getFlag() & ItemConstants.SPIKES) == ItemConstants.SPIKES) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int banMap = this.getMapId();
|
|
int banSp = this.getMap().findClosestPlayerSpawnpoint(this.getPosition()).getId();
|
|
long banTime = System.currentTimeMillis();
|
|
|
|
if (msg != null) {
|
|
dropMessage(5, msg);
|
|
}
|
|
|
|
MapleMap map_ = getWarpMap(mapid);
|
|
MaplePortal portal_ = map_.getPortal(portal);
|
|
changeMap(map_, portal_ != null ? portal_ : map_.getRandomPlayerSpawnpoint());
|
|
|
|
setBanishPlayerData(banMap, banSp, banTime);
|
|
}
|
|
|
|
public void changeMap(int map) {
|
|
MapleMap warpMap;
|
|
EventInstanceManager eim = getEventInstance();
|
|
|
|
if (eim != null) {
|
|
warpMap = eim.getMapInstance(map);
|
|
} else {
|
|
warpMap = client.getChannelServer().getMapFactory().getMap(map);
|
|
}
|
|
|
|
changeMap(warpMap, warpMap.getRandomPlayerSpawnpoint());
|
|
}
|
|
|
|
public void changeMap(int map, int portal) {
|
|
MapleMap warpMap;
|
|
EventInstanceManager eim = getEventInstance();
|
|
|
|
if (eim != null) {
|
|
warpMap = eim.getMapInstance(map);
|
|
} else {
|
|
warpMap = client.getChannelServer().getMapFactory().getMap(map);
|
|
}
|
|
|
|
changeMap(warpMap, warpMap.getPortal(portal));
|
|
}
|
|
|
|
public void changeMap(int map, String portal) {
|
|
MapleMap warpMap;
|
|
EventInstanceManager eim = getEventInstance();
|
|
|
|
if (eim != null) {
|
|
warpMap = eim.getMapInstance(map);
|
|
} else {
|
|
warpMap = client.getChannelServer().getMapFactory().getMap(map);
|
|
}
|
|
|
|
changeMap(warpMap, warpMap.getPortal(portal));
|
|
}
|
|
|
|
public void changeMap(int map, MaplePortal portal) {
|
|
MapleMap warpMap;
|
|
EventInstanceManager eim = getEventInstance();
|
|
|
|
if (eim != null) {
|
|
warpMap = eim.getMapInstance(map);
|
|
} else {
|
|
warpMap = client.getChannelServer().getMapFactory().getMap(map);
|
|
}
|
|
|
|
changeMap(warpMap, portal);
|
|
}
|
|
|
|
public void changeMap(MapleMap to) {
|
|
changeMap(to, 0);
|
|
}
|
|
|
|
public void changeMap(MapleMap to, int portal) {
|
|
changeMap(to, to.getPortal(portal));
|
|
}
|
|
|
|
public void changeMap(final MapleMap target, final MaplePortal pto) {
|
|
canWarpCounter++;
|
|
|
|
eventChangedMap(target.getId()); // player can be dropped from an event here, hence the new warping target.
|
|
MapleMap to = getWarpMap(target.getId());
|
|
changeMapInternal(to, pto.getPosition(), MaplePacketCreator.getWarpToMap(to, pto.getId(), this));
|
|
canWarpMap = false;
|
|
|
|
canWarpCounter--;
|
|
if (canWarpCounter == 0) {
|
|
canWarpMap = true;
|
|
}
|
|
|
|
eventAfterChangedMap(this.getMapId());
|
|
}
|
|
|
|
public void changeMap(final MapleMap target, final Point pos) {
|
|
canWarpCounter++;
|
|
|
|
eventChangedMap(target.getId());
|
|
MapleMap to = getWarpMap(target.getId());
|
|
changeMapInternal(to, pos, MaplePacketCreator.getWarpToMap(to, 0x80, pos, this));
|
|
canWarpMap = false;
|
|
|
|
canWarpCounter--;
|
|
if (canWarpCounter == 0) {
|
|
canWarpMap = true;
|
|
}
|
|
|
|
eventAfterChangedMap(this.getMapId());
|
|
}
|
|
|
|
public void forceChangeMap(final MapleMap target, final MaplePortal pto) {
|
|
// will actually enter the map given as parameter, regardless of being an eventmap or whatnot
|
|
|
|
canWarpCounter++;
|
|
eventChangedMap(999999999);
|
|
|
|
EventInstanceManager mapEim = target.getEventInstance();
|
|
if (mapEim != null) {
|
|
EventInstanceManager playerEim = this.getEventInstance();
|
|
if (playerEim != null) {
|
|
playerEim.exitPlayer(this);
|
|
if (playerEim.getPlayerCount() == 0) {
|
|
playerEim.dispose();
|
|
}
|
|
}
|
|
|
|
// thanks Thora for finding an issue with players not being actually warped into the target event map (rather sent to the event starting map)
|
|
mapEim.registerPlayer(this, false);
|
|
}
|
|
|
|
MapleMap to = target; // warps directly to the target intead of the target's map id, this allows GMs to patrol players inside instances.
|
|
changeMapInternal(to, pto.getPosition(), MaplePacketCreator.getWarpToMap(to, pto.getId(), this));
|
|
canWarpMap = false;
|
|
|
|
canWarpCounter--;
|
|
if (canWarpCounter == 0) {
|
|
canWarpMap = true;
|
|
}
|
|
|
|
eventAfterChangedMap(this.getMapId());
|
|
}
|
|
|
|
private boolean buffMapProtection() {
|
|
int thisMapid = mapid;
|
|
int returnMapid = client.getChannelServer().getMapFactory().getMap(thisMapid).getReturnMapId();
|
|
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> mbs : effects.entrySet()) {
|
|
if (mbs.getKey() == MapleBuffStat.MAP_PROTECTION) {
|
|
byte value = (byte) mbs.getValue().value;
|
|
|
|
if (value == 1 && ((returnMapid == 211000000 && thisMapid != 200082300) || returnMapid == 193000000)) {
|
|
return true; //protection from cold
|
|
} else if (value == 2 && (returnMapid == 230000000 || thisMapid == 200082300)) {
|
|
return true; //breathing underwater
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
|
|
for (Item it : this.getInventory(MapleInventoryType.EQUIPPED).list()) {
|
|
if ((it.getFlag() & ItemConstants.COLD) == ItemConstants.COLD && ((returnMapid == 211000000 && thisMapid != 200082300) || returnMapid == 193000000)) {
|
|
return true; //protection from cold
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public List<Integer> getLastVisitedMapids() {
|
|
List<Integer> lastVisited = new ArrayList<>(5);
|
|
|
|
petLock.lock();
|
|
try {
|
|
for (WeakReference<MapleMap> lv : lastVisitedMaps) {
|
|
MapleMap lvm = lv.get();
|
|
|
|
if (lvm != null) {
|
|
lastVisited.add(lvm.getId());
|
|
}
|
|
}
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
|
|
return lastVisited;
|
|
}
|
|
|
|
public void partyOperationUpdate(MapleParty party, List<MapleCharacter> exPartyMembers) {
|
|
List<WeakReference<MapleMap>> mapids;
|
|
|
|
petLock.lock();
|
|
try {
|
|
mapids = new LinkedList<>(lastVisitedMaps);
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
|
|
List<MapleCharacter> partyMembers = new LinkedList<>();
|
|
for (MapleCharacter mc : (exPartyMembers != null) ? exPartyMembers : this.getPartyMembers()) {
|
|
if (mc.isLoggedinWorld()) {
|
|
partyMembers.add(mc);
|
|
}
|
|
}
|
|
|
|
MapleCharacter partyLeaver = null;
|
|
if (exPartyMembers != null) {
|
|
partyMembers.remove(this);
|
|
partyLeaver = this;
|
|
}
|
|
|
|
int partyId = exPartyMembers != null ? 0 : this.getPartyId();
|
|
for (WeakReference<MapleMap> mapRef : mapids) {
|
|
MapleMap mapObj = mapRef.get();
|
|
|
|
if (mapObj != null) {
|
|
mapObj.updatePlayerItemDrops(partyId, id, partyMembers, partyLeaver);
|
|
}
|
|
}
|
|
|
|
updatePartyTownDoors(party, this, partyLeaver, partyMembers);
|
|
}
|
|
|
|
private static void addPartyPlayerDoor(MapleCharacter target) {
|
|
MapleDoor targetDoor = target.getPlayerDoor();
|
|
if (targetDoor != null) {
|
|
target.applyPartyDoor(targetDoor, true);
|
|
}
|
|
}
|
|
|
|
private static void removePartyPlayerDoor(MapleParty party, MapleCharacter target) {
|
|
target.removePartyDoor(party);
|
|
}
|
|
|
|
private static void updatePartyTownDoors(MapleParty party, MapleCharacter target, MapleCharacter partyLeaver, List<MapleCharacter> partyMembers) {
|
|
if (partyLeaver != null) {
|
|
removePartyPlayerDoor(party, target);
|
|
} else {
|
|
addPartyPlayerDoor(target);
|
|
}
|
|
|
|
Map<Integer, MapleDoor> partyDoors = null;
|
|
if (!partyMembers.isEmpty()) {
|
|
partyDoors = party.getDoors();
|
|
|
|
for (MapleCharacter pchr : partyMembers) {
|
|
MapleDoor door = partyDoors.get(pchr.getId());
|
|
if (door != null) {
|
|
door.updateDoorPortal(pchr);
|
|
}
|
|
}
|
|
|
|
for (MapleDoor door : partyDoors.values()) {
|
|
for (MapleCharacter pchar : partyMembers) {
|
|
door.getTownDoor().sendDestroyData(pchar.getClient(), true);
|
|
}
|
|
}
|
|
|
|
if (partyLeaver != null) {
|
|
Collection<MapleDoor> leaverDoors = partyLeaver.getDoors();
|
|
for (MapleDoor door : leaverDoors) {
|
|
for (MapleCharacter pchar : partyMembers) {
|
|
door.getTownDoor().sendDestroyData(pchar.getClient(), true);
|
|
}
|
|
}
|
|
}
|
|
|
|
List<Integer> histMembers = party.getMembersSortedByHistory();
|
|
for (Integer chrid : histMembers) {
|
|
MapleDoor door = partyDoors.get(chrid);
|
|
|
|
if (door != null) {
|
|
for (MapleCharacter pchar : partyMembers) {
|
|
door.getTownDoor().sendSpawnData(pchar.getClient());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (partyLeaver != null) {
|
|
Collection<MapleDoor> leaverDoors = partyLeaver.getDoors();
|
|
|
|
if (partyDoors != null) {
|
|
for (MapleDoor door : partyDoors.values()) {
|
|
door.getTownDoor().sendDestroyData(partyLeaver.getClient(), true);
|
|
}
|
|
}
|
|
|
|
for (MapleDoor door : leaverDoors) {
|
|
door.getTownDoor().sendDestroyData(partyLeaver.getClient(), true);
|
|
}
|
|
|
|
for (MapleDoor door : leaverDoors) {
|
|
door.updateDoorPortal(partyLeaver);
|
|
door.getTownDoor().sendSpawnData(partyLeaver.getClient());
|
|
}
|
|
}
|
|
}
|
|
|
|
private Integer getVisitedMapIndex(MapleMap map) {
|
|
int idx = 0;
|
|
|
|
for (WeakReference<MapleMap> mapRef : lastVisitedMaps) {
|
|
if (map.equals(mapRef.get())) {
|
|
return idx;
|
|
}
|
|
|
|
idx++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
public void visitMap(MapleMap map) {
|
|
petLock.lock();
|
|
try {
|
|
int idx = getVisitedMapIndex(map);
|
|
|
|
if (idx == -1) {
|
|
if (lastVisitedMaps.size() == ServerConstants.MAP_VISITED_SIZE) {
|
|
lastVisitedMaps.remove(0);
|
|
}
|
|
} else {
|
|
WeakReference<MapleMap> mapRef = lastVisitedMaps.remove(idx);
|
|
lastVisitedMaps.add(mapRef);
|
|
return;
|
|
}
|
|
|
|
lastVisitedMaps.add(new WeakReference<>(map));
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void notifyMapTransferToPartner(int mapid) {
|
|
if (partnerId > 0) {
|
|
final MapleCharacter partner = getWorldServer().getPlayerStorage().getCharacterById(partnerId);
|
|
if (partner != null && !partner.isAwayFromWorld()) {
|
|
partner.announce(Wedding.OnNotifyWeddingPartnerTransfer(id, mapid));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void changeMapInternal(final MapleMap to, final Point pos, final byte[] warpPacket) {
|
|
if (!canWarpMap) {
|
|
return;
|
|
}
|
|
|
|
this.mapTransitioning.set(true);
|
|
|
|
this.unregisterChairBuff();
|
|
this.clearBanishPlayerData();
|
|
this.closePlayerInteractions();
|
|
|
|
MapleParty e = null;
|
|
if (this.getParty() != null && this.getParty().getEnemy() != null) {
|
|
e = this.getParty().getEnemy();
|
|
}
|
|
final MapleParty k = e;
|
|
client.announce(warpPacket);
|
|
map.removePlayer(this);
|
|
if (client.getChannelServer().getPlayerStorage().getCharacterById(getId()) != null) {
|
|
map = to;
|
|
setPosition(pos);
|
|
map.addPlayer(this);
|
|
visitMap(map);
|
|
|
|
prtLock.lock();
|
|
try {
|
|
if (party != null) {
|
|
mpc.setMapId(to.getId());
|
|
client.announce(MaplePacketCreator.updateParty(client.getChannel(), party, PartyOperation.SILENT_UPDATE, null));
|
|
updatePartyMemberHPInternal();
|
|
}
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
if (MapleCharacter.this.getParty() != null) {
|
|
MapleCharacter.this.getParty().setEnemy(k);
|
|
}
|
|
silentPartyUpdateInternal(getParty()); // EIM script calls inside
|
|
|
|
if (getMap().getHPDec() > 0) {
|
|
resetHpDecreaseTask();
|
|
}
|
|
} else {
|
|
FilePrinter.printError(FilePrinter.MAPLE_MAP, "Character " + this.getName() + " got stuck when moving to map " + map.getId() + ".");
|
|
}
|
|
|
|
notifyMapTransferToPartner(map.getId());
|
|
|
|
//alas, new map has been specified when a warping was being processed...
|
|
if (newWarpMap != -1) {
|
|
canWarpMap = true;
|
|
|
|
int temp = newWarpMap;
|
|
newWarpMap = -1;
|
|
changeMap(temp);
|
|
} else {
|
|
// if this event map has a gate already opened, render it
|
|
EventInstanceManager eim = getEventInstance();
|
|
if (eim != null) {
|
|
eim.recoverOpenedGate(this, map.getId());
|
|
}
|
|
|
|
// if this map has obstacle components moving, make it do so for this client
|
|
announce(MaplePacketCreator.environmentMoveList(map.getEnvironment().entrySet()));
|
|
}
|
|
}
|
|
|
|
public boolean isChangingMaps() {
|
|
return this.mapTransitioning.get();
|
|
}
|
|
|
|
public void setMapTransitionComplete() {
|
|
this.mapTransitioning.set(false);
|
|
}
|
|
|
|
public void changePage(int page) {
|
|
this.currentPage = page;
|
|
}
|
|
|
|
public void changeSkillLevel(Skill skill, byte newLevel, int newMasterlevel, long expiration) {
|
|
if (newLevel > -1) {
|
|
skills.put(skill, new SkillEntry(newLevel, newMasterlevel, expiration));
|
|
if (!GameConstants.isHiddenSkills(skill.getId())) {
|
|
this.client.announce(MaplePacketCreator.updateSkill(skill.getId(), newLevel, newMasterlevel, expiration));
|
|
}
|
|
} else {
|
|
skills.remove(skill);
|
|
this.client.announce(MaplePacketCreator.updateSkill(skill.getId(), newLevel, newMasterlevel, -1)); //Shouldn't use expiration anymore :)
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM skills WHERE skillid = ? AND characterid = ?")) {
|
|
ps.setInt(1, skill.getId());
|
|
ps.setInt(2, id);
|
|
ps.execute();
|
|
} finally {
|
|
con.close();
|
|
}
|
|
} catch (SQLException ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void changeTab(int tab) {
|
|
this.currentTab = tab;
|
|
}
|
|
|
|
public void changeType(int type) {
|
|
this.currentType = type;
|
|
}
|
|
|
|
public void checkBerserk(final boolean isHidden) {
|
|
if (berserkSchedule != null) {
|
|
berserkSchedule.cancel(false);
|
|
}
|
|
final MapleCharacter chr = this;
|
|
if (job.equals(MapleJob.DARKKNIGHT)) {
|
|
Skill BerserkX = SkillFactory.getSkill(DarkKnight.BERSERK);
|
|
final int skilllevel = getSkillLevel(BerserkX);
|
|
if (skilllevel > 0) {
|
|
berserk = chr.getHp() * 100 / chr.getCurrentMaxHp() < BerserkX.getEffect(skilllevel).getX();
|
|
berserkSchedule = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (awayFromWorld.get()) {
|
|
return;
|
|
}
|
|
|
|
client.announce(MaplePacketCreator.showOwnBerserk(skilllevel, berserk));
|
|
if (!isHidden) {
|
|
getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showBerserk(getId(), skilllevel, berserk), false);
|
|
} else {
|
|
getMap().broadcastGMMessage(MapleCharacter.this, MaplePacketCreator.showBerserk(getId(), skilllevel, berserk), false);
|
|
}
|
|
}
|
|
}, 5000, 3000);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void checkMessenger() {
|
|
if (messenger != null && messengerposition < 4 && messengerposition > -1) {
|
|
World worldz = getWorldServer();
|
|
worldz.silentJoinMessenger(messenger.getId(), new MapleMessengerCharacter(this, messengerposition), messengerposition);
|
|
worldz.updateMessenger(getMessenger().getId(), name, client.getChannel());
|
|
}
|
|
}
|
|
|
|
public void controlMonster(MapleMonster monster) {
|
|
if (cpnLock.tryLock()) {
|
|
try {
|
|
controlled.add(monster);
|
|
} finally {
|
|
cpnLock.unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void stopControllingMonster(MapleMonster monster) {
|
|
if (cpnLock.tryLock()) {
|
|
try {
|
|
controlled.remove(monster);
|
|
} finally {
|
|
cpnLock.unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
public int getNumControlledMonsters() {
|
|
cpnLock.lock();
|
|
try {
|
|
return controlled.size();
|
|
} finally {
|
|
cpnLock.unlock();
|
|
}
|
|
}
|
|
|
|
public Collection<MapleMonster> getControlledMonsters() {
|
|
cpnLock.lock();
|
|
try {
|
|
return new ArrayList<>(controlled);
|
|
} finally {
|
|
cpnLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void releaseControlledMonsters() {
|
|
Collection<MapleMonster> controlledMonsters;
|
|
|
|
cpnLock.lock();
|
|
try {
|
|
controlledMonsters = new ArrayList<>(controlled);
|
|
controlled.clear();
|
|
} finally {
|
|
cpnLock.unlock();
|
|
}
|
|
|
|
for (MapleMonster monster : controlledMonsters) {
|
|
monster.aggroRedirectController();
|
|
}
|
|
}
|
|
|
|
private boolean useItem(final int id) {
|
|
if (id / 1000000 == 2) {
|
|
if (ii.isConsumeOnPickup(id)) {
|
|
if (ItemConstants.isPartyItem(id)) {
|
|
List<MapleCharacter> pchr = this.getPartyMembersOnSameMap();
|
|
|
|
if (!ItemConstants.isPartyAllcure(id)) {
|
|
MapleStatEffect mse = ii.getItemEffect(id);
|
|
|
|
if (!pchr.isEmpty()) {
|
|
for (MapleCharacter mc : pchr) {
|
|
mse.applyTo(mc);
|
|
}
|
|
} else {
|
|
mse.applyTo(this);
|
|
}
|
|
} else {
|
|
if (!pchr.isEmpty()) {
|
|
for (MapleCharacter mc : pchr) {
|
|
mc.dispelDebuffs();
|
|
}
|
|
} else {
|
|
this.dispelDebuffs();
|
|
}
|
|
}
|
|
} else {
|
|
ii.getItemEffect(id).applyTo(this);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public final void pickupItem(MapleMapObject ob) {
|
|
pickupItem(ob, -1);
|
|
}
|
|
|
|
public final void pickupItem(MapleMapObject ob, int petIndex) { // yes, one picks the MapleMapObject, not the MapleMapItem
|
|
if (ob == null) { // pet index refers to the one picking up the item
|
|
return;
|
|
}
|
|
|
|
if (ob instanceof MapleMapItem) {
|
|
MapleMapItem mapitem = (MapleMapItem) ob;
|
|
if (System.currentTimeMillis() - mapitem.getDropTime() < 400 || !mapitem.canBePickedBy(this)) {
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
return;
|
|
}
|
|
|
|
List<MapleCharacter> mpcs = new LinkedList<>();
|
|
if (mapitem.getMeso() > 0 && !mapitem.isPickedUp()) {
|
|
mpcs = getPartyMembersOnSameMap();
|
|
}
|
|
|
|
ScriptedItem itemScript = null;
|
|
mapitem.lockItem();
|
|
try {
|
|
if (mapitem.isPickedUp()) {
|
|
client.announce(MaplePacketCreator.showItemUnavailable());
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
return;
|
|
}
|
|
|
|
boolean isPet = petIndex > -1;
|
|
final byte[] pickupPacket = MaplePacketCreator.removeItemFromMap(mapitem.getObjectId(), (isPet) ? 5 : 2, this.getId(), isPet, petIndex);
|
|
|
|
Item mItem = mapitem.getItem();
|
|
boolean hasSpaceInventory = true;
|
|
if (mapitem.getItemId() == 4031865 || mapitem.getItemId() == 4031866 || mapitem.getMeso() > 0 || ii.isConsumeOnPickup(mapitem.getItemId()) || (hasSpaceInventory = MapleInventoryManipulator.checkSpace(client, mapitem.getItemId(), mItem.getQuantity(), mItem.getOwner()))) {
|
|
int mapId = this.getMapId();
|
|
|
|
if ((mapId > 209000000 && mapId < 209000016) || (mapId >= 990000500 && mapId <= 990000502)) {//happyville trees and guild PQ
|
|
if (!mapitem.isPlayerDrop() || mapitem.getDropper().getObjectId() == client.getPlayer().getObjectId()) {
|
|
if (mapitem.getMeso() > 0) {
|
|
if (!mpcs.isEmpty()) {
|
|
int mesosamm = mapitem.getMeso() / mpcs.size();
|
|
for (MapleCharacter partymem : mpcs) {
|
|
if (partymem.isLoggedinWorld()) {
|
|
partymem.gainMeso(mesosamm, true, true, false);
|
|
}
|
|
}
|
|
} else {
|
|
this.gainMeso(mapitem.getMeso(), true, true, false);
|
|
}
|
|
|
|
this.getMap().pickItemDrop(pickupPacket, mapitem);
|
|
} else if (mapitem.getItemId() == 4031865 || mapitem.getItemId() == 4031866) {
|
|
// Add NX to account, show effect and make item disappear
|
|
int nxGain = mapitem.getItemId() == 4031865 ? 100 : 250;
|
|
this.getCashShop().gainCash(1, nxGain);
|
|
|
|
showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(1) + " NX)", 300);
|
|
|
|
this.getMap().pickItemDrop(pickupPacket, mapitem);
|
|
} else if (MapleInventoryManipulator.addFromDrop(client, mItem, true)) {
|
|
this.getMap().pickItemDrop(pickupPacket, mapitem);
|
|
} else {
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
return;
|
|
}
|
|
} else {
|
|
client.announce(MaplePacketCreator.showItemUnavailable());
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
return;
|
|
}
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
return;
|
|
}
|
|
|
|
if (!this.needQuestItem(mapitem.getQuest(), mapitem.getItemId())) {
|
|
client.announce(MaplePacketCreator.showItemUnavailable());
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
return;
|
|
}
|
|
|
|
if (mapitem.getMeso() > 0) {
|
|
if (!mpcs.isEmpty()) {
|
|
int mesosamm = mapitem.getMeso() / mpcs.size();
|
|
for (MapleCharacter partymem : mpcs) {
|
|
if (partymem.isLoggedinWorld()) {
|
|
partymem.gainMeso(mesosamm, true, true, false);
|
|
}
|
|
}
|
|
} else {
|
|
this.gainMeso(mapitem.getMeso(), true, true, false);
|
|
}
|
|
} else if (mItem.getItemId() / 10000 == 243) {
|
|
ScriptedItem info = ii.getScriptedItemInfo(mItem.getItemId());
|
|
if (info != null && info.runOnPickup()) {
|
|
itemScript = info;
|
|
} else {
|
|
if (!MapleInventoryManipulator.addFromDrop(client, mItem, true)) {
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
return;
|
|
}
|
|
}
|
|
} else if (mapitem.getItemId() == 4031865 || mapitem.getItemId() == 4031866) {
|
|
// Add NX to account, show effect and make item disappear
|
|
int nxGain = mapitem.getItemId() == 4031865 ? 100 : 250;
|
|
this.getCashShop().gainCash(1, nxGain);
|
|
|
|
showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(1) + " NX)", 300);
|
|
} else if (useItem(mItem.getItemId())) {
|
|
if (mItem.getItemId() / 10000 == 238) {
|
|
this.getMonsterBook().addCard(client, mItem.getItemId());
|
|
}
|
|
} else if (MapleInventoryManipulator.addFromDrop(client, mItem, true)) {
|
|
} else {
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
return;
|
|
}
|
|
if (mItem.getItemId() == 4031868) {
|
|
updateAriantScore();
|
|
getMap().broadcastMessage(MaplePacketCreator.updateAriantPQRanking(this.getName(), this.getItemQuantity(4031868, false), false));
|
|
}
|
|
|
|
this.getMap().pickItemDrop(pickupPacket, mapitem);
|
|
} else if (!hasSpaceInventory) {
|
|
client.announce(MaplePacketCreator.getInventoryFull());
|
|
client.announce(MaplePacketCreator.getShowInventoryFull());
|
|
}
|
|
} finally {
|
|
mapitem.unlockItem();
|
|
}
|
|
|
|
if (itemScript != null) {
|
|
ItemScriptManager ism = ItemScriptManager.getInstance();
|
|
ism.runItemScript(client, itemScript);
|
|
}
|
|
}
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
}
|
|
|
|
public int countItem(int itemid) {
|
|
return inventory[ItemConstants.getInventoryType(itemid).ordinal()].countById(itemid);
|
|
}
|
|
|
|
public boolean canHold(int itemid) {
|
|
return canHold(itemid, 1);
|
|
}
|
|
|
|
public boolean canHold(int itemid, int quantity) {
|
|
return client.getAbstractPlayerInteraction().canHold(itemid, quantity);
|
|
}
|
|
|
|
public boolean isRidingBattleship() {
|
|
Integer bv = getBuffedValue(MapleBuffStat.MONSTER_RIDING);
|
|
return bv != null && bv.equals(Corsair.BATTLE_SHIP);
|
|
}
|
|
|
|
public void announceBattleshipHp() {
|
|
announce(MaplePacketCreator.skillCooldown(5221999, battleshipHp));
|
|
}
|
|
|
|
public void decreaseBattleshipHp(int decrease) {
|
|
this.battleshipHp -= decrease;
|
|
if (battleshipHp <= 0) {
|
|
Skill battleship = SkillFactory.getSkill(Corsair.BATTLE_SHIP);
|
|
int cooldown = battleship.getEffect(getSkillLevel(battleship)).getCooldown();
|
|
announce(MaplePacketCreator.skillCooldown(Corsair.BATTLE_SHIP, cooldown));
|
|
addCooldown(Corsair.BATTLE_SHIP, Server.getInstance().getCurrentTime(), (long) (cooldown * 1000));
|
|
removeCooldown(5221999);
|
|
cancelEffectFromBuffStat(MapleBuffStat.MONSTER_RIDING);
|
|
} else {
|
|
announceBattleshipHp();
|
|
addCooldown(5221999, 0, Long.MAX_VALUE);
|
|
}
|
|
}
|
|
|
|
public void decreaseReports() {
|
|
this.possibleReports--;
|
|
}
|
|
|
|
public void deleteGuild(int guildId) {
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try {
|
|
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET guildid = 0, guildrank = 5 WHERE guildid = ?")) {
|
|
ps.setInt(1, guildId);
|
|
ps.execute();
|
|
}
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM guilds WHERE guildid = ?")) {
|
|
ps.setInt(1, id);
|
|
ps.execute();
|
|
}
|
|
} finally {
|
|
con.close();
|
|
}
|
|
} catch (SQLException ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private void nextPendingRequest(MapleClient c) {
|
|
CharacterNameAndId pendingBuddyRequest = c.getPlayer().getBuddylist().pollPendingRequest();
|
|
if (pendingBuddyRequest != null) {
|
|
c.announce(MaplePacketCreator.requestBuddylistAdd(pendingBuddyRequest.getId(), c.getPlayer().getId(), pendingBuddyRequest.getName()));
|
|
}
|
|
}
|
|
|
|
private void notifyRemoteChannel(MapleClient c, int remoteChannel, int otherCid, BuddyList.BuddyOperation operation) {
|
|
MapleCharacter player = c.getPlayer();
|
|
if (remoteChannel != -1) {
|
|
c.getWorldServer().buddyChanged(otherCid, player.getId(), player.getName(), c.getChannel(), operation);
|
|
}
|
|
}
|
|
|
|
public void deleteBuddy(int otherCid) {
|
|
BuddyList bl = getBuddylist();
|
|
|
|
if (bl.containsVisible(otherCid)) {
|
|
notifyRemoteChannel(client, getWorldServer().find(otherCid), otherCid, BuddyList.BuddyOperation.DELETED);
|
|
}
|
|
bl.remove(otherCid);
|
|
client.announce(MaplePacketCreator.updateBuddylist(getBuddylist().getBuddies()));
|
|
nextPendingRequest(client);
|
|
}
|
|
|
|
public static boolean deleteCharFromDB(MapleCharacter player, int senderAccId) {
|
|
int cid = player.getId();
|
|
if (!Server.getInstance().haveCharacterEntry(senderAccId, cid)) { // thanks zera (EpiphanyMS) for pointing a critical exploit with non-authored character deletion request
|
|
return false;
|
|
}
|
|
|
|
int accId = senderAccId, world = 0;
|
|
Connection con = null;
|
|
try {
|
|
con = DatabaseConnection.getConnection();
|
|
|
|
try (PreparedStatement ps = con.prepareStatement("SELECT world FROM characters WHERE id = ?")) {
|
|
ps.setInt(1, cid);
|
|
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
if (rs.next()) {
|
|
world = rs.getInt("world");
|
|
}
|
|
}
|
|
}
|
|
|
|
try (PreparedStatement ps = con.prepareStatement("SELECT buddyid FROM buddies WHERE characterid = ?")) {
|
|
ps.setInt(1, cid);
|
|
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
while (rs.next()) {
|
|
int buddyid = rs.getInt("buddyid");
|
|
MapleCharacter buddy = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterById(buddyid);
|
|
|
|
if (buddy != null) {
|
|
buddy.deleteBuddy(cid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM buddies WHERE characterid = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
|
|
try (PreparedStatement ps = con.prepareStatement("SELECT threadid FROM bbs_threads WHERE postercid = ?")) {
|
|
ps.setInt(1, cid);
|
|
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
while (rs.next()) {
|
|
int tid = rs.getInt("threadid");
|
|
|
|
try (PreparedStatement ps2 = con.prepareStatement("DELETE FROM bbs_replies WHERE threadid = ?")) {
|
|
ps2.setInt(1, tid);
|
|
ps2.executeUpdate();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM bbs_threads WHERE postercid = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
|
|
try (PreparedStatement ps = con.prepareStatement("SELECT id, guildid, guildrank, name, allianceRank FROM characters WHERE id = ? AND accountid = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.setInt(2, accId);
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
if (rs.next() && rs.getInt("guildid") > 0) {
|
|
Server.getInstance().deleteGuildCharacter(new MapleGuildCharacter(player, cid, 0, rs.getString("name"), (byte) -1, (byte) -1, 0, rs.getInt("guildrank"), rs.getInt("guildid"), false, rs.getInt("allianceRank")));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (con.isClosed()) {
|
|
con = DatabaseConnection.getConnection(); //wtf tho
|
|
}
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM wishlists WHERE charid = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM cooldowns WHERE charid = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM playerdiseases WHERE charid = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM area_info WHERE charid = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM monsterbook WHERE charid = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM characters WHERE id = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM famelog WHERE characterid_to = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
|
|
try (PreparedStatement ps = con.prepareStatement("SELECT inventoryitemid, petid FROM inventoryitems WHERE characterid = ?")) {
|
|
ps.setInt(1, cid);
|
|
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
while (rs.next()) {
|
|
int inventoryitemid = rs.getInt("inventoryitemid");
|
|
|
|
try (PreparedStatement ps2 = con.prepareStatement("SELECT ringid FROM inventoryequipment WHERE inventoryitemid = ?")) {
|
|
ps2.setInt(1, inventoryitemid);
|
|
|
|
try (ResultSet rs2 = ps2.executeQuery()) {
|
|
while (rs2.next()) {
|
|
int ringid = rs2.getInt("ringid");
|
|
|
|
if (ringid > -1) {
|
|
try (PreparedStatement ps3 = con.prepareStatement("DELETE FROM rings WHERE id = ?")) {
|
|
ps3.setInt(1, ringid);
|
|
ps3.executeUpdate();
|
|
}
|
|
|
|
MapleCashidGenerator.freeCashId(ringid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
try (PreparedStatement ps2 = con.prepareStatement("DELETE FROM inventoryequipment WHERE inventoryitemid = ?")) {
|
|
ps2.setInt(1, inventoryitemid);
|
|
ps2.executeUpdate();
|
|
}
|
|
|
|
int petid = rs.getInt("petid");
|
|
if (petid > -1) {
|
|
try (PreparedStatement ps2 = con.prepareStatement("DELETE FROM pets WHERE petid = ?")) {
|
|
ps2.setInt(1, petid);
|
|
ps2.executeUpdate();
|
|
}
|
|
MapleCashidGenerator.freeCashId(petid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
deleteQuestProgressWhereCharacterId(con, cid);
|
|
|
|
try (PreparedStatement ps = con.prepareStatement("SELECT id FROM mts_cart WHERE cid = ?")) {
|
|
ps.setInt(1, cid);
|
|
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
while (rs.next()) {
|
|
int mtsid = rs.getInt("id");
|
|
|
|
try (PreparedStatement ps2 = con.prepareStatement("DELETE FROM mts_items WHERE id = ?")) {
|
|
ps2.setInt(1, mtsid);
|
|
ps2.executeUpdate();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM mts_cart WHERE cid = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
|
|
String[] toDel = {"famelog", "inventoryitems", "keymap", "queststatus", "savedlocations", "trocklocations", "skillmacros", "skills", "eventstats", "server_queue"};
|
|
for (String s : toDel) {
|
|
MapleCharacter.deleteWhereCharacterId(con, "DELETE FROM `" + s + "` WHERE characterid = ?", cid);
|
|
}
|
|
|
|
con.close();
|
|
Server.getInstance().deleteCharacterEntry(accId, cid);
|
|
return true;
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static void deleteQuestProgressWhereCharacterId(Connection con, int cid) throws SQLException {
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM medalmaps WHERE characterid = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM questprogress WHERE characterid = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
|
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM queststatus WHERE characterid = ?")) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
}
|
|
|
|
private void deleteWhereCharacterId(Connection con, String sql) throws SQLException {
|
|
try (PreparedStatement ps = con.prepareStatement(sql)) {
|
|
ps.setInt(1, id);
|
|
ps.executeUpdate();
|
|
}
|
|
}
|
|
|
|
public static void deleteWhereCharacterId(Connection con, String sql, int cid) throws SQLException {
|
|
try (PreparedStatement ps = con.prepareStatement(sql)) {
|
|
ps.setInt(1, cid);
|
|
ps.executeUpdate();
|
|
}
|
|
}
|
|
|
|
private void stopChairTask() {
|
|
chrLock.lock();
|
|
try {
|
|
if (chairRecoveryTask != null) {
|
|
chairRecoveryTask.cancel(false);
|
|
chairRecoveryTask = null;
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
private static Pair<Integer, Pair<Integer, Integer>> getChairTaskIntervalRate(int maxhp, int maxmp) {
|
|
float toHeal = Math.max(maxhp, maxmp);
|
|
float maxDuration = ServerConstants.CHAIR_EXTRA_HEAL_MAX_DELAY * 1000;
|
|
|
|
int rate = 0;
|
|
int minRegen = 1, maxRegen = (256 * ServerConstants.CHAIR_EXTRA_HEAL_MULTIPLIER) - 1, midRegen = 1;
|
|
while (minRegen < maxRegen) {
|
|
midRegen = (int) ((minRegen + maxRegen) * 0.94);
|
|
|
|
float procs = toHeal / midRegen;
|
|
float newRate = maxDuration / procs;
|
|
rate = (int) newRate;
|
|
|
|
if (newRate < 420) {
|
|
minRegen = (int) (1.2 * midRegen);
|
|
} else if (newRate > 5000) {
|
|
maxRegen = (int) (0.8 * midRegen);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
float procs = maxDuration / rate;
|
|
int hpRegen, mpRegen;
|
|
if (maxhp > maxmp) {
|
|
hpRegen = midRegen;
|
|
mpRegen = (int) Math.ceil(maxmp / procs);
|
|
} else {
|
|
hpRegen = (int) Math.ceil(maxhp / procs);
|
|
mpRegen = midRegen;
|
|
}
|
|
|
|
return new Pair<>(rate, new Pair<>(hpRegen, mpRegen));
|
|
}
|
|
|
|
private void updateChairHealStats() {
|
|
statRlock.lock();
|
|
try {
|
|
if (localchairrate != -1) {
|
|
return;
|
|
}
|
|
} finally {
|
|
statRlock.unlock();
|
|
}
|
|
|
|
effLock.lock();
|
|
statWlock.lock();
|
|
try {
|
|
Pair<Integer, Pair<Integer, Integer>> p = getChairTaskIntervalRate(localmaxhp, localmaxmp);
|
|
|
|
localchairrate = p.getLeft();
|
|
localchairhp = p.getRight().getLeft();
|
|
localchairmp = p.getRight().getRight();
|
|
} finally {
|
|
statWlock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
private void startChairTask() {
|
|
if (chair.get() == 0) {
|
|
return;
|
|
}
|
|
|
|
int healInterval;
|
|
effLock.lock();
|
|
try {
|
|
updateChairHealStats();
|
|
healInterval = localchairrate;
|
|
} finally {
|
|
effLock.unlock();
|
|
}
|
|
|
|
chrLock.lock();
|
|
try {
|
|
if (chairRecoveryTask != null) {
|
|
stopChairTask();
|
|
}
|
|
|
|
chairRecoveryTask = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
updateChairHealStats();
|
|
final int healHP = localchairhp;
|
|
final int healMP = localchairmp;
|
|
|
|
if (MapleCharacter.this.getHp() < localmaxhp) {
|
|
byte recHP = (byte) (healHP / ServerConstants.CHAIR_EXTRA_HEAL_MULTIPLIER);
|
|
|
|
client.announce(MaplePacketCreator.showOwnRecovery(recHP));
|
|
getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showRecovery(id, recHP), false);
|
|
} else if (MapleCharacter.this.getMp() >= localmaxmp) {
|
|
stopChairTask(); // optimizing schedule management when player is already with full pool.
|
|
}
|
|
|
|
addMPHP(healHP, healMP);
|
|
}
|
|
}, healInterval, healInterval);
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
private void stopExtraTask() {
|
|
chrLock.lock();
|
|
try {
|
|
if (extraRecoveryTask != null) {
|
|
extraRecoveryTask.cancel(false);
|
|
extraRecoveryTask = null;
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
private void startExtraTask(final byte healHP, final byte healMP, final short healInterval) {
|
|
chrLock.lock();
|
|
try {
|
|
startExtraTaskInternal(healHP, healMP, healInterval);
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
private void startExtraTaskInternal(final byte healHP, final byte healMP, final short healInterval) {
|
|
extraRecInterval = healInterval;
|
|
|
|
extraRecoveryTask = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (getBuffSource(MapleBuffStat.HPREC) == -1 && getBuffSource(MapleBuffStat.MPREC) == -1) {
|
|
stopExtraTask();
|
|
return;
|
|
}
|
|
|
|
if (MapleCharacter.this.getHp() < localmaxhp) {
|
|
if (healHP > 0) {
|
|
client.announce(MaplePacketCreator.showOwnRecovery(healHP));
|
|
getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showRecovery(id, healHP), false);
|
|
}
|
|
}
|
|
|
|
addMPHP(healHP, healMP);
|
|
}
|
|
}, healInterval, healInterval);
|
|
}
|
|
|
|
public void disableDoorSpawn() {
|
|
canDoor = false;
|
|
|
|
Runnable r = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
canDoor = true;
|
|
}
|
|
};
|
|
|
|
client.getChannelServer().registerOverallAction(mapid, r, 5000);
|
|
}
|
|
|
|
public void disbandGuild() {
|
|
if (guildid < 1 || guildRank != 1) {
|
|
return;
|
|
}
|
|
try {
|
|
Server.getInstance().disbandGuild(guildid);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void dispel() {
|
|
if (!(ServerConstants.USE_UNDISPEL_HOLY_SHIELD && this.isActiveBuffedValue(Bishop.HOLY_SHIELD))) {
|
|
List<MapleBuffStatValueHolder> mbsvhList = getAllStatups();
|
|
for (MapleBuffStatValueHolder mbsvh : mbsvhList) {
|
|
if (mbsvh.effect.isSkill()) {
|
|
if (mbsvh.effect.getBuffSourceId() != Aran.COMBO_ABILITY) { // check discovered thanks to Croosade dev team
|
|
cancelEffect(mbsvh.effect, false, mbsvh.startTime);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public final boolean hasDisease(final MapleDisease dis) {
|
|
chrLock.lock();
|
|
try {
|
|
return diseases.containsKey(dis);
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public final int getDiseasesSize() {
|
|
chrLock.lock();
|
|
try {
|
|
return diseases.size();
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public Map<MapleDisease, Pair<Long, MobSkill>> getAllDiseases() {
|
|
chrLock.lock();
|
|
try {
|
|
long curtime = Server.getInstance().getCurrentTime();
|
|
Map<MapleDisease, Pair<Long, MobSkill>> ret = new LinkedHashMap<>();
|
|
|
|
for (Entry<MapleDisease, Long> de : diseaseExpires.entrySet()) {
|
|
Pair<MapleDiseaseValueHolder, MobSkill> dee = diseases.get(de.getKey());
|
|
MapleDiseaseValueHolder mdvh = dee.getLeft();
|
|
|
|
ret.put(de.getKey(), new Pair<>(mdvh.length - (curtime - mdvh.startTime), dee.getRight()));
|
|
}
|
|
|
|
return ret;
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void silentApplyDiseases(Map<MapleDisease, Pair<Long, MobSkill>> diseaseMap) {
|
|
chrLock.lock();
|
|
try {
|
|
long curTime = Server.getInstance().getCurrentTime();
|
|
|
|
for (Entry<MapleDisease, Pair<Long, MobSkill>> di : diseaseMap.entrySet()) {
|
|
long expTime = curTime + di.getValue().getLeft();
|
|
|
|
diseaseExpires.put(di.getKey(), expTime);
|
|
diseases.put(di.getKey(), new Pair<>(new MapleDiseaseValueHolder(curTime, di.getValue().getLeft()), di.getValue().getRight()));
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void announceDiseases() {
|
|
Set<Entry<MapleDisease, Pair<MapleDiseaseValueHolder, MobSkill>>> chrDiseases;
|
|
|
|
chrLock.lock();
|
|
try {
|
|
// Poison damage visibility and diseases status visibility, extended through map transitions thanks to Ronan
|
|
if (!this.isLoggedinWorld()) {
|
|
return;
|
|
}
|
|
|
|
chrDiseases = new LinkedHashSet<>(diseases.entrySet());
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
|
|
for (Entry<MapleDisease, Pair<MapleDiseaseValueHolder, MobSkill>> di : chrDiseases) {
|
|
MapleDisease disease = di.getKey();
|
|
MobSkill skill = di.getValue().getRight();
|
|
final List<Pair<MapleDisease, Integer>> debuff = Collections.singletonList(new Pair<>(disease, Integer.valueOf(skill.getX())));
|
|
|
|
if (disease != MapleDisease.SLOW) {
|
|
map.broadcastMessage(MaplePacketCreator.giveForeignDebuff(id, debuff, skill));
|
|
} else {
|
|
map.broadcastMessage(MaplePacketCreator.giveForeignSlowDebuff(id, debuff, skill));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void giveDebuff(final MapleDisease disease, MobSkill skill) {
|
|
if (!hasDisease(disease) && getDiseasesSize() < 2) {
|
|
if (!(disease == MapleDisease.SEDUCE || disease == MapleDisease.STUN)) {
|
|
if (isActiveBuffedValue(Bishop.HOLY_SHIELD)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
chrLock.lock();
|
|
try {
|
|
long curTime = Server.getInstance().getCurrentTime();
|
|
diseaseExpires.put(disease, curTime + skill.getDuration());
|
|
diseases.put(disease, new Pair<>(new MapleDiseaseValueHolder(curTime, skill.getDuration()), skill));
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
|
|
if (disease == MapleDisease.SEDUCE && chair.get() != 0) {
|
|
sitChair(0);
|
|
}
|
|
|
|
final List<Pair<MapleDisease, Integer>> debuff = Collections.singletonList(new Pair<>(disease, Integer.valueOf(skill.getX())));
|
|
client.announce(MaplePacketCreator.giveDebuff(debuff, skill));
|
|
|
|
if (disease != MapleDisease.SLOW) {
|
|
map.broadcastMessage(this, MaplePacketCreator.giveForeignDebuff(id, debuff, skill), false);
|
|
} else {
|
|
map.broadcastMessage(this, MaplePacketCreator.giveForeignSlowDebuff(id, debuff, skill), false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void dispelDebuff(MapleDisease debuff) {
|
|
if (hasDisease(debuff)) {
|
|
long mask = debuff.getValue();
|
|
announce(MaplePacketCreator.cancelDebuff(mask));
|
|
|
|
if (debuff != MapleDisease.SLOW) {
|
|
map.broadcastMessage(this, MaplePacketCreator.cancelForeignDebuff(id, mask), false);
|
|
} else {
|
|
map.broadcastMessage(this, MaplePacketCreator.cancelForeignSlowDebuff(id), false);
|
|
}
|
|
|
|
chrLock.lock();
|
|
try {
|
|
diseases.remove(debuff);
|
|
diseaseExpires.remove(debuff);
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void dispelDebuffs() {
|
|
dispelDebuff(MapleDisease.CURSE);
|
|
dispelDebuff(MapleDisease.DARKNESS);
|
|
dispelDebuff(MapleDisease.POISON);
|
|
dispelDebuff(MapleDisease.SEAL);
|
|
dispelDebuff(MapleDisease.WEAKEN);
|
|
dispelDebuff(MapleDisease.SLOW);
|
|
}
|
|
|
|
public void cancelAllDebuffs() {
|
|
chrLock.lock();
|
|
try {
|
|
diseases.clear();
|
|
diseaseExpires.clear();
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void dispelSkill(int skillid) {
|
|
List<MapleBuffStatValueHolder> allBuffs = getAllStatups();
|
|
for (MapleBuffStatValueHolder mbsvh : allBuffs) {
|
|
if (skillid == 0) {
|
|
if (mbsvh.effect.isSkill() && (mbsvh.effect.getSourceId() % 10000000 == 1004 || dispelSkills(mbsvh.effect.getSourceId()))) {
|
|
cancelEffect(mbsvh.effect, false, mbsvh.startTime);
|
|
}
|
|
} else if (mbsvh.effect.isSkill() && mbsvh.effect.getSourceId() == skillid) {
|
|
cancelEffect(mbsvh.effect, false, mbsvh.startTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static boolean dispelSkills(int skillid) {
|
|
switch (skillid) {
|
|
case DarkKnight.BEHOLDER:
|
|
case FPArchMage.ELQUINES:
|
|
case ILArchMage.IFRIT:
|
|
case Priest.SUMMON_DRAGON:
|
|
case Bishop.BAHAMUT:
|
|
case Ranger.PUPPET:
|
|
case Ranger.SILVER_HAWK:
|
|
case Sniper.PUPPET:
|
|
case Sniper.GOLDEN_EAGLE:
|
|
case Hermit.SHADOW_PARTNER:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public void changeFaceExpression(int emote) {
|
|
long timeNow = Server.getInstance().getCurrentTime();
|
|
if (timeNow - lastExpression > 2000) {
|
|
lastExpression = timeNow;
|
|
client.getChannelServer().registerFaceExpression(map, this, emote);
|
|
}
|
|
}
|
|
|
|
private void doHurtHp() {
|
|
if (!(this.getInventory(MapleInventoryType.EQUIPPED).findById(getMap().getHPDecProtect()) != null || buffMapProtection())) {
|
|
addHP(-getMap().getHPDec());
|
|
lastHpDec = Server.getInstance().getCurrentTime();
|
|
}
|
|
}
|
|
|
|
private void startHpDecreaseTask(long lastHpTask) {
|
|
hpDecreaseTask = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
doHurtHp();
|
|
}
|
|
}, ServerConstants.MAP_DAMAGE_OVERTIME_INTERVAL, ServerConstants.MAP_DAMAGE_OVERTIME_INTERVAL - lastHpTask);
|
|
}
|
|
|
|
public void resetHpDecreaseTask() {
|
|
if (hpDecreaseTask != null) {
|
|
hpDecreaseTask.cancel(false);
|
|
}
|
|
|
|
long lastHpTask = Server.getInstance().getCurrentTime() - lastHpDec;
|
|
startHpDecreaseTask((lastHpTask > ServerConstants.MAP_DAMAGE_OVERTIME_INTERVAL) ? ServerConstants.MAP_DAMAGE_OVERTIME_INTERVAL : lastHpTask);
|
|
}
|
|
|
|
public void dropMessage(String message) {
|
|
dropMessage(0, message);
|
|
}
|
|
|
|
public void dropMessage(int type, String message) {
|
|
client.announce(MaplePacketCreator.serverNotice(type, message));
|
|
}
|
|
|
|
public void enteredScript(String script, int mapid) {
|
|
if (!entered.containsKey(mapid)) {
|
|
entered.put(mapid, script);
|
|
}
|
|
}
|
|
|
|
public void equipChanged() {
|
|
getMap().broadcastUpdateCharLookMessage(this, this);
|
|
equipchanged = true;
|
|
updateLocalStats();
|
|
if (getMessenger() != null) {
|
|
getWorldServer().updateMessenger(getMessenger(), getName(), getWorld(), client.getChannel());
|
|
}
|
|
}
|
|
|
|
public void cancelDiseaseExpireTask() {
|
|
if (diseaseExpireTask != null) {
|
|
diseaseExpireTask.cancel(false);
|
|
diseaseExpireTask = null;
|
|
}
|
|
}
|
|
|
|
public void diseaseExpireTask() {
|
|
if (diseaseExpireTask == null) {
|
|
diseaseExpireTask = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Set<MapleDisease> toExpire = new LinkedHashSet<>();
|
|
|
|
chrLock.lock();
|
|
try {
|
|
long curTime = Server.getInstance().getCurrentTime();
|
|
|
|
for (Entry<MapleDisease, Long> de : diseaseExpires.entrySet()) {
|
|
if (de.getValue() < curTime) {
|
|
toExpire.add(de.getKey());
|
|
}
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
|
|
for (MapleDisease d : toExpire) {
|
|
dispelDebuff(d);
|
|
}
|
|
}
|
|
}, 1500);
|
|
}
|
|
}
|
|
|
|
public void cancelBuffExpireTask() {
|
|
if (buffExpireTask != null) {
|
|
buffExpireTask.cancel(false);
|
|
buffExpireTask = null;
|
|
}
|
|
}
|
|
|
|
public void buffExpireTask() {
|
|
if (buffExpireTask == null) {
|
|
buffExpireTask = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Set<Entry<Integer, Long>> es;
|
|
List<MapleBuffStatValueHolder> toCancel = new ArrayList<>();
|
|
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
es = new LinkedHashSet<>(buffExpires.entrySet());
|
|
|
|
long curTime = Server.getInstance().getCurrentTime();
|
|
for (Entry<Integer, Long> bel : es) {
|
|
if (curTime >= bel.getValue()) {
|
|
toCancel.add(buffEffects.get(bel.getKey()).entrySet().iterator().next().getValue()); //rofl
|
|
}
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
|
|
for (MapleBuffStatValueHolder mbsvh : toCancel) {
|
|
cancelEffect(mbsvh.effect, false, mbsvh.startTime);
|
|
}
|
|
}
|
|
}, 1500);
|
|
}
|
|
}
|
|
|
|
public void cancelSkillCooldownTask() {
|
|
if (skillCooldownTask != null) {
|
|
skillCooldownTask.cancel(false);
|
|
skillCooldownTask = null;
|
|
}
|
|
}
|
|
|
|
public void skillCooldownTask() {
|
|
if (skillCooldownTask == null) {
|
|
skillCooldownTask = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Set<Entry<Integer, MapleCoolDownValueHolder>> es;
|
|
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
es = new LinkedHashSet<>(coolDowns.entrySet());
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
|
|
long curTime = Server.getInstance().getCurrentTime();
|
|
for (Entry<Integer, MapleCoolDownValueHolder> bel : es) {
|
|
MapleCoolDownValueHolder mcdvh = bel.getValue();
|
|
if (curTime >= mcdvh.startTime + mcdvh.length) {
|
|
removeCooldown(mcdvh.skillId);
|
|
client.announce(MaplePacketCreator.skillCooldown(mcdvh.skillId, 0));
|
|
}
|
|
}
|
|
}
|
|
}, 1500);
|
|
}
|
|
}
|
|
|
|
public void cancelExpirationTask() {
|
|
if (itemExpireTask != null) {
|
|
itemExpireTask.cancel(false);
|
|
itemExpireTask = null;
|
|
}
|
|
}
|
|
|
|
public void expirationTask() {
|
|
if (itemExpireTask == null) {
|
|
itemExpireTask = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
boolean deletedCoupon = false;
|
|
|
|
long expiration, currenttime = System.currentTimeMillis();
|
|
Set<Skill> keys = getSkills().keySet();
|
|
for (Iterator<Skill> i = keys.iterator(); i.hasNext();) {
|
|
Skill key = i.next();
|
|
SkillEntry skill = getSkills().get(key);
|
|
if (skill.expiration != -1 && skill.expiration < currenttime) {
|
|
changeSkillLevel(key, (byte) -1, 0, -1);
|
|
}
|
|
}
|
|
|
|
List<Item> toberemove = new ArrayList<>();
|
|
for (MapleInventory inv : inventory) {
|
|
for (Item item : inv.list()) {
|
|
expiration = item.getExpiration();
|
|
|
|
if (expiration != -1 && (expiration < currenttime) && ((item.getFlag() & ItemConstants.LOCK) == ItemConstants.LOCK)) {
|
|
byte aids = item.getFlag();
|
|
aids &= ~(ItemConstants.LOCK);
|
|
item.setFlag(aids); //Probably need a check, else people can make expiring items into permanent items...
|
|
item.setExpiration(-1);
|
|
forceUpdateItem(item); //TEST :3
|
|
} else if (expiration != -1 && expiration < currenttime) {
|
|
if (!ItemConstants.isPet(item.getItemId())) {
|
|
client.announce(MaplePacketCreator.itemExpired(item.getItemId()));
|
|
toberemove.add(item);
|
|
if (ItemConstants.isRateCoupon(item.getItemId())) {
|
|
deletedCoupon = true;
|
|
}
|
|
} else {
|
|
if (ItemConstants.isExpirablePet(item.getItemId())) {
|
|
client.announce(MaplePacketCreator.itemExpired(item.getItemId()));
|
|
toberemove.add(item);
|
|
} else {
|
|
item.setExpiration(-1);
|
|
forceUpdateItem(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!toberemove.isEmpty()) {
|
|
for (Item item : toberemove) {
|
|
MapleInventoryManipulator.removeFromSlot(client, inv.getType(), item.getPosition(), item.getQuantity(), true);
|
|
}
|
|
|
|
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
|
for (Item item : toberemove) {
|
|
List<Integer> toadd = new ArrayList<>();
|
|
Pair<Integer, String> replace = ii.getReplaceOnExpire(item.getItemId());
|
|
if (replace.left > 0) {
|
|
toadd.add(replace.left);
|
|
if (!replace.right.isEmpty()) {
|
|
dropMessage(replace.right);
|
|
}
|
|
}
|
|
for (Integer itemid : toadd) {
|
|
MapleInventoryManipulator.addById(client, itemid, (short) 1);
|
|
}
|
|
}
|
|
|
|
toberemove.clear();
|
|
}
|
|
|
|
if (deletedCoupon) {
|
|
updateCouponRates();
|
|
}
|
|
}
|
|
}
|
|
}, 60000);
|
|
}
|
|
}
|
|
|
|
public enum FameStatus {
|
|
|
|
OK, NOT_TODAY, NOT_THIS_MONTH
|
|
}
|
|
|
|
public void forceUpdateItem(Item item) {
|
|
final List<ModifyInventory> mods = new LinkedList<>();
|
|
mods.add(new ModifyInventory(3, item));
|
|
mods.add(new ModifyInventory(0, item));
|
|
client.announce(MaplePacketCreator.modifyInventory(true, mods));
|
|
}
|
|
|
|
public void gainGachaExp() {
|
|
int expgain = 0;
|
|
int currentgexp = gachaexp.get();
|
|
if ((currentgexp + exp.get()) >= ExpTable.getExpNeededForLevel(level)) {
|
|
expgain += ExpTable.getExpNeededForLevel(level) - exp.get();
|
|
int nextneed = ExpTable.getExpNeededForLevel(level + 1);
|
|
if ((currentgexp - expgain) >= nextneed) {
|
|
expgain += nextneed;
|
|
}
|
|
this.gachaexp.set(currentgexp - expgain);
|
|
} else {
|
|
expgain = this.gachaexp.getAndSet(0);
|
|
}
|
|
gainExp(expgain, false, false);
|
|
updateSingleStat(MapleStat.GACHAEXP, this.gachaexp.get());
|
|
}
|
|
|
|
public void gainGachaExp(int gain) {
|
|
updateSingleStat(MapleStat.GACHAEXP, gachaexp.addAndGet(gain));
|
|
}
|
|
|
|
public void gainExp(int gain) {
|
|
gainExp(gain, true, true);
|
|
}
|
|
|
|
public void gainExp(int gain, boolean show, boolean inChat) {
|
|
gainExp(gain, show, inChat, true);
|
|
}
|
|
|
|
public void gainExp(int gain, boolean show, boolean inChat, boolean white) {
|
|
gainExp(gain, 0, show, inChat, white);
|
|
}
|
|
|
|
public void gainExp(int gain, int party, boolean show, boolean inChat, boolean white) {
|
|
if (hasDisease(MapleDisease.CURSE)) {
|
|
gain *= 0.5;
|
|
party *= 0.5;
|
|
}
|
|
|
|
if (gain < 0) {
|
|
gain = Integer.MAX_VALUE; // integer overflow, heh.
|
|
}
|
|
if (party < 0) {
|
|
party = Integer.MAX_VALUE; // integer overflow, heh.
|
|
}
|
|
int equip = (int) Math.min((long) (gain / 10) * pendantExp, Integer.MAX_VALUE);
|
|
|
|
long total = (long) gain + equip + party;
|
|
gainExpInternal(total, equip, party, show, inChat, white);
|
|
}
|
|
|
|
public void loseExp(int loss, boolean show, boolean inChat) {
|
|
loseExp(loss, show, inChat, true);
|
|
}
|
|
|
|
public void loseExp(int loss, boolean show, boolean inChat, boolean white) {
|
|
gainExpInternal(-loss, 0, 0, show, inChat, white);
|
|
}
|
|
|
|
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() && (allowExpGain || this.getEventInstance() != null)) {
|
|
long leftover = 0;
|
|
long nextExp = exp.get() + total;
|
|
|
|
if (nextExp > (long) Integer.MAX_VALUE) {
|
|
total = Integer.MAX_VALUE - exp.get();
|
|
leftover = nextExp - Integer.MAX_VALUE;
|
|
}
|
|
updateSingleStat(MapleStat.EXP, exp.addAndGet((int) total));
|
|
if (show && gain != 0) {
|
|
client.announce(MaplePacketCreator.getShowExpGain((int) Math.min(gain, Integer.MAX_VALUE), equip, party, inChat, white));
|
|
}
|
|
while (exp.get() >= ExpTable.getExpNeededForLevel(level)) {
|
|
levelUp(true);
|
|
if (level == getMaxLevel()) {
|
|
setExp(0);
|
|
updateSingleStat(MapleStat.EXP, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (leftover > 0) {
|
|
gainExpInternal(leftover, equip, party, false, inChat, white);
|
|
} else {
|
|
lastExpGainTime = System.currentTimeMillis();
|
|
}
|
|
}
|
|
}
|
|
|
|
private Pair<Integer, Integer> applyFame(int delta) {
|
|
petLock.lock();
|
|
try {
|
|
int newFame = fame + delta;
|
|
if (newFame < -30000) {
|
|
delta = -(30000 + fame);
|
|
} else if (newFame > 30000) {
|
|
delta = 30000 - fame;
|
|
}
|
|
|
|
fame += delta;
|
|
return new Pair<>(fame, delta);
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void gainFame(int delta) {
|
|
gainFame(delta, null, 0);
|
|
}
|
|
|
|
public boolean gainFame(int delta, MapleCharacter fromPlayer, int mode) {
|
|
Pair<Integer, Integer> fameRes = applyFame(delta);
|
|
delta = fameRes.getRight();
|
|
if (delta != 0) {
|
|
int thisFame = fameRes.getLeft();
|
|
updateSingleStat(MapleStat.FAME, thisFame);
|
|
|
|
if (fromPlayer != null) {
|
|
fromPlayer.announce(MaplePacketCreator.giveFameResponse(mode, getName(), thisFame));
|
|
announce(MaplePacketCreator.receiveFame(mode, fromPlayer.getName()));
|
|
} else {
|
|
announce(MaplePacketCreator.getShowFameGain(delta));
|
|
}
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public boolean canHoldMeso(int gain) { // thanks lucasziron found pointing out a need to check space availability for mesos on player transactions
|
|
long nextMeso = (long) meso.get() + gain;
|
|
return nextMeso <= Integer.MAX_VALUE;
|
|
}
|
|
|
|
public void gainMeso(int gain) {
|
|
gainMeso(gain, true, false, true);
|
|
}
|
|
|
|
public void gainMeso(int gain, boolean show) {
|
|
gainMeso(gain, show, false, false);
|
|
}
|
|
|
|
public void gainMeso(int gain, boolean show, boolean enableActions, boolean inChat) {
|
|
long nextMeso;
|
|
petLock.lock();
|
|
try {
|
|
nextMeso = (long) meso.get() + gain; // thanks Thora for pointing integer overflow here
|
|
if (nextMeso > Integer.MAX_VALUE) {
|
|
gain -= (nextMeso - Integer.MAX_VALUE);
|
|
} else if (nextMeso < 0) {
|
|
gain = -meso.get();
|
|
}
|
|
nextMeso = meso.addAndGet(gain);
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
|
|
if (gain != 0) {
|
|
updateSingleStat(MapleStat.MESO, (int) nextMeso, enableActions);
|
|
if (show) {
|
|
client.announce(MaplePacketCreator.getShowMesoGain(gain, inChat));
|
|
}
|
|
} else {
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
}
|
|
}
|
|
|
|
public void genericGuildMessage(int code) {
|
|
this.client.announce(MaplePacketCreator.genericGuildMessage((byte) code));
|
|
}
|
|
|
|
public int getAccountID() {
|
|
return accountid;
|
|
}
|
|
|
|
public List<PlayerCoolDownValueHolder> getAllCooldowns() {
|
|
List<PlayerCoolDownValueHolder> ret = new ArrayList<>();
|
|
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
for (MapleCoolDownValueHolder mcdvh : coolDowns.values()) {
|
|
ret.add(new PlayerCoolDownValueHolder(mcdvh.skillId, mcdvh.startTime, mcdvh.length));
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
public int getAllianceRank() {
|
|
return allianceRank;
|
|
}
|
|
|
|
public static String getAriantRoomLeaderName(int room) {
|
|
return ariantroomleader[room];
|
|
}
|
|
|
|
public static int getAriantSlotsRoom(int room) {
|
|
return ariantroomslot[room];
|
|
}
|
|
|
|
public int getBattleshipHp() {
|
|
return battleshipHp;
|
|
}
|
|
|
|
public BuddyList getBuddylist() {
|
|
return buddylist;
|
|
}
|
|
|
|
public static Map<String, String> getCharacterFromDatabase(String name) {
|
|
Map<String, String> character = new LinkedHashMap<>();
|
|
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
|
|
try (PreparedStatement ps = con.prepareStatement("SELECT `id`, `accountid`, `name` FROM `characters` WHERE `name` = ?")) {
|
|
ps.setString(1, name);
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
if (!rs.next()) {
|
|
rs.close();
|
|
ps.close();
|
|
return null;
|
|
}
|
|
|
|
for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
|
|
character.put(rs.getMetaData().getColumnLabel(i), rs.getString(i));
|
|
}
|
|
}
|
|
} finally {
|
|
con.close();
|
|
}
|
|
} catch (SQLException sqle) {
|
|
sqle.printStackTrace();
|
|
}
|
|
|
|
return character;
|
|
}
|
|
|
|
public Long getBuffedStarttime(MapleBuffStat effect) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
MapleBuffStatValueHolder mbsvh = effects.get(effect);
|
|
if (mbsvh == null) {
|
|
return null;
|
|
}
|
|
return Long.valueOf(mbsvh.startTime);
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public Integer getBuffedValue(MapleBuffStat effect) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
MapleBuffStatValueHolder mbsvh = effects.get(effect);
|
|
if (mbsvh == null) {
|
|
return null;
|
|
}
|
|
return Integer.valueOf(mbsvh.value);
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public int getBuffSource(MapleBuffStat stat) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
MapleBuffStatValueHolder mbsvh = effects.get(stat);
|
|
if (mbsvh == null) {
|
|
return -1;
|
|
}
|
|
return mbsvh.effect.getSourceId();
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public MapleStatEffect getBuffEffect(MapleBuffStat stat) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
MapleBuffStatValueHolder mbsvh = effects.get(stat);
|
|
if (mbsvh == null) {
|
|
return null;
|
|
} else {
|
|
return mbsvh.effect;
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public Set<Integer> getAvailableBuffs() {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
return new LinkedHashSet<>(buffEffects.keySet());
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
private List<MapleBuffStatValueHolder> getAllStatups() {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
List<MapleBuffStatValueHolder> ret = new ArrayList<>();
|
|
for (Map<MapleBuffStat, MapleBuffStatValueHolder> bel : buffEffects.values()) {
|
|
for (MapleBuffStatValueHolder mbsvh : bel.values()) {
|
|
ret.add(mbsvh);
|
|
}
|
|
}
|
|
return ret;
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public List<PlayerBuffValueHolder> getAllBuffs() { // buff values will be stored in an arbitrary order
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
long curtime = Server.getInstance().getCurrentTime();
|
|
|
|
Map<Integer, PlayerBuffValueHolder> ret = new LinkedHashMap<>();
|
|
for (Map<MapleBuffStat, MapleBuffStatValueHolder> bel : buffEffects.values()) {
|
|
for (MapleBuffStatValueHolder mbsvh : bel.values()) {
|
|
int srcid = mbsvh.effect.getBuffSourceId();
|
|
if (!ret.containsKey(srcid)) {
|
|
ret.put(srcid, new PlayerBuffValueHolder((int) (curtime - mbsvh.startTime), mbsvh.effect));
|
|
}
|
|
}
|
|
}
|
|
return new ArrayList<>(ret.values());
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public List<Pair<MapleBuffStat, Integer>> getAllActiveStatups() {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
List<Pair<MapleBuffStat, Integer>> ret = new ArrayList<>();
|
|
for (MapleBuffStat mbs : effects.keySet()) {
|
|
MapleBuffStatValueHolder mbsvh = effects.get(mbs);
|
|
ret.add(new Pair<>(mbs, mbsvh.value));
|
|
}
|
|
return ret;
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public boolean hasBuffFromSourceid(int sourceid) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
return buffEffects.containsKey(sourceid);
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
private List<Pair<MapleBuffStat, Integer>> getActiveStatupsFromSourceid(int sourceid) { // already under effLock & chrLock
|
|
List<Pair<MapleBuffStat, Integer>> ret = new ArrayList<>();
|
|
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> bel : buffEffects.get(sourceid).entrySet()) {
|
|
MapleBuffStat mbs = bel.getKey();
|
|
MapleBuffStatValueHolder mbsvh = effects.get(bel.getKey());
|
|
|
|
if (mbsvh != null) {
|
|
ret.add(new Pair<>(mbs, mbsvh.value));
|
|
} else {
|
|
ret.add(new Pair<>(mbs, 0));
|
|
}
|
|
}
|
|
|
|
Collections.sort(ret, new Comparator<Pair<MapleBuffStat, Integer>>() {
|
|
@Override
|
|
public int compare(Pair<MapleBuffStat, Integer> p1, Pair<MapleBuffStat, Integer> p2) {
|
|
return p1.getLeft().compareTo(p2.getLeft());
|
|
}
|
|
});
|
|
|
|
return ret;
|
|
}
|
|
|
|
private void addItemEffectHolder(Integer sourceid, long expirationtime, Map<MapleBuffStat, MapleBuffStatValueHolder> statups) {
|
|
buffEffects.put(sourceid, statups);
|
|
buffExpires.put(sourceid, expirationtime);
|
|
}
|
|
|
|
private boolean removeEffectFromItemEffectHolder(Integer sourceid, MapleBuffStat buffStat) {
|
|
Map<MapleBuffStat, MapleBuffStatValueHolder> lbe = buffEffects.get(sourceid);
|
|
|
|
if (lbe.remove(buffStat) != null) {
|
|
buffEffectsCount.put(buffStat, (byte) (buffEffectsCount.get(buffStat) - 1));
|
|
|
|
if (lbe.isEmpty()) {
|
|
buffEffects.remove(sourceid);
|
|
buffExpires.remove(sourceid);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void removeItemEffectHolder(Integer sourceid) {
|
|
Map<MapleBuffStat, MapleBuffStatValueHolder> be = buffEffects.remove(sourceid);
|
|
if (be != null) {
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> bei : be.entrySet()) {
|
|
buffEffectsCount.put(bei.getKey(), (byte) (buffEffectsCount.get(bei.getKey()) - 1));
|
|
}
|
|
}
|
|
|
|
buffExpires.remove(sourceid);
|
|
}
|
|
|
|
private void dropWorstEffectFromItemEffectHolder(MapleBuffStat mbs) {
|
|
Integer min = Integer.MAX_VALUE;
|
|
Integer srcid = -1;
|
|
for (Entry<Integer, Map<MapleBuffStat, MapleBuffStatValueHolder>> bpl : buffEffects.entrySet()) {
|
|
MapleBuffStatValueHolder mbsvh = bpl.getValue().get(mbs);
|
|
if (mbsvh != null) {
|
|
if (mbsvh.value < min) {
|
|
min = mbsvh.value;
|
|
srcid = bpl.getKey();
|
|
}
|
|
}
|
|
}
|
|
|
|
removeEffectFromItemEffectHolder(srcid, mbs);
|
|
}
|
|
|
|
private MapleBuffStatValueHolder fetchBestEffectFromItemEffectHolder(MapleBuffStat mbs) {
|
|
Pair<Integer, Integer> max = new Pair<>(Integer.MIN_VALUE, 0);
|
|
MapleBuffStatValueHolder mbsvh = null;
|
|
for (Entry<Integer, Map<MapleBuffStat, MapleBuffStatValueHolder>> bpl : buffEffects.entrySet()) {
|
|
MapleBuffStatValueHolder mbsvhi = bpl.getValue().get(mbs);
|
|
if (mbsvhi != null) {
|
|
if (mbsvhi.value > max.left) {
|
|
max = new Pair<>(mbsvhi.value, mbsvhi.effect.getStatups().size());
|
|
mbsvh = mbsvhi;
|
|
} else if (mbsvhi.value == max.left && mbsvhi.effect.getStatups().size() > max.right) {
|
|
max = new Pair<>(mbsvhi.value, mbsvhi.effect.getStatups().size());
|
|
mbsvh = mbsvhi;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mbsvh != null) {
|
|
effects.put(mbs, mbsvh);
|
|
}
|
|
return mbsvh;
|
|
}
|
|
|
|
private void extractBuffValue(int sourceid, MapleBuffStat stat) {
|
|
chrLock.lock();
|
|
try {
|
|
removeEffectFromItemEffectHolder(sourceid, stat);
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void debugListAllBuffs() {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
System.out.println("-------------------");
|
|
System.out.println("CACHED BUFFS: ");
|
|
for (Entry<Integer, Map<MapleBuffStat, MapleBuffStatValueHolder>> bpl : buffEffects.entrySet()) {
|
|
System.out.print(bpl.getKey() + ": ");
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> pble : bpl.getValue().entrySet()) {
|
|
System.out.print(pble.getKey().name() + pble.getValue().value + ", ");
|
|
}
|
|
System.out.println();
|
|
}
|
|
System.out.println("-------------------");
|
|
|
|
System.out.println("IN ACTION:");
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> bpl : effects.entrySet()) {
|
|
System.out.println(bpl.getKey().name() + " -> " + MapleItemInformationProvider.getInstance().getName(bpl.getValue().effect.getSourceId()));
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void debugListAllBuffsCount() {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
for (Entry<MapleBuffStat, Byte> mbsl : buffEffectsCount.entrySet()) {
|
|
System.out.println(mbsl.getKey().name() + " -> " + mbsl.getValue());
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void cancelAllBuffs(boolean softcancel) {
|
|
if (softcancel) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
cancelEffectFromBuffStat(MapleBuffStat.SUMMON);
|
|
cancelEffectFromBuffStat(MapleBuffStat.PUPPET);
|
|
cancelEffectFromBuffStat(MapleBuffStat.COMBO);
|
|
|
|
effects.clear();
|
|
|
|
for (Integer srcid : new ArrayList<>(buffEffects.keySet())) {
|
|
removeItemEffectHolder(srcid);
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
} else {
|
|
Map<MapleStatEffect, Long> mseBuffs = new LinkedHashMap<>();
|
|
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
for (Entry<Integer, Map<MapleBuffStat, MapleBuffStatValueHolder>> bpl : buffEffects.entrySet()) {
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> mbse : bpl.getValue().entrySet()) {
|
|
mseBuffs.put(mbse.getValue().effect, mbse.getValue().startTime);
|
|
}
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
|
|
for (Entry<MapleStatEffect, Long> mse : mseBuffs.entrySet()) {
|
|
cancelEffect(mse.getKey(), false, mse.getValue());
|
|
}
|
|
}
|
|
}
|
|
|
|
private void dropBuffStats(List<Pair<MapleBuffStat, MapleBuffStatValueHolder>> effectsToCancel) {
|
|
for (Pair<MapleBuffStat, MapleBuffStatValueHolder> cancelEffectCancelTasks : effectsToCancel) {
|
|
//boolean nestedCancel = false;
|
|
|
|
chrLock.lock();
|
|
try {
|
|
/*
|
|
if (buffExpires.get(cancelEffectCancelTasks.getRight().effect.getBuffSourceId()) != null) {
|
|
nestedCancel = true;
|
|
}*/
|
|
|
|
if (cancelEffectCancelTasks.getRight().bestApplied) {
|
|
fetchBestEffectFromItemEffectHolder(cancelEffectCancelTasks.getLeft());
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
|
|
/*
|
|
if (nestedCancel) {
|
|
this.cancelEffect(cancelEffectCancelTasks.getRight().effect, false, -1, false);
|
|
}*/
|
|
}
|
|
}
|
|
|
|
private List<Pair<MapleBuffStat, MapleBuffStatValueHolder>> deregisterBuffStats(Map<MapleBuffStat, MapleBuffStatValueHolder> stats) {
|
|
chrLock.lock();
|
|
try {
|
|
List<Pair<MapleBuffStat, MapleBuffStatValueHolder>> effectsToCancel = new ArrayList<>(stats.size());
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> stat : stats.entrySet()) {
|
|
int sourceid = stat.getValue().effect.getBuffSourceId();
|
|
|
|
if (!buffEffects.containsKey(sourceid)) {
|
|
buffExpires.remove(sourceid);
|
|
}
|
|
|
|
MapleBuffStat mbs = stat.getKey();
|
|
effectsToCancel.add(new Pair<>(mbs, stat.getValue()));
|
|
|
|
MapleBuffStatValueHolder mbsvh = effects.get(mbs);
|
|
if (mbsvh != null && mbsvh.effect.getBuffSourceId() == sourceid) {
|
|
mbsvh.bestApplied = true;
|
|
effects.remove(mbs);
|
|
|
|
if (mbs == MapleBuffStat.RECOVERY) {
|
|
if (recoveryTask != null) {
|
|
recoveryTask.cancel(false);
|
|
recoveryTask = null;
|
|
}
|
|
} else if (mbs == MapleBuffStat.SUMMON || mbs == MapleBuffStat.PUPPET) {
|
|
int summonId = mbsvh.effect.getSourceId();
|
|
|
|
MapleSummon summon = summons.get(summonId);
|
|
if (summon != null) {
|
|
getMap().broadcastMessage(MaplePacketCreator.removeSummon(summon, true), summon.getPosition());
|
|
getMap().removeMapObject(summon);
|
|
removeVisibleMapObject(summon);
|
|
|
|
summons.remove(summonId);
|
|
if (summon.isPuppet()) {
|
|
map.removePlayerPuppet(this);
|
|
} else if (summon.getSkill() == DarkKnight.BEHOLDER) {
|
|
if (beholderHealingSchedule != null) {
|
|
beholderHealingSchedule.cancel(false);
|
|
beholderHealingSchedule = null;
|
|
}
|
|
if (beholderBuffSchedule != null) {
|
|
beholderBuffSchedule.cancel(false);
|
|
beholderBuffSchedule = null;
|
|
}
|
|
}
|
|
}
|
|
} else if (mbs == MapleBuffStat.DRAGONBLOOD) {
|
|
dragonBloodSchedule.cancel(false);
|
|
dragonBloodSchedule = null;
|
|
} else if (mbs == MapleBuffStat.HPREC || mbs == MapleBuffStat.MPREC) {
|
|
if (mbs == MapleBuffStat.HPREC) {
|
|
extraHpRec = 0;
|
|
} else {
|
|
extraMpRec = 0;
|
|
}
|
|
|
|
if (extraRecoveryTask != null) {
|
|
extraRecoveryTask.cancel(false);
|
|
extraRecoveryTask = null;
|
|
}
|
|
|
|
if (extraHpRec != 0 || extraMpRec != 0) {
|
|
startExtraTaskInternal(extraHpRec, extraMpRec, extraRecInterval);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return effectsToCancel;
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void cancelEffect(int itemId) {
|
|
cancelEffect(ii.getItemEffect(itemId), false, -1);
|
|
}
|
|
|
|
public boolean cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime) {
|
|
effLock.lock();
|
|
try {
|
|
return cancelEffect(effect, overwrite, startTime, true);
|
|
} finally {
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
private void updateEffects(Set<MapleBuffStat> removedStats) {
|
|
chrLock.lock();
|
|
try {
|
|
Map<Integer, Pair<MapleStatEffect, Long>> retrievedEffects = new LinkedHashMap<>();
|
|
Map<MapleBuffStat, Pair<Integer, Integer>> maxStatups = new LinkedHashMap<>();
|
|
|
|
for (Entry<Integer, Map<MapleBuffStat, MapleBuffStatValueHolder>> bel : buffEffects.entrySet()) {
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> belv : bel.getValue().entrySet()) {
|
|
if (removedStats.contains(belv.getKey())) {
|
|
if (!retrievedEffects.containsKey(bel.getKey())) {
|
|
retrievedEffects.put(bel.getKey(), new Pair<>(belv.getValue().effect, belv.getValue().startTime));
|
|
}
|
|
|
|
Pair<Integer, Integer> thisStat = maxStatups.get(belv.getKey());
|
|
if (thisStat == null || belv.getValue().value > thisStat.getRight()) {
|
|
maxStatups.put(belv.getKey(), new Pair<>(bel.getKey(), belv.getValue().value));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Map<Integer, Pair<MapleStatEffect, Long>> bestEffects = new LinkedHashMap<>();
|
|
Set<MapleBuffStat> retrievedStats = new LinkedHashSet<>();
|
|
for (Entry<MapleBuffStat, Pair<Integer, Integer>> lmsee : maxStatups.entrySet()) {
|
|
if (isSingletonStatup(lmsee.getKey())) {
|
|
continue;
|
|
}
|
|
|
|
Integer srcid = lmsee.getValue().getLeft();
|
|
if (!bestEffects.containsKey(srcid)) {
|
|
Pair<MapleStatEffect, Long> msel = retrievedEffects.get(srcid);
|
|
|
|
bestEffects.put(srcid, msel);
|
|
for (Pair<MapleBuffStat, Integer> mbsi : msel.getLeft().getStatups()) {
|
|
retrievedStats.add(mbsi.getLeft());
|
|
}
|
|
}
|
|
}
|
|
|
|
propagateBuffEffectUpdates(bestEffects, retrievedStats);
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
private boolean cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime, boolean firstCancel) {
|
|
Set<MapleBuffStat> removedStats = new LinkedHashSet<>();
|
|
dropBuffStats(cancelEffectInternal(effect, overwrite, startTime, removedStats));
|
|
updateLocalStats();
|
|
updateEffects(removedStats);
|
|
|
|
return !removedStats.isEmpty();
|
|
}
|
|
|
|
private List<Pair<MapleBuffStat, MapleBuffStatValueHolder>> cancelEffectInternal(MapleStatEffect effect, boolean overwrite, long startTime, Set<MapleBuffStat> removedStats) {
|
|
Map<MapleBuffStat, MapleBuffStatValueHolder> buffstats = null;
|
|
MapleBuffStat ombs;
|
|
if (!overwrite) { // is removing the source effect, meaning every effect from this srcid is being purged
|
|
buffstats = extractCurrentBuffStats(effect);
|
|
} else if ((ombs = getSingletonStatupFromEffect(effect)) != null) { // removing all effects of a buff having non-shareable buff stat.
|
|
MapleBuffStatValueHolder mbsvh = effects.get(ombs);
|
|
if (mbsvh != null) {
|
|
buffstats = extractCurrentBuffStats(mbsvh.effect);
|
|
}
|
|
}
|
|
|
|
if (buffstats == null) { // all else, is dropping ALL current statups that uses same stats as the given effect
|
|
buffstats = extractLeastRelevantStatEffectsIfFull(effect);
|
|
}
|
|
|
|
if (effect.isMagicDoor()) {
|
|
MapleDoor destroyDoor = removePartyDoor(false);
|
|
|
|
if (destroyDoor != null) {
|
|
destroyDoor.getTarget().removeMapObject(destroyDoor.getAreaDoor());
|
|
destroyDoor.getTown().removeMapObject(destroyDoor.getTownDoor());
|
|
|
|
for (MapleCharacter chr : destroyDoor.getTarget().getCharacters()) {
|
|
destroyDoor.getAreaDoor().sendDestroyData(chr.getClient());
|
|
}
|
|
|
|
Collection<MapleCharacter> townChars = destroyDoor.getTown().getCharacters();
|
|
for (MapleCharacter chr : townChars) {
|
|
destroyDoor.getTownDoor().sendDestroyData(chr.getClient());
|
|
}
|
|
if (destroyDoor.getTownPortal().getId() == 0x80) {
|
|
for (MapleCharacter chr : townChars) {
|
|
MapleDoor door = chr.getMainTownDoor();
|
|
if (door != null) {
|
|
destroyDoor.getTownDoor().sendSpawnData(chr.getClient());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (effect.isMapChair()) {
|
|
stopChairTask();
|
|
}
|
|
|
|
List<Pair<MapleBuffStat, MapleBuffStatValueHolder>> toCancel = deregisterBuffStats(buffstats);
|
|
if (effect.isMonsterRiding()) {
|
|
if (effect.getSourceId() != Corsair.BATTLE_SHIP) {
|
|
this.getClient().getWorldServer().unregisterMountHunger(this);
|
|
this.getMount().setActive(false);
|
|
}
|
|
}
|
|
|
|
if (!overwrite) {
|
|
List<MapleBuffStat> cancelStats = new LinkedList<>();
|
|
|
|
chrLock.lock();
|
|
try {
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> mbsl : buffstats.entrySet()) {
|
|
cancelStats.add(mbsl.getKey());
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
|
|
for (MapleBuffStat mbs : cancelStats) {
|
|
removedStats.add(mbs);
|
|
}
|
|
cancelPlayerBuffs(cancelStats);
|
|
}
|
|
|
|
return toCancel;
|
|
}
|
|
|
|
public void cancelEffectFromBuffStat(MapleBuffStat stat) {
|
|
MapleBuffStatValueHolder effect;
|
|
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
effect = effects.get(stat);
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
if (effect != null) {
|
|
cancelEffect(effect.effect, false, -1);
|
|
}
|
|
}
|
|
|
|
public void cancelBuffStats(MapleBuffStat stat) {
|
|
effLock.lock();
|
|
try {
|
|
List<Pair<Integer, MapleBuffStatValueHolder>> cancelList = new LinkedList<>();
|
|
|
|
chrLock.lock();
|
|
try {
|
|
for (Entry<Integer, Map<MapleBuffStat, MapleBuffStatValueHolder>> bel : this.buffEffects.entrySet()) {
|
|
MapleBuffStatValueHolder beli = bel.getValue().get(stat);
|
|
if (beli != null) {
|
|
cancelList.add(new Pair<>(bel.getKey(), beli));
|
|
}
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
|
|
Map<MapleBuffStat, MapleBuffStatValueHolder> buffStatList = new LinkedHashMap<>();
|
|
for (Pair<Integer, MapleBuffStatValueHolder> p : cancelList) {
|
|
buffStatList.put(stat, p.getRight());
|
|
extractBuffValue(p.getLeft(), stat);
|
|
dropBuffStats(deregisterBuffStats(buffStatList));
|
|
}
|
|
} finally {
|
|
effLock.unlock();
|
|
}
|
|
|
|
cancelPlayerBuffs(Arrays.asList(stat));
|
|
}
|
|
|
|
private Map<MapleBuffStat, MapleBuffStatValueHolder> extractCurrentBuffStats(MapleStatEffect effect) {
|
|
chrLock.lock();
|
|
try {
|
|
Map<MapleBuffStat, MapleBuffStatValueHolder> stats = new LinkedHashMap<>();
|
|
Map<MapleBuffStat, MapleBuffStatValueHolder> buffList = buffEffects.remove(effect.getBuffSourceId());
|
|
|
|
if (buffList != null) {
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> stateffect : buffList.entrySet()) {
|
|
stats.put(stateffect.getKey(), stateffect.getValue());
|
|
buffEffectsCount.put(stateffect.getKey(), (byte) (buffEffectsCount.get(stateffect.getKey()) - 1));
|
|
}
|
|
}
|
|
|
|
return stats;
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
private Map<MapleBuffStat, MapleBuffStatValueHolder> extractLeastRelevantStatEffectsIfFull(MapleStatEffect effect) {
|
|
Map<MapleBuffStat, MapleBuffStatValueHolder> extractedStatBuffs = new LinkedHashMap<>();
|
|
|
|
chrLock.lock();
|
|
try {
|
|
Map<MapleBuffStat, Byte> stats = new LinkedHashMap<>();
|
|
Map<MapleBuffStat, MapleBuffStatValueHolder> minStatBuffs = new LinkedHashMap<>();
|
|
|
|
for (Entry<Integer, Map<MapleBuffStat, MapleBuffStatValueHolder>> mbsvhi : buffEffects.entrySet()) {
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> mbsvhe : mbsvhi.getValue().entrySet()) {
|
|
MapleBuffStat mbs = mbsvhe.getKey();
|
|
Byte b = stats.get(mbs);
|
|
|
|
if (b != null) {
|
|
stats.put(mbs, (byte) (b + 1));
|
|
if (mbsvhe.getValue().value < minStatBuffs.get(mbs).value) {
|
|
minStatBuffs.put(mbs, mbsvhe.getValue());
|
|
}
|
|
} else {
|
|
stats.put(mbs, (byte) 1);
|
|
minStatBuffs.put(mbs, mbsvhe.getValue());
|
|
}
|
|
}
|
|
}
|
|
|
|
Set<MapleBuffStat> effectStatups = new LinkedHashSet<>();
|
|
for (Pair<MapleBuffStat, Integer> efstat : effect.getStatups()) {
|
|
effectStatups.add(efstat.getLeft());
|
|
}
|
|
|
|
for (Entry<MapleBuffStat, Byte> it : stats.entrySet()) {
|
|
boolean uniqueBuff = isSingletonStatup(it.getKey());
|
|
|
|
if (it.getValue() >= (!uniqueBuff ? ServerConstants.MAX_MONITORED_BUFFSTATS : 1) && effectStatups.contains(it.getKey())) {
|
|
MapleBuffStatValueHolder mbsvh = minStatBuffs.get(it.getKey());
|
|
|
|
Map<MapleBuffStat, MapleBuffStatValueHolder> lpbe = buffEffects.get(mbsvh.effect.getBuffSourceId());
|
|
lpbe.remove(it.getKey());
|
|
buffEffectsCount.put(it.getKey(), (byte) (buffEffectsCount.get(it.getKey()) - 1));
|
|
|
|
if (lpbe.isEmpty()) {
|
|
buffEffects.remove(mbsvh.effect.getBuffSourceId());
|
|
}
|
|
extractedStatBuffs.put(it.getKey(), mbsvh);
|
|
}
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
|
|
return extractedStatBuffs;
|
|
}
|
|
|
|
private void propagateBuffEffectUpdates(Map<Integer, Pair<MapleStatEffect, Long>> retrievedEffects, Set<MapleBuffStat> retrievedStats) {
|
|
if (retrievedStats.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
Map<MapleBuffStat, Pair<Integer, MapleStatEffect>> maxBuffValue = new LinkedHashMap<>();
|
|
for (MapleBuffStat mbs : retrievedStats) {
|
|
MapleBuffStatValueHolder mbsvh = effects.get(mbs);
|
|
if (mbsvh != null) {
|
|
retrievedEffects.put(mbsvh.effect.getBuffSourceId(), new Pair<>(mbsvh.effect, mbsvh.startTime));
|
|
}
|
|
|
|
maxBuffValue.put(mbs, new Pair<>(Integer.MIN_VALUE, (MapleStatEffect) null));
|
|
}
|
|
|
|
Map<MapleStatEffect, Pair<Integer, Integer>> updateEffects = new LinkedHashMap<>();
|
|
|
|
List<MapleStatEffect> recalcMseList = new LinkedList<>();
|
|
for (Entry<Integer, Pair<MapleStatEffect, Long>> re : retrievedEffects.entrySet()) {
|
|
recalcMseList.add(re.getValue().getLeft());
|
|
}
|
|
|
|
boolean mageJob = this.getJobStyle() == MapleJob.MAGICIAN;
|
|
do {
|
|
List<MapleStatEffect> mseList = recalcMseList;
|
|
recalcMseList = new LinkedList<>();
|
|
|
|
for (MapleStatEffect mse : mseList) {
|
|
int mseAmount = 0;
|
|
int maxEffectiveStatup = Integer.MIN_VALUE;
|
|
for (Pair<MapleBuffStat, Integer> st : mse.getStatups()) {
|
|
MapleBuffStat mbs = st.getLeft();
|
|
|
|
boolean relevantStatup = true;
|
|
if (mbs == MapleBuffStat.WATK) { // not relevant for mages
|
|
if (mageJob) {
|
|
relevantStatup = false;
|
|
}
|
|
} else if (mbs == MapleBuffStat.MATK) { // not relevant for non-mages
|
|
if (!mageJob) {
|
|
relevantStatup = false;
|
|
}
|
|
}
|
|
|
|
Pair<Integer, MapleStatEffect> mbv = maxBuffValue.get(mbs);
|
|
if (mbv == null) {
|
|
continue;
|
|
}
|
|
|
|
if (mbv.getLeft() < st.getRight()) {
|
|
MapleStatEffect msbe = mbv.getRight();
|
|
if (msbe != null) {
|
|
recalcMseList.add(msbe);
|
|
}
|
|
|
|
maxBuffValue.put(mbs, new Pair<>(st.getRight(), mse));
|
|
|
|
if (relevantStatup) {
|
|
if (maxEffectiveStatup < st.getRight()) {
|
|
maxEffectiveStatup = st.getRight();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (relevantStatup) {
|
|
mseAmount += st.getRight();
|
|
}
|
|
}
|
|
|
|
updateEffects.put(mse, new Pair<>(maxEffectiveStatup, mseAmount));
|
|
}
|
|
} while (!recalcMseList.isEmpty());
|
|
|
|
List<Pair<MapleStatEffect, Pair<Integer, Integer>>> updateEffectsList = new ArrayList<>();
|
|
for (Entry<MapleStatEffect, Pair<Integer, Integer>> ue : updateEffects.entrySet()) {
|
|
updateEffectsList.add(new Pair<>(ue.getKey(), ue.getValue()));
|
|
}
|
|
|
|
Collections.sort(updateEffectsList, new Comparator<Pair<MapleStatEffect, Pair<Integer, Integer>>>() {
|
|
@Override
|
|
public int compare(Pair<MapleStatEffect, Pair<Integer, Integer>> o1, Pair<MapleStatEffect, Pair<Integer, Integer>> o2) {
|
|
if (o1.getRight().getLeft().equals(o2.getRight().getLeft())) {
|
|
return o1.getRight().getRight().compareTo(o2.getRight().getRight());
|
|
} else {
|
|
return o1.getRight().getLeft().compareTo(o2.getRight().getLeft());
|
|
}
|
|
}
|
|
});
|
|
|
|
List<Pair<Integer, Pair<MapleStatEffect, Long>>> toUpdateEffects = new LinkedList<>();
|
|
for (Pair<MapleStatEffect, Pair<Integer, Integer>> msep : updateEffectsList) {
|
|
MapleStatEffect mse = msep.getLeft();
|
|
toUpdateEffects.add(new Pair<>(mse.getBuffSourceId(), retrievedEffects.get(mse.getBuffSourceId())));
|
|
}
|
|
|
|
List<Pair<MapleBuffStat, Integer>> activeStatups = new LinkedList<>();
|
|
for (Pair<Integer, Pair<MapleStatEffect, Long>> lmse : toUpdateEffects) {
|
|
Pair<MapleStatEffect, Long> msel = lmse.getRight();
|
|
|
|
for (Pair<MapleBuffStat, Integer> statup : getActiveStatupsFromSourceid(lmse.getLeft())) {
|
|
if (!isSingletonStatup(statup.getLeft())) {
|
|
activeStatups.add(statup);
|
|
}
|
|
}
|
|
|
|
msel.getLeft().updateBuffEffect(this, activeStatups, msel.getRight());
|
|
activeStatups.clear();
|
|
}
|
|
|
|
if (this.isRidingBattleship()) {
|
|
List<Pair<MapleBuffStat, Integer>> statups = new ArrayList<>(1);
|
|
statups.add(new Pair<>(MapleBuffStat.MONSTER_RIDING, 0));
|
|
this.announce(MaplePacketCreator.giveBuff(1932000, 5221006, statups));
|
|
this.announceBattleshipHp();
|
|
}
|
|
}
|
|
|
|
private static MapleBuffStat getSingletonStatupFromEffect(MapleStatEffect mse) {
|
|
for (Pair<MapleBuffStat, Integer> mbs : mse.getStatups()) {
|
|
if (isSingletonStatup(mbs.getLeft())) {
|
|
return mbs.getLeft();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static boolean isSingletonStatup(MapleBuffStat mbs) {
|
|
switch (mbs) { //HPREC and MPREC are supposed to be singleton
|
|
case COUPON_EXP1:
|
|
case COUPON_EXP2:
|
|
case COUPON_EXP3:
|
|
case COUPON_EXP4:
|
|
case COUPON_DRP1:
|
|
case COUPON_DRP2:
|
|
case COUPON_DRP3:
|
|
case WATK:
|
|
case WDEF:
|
|
case MATK:
|
|
case MDEF:
|
|
case ACC:
|
|
case AVOID:
|
|
case SPEED:
|
|
case JUMP:
|
|
return false;
|
|
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public void registerEffect(MapleStatEffect effect, long starttime, long expirationtime, boolean isSilent) {
|
|
if (effect.isDragonBlood()) {
|
|
prepareDragonBlood(effect);
|
|
} else if (effect.isBerserk()) {
|
|
checkBerserk(isHidden());
|
|
} else if (effect.isBeholder()) {
|
|
final int beholder = DarkKnight.BEHOLDER;
|
|
if (beholderHealingSchedule != null) {
|
|
beholderHealingSchedule.cancel(false);
|
|
}
|
|
if (beholderBuffSchedule != null) {
|
|
beholderBuffSchedule.cancel(false);
|
|
}
|
|
Skill bHealing = SkillFactory.getSkill(DarkKnight.AURA_OF_BEHOLDER);
|
|
int bHealingLvl = getSkillLevel(bHealing);
|
|
if (bHealingLvl > 0) {
|
|
final MapleStatEffect healEffect = bHealing.getEffect(bHealingLvl);
|
|
int healInterval = healEffect.getX() * 1000;
|
|
beholderHealingSchedule = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (awayFromWorld.get()) {
|
|
return;
|
|
}
|
|
|
|
addHP(healEffect.getHp());
|
|
client.announce(MaplePacketCreator.showOwnBuffEffect(beholder, 2));
|
|
getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.summonSkill(getId(), beholder, 5), true);
|
|
getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showOwnBuffEffect(beholder, 2), false);
|
|
}
|
|
}, healInterval, healInterval);
|
|
}
|
|
Skill bBuff = SkillFactory.getSkill(DarkKnight.HEX_OF_BEHOLDER);
|
|
if (getSkillLevel(bBuff) > 0) {
|
|
final MapleStatEffect buffEffect = bBuff.getEffect(getSkillLevel(bBuff));
|
|
int buffInterval = buffEffect.getX() * 1000;
|
|
beholderBuffSchedule = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (awayFromWorld.get()) {
|
|
return;
|
|
}
|
|
|
|
buffEffect.applyTo(MapleCharacter.this);
|
|
client.announce(MaplePacketCreator.showOwnBuffEffect(beholder, 2));
|
|
getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.summonSkill(getId(), beholder, (int) (Math.random() * 3) + 6), true);
|
|
getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showBuffeffect(getId(), beholder, 2), false);
|
|
}
|
|
}, buffInterval, buffInterval);
|
|
}
|
|
} else if (effect.isRecovery()) {
|
|
int healInterval = (ServerConstants.USE_ULTRA_RECOVERY) ? 2000 : 5000;
|
|
final byte heal = (byte) effect.getX();
|
|
|
|
chrLock.lock();
|
|
try {
|
|
if (recoveryTask != null) {
|
|
recoveryTask.cancel(false);
|
|
}
|
|
|
|
recoveryTask = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (getBuffSource(MapleBuffStat.RECOVERY) == -1) {
|
|
chrLock.lock();
|
|
try {
|
|
if (recoveryTask != null) {
|
|
recoveryTask.cancel(false);
|
|
recoveryTask = null;
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
addHP(heal);
|
|
client.announce(MaplePacketCreator.showOwnRecovery(heal));
|
|
getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showRecovery(id, heal), false);
|
|
}
|
|
}, healInterval, healInterval);
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
} else if (effect.getHpRRate() > 0 || effect.getMpRRate() > 0) {
|
|
if (effect.getHpRRate() > 0) {
|
|
extraHpRec = effect.getHpR();
|
|
extraRecInterval = effect.getHpRRate();
|
|
}
|
|
|
|
if (effect.getMpRRate() > 0) {
|
|
extraMpRec = effect.getMpR();
|
|
extraRecInterval = effect.getMpRRate();
|
|
}
|
|
|
|
chrLock.lock();
|
|
try {
|
|
stopExtraTask();
|
|
startExtraTask(extraHpRec, extraMpRec, extraRecInterval); // HP & MP sharing the same task holder
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
|
|
} else if (effect.isMapChair()) {
|
|
startChairTask();
|
|
}
|
|
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
Integer sourceid = effect.getBuffSourceId();
|
|
Map<MapleBuffStat, MapleBuffStatValueHolder> toDeploy;
|
|
Map<MapleBuffStat, MapleBuffStatValueHolder> appliedStatups = new LinkedHashMap<>();
|
|
|
|
for (Pair<MapleBuffStat, Integer> ps : effect.getStatups()) {
|
|
appliedStatups.put(ps.getLeft(), new MapleBuffStatValueHolder(effect, starttime, ps.getRight()));
|
|
}
|
|
|
|
if (ServerConstants.USE_BUFF_MOST_SIGNIFICANT) {
|
|
toDeploy = new LinkedHashMap<>();
|
|
Map<Integer, Pair<MapleStatEffect, Long>> retrievedEffects = new LinkedHashMap<>();
|
|
Set<MapleBuffStat> retrievedStats = new LinkedHashSet<>();
|
|
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> statup : appliedStatups.entrySet()) {
|
|
MapleBuffStatValueHolder mbsvh = effects.get(statup.getKey());
|
|
MapleBuffStatValueHolder statMbsvh = statup.getValue();
|
|
|
|
if (mbsvh == null || mbsvh.value < statMbsvh.value || (mbsvh.value == statMbsvh.value && mbsvh.effect.getStatups().size() <= statMbsvh.effect.getStatups().size())) {
|
|
toDeploy.put(statup.getKey(), statMbsvh);
|
|
} else {
|
|
if (!isSingletonStatup(statup.getKey())) {
|
|
retrievedEffects.put(mbsvh.effect.getBuffSourceId(), new Pair<>(mbsvh.effect, mbsvh.startTime));
|
|
for (Pair<MapleBuffStat, Integer> mbs : mbsvh.effect.getStatups()) {
|
|
retrievedStats.add(mbs.getLeft());
|
|
}
|
|
}
|
|
}
|
|
|
|
Byte val = buffEffectsCount.get(statup.getKey());
|
|
if (val != null) {
|
|
val = (byte) (val + 1);
|
|
} else {
|
|
val = (byte) 1;
|
|
}
|
|
|
|
buffEffectsCount.put(statup.getKey(), val);
|
|
}
|
|
|
|
if (!isSilent) {
|
|
addItemEffectHolder(sourceid, expirationtime, appliedStatups);
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> statup : toDeploy.entrySet()) {
|
|
effects.put(statup.getKey(), statup.getValue());
|
|
}
|
|
|
|
retrievedEffects.put(sourceid, new Pair<>(effect, starttime));
|
|
propagateBuffEffectUpdates(retrievedEffects, retrievedStats);
|
|
}
|
|
} else {
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> statup : appliedStatups.entrySet()) {
|
|
Byte val = buffEffectsCount.get(statup.getKey());
|
|
if (val != null) {
|
|
val = (byte) (val + 1);
|
|
} else {
|
|
val = (byte) 1;
|
|
}
|
|
|
|
buffEffectsCount.put(statup.getKey(), val);
|
|
}
|
|
|
|
toDeploy = appliedStatups;
|
|
}
|
|
|
|
addItemEffectHolder(sourceid, expirationtime, appliedStatups);
|
|
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> statup : toDeploy.entrySet()) {
|
|
effects.put(statup.getKey(), statup.getValue());
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
|
|
updateLocalStats();
|
|
}
|
|
|
|
private static int getJobMapChair(MapleJob job) {
|
|
switch (job.getId() / 1000) {
|
|
case 0:
|
|
return Beginner.MAP_CHAIR;
|
|
case 1:
|
|
return Noblesse.MAP_CHAIR;
|
|
default:
|
|
return Legend.MAP_CHAIR;
|
|
}
|
|
}
|
|
|
|
public boolean unregisterChairBuff() {
|
|
if (!ServerConstants.USE_CHAIR_EXTRAHEAL) {
|
|
return false;
|
|
}
|
|
|
|
int skillId = getJobMapChair(job);
|
|
int skillLv = getSkillLevel(skillId);
|
|
if (skillLv > 0) {
|
|
MapleStatEffect mapChairSkill = SkillFactory.getSkill(skillId).getEffect(skillLv);
|
|
return cancelEffect(mapChairSkill, false, -1);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public boolean registerChairBuff() {
|
|
if (!ServerConstants.USE_CHAIR_EXTRAHEAL) {
|
|
return false;
|
|
}
|
|
|
|
int skillId = getJobMapChair(job);
|
|
int skillLv = getSkillLevel(skillId);
|
|
if (skillLv > 0) {
|
|
MapleStatEffect mapChairSkill = SkillFactory.getSkill(skillId).getEffect(skillLv);
|
|
mapChairSkill.applyTo(this);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public int getChair() {
|
|
return chair.get();
|
|
}
|
|
|
|
public String getChalkboard() {
|
|
return this.chalktext;
|
|
}
|
|
|
|
public MapleClient getClient() {
|
|
return client;
|
|
}
|
|
|
|
public final List<MapleQuestStatus> getCompletedQuests() {
|
|
synchronized (quests) {
|
|
List<MapleQuestStatus> ret = new LinkedList<>();
|
|
for (MapleQuestStatus q : quests.values()) {
|
|
if (q.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) {
|
|
ret.add(q);
|
|
}
|
|
}
|
|
|
|
return Collections.unmodifiableList(ret);
|
|
}
|
|
}
|
|
|
|
public List<MapleRing> getCrushRings() {
|
|
Collections.sort(crushRings);
|
|
return crushRings;
|
|
}
|
|
|
|
public int getCurrentCI() {
|
|
return ci;
|
|
}
|
|
|
|
public int getCurrentPage() {
|
|
return currentPage;
|
|
}
|
|
|
|
public int getCurrentTab() {
|
|
return currentTab;
|
|
}
|
|
|
|
public int getCurrentType() {
|
|
return currentType;
|
|
}
|
|
|
|
public int getDojoEnergy() {
|
|
return dojoEnergy;
|
|
}
|
|
|
|
public boolean getDojoParty() {
|
|
return mapid >= 925030100 && mapid < 925040000;
|
|
}
|
|
|
|
public int getDojoPoints() {
|
|
return dojoPoints;
|
|
}
|
|
|
|
public int getDojoStage() {
|
|
return dojoStage;
|
|
}
|
|
|
|
public Collection<MapleDoor> getDoors() {
|
|
prtLock.lock();
|
|
try {
|
|
return (party != null ? Collections.unmodifiableCollection(party.getDoors().values()) : (pdoor != null ? Collections.singleton(pdoor) : new LinkedHashSet<MapleDoor>()));
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public MapleDoor getPlayerDoor() {
|
|
prtLock.lock();
|
|
try {
|
|
return pdoor;
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public MapleDoor getMainTownDoor() {
|
|
for (MapleDoor door : getDoors()) {
|
|
if (door.getTownPortal().getId() == 0x80) {
|
|
return door;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public void applyPartyDoor(MapleDoor door, boolean partyUpdate) {
|
|
MapleParty chrParty;
|
|
prtLock.lock();
|
|
try {
|
|
if (!partyUpdate) {
|
|
pdoor = door;
|
|
}
|
|
|
|
chrParty = getParty();
|
|
if (chrParty != null) {
|
|
chrParty.addDoor(id, door);
|
|
}
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
|
|
silentPartyUpdateInternal(chrParty);
|
|
}
|
|
|
|
private MapleDoor removePartyDoor(boolean partyUpdate) {
|
|
MapleDoor ret = null;
|
|
MapleParty chrParty;
|
|
|
|
prtLock.lock();
|
|
try {
|
|
chrParty = getParty();
|
|
if (chrParty != null) {
|
|
chrParty.removeDoor(id);
|
|
}
|
|
|
|
if (!partyUpdate) {
|
|
ret = pdoor;
|
|
pdoor = null;
|
|
}
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
|
|
silentPartyUpdateInternal(chrParty);
|
|
return ret;
|
|
}
|
|
|
|
private void removePartyDoor(MapleParty formerParty) { // player is no longer registered at this party
|
|
formerParty.removeDoor(id);
|
|
}
|
|
|
|
public int getEnergyBar() {
|
|
return energybar;
|
|
}
|
|
|
|
public EventInstanceManager getEventInstance() {
|
|
evtLock.lock();
|
|
try {
|
|
return eventInstance;
|
|
} finally {
|
|
evtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void resetExcluded(int petId) {
|
|
chrLock.lock();
|
|
try {
|
|
Set<Integer> petExclude = excluded.get(petId);
|
|
|
|
if (petExclude != null) {
|
|
petExclude.clear();
|
|
} else {
|
|
excluded.put(petId, new LinkedHashSet<Integer>());
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void addExcluded(int petId, int x) {
|
|
chrLock.lock();
|
|
try {
|
|
excluded.get(petId).add(x);
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void commitExcludedItems() {
|
|
Map<Integer, Set<Integer>> petExcluded = this.getExcluded();
|
|
|
|
chrLock.lock();
|
|
try {
|
|
excludedItems.clear();
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
|
|
for (Map.Entry<Integer, Set<Integer>> pe : petExcluded.entrySet()) {
|
|
byte petIndex = this.getPetIndex(pe.getKey());
|
|
if (petIndex < 0) {
|
|
continue;
|
|
}
|
|
|
|
Set<Integer> exclItems = pe.getValue();
|
|
if (!exclItems.isEmpty()) {
|
|
client.announce(MaplePacketCreator.loadExceptionList(this.getId(), pe.getKey(), petIndex, new ArrayList<>(exclItems)));
|
|
|
|
chrLock.lock();
|
|
try {
|
|
for (Integer itemid : exclItems) {
|
|
excludedItems.add(itemid);
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void exportExcludedItems(MapleClient c) {
|
|
Map<Integer, Set<Integer>> petExcluded = this.getExcluded();
|
|
for (Map.Entry<Integer, Set<Integer>> pe : petExcluded.entrySet()) {
|
|
byte petIndex = this.getPetIndex(pe.getKey());
|
|
if (petIndex < 0) {
|
|
continue;
|
|
}
|
|
|
|
Set<Integer> exclItems = pe.getValue();
|
|
if (!exclItems.isEmpty()) {
|
|
c.announce(MaplePacketCreator.loadExceptionList(this.getId(), pe.getKey(), petIndex, new ArrayList<>(exclItems)));
|
|
}
|
|
}
|
|
}
|
|
|
|
public Map<Integer, Set<Integer>> getExcluded() {
|
|
chrLock.lock();
|
|
try {
|
|
return Collections.unmodifiableMap(excluded);
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public Set<Integer> getExcludedItems() {
|
|
chrLock.lock();
|
|
try {
|
|
return Collections.unmodifiableSet(excludedItems);
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public int getExp() {
|
|
return exp.get();
|
|
}
|
|
|
|
public int getGachaExp() {
|
|
return gachaexp.get();
|
|
}
|
|
|
|
public boolean hasNoviceExpRate() {
|
|
return ServerConstants.USE_ENFORCE_NOVICE_EXPRATE && isBeginnerJob() && level < 11;
|
|
}
|
|
|
|
public int getExpRate() {
|
|
if (hasNoviceExpRate()) { // base exp rate 1x for early levels idea thanks to Vcoc
|
|
return 1;
|
|
}
|
|
|
|
return expRate;
|
|
}
|
|
|
|
public int getCouponExpRate() {
|
|
return expCoupon;
|
|
}
|
|
|
|
public int getRawExpRate() {
|
|
return expRate / (expCoupon * getWorldServer().getExpRate());
|
|
}
|
|
|
|
public int getDropRate() {
|
|
return dropRate;
|
|
}
|
|
|
|
public int getCouponDropRate() {
|
|
return dropCoupon;
|
|
}
|
|
|
|
public int getRawDropRate() {
|
|
return dropRate / (dropCoupon * getWorldServer().getDropRate());
|
|
}
|
|
|
|
public int getBossDropRate() {
|
|
World w = getWorldServer();
|
|
return (dropRate / w.getDropRate()) * w.getBossDropRate();
|
|
}
|
|
|
|
public int getMesoRate() {
|
|
return mesoRate;
|
|
}
|
|
|
|
public int getCouponMesoRate() {
|
|
return mesoCoupon;
|
|
}
|
|
|
|
public int getRawMesoRate() {
|
|
return mesoRate / (mesoCoupon * getWorldServer().getMesoRate());
|
|
}
|
|
|
|
public int getQuestExpRate() {
|
|
World w = getWorldServer();
|
|
return w.getExpRate() * w.getQuestRate();
|
|
}
|
|
|
|
public int getQuestMesoRate() {
|
|
World w = getWorldServer();
|
|
return w.getMesoRate() * w.getQuestRate();
|
|
}
|
|
|
|
public int getFace() {
|
|
return face;
|
|
}
|
|
|
|
public int getFame() {
|
|
return fame;
|
|
}
|
|
|
|
public MapleFamily getFamily() {
|
|
return family;
|
|
}
|
|
|
|
public void setFamily(MapleFamily f) {
|
|
this.family = f;
|
|
}
|
|
|
|
public int getFamilyId() {
|
|
return familyId;
|
|
}
|
|
|
|
public boolean getFinishedDojoTutorial() {
|
|
return finishedDojoTutorial;
|
|
}
|
|
|
|
public void setUsedStorage() {
|
|
usedStorage = true;
|
|
}
|
|
|
|
public List<MapleRing> getFriendshipRings() {
|
|
Collections.sort(friendshipRings);
|
|
return friendshipRings;
|
|
}
|
|
|
|
public int getGender() {
|
|
return gender;
|
|
}
|
|
|
|
public boolean isMale() {
|
|
return getGender() == 0;
|
|
}
|
|
|
|
public MapleGuild getGuild() {
|
|
try {
|
|
return Server.getInstance().getGuild(getGuildId(), getWorld(), this);
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public MapleAlliance getAlliance() {
|
|
if (mgc != null) {
|
|
try {
|
|
return Server.getInstance().getAlliance(getGuild().getAllianceId());
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public int getGuildId() {
|
|
return guildid;
|
|
}
|
|
|
|
public int getGuildRank() {
|
|
return guildRank;
|
|
}
|
|
|
|
public int getHair() {
|
|
return hair;
|
|
}
|
|
|
|
public MapleHiredMerchant getHiredMerchant() {
|
|
return hiredMerchant;
|
|
}
|
|
|
|
public int getId() {
|
|
return id;
|
|
}
|
|
|
|
public static int getAccountIdByName(String name) {
|
|
try {
|
|
int id;
|
|
Connection con = DatabaseConnection.getConnection();
|
|
|
|
try (PreparedStatement ps = con.prepareStatement("SELECT accountid FROM characters WHERE name = ?")) {
|
|
ps.setString(1, name);
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
if (!rs.next()) {
|
|
rs.close();
|
|
ps.close();
|
|
return -1;
|
|
}
|
|
id = rs.getInt("accountid");
|
|
}
|
|
} finally {
|
|
con.close();
|
|
}
|
|
return id;
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public static int getIdByName(String name) {
|
|
try {
|
|
int id;
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try (PreparedStatement ps = con.prepareStatement("SELECT id FROM characters WHERE name = ?")) {
|
|
ps.setString(1, name);
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
if (!rs.next()) {
|
|
rs.close();
|
|
ps.close();
|
|
return -1;
|
|
}
|
|
id = rs.getInt("id");
|
|
}
|
|
} finally {
|
|
con.close();
|
|
}
|
|
return id;
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public static String getNameById(int id) {
|
|
try {
|
|
String name;
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try (PreparedStatement ps = con.prepareStatement("SELECT name FROM characters WHERE id = ?")) {
|
|
ps.setInt(1, id);
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
if (!rs.next()) {
|
|
rs.close();
|
|
ps.close();
|
|
return null;
|
|
}
|
|
name = rs.getString("name");
|
|
}
|
|
} finally {
|
|
con.close();
|
|
}
|
|
return name;
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public int getInitialSpawnpoint() {
|
|
return initialSpawnPoint;
|
|
}
|
|
|
|
public MapleInventory getInventory(MapleInventoryType type) {
|
|
return inventory[type.ordinal()];
|
|
}
|
|
|
|
public int getItemEffect() {
|
|
return itemEffect;
|
|
}
|
|
|
|
public boolean haveItemWithId(int itemid, boolean checkEquipped) {
|
|
return (inventory[ItemConstants.getInventoryType(itemid).ordinal()].findById(itemid) != null)
|
|
|| (checkEquipped && inventory[MapleInventoryType.EQUIPPED.ordinal()].findById(itemid) != null);
|
|
}
|
|
|
|
public boolean haveItemEquipped(int itemid) {
|
|
return (inventory[MapleInventoryType.EQUIPPED.ordinal()].findById(itemid) != null);
|
|
}
|
|
|
|
public boolean haveWeddingRing() {
|
|
int rings[] = {1112806, 1112803, 1112807, 1112809};
|
|
|
|
for (int ringid : rings) {
|
|
if (haveItemWithId(ringid, true)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public int getItemQuantity(int itemid, boolean checkEquipped) {
|
|
int count = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countById(itemid);
|
|
if (checkEquipped) {
|
|
count += inventory[MapleInventoryType.EQUIPPED.ordinal()].countById(itemid);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public int getCleanItemQuantity(int itemid, boolean checkEquipped) {
|
|
int count = inventory[ItemConstants.getInventoryType(itemid).ordinal()].countNotOwnedById(itemid);
|
|
if (checkEquipped) {
|
|
count += inventory[MapleInventoryType.EQUIPPED.ordinal()].countNotOwnedById(itemid);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public MapleJob getJob() {
|
|
return job;
|
|
}
|
|
|
|
public int getJobRank() {
|
|
return jobRank;
|
|
}
|
|
|
|
public int getJobRankMove() {
|
|
return jobRankMove;
|
|
}
|
|
|
|
public int getJobType() {
|
|
return job.getId() / 1000;
|
|
}
|
|
|
|
public Map<Integer, MapleKeyBinding> getKeymap() {
|
|
return keymap;
|
|
}
|
|
|
|
public long getLastHealed() {
|
|
return lastHealed;
|
|
}
|
|
|
|
public long getLastUsedCashItem() {
|
|
return lastUsedCashItem;
|
|
}
|
|
|
|
public int getLevel() {
|
|
return level;
|
|
}
|
|
|
|
public int getFh() {
|
|
Point pos = this.getPosition();
|
|
pos.y -= 6;
|
|
|
|
if (map.getFootholds().findBelow(pos) == null) {
|
|
return 0;
|
|
} else {
|
|
return map.getFootholds().findBelow(pos).getY1();
|
|
}
|
|
}
|
|
|
|
public MapleMap getMap() {
|
|
return map;
|
|
}
|
|
|
|
public int getMapId() {
|
|
if (map != null) {
|
|
return map.getId();
|
|
}
|
|
return mapid;
|
|
}
|
|
|
|
public MapleRing getMarriageRing() {
|
|
return partnerId > 0 ? marriageRing : null;
|
|
}
|
|
|
|
public int getMasterLevel(Skill skill) {
|
|
if (skills.get(skill) == null) {
|
|
return 0;
|
|
}
|
|
return skills.get(skill).masterlevel;
|
|
}
|
|
|
|
public int getTotalStr() {
|
|
return localstr;
|
|
}
|
|
|
|
public int getTotalDex() {
|
|
return localdex;
|
|
}
|
|
|
|
public int getTotalInt() {
|
|
return localint_;
|
|
}
|
|
|
|
public int getTotalLuk() {
|
|
return localluk;
|
|
}
|
|
|
|
public int getTotalMagic() {
|
|
return localmagic;
|
|
}
|
|
|
|
public int getTotalWatk() {
|
|
return localwatk;
|
|
}
|
|
|
|
public int getMaxClassLevel() {
|
|
return isCygnus() ? 120 : 200;
|
|
}
|
|
|
|
public int getMaxLevel() {
|
|
if (!ServerConstants.USE_ENFORCE_JOB_LEVEL_RANGE || isGmJob()) {
|
|
return getMaxClassLevel();
|
|
}
|
|
|
|
return GameConstants.getJobMaxLevel(job);
|
|
}
|
|
|
|
public int getMeso() {
|
|
return meso.get();
|
|
}
|
|
|
|
public int getMerchantMeso() {
|
|
return merchantmeso;
|
|
}
|
|
|
|
public int getMesosTraded() {
|
|
return mesosTraded;
|
|
}
|
|
|
|
public int getMessengerPosition() {
|
|
return messengerposition;
|
|
}
|
|
|
|
public MapleGuildCharacter getMGC() {
|
|
return mgc;
|
|
}
|
|
|
|
public void setMGC(MapleGuildCharacter mgc) {
|
|
this.mgc = mgc;
|
|
}
|
|
|
|
public MaplePartyCharacter getMPC() {
|
|
if (mpc == null) {
|
|
mpc = new MaplePartyCharacter(this);
|
|
}
|
|
return mpc;
|
|
}
|
|
|
|
public void setMPC(MaplePartyCharacter mpc) {
|
|
this.mpc = mpc;
|
|
}
|
|
|
|
public int getTargetHpBarHash() {
|
|
return this.targetHpBarHash;
|
|
}
|
|
|
|
public void setTargetHpBarHash(int mobHash) {
|
|
this.targetHpBarHash = mobHash;
|
|
}
|
|
|
|
public long getTargetHpBarTime() {
|
|
return this.targetHpBarTime;
|
|
}
|
|
|
|
public void setTargetHpBarTime(long timeNow) {
|
|
this.targetHpBarTime = timeNow;
|
|
}
|
|
|
|
public void setPlayerAggro(int mobHash) {
|
|
setTargetHpBarHash(mobHash);
|
|
setTargetHpBarTime(System.currentTimeMillis());
|
|
}
|
|
|
|
public void resetPlayerAggro() {
|
|
if (getWorldServer().unregisterDisabledServerMessage(id)) {
|
|
client.announceServerMessage();
|
|
}
|
|
|
|
setTargetHpBarHash(0);
|
|
setTargetHpBarTime(0);
|
|
}
|
|
|
|
public MapleMiniGame getMiniGame() {
|
|
return miniGame;
|
|
}
|
|
|
|
public int getMiniGamePoints(MiniGameResult type, boolean omok) {
|
|
if (omok) {
|
|
switch (type) {
|
|
case WIN:
|
|
return omokwins;
|
|
case LOSS:
|
|
return omoklosses;
|
|
default:
|
|
return omokties;
|
|
}
|
|
} else {
|
|
switch (type) {
|
|
case WIN:
|
|
return matchcardwins;
|
|
case LOSS:
|
|
return matchcardlosses;
|
|
default:
|
|
return matchcardties;
|
|
}
|
|
}
|
|
}
|
|
|
|
public MonsterBook getMonsterBook() {
|
|
return monsterbook;
|
|
}
|
|
|
|
public int getMonsterBookCover() {
|
|
return bookCover;
|
|
}
|
|
|
|
public MapleMount getMount() {
|
|
return maplemount;
|
|
}
|
|
|
|
public MapleMessenger getMessenger() {
|
|
return messenger;
|
|
}
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
public int getNextEmptyPetIndex() {
|
|
petLock.lock();
|
|
try {
|
|
for (int i = 0; i < 3; i++) {
|
|
if (pets[i] == null) {
|
|
return i;
|
|
}
|
|
}
|
|
return 3;
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
}
|
|
|
|
public int getNoPets() {
|
|
petLock.lock();
|
|
try {
|
|
int ret = 0;
|
|
for (int i = 0; i < 3; i++) {
|
|
if (pets[i] != null) {
|
|
ret++;
|
|
}
|
|
}
|
|
return ret;
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
}
|
|
|
|
public MapleParty getParty() {
|
|
prtLock.lock();
|
|
try {
|
|
return party;
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public int getPartyId() {
|
|
prtLock.lock();
|
|
try {
|
|
return (party != null ? party.getId() : -1);
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public List<MapleCharacter> getPartyMembers() {
|
|
List<MapleCharacter> list = new LinkedList<>();
|
|
|
|
prtLock.lock();
|
|
try {
|
|
if (party != null) {
|
|
for (MaplePartyCharacter partyMembers : party.getMembers()) {
|
|
list.add(partyMembers.getPlayer());
|
|
}
|
|
}
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
public List<MapleCharacter> getPartyMembersOnSameMap() {
|
|
List<MapleCharacter> list = new LinkedList<>();
|
|
int thisMapHash = this.getMap().hashCode();
|
|
|
|
prtLock.lock();
|
|
try {
|
|
if (party != null) {
|
|
for (MaplePartyCharacter partyMembers : party.getMembers()) {
|
|
MapleCharacter chr = partyMembers.getPlayer();
|
|
MapleMap chrMap = chr.getMap();
|
|
if (chrMap != null && chrMap.hashCode() == thisMapHash && chr.isLoggedinWorld()) {
|
|
list.add(chr);
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
public boolean isPartyMember(MapleCharacter chr) {
|
|
return isPartyMember(chr.getId());
|
|
}
|
|
|
|
public boolean isPartyMember(int cid) {
|
|
for (MapleCharacter mpcu : getPartyMembers()) {
|
|
if (mpcu.getId() == cid) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public List<MonsterDropEntry> retrieveRelevantDrops(int monsterId) {
|
|
List<MapleCharacter> pchars = new LinkedList<>();
|
|
for (MapleCharacter chr : getPartyMembers()) {
|
|
if (chr.isLoggedinWorld()) {
|
|
pchars.add(chr);
|
|
}
|
|
}
|
|
|
|
if (pchars.isEmpty()) {
|
|
pchars.add(this);
|
|
}
|
|
return MapleLootManager.retrieveRelevantDrops(monsterId, pchars);
|
|
}
|
|
|
|
public MaplePlayerShop getPlayerShop() {
|
|
return playerShop;
|
|
}
|
|
|
|
public void setGMLevel(int level) {
|
|
this.gmLevel = Math.min(level, 6);
|
|
this.gmLevel = Math.max(level, 0);
|
|
}
|
|
|
|
public void closePlayerInteractions() {
|
|
closeNpcShop();
|
|
closeTrade();
|
|
closePlayerShop();
|
|
closeMiniGame();
|
|
closeHiredMerchant(false);
|
|
closePlayerMessenger();
|
|
|
|
client.closePlayerScriptInteractions();
|
|
resetPlayerAggro();
|
|
}
|
|
|
|
public void closeNpcShop() {
|
|
setShop(null);
|
|
}
|
|
|
|
public void closeTrade() {
|
|
MapleTrade.cancelTrade(this);
|
|
}
|
|
|
|
public void closePlayerShop() {
|
|
MaplePlayerShop mps = this.getPlayerShop();
|
|
if (mps == null) {
|
|
return;
|
|
}
|
|
|
|
if (mps.isOwner(this)) {
|
|
mps.setOpen(false);
|
|
getWorldServer().unregisterPlayerShop(mps);
|
|
|
|
for (MaplePlayerShopItem mpsi : mps.getItems()) {
|
|
if (mpsi.getBundles() >= 2) {
|
|
Item iItem = mpsi.getItem().copy();
|
|
iItem.setQuantity((short) (mpsi.getBundles() * iItem.getQuantity()));
|
|
MapleInventoryManipulator.addFromDrop(this.getClient(), iItem, false);
|
|
} else if (mpsi.isExist()) {
|
|
MapleInventoryManipulator.addFromDrop(this.getClient(), mpsi.getItem(), true);
|
|
}
|
|
}
|
|
mps.closeShop();
|
|
} else {
|
|
mps.removeVisitor(this);
|
|
}
|
|
this.setPlayerShop(null);
|
|
}
|
|
|
|
public void closeMiniGame() {
|
|
MapleMiniGame game = this.getMiniGame();
|
|
if (game == null) {
|
|
return;
|
|
}
|
|
|
|
this.setMiniGame(null);
|
|
if (game.isOwner(this)) {
|
|
this.getMap().broadcastMessage(MaplePacketCreator.removeMinigameBox(this));
|
|
game.broadcastToVisitor(MaplePacketCreator.getMiniGameClose(3));
|
|
} else {
|
|
game.removeVisitor(this);
|
|
}
|
|
}
|
|
|
|
public void closeHiredMerchant(boolean closeMerchant) {
|
|
MapleHiredMerchant merchant = this.getHiredMerchant();
|
|
if (merchant == null) {
|
|
return;
|
|
}
|
|
|
|
if (closeMerchant) {
|
|
merchant.removeVisitor(this);
|
|
this.setHiredMerchant(null);
|
|
} else {
|
|
if (merchant.isOwner(this)) {
|
|
merchant.setOpen(true);
|
|
} else {
|
|
merchant.removeVisitor(this);
|
|
}
|
|
try {
|
|
merchant.saveItems(false);
|
|
} catch (SQLException ex) {
|
|
ex.printStackTrace();
|
|
FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Error while saving " + name + "'s Hired Merchant items.");
|
|
}
|
|
}
|
|
}
|
|
|
|
public void closePlayerMessenger() {
|
|
MapleMessenger m = this.getMessenger();
|
|
if (m == null) {
|
|
return;
|
|
}
|
|
|
|
World w = getWorldServer();
|
|
MapleMessengerCharacter messengerplayer = new MapleMessengerCharacter(this, this.getMessengerPosition());
|
|
|
|
w.leaveMessenger(m.getId(), messengerplayer);
|
|
this.setMessenger(null);
|
|
this.setMessengerPosition(4);
|
|
}
|
|
|
|
public MaplePet[] getPets() {
|
|
petLock.lock();
|
|
try {
|
|
return Arrays.copyOf(pets, pets.length);
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
}
|
|
|
|
public MaplePet getPet(int index) {
|
|
if (index < 0) {
|
|
return null;
|
|
}
|
|
|
|
petLock.lock();
|
|
try {
|
|
return pets[index];
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
}
|
|
|
|
public byte getPetIndex(int petId) {
|
|
petLock.lock();
|
|
try {
|
|
for (byte i = 0; i < 3; i++) {
|
|
if (pets[i] != null) {
|
|
if (pets[i].getUniqueId() == petId) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
}
|
|
|
|
public byte getPetIndex(MaplePet pet) {
|
|
petLock.lock();
|
|
try {
|
|
for (byte i = 0; i < 3; i++) {
|
|
if (pets[i] != null) {
|
|
if (pets[i].getUniqueId() == pet.getUniqueId()) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
}
|
|
|
|
public int getPossibleReports() {
|
|
return possibleReports;
|
|
}
|
|
|
|
public final byte getQuestStatus(final int quest) {
|
|
synchronized (quests) {
|
|
for (final MapleQuestStatus q : quests.values()) {
|
|
if (q.getQuest().getId() == quest) {
|
|
return (byte) q.getStatus().getId();
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public final MapleQuestStatus getMapleQuestStatus(final int quest) {
|
|
synchronized (quests) {
|
|
for (final MapleQuestStatus q : quests.values()) {
|
|
if (q.getQuest().getId() == quest) {
|
|
return q;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public MapleQuestStatus getQuest(MapleQuest quest) {
|
|
synchronized (quests) {
|
|
if (!quests.containsKey(quest.getId())) {
|
|
return new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED);
|
|
}
|
|
return quests.get(quest.getId());
|
|
}
|
|
}
|
|
|
|
//---- \/ \/ \/ \/ \/ \/ \/ NOT TESTED \/ \/ \/ \/ \/ \/ \/ \/ \/ ----
|
|
public final void setQuestAdd(final MapleQuest quest, final byte status, final String customData) {
|
|
synchronized (quests) {
|
|
if (!quests.containsKey(quest.getId())) {
|
|
final MapleQuestStatus stat = new MapleQuestStatus(quest, MapleQuestStatus.Status.getById((int) status));
|
|
stat.setCustomData(customData);
|
|
quests.put(quest.getId(), stat);
|
|
}
|
|
}
|
|
}
|
|
|
|
public final MapleQuestStatus getQuestNAdd(final MapleQuest quest) {
|
|
synchronized (quests) {
|
|
if (!quests.containsKey(quest.getId())) {
|
|
final MapleQuestStatus status = new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED);
|
|
quests.put(quest.getId(), status);
|
|
return status;
|
|
}
|
|
return quests.get(quest.getId());
|
|
}
|
|
}
|
|
|
|
public final MapleQuestStatus getQuestNoAdd(final MapleQuest quest) {
|
|
synchronized (quests) {
|
|
return quests.get(quest.getId());
|
|
}
|
|
}
|
|
|
|
public final MapleQuestStatus getQuestRemove(final MapleQuest quest) {
|
|
synchronized (quests) {
|
|
return quests.remove(quest.getId());
|
|
}
|
|
}
|
|
|
|
//---- /\ /\ /\ /\ /\ /\ /\ NOT TESTED /\ /\ /\ /\ /\ /\ /\ /\ /\ ----
|
|
public boolean needQuestItem(int questid, int itemid) {
|
|
if (questid <= 0) {
|
|
return true; //For non quest items :3
|
|
}
|
|
int amountNeeded, questStatus = this.getQuestStatus(questid);
|
|
if (questStatus == 0) {
|
|
amountNeeded = MapleQuest.getInstance(questid).getStartItemAmountNeeded(itemid);
|
|
} else if (questStatus != 1) {
|
|
return false;
|
|
} else {
|
|
amountNeeded = MapleQuest.getInstance(questid).getCompleteItemAmountNeeded(itemid);
|
|
}
|
|
|
|
return amountNeeded > 0 && getInventory(ItemConstants.getInventoryType(itemid)).countById(itemid) < amountNeeded;
|
|
}
|
|
|
|
public int getRank() {
|
|
return rank;
|
|
}
|
|
|
|
public int getRankMove() {
|
|
return rankMove;
|
|
}
|
|
|
|
public void clearSavedLocation(SavedLocationType type) {
|
|
savedLocations[type.ordinal()] = null;
|
|
}
|
|
|
|
public int peekSavedLocation(String type) {
|
|
SavedLocation sl = savedLocations[SavedLocationType.fromString(type).ordinal()];
|
|
if (sl == null) {
|
|
return -1;
|
|
}
|
|
return sl.getMapId();
|
|
}
|
|
|
|
public int getSavedLocation(String type) {
|
|
int m = peekSavedLocation(type);
|
|
clearSavedLocation(SavedLocationType.fromString(type));
|
|
|
|
return m;
|
|
}
|
|
|
|
public String getSearch() {
|
|
return search;
|
|
}
|
|
|
|
public MapleShop getShop() {
|
|
return shop;
|
|
}
|
|
|
|
public Map<Skill, SkillEntry> getSkills() {
|
|
return Collections.unmodifiableMap(skills);
|
|
}
|
|
|
|
public int getSkillLevel(int skill) {
|
|
SkillEntry ret = skills.get(SkillFactory.getSkill(skill));
|
|
if (ret == null) {
|
|
return 0;
|
|
}
|
|
return ret.skillevel;
|
|
}
|
|
|
|
public byte getSkillLevel(Skill skill) {
|
|
if (skills.get(skill) == null) {
|
|
return 0;
|
|
}
|
|
return skills.get(skill).skillevel;
|
|
}
|
|
|
|
public long getSkillExpiration(int skill) {
|
|
SkillEntry ret = skills.get(SkillFactory.getSkill(skill));
|
|
if (ret == null) {
|
|
return -1;
|
|
}
|
|
return ret.expiration;
|
|
}
|
|
|
|
public long getSkillExpiration(Skill skill) {
|
|
if (skills.get(skill) == null) {
|
|
return -1;
|
|
}
|
|
return skills.get(skill).expiration;
|
|
}
|
|
|
|
public MapleSkinColor getSkinColor() {
|
|
return skinColor;
|
|
}
|
|
|
|
public int getSlot() {
|
|
return slots;
|
|
}
|
|
|
|
public final List<MapleQuestStatus> getStartedQuests() {
|
|
synchronized (quests) {
|
|
List<MapleQuestStatus> ret = new LinkedList<>();
|
|
for (MapleQuestStatus q : quests.values()) {
|
|
if (q.getStatus().equals(MapleQuestStatus.Status.STARTED)) {
|
|
ret.add(q);
|
|
}
|
|
}
|
|
return Collections.unmodifiableList(ret);
|
|
}
|
|
}
|
|
|
|
public final int getStartedQuestsSize() {
|
|
synchronized (quests) {
|
|
int i = 0;
|
|
for (MapleQuestStatus q : quests.values()) {
|
|
if (q.getStatus().equals(MapleQuestStatus.Status.STARTED)) {
|
|
if (q.getQuest().getInfoNumber() > 0) {
|
|
i++;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
}
|
|
|
|
public MapleStatEffect getStatForBuff(MapleBuffStat effect) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
MapleBuffStatValueHolder mbsvh = effects.get(effect);
|
|
if (mbsvh == null) {
|
|
return null;
|
|
}
|
|
return mbsvh.effect;
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public MapleStorage getStorage() {
|
|
return storage;
|
|
}
|
|
|
|
public Collection<MapleSummon> getSummonsValues() {
|
|
return summons.values();
|
|
}
|
|
|
|
public void clearSummons() {
|
|
summons.clear();
|
|
}
|
|
|
|
public MapleSummon getSummonByKey(int id) {
|
|
return summons.get(id);
|
|
}
|
|
|
|
public boolean isSummonsEmpty() {
|
|
return summons.isEmpty();
|
|
}
|
|
|
|
public boolean containsSummon(MapleSummon summon) {
|
|
return summons.containsValue(summon);
|
|
}
|
|
|
|
public MapleTrade getTrade() {
|
|
return trade;
|
|
}
|
|
|
|
public int getVanquisherKills() {
|
|
return vanquisherKills;
|
|
}
|
|
|
|
public int getVanquisherStage() {
|
|
return vanquisherStage;
|
|
}
|
|
|
|
public Collection<MapleMapObject> getVisibleMapObjects() {
|
|
return Collections.unmodifiableCollection(visibleMapObjects);
|
|
}
|
|
|
|
public int getWorld() {
|
|
return world;
|
|
}
|
|
|
|
public World getWorldServer() {
|
|
return Server.getInstance().getWorld(world);
|
|
}
|
|
|
|
public void giveCoolDowns(final int skillid, long starttime, long length) {
|
|
if (skillid == 5221999) {
|
|
this.battleshipHp = (int) length;
|
|
addCooldown(skillid, 0, length);
|
|
} else {
|
|
long timeNow = Server.getInstance().getCurrentTime();
|
|
int time = (int) ((length + starttime) - timeNow);
|
|
addCooldown(skillid, timeNow, time);
|
|
}
|
|
}
|
|
|
|
public int gmLevel() {
|
|
return gmLevel;
|
|
}
|
|
|
|
private void guildUpdate() {
|
|
mgc.setLevel(level);
|
|
mgc.setJobId(job.getId());
|
|
|
|
if (this.guildid < 1) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
Server.getInstance().memberLevelJobUpdate(this.mgc);
|
|
//Server.getInstance().getGuild(guildid, world, mgc).gainGP(40);
|
|
int allianceId = getGuild().getAllianceId();
|
|
if (allianceId > 0) {
|
|
Server.getInstance().allianceMessage(allianceId, MaplePacketCreator.updateAllianceJobLevel(this), getId(), -1);
|
|
}
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void handleEnergyChargeGain() { // to get here energychargelevel has to be > 0
|
|
Skill energycharge = isCygnus() ? SkillFactory.getSkill(ThunderBreaker.ENERGY_CHARGE) : SkillFactory.getSkill(Marauder.ENERGY_CHARGE);
|
|
MapleStatEffect ceffect;
|
|
ceffect = energycharge.getEffect(getSkillLevel(energycharge));
|
|
TimerManager tMan = TimerManager.getInstance();
|
|
if (energybar < 10000) {
|
|
energybar += 102;
|
|
if (energybar > 10000) {
|
|
energybar = 10000;
|
|
}
|
|
List<Pair<MapleBuffStat, Integer>> stat = Collections.singletonList(new Pair<>(MapleBuffStat.ENERGY_CHARGE, energybar));
|
|
setBuffedValue(MapleBuffStat.ENERGY_CHARGE, energybar);
|
|
client.announce(MaplePacketCreator.giveBuff(energybar, 0, stat));
|
|
client.announce(MaplePacketCreator.showOwnBuffEffect(energycharge.getId(), 2));
|
|
getMap().broadcastMessage(this, MaplePacketCreator.showBuffeffect(id, energycharge.getId(), 2));
|
|
getMap().broadcastMessage(this, MaplePacketCreator.giveForeignBuff(energybar, stat));
|
|
}
|
|
if (energybar >= 10000 && energybar < 11000) {
|
|
energybar = 15000;
|
|
final MapleCharacter chr = this;
|
|
tMan.schedule(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
energybar = 0;
|
|
List<Pair<MapleBuffStat, Integer>> stat = Collections.singletonList(new Pair<>(MapleBuffStat.ENERGY_CHARGE, energybar));
|
|
setBuffedValue(MapleBuffStat.ENERGY_CHARGE, energybar);
|
|
client.announce(MaplePacketCreator.giveBuff(energybar, 0, stat));
|
|
getMap().broadcastMessage(chr, MaplePacketCreator.giveForeignBuff(energybar, stat));
|
|
}
|
|
}, ceffect.getDuration());
|
|
}
|
|
}
|
|
|
|
public void handleOrbconsume() {
|
|
int skillid = isCygnus() ? DawnWarrior.COMBO : Crusader.COMBO;
|
|
Skill combo = SkillFactory.getSkill(skillid);
|
|
List<Pair<MapleBuffStat, Integer>> stat = Collections.singletonList(new Pair<>(MapleBuffStat.COMBO, 1));
|
|
setBuffedValue(MapleBuffStat.COMBO, 1);
|
|
client.announce(MaplePacketCreator.giveBuff(skillid, combo.getEffect(getSkillLevel(combo)).getDuration() + (int) ((getBuffedStarttime(MapleBuffStat.COMBO) - System.currentTimeMillis())), stat));
|
|
getMap().broadcastMessage(this, MaplePacketCreator.giveForeignBuff(getId(), stat), false);
|
|
}
|
|
|
|
public boolean hasEntered(String script) {
|
|
for (int mapId : entered.keySet()) {
|
|
if (entered.get(mapId).equals(script)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public boolean hasEntered(String script, int mapId) {
|
|
if (entered.containsKey(mapId)) {
|
|
if (entered.get(mapId).equals(script)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void hasGivenFame(MapleCharacter to) {
|
|
lastfametime = System.currentTimeMillis();
|
|
lastmonthfameids.add(Integer.valueOf(to.getId()));
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try (PreparedStatement ps = con.prepareStatement("INSERT INTO famelog (characterid, characterid_to) VALUES (?, ?)")) {
|
|
ps.setInt(1, getId());
|
|
ps.setInt(2, to.getId());
|
|
ps.executeUpdate();
|
|
} finally {
|
|
con.close();
|
|
}
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public boolean hasMerchant() {
|
|
return hasMerchant;
|
|
}
|
|
|
|
public boolean haveItem(int itemid) {
|
|
return getItemQuantity(itemid, false) > 0;
|
|
}
|
|
|
|
public boolean haveCleanItem(int itemid) {
|
|
return getCleanItemQuantity(itemid, false) > 0;
|
|
}
|
|
|
|
public boolean hasEmptySlot(int itemId) {
|
|
return getInventory(ItemConstants.getInventoryType(itemId)).getNextFreeSlot() > -1;
|
|
}
|
|
|
|
public boolean hasEmptySlot(byte invType) {
|
|
return getInventory(MapleInventoryType.getByType(invType)).getNextFreeSlot() > -1;
|
|
}
|
|
|
|
public void increaseGuildCapacity() {
|
|
int cost = MapleGuild.getIncreaseGuildCost(getGuild().getCapacity());
|
|
|
|
if (getMeso() < cost) {
|
|
dropMessage(1, "You don't have enough mesos.");
|
|
return;
|
|
}
|
|
|
|
if (Server.getInstance().increaseGuildCapacity(guildid)) {
|
|
gainMeso(-cost, true, false, true);
|
|
} else {
|
|
dropMessage(1, "Your guild already reached the maximum capacity of players.");
|
|
}
|
|
}
|
|
|
|
public boolean isActiveBuffedValue(int skillid) {
|
|
LinkedList<MapleBuffStatValueHolder> allBuffs;
|
|
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
allBuffs = new LinkedList<>(effects.values());
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
|
|
for (MapleBuffStatValueHolder mbsvh : allBuffs) {
|
|
if (mbsvh.effect.isSkill() && mbsvh.effect.getSourceId() == skillid) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean canBuyback(int fee, boolean usingMesos) {
|
|
return (usingMesos ? this.getMeso() : cashshop.getCash(1)) >= fee;
|
|
}
|
|
|
|
private void applyBuybackFee(int fee, boolean usingMesos) {
|
|
if (usingMesos) {
|
|
this.gainMeso(-fee);
|
|
} else {
|
|
cashshop.gainCash(1, -fee);
|
|
}
|
|
}
|
|
|
|
private long getNextBuybackTime() {
|
|
return lastBuyback + ServerConstants.BUYBACK_COOLDOWN_MINUTES * 60 * 1000;
|
|
}
|
|
|
|
private boolean isBuybackInvincible() {
|
|
return Server.getInstance().getCurrentTime() - lastBuyback < 4200;
|
|
}
|
|
|
|
private int getBuybackFee() {
|
|
float fee = ServerConstants.BUYBACK_FEE;
|
|
int grade = Math.min(Math.max(level, 30), 120) - 30;
|
|
|
|
fee += (grade * ServerConstants.BUYBACK_LEVEL_STACK_FEE);
|
|
if (ServerConstants.USE_BUYBACK_WITH_MESOS) {
|
|
fee *= ServerConstants.BUYBACK_MESO_MULTIPLIER;
|
|
}
|
|
|
|
return (int) Math.floor(fee);
|
|
}
|
|
|
|
public void showBuybackInfo() {
|
|
String s = "#eBUYBACK STATUS#n\r\n\r\nCurrent buyback fee: #b" + getBuybackFee() + " " + (ServerConstants.USE_BUYBACK_WITH_MESOS ? "mesos" : "NX") + "#k\r\n\r\n";
|
|
|
|
long timeNow = Server.getInstance().getCurrentTime();
|
|
boolean avail = true;
|
|
if (!isAlive()) {
|
|
long timeLapsed = timeNow - lastDeathtime;
|
|
long timeRemaining = ServerConstants.BUYBACK_RETURN_MINUTES * 60 * 1000 - (timeLapsed + Math.max(0, getNextBuybackTime() - timeNow));
|
|
if (timeRemaining < 1) {
|
|
s += "Buyback #e#rUNAVAILABLE#k#n";
|
|
avail = false;
|
|
} else {
|
|
s += "Buyback countdown: #e#b" + getTimeRemaining(ServerConstants.BUYBACK_RETURN_MINUTES * 60 * 1000 - timeLapsed) + "#k#n";
|
|
}
|
|
s += "\r\n";
|
|
}
|
|
|
|
if (timeNow < getNextBuybackTime() && avail) {
|
|
s += "Buyback available in #r" + getTimeRemaining(getNextBuybackTime() - timeNow) + "#k";
|
|
s += "\r\n";
|
|
}
|
|
|
|
this.showHint(s);
|
|
}
|
|
|
|
private static String getTimeRemaining(long timeLeft) {
|
|
int seconds = (int) Math.floor(timeLeft / 1000) % 60;
|
|
int minutes = (int) Math.floor(timeLeft / (1000 * 60)) % 60;
|
|
|
|
return (minutes > 0 ? (String.format("%02d", minutes) + " minutes, ") : "") + String.format("%02d", seconds) + " seconds";
|
|
}
|
|
|
|
public boolean couldBuyback() { // Ronan's buyback system
|
|
long timeNow = Server.getInstance().getCurrentTime();
|
|
|
|
if (timeNow - lastDeathtime > ServerConstants.BUYBACK_RETURN_MINUTES * 60 * 1000) {
|
|
this.dropMessage(5, "The period of time to decide has expired, therefore you are unable to buyback.");
|
|
return false;
|
|
}
|
|
|
|
long nextBuybacktime = getNextBuybackTime();
|
|
if (timeNow < nextBuybacktime) {
|
|
long timeLeft = nextBuybacktime - timeNow;
|
|
this.dropMessage(5, "Next buyback available in " + getTimeRemaining(timeLeft) + ".");
|
|
return false;
|
|
}
|
|
|
|
boolean usingMesos = ServerConstants.USE_BUYBACK_WITH_MESOS;
|
|
int fee = getBuybackFee();
|
|
|
|
if (!canBuyback(fee, usingMesos)) {
|
|
this.dropMessage(5, "You don't have " + fee + " " + (usingMesos ? "mesos" : "NX") + " to buyback.");
|
|
return false;
|
|
}
|
|
|
|
lastBuyback = timeNow;
|
|
applyBuybackFee(fee, usingMesos);
|
|
return true;
|
|
}
|
|
|
|
public boolean isBuffFrom(MapleBuffStat stat, Skill skill) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
MapleBuffStatValueHolder mbsvh = effects.get(stat);
|
|
if (mbsvh == null) {
|
|
return false;
|
|
}
|
|
return mbsvh.effect.isSkill() && mbsvh.effect.getSourceId() == skill.getId();
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public boolean isGmJob() {
|
|
int jn = job.getJobNiche();
|
|
return jn >= 8 && jn <= 9;
|
|
}
|
|
|
|
public boolean isCygnus() {
|
|
return getJobType() == 1;
|
|
}
|
|
|
|
public boolean isAran() {
|
|
return job.getId() >= 2000 && job.getId() <= 2112;
|
|
}
|
|
|
|
public boolean isBeginnerJob() {
|
|
return (job.getId() == 0 || job.getId() == 1000 || job.getId() == 2000);
|
|
}
|
|
|
|
public boolean isGM() {
|
|
return gmLevel > 1;
|
|
}
|
|
|
|
public boolean isHidden() {
|
|
return hidden;
|
|
}
|
|
|
|
public boolean isMapObjectVisible(MapleMapObject mo) {
|
|
return visibleMapObjects.contains(mo);
|
|
}
|
|
|
|
public boolean isPartyLeader() {
|
|
prtLock.lock();
|
|
try {
|
|
return party.getLeaderId() == getId();
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public boolean isGuildLeader() { // true on guild master or jr. master
|
|
return guildid > 0 && guildRank < 3;
|
|
}
|
|
|
|
public void leaveMap() {
|
|
releaseControlledMonsters();
|
|
visibleMapObjects.clear();
|
|
setChair(0);
|
|
if (hpDecreaseTask != null) {
|
|
hpDecreaseTask.cancel(false);
|
|
}
|
|
cancelPqMapleMap();
|
|
cancelAriantScore();
|
|
}
|
|
|
|
public void cancelPqMapleMap() {
|
|
if (pqMapleMap != null) {
|
|
pqMapleMap.cancel(true);
|
|
pqMapleMap = null;
|
|
}
|
|
}
|
|
|
|
public void cancelAriantScore() {
|
|
if (ariantScore != null) {
|
|
ariantScore.cancel(true);
|
|
ariantScore = null;
|
|
}
|
|
}
|
|
|
|
private int getChangedJobSp(MapleJob newJob) {
|
|
int curSp = getUsedSp(newJob) + getJobRemainingSp(newJob);
|
|
int spGain = 0;
|
|
int expectedSp = getJobLevelSp(level - 10, newJob, GameConstants.getJobBranch(newJob));
|
|
if (curSp < expectedSp) {
|
|
spGain += (expectedSp - curSp);
|
|
}
|
|
|
|
return getSpGain(spGain, curSp, job);
|
|
}
|
|
|
|
private int getUsedSp(MapleJob job) {
|
|
int jobId = job.getId();
|
|
int spUsed = 0;
|
|
|
|
for (Entry<Skill, SkillEntry> s : this.getSkills().entrySet()) {
|
|
Skill skill = s.getKey();
|
|
if (GameConstants.isInJobTree(skill.getId(), jobId) && !skill.isBeginnerSkill()) {
|
|
spUsed += s.getValue().skillevel;
|
|
}
|
|
}
|
|
|
|
return spUsed;
|
|
}
|
|
|
|
private int getJobLevelSp(int level, MapleJob job, int jobBranch) {
|
|
if (getJobStyleInternal(job.getId(), (byte) 0x40) == MapleJob.MAGICIAN) {
|
|
level += 2; // starts earlier, level 8
|
|
}
|
|
|
|
return 3 * level + GameConstants.getChangeJobSpUpgrade(jobBranch);
|
|
}
|
|
|
|
private int getJobMaxSp(MapleJob job) {
|
|
int jobBranch = GameConstants.getJobBranch(job);
|
|
int jobRange = GameConstants.getJobUpgradeLevelRange(jobBranch);
|
|
return getJobLevelSp(jobRange, job, jobBranch);
|
|
}
|
|
|
|
private int getJobRemainingSp(MapleJob job) {
|
|
int skillBook = GameConstants.getSkillBook(job.getId());
|
|
|
|
int ret = 0;
|
|
for (int i = 0; i <= skillBook; i++) {
|
|
ret += this.getRemainingSp(i);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
private int getSpGain(int spGain, MapleJob job) {
|
|
int curSp = getUsedSp(job) + getJobRemainingSp(job);
|
|
return getSpGain(spGain, curSp, job);
|
|
}
|
|
|
|
private int getSpGain(int spGain, int curSp, MapleJob job) {
|
|
int maxSp = getJobMaxSp(job);
|
|
|
|
spGain = Math.min(spGain, maxSp - curSp);
|
|
int jobBranch = GameConstants.getJobBranch(job);
|
|
return spGain;
|
|
}
|
|
|
|
private void levelUpGainSp() {
|
|
if (GameConstants.getJobBranch(job) == 0) {
|
|
return;
|
|
}
|
|
|
|
int spGain = 3;
|
|
if (ServerConstants.USE_ENFORCE_JOB_SP_RANGE && !GameConstants.hasSPTable(job)) {
|
|
spGain = getSpGain(spGain, job);
|
|
}
|
|
|
|
if (spGain > 0) {
|
|
gainSp(spGain, GameConstants.getSkillBook(job.getId()), true);
|
|
}
|
|
}
|
|
|
|
public synchronized void levelUp(boolean takeexp) {
|
|
Skill improvingMaxHP = null;
|
|
Skill improvingMaxMP = null;
|
|
int improvingMaxHPLevel = 0;
|
|
int improvingMaxMPLevel = 0;
|
|
|
|
boolean isBeginner = isBeginnerJob();
|
|
if (ServerConstants.USE_AUTOASSIGN_STARTERS_AP && isBeginner && level < 11) {
|
|
effLock.lock();
|
|
statWlock.lock();
|
|
try {
|
|
gainAp(5, true);
|
|
|
|
int str = 0, dex = 0;
|
|
if (level < 6) {
|
|
str += 5;
|
|
} else {
|
|
str += 4;
|
|
dex += 1;
|
|
}
|
|
|
|
assignStrDexIntLuk(str, dex, 0, 0);
|
|
} finally {
|
|
statWlock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
} else {
|
|
int remainingAp = 5;
|
|
if (isCygnus() && level > 10 && level < 70) {
|
|
remainingAp++;
|
|
}
|
|
|
|
gainAp(remainingAp, true);
|
|
}
|
|
|
|
int addhp = 0, addmp = 0;
|
|
if (isBeginner) {
|
|
addhp += Randomizer.rand(12, 16);
|
|
addmp += Randomizer.rand(10, 12);
|
|
} else if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1)) {
|
|
improvingMaxHP = isCygnus() ? SkillFactory.getSkill(DawnWarrior.MAX_HP_INCREASE) : SkillFactory.getSkill(Swordsman.IMPROVED_MAX_HP_INCREASE);
|
|
if (job.isA(MapleJob.CRUSADER)) {
|
|
improvingMaxMP = SkillFactory.getSkill(1210000);
|
|
} else if (job.isA(MapleJob.DAWNWARRIOR2)) {
|
|
improvingMaxMP = SkillFactory.getSkill(11110000);
|
|
}
|
|
improvingMaxHPLevel = getSkillLevel(improvingMaxHP);
|
|
addhp += Randomizer.rand(24, 28);
|
|
addmp += Randomizer.rand(4, 6);
|
|
} else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
|
|
improvingMaxMP = isCygnus() ? SkillFactory.getSkill(BlazeWizard.INCREASING_MAX_MP) : SkillFactory.getSkill(Magician.IMPROVED_MAX_MP_INCREASE);
|
|
improvingMaxMPLevel = getSkillLevel(improvingMaxMP);
|
|
addhp += Randomizer.rand(10, 14);
|
|
addmp += Randomizer.rand(22, 24);
|
|
} else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.THIEF) || (job.getId() > 1299 && job.getId() < 1500)) {
|
|
addhp += Randomizer.rand(20, 24);
|
|
addmp += Randomizer.rand(14, 16);
|
|
} else if (job.isA(MapleJob.GM)) {
|
|
addhp += 30000;
|
|
addmp += 30000;
|
|
} else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
|
|
improvingMaxHP = isCygnus() ? SkillFactory.getSkill(ThunderBreaker.IMPROVE_MAX_HP) : SkillFactory.getSkill(5100000);
|
|
improvingMaxHPLevel = getSkillLevel(improvingMaxHP);
|
|
addhp += Randomizer.rand(22, 28);
|
|
addmp += Randomizer.rand(18, 23);
|
|
} else if (job.isA(MapleJob.ARAN1)) {
|
|
addhp += Randomizer.rand(44, 48);
|
|
int aids = Randomizer.rand(4, 8);
|
|
addmp += aids + Math.floor(aids * 0.1);
|
|
}
|
|
if (improvingMaxHPLevel > 0 && (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.PIRATE) || job.isA(MapleJob.DAWNWARRIOR1))) {
|
|
addhp += improvingMaxHP.getEffect(improvingMaxHPLevel).getX();
|
|
}
|
|
if (improvingMaxMPLevel > 0 && (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.CRUSADER) || job.isA(MapleJob.BLAZEWIZARD1))) {
|
|
addmp += improvingMaxMP.getEffect(improvingMaxMPLevel).getX();
|
|
}
|
|
|
|
if (ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
|
|
if (getJobStyle() == MapleJob.MAGICIAN) {
|
|
addmp += localint_ / 20;
|
|
} else {
|
|
addmp += localint_ / 10;
|
|
}
|
|
}
|
|
|
|
addMaxMPMaxHP(addhp, addmp, true);
|
|
|
|
if (takeexp) {
|
|
exp.addAndGet(-ExpTable.getExpNeededForLevel(level));
|
|
if (exp.get() < 0) {
|
|
exp.set(0);
|
|
}
|
|
}
|
|
|
|
level++;
|
|
if (level >= getMaxClassLevel()) {
|
|
exp.set(0);
|
|
|
|
int maxClassLevel = getMaxClassLevel();
|
|
if (level == maxClassLevel) {
|
|
if (!this.isGM()) {
|
|
if (ServerConstants.PLAYERNPC_AUTODEPLOY) {
|
|
ThreadManager.getInstance().newTask(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
MaplePlayerNPC.spawnPlayerNPC(GameConstants.getHallOfFameMapid(job), MapleCharacter.this);
|
|
}
|
|
});
|
|
}
|
|
|
|
final String names = (getMedalText() + name);
|
|
getWorldServer().broadcastPacket(MaplePacketCreator.serverNotice(6, String.format(LEVEL_200, names, maxClassLevel, names)));
|
|
}
|
|
}
|
|
|
|
level = maxClassLevel; //To prevent levels past the maximum
|
|
}
|
|
|
|
levelUpGainSp();
|
|
|
|
effLock.lock();
|
|
statWlock.lock();
|
|
try {
|
|
recalcLocalStats();
|
|
changeHpMp(localmaxhp, localmaxmp, true);
|
|
|
|
List<Pair<MapleStat, Integer>> statup = new ArrayList<>(10);
|
|
statup.add(new Pair<>(MapleStat.AVAILABLEAP, remainingAp));
|
|
statup.add(new Pair<>(MapleStat.AVAILABLESP, remainingSp[GameConstants.getSkillBook(job.getId())]));
|
|
statup.add(new Pair<>(MapleStat.HP, hp));
|
|
statup.add(new Pair<>(MapleStat.MP, mp));
|
|
statup.add(new Pair<>(MapleStat.EXP, exp.get()));
|
|
statup.add(new Pair<>(MapleStat.LEVEL, level));
|
|
statup.add(new Pair<>(MapleStat.MAXHP, clientmaxhp));
|
|
statup.add(new Pair<>(MapleStat.MAXMP, clientmaxmp));
|
|
statup.add(new Pair<>(MapleStat.STR, str));
|
|
statup.add(new Pair<>(MapleStat.DEX, dex));
|
|
|
|
client.announce(MaplePacketCreator.updatePlayerStats(statup, true, this));
|
|
} finally {
|
|
statWlock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
|
|
getMap().broadcastMessage(this, MaplePacketCreator.showForeignEffect(getId(), 0), false);
|
|
setMPC(new MaplePartyCharacter(this));
|
|
silentPartyUpdate();
|
|
|
|
if (this.guildid > 0) {
|
|
getGuild().broadcast(MaplePacketCreator.levelUpMessage(2, level, name), this.getId());
|
|
}
|
|
|
|
if (level % 20 == 0) {
|
|
if (ServerConstants.USE_ADD_SLOTS_BY_LEVEL == true) {
|
|
if (!isGM()) {
|
|
for (byte i = 1; i < 5; i++) {
|
|
gainSlots(i, 4, true);
|
|
}
|
|
|
|
this.yellowMessage("You reached level " + level + ". Congratulations! As a token of your success, your inventory has been expanded a little bit.");
|
|
}
|
|
}
|
|
if (ServerConstants.USE_ADD_RATES_BY_LEVEL == true) { //For the rate upgrade
|
|
revertLastPlayerRates();
|
|
setPlayerRates();
|
|
this.yellowMessage("You managed to get level " + level + "! Getting experience and items seems a little easier now, huh?");
|
|
}
|
|
}
|
|
|
|
if (ServerConstants.USE_PERFECT_PITCH && level >= 30) {
|
|
//milestones?
|
|
if (MapleInventoryManipulator.checkSpace(client, 4310000, (short) 1, "")) {
|
|
MapleInventoryManipulator.addById(client, 4310000, (short) 1, "", -1);
|
|
}
|
|
} else if (level == 10) {
|
|
Runnable r = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
MapleParty party;
|
|
boolean partyLeader;
|
|
|
|
prtLock.lock();
|
|
try {
|
|
party = getParty();
|
|
partyLeader = party != null && isPartyLeader();
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
|
|
if (party != null) {
|
|
if (partyLeader) {
|
|
party.assignNewLeader(client);
|
|
}
|
|
PartyOperationHandler.leaveParty(party, mpc, client);
|
|
|
|
showHint("You have reached #blevel 10#k, therefore you must leave your #rstarter party#k.");
|
|
}
|
|
}
|
|
};
|
|
|
|
ThreadManager.getInstance().newTask(r);
|
|
}
|
|
|
|
levelUpMessages();
|
|
guildUpdate();
|
|
}
|
|
|
|
private void levelUpMessages() {
|
|
if (level % 5 != 0) { //Performance FTW?
|
|
return;
|
|
}
|
|
if (level == 5) {
|
|
yellowMessage("Aww, you're level 5, how cute!");
|
|
} else if (level == 10) {
|
|
yellowMessage("Henesys Party Quest is now open to you! Head over to Henesys, find some friends, and try it out!");
|
|
} else if (level == 15) {
|
|
yellowMessage("Half-way to your 2nd job advancement, nice work!");
|
|
} else if (level == 20) {
|
|
yellowMessage("You can almost Kerning Party Quest!");
|
|
} else if (level == 25) {
|
|
yellowMessage("You seem to be improving, but you are still not ready to move on to the next step.");
|
|
} else if (level == 30) {
|
|
yellowMessage("You have finally reached level 30! Try job advancing, after that try the Mushroom Castle!");
|
|
} else if (level == 35) {
|
|
yellowMessage("Hey did you hear about this mall that opened in Kerning? Try visiting the Kerning Mall.");
|
|
} else if (level == 40) {
|
|
yellowMessage("Do @rates to see what all your rates are!");
|
|
} else if (level == 45) {
|
|
yellowMessage("I heard that a rock and roll artist died during the grand opening of the Kerning Mall. People are naming him the Spirit of Rock.");
|
|
} else if (level == 50) {
|
|
yellowMessage("You seem to be growing very fast, would you like to test your new found strength with the mighty Zakum?");
|
|
} else if (level == 55) {
|
|
yellowMessage("You can now try out the Ludibrium Maze Party Quest!");
|
|
} else if (level == 60) {
|
|
yellowMessage("Feels good to be near the end of 2nd job, doesn't it?");
|
|
} else if (level == 65) {
|
|
yellowMessage("You're only 5 more levels away from 3rd job, not bad!");
|
|
} else if (level == 70) {
|
|
yellowMessage("I see many people wearing a teddy bear helmet. I should ask someone where they got it from.");
|
|
} else if (level == 75) {
|
|
yellowMessage("You have reached level 3 quarters!");
|
|
} else if (level == 80) {
|
|
yellowMessage("You think you are powerful enough? Try facing horntail!");
|
|
} else if (level == 85) {
|
|
yellowMessage("Did you know? The majority of people who hit level 85 in HeavenMS don't live to be 85 years old?");
|
|
} else if (level == 90) {
|
|
yellowMessage("Hey do you like the amusement park? I heard Spooky World is the best theme park around. I heard they sell cute teddy-bears.");
|
|
} else if (level == 95) {
|
|
yellowMessage("100% of people who hit level 95 in HeavenMS don't live to be 95 years old.");
|
|
} else if (level == 100) {
|
|
yellowMessage("Mid-journey so far... You just reached level 100! Now THAT's such a feat, however to manage the 200 you will need even more passion and determination than ever! Good hunting!");
|
|
} else if (level == 105) {
|
|
yellowMessage("Have you ever been to leafre? I heard they have dragons!");
|
|
} else if (level == 110) {
|
|
yellowMessage("I see many people wearing a teddy bear helmet. I should ask someone where they got it from.");
|
|
} else if (level == 115) {
|
|
yellowMessage("I bet all you can think of is level 120, huh? Level 115 gets no love.");
|
|
} else if (level == 120) {
|
|
yellowMessage("Are you ready to learn from the masters? Head over to your job instructor!");
|
|
} else if (level == 125) {
|
|
yellowMessage("The struggle for mastery books has begun, huh?");
|
|
} else if (level == 130) {
|
|
yellowMessage("You should try Temple of Time. It should be pretty decent EXP.");
|
|
} else if (level == 135) {
|
|
yellowMessage("I hope you're still not struggling for mastery books!");
|
|
} else if (level == 140) {
|
|
yellowMessage("You're well into 4th job at this point, great work!");
|
|
} else if (level == 145) {
|
|
yellowMessage("Level 145 is serious business!");
|
|
} else if (level == 150) {
|
|
yellowMessage("You have becomed quite strong, but the journey is not yet over.");
|
|
} else if (level == 155) {
|
|
yellowMessage("At level 155, Zakum should be a joke to you. Nice job!");
|
|
} else if (level == 160) {
|
|
yellowMessage("Level 160 is pretty impressive. Try taking a picture and putting it on Instagram.");
|
|
} else if (level == 165) {
|
|
yellowMessage("At this level, you should start looking into doing some boss runs.");
|
|
} else if (level == 170) {
|
|
yellowMessage("Level 170, huh? You have the heart of a champion.");
|
|
} else if (level == 175) {
|
|
yellowMessage("You came a long way from level 1. Amazing job so far.");
|
|
} else if (level == 180) {
|
|
yellowMessage("Have you ever tried taking a boss on by yourself? It is quite difficult.");
|
|
} else if (level == 185) {
|
|
yellowMessage("Legend has it that you're a legend.");
|
|
} else if (level == 190) {
|
|
yellowMessage("You only have 10 more levels to go until you hit 200!");
|
|
} else if (level == 195) {
|
|
yellowMessage("Nothing is stopping you at this point, level 195!");
|
|
} else if (level == 200) {
|
|
yellowMessage("Very nicely done! You have reached the so-long dreamed LEVEL 200!!! You are truly a hero among men, cheers upon you!");
|
|
}
|
|
}
|
|
|
|
public void setPlayerRates() {
|
|
this.expRate *= GameConstants.getPlayerBonusExpRate(this.level / 20);
|
|
this.mesoRate *= GameConstants.getPlayerBonusMesoRate(this.level / 20);
|
|
this.dropRate *= GameConstants.getPlayerBonusDropRate(this.level / 20);
|
|
}
|
|
|
|
public void revertLastPlayerRates() {
|
|
this.expRate /= GameConstants.getPlayerBonusExpRate((this.level - 1) / 20);
|
|
this.mesoRate /= GameConstants.getPlayerBonusMesoRate((this.level - 1) / 20);
|
|
this.dropRate /= GameConstants.getPlayerBonusDropRate((this.level - 1) / 20);
|
|
}
|
|
|
|
public void revertPlayerRates() {
|
|
this.expRate /= GameConstants.getPlayerBonusExpRate(this.level / 20);
|
|
this.mesoRate /= GameConstants.getPlayerBonusMesoRate(this.level / 20);
|
|
this.dropRate /= GameConstants.getPlayerBonusDropRate(this.level / 20);
|
|
}
|
|
|
|
public void setWorldRates() {
|
|
World worldz = getWorldServer();
|
|
this.expRate *= worldz.getExpRate();
|
|
this.mesoRate *= worldz.getMesoRate();
|
|
this.dropRate *= worldz.getDropRate();
|
|
}
|
|
|
|
public void revertWorldRates() {
|
|
World worldz = getWorldServer();
|
|
this.expRate /= worldz.getExpRate();
|
|
this.mesoRate /= worldz.getMesoRate();
|
|
this.dropRate /= worldz.getDropRate();
|
|
}
|
|
|
|
private void setCouponRates() {
|
|
List<Integer> couponEffects;
|
|
|
|
Collection<Item> cashItems = this.getInventory(MapleInventoryType.CASH).list();
|
|
chrLock.lock();
|
|
try {
|
|
setActiveCoupons(cashItems);
|
|
couponEffects = activateCouponsEffects();
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
|
|
for (Integer couponId : couponEffects) {
|
|
commitBuffCoupon(couponId);
|
|
}
|
|
}
|
|
|
|
private void revertCouponRates() {
|
|
revertCouponsEffects();
|
|
}
|
|
|
|
public void updateCouponRates() {
|
|
if (cpnLock.tryLock()) {
|
|
MapleInventory cashInv = this.getInventory(MapleInventoryType.CASH);
|
|
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
cashInv.lockInventory();
|
|
try {
|
|
revertCouponRates();
|
|
setCouponRates();
|
|
} finally {
|
|
cpnLock.unlock();
|
|
|
|
cashInv.unlockInventory();
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void resetPlayerRates() {
|
|
expRate = 1;
|
|
mesoRate = 1;
|
|
dropRate = 1;
|
|
|
|
expCoupon = 1;
|
|
mesoCoupon = 1;
|
|
dropCoupon = 1;
|
|
}
|
|
|
|
private int getCouponMultiplier(int couponId) {
|
|
return activeCouponRates.get(couponId);
|
|
}
|
|
|
|
private void setExpCouponRate(int couponId, int couponQty) {
|
|
this.expCoupon *= (getCouponMultiplier(couponId) * couponQty);
|
|
}
|
|
|
|
private void setDropCouponRate(int couponId, int couponQty) {
|
|
this.dropCoupon *= (getCouponMultiplier(couponId) * couponQty);
|
|
this.mesoCoupon *= (getCouponMultiplier(couponId) * couponQty);
|
|
}
|
|
|
|
private void revertCouponsEffects() {
|
|
dispelBuffCoupons();
|
|
|
|
this.expRate /= this.expCoupon;
|
|
this.dropRate /= this.dropCoupon;
|
|
this.mesoRate /= this.mesoCoupon;
|
|
|
|
this.expCoupon = 1;
|
|
this.dropCoupon = 1;
|
|
this.mesoCoupon = 1;
|
|
}
|
|
|
|
private List<Integer> activateCouponsEffects() {
|
|
List<Integer> toCommitEffect = new LinkedList<>();
|
|
|
|
if (ServerConstants.USE_STACK_COUPON_RATES) {
|
|
for (Entry<Integer, Integer> coupon : activeCoupons.entrySet()) {
|
|
int couponId = coupon.getKey();
|
|
int couponQty = coupon.getValue();
|
|
|
|
toCommitEffect.add(couponId);
|
|
|
|
if (ItemConstants.isExpCoupon(couponId)) {
|
|
setExpCouponRate(couponId, couponQty);
|
|
} else {
|
|
setDropCouponRate(couponId, couponQty);
|
|
}
|
|
}
|
|
} else {
|
|
int maxExpRate = 1, maxDropRate = 1, maxExpCouponId = -1, maxDropCouponId = -1;
|
|
|
|
for (Entry<Integer, Integer> coupon : activeCoupons.entrySet()) {
|
|
int couponId = coupon.getKey();
|
|
|
|
if (ItemConstants.isExpCoupon(couponId)) {
|
|
if (maxExpRate < getCouponMultiplier(couponId)) {
|
|
maxExpCouponId = couponId;
|
|
maxExpRate = getCouponMultiplier(couponId);
|
|
}
|
|
} else {
|
|
if (maxDropRate < getCouponMultiplier(couponId)) {
|
|
maxDropCouponId = couponId;
|
|
maxDropRate = getCouponMultiplier(couponId);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (maxExpCouponId > -1) {
|
|
toCommitEffect.add(maxExpCouponId);
|
|
}
|
|
if (maxDropCouponId > -1) {
|
|
toCommitEffect.add(maxDropCouponId);
|
|
}
|
|
|
|
this.expCoupon = maxExpRate;
|
|
this.dropCoupon = maxDropRate;
|
|
this.mesoCoupon = maxDropRate;
|
|
}
|
|
|
|
this.expRate *= this.expCoupon;
|
|
this.dropRate *= this.dropCoupon;
|
|
this.mesoRate *= this.mesoCoupon;
|
|
|
|
return toCommitEffect;
|
|
}
|
|
|
|
private void setActiveCoupons(Collection<Item> cashItems) {
|
|
activeCoupons.clear();
|
|
activeCouponRates.clear();
|
|
|
|
Map<Integer, Integer> coupons = Server.getInstance().getCouponRates();
|
|
List<Integer> active = Server.getInstance().getActiveCoupons();
|
|
|
|
for (Item it : cashItems) {
|
|
if (ItemConstants.isRateCoupon(it.getItemId()) && active.contains(it.getItemId())) {
|
|
Integer count = activeCoupons.get(it.getItemId());
|
|
|
|
if (count != null) {
|
|
activeCoupons.put(it.getItemId(), count + 1);
|
|
} else {
|
|
activeCoupons.put(it.getItemId(), 1);
|
|
activeCouponRates.put(it.getItemId(), coupons.get(it.getItemId()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void commitBuffCoupon(int couponid) {
|
|
if (!isLoggedin() || getCashShop().isOpened()) {
|
|
return;
|
|
}
|
|
|
|
MapleStatEffect mse = ii.getItemEffect(couponid);
|
|
mse.applyTo(this);
|
|
}
|
|
|
|
public void dispelBuffCoupons() {
|
|
List<MapleBuffStatValueHolder> allBuffs = getAllStatups();
|
|
|
|
for (MapleBuffStatValueHolder mbsvh : allBuffs) {
|
|
if (ItemConstants.isRateCoupon(mbsvh.effect.getSourceId())) {
|
|
cancelEffect(mbsvh.effect, false, mbsvh.startTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
public Set<Integer> getActiveCoupons() {
|
|
chrLock.lock();
|
|
try {
|
|
return Collections.unmodifiableSet(activeCoupons.keySet());
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void addPlayerRing(MapleRing ring) {
|
|
int ringItemId = ring.getItemId();
|
|
if (ItemConstants.isWeddingRing(ringItemId)) {
|
|
this.addMarriageRing(ring);
|
|
} else if (ring.getItemId() > 1112012) {
|
|
this.addFriendshipRing(ring);
|
|
} else {
|
|
this.addCrushRing(ring);
|
|
}
|
|
}
|
|
|
|
public static MapleCharacter loadCharacterEntryFromDB(ResultSet rs, List<Item> equipped) {
|
|
MapleCharacter ret = new MapleCharacter();
|
|
|
|
try {
|
|
ret.accountid = rs.getInt("accountid");
|
|
ret.id = rs.getInt("id");
|
|
ret.name = rs.getString("name");
|
|
ret.gender = rs.getInt("gender");
|
|
ret.skinColor = MapleSkinColor.getById(rs.getInt("skincolor"));
|
|
ret.face = rs.getInt("face");
|
|
ret.hair = rs.getInt("hair");
|
|
|
|
// skipping pets, probably unneeded here
|
|
ret.level = rs.getInt("level");
|
|
ret.job = MapleJob.getById(rs.getInt("job"));
|
|
ret.str = rs.getInt("str");
|
|
ret.dex = rs.getInt("dex");
|
|
ret.int_ = rs.getInt("int");
|
|
ret.luk = rs.getInt("luk");
|
|
ret.hp = rs.getInt("hp");
|
|
ret.setMaxHp(rs.getInt("maxhp"));
|
|
ret.mp = rs.getInt("mp");
|
|
ret.setMaxMp(rs.getInt("maxmp"));
|
|
ret.remainingAp = rs.getInt("ap");
|
|
ret.loadCharSkillPoints(rs.getString("sp").split(","));
|
|
ret.exp.set(rs.getInt("exp"));
|
|
ret.fame = rs.getInt("fame");
|
|
ret.gachaexp.set(rs.getInt("gachaexp"));
|
|
ret.mapid = rs.getInt("map");
|
|
ret.initialSpawnPoint = rs.getInt("spawnpoint");
|
|
|
|
ret.gmLevel = rs.getInt("gm");
|
|
ret.world = rs.getByte("world");
|
|
ret.rank = rs.getInt("rank");
|
|
ret.rankMove = rs.getInt("rankMove");
|
|
ret.jobRank = rs.getInt("jobRank");
|
|
ret.jobRankMove = rs.getInt("jobRankMove");
|
|
|
|
if (equipped != null) { // players can have no equipped items at all, ofc
|
|
MapleInventory inv = ret.inventory[MapleInventoryType.EQUIPPED.ordinal()];
|
|
for (Item item : equipped) {
|
|
inv.addItemFromDB(item);
|
|
}
|
|
}
|
|
} catch (SQLException sqle) {
|
|
sqle.printStackTrace();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
public MapleCharacter generateCharacterEntry() {
|
|
MapleCharacter ret = new MapleCharacter();
|
|
|
|
ret.accountid = this.getAccountID();
|
|
ret.id = this.getId();
|
|
ret.name = this.getName();
|
|
ret.gender = this.getGender();
|
|
ret.skinColor = this.getSkinColor();
|
|
ret.face = this.getFace();
|
|
ret.hair = this.getHair();
|
|
|
|
// skipping pets, probably unneeded here
|
|
ret.level = this.getLevel();
|
|
ret.job = this.getJob();
|
|
ret.str = this.getStr();
|
|
ret.dex = this.getDex();
|
|
ret.int_ = this.getInt();
|
|
ret.luk = this.getLuk();
|
|
ret.hp = this.getHp();
|
|
ret.setMaxHp(this.getMaxHp());
|
|
ret.mp = this.getMp();
|
|
ret.setMaxMp(this.getMaxMp());
|
|
ret.remainingAp = this.getRemainingAp();
|
|
ret.setRemainingSp(this.getRemainingSps());
|
|
ret.exp.set(this.getExp());
|
|
ret.fame = this.getFame();
|
|
ret.gachaexp.set(this.getGachaExp());
|
|
ret.mapid = this.getMapId();
|
|
ret.initialSpawnPoint = this.getInitialSpawnpoint();
|
|
|
|
ret.inventory[MapleInventoryType.EQUIPPED.ordinal()] = this.getInventory(MapleInventoryType.EQUIPPED);
|
|
|
|
ret.gmLevel = this.gmLevel();
|
|
ret.world = this.getWorld();
|
|
ret.rank = this.getRank();
|
|
ret.rankMove = this.getRankMove();
|
|
ret.jobRank = this.getJobRank();
|
|
ret.jobRankMove = this.getJobRankMove();
|
|
|
|
return ret;
|
|
}
|
|
|
|
private void loadCharSkillPoints(String[] skillPoints) {
|
|
int sps[] = new int[skillPoints.length];
|
|
for (int i = 0; i < skillPoints.length; i++) {
|
|
sps[i] = Integer.parseInt(skillPoints[i]);
|
|
}
|
|
|
|
setRemainingSp(sps);
|
|
}
|
|
|
|
public int getRemainingSp() {
|
|
return getRemainingSp(job.getId()); //default
|
|
}
|
|
|
|
public void updateRemainingSp(int remainingSp) {
|
|
updateRemainingSp(remainingSp, GameConstants.getSkillBook(job.getId()));
|
|
}
|
|
|
|
public static MapleCharacter loadCharFromDB(int charid, MapleClient client, boolean channelserver) throws SQLException {
|
|
try {
|
|
MapleCharacter ret = new MapleCharacter();
|
|
ret.client = client;
|
|
ret.id = charid;
|
|
|
|
Connection con = DatabaseConnection.getConnection();
|
|
PreparedStatement ps = con.prepareStatement("SELECT * FROM characters WHERE id = ?");
|
|
ps.setInt(1, charid);
|
|
ResultSet rs = ps.executeQuery();
|
|
if (!rs.next()) {
|
|
rs.close();
|
|
ps.close();
|
|
throw new RuntimeException("Loading char failed (not found)");
|
|
}
|
|
ret.name = rs.getString("name");
|
|
ret.level = rs.getInt("level");
|
|
ret.fame = rs.getInt("fame");
|
|
ret.quest_fame = rs.getInt("fquest");
|
|
ret.str = rs.getInt("str");
|
|
ret.dex = rs.getInt("dex");
|
|
ret.int_ = rs.getInt("int");
|
|
ret.luk = rs.getInt("luk");
|
|
ret.exp.set(rs.getInt("exp"));
|
|
ret.gachaexp.set(rs.getInt("gachaexp"));
|
|
ret.hp = rs.getInt("hp");
|
|
ret.setMaxHp(rs.getInt("maxhp"));
|
|
ret.mp = rs.getInt("mp");
|
|
ret.setMaxMp(rs.getInt("maxmp"));
|
|
ret.hpMpApUsed = rs.getInt("hpMpUsed");
|
|
ret.hasMerchant = rs.getInt("HasMerchant") == 1;
|
|
ret.remainingAp = rs.getInt("ap");
|
|
ret.loadCharSkillPoints(rs.getString("sp").split(","));
|
|
ret.meso.set(rs.getInt("meso"));
|
|
ret.merchantmeso = rs.getInt("MerchantMesos");
|
|
ret.gmLevel = rs.getInt("gm");
|
|
ret.skinColor = MapleSkinColor.getById(rs.getInt("skincolor"));
|
|
ret.gender = rs.getInt("gender");
|
|
ret.job = MapleJob.getById(rs.getInt("job"));
|
|
ret.finishedDojoTutorial = rs.getInt("finishedDojoTutorial") == 1;
|
|
ret.vanquisherKills = rs.getInt("vanquisherKills");
|
|
ret.omokwins = rs.getInt("omokwins");
|
|
ret.omoklosses = rs.getInt("omoklosses");
|
|
ret.omokties = rs.getInt("omokties");
|
|
ret.matchcardwins = rs.getInt("matchcardwins");
|
|
ret.matchcardlosses = rs.getInt("matchcardlosses");
|
|
ret.matchcardties = rs.getInt("matchcardties");
|
|
ret.hair = rs.getInt("hair");
|
|
ret.face = rs.getInt("face");
|
|
ret.accountid = rs.getInt("accountid");
|
|
ret.mapid = rs.getInt("map");
|
|
ret.jailExpiration = rs.getLong("jailexpire");
|
|
ret.initialSpawnPoint = rs.getInt("spawnpoint");
|
|
ret.world = rs.getByte("world");
|
|
ret.rank = rs.getInt("rank");
|
|
ret.rankMove = rs.getInt("rankMove");
|
|
ret.jobRank = rs.getInt("jobRank");
|
|
ret.jobRankMove = rs.getInt("jobRankMove");
|
|
int mountexp = rs.getInt("mountexp");
|
|
int mountlevel = rs.getInt("mountlevel");
|
|
int mounttiredness = rs.getInt("mounttiredness");
|
|
ret.guildid = rs.getInt("guildid");
|
|
ret.guildRank = rs.getInt("guildrank");
|
|
ret.allianceRank = rs.getInt("allianceRank");
|
|
ret.familyId = rs.getInt("familyId");
|
|
ret.bookCover = rs.getInt("monsterbookcover");
|
|
ret.monsterbook = new MonsterBook();
|
|
ret.monsterbook.loadCards(charid);
|
|
ret.vanquisherStage = rs.getInt("vanquisherStage");
|
|
ret.dojoPoints = rs.getInt("dojoPoints");
|
|
ret.dojoStage = rs.getInt("lastDojoStage");
|
|
ret.dataString = rs.getString("dataString");
|
|
ret.mgc = new MapleGuildCharacter(ret);
|
|
int buddyCapacity = rs.getInt("buddyCapacity");
|
|
ret.buddylist = new BuddyList(buddyCapacity);
|
|
ret.lastExpGainTime = rs.getTimestamp("lastExpGainTime").getTime();
|
|
|
|
ret.getInventory(MapleInventoryType.EQUIP).setSlotLimit(rs.getByte("equipslots"));
|
|
ret.getInventory(MapleInventoryType.USE).setSlotLimit(rs.getByte("useslots"));
|
|
ret.getInventory(MapleInventoryType.SETUP).setSlotLimit(rs.getByte("setupslots"));
|
|
ret.getInventory(MapleInventoryType.ETC).setSlotLimit(rs.getByte("etcslots"));
|
|
|
|
byte sandboxCheck = 0x0;
|
|
for (Pair<Item, MapleInventoryType> item : ItemFactory.INVENTORY.loadItems(ret.id, !channelserver)) {
|
|
sandboxCheck |= item.getLeft().getFlag();
|
|
|
|
ret.getInventory(item.getRight()).addItemFromDB(item.getLeft());
|
|
Item itemz = item.getLeft();
|
|
if (itemz.getPetId() > -1) {
|
|
MaplePet pet = itemz.getPet();
|
|
if (pet != null && pet.isSummoned()) {
|
|
ret.addPet(pet);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
MapleInventoryType mit = item.getRight();
|
|
if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) {
|
|
Equip equip = (Equip) item.getLeft();
|
|
if (equip.getRingId() > -1) {
|
|
MapleRing ring = MapleRing.loadFromDb(equip.getRingId());
|
|
if (item.getRight().equals(MapleInventoryType.EQUIPPED)) {
|
|
ring.equip();
|
|
}
|
|
|
|
ret.addPlayerRing(ring);
|
|
}
|
|
}
|
|
}
|
|
if ((sandboxCheck & ItemConstants.SANDBOX) == ItemConstants.SANDBOX) {
|
|
ret.setHasSandboxItem();
|
|
}
|
|
|
|
World wserv = Server.getInstance().getWorld(ret.world);
|
|
|
|
ret.partnerId = rs.getInt("partnerId");
|
|
ret.marriageItemid = rs.getInt("marriageItemId");
|
|
if (ret.marriageItemid > 0 && ret.partnerId <= 0) {
|
|
ret.marriageItemid = -1;
|
|
} else if (ret.partnerId > 0 && wserv.getRelationshipId(ret.id) <= 0) {
|
|
ret.marriageItemid = -1;
|
|
ret.partnerId = -1;
|
|
}
|
|
|
|
NewYearCardRecord.loadPlayerNewYearCards(ret);
|
|
|
|
PreparedStatement ps2, ps3;
|
|
ResultSet rs2, rs3;
|
|
|
|
ps3 = con.prepareStatement("SELECT petid FROM inventoryitems WHERE characterid = ? AND petid > -1");
|
|
ps3.setInt(1, charid);
|
|
rs3 = ps3.executeQuery();
|
|
while (rs3.next()) {
|
|
int petId = rs3.getInt("petid");
|
|
|
|
ps2 = con.prepareStatement("SELECT itemid FROM petignores WHERE petid = ?");
|
|
ps2.setInt(1, petId);
|
|
|
|
ret.resetExcluded(petId);
|
|
|
|
rs2 = ps2.executeQuery();
|
|
while (rs2.next()) {
|
|
ret.addExcluded(petId, rs2.getInt("itemid"));
|
|
}
|
|
|
|
ps2.close();
|
|
rs2.close();
|
|
}
|
|
ps3.close();
|
|
rs3.close();
|
|
|
|
ret.commitExcludedItems();
|
|
|
|
if (channelserver) {
|
|
MapleMapFactory mapFactory = client.getChannelServer().getMapFactory();
|
|
ret.map = mapFactory.getMap(ret.mapid);
|
|
|
|
if (ret.map == null) {
|
|
ret.map = mapFactory.getMap(100000000);
|
|
}
|
|
MaplePortal portal = ret.map.getPortal(ret.initialSpawnPoint);
|
|
if (portal == null) {
|
|
portal = ret.map.getPortal(0);
|
|
ret.initialSpawnPoint = 0;
|
|
}
|
|
ret.setPosition(portal.getPosition());
|
|
int partyid = rs.getInt("party");
|
|
MapleParty party = wserv.getParty(partyid);
|
|
if (party != null) {
|
|
ret.mpc = party.getMemberById(ret.id);
|
|
if (ret.mpc != null) {
|
|
ret.mpc = new MaplePartyCharacter(ret);
|
|
ret.party = party;
|
|
}
|
|
}
|
|
int messengerid = rs.getInt("messengerid");
|
|
int position = rs.getInt("messengerposition");
|
|
if (messengerid > 0 && position < 4 && position > -1) {
|
|
MapleMessenger messenger = wserv.getMessenger(messengerid);
|
|
if (messenger != null) {
|
|
ret.messenger = messenger;
|
|
ret.messengerposition = position;
|
|
}
|
|
}
|
|
ret.loggedIn = true;
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
ps = con.prepareStatement("SELECT mapid,vip FROM trocklocations WHERE characterid = ? LIMIT 15");
|
|
ps.setInt(1, charid);
|
|
rs = ps.executeQuery();
|
|
byte v = 0;
|
|
byte r = 0;
|
|
while (rs.next()) {
|
|
if (rs.getInt("vip") == 1) {
|
|
ret.viptrockmaps.add(rs.getInt("mapid"));
|
|
v++;
|
|
} else {
|
|
ret.trockmaps.add(rs.getInt("mapid"));
|
|
r++;
|
|
}
|
|
}
|
|
while (v < 10) {
|
|
ret.viptrockmaps.add(999999999);
|
|
v++;
|
|
}
|
|
while (r < 5) {
|
|
ret.trockmaps.add(999999999);
|
|
r++;
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
ps = con.prepareStatement("SELECT name, characterslots FROM accounts WHERE id = ?", Statement.RETURN_GENERATED_KEYS);
|
|
ps.setInt(1, ret.accountid);
|
|
rs = ps.executeQuery();
|
|
if (rs.next()) {
|
|
MapleClient retClient = ret.getClient();
|
|
|
|
retClient.setAccountName(rs.getString("name"));
|
|
retClient.setCharacterSlots(rs.getByte("characterslots"));
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
ps = con.prepareStatement("SELECT `area`,`info` FROM area_info WHERE charid = ?");
|
|
ps.setInt(1, ret.id);
|
|
rs = ps.executeQuery();
|
|
while (rs.next()) {
|
|
ret.area_info.put(rs.getShort("area"), rs.getString("info"));
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
ps = con.prepareStatement("SELECT `name`,`info` FROM eventstats WHERE characterid = ?");
|
|
ps.setInt(1, ret.id);
|
|
rs = ps.executeQuery();
|
|
while (rs.next()) {
|
|
String name = rs.getString("name");
|
|
if (rs.getString("name").contentEquals("rescueGaga")) {
|
|
ret.events.put(name, new RescueGaga(rs.getInt("info")));
|
|
}
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
ret.cashshop = new CashShop(ret.accountid, ret.id, ret.getJobType());
|
|
ret.autoban = new AutobanManager(ret);
|
|
ps = con.prepareStatement("SELECT name, level FROM characters WHERE accountid = ? AND id != ? ORDER BY level DESC limit 1");
|
|
ps.setInt(1, ret.accountid);
|
|
ps.setInt(2, charid);
|
|
rs = ps.executeQuery();
|
|
if (rs.next()) {
|
|
ret.linkedName = rs.getString("name");
|
|
ret.linkedLevel = rs.getInt("level");
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
if (channelserver) {
|
|
ps = con.prepareStatement("SELECT * FROM queststatus WHERE characterid = ?");
|
|
ps.setInt(1, charid);
|
|
rs = ps.executeQuery();
|
|
|
|
Map<Integer, MapleQuestStatus> loadedQuestStatus = new LinkedHashMap<>();
|
|
while (rs.next()) {
|
|
MapleQuest q = MapleQuest.getInstance(rs.getShort("quest"));
|
|
MapleQuestStatus status = new MapleQuestStatus(q, MapleQuestStatus.Status.getById(rs.getInt("status")));
|
|
long cTime = rs.getLong("time");
|
|
if (cTime > -1) {
|
|
status.setCompletionTime(cTime * 1000);
|
|
}
|
|
|
|
long eTime = rs.getLong("expires");
|
|
if (eTime > 0) {
|
|
status.setExpirationTime(eTime);
|
|
}
|
|
|
|
status.setForfeited(rs.getInt("forfeited"));
|
|
ret.quests.put(q.getId(), status);
|
|
loadedQuestStatus.put(rs.getInt("queststatusid"), status);
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
|
|
// opportunity for improvement on questprogress/medalmaps calls to DB
|
|
try (PreparedStatement pse = con.prepareStatement("SELECT * FROM questprogress WHERE characterid = ?")) {
|
|
pse.setInt(1, charid);
|
|
try (ResultSet rsProgress = pse.executeQuery()) {
|
|
while (rsProgress.next()) {
|
|
MapleQuestStatus status = loadedQuestStatus.get(rsProgress.getInt("queststatusid"));
|
|
if (status != null) {
|
|
status.setProgress(rsProgress.getInt("progressid"), rsProgress.getString("progress"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
try (PreparedStatement pse = con.prepareStatement("SELECT * FROM medalmaps WHERE characterid = ?")) {
|
|
pse.setInt(1, charid);
|
|
try (ResultSet rsMedalMaps = pse.executeQuery()) {
|
|
while (rsMedalMaps.next()) {
|
|
MapleQuestStatus status = loadedQuestStatus.get(rsMedalMaps.getInt("queststatusid"));
|
|
if (status != null) {
|
|
status.addMedalMap(rsMedalMaps.getInt("mapid"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
loadedQuestStatus.clear();
|
|
|
|
ps = con.prepareStatement("SELECT skillid,skilllevel,masterlevel,expiration FROM skills WHERE characterid = ?");
|
|
ps.setInt(1, charid);
|
|
rs = ps.executeQuery();
|
|
while (rs.next()) {
|
|
ret.skills.put(SkillFactory.getSkill(rs.getInt("skillid")), new SkillEntry(rs.getByte("skilllevel"), rs.getInt("masterlevel"), rs.getLong("expiration")));
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
ps = con.prepareStatement("SELECT SkillID,StartTime,length FROM cooldowns WHERE charid = ?");
|
|
ps.setInt(1, ret.getId());
|
|
rs = ps.executeQuery();
|
|
long curTime = Server.getInstance().getCurrentTime();
|
|
while (rs.next()) {
|
|
final int skillid = rs.getInt("SkillID");
|
|
final long length = rs.getLong("length"), startTime = rs.getLong("StartTime");
|
|
if (skillid != 5221999 && (length + startTime < curTime)) {
|
|
continue;
|
|
}
|
|
ret.giveCoolDowns(skillid, startTime, length);
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
ps = con.prepareStatement("DELETE FROM cooldowns WHERE charid = ?");
|
|
ps.setInt(1, ret.getId());
|
|
ps.executeUpdate();
|
|
ps.close();
|
|
Map<MapleDisease, Pair<Long, MobSkill>> loadedDiseases = new LinkedHashMap<>();
|
|
ps = con.prepareStatement("SELECT * FROM playerdiseases WHERE charid = ?");
|
|
ps.setInt(1, ret.getId());
|
|
rs = ps.executeQuery();
|
|
while (rs.next()) {
|
|
final MapleDisease disease = MapleDisease.ordinal(rs.getInt("disease"));
|
|
if (disease == MapleDisease.NULL) {
|
|
continue;
|
|
}
|
|
|
|
final int skillid = rs.getInt("mobskillid"), skilllv = rs.getInt("mobskilllv");
|
|
final long length = rs.getInt("length");
|
|
|
|
MobSkill ms = MobSkillFactory.getMobSkill(skillid, skilllv);
|
|
if (ms != null) {
|
|
loadedDiseases.put(disease, new Pair<>(length, ms));
|
|
}
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
ps = con.prepareStatement("DELETE FROM playerdiseases WHERE charid = ?");
|
|
ps.setInt(1, ret.getId());
|
|
ps.executeUpdate();
|
|
ps.close();
|
|
if (!loadedDiseases.isEmpty()) {
|
|
Server.getInstance().getPlayerBuffStorage().addDiseasesToStorage(ret.id, loadedDiseases);
|
|
}
|
|
ps = con.prepareStatement("SELECT * FROM skillmacros WHERE characterid = ?");
|
|
ps.setInt(1, charid);
|
|
rs = ps.executeQuery();
|
|
while (rs.next()) {
|
|
int position = rs.getInt("position");
|
|
SkillMacro macro = new SkillMacro(rs.getInt("skill1"), rs.getInt("skill2"), rs.getInt("skill3"), rs.getString("name"), rs.getInt("shout"), position);
|
|
ret.skillMacros[position] = macro;
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
ps = con.prepareStatement("SELECT `key`,`type`,`action` FROM keymap WHERE characterid = ?");
|
|
ps.setInt(1, charid);
|
|
rs = ps.executeQuery();
|
|
while (rs.next()) {
|
|
int key = rs.getInt("key");
|
|
int type = rs.getInt("type");
|
|
int action = rs.getInt("action");
|
|
ret.keymap.put(Integer.valueOf(key), new MapleKeyBinding(type, action));
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
ps = con.prepareStatement("SELECT `locationtype`,`map`,`portal` FROM savedlocations WHERE characterid = ?");
|
|
ps.setInt(1, charid);
|
|
rs = ps.executeQuery();
|
|
while (rs.next()) {
|
|
ret.savedLocations[SavedLocationType.valueOf(rs.getString("locationtype")).ordinal()] = new SavedLocation(rs.getInt("map"), rs.getInt("portal"));
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
ps = con.prepareStatement("SELECT `characterid_to`,`when` FROM famelog WHERE characterid = ? AND DATEDIFF(NOW(),`when`) < 30");
|
|
ps.setInt(1, charid);
|
|
rs = ps.executeQuery();
|
|
ret.lastfametime = 0;
|
|
ret.lastmonthfameids = new ArrayList<>(31);
|
|
while (rs.next()) {
|
|
ret.lastfametime = Math.max(ret.lastfametime, rs.getTimestamp("when").getTime());
|
|
ret.lastmonthfameids.add(Integer.valueOf(rs.getInt("characterid_to")));
|
|
}
|
|
rs.close();
|
|
ps.close();
|
|
ret.buddylist.loadFromDb(charid);
|
|
ret.storage = MapleStorage.loadOrCreateFromDB(ret.accountid, ret.world);
|
|
|
|
int startHp = ret.hp, startMp = ret.mp;
|
|
ret.reapplyLocalStats();
|
|
ret.changeHpMp(startHp, startMp, true);
|
|
//ret.resetBattleshipHp();
|
|
}
|
|
int mountid = ret.getJobType() * 10000000 + 1004;
|
|
if (ret.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -18) != null) {
|
|
ret.maplemount = new MapleMount(ret, ret.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -18).getItemId(), mountid);
|
|
} else {
|
|
ret.maplemount = new MapleMount(ret, 0, mountid);
|
|
}
|
|
ret.maplemount.setExp(mountexp);
|
|
ret.maplemount.setLevel(mountlevel);
|
|
ret.maplemount.setTiredness(mounttiredness);
|
|
ret.maplemount.setActive(false);
|
|
|
|
con.close();
|
|
return ret;
|
|
} catch (SQLException | RuntimeException e) {
|
|
e.printStackTrace();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void reloadQuestExpirations() {
|
|
for (MapleQuestStatus mqs : quests.values()) {
|
|
if (mqs.getExpirationTime() > 0) {
|
|
questTimeLimit2(mqs.getQuest(), mqs.getExpirationTime());
|
|
}
|
|
}
|
|
}
|
|
|
|
public static String makeMapleReadable(String in) {
|
|
String i = in.replace('I', 'i');
|
|
i = i.replace('l', 'L');
|
|
i = i.replace("rn", "Rn");
|
|
i = i.replace("vv", "Vv");
|
|
i = i.replace("VV", "Vv");
|
|
|
|
return i;
|
|
}
|
|
|
|
private static class MapleBuffStatValueHolder {
|
|
|
|
public MapleStatEffect effect;
|
|
public long startTime;
|
|
public int value;
|
|
public boolean bestApplied;
|
|
|
|
public MapleBuffStatValueHolder(MapleStatEffect effect, long startTime, int value) {
|
|
super();
|
|
this.effect = effect;
|
|
this.startTime = startTime;
|
|
this.value = value;
|
|
this.bestApplied = false;
|
|
}
|
|
}
|
|
|
|
public static class MapleCoolDownValueHolder {
|
|
|
|
public int skillId;
|
|
public long startTime, length;
|
|
|
|
public MapleCoolDownValueHolder(int skillId, long startTime, long length) {
|
|
super();
|
|
this.skillId = skillId;
|
|
this.startTime = startTime;
|
|
this.length = length;
|
|
}
|
|
}
|
|
|
|
public void message(String m) {
|
|
dropMessage(5, m);
|
|
}
|
|
|
|
public void yellowMessage(String m) {
|
|
announce(MaplePacketCreator.sendYellowTip(m));
|
|
}
|
|
|
|
public void updateQuestMobCount(int id) {
|
|
// It seems nexon uses monsters that don't exist in the WZ (except string) to merge multiple mobs together for these 3 monsters.
|
|
// We also want to run mobKilled for both since there are some quest that don't use the updated ID...
|
|
if (id == 1110100 || id == 1110130) {
|
|
updateQuestMobCount(9101000);
|
|
} else if (id == 2230101 || id == 2230131) {
|
|
updateQuestMobCount(9101001);
|
|
} else if (id == 1140100 || id == 1140130) {
|
|
updateQuestMobCount(9101002);
|
|
}
|
|
|
|
int lastQuestProcessed = 0;
|
|
try {
|
|
synchronized (quests) {
|
|
for (MapleQuestStatus q : quests.values()) {
|
|
lastQuestProcessed = q.getQuest().getId();
|
|
if (q.getStatus() == MapleQuestStatus.Status.COMPLETED || q.getQuest().canComplete(this, null)) {
|
|
continue;
|
|
}
|
|
String progress = q.getProgress(id);
|
|
if (!progress.isEmpty() && Integer.parseInt(progress) >= q.getQuest().getMobAmountNeeded(id)) {
|
|
continue;
|
|
}
|
|
if (q.progress(id)) {
|
|
client.announce(MaplePacketCreator.updateQuest(q, false));
|
|
}
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, e, "MapleCharacter.mobKilled. CID: " + this.id + " last Quest Processed: " + lastQuestProcessed);
|
|
}
|
|
}
|
|
|
|
public void mount(int id, int skillid) {
|
|
maplemount = new MapleMount(this, id, skillid);
|
|
}
|
|
|
|
private void playerDead() {
|
|
if (this.getMap().isCPQMap()) {
|
|
int losing = getMap().getDeathCP();
|
|
if (getCP() < losing) {
|
|
losing = getCP();
|
|
}
|
|
getMap().broadcastMessage(MaplePacketCreator.playerDiedMessage(getName(), losing, getTeam()));
|
|
gainCP(-losing);
|
|
return;
|
|
}
|
|
cancelAllBuffs(false);
|
|
dispelDebuffs();
|
|
lastDeathtime = Server.getInstance().getCurrentTime();
|
|
|
|
EventInstanceManager eim = getEventInstance();
|
|
if (eim != null) {
|
|
eim.playerKilled(this);
|
|
}
|
|
int[] charmID = {5130000, 4031283, 4140903};
|
|
int possesed = 0;
|
|
int i;
|
|
for (i = 0; i < charmID.length; i++) {
|
|
int quantity = getItemQuantity(charmID[i], false);
|
|
if (possesed == 0 && quantity > 0) {
|
|
possesed = quantity;
|
|
break;
|
|
}
|
|
}
|
|
if (possesed > 0) {
|
|
message("You have used a safety charm, so your EXP points have not been decreased.");
|
|
MapleInventoryManipulator.removeById(client, ItemConstants.getInventoryType(charmID[i]), charmID[i], 1, true, false);
|
|
} else if (mapid > 925020000 && mapid < 925030000) {
|
|
this.dojoStage = 0;
|
|
} else if (getJob() != MapleJob.BEGINNER) { //Hmm...
|
|
int XPdummy = ExpTable.getExpNeededForLevel(getLevel());
|
|
if (getMap().isTown()) {
|
|
XPdummy /= 100;
|
|
}
|
|
if (XPdummy == ExpTable.getExpNeededForLevel(getLevel())) {
|
|
if (getLuk() <= 100 && getLuk() > 8) {
|
|
XPdummy *= (200 - getLuk()) / 2000;
|
|
} else if (getLuk() < 8) {
|
|
XPdummy /= 10;
|
|
} else {
|
|
XPdummy /= 20;
|
|
}
|
|
}
|
|
if (getExp() > XPdummy) {
|
|
loseExp(XPdummy, false, false);
|
|
} else {
|
|
loseExp(getExp(), false, false);
|
|
}
|
|
}
|
|
if (getBuffedValue(MapleBuffStat.MORPH) != null) {
|
|
cancelEffectFromBuffStat(MapleBuffStat.MORPH);
|
|
}
|
|
|
|
if (getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null) {
|
|
cancelEffectFromBuffStat(MapleBuffStat.MONSTER_RIDING);
|
|
}
|
|
|
|
unsitChairInternal();
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
}
|
|
|
|
private void unsitChairInternal() {
|
|
if (chair.get() != 0) {
|
|
setChair(0);
|
|
if (unregisterChairBuff()) {
|
|
getMap().broadcastMessage(this, MaplePacketCreator.cancelForeignChairSkillEffect(this.getId()), false);
|
|
}
|
|
|
|
getMap().broadcastMessage(this, MaplePacketCreator.showChair(this.getId(), 0), false);
|
|
}
|
|
|
|
announce(MaplePacketCreator.cancelChair(-1));
|
|
}
|
|
|
|
public void sitChair(int itemId) {
|
|
if (client.tryacquireClient()) {
|
|
try {
|
|
if (itemId >= 1000000) { // sit on item chair
|
|
if (chair.get() == 0) {
|
|
setChair(itemId);
|
|
getMap().broadcastMessage(this, MaplePacketCreator.showChair(this.getId(), itemId), false);
|
|
}
|
|
announce(MaplePacketCreator.enableActions());
|
|
} else if (itemId != 0) { // sit on map chair
|
|
if (chair.get() == 0) {
|
|
setChair(itemId);
|
|
if (registerChairBuff()) {
|
|
getMap().broadcastMessage(this, MaplePacketCreator.giveForeignChairSkillEffect(this.getId()), false);
|
|
}
|
|
announce(MaplePacketCreator.cancelChair(itemId));
|
|
}
|
|
} else { // stand up
|
|
unsitChairInternal();
|
|
}
|
|
} finally {
|
|
client.releaseClient();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void setChair(int chair) {
|
|
this.chair.set(chair);
|
|
}
|
|
|
|
public void respawn(int returnMap) {
|
|
respawn(null, returnMap); // unspecified EIM, don't force EIM unregister in this case
|
|
}
|
|
|
|
public void respawn(EventInstanceManager eim, int returnMap) {
|
|
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) {
|
|
if (dragonBloodSchedule != null) {
|
|
dragonBloodSchedule.cancel(false);
|
|
}
|
|
dragonBloodSchedule = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (awayFromWorld.get()) {
|
|
return;
|
|
}
|
|
|
|
addHP(-bloodEffect.getX());
|
|
announce(MaplePacketCreator.showOwnBuffEffect(bloodEffect.getSourceId(), 5));
|
|
getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showBuffeffect(getId(), bloodEffect.getSourceId(), 5), false);
|
|
}
|
|
}, 4000, 4000);
|
|
}
|
|
|
|
private void recalcEquipStats() {
|
|
if (equipchanged) {
|
|
equipmaxhp = 0;
|
|
equipmaxmp = 0;
|
|
equipdex = 0;
|
|
equipint_ = 0;
|
|
equipstr = 0;
|
|
equipluk = 0;
|
|
equipmagic = 0;
|
|
equipwatk = 0;
|
|
//equipspeed = 0;
|
|
//equipjump = 0;
|
|
|
|
for (Item item : getInventory(MapleInventoryType.EQUIPPED)) {
|
|
Equip equip = (Equip) item;
|
|
equipmaxhp += equip.getHp();
|
|
equipmaxmp += equip.getMp();
|
|
equipdex += equip.getDex();
|
|
equipint_ += equip.getInt();
|
|
equipstr += equip.getStr();
|
|
equipluk += equip.getLuk();
|
|
equipmagic += equip.getMatk() + equip.getInt();
|
|
equipwatk += equip.getWatk();
|
|
//equipspeed += equip.getSpeed();
|
|
//equipjump += equip.getJump();
|
|
}
|
|
|
|
equipchanged = false;
|
|
}
|
|
|
|
localmaxhp += equipmaxhp;
|
|
localmaxmp += equipmaxmp;
|
|
localdex += equipdex;
|
|
localint_ += equipint_;
|
|
localstr += equipstr;
|
|
localluk += equipluk;
|
|
localmagic += equipmagic;
|
|
localwatk += equipwatk;
|
|
}
|
|
|
|
private void reapplyLocalStats() {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
statWlock.lock();
|
|
try {
|
|
localmaxhp = getMaxHp();
|
|
localmaxmp = getMaxMp();
|
|
localdex = getDex();
|
|
localint_ = getInt();
|
|
localstr = getStr();
|
|
localluk = getLuk();
|
|
localmagic = localint_;
|
|
localwatk = 0;
|
|
localchairrate = -1;
|
|
|
|
recalcEquipStats();
|
|
|
|
localmagic = Math.min(localmagic, 2000);
|
|
|
|
Integer hbhp = getBuffedValue(MapleBuffStat.HYPERBODYHP);
|
|
if (hbhp != null) {
|
|
localmaxhp += (hbhp.doubleValue() / 100) * localmaxhp;
|
|
}
|
|
Integer hbmp = getBuffedValue(MapleBuffStat.HYPERBODYMP);
|
|
if (hbmp != null) {
|
|
localmaxmp += (hbmp.doubleValue() / 100) * localmaxmp;
|
|
}
|
|
|
|
localmaxhp = Math.min(30000, localmaxhp);
|
|
localmaxmp = Math.min(30000, localmaxmp);
|
|
|
|
MapleStatEffect combo = getBuffEffect(MapleBuffStat.ARAN_COMBO);
|
|
if (combo != null) {
|
|
localwatk += combo.getX();
|
|
}
|
|
|
|
if (energybar == 15000) {
|
|
Skill energycharge = isCygnus() ? SkillFactory.getSkill(ThunderBreaker.ENERGY_CHARGE) : SkillFactory.getSkill(Marauder.ENERGY_CHARGE);
|
|
MapleStatEffect ceffect = energycharge.getEffect(getSkillLevel(energycharge));
|
|
localwatk += ceffect.getWatk();
|
|
}
|
|
|
|
Integer mwarr = getBuffedValue(MapleBuffStat.MAPLE_WARRIOR);
|
|
if (mwarr != null) {
|
|
localstr += getStr() * mwarr / 100;
|
|
localdex += getDex() * mwarr / 100;
|
|
localint_ += getInt() * mwarr / 100;
|
|
localluk += getLuk() * mwarr / 100;
|
|
}
|
|
if (job.isA(MapleJob.BOWMAN)) {
|
|
Skill expert = null;
|
|
if (job.isA(MapleJob.MARKSMAN)) {
|
|
expert = SkillFactory.getSkill(3220004);
|
|
} else if (job.isA(MapleJob.BOWMASTER)) {
|
|
expert = SkillFactory.getSkill(3120005);
|
|
}
|
|
if (expert != null) {
|
|
int boostLevel = getSkillLevel(expert);
|
|
if (boostLevel > 0) {
|
|
localwatk += expert.getEffect(boostLevel).getX();
|
|
}
|
|
}
|
|
}
|
|
|
|
Integer watkbuff = getBuffedValue(MapleBuffStat.WATK);
|
|
if (watkbuff != null) {
|
|
localwatk += watkbuff.intValue();
|
|
}
|
|
Integer matkbuff = getBuffedValue(MapleBuffStat.MATK);
|
|
if (matkbuff != null) {
|
|
localmagic += matkbuff.intValue();
|
|
}
|
|
|
|
/*
|
|
Integer speedbuff = getBuffedValue(MapleBuffStat.SPEED);
|
|
if (speedbuff != null) {
|
|
localspeed += speedbuff.intValue();
|
|
}
|
|
Integer jumpbuff = getBuffedValue(MapleBuffStat.JUMP);
|
|
if (jumpbuff != null) {
|
|
localjump += jumpbuff.intValue();
|
|
}
|
|
*/
|
|
Integer blessing = getSkillLevel(10000000 * getJobType() + 12);
|
|
if (blessing > 0) {
|
|
localwatk += blessing;
|
|
localmagic += blessing * 2;
|
|
}
|
|
|
|
if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.PIRATE) || job.isA(MapleJob.NIGHTWALKER1) || job.isA(MapleJob.WINDARCHER1)) {
|
|
Item weapon_item = getInventory(MapleInventoryType.EQUIPPED).getItem((short) -11);
|
|
if (weapon_item != null) {
|
|
MapleWeaponType weapon = ii.getWeaponType(weapon_item.getItemId());
|
|
boolean bow = weapon == MapleWeaponType.BOW;
|
|
boolean crossbow = weapon == MapleWeaponType.CROSSBOW;
|
|
boolean claw = weapon == MapleWeaponType.CLAW;
|
|
boolean gun = weapon == MapleWeaponType.GUN;
|
|
if (bow || crossbow || claw || gun) {
|
|
// Also calc stars into this.
|
|
MapleInventory inv = getInventory(MapleInventoryType.USE);
|
|
for (short i = 1; i <= inv.getSlotLimit(); i++) {
|
|
Item item = inv.getItem(i);
|
|
if (item != null) {
|
|
if ((claw && ItemConstants.isThrowingStar(item.getItemId())) || (gun && ItemConstants.isBullet(item.getItemId())) || (bow && ItemConstants.isArrowForBow(item.getItemId())) || (crossbow && ItemConstants.isArrowForCrossBow(item.getItemId()))) {
|
|
if (item.getQuantity() > 0) {
|
|
// Finally there!
|
|
localwatk += ii.getWatkForProjectile(item.getItemId());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Add throwing stars to dmg.
|
|
}
|
|
} finally {
|
|
statWlock.unlock();
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
private List<Pair<MapleStat, Integer>> recalcLocalStats() {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
statWlock.lock();
|
|
try {
|
|
List<Pair<MapleStat, Integer>> hpmpupdate = new ArrayList<>(2);
|
|
int oldlocalmaxhp = localmaxhp;
|
|
int oldlocalmaxmp = localmaxmp;
|
|
|
|
reapplyLocalStats();
|
|
|
|
if (ServerConstants.USE_FIXED_RATIO_HPMP_UPDATE) {
|
|
if (localmaxhp != oldlocalmaxhp) {
|
|
Pair<MapleStat, Integer> hpUpdate;
|
|
|
|
if (transienthp == Float.NEGATIVE_INFINITY) {
|
|
hpUpdate = calcHpRatioUpdate(localmaxhp, oldlocalmaxhp);
|
|
} else {
|
|
hpUpdate = calcHpRatioTransient();
|
|
}
|
|
|
|
hpmpupdate.add(hpUpdate);
|
|
}
|
|
|
|
if (localmaxmp != oldlocalmaxmp) {
|
|
Pair<MapleStat, Integer> mpUpdate;
|
|
|
|
if (transientmp == Float.NEGATIVE_INFINITY) {
|
|
mpUpdate = calcMpRatioUpdate(localmaxmp, oldlocalmaxmp);
|
|
} else {
|
|
mpUpdate = calcMpRatioTransient();
|
|
}
|
|
|
|
hpmpupdate.add(mpUpdate);
|
|
}
|
|
}
|
|
|
|
return hpmpupdate;
|
|
} finally {
|
|
statWlock.unlock();
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
private void updateLocalStats() {
|
|
effLock.lock();
|
|
statWlock.lock();
|
|
try {
|
|
int oldmaxhp = localmaxhp;
|
|
List<Pair<MapleStat, Integer>> hpmpupdate = recalcLocalStats();
|
|
enforceMaxHpMp();
|
|
|
|
if (!hpmpupdate.isEmpty()) {
|
|
client.announce(MaplePacketCreator.updatePlayerStats(hpmpupdate, true, this));
|
|
}
|
|
|
|
if (oldmaxhp != localmaxhp) {
|
|
updatePartyMemberHP();
|
|
}
|
|
} finally {
|
|
statWlock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void receivePartyMemberHP() {
|
|
prtLock.lock();
|
|
try {
|
|
if (party != null) {
|
|
for (MapleCharacter partychar : this.getPartyMembersOnSameMap()) {
|
|
announce(MaplePacketCreator.updatePartyMemberHP(partychar.getId(), partychar.getHp(), partychar.getCurrentMaxHp()));
|
|
}
|
|
}
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void removeAllCooldownsExcept(int id, boolean packet) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
ArrayList<MapleCoolDownValueHolder> list = new ArrayList<>(coolDowns.values());
|
|
for (MapleCoolDownValueHolder mcvh : list) {
|
|
if (mcvh.skillId != id) {
|
|
coolDowns.remove(mcvh.skillId);
|
|
if (packet) {
|
|
client.announce(MaplePacketCreator.skillCooldown(mcvh.skillId, 0));
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public static void removeAriantRoom(int room) {
|
|
ariantroomleader[room] = "";
|
|
ariantroomslot[room] = 0;
|
|
}
|
|
|
|
public void removeCooldown(int skillId) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
if (this.coolDowns.containsKey(skillId)) {
|
|
this.coolDowns.remove(skillId);
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void removePet(MaplePet pet, boolean shift_left) {
|
|
petLock.lock();
|
|
try {
|
|
int slot = -1;
|
|
for (int i = 0; i < 3; i++) {
|
|
if (pets[i] != null) {
|
|
if (pets[i].getUniqueId() == pet.getUniqueId()) {
|
|
pets[i] = null;
|
|
slot = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (shift_left) {
|
|
if (slot > -1) {
|
|
for (int i = slot; i < 3; i++) {
|
|
if (i != 2) {
|
|
pets[i] = pets[i + 1];
|
|
} else {
|
|
pets[i] = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void removeVisibleMapObject(MapleMapObject mo) {
|
|
visibleMapObjects.remove(mo);
|
|
}
|
|
|
|
public synchronized void resetStats() {
|
|
if (!ServerConstants.USE_AUTOASSIGN_STARTERS_AP) {
|
|
return;
|
|
}
|
|
|
|
effLock.lock();
|
|
statWlock.lock();
|
|
try {
|
|
int tap = remainingAp + str + dex + int_ + luk, tsp = 1;
|
|
int tstr = 4, tdex = 4, tint = 4, tluk = 4;
|
|
|
|
switch (job.getId()) {
|
|
case 100:
|
|
case 1100:
|
|
case 2100:
|
|
tstr = 35;
|
|
tsp += ((getLevel() - 10) * 3);
|
|
break;
|
|
case 200:
|
|
case 1200:
|
|
tint = 20;
|
|
tsp += ((getLevel() - 8) * 3);
|
|
break;
|
|
case 300:
|
|
case 1300:
|
|
case 400:
|
|
case 1400:
|
|
tdex = 25;
|
|
tsp += ((getLevel() - 10) * 3);
|
|
break;
|
|
case 500:
|
|
case 1500:
|
|
tdex = 20;
|
|
tsp += ((getLevel() - 10) * 3);
|
|
break;
|
|
}
|
|
|
|
tap -= tstr;
|
|
tap -= tdex;
|
|
tap -= tint;
|
|
tap -= tluk;
|
|
|
|
if (tap >= 0) {
|
|
updateStrDexIntLukSp(tstr, tdex, tint, tluk, tap, tsp, GameConstants.getSkillBook(job.getId()));
|
|
} else {
|
|
FilePrinter.print(FilePrinter.EXCEPTION_CAUGHT, name + " tried to get their stats reseted, without having enough AP available.");
|
|
}
|
|
} finally {
|
|
statWlock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void resetBattleshipHp() {
|
|
this.battleshipHp = 400 * getSkillLevel(SkillFactory.getSkill(Corsair.BATTLE_SHIP)) + ((getLevel() - 120) * 200);
|
|
}
|
|
|
|
public void resetEnteredScript() {
|
|
if (entered.containsKey(map.getId())) {
|
|
entered.remove(map.getId());
|
|
}
|
|
}
|
|
|
|
public void resetEnteredScript(int mapId) {
|
|
if (entered.containsKey(mapId)) {
|
|
entered.remove(mapId);
|
|
}
|
|
}
|
|
|
|
public void resetEnteredScript(String script) {
|
|
for (int mapId : entered.keySet()) {
|
|
if (entered.get(mapId).equals(script)) {
|
|
entered.remove(mapId);
|
|
}
|
|
}
|
|
}
|
|
|
|
public synchronized void saveCooldowns() {
|
|
List<PlayerCoolDownValueHolder> listcd = getAllCooldowns();
|
|
|
|
if (!listcd.isEmpty()) {
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
deleteWhereCharacterId(con, "DELETE FROM cooldowns WHERE charid = ?");
|
|
try (PreparedStatement ps = con.prepareStatement("INSERT INTO cooldowns (charid, SkillID, StartTime, length) VALUES (?, ?, ?, ?)")) {
|
|
ps.setInt(1, getId());
|
|
for (PlayerCoolDownValueHolder cooling : listcd) {
|
|
ps.setInt(2, cooling.skillId);
|
|
ps.setLong(3, cooling.startTime);
|
|
ps.setLong(4, cooling.length);
|
|
ps.addBatch();
|
|
}
|
|
ps.executeBatch();
|
|
}
|
|
|
|
con.close();
|
|
} catch (SQLException se) {
|
|
se.printStackTrace();
|
|
}
|
|
}
|
|
|
|
Map<MapleDisease, Pair<Long, MobSkill>> listds = getAllDiseases();
|
|
if (!listds.isEmpty()) {
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
deleteWhereCharacterId(con, "DELETE FROM playerdiseases WHERE charid = ?");
|
|
try (PreparedStatement ps = con.prepareStatement("INSERT INTO playerdiseases (charid, disease, mobskillid, mobskilllv, length) VALUES (?, ?, ?, ?, ?)")) {
|
|
ps.setInt(1, getId());
|
|
|
|
for (Entry<MapleDisease, Pair<Long, MobSkill>> e : listds.entrySet()) {
|
|
ps.setInt(2, e.getKey().ordinal());
|
|
|
|
MobSkill ms = e.getValue().getRight();
|
|
ps.setInt(3, ms.getSkillId());
|
|
ps.setInt(4, ms.getSkillLevel());
|
|
ps.setInt(5, e.getValue().getLeft().intValue());
|
|
ps.addBatch();
|
|
}
|
|
|
|
ps.executeBatch();
|
|
}
|
|
|
|
con.close();
|
|
} catch (SQLException se) {
|
|
se.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void saveGuildStatus() {
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET guildid = ?, guildrank = ?, allianceRank = ? WHERE id = ?")) {
|
|
ps.setInt(1, guildid);
|
|
ps.setInt(2, guildRank);
|
|
ps.setInt(3, allianceRank);
|
|
ps.setInt(4, id);
|
|
ps.executeUpdate();
|
|
}
|
|
|
|
con.close();
|
|
} catch (SQLException se) {
|
|
se.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void saveLocationOnWarp() { // suggestion to remember the map before warp command thanks to Lei
|
|
MaplePortal closest = map.findClosestPortal(getPosition());
|
|
int curMapid = getMapId();
|
|
|
|
for (int i = 0; i < savedLocations.length; i++) {
|
|
if (savedLocations[i] == null) {
|
|
savedLocations[i] = new SavedLocation(curMapid, closest != null ? closest.getId() : 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void saveLocation(String type) {
|
|
MaplePortal closest = map.findClosestPortal(getPosition());
|
|
savedLocations[SavedLocationType.fromString(type).ordinal()] = new SavedLocation(getMapId(), closest != null ? closest.getId() : 0);
|
|
}
|
|
|
|
public final boolean insertNewChar(CharacterFactoryRecipe recipe) {
|
|
str = recipe.getStr();
|
|
dex = recipe.getDex();
|
|
int_ = recipe.getInt();
|
|
luk = recipe.getLuk();
|
|
setMaxHp(recipe.getMaxHp());
|
|
setMaxMp(recipe.getMaxMp());
|
|
hp = maxhp;
|
|
mp = maxmp;
|
|
level = recipe.getLevel();
|
|
remainingAp = recipe.getRemainingAp();
|
|
remainingSp[GameConstants.getSkillBook(job.getId())] = recipe.getRemainingSp();
|
|
mapid = recipe.getMap();
|
|
meso.set(recipe.getMeso());
|
|
|
|
List<Pair<Skill, Integer>> startingSkills = recipe.getStartingSkillLevel();
|
|
for (Pair<Skill, Integer> skEntry : startingSkills) {
|
|
Skill skill = skEntry.getLeft();
|
|
this.changeSkillLevel(skill, skEntry.getRight().byteValue(), skill.getMaxLevel(), -1);
|
|
}
|
|
|
|
List<Pair<Item, MapleInventoryType>> itemsWithType = recipe.getStartingItems();
|
|
for (Pair<Item, MapleInventoryType> itEntry : itemsWithType) {
|
|
this.getInventory(itEntry.getRight()).addItem(itEntry.getLeft());
|
|
}
|
|
|
|
this.events.put("rescueGaga", new RescueGaga(0));
|
|
|
|
Connection con = null;
|
|
PreparedStatement ps = null;
|
|
|
|
try {
|
|
con = DatabaseConnection.getConnection();
|
|
|
|
con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
|
|
con.setAutoCommit(false);
|
|
ps = con.prepareStatement("INSERT INTO characters (str, dex, luk, `int`, gm, skincolor, gender, job, hair, face, map, meso, spawnpoint, accountid, name, world, hp, mp, maxhp, maxmp, level, ap, sp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
|
|
ps.setInt(1, str);
|
|
ps.setInt(2, dex);
|
|
ps.setInt(3, luk);
|
|
ps.setInt(4, int_);
|
|
ps.setInt(5, gmLevel);
|
|
ps.setInt(6, skinColor.getId());
|
|
ps.setInt(7, gender);
|
|
ps.setInt(8, getJob().getId());
|
|
ps.setInt(9, hair);
|
|
ps.setInt(10, face);
|
|
ps.setInt(11, mapid);
|
|
ps.setInt(12, Math.abs(meso.get()));
|
|
ps.setInt(13, 0);
|
|
ps.setInt(14, accountid);
|
|
ps.setString(15, name);
|
|
ps.setInt(16, world);
|
|
ps.setInt(17, hp);
|
|
ps.setInt(18, mp);
|
|
ps.setInt(19, maxhp);
|
|
ps.setInt(20, maxmp);
|
|
ps.setInt(21, level);
|
|
ps.setInt(22, remainingAp);
|
|
|
|
StringBuilder sps = new StringBuilder();
|
|
for (int i = 0; i < remainingSp.length; i++) {
|
|
sps.append(remainingSp[i]);
|
|
sps.append(",");
|
|
}
|
|
String sp = sps.toString();
|
|
ps.setString(23, sp.substring(0, sp.length() - 1));
|
|
|
|
int updateRows = ps.executeUpdate();
|
|
if (updateRows < 1) {
|
|
ps.close();
|
|
FilePrinter.printError(FilePrinter.INSERT_CHAR, "Error trying to insert " + name);
|
|
return false;
|
|
}
|
|
ResultSet rs = ps.getGeneratedKeys();
|
|
if (rs.next()) {
|
|
this.id = rs.getInt(1);
|
|
rs.close();
|
|
ps.close();
|
|
} else {
|
|
rs.close();
|
|
ps.close();
|
|
FilePrinter.printError(FilePrinter.INSERT_CHAR, "Inserting char failed " + name);
|
|
return false;
|
|
}
|
|
|
|
// Select a keybinding method
|
|
int[] selectedKey;
|
|
int[] selectedType;
|
|
int[] selectedAction;
|
|
|
|
if (ServerConstants.USE_CUSTOM_KEYSET) {
|
|
selectedKey = GameConstants.getCustomKey(true);
|
|
selectedType = GameConstants.getCustomType(true);
|
|
selectedAction = GameConstants.getCustomAction(true);
|
|
} else {
|
|
selectedKey = GameConstants.getCustomKey(false);
|
|
selectedType = GameConstants.getCustomType(false);
|
|
selectedAction = GameConstants.getCustomAction(false);
|
|
}
|
|
|
|
ps = con.prepareStatement("INSERT INTO keymap (characterid, `key`, `type`, `action`) VALUES (?, ?, ?, ?)");
|
|
ps.setInt(1, id);
|
|
for (int i = 0; i < selectedKey.length; i++) {
|
|
ps.setInt(2, selectedKey[i]);
|
|
ps.setInt(3, selectedType[i]);
|
|
ps.setInt(4, selectedAction[i]);
|
|
ps.execute();
|
|
}
|
|
ps.close();
|
|
|
|
itemsWithType = new ArrayList<>();
|
|
for (MapleInventory iv : inventory) {
|
|
for (Item item : iv.list()) {
|
|
itemsWithType.add(new Pair<>(item, iv.getType()));
|
|
}
|
|
}
|
|
|
|
ItemFactory.INVENTORY.saveItems(itemsWithType, id, con);
|
|
|
|
if (!skills.isEmpty()) {
|
|
ps = con.prepareStatement("INSERT INTO skills (characterid, skillid, skilllevel, masterlevel, expiration) VALUES (?, ?, ?, ?, ?)");
|
|
ps.setInt(1, id);
|
|
for (Entry<Skill, SkillEntry> skill : skills.entrySet()) {
|
|
ps.setInt(2, skill.getKey().getId());
|
|
ps.setInt(3, skill.getValue().skillevel);
|
|
ps.setInt(4, skill.getValue().masterlevel);
|
|
ps.setLong(5, skill.getValue().expiration);
|
|
ps.addBatch();
|
|
}
|
|
ps.executeBatch();
|
|
ps.close();
|
|
}
|
|
|
|
con.commit();
|
|
return true;
|
|
} catch (Throwable t) {
|
|
FilePrinter.printError(FilePrinter.INSERT_CHAR, t, "Error creating " + name + " Level: " + level + " Job: " + job.getId());
|
|
try {
|
|
con.rollback();
|
|
} catch (SQLException se) {
|
|
FilePrinter.printError(FilePrinter.INSERT_CHAR, se, "Error trying to rollback " + name);
|
|
}
|
|
return false;
|
|
} finally {
|
|
try {
|
|
if (ps != null && !ps.isClosed()) {
|
|
ps.close();
|
|
}
|
|
con.setAutoCommit(true);
|
|
con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
|
|
con.close();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void saveCharToDB() {
|
|
if (ServerConstants.USE_AUTOSAVE) {
|
|
Runnable r = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
saveCharToDB(true);
|
|
}
|
|
};
|
|
|
|
ThreadManager.getInstance().newTask(r); //spawns a new thread to deal with this
|
|
} else {
|
|
saveCharToDB(true);
|
|
}
|
|
}
|
|
|
|
//ItemFactory saveItems and monsterbook.saveCards are the most time consuming here.
|
|
public synchronized void saveCharToDB(boolean notAutosave) {
|
|
if (!loggedIn) {
|
|
return;
|
|
}
|
|
|
|
Calendar c = Calendar.getInstance();
|
|
|
|
if (notAutosave) {
|
|
FilePrinter.print(FilePrinter.SAVING_CHARACTER, "Attempting to save " + name + " at " + c.getTime().toString());
|
|
} else {
|
|
FilePrinter.print(FilePrinter.AUTOSAVING_CHARACTER, "Attempting to autosave " + name + " at " + c.getTime().toString());
|
|
}
|
|
|
|
Server.getInstance().updateCharacterEntry(this);
|
|
|
|
Connection con = null;
|
|
try {
|
|
con = DatabaseConnection.getConnection();
|
|
con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
|
|
con.setAutoCommit(false);
|
|
PreparedStatement ps;
|
|
ps = con.prepareStatement("UPDATE characters SET level = ?, fame = ?, str = ?, dex = ?, luk = ?, `int` = ?, exp = ?, gachaexp = ?, hp = ?, mp = ?, maxhp = ?, maxmp = ?, sp = ?, ap = ?, gm = ?, skincolor = ?, gender = ?, job = ?, hair = ?, face = ?, map = ?, meso = ?, hpMpUsed = ?, spawnpoint = ?, party = ?, buddyCapacity = ?, messengerid = ?, messengerposition = ?, mountlevel = ?, mountexp = ?, mounttiredness= ?, equipslots = ?, useslots = ?, setupslots = ?, etcslots = ?, monsterbookcover = ?, vanquisherStage = ?, dojoPoints = ?, lastDojoStage = ?, finishedDojoTutorial = ?, vanquisherKills = ?, matchcardwins = ?, matchcardlosses = ?, matchcardties = ?, omokwins = ?, omoklosses = ?, omokties = ?, dataString = ?, fquest = ?, jailexpire = ?, partnerId = ?, marriageItemId = ?, lastExpGainTime = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS);
|
|
if (gmLevel < 1 && level > 199) {
|
|
ps.setInt(1, isCygnus() ? 120 : 200);
|
|
} else {
|
|
ps.setInt(1, level);
|
|
}
|
|
ps.setInt(2, fame);
|
|
|
|
effLock.lock();
|
|
statWlock.lock();
|
|
try {
|
|
ps.setInt(3, str);
|
|
ps.setInt(4, dex);
|
|
ps.setInt(5, luk);
|
|
ps.setInt(6, int_);
|
|
ps.setInt(7, Math.abs(exp.get()));
|
|
ps.setInt(8, Math.abs(gachaexp.get()));
|
|
ps.setInt(9, hp);
|
|
ps.setInt(10, mp);
|
|
ps.setInt(11, maxhp);
|
|
ps.setInt(12, maxmp);
|
|
StringBuilder sps = new StringBuilder();
|
|
for (int i = 0; i < remainingSp.length; i++) {
|
|
sps.append(remainingSp[i]);
|
|
sps.append(",");
|
|
}
|
|
String sp = sps.toString();
|
|
ps.setString(13, sp.substring(0, sp.length() - 1));
|
|
ps.setInt(14, remainingAp);
|
|
} finally {
|
|
statWlock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
|
|
ps.setInt(15, gmLevel);
|
|
ps.setInt(16, skinColor.getId());
|
|
ps.setInt(17, gender);
|
|
ps.setInt(18, job.getId());
|
|
ps.setInt(19, hair);
|
|
ps.setInt(20, face);
|
|
if (map == null || (cashshop != null && cashshop.isOpened())) {
|
|
ps.setInt(21, mapid);
|
|
} else {
|
|
if (map.getForcedReturnId() != 999999999) {
|
|
ps.setInt(21, map.getForcedReturnId());
|
|
} else {
|
|
ps.setInt(21, getHp() < 1 ? map.getReturnMapId() : map.getId());
|
|
}
|
|
}
|
|
ps.setInt(22, meso.get());
|
|
ps.setInt(23, hpMpApUsed);
|
|
if (map == null || map.getId() == 610020000 || map.getId() == 610020001) { // reset to first spawnpoint on those maps
|
|
ps.setInt(24, 0);
|
|
} else {
|
|
MaplePortal closest = map.findClosestPlayerSpawnpoint(getPosition());
|
|
if (closest != null) {
|
|
ps.setInt(24, closest.getId());
|
|
} else {
|
|
ps.setInt(24, 0);
|
|
}
|
|
}
|
|
|
|
prtLock.lock();
|
|
try {
|
|
if (party != null) {
|
|
ps.setInt(25, party.getId());
|
|
} else {
|
|
ps.setInt(25, -1);
|
|
}
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
|
|
ps.setInt(26, buddylist.getCapacity());
|
|
if (messenger != null) {
|
|
ps.setInt(27, messenger.getId());
|
|
ps.setInt(28, messengerposition);
|
|
} else {
|
|
ps.setInt(27, 0);
|
|
ps.setInt(28, 4);
|
|
}
|
|
if (maplemount != null) {
|
|
ps.setInt(29, maplemount.getLevel());
|
|
ps.setInt(30, maplemount.getExp());
|
|
ps.setInt(31, maplemount.getTiredness());
|
|
} else {
|
|
ps.setInt(29, 1);
|
|
ps.setInt(30, 0);
|
|
ps.setInt(31, 0);
|
|
}
|
|
for (int i = 1; i < 5; i++) {
|
|
ps.setInt(i + 31, getSlots(i));
|
|
}
|
|
|
|
monsterbook.saveCards(getId());
|
|
|
|
ps.setInt(36, bookCover);
|
|
ps.setInt(37, vanquisherStage);
|
|
ps.setInt(38, dojoPoints);
|
|
ps.setInt(39, dojoStage);
|
|
ps.setInt(40, finishedDojoTutorial ? 1 : 0);
|
|
ps.setInt(41, vanquisherKills);
|
|
ps.setInt(42, matchcardwins);
|
|
ps.setInt(43, matchcardlosses);
|
|
ps.setInt(44, matchcardties);
|
|
ps.setInt(45, omokwins);
|
|
ps.setInt(46, omoklosses);
|
|
ps.setInt(47, omokties);
|
|
ps.setString(48, dataString);
|
|
ps.setInt(49, quest_fame);
|
|
ps.setLong(50, jailExpiration);
|
|
ps.setInt(51, partnerId);
|
|
ps.setInt(52, marriageItemid);
|
|
ps.setTimestamp(53, new Timestamp(lastExpGainTime));
|
|
ps.setInt(54, id);
|
|
|
|
int updateRows = ps.executeUpdate();
|
|
ps.close();
|
|
|
|
if (updateRows < 1) {
|
|
throw new RuntimeException("Character not in database (" + id + ")");
|
|
}
|
|
|
|
List<MaplePet> petList = new LinkedList<>();
|
|
petLock.lock();
|
|
try {
|
|
for (int i = 0; i < 3; i++) {
|
|
if (pets[i] != null) {
|
|
petList.add(pets[i]);
|
|
}
|
|
}
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
|
|
for (MaplePet pet : petList) {
|
|
pet.saveToDb();
|
|
}
|
|
|
|
for (Entry<Integer, Set<Integer>> es : getExcluded().entrySet()) { // this set is already protected
|
|
try (PreparedStatement ps2 = con.prepareStatement("DELETE FROM petignores WHERE petid=?")) {
|
|
ps2.setInt(1, es.getKey());
|
|
ps2.executeUpdate();
|
|
}
|
|
|
|
try (PreparedStatement ps2 = con.prepareStatement("INSERT INTO petignores (petid, itemid) VALUES (?, ?)")) {
|
|
ps2.setInt(1, es.getKey());
|
|
for (Integer x : es.getValue()) {
|
|
ps2.setInt(2, x);
|
|
ps2.addBatch();
|
|
}
|
|
ps2.executeBatch();
|
|
}
|
|
}
|
|
|
|
deleteWhereCharacterId(con, "DELETE FROM keymap WHERE characterid = ?");
|
|
ps = con.prepareStatement("INSERT INTO keymap (characterid, `key`, `type`, `action`) VALUES (?, ?, ?, ?)");
|
|
ps.setInt(1, id);
|
|
|
|
Set<Entry<Integer, MapleKeyBinding>> keybindingItems = Collections.unmodifiableSet(keymap.entrySet());
|
|
for (Entry<Integer, MapleKeyBinding> keybinding : keybindingItems) {
|
|
ps.setInt(2, keybinding.getKey());
|
|
ps.setInt(3, keybinding.getValue().getType());
|
|
ps.setInt(4, keybinding.getValue().getAction());
|
|
ps.addBatch();
|
|
}
|
|
ps.executeBatch();
|
|
ps.close();
|
|
|
|
deleteWhereCharacterId(con, "DELETE FROM skillmacros WHERE characterid = ?");
|
|
ps = con.prepareStatement("INSERT INTO skillmacros (characterid, skill1, skill2, skill3, name, shout, position) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
|
ps.setInt(1, getId());
|
|
for (int i = 0; i < 5; i++) {
|
|
SkillMacro macro = skillMacros[i];
|
|
if (macro != null) {
|
|
ps.setInt(2, macro.getSkill1());
|
|
ps.setInt(3, macro.getSkill2());
|
|
ps.setInt(4, macro.getSkill3());
|
|
ps.setString(5, macro.getName());
|
|
ps.setInt(6, macro.getShout());
|
|
ps.setInt(7, i);
|
|
ps.addBatch();
|
|
}
|
|
}
|
|
ps.executeBatch();
|
|
ps.close();
|
|
|
|
List<Pair<Item, MapleInventoryType>> itemsWithType = new ArrayList<>();
|
|
for (MapleInventory iv : inventory) {
|
|
for (Item item : iv.list()) {
|
|
itemsWithType.add(new Pair<>(item, iv.getType()));
|
|
}
|
|
}
|
|
|
|
ItemFactory.INVENTORY.saveItems(itemsWithType, id, con);
|
|
|
|
deleteWhereCharacterId(con, "DELETE FROM skills WHERE characterid = ?");
|
|
ps = con.prepareStatement("INSERT INTO skills (characterid, skillid, skilllevel, masterlevel, expiration) VALUES (?, ?, ?, ?, ?)");
|
|
ps.setInt(1, id);
|
|
for (Entry<Skill, SkillEntry> skill : skills.entrySet()) {
|
|
ps.setInt(2, skill.getKey().getId());
|
|
ps.setInt(3, skill.getValue().skillevel);
|
|
ps.setInt(4, skill.getValue().masterlevel);
|
|
ps.setLong(5, skill.getValue().expiration);
|
|
ps.addBatch();
|
|
}
|
|
ps.executeBatch();
|
|
ps.close();
|
|
|
|
deleteWhereCharacterId(con, "DELETE FROM savedlocations WHERE characterid = ?");
|
|
ps = con.prepareStatement("INSERT INTO savedlocations (characterid, `locationtype`, `map`, `portal`) VALUES (?, ?, ?, ?)");
|
|
ps.setInt(1, id);
|
|
for (SavedLocationType savedLocationType : SavedLocationType.values()) {
|
|
if (savedLocations[savedLocationType.ordinal()] != null) {
|
|
ps.setString(2, savedLocationType.name());
|
|
ps.setInt(3, savedLocations[savedLocationType.ordinal()].getMapId());
|
|
ps.setInt(4, savedLocations[savedLocationType.ordinal()].getPortal());
|
|
ps.addBatch();
|
|
}
|
|
}
|
|
ps.executeBatch();
|
|
ps.close();
|
|
|
|
deleteWhereCharacterId(con, "DELETE FROM trocklocations WHERE characterid = ?");
|
|
ps = con.prepareStatement("INSERT INTO trocklocations(characterid, mapid, vip) VALUES (?, ?, 0)");
|
|
for (int i = 0; i < getTrockSize(); i++) {
|
|
if (trockmaps.get(i) != 999999999) {
|
|
ps.setInt(1, getId());
|
|
ps.setInt(2, trockmaps.get(i));
|
|
ps.addBatch();
|
|
}
|
|
}
|
|
ps.executeBatch();
|
|
ps.close();
|
|
|
|
ps = con.prepareStatement("INSERT INTO trocklocations(characterid, mapid, vip) VALUES (?, ?, 1)");
|
|
for (int i = 0; i < getVipTrockSize(); i++) {
|
|
if (viptrockmaps.get(i) != 999999999) {
|
|
ps.setInt(1, getId());
|
|
ps.setInt(2, viptrockmaps.get(i));
|
|
ps.addBatch();
|
|
}
|
|
}
|
|
ps.executeBatch();
|
|
ps.close();
|
|
|
|
deleteWhereCharacterId(con, "DELETE FROM buddies WHERE characterid = ? AND pending = 0");
|
|
ps = con.prepareStatement("INSERT INTO buddies (characterid, `buddyid`, `pending`, `group`) VALUES (?, ?, 0, ?)");
|
|
ps.setInt(1, id);
|
|
for (BuddylistEntry entry : buddylist.getBuddies()) {
|
|
if (entry.isVisible()) {
|
|
ps.setInt(2, entry.getCharacterId());
|
|
ps.setString(3, entry.getGroup());
|
|
ps.addBatch();
|
|
}
|
|
}
|
|
ps.executeBatch();
|
|
ps.close();
|
|
|
|
deleteWhereCharacterId(con, "DELETE FROM area_info WHERE charid = ?");
|
|
ps = con.prepareStatement("INSERT INTO area_info (id, charid, area, info) VALUES (DEFAULT, ?, ?, ?)");
|
|
ps.setInt(1, id);
|
|
for (Entry<Short, String> area : area_info.entrySet()) {
|
|
ps.setInt(2, area.getKey());
|
|
ps.setString(3, area.getValue());
|
|
ps.addBatch();
|
|
}
|
|
ps.executeBatch();
|
|
ps.close();
|
|
|
|
deleteWhereCharacterId(con, "DELETE FROM eventstats WHERE characterid = ?");
|
|
ps = con.prepareStatement("INSERT INTO eventstats (characterid, name, info) VALUES (?, ?, ?)");
|
|
ps.setInt(1, id);
|
|
|
|
for (Map.Entry<String, MapleEvents> entry : events.entrySet()) {
|
|
ps.setString(2, entry.getKey());
|
|
ps.setInt(3, entry.getValue().getInfo());
|
|
ps.addBatch();
|
|
}
|
|
|
|
ps.executeBatch();
|
|
ps.close();
|
|
|
|
deleteQuestProgressWhereCharacterId(con, id);
|
|
|
|
ps = con.prepareStatement("INSERT INTO queststatus (`queststatusid`, `characterid`, `quest`, `status`, `time`, `expires`, `forfeited`) VALUES (DEFAULT, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
|
|
PreparedStatement psf;
|
|
try (PreparedStatement pse = con.prepareStatement("INSERT INTO questprogress VALUES (DEFAULT, ?, ?, ?, ?)")) {
|
|
psf = con.prepareStatement("INSERT INTO medalmaps VALUES (DEFAULT, ?, ?, ?)");
|
|
ps.setInt(1, id);
|
|
|
|
synchronized (quests) {
|
|
for (MapleQuestStatus q : quests.values()) {
|
|
ps.setInt(2, q.getQuest().getId());
|
|
ps.setInt(3, q.getStatus().getId());
|
|
ps.setInt(4, (int) (q.getCompletionTime() / 1000));
|
|
ps.setLong(5, q.getExpirationTime());
|
|
ps.setInt(6, q.getForfeited());
|
|
ps.executeUpdate();
|
|
try (ResultSet rs = ps.getGeneratedKeys()) {
|
|
rs.next();
|
|
for (int mob : q.getProgress().keySet()) {
|
|
pse.setInt(1, id);
|
|
pse.setInt(2, rs.getInt(1));
|
|
pse.setInt(3, mob);
|
|
pse.setString(4, q.getProgress(mob));
|
|
pse.addBatch();
|
|
}
|
|
for (int i = 0; i < q.getMedalMaps().size(); i++) {
|
|
psf.setInt(1, id);
|
|
psf.setInt(2, rs.getInt(1));
|
|
psf.setInt(3, q.getMedalMaps().get(i));
|
|
psf.addBatch();
|
|
}
|
|
pse.executeBatch();
|
|
psf.executeBatch();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
psf.close();
|
|
ps.close();
|
|
|
|
con.commit();
|
|
con.setAutoCommit(true);
|
|
|
|
if (cashshop != null) {
|
|
cashshop.save(con);
|
|
}
|
|
|
|
if (storage != null && usedStorage) {
|
|
storage.saveToDB(con);
|
|
usedStorage = false;
|
|
}
|
|
|
|
} catch (SQLException | RuntimeException t) {
|
|
FilePrinter.printError(FilePrinter.SAVE_CHAR, t, "Error saving " + name + " Level: " + level + " Job: " + job.getId());
|
|
try {
|
|
con.rollback();
|
|
} catch (SQLException se) {
|
|
FilePrinter.printError(FilePrinter.SAVE_CHAR, se, "Error trying to rollback " + name);
|
|
}
|
|
} catch (Exception e) {
|
|
FilePrinter.printError(FilePrinter.SAVE_CHAR, e, "Error saving " + name + " Level: " + level + " Job: " + job.getId());
|
|
} finally {
|
|
try {
|
|
con.setAutoCommit(true);
|
|
con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
|
|
con.close();
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void sendPolice(int greason, String reason, int duration) {
|
|
announce(MaplePacketCreator.sendPolice(String.format("You have been blocked by the#b %s Police for %s.#k", "HeavenMS", reason)));
|
|
this.isbanned = true;
|
|
TimerManager.getInstance().schedule(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
client.disconnect(false, false);
|
|
}
|
|
}, duration);
|
|
}
|
|
|
|
public void sendPolice(String text) {
|
|
String message = getName() + " received this - " + text;
|
|
if (Server.getInstance().isGmOnline(this.getWorld())) { //Alert and log if a GM is online
|
|
Server.getInstance().broadcastGMMessage(this.getWorld(), MaplePacketCreator.sendYellowTip(message));
|
|
FilePrinter.print(FilePrinter.AUTOBAN_WARNING, message);
|
|
} else { //Auto DC and log if no GM is online
|
|
client.disconnect(false, false);
|
|
FilePrinter.print(FilePrinter.AUTOBAN_DC, message);
|
|
}
|
|
//Server.getInstance().broadcastGMMessage(0, MaplePacketCreator.serverNotice(1, getName() + " received this - " + text));
|
|
//announce(MaplePacketCreator.sendPolice(text));
|
|
//this.isbanned = true;
|
|
//TimerManager.getInstance().schedule(new Runnable() {
|
|
// @Override
|
|
// public void run() {
|
|
// client.disconnect(false, false);
|
|
// }
|
|
//}, 6000);
|
|
}
|
|
|
|
public void sendKeymap() {
|
|
client.announce(MaplePacketCreator.getKeymap(keymap));
|
|
}
|
|
|
|
public void sendMacros() {
|
|
// Always send the macro packet to fix a client side bug when switching characters.
|
|
client.announce(MaplePacketCreator.getMacros(skillMacros));
|
|
}
|
|
|
|
public void sendNote(String to, String msg, byte fame) throws SQLException {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try (PreparedStatement ps = con.prepareStatement("INSERT INTO notes (`to`, `from`, `message`, `timestamp`, `fame`) VALUES (?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS)) {
|
|
ps.setString(1, to);
|
|
ps.setString(2, this.getName());
|
|
ps.setString(3, msg);
|
|
ps.setLong(4, Server.getInstance().getCurrentTime());
|
|
ps.setByte(5, fame);
|
|
ps.executeUpdate();
|
|
} finally {
|
|
con.close();
|
|
}
|
|
}
|
|
|
|
public static void setAriantRoomLeader(int room, String charname) {
|
|
ariantroomleader[room] = charname;
|
|
}
|
|
|
|
public static void setAriantSlotRoom(int room, int slot) {
|
|
ariantroomslot[room] = slot;
|
|
}
|
|
|
|
public void setBattleshipHp(int battleshipHp) {
|
|
this.battleshipHp = battleshipHp;
|
|
}
|
|
|
|
public void setBuddyCapacity(int capacity) {
|
|
buddylist.setCapacity(capacity);
|
|
client.announce(MaplePacketCreator.updateBuddyCapacity(capacity));
|
|
}
|
|
|
|
public void setBuffedValue(MapleBuffStat effect, int value) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
MapleBuffStatValueHolder mbsvh = effects.get(effect);
|
|
if (mbsvh == null) {
|
|
return;
|
|
}
|
|
mbsvh.value = value;
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void setChalkboard(String text) {
|
|
this.chalktext = text;
|
|
}
|
|
|
|
public void setDojoEnergy(int x) {
|
|
this.dojoEnergy = Math.min(x, 10000);
|
|
}
|
|
|
|
public void setDojoPoints(int x) {
|
|
this.dojoPoints = x;
|
|
}
|
|
|
|
public void setDojoStage(int x) {
|
|
this.dojoStage = x;
|
|
}
|
|
|
|
public void setEnergyBar(int set) {
|
|
energybar = set;
|
|
}
|
|
|
|
public void setEventInstance(EventInstanceManager eventInstance) {
|
|
evtLock.lock();
|
|
try {
|
|
this.eventInstance = eventInstance;
|
|
} finally {
|
|
evtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void setExp(int amount) {
|
|
this.exp.set(amount);
|
|
}
|
|
|
|
public void setGachaExp(int amount) {
|
|
this.gachaexp.set(amount);
|
|
}
|
|
|
|
public void setFace(int face) {
|
|
this.face = face;
|
|
}
|
|
|
|
public void setFame(int fame) {
|
|
this.fame = fame;
|
|
}
|
|
|
|
public void setFamilyId(int familyId) {
|
|
this.familyId = familyId;
|
|
}
|
|
|
|
public void setFinishedDojoTutorial() {
|
|
this.finishedDojoTutorial = true;
|
|
}
|
|
|
|
public void setGender(int gender) {
|
|
this.gender = gender;
|
|
}
|
|
|
|
public void setGM(int level) {
|
|
this.gmLevel = level;
|
|
}
|
|
|
|
public void setGuildId(int _id) {
|
|
guildid = _id;
|
|
}
|
|
|
|
public void setGuildRank(int _rank) {
|
|
guildRank = _rank;
|
|
}
|
|
|
|
public void setAllianceRank(int _rank) {
|
|
allianceRank = _rank;
|
|
}
|
|
|
|
public void setHair(int hair) {
|
|
this.hair = hair;
|
|
}
|
|
|
|
public void setHasMerchant(boolean set) {
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
|
|
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET HasMerchant = ? WHERE id = ?")) {
|
|
ps.setInt(1, set ? 1 : 0);
|
|
ps.setInt(2, id);
|
|
ps.executeUpdate();
|
|
}
|
|
|
|
con.close();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
hasMerchant = set;
|
|
}
|
|
|
|
public void addMerchantMesos(int add) {
|
|
int newAmount;
|
|
|
|
try {
|
|
newAmount = (int) Math.min((long) merchantmeso + add, Integer.MAX_VALUE);
|
|
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET MerchantMesos = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
|
|
ps.setInt(1, newAmount);
|
|
ps.setInt(2, id);
|
|
ps.executeUpdate();
|
|
}
|
|
|
|
con.close();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return;
|
|
}
|
|
merchantmeso = newAmount;
|
|
}
|
|
|
|
public void setMerchantMeso(int set) {
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET MerchantMesos = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
|
|
ps.setInt(1, set);
|
|
ps.setInt(2, id);
|
|
ps.executeUpdate();
|
|
}
|
|
con.close();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return;
|
|
}
|
|
merchantmeso = set;
|
|
}
|
|
|
|
public synchronized void withdrawMerchantMesos() {
|
|
int merchantMeso = this.getMerchantMeso();
|
|
if (merchantMeso > 0) {
|
|
int possible = Integer.MAX_VALUE - merchantMeso;
|
|
|
|
if (possible > 0) {
|
|
if (possible < merchantMeso) {
|
|
this.gainMeso(possible, false);
|
|
this.setMerchantMeso(merchantMeso - possible);
|
|
} else {
|
|
this.gainMeso(merchantMeso, false);
|
|
this.setMerchantMeso(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setHiredMerchant(MapleHiredMerchant merchant) {
|
|
this.hiredMerchant = merchant;
|
|
}
|
|
|
|
private void hpChangeAction(int oldHp) {
|
|
boolean playerDied = false;
|
|
if (hp <= 0) {
|
|
if (oldHp > hp) {
|
|
if (!isBuybackInvincible()) {
|
|
playerDied = true;
|
|
} else {
|
|
hp = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
updatePartyMemberHP();
|
|
|
|
if (playerDied) {
|
|
playerDead();
|
|
} else {
|
|
checkBerserk(isHidden());
|
|
}
|
|
}
|
|
|
|
private Pair<MapleStat, Integer> calcHpRatioUpdate(int newHp, int oldHp) {
|
|
int delta = newHp - oldHp;
|
|
this.hp = calcHpRatioUpdate(hp, oldHp, delta);
|
|
|
|
hpChangeAction(Short.MIN_VALUE);
|
|
return new Pair<>(MapleStat.HP, hp);
|
|
}
|
|
|
|
private Pair<MapleStat, Integer> calcMpRatioUpdate(int newMp, int oldMp) {
|
|
int delta = newMp - oldMp;
|
|
this.mp = calcMpRatioUpdate(mp, oldMp, delta);
|
|
return new Pair<>(MapleStat.MP, mp);
|
|
}
|
|
|
|
private static int calcTransientRatio(float transientpoint) {
|
|
int ret = (int) transientpoint;
|
|
return !(ret <= 0 && transientpoint > 0.0f) ? ret : 1;
|
|
}
|
|
|
|
private Pair<MapleStat, Integer> calcHpRatioTransient() {
|
|
this.hp = calcTransientRatio(transienthp * localmaxhp);
|
|
|
|
hpChangeAction(Short.MIN_VALUE);
|
|
return new Pair<>(MapleStat.HP, hp);
|
|
}
|
|
|
|
private Pair<MapleStat, Integer> calcMpRatioTransient() {
|
|
this.mp = calcTransientRatio(transientmp * localmaxmp);
|
|
return new Pair<>(MapleStat.MP, mp);
|
|
}
|
|
|
|
private int calcHpRatioUpdate(int curpoint, int maxpoint, int diffpoint) {
|
|
int curMax = maxpoint;
|
|
int nextMax = Math.min(30000, maxpoint + diffpoint);
|
|
|
|
float temp = curpoint * nextMax;
|
|
int ret = (int) Math.ceil(temp / curMax);
|
|
|
|
transienthp = (maxpoint > nextMax) ? ((float) curpoint) / maxpoint : ((float) ret) / nextMax;
|
|
return ret;
|
|
}
|
|
|
|
private int calcMpRatioUpdate(int curpoint, int maxpoint, int diffpoint) {
|
|
int curMax = maxpoint;
|
|
int nextMax = Math.min(30000, maxpoint + diffpoint);
|
|
|
|
float temp = curpoint * nextMax;
|
|
int ret = (int) Math.ceil(temp / curMax);
|
|
|
|
transientmp = (maxpoint > nextMax) ? ((float) curpoint) / maxpoint : ((float) ret) / nextMax;
|
|
return ret;
|
|
}
|
|
|
|
public boolean applyHpMpChange(int hpCon, int hpchange, int mpchange) {
|
|
boolean zombify = hasDisease(MapleDisease.ZOMBIFY);
|
|
|
|
effLock.lock();
|
|
statWlock.lock();
|
|
try {
|
|
int nextHp = hp + hpchange, nextMp = mp + mpchange;
|
|
boolean cannotApplyHp = hpchange != 0 && nextHp <= 0 && (!zombify || hpCon > 0);
|
|
boolean cannotApplyMp = mpchange != 0 && nextMp < 0;
|
|
|
|
if (cannotApplyHp || cannotApplyMp) {
|
|
if (!isGM()) {
|
|
return false;
|
|
}
|
|
|
|
if (cannotApplyHp) {
|
|
nextHp = 1;
|
|
}
|
|
}
|
|
|
|
updateHpMp(nextHp, nextMp);
|
|
return true;
|
|
} finally {
|
|
statWlock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void setInventory(MapleInventoryType type, MapleInventory inv) {
|
|
inventory[type.ordinal()] = inv;
|
|
}
|
|
|
|
public void setItemEffect(int itemEffect) {
|
|
this.itemEffect = itemEffect;
|
|
}
|
|
|
|
public void setJob(MapleJob job) {
|
|
this.job = job;
|
|
}
|
|
|
|
public void setLastHealed(long time) {
|
|
this.lastHealed = time;
|
|
}
|
|
|
|
public void setLastUsedCashItem(long time) {
|
|
this.lastUsedCashItem = time;
|
|
}
|
|
|
|
public void setLevel(int level) {
|
|
this.level = level;
|
|
}
|
|
|
|
public void setMap(int PmapId) {
|
|
this.mapid = PmapId;
|
|
}
|
|
|
|
public void setMap(MapleMap newmap) {
|
|
this.map = newmap;
|
|
}
|
|
|
|
public void setMessenger(MapleMessenger messenger) {
|
|
this.messenger = messenger;
|
|
}
|
|
|
|
public void setMessengerPosition(int position) {
|
|
this.messengerposition = position;
|
|
}
|
|
|
|
public void setMiniGame(MapleMiniGame miniGame) {
|
|
this.miniGame = miniGame;
|
|
}
|
|
|
|
public void setMiniGamePoints(MapleCharacter visitor, int winnerslot, boolean omok) {
|
|
if (omok) {
|
|
if (winnerslot == 1) {
|
|
this.omokwins++;
|
|
visitor.omoklosses++;
|
|
} else if (winnerslot == 2) {
|
|
visitor.omokwins++;
|
|
this.omoklosses++;
|
|
} else {
|
|
this.omokties++;
|
|
visitor.omokties++;
|
|
}
|
|
} else {
|
|
if (winnerslot == 1) {
|
|
this.matchcardwins++;
|
|
visitor.matchcardlosses++;
|
|
} else if (winnerslot == 2) {
|
|
visitor.matchcardwins++;
|
|
this.matchcardlosses++;
|
|
} else {
|
|
this.matchcardties++;
|
|
visitor.matchcardties++;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setMonsterBookCover(int bookCover) {
|
|
this.bookCover = bookCover;
|
|
}
|
|
|
|
public void setName(String name) {
|
|
this.name = name;
|
|
}
|
|
|
|
public void changeName(String name) {
|
|
this.name = name;
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
PreparedStatement ps = con.prepareStatement("UPDATE `characters` SET `name` = ? WHERE `id` = ?");
|
|
ps.setString(1, name);
|
|
ps.setInt(2, id);
|
|
ps.executeUpdate();
|
|
ps.close();
|
|
con.close();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public int getDoorSlot() {
|
|
if (doorSlot != -1) {
|
|
return doorSlot;
|
|
}
|
|
return fetchDoorSlot();
|
|
}
|
|
|
|
public int fetchDoorSlot() {
|
|
prtLock.lock();
|
|
try {
|
|
doorSlot = (party == null) ? 0 : party.getPartyDoor(this.getId());
|
|
return doorSlot;
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void setParty(MapleParty p) {
|
|
prtLock.lock();
|
|
try {
|
|
if (p == null) {
|
|
this.mpc = null;
|
|
doorSlot = -1;
|
|
|
|
party = null;
|
|
} else {
|
|
party = p;
|
|
}
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void setPlayerShop(MaplePlayerShop playerShop) {
|
|
this.playerShop = playerShop;
|
|
}
|
|
|
|
public void setSearch(String find) {
|
|
search = find;
|
|
}
|
|
|
|
public void setSkinColor(MapleSkinColor skinColor) {
|
|
this.skinColor = skinColor;
|
|
}
|
|
|
|
public byte getSlots(int type) {
|
|
return type == MapleInventoryType.CASH.getType() ? 96 : inventory[type].getSlotLimit();
|
|
}
|
|
|
|
public boolean gainSlots(int type, int slots) {
|
|
return gainSlots(type, slots, true);
|
|
}
|
|
|
|
public boolean gainSlots(int type, int slots, boolean update) {
|
|
slots += inventory[type].getSlotLimit();
|
|
if (slots <= 96) {
|
|
inventory[type].setSlotLimit(slots);
|
|
|
|
this.saveCharToDB();
|
|
if (update) {
|
|
client.announce(MaplePacketCreator.updateInventorySlotLimit(type, slots));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public int sellAllItemsFromName(byte invTypeId, String name) {
|
|
//player decides from which inventory items should be sold.
|
|
MapleInventoryType type = MapleInventoryType.getByType(invTypeId);
|
|
|
|
MapleInventory inv = getInventory(type);
|
|
inv.lockInventory();
|
|
try {
|
|
Item it = inv.findByName(name);
|
|
if (it == null) {
|
|
return (-1);
|
|
}
|
|
|
|
return (sellAllItemsFromPosition(ii, type, it.getPosition()));
|
|
} finally {
|
|
inv.unlockInventory();
|
|
}
|
|
}
|
|
|
|
public int sellAllItemsFromPosition(MapleItemInformationProvider ii, MapleInventoryType type, short pos) {
|
|
int mesoGain = 0;
|
|
|
|
MapleInventory inv = getInventory(type);
|
|
inv.lockInventory();
|
|
try {
|
|
for (short i = pos; i <= inv.getSlotLimit(); i++) {
|
|
if (inv.getItem(i) == null) {
|
|
continue;
|
|
}
|
|
mesoGain += standaloneSell(getClient(), ii, type, i, inv.getItem(i).getQuantity());
|
|
}
|
|
} finally {
|
|
inv.unlockInventory();
|
|
}
|
|
|
|
return (mesoGain);
|
|
}
|
|
|
|
private int standaloneSell(MapleClient c, MapleItemInformationProvider ii, MapleInventoryType type, short slot, short quantity) {
|
|
if (quantity == 0xFFFF || quantity == 0) {
|
|
quantity = 1;
|
|
}
|
|
|
|
MapleInventory inv = getInventory(type);
|
|
inv.lockInventory();
|
|
try {
|
|
Item item = inv.getItem((short) slot);
|
|
if (item == null) { //Basic check
|
|
return (0);
|
|
}
|
|
|
|
int itemid = item.getItemId();
|
|
if (ItemConstants.isRechargeable(itemid)) {
|
|
quantity = item.getQuantity();
|
|
} else if (ItemConstants.isWeddingToken(itemid) || ItemConstants.isWeddingRing(itemid)) {
|
|
return (0);
|
|
}
|
|
|
|
if (quantity < 0) {
|
|
return (0);
|
|
}
|
|
short iQuant = item.getQuantity();
|
|
if (iQuant == 0xFFFF) {
|
|
iQuant = 1;
|
|
}
|
|
|
|
if (quantity <= iQuant && iQuant > 0) {
|
|
MapleInventoryManipulator.removeFromSlot(c, type, (byte) slot, quantity, false);
|
|
int recvMesos = ii.getPrice(itemid, quantity);
|
|
if (recvMesos > 0) {
|
|
gainMeso(recvMesos, false);
|
|
return (recvMesos);
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
} finally {
|
|
inv.unlockInventory();
|
|
}
|
|
}
|
|
|
|
private static boolean hasMergeFlag(Item item) {
|
|
return (item.getFlag() & ItemConstants.MERGE_UNTRADEABLE) == ItemConstants.MERGE_UNTRADEABLE;
|
|
}
|
|
|
|
private static void setMergeFlag(Item item) {
|
|
int flag = item.getFlag();
|
|
flag |= ItemConstants.MERGE_UNTRADEABLE;
|
|
flag |= ItemConstants.UNTRADEABLE;
|
|
item.setFlag((byte) flag);
|
|
}
|
|
|
|
private List<Equip> getUpgradeableEquipped() {
|
|
List<Equip> list = new LinkedList<>();
|
|
|
|
for (Item item : getInventory(MapleInventoryType.EQUIPPED)) {
|
|
if (ii.isUpgradeable(item.getItemId())) {
|
|
list.add((Equip) item);
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
private static List<Equip> getEquipsWithStat(List<Pair<Equip, Map<StatUpgrade, Short>>> equipped, StatUpgrade stat) {
|
|
List<Equip> equippedWithStat = new LinkedList<>();
|
|
|
|
for (Pair<Equip, Map<StatUpgrade, Short>> eq : equipped) {
|
|
if (eq.getRight().containsKey(stat)) {
|
|
equippedWithStat.add(eq.getLeft());
|
|
}
|
|
}
|
|
|
|
return equippedWithStat;
|
|
}
|
|
|
|
public boolean mergeAllItemsFromName(String name) {
|
|
MapleInventoryType type = MapleInventoryType.EQUIP;
|
|
|
|
MapleInventory inv = getInventory(type);
|
|
inv.lockInventory();
|
|
try {
|
|
Item it = inv.findByName(name);
|
|
if (it == null) {
|
|
return false;
|
|
}
|
|
|
|
Map<StatUpgrade, Float> statups = new LinkedHashMap<>();
|
|
mergeAllItemsFromPosition(statups, it.getPosition());
|
|
|
|
List<Pair<Equip, Map<StatUpgrade, Short>>> upgradeableEquipped = new LinkedList<>();
|
|
Map<Equip, List<Pair<StatUpgrade, Integer>>> equipUpgrades = new LinkedHashMap<>();
|
|
for (Equip eq : getUpgradeableEquipped()) {
|
|
upgradeableEquipped.add(new Pair<>(eq, eq.getStats()));
|
|
equipUpgrades.put(eq, new LinkedList<Pair<StatUpgrade, Integer>>());
|
|
}
|
|
|
|
/*
|
|
for (Entry<StatUpgrade, Float> es : statups.entrySet()) {
|
|
System.out.println(es);
|
|
}
|
|
*/
|
|
for (Entry<StatUpgrade, Float> e : statups.entrySet()) {
|
|
Double ev = Math.sqrt(e.getValue());
|
|
|
|
Set<Equip> extraEquipped = new LinkedHashSet<>(equipUpgrades.keySet());
|
|
List<Equip> statEquipped = getEquipsWithStat(upgradeableEquipped, e.getKey());
|
|
float extraRate = (float) (0.2 * Math.random());
|
|
|
|
if (!statEquipped.isEmpty()) {
|
|
float statRate = 1.0f - extraRate;
|
|
|
|
int statup = (int) Math.ceil((ev * statRate) / statEquipped.size());
|
|
for (Equip statEq : statEquipped) {
|
|
equipUpgrades.get(statEq).add(new Pair<>(e.getKey(), statup));
|
|
extraEquipped.remove(statEq);
|
|
}
|
|
}
|
|
|
|
if (!extraEquipped.isEmpty()) {
|
|
int statup = (int) Math.round((ev * extraRate) / extraEquipped.size());
|
|
if (statup > 0) {
|
|
for (Equip extraEq : extraEquipped) {
|
|
equipUpgrades.get(extraEq).add(new Pair<>(e.getKey(), statup));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
dropMessage(6, "EQUIPMENT MERGE operation results:");
|
|
for (Entry<Equip, List<Pair<StatUpgrade, Integer>>> eqpUpg : equipUpgrades.entrySet()) {
|
|
List<Pair<StatUpgrade, Integer>> eqpStatups = eqpUpg.getValue();
|
|
if (!eqpStatups.isEmpty()) {
|
|
Equip eqp = eqpUpg.getKey();
|
|
setMergeFlag(eqp);
|
|
|
|
String showStr = " '" + MapleItemInformationProvider.getInstance().getName(eqp.getItemId()) + "': ";
|
|
String upgdStr = eqp.gainStats(eqpStatups).getLeft();
|
|
|
|
this.forceUpdateItem(eqp);
|
|
|
|
showStr += upgdStr;
|
|
dropMessage(6, showStr);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
} finally {
|
|
inv.unlockInventory();
|
|
}
|
|
}
|
|
|
|
public void mergeAllItemsFromPosition(Map<StatUpgrade, Float> statups, short pos) {
|
|
MapleInventory inv = getInventory(MapleInventoryType.EQUIP);
|
|
inv.lockInventory();
|
|
try {
|
|
for (short i = pos; i <= inv.getSlotLimit(); i++) {
|
|
standaloneMerge(statups, getClient(), MapleInventoryType.EQUIP, i, inv.getItem(i));
|
|
}
|
|
} finally {
|
|
inv.unlockInventory();
|
|
}
|
|
}
|
|
|
|
private void standaloneMerge(Map<StatUpgrade, Float> statups, MapleClient c, MapleInventoryType type, short slot, Item item) {
|
|
short quantity;
|
|
if (item == null || (quantity = item.getQuantity()) < 1 || ii.isCash(item.getItemId()) || !ii.isUpgradeable(item.getItemId()) || hasMergeFlag(item)) {
|
|
return;
|
|
}
|
|
|
|
Equip e = (Equip) item;
|
|
for (Entry<StatUpgrade, Short> s : e.getStats().entrySet()) {
|
|
Float newVal = statups.get(s.getKey());
|
|
|
|
float incVal = s.getValue().floatValue();
|
|
switch (s.getKey()) {
|
|
case incPAD:
|
|
case incMAD:
|
|
case incPDD:
|
|
case incMDD:
|
|
incVal = (float) Math.log(incVal);
|
|
break;
|
|
}
|
|
|
|
if (newVal != null) {
|
|
newVal += incVal;
|
|
} else {
|
|
newVal = incVal;
|
|
}
|
|
|
|
statups.put(s.getKey(), newVal);
|
|
}
|
|
|
|
MapleInventoryManipulator.removeFromSlot(c, type, (byte) slot, quantity, false);
|
|
}
|
|
|
|
public void setShop(MapleShop shop) {
|
|
this.shop = shop;
|
|
}
|
|
|
|
public void setSlot(int slotid) {
|
|
slots = slotid;
|
|
}
|
|
|
|
public void setTrade(MapleTrade trade) {
|
|
this.trade = trade;
|
|
}
|
|
|
|
public void setVanquisherKills(int x) {
|
|
this.vanquisherKills = x;
|
|
}
|
|
|
|
public void setVanquisherStage(int x) {
|
|
this.vanquisherStage = x;
|
|
}
|
|
|
|
public void setWorld(int world) {
|
|
this.world = world;
|
|
}
|
|
|
|
public void shiftPetsRight() {
|
|
petLock.lock();
|
|
try {
|
|
if (pets[2] == null) {
|
|
pets[2] = pets[1];
|
|
pets[1] = pets[0];
|
|
pets[0] = null;
|
|
}
|
|
} finally {
|
|
petLock.unlock();
|
|
}
|
|
}
|
|
|
|
private long getDojoTimeLeft() {
|
|
return client.getChannelServer().getDojoFinishTime(map.getId()) - Server.getInstance().getCurrentTime();
|
|
}
|
|
|
|
public void showDojoClock() {
|
|
if (map.isDojoFightMap()) {
|
|
client.announce(MaplePacketCreator.getClock((int) (getDojoTimeLeft() / 1000)));
|
|
}
|
|
}
|
|
|
|
public void timeoutFromDojo() {
|
|
if (map.isDojoMap()) {
|
|
client.getPlayer().changeMap(client.getChannelServer().getMapFactory().getMap(925020002));
|
|
}
|
|
}
|
|
|
|
public void showUnderleveledInfo(MapleMonster mob) {
|
|
chrLock.lock();
|
|
try {
|
|
long curTime = Server.getInstance().getCurrentTime();
|
|
if (nextUnderlevelTime < curTime) {
|
|
nextUnderlevelTime = curTime + (60 * 1000); // show underlevel info again after 1 minute
|
|
|
|
showHint("You have gained #rno experience#k from defeating #e#b" + mob.getName() + "#k#n (lv. #b" + mob.getLevel() + "#k)! Take note you must have around the same level as the mob to start earning EXP from it.");
|
|
}
|
|
} finally {
|
|
chrLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void showHint(String msg) {
|
|
showHint(msg, 500);
|
|
}
|
|
|
|
public void showHint(String msg, int length) {
|
|
client.announceHint(msg, length);
|
|
}
|
|
|
|
public void showNote() {
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM notes WHERE `to` = ? AND `deleted` = 0", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) {
|
|
ps.setString(1, this.getName());
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
rs.last();
|
|
int count = rs.getRow();
|
|
rs.first();
|
|
client.announce(MaplePacketCreator.showNotes(rs, count));
|
|
}
|
|
}
|
|
|
|
con.close();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void silentGiveBuffs(List<Pair<Long, PlayerBuffValueHolder>> buffs) {
|
|
for (Pair<Long, PlayerBuffValueHolder> mbsv : buffs) {
|
|
PlayerBuffValueHolder mbsvh = mbsv.getRight();
|
|
mbsvh.effect.silentApplyBuff(this, mbsv.getLeft());
|
|
}
|
|
}
|
|
|
|
public void silentPartyUpdate() {
|
|
silentPartyUpdateInternal(getParty());
|
|
}
|
|
|
|
private void silentPartyUpdateInternal(MapleParty chrParty) {
|
|
if (chrParty != null) {
|
|
getWorldServer().updateParty(chrParty.getId(), PartyOperation.SILENT_UPDATE, getMPC());
|
|
}
|
|
}
|
|
|
|
public static class SkillEntry {
|
|
|
|
public int masterlevel;
|
|
public byte skillevel;
|
|
public long expiration;
|
|
|
|
public SkillEntry(byte skillevel, int masterlevel, long expiration) {
|
|
this.skillevel = skillevel;
|
|
this.masterlevel = masterlevel;
|
|
this.expiration = expiration;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return skillevel + ":" + masterlevel;
|
|
}
|
|
}
|
|
|
|
public boolean skillIsCooling(int skillId) {
|
|
effLock.lock();
|
|
chrLock.lock();
|
|
try {
|
|
return coolDowns.containsKey(Integer.valueOf(skillId));
|
|
} finally {
|
|
chrLock.unlock();
|
|
effLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void runFullnessSchedule(int petSlot) {
|
|
MaplePet pet = getPet(petSlot);
|
|
if (pet == null) {
|
|
return;
|
|
}
|
|
|
|
int newFullness = pet.getFullness() - PetDataFactory.getHunger(pet.getItemId());
|
|
if (newFullness <= 5) {
|
|
pet.setFullness(15);
|
|
pet.saveToDb();
|
|
unequipPet(pet, true);
|
|
dropMessage(6, "Your pet grew hungry! Treat it some pet food to keep it healthy!");
|
|
} else {
|
|
pet.setFullness(newFullness);
|
|
pet.saveToDb();
|
|
Item petz = getInventory(MapleInventoryType.CASH).getItem(pet.getPosition());
|
|
if (petz != null) {
|
|
forceUpdateItem(petz);
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean runTirednessSchedule() {
|
|
if (maplemount != null) {
|
|
int tiredness = maplemount.incrementAndGetTiredness();
|
|
|
|
this.getMap().broadcastMessage(MaplePacketCreator.updateMount(this.getId(), maplemount, false));
|
|
if (tiredness > 99) {
|
|
maplemount.setTiredness(99);
|
|
this.dispelSkill(this.getJobType() * 10000000 + 1004);
|
|
this.dropMessage(6, "Your mount grew tired! Treat it some revitalizer before riding it again!");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void startMapEffect(String msg, int itemId) {
|
|
startMapEffect(msg, itemId, 30000);
|
|
}
|
|
|
|
public void startMapEffect(String msg, int itemId, int duration) {
|
|
final MapleMapEffect mapEffect = new MapleMapEffect(msg, itemId);
|
|
getClient().announce(mapEffect.makeStartData());
|
|
TimerManager.getInstance().schedule(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
getClient().announce(mapEffect.makeDestroyData());
|
|
}
|
|
}, duration);
|
|
}
|
|
|
|
public void unequipAllPets() {
|
|
for (int i = 0; i < 3; i++) {
|
|
MaplePet pet = getPet(i);
|
|
if (pet != null) {
|
|
unequipPet(pet, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void unequipPet(MaplePet pet, boolean shift_left) {
|
|
unequipPet(pet, shift_left, false);
|
|
}
|
|
|
|
public void unequipPet(MaplePet pet, boolean shift_left, boolean hunger) {
|
|
byte petIdx = this.getPetIndex(pet);
|
|
MaplePet chrPet = this.getPet(petIdx);
|
|
|
|
if (chrPet != null) {
|
|
chrPet.setSummoned(false);
|
|
chrPet.saveToDb();
|
|
}
|
|
|
|
this.getClient().getWorldServer().unregisterPetHunger(this, petIdx);
|
|
getMap().broadcastMessage(this, MaplePacketCreator.showPet(this, pet, true, hunger), true);
|
|
|
|
removePet(pet, shift_left);
|
|
commitExcludedItems();
|
|
|
|
client.announce(MaplePacketCreator.petStatUpdate(this));
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
}
|
|
|
|
public void updateMacros(int position, SkillMacro updateMacro) {
|
|
skillMacros[position] = updateMacro;
|
|
}
|
|
|
|
public void updatePartyMemberHP() {
|
|
prtLock.lock();
|
|
try {
|
|
updatePartyMemberHPInternal();
|
|
} finally {
|
|
prtLock.unlock();
|
|
}
|
|
}
|
|
|
|
private void updatePartyMemberHPInternal() {
|
|
if (party != null) {
|
|
int curmaxhp = getCurrentMaxHp();
|
|
int curhp = getHp();
|
|
for (MapleCharacter partychar : this.getPartyMembersOnSameMap()) {
|
|
partychar.announce(MaplePacketCreator.updatePartyMemberHP(getId(), curhp, curmaxhp));
|
|
}
|
|
}
|
|
}
|
|
|
|
public String getQuestInfo(int quest) {
|
|
MapleQuestStatus qs = getQuest(MapleQuest.getInstance(quest));
|
|
return qs.getInfo();
|
|
}
|
|
|
|
public void updateQuestInfo(int quest, String info) {
|
|
MapleQuest q = MapleQuest.getInstance(quest);
|
|
MapleQuestStatus qs = getQuest(q);
|
|
qs.setInfo(info);
|
|
|
|
synchronized (quests) {
|
|
quests.put(q.getId(), qs);
|
|
}
|
|
|
|
announce(MaplePacketCreator.updateQuest(qs, false));
|
|
if (qs.getQuest().getInfoNumber() > 0) {
|
|
announce(MaplePacketCreator.updateQuest(qs, true));
|
|
}
|
|
announce(MaplePacketCreator.updateQuestInfo((short) qs.getQuest().getId(), qs.getNpc()));
|
|
}
|
|
|
|
public void awardQuestPoint(int awardedPoints) {
|
|
if (ServerConstants.QUEST_POINT_REQUIREMENT < 1 || awardedPoints < 1) {
|
|
return;
|
|
}
|
|
|
|
int delta;
|
|
synchronized (quests) {
|
|
quest_fame += awardedPoints;
|
|
|
|
delta = quest_fame / ServerConstants.QUEST_POINT_REQUIREMENT;
|
|
quest_fame %= ServerConstants.QUEST_POINT_REQUIREMENT;
|
|
}
|
|
|
|
if (delta > 0) {
|
|
gainFame(delta);
|
|
}
|
|
}
|
|
|
|
public void updateQuest(MapleQuestStatus quest) {
|
|
synchronized (quests) {
|
|
quests.put(quest.getQuestID(), quest);
|
|
}
|
|
if (quest.getStatus().equals(MapleQuestStatus.Status.STARTED)) {
|
|
announce(MaplePacketCreator.updateQuest(quest, false));
|
|
if (quest.getQuest().getInfoNumber() > 0) {
|
|
announce(MaplePacketCreator.updateQuest(quest, true));
|
|
}
|
|
announce(MaplePacketCreator.updateQuestInfo((short) quest.getQuest().getId(), quest.getNpc()));
|
|
} else if (quest.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) {
|
|
MapleQuest mquest = quest.getQuest();
|
|
short questid = mquest.getId();
|
|
if (!mquest.isSameDayRepeatable() && !MapleQuest.isExploitableQuest(questid)) {
|
|
awardQuestPoint(ServerConstants.QUEST_POINT_PER_QUEST_COMPLETE);
|
|
}
|
|
|
|
announce(MaplePacketCreator.completeQuest(questid, quest.getCompletionTime()));
|
|
} else if (quest.getStatus().equals(MapleQuestStatus.Status.NOT_STARTED)) {
|
|
announce(MaplePacketCreator.updateQuest(quest, false));
|
|
if (quest.getQuest().getInfoNumber() > 0) {
|
|
announce(MaplePacketCreator.updateQuest(quest, true));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void expireQuest(MapleQuest quest) {
|
|
if (getQuestStatus(quest.getId()) == MapleQuestStatus.Status.COMPLETED.getId()) {
|
|
return;
|
|
}
|
|
if (System.currentTimeMillis() < getMapleQuestStatus(quest.getId()).getExpirationTime()) {
|
|
return;
|
|
}
|
|
|
|
announce(MaplePacketCreator.questExpire(quest.getId()));
|
|
MapleQuestStatus newStatus = new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED);
|
|
newStatus.setForfeited(getQuest(quest).getForfeited() + 1);
|
|
updateQuest(newStatus);
|
|
}
|
|
|
|
public void cancelQuestExpirationTask() {
|
|
evtLock.lock();
|
|
try {
|
|
if (questExpireTask != null) {
|
|
questExpireTask.cancel(false);
|
|
questExpireTask = null;
|
|
}
|
|
} finally {
|
|
evtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void forfeitExpirableQuests() {
|
|
evtLock.lock();
|
|
try {
|
|
for (MapleQuest quest : questExpirations.keySet()) {
|
|
quest.forfeit(this);
|
|
}
|
|
|
|
questExpirations.clear();
|
|
} finally {
|
|
evtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void questExpirationTask() {
|
|
evtLock.lock();
|
|
try {
|
|
if (!questExpirations.isEmpty()) {
|
|
if (questExpireTask == null) {
|
|
questExpireTask = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
runQuestExpireTask();
|
|
}
|
|
}, 10 * 1000);
|
|
}
|
|
}
|
|
} finally {
|
|
evtLock.unlock();
|
|
}
|
|
}
|
|
|
|
private void runQuestExpireTask() {
|
|
evtLock.lock();
|
|
try {
|
|
long timeNow = Server.getInstance().getCurrentTime();
|
|
List<MapleQuest> expireList = new LinkedList<>();
|
|
|
|
for (Entry<MapleQuest, Long> qe : questExpirations.entrySet()) {
|
|
if (qe.getValue() <= timeNow) {
|
|
expireList.add(qe.getKey());
|
|
}
|
|
}
|
|
|
|
if (!expireList.isEmpty()) {
|
|
for (MapleQuest quest : expireList) {
|
|
expireQuest(quest);
|
|
questExpirations.remove(quest);
|
|
}
|
|
|
|
if (questExpirations.isEmpty()) {
|
|
questExpireTask.cancel(false);
|
|
questExpireTask = null;
|
|
}
|
|
}
|
|
} finally {
|
|
evtLock.unlock();
|
|
}
|
|
}
|
|
|
|
private void registerQuestExpire(MapleQuest quest, long time) {
|
|
evtLock.lock();
|
|
try {
|
|
if (questExpireTask == null) {
|
|
questExpireTask = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
runQuestExpireTask();
|
|
}
|
|
}, 10 * 1000);
|
|
}
|
|
|
|
questExpirations.put(quest, Server.getInstance().getCurrentTime() + time);
|
|
} finally {
|
|
evtLock.unlock();
|
|
}
|
|
}
|
|
|
|
public void questTimeLimit(final MapleQuest quest, int seconds) {
|
|
registerQuestExpire(quest, seconds * 1000);
|
|
announce(MaplePacketCreator.addQuestTimeLimit(quest.getId(), seconds * 1000));
|
|
}
|
|
|
|
public void questTimeLimit2(final MapleQuest quest, long expires) {
|
|
long timeLeft = expires - System.currentTimeMillis();
|
|
|
|
if (timeLeft <= 0) {
|
|
expireQuest(quest);
|
|
} else {
|
|
registerQuestExpire(quest, timeLeft);
|
|
}
|
|
}
|
|
|
|
public void updateSingleStat(MapleStat stat, int newval) {
|
|
updateSingleStat(stat, newval, false);
|
|
}
|
|
|
|
private void updateSingleStat(MapleStat stat, int newval, boolean itemReaction) {
|
|
announce(MaplePacketCreator.updatePlayerStats(Collections.singletonList(new Pair<>(stat, Integer.valueOf(newval))), itemReaction, this));
|
|
}
|
|
|
|
public void announce(final byte[] packet) {
|
|
client.announce(packet);
|
|
}
|
|
|
|
@Override
|
|
public int getObjectId() {
|
|
return getId();
|
|
}
|
|
|
|
@Override
|
|
public MapleMapObjectType getType() {
|
|
return MapleMapObjectType.PLAYER;
|
|
}
|
|
|
|
@Override
|
|
public void sendDestroyData(MapleClient client) {
|
|
client.announce(MaplePacketCreator.removePlayerFromMap(this.getObjectId()));
|
|
}
|
|
|
|
@Override
|
|
public void sendSpawnData(MapleClient client) {
|
|
if (!this.isHidden() || client.getPlayer().gmLevel() > 1) {
|
|
client.announce(MaplePacketCreator.spawnPlayerMapObject(client, this, false));
|
|
|
|
if (buffEffects.containsKey(getJobMapChair(job))) { // mustn't effLock, chrLock this function
|
|
client.announce(MaplePacketCreator.giveForeignChairSkillEffect(id));
|
|
}
|
|
}
|
|
|
|
if (this.isHidden()) {
|
|
List<Pair<MapleBuffStat, Integer>> dsstat = Collections.singletonList(new Pair<>(MapleBuffStat.DARKSIGHT, 0));
|
|
getMap().broadcastGMMessage(this, MaplePacketCreator.giveForeignBuff(getId(), dsstat), false);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setObjectId(int id) {
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return name;
|
|
}
|
|
|
|
public int getLinkedLevel() {
|
|
return linkedLevel;
|
|
}
|
|
|
|
public String getLinkedName() {
|
|
return linkedName;
|
|
}
|
|
|
|
public CashShop getCashShop() {
|
|
return cashshop;
|
|
}
|
|
|
|
public Set<NewYearCardRecord> getNewYearRecords() {
|
|
return newyears;
|
|
}
|
|
|
|
public Set<NewYearCardRecord> getReceivedNewYearRecords() {
|
|
Set<NewYearCardRecord> received = new LinkedHashSet<>();
|
|
|
|
for (NewYearCardRecord nyc : newyears) {
|
|
if (nyc.isReceiverCardReceived()) {
|
|
received.add(nyc);
|
|
}
|
|
}
|
|
|
|
return received;
|
|
}
|
|
|
|
public NewYearCardRecord getNewYearRecord(int cardid) {
|
|
for (NewYearCardRecord nyc : newyears) {
|
|
if (nyc.getId() == cardid) {
|
|
return nyc;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public void addNewYearRecord(NewYearCardRecord newyear) {
|
|
newyears.add(newyear);
|
|
}
|
|
|
|
public void removeNewYearRecord(NewYearCardRecord newyear) {
|
|
newyears.remove(newyear);
|
|
}
|
|
|
|
public void portalDelay(long delay) {
|
|
this.portaldelay = System.currentTimeMillis() + delay;
|
|
}
|
|
|
|
public long portalDelay() {
|
|
return portaldelay;
|
|
}
|
|
|
|
public void blockPortal(String scriptName) {
|
|
if (!blockedPortals.contains(scriptName) && scriptName != null) {
|
|
blockedPortals.add(scriptName);
|
|
client.announce(MaplePacketCreator.enableActions());
|
|
}
|
|
}
|
|
|
|
public void unblockPortal(String scriptName) {
|
|
if (blockedPortals.contains(scriptName) && scriptName != null) {
|
|
blockedPortals.remove(scriptName);
|
|
}
|
|
}
|
|
|
|
public List<String> getBlockedPortals() {
|
|
return blockedPortals;
|
|
}
|
|
|
|
public boolean containsAreaInfo(int area, String info) {
|
|
Short area_ = Short.valueOf((short) area);
|
|
if (area_info.containsKey(area_)) {
|
|
return area_info.get(area_).contains(info);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void updateAreaInfo(int area, String info) {
|
|
area_info.put(Short.valueOf((short) area), info);
|
|
announce(MaplePacketCreator.updateAreaInfo(area, info));
|
|
}
|
|
|
|
public String getAreaInfo(int area) {
|
|
return area_info.get(Short.valueOf((short) area));
|
|
}
|
|
|
|
public Map<Short, String> getAreaInfos() {
|
|
return area_info;
|
|
}
|
|
|
|
public void autoban(String reason) {
|
|
this.ban(reason);
|
|
announce(MaplePacketCreator.sendPolice(String.format("You have been blocked by the#b %s Police for HACK reason.#k", "HeavenMS")));
|
|
TimerManager.getInstance().schedule(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
client.disconnect(false, false);
|
|
}
|
|
}, 5000);
|
|
|
|
Server.getInstance().broadcastGMMessage(this.getWorld(), MaplePacketCreator.serverNotice(6, MapleCharacter.makeMapleReadable(this.name) + " was autobanned for " + reason));
|
|
}
|
|
|
|
public void block(int reason, int days, String desc) {
|
|
Calendar cal = Calendar.getInstance();
|
|
cal.add(Calendar.DATE, days);
|
|
Timestamp TS = new Timestamp(cal.getTimeInMillis());
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET banreason = ?, tempban = ?, greason = ? WHERE id = ?")) {
|
|
ps.setString(1, desc);
|
|
ps.setTimestamp(2, TS);
|
|
ps.setInt(3, reason);
|
|
ps.setInt(4, accountid);
|
|
ps.executeUpdate();
|
|
} finally {
|
|
con.close();
|
|
}
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public boolean isBanned() {
|
|
return isbanned;
|
|
}
|
|
|
|
public List<Integer> getTrockMaps() {
|
|
return trockmaps;
|
|
}
|
|
|
|
public List<Integer> getVipTrockMaps() {
|
|
return viptrockmaps;
|
|
}
|
|
|
|
public int getTrockSize() {
|
|
int ret = trockmaps.indexOf(999999999);
|
|
if (ret == -1) {
|
|
ret = 5;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
public void deleteFromTrocks(int map) {
|
|
trockmaps.remove(Integer.valueOf(map));
|
|
while (trockmaps.size() < 10) {
|
|
trockmaps.add(999999999);
|
|
}
|
|
}
|
|
|
|
public void addTrockMap() {
|
|
int index = trockmaps.indexOf(999999999);
|
|
if (index != -1) {
|
|
trockmaps.set(index, getMapId());
|
|
}
|
|
}
|
|
|
|
public boolean isTrockMap(int id) {
|
|
int index = trockmaps.indexOf(id);
|
|
if (index != -1) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public int getVipTrockSize() {
|
|
int ret = viptrockmaps.indexOf(999999999);
|
|
|
|
if (ret == -1) {
|
|
ret = 10;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
public void deleteFromVipTrocks(int map) {
|
|
viptrockmaps.remove(Integer.valueOf(map));
|
|
while (viptrockmaps.size() < 10) {
|
|
viptrockmaps.add(999999999);
|
|
}
|
|
}
|
|
|
|
public void addVipTrockMap() {
|
|
int index = viptrockmaps.indexOf(999999999);
|
|
if (index != -1) {
|
|
viptrockmaps.set(index, getMapId());
|
|
}
|
|
}
|
|
|
|
public boolean isVipTrockMap(int id) {
|
|
int index = viptrockmaps.indexOf(id);
|
|
if (index != -1) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//EVENTS
|
|
private byte team = 0;
|
|
private MapleFitness fitness;
|
|
private MapleOla ola;
|
|
private long snowballattack;
|
|
public static final List<String> itens = new ArrayList();
|
|
public static final List<Item> item = new ArrayList();
|
|
|
|
public byte getTeam() {
|
|
return team;
|
|
}
|
|
|
|
public void setTeam(int team) {
|
|
this.team = (byte) team;
|
|
}
|
|
|
|
public MapleOla getOla() {
|
|
return ola;
|
|
}
|
|
|
|
public void setOla(MapleOla ola) {
|
|
this.ola = ola;
|
|
}
|
|
|
|
public MapleFitness getFitness() {
|
|
return fitness;
|
|
}
|
|
|
|
public void setFitness(MapleFitness fit) {
|
|
this.fitness = fit;
|
|
}
|
|
|
|
public long getLastSnowballAttack() {
|
|
return snowballattack;
|
|
}
|
|
|
|
public void setLastSnowballAttack(long time) {
|
|
this.snowballattack = time;
|
|
}
|
|
|
|
public AutobanManager getAutobanManager() {
|
|
return autoban;
|
|
}
|
|
|
|
public void equippedItem(Equip equip) {
|
|
int itemid = equip.getItemId();
|
|
|
|
if (itemid == 1122017) {
|
|
this.equipPendantOfSpirit();
|
|
} else if (itemid == 1812000) { // meso magnet
|
|
equippedMesoMagnet = true;
|
|
} else if (itemid == 1812001) { // item pouch
|
|
equippedItemPouch = true;
|
|
} else if (itemid == 1812007) { // item ignore pendant
|
|
equippedPetItemIgnore = true;
|
|
}
|
|
}
|
|
|
|
public void unequippedItem(Equip equip) {
|
|
int itemid = equip.getItemId();
|
|
|
|
if (itemid == 1122017) {
|
|
this.unequipPendantOfSpirit();
|
|
} else if (itemid == 1812000) { // meso magnet
|
|
equippedMesoMagnet = false;
|
|
} else if (itemid == 1812001) { // item pouch
|
|
equippedItemPouch = false;
|
|
} else if (itemid == 1812007) { // item ignore pendant
|
|
equippedPetItemIgnore = false;
|
|
}
|
|
}
|
|
|
|
public boolean isEquippedMesoMagnet() {
|
|
return equippedMesoMagnet;
|
|
}
|
|
|
|
public boolean isEquippedItemPouch() {
|
|
return equippedItemPouch;
|
|
}
|
|
|
|
public boolean isEquippedPetItemIgnore() {
|
|
return equippedPetItemIgnore;
|
|
}
|
|
|
|
private void equipPendantOfSpirit() {
|
|
if (pendantOfSpirit == null) {
|
|
pendantOfSpirit = TimerManager.getInstance().register(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (pendantExp < 3) {
|
|
pendantExp++;
|
|
message("Pendant of the Spirit has been equipped for " + pendantExp + " hour(s), you will now receive " + pendantExp + "0% bonus exp.");
|
|
} else {
|
|
pendantOfSpirit.cancel(false);
|
|
}
|
|
}
|
|
}, 3600000); //1 hour
|
|
}
|
|
}
|
|
|
|
private void unequipPendantOfSpirit() {
|
|
if (pendantOfSpirit != null) {
|
|
pendantOfSpirit.cancel(false);
|
|
pendantOfSpirit = null;
|
|
}
|
|
pendantExp = 0;
|
|
}
|
|
|
|
private Collection<Item> getUpgradeableEquipList() {
|
|
Collection<Item> fullList = getInventory(MapleInventoryType.EQUIPPED).list();
|
|
if (ServerConstants.USE_EQUIPMNT_LVLUP_CASH) {
|
|
return fullList;
|
|
}
|
|
|
|
Collection<Item> eqpList = new LinkedHashSet<>();
|
|
for (Item it : fullList) {
|
|
if (!ii.isCash(it.getItemId())) {
|
|
eqpList.add(it);
|
|
}
|
|
}
|
|
|
|
return eqpList;
|
|
}
|
|
|
|
public void increaseEquipExp(int expGain) {
|
|
if (expGain < 0) {
|
|
expGain = Integer.MAX_VALUE;
|
|
}
|
|
|
|
for (Item item : getUpgradeableEquipList()) {
|
|
Equip nEquip = (Equip) item;
|
|
String itemName = ii.getName(nEquip.getItemId());
|
|
if (itemName == null) {
|
|
continue;
|
|
}
|
|
|
|
nEquip.gainItemExp(client, expGain);
|
|
}
|
|
}
|
|
|
|
public void showAllEquipFeatures() {
|
|
String showMsg = "";
|
|
|
|
for (Item item : getInventory(MapleInventoryType.EQUIPPED).list()) {
|
|
Equip nEquip = (Equip) item;
|
|
String itemName = ii.getName(nEquip.getItemId());
|
|
if (itemName == null) {
|
|
continue;
|
|
}
|
|
|
|
showMsg += nEquip.showEquipFeatures(client);
|
|
}
|
|
|
|
if (!showMsg.isEmpty()) {
|
|
this.showHint("#ePLAYER EQUIPMENTS:#n\r\n\r\n" + showMsg, 400);
|
|
}
|
|
}
|
|
|
|
public void broadcastMarriageMessage() {
|
|
MapleGuild guild = this.getGuild();
|
|
if (guild != null) {
|
|
guild.broadcast(MaplePacketCreator.marriageMessage(0, name));
|
|
}
|
|
|
|
MapleFamily family = this.getFamily();
|
|
if (family != null) {
|
|
family.broadcast(MaplePacketCreator.marriageMessage(1, name));
|
|
}
|
|
}
|
|
|
|
public Map<String, MapleEvents> getEvents() {
|
|
return events;
|
|
}
|
|
|
|
public PartyQuest getPartyQuest() {
|
|
return partyQuest;
|
|
}
|
|
|
|
public void setPartyQuest(PartyQuest pq) {
|
|
this.partyQuest = pq;
|
|
}
|
|
|
|
public final void empty(final boolean remove) {
|
|
if (dragonBloodSchedule != null) {
|
|
dragonBloodSchedule.cancel(true);
|
|
}
|
|
dragonBloodSchedule = null;
|
|
|
|
if (hpDecreaseTask != null) {
|
|
hpDecreaseTask.cancel(true);
|
|
}
|
|
hpDecreaseTask = null;
|
|
|
|
if (beholderHealingSchedule != null) {
|
|
beholderHealingSchedule.cancel(true);
|
|
}
|
|
beholderHealingSchedule = null;
|
|
|
|
if (beholderBuffSchedule != null) {
|
|
beholderBuffSchedule.cancel(true);
|
|
}
|
|
beholderBuffSchedule = null;
|
|
|
|
if (berserkSchedule != null) {
|
|
berserkSchedule.cancel(true);
|
|
}
|
|
berserkSchedule = null;
|
|
|
|
unregisterChairBuff();
|
|
cancelBuffExpireTask();
|
|
cancelDiseaseExpireTask();
|
|
cancelSkillCooldownTask();
|
|
cancelExpirationTask();
|
|
|
|
if (questExpireTask != null) {
|
|
questExpireTask.cancel(true);
|
|
}
|
|
questExpireTask = null;
|
|
|
|
if (recoveryTask != null) {
|
|
recoveryTask.cancel(true);
|
|
}
|
|
recoveryTask = null;
|
|
|
|
if (extraRecoveryTask != null) {
|
|
extraRecoveryTask.cancel(true);
|
|
}
|
|
extraRecoveryTask = null;
|
|
|
|
// already done on unregisterChairBuff
|
|
/* if (chairRecoveryTask != null) { chairRecoveryTask.cancel(true); }
|
|
chairRecoveryTask = null; */
|
|
if (pendantOfSpirit != null) {
|
|
pendantOfSpirit.cancel(true);
|
|
}
|
|
pendantOfSpirit = null;
|
|
|
|
if (timer != null) {
|
|
timer.cancel(true);
|
|
}
|
|
timer = null;
|
|
|
|
evtLock.lock();
|
|
try {
|
|
if (questExpireTask != null) {
|
|
questExpireTask.cancel(false);
|
|
questExpireTask = null;
|
|
|
|
questExpirations.clear();
|
|
questExpirations = null;
|
|
}
|
|
} finally {
|
|
evtLock.unlock();
|
|
}
|
|
|
|
if (maplemount != null) {
|
|
maplemount.empty();
|
|
maplemount = null;
|
|
}
|
|
if (remove) {
|
|
partyQuest = null;
|
|
events = null;
|
|
mpc = null;
|
|
mgc = null;
|
|
party = null;
|
|
family = null;
|
|
|
|
getWorldServer().registerTimedMapObject(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
client = null; // clients still triggers handlers a few times after disconnecting
|
|
map = null;
|
|
setListener(null);
|
|
}
|
|
}, 5 * 60 * 1000);
|
|
}
|
|
}
|
|
|
|
public void logOff() {
|
|
this.loggedIn = false;
|
|
|
|
try (Connection con = DatabaseConnection.getConnection(); PreparedStatement ps = con.prepareStatement("UPDATE characters SET lastLogoutTime=? WHERE id=?")) {
|
|
ps.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
|
|
ps.setInt(2, getId());
|
|
ps.executeUpdate();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public boolean isLoggedin() {
|
|
return loggedIn;
|
|
}
|
|
|
|
public void setMapId(int mapid) {
|
|
this.mapid = mapid;
|
|
}
|
|
|
|
public boolean getWhiteChat() {
|
|
return !isGM() ? false : whiteChat;
|
|
}
|
|
|
|
public void toggleWhiteChat() {
|
|
whiteChat = !whiteChat;
|
|
}
|
|
|
|
public boolean canDropMeso() {
|
|
if (System.currentTimeMillis() - lastMesoDrop >= 200 || lastMesoDrop == -1) { //About 200 meso drops a minute
|
|
lastMesoDrop = System.currentTimeMillis();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// These need to be renamed, but I am too lazy right now to go through the scripts and rename them...
|
|
public String getPartyQuestItems() {
|
|
return dataString;
|
|
}
|
|
|
|
public boolean gotPartyQuestItem(String partyquestchar) {
|
|
return dataString.contains(partyquestchar);
|
|
}
|
|
|
|
public void removePartyQuestItem(String letter) {
|
|
if (gotPartyQuestItem(letter)) {
|
|
dataString = dataString.substring(0, dataString.indexOf(letter)) + dataString.substring(dataString.indexOf(letter) + letter.length());
|
|
}
|
|
}
|
|
|
|
public void setPartyQuestItemObtained(String partyquestchar) {
|
|
if (!dataString.contains(partyquestchar)) {
|
|
this.dataString += partyquestchar;
|
|
}
|
|
}
|
|
|
|
public void createDragon() {
|
|
dragon = new MapleDragon(this);
|
|
}
|
|
|
|
public MapleDragon getDragon() {
|
|
return dragon;
|
|
}
|
|
|
|
public void setDragon(MapleDragon dragon) {
|
|
this.dragon = dragon;
|
|
}
|
|
|
|
public long getJailExpirationTimeLeft() {
|
|
return jailExpiration - System.currentTimeMillis();
|
|
}
|
|
|
|
private void setFutureJailExpiration(long time) {
|
|
jailExpiration = System.currentTimeMillis() + time;
|
|
}
|
|
|
|
public void addJailExpirationTime(long time) {
|
|
long timeLeft = getJailExpirationTimeLeft();
|
|
|
|
if (timeLeft <= 0) {
|
|
setFutureJailExpiration(time);
|
|
} else {
|
|
setFutureJailExpiration(timeLeft + time);
|
|
}
|
|
}
|
|
|
|
public void removeJailExpirationTime() {
|
|
jailExpiration = 0;
|
|
}
|
|
|
|
public int getRewardPoints() {
|
|
Connection con = null;
|
|
PreparedStatement ps = null;
|
|
try {
|
|
con = DatabaseConnection.getConnection();
|
|
ps = con.prepareStatement("SELECT rewardpoints FROM accounts WHERE id=?;");
|
|
ps.setInt(1, accountid);
|
|
ResultSet resultSet = ps.executeQuery();
|
|
int point = -1;
|
|
if (resultSet.next()) {
|
|
point = resultSet.getInt(1);
|
|
}
|
|
return point;
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
} finally {
|
|
try {
|
|
ps.close();
|
|
} catch (Exception e) { /* ignored */ }
|
|
try {
|
|
con.close();
|
|
} catch (Exception e) { /* ignored */ }
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public void setRewardPoints(int value) {
|
|
Connection con = null;
|
|
PreparedStatement ps = null;
|
|
try {
|
|
con = DatabaseConnection.getConnection();
|
|
ps = con.prepareStatement("UPDATE accounts SET rewardpoints=? WHERE id=?;");
|
|
ps.setInt(1, value);
|
|
ps.setInt(2, accountid);
|
|
ps.executeUpdate();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
} finally {
|
|
try {
|
|
ps.close();
|
|
} catch (Exception e) { /* ignored */ }
|
|
try {
|
|
con.close();
|
|
} catch (Exception e) { /* ignored */ }
|
|
}
|
|
}
|
|
|
|
public void setReborns(int value) {
|
|
if (!ServerConstants.USE_REBIRTH_SYSTEM) {
|
|
yellowMessage("Rebirth system is not enabled!");
|
|
throw new NotEnabledException();
|
|
}
|
|
Connection con = null;
|
|
PreparedStatement ps = null;
|
|
try {
|
|
con = DatabaseConnection.getConnection();
|
|
ps = con.prepareStatement("UPDATE characters SET reborns=? WHERE id=?;");
|
|
ps.setInt(1, value);
|
|
ps.setInt(2, id);
|
|
ps.executeUpdate();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
} finally {
|
|
try {
|
|
ps.close();
|
|
} catch (Exception e) { /* ignored */ }
|
|
try {
|
|
con.close();
|
|
} catch (Exception e) { /* ignored */ }
|
|
}
|
|
}
|
|
|
|
public void addReborns() {
|
|
setReborns(getReborns() + 1);
|
|
}
|
|
|
|
public int getReborns() {
|
|
if (!ServerConstants.USE_REBIRTH_SYSTEM) {
|
|
yellowMessage("Rebirth system is not enabled!");
|
|
throw new NotEnabledException();
|
|
}
|
|
Connection con = null;
|
|
PreparedStatement ps = null;
|
|
try {
|
|
con = DatabaseConnection.getConnection();
|
|
ps = con.prepareStatement("SELECT reborns FROM characters WHERE id=?;");
|
|
ps.setInt(1, id);
|
|
ResultSet resultSet = ps.executeQuery();
|
|
resultSet.next();
|
|
return resultSet.getInt(1);
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
} finally {
|
|
try {
|
|
ps.close();
|
|
} catch (Exception e) { /* ignored */ }
|
|
try {
|
|
con.close();
|
|
} catch (Exception e) { /* ignored */ }
|
|
}
|
|
throw new RuntimeException();
|
|
}
|
|
|
|
public void executeReborn() {
|
|
if (!ServerConstants.USE_REBIRTH_SYSTEM) {
|
|
yellowMessage("Rebirth system is not enabled!");
|
|
throw new NotEnabledException();
|
|
}
|
|
if (getLevel() != 200) {
|
|
return;
|
|
}
|
|
addReborns();
|
|
changeJob(MapleJob.BEGINNER);
|
|
setLevel(0);
|
|
levelUp(true);
|
|
}
|
|
|
|
private int cp = 0;
|
|
private int totCP = 0;
|
|
private MonsterCarnival monsterCarnival;
|
|
private int FestivalPoints;
|
|
private boolean challenged = false;
|
|
|
|
public void gainFestivalPoints(int gain) {
|
|
this.FestivalPoints += gain;
|
|
}
|
|
|
|
public int getFestivalPoints() {
|
|
return this.FestivalPoints;
|
|
}
|
|
|
|
public void setFestivalPoints(int pontos) {
|
|
this.FestivalPoints = pontos;
|
|
}
|
|
|
|
public int getCP() {
|
|
return cp;
|
|
}
|
|
|
|
public short totalCP, availableCP;
|
|
|
|
public void addCP(int ammount) {
|
|
totalCP += ammount;
|
|
availableCP += ammount;
|
|
}
|
|
|
|
public void useCP(int ammount) {
|
|
availableCP -= ammount;
|
|
}
|
|
|
|
public void gainCP(int gain) {
|
|
if (this.getMonsterCarnival() != null) {
|
|
if (gain > 0) {
|
|
this.setTotalCP(this.getTotalCP() + gain);
|
|
}
|
|
this.setCP(this.getCP() + gain);
|
|
if (this.getParty() != null) {
|
|
this.getMonsterCarnival().setCP(this.getMonsterCarnival().getCP(team) + gain, team);
|
|
if (gain > 0) {
|
|
this.getMonsterCarnival().setTotalCP(this.getMonsterCarnival().getTotalCP(team) + gain, team);
|
|
}
|
|
}
|
|
if (this.getCP() > this.getTotalCP()) {
|
|
this.setTotalCP(this.getCP());
|
|
}
|
|
this.getClient().announce(MaplePacketCreator.CPUpdate(false, this.getCP(), this.getTotalCP(), getTeam()));
|
|
if (this.getParty() != null && getTeam() != -1) {
|
|
this.getMap().broadcastMessage(MaplePacketCreator.CPUpdate(true, this.getMonsterCarnival().getCP(team), this.getMonsterCarnival().getTotalCP(team), getTeam()));
|
|
} else {
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setTotalCP(int a) {
|
|
this.totCP = a;
|
|
}
|
|
|
|
public void setCP(int a) {
|
|
this.cp = a;
|
|
}
|
|
|
|
public int getTotalCP() {
|
|
return totCP;
|
|
}
|
|
|
|
public int getAvailableCP() {
|
|
return availableCP;
|
|
}
|
|
|
|
public void resetCP() {
|
|
this.cp = 0;
|
|
this.totCP = 0;
|
|
this.monsterCarnival = null;
|
|
}
|
|
|
|
public MonsterCarnival getMonsterCarnival() {
|
|
return monsterCarnival;
|
|
}
|
|
|
|
public void setMonsterCarnival(MonsterCarnival monsterCarnival) {
|
|
this.monsterCarnival = monsterCarnival;
|
|
}
|
|
|
|
public boolean isChallenged() {
|
|
return challenged;
|
|
}
|
|
|
|
public void setChallenged(boolean challenged) {
|
|
this.challenged = challenged;
|
|
}
|
|
|
|
public void setLingua(int num) {
|
|
getClient().setLingua(num);
|
|
try {
|
|
Connection con = DatabaseConnection.getConnection();
|
|
try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET lingua = ? WHERE id = ?")) {
|
|
ps.setInt(1, num);
|
|
ps.setInt(2, getClient().getAccID());
|
|
ps.executeUpdate();
|
|
} finally {
|
|
con.close();
|
|
}
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public int getLingua() {
|
|
return getClient().getLingua();
|
|
}
|
|
|
|
public void setItens(String item) {
|
|
if (!itens.contains(item)) {
|
|
this.itens.add(item);
|
|
}
|
|
}
|
|
|
|
public static List<String> getItens() {
|
|
return itens;
|
|
}
|
|
|
|
public void setEquips(Item item) {
|
|
this.item.add(item);
|
|
}
|
|
|
|
public static List<Item> getItem() {
|
|
return item;
|
|
}
|
|
|
|
public Item getItemid(int numb) {
|
|
return this.item.get(numb);
|
|
}
|
|
|
|
public void removeItem(Item item) {
|
|
if (this.item.contains(item)) {
|
|
this.item.remove(item);
|
|
}
|
|
}
|
|
|
|
public void obterItens() {
|
|
for (Item item : getItem()) {
|
|
getClient().getAbstractPlayerInteraction().gainItem(item.getItemId(), item.getQuantity());
|
|
}
|
|
}
|
|
|
|
public int getAriantScore() {
|
|
return this.countItem(4031868);
|
|
}
|
|
|
|
public void updateAriantScore() {
|
|
this.getMap().broadcastMessage(MaplePacketCreator.updateAriantPQRanking(this.getName(), getAriantScore(), false));
|
|
}
|
|
|
|
public void disease(int type, int level) {
|
|
if (MapleDisease.getBySkill(type) == null) {
|
|
return;
|
|
}
|
|
giveDebuff(MapleDisease.getBySkill(type), MobSkillFactory.getMobSkill(type, level));
|
|
}
|
|
|
|
public void shield() {
|
|
List<Pair<MapleBuffStat, Integer>> ldsstat = Collections.singletonList(new Pair<MapleBuffStat, Integer>(MapleBuffStat.ARIANT_PQ_SHIELD, 1));
|
|
getMap().broadcastMessage(this, MaplePacketCreator.giveForeignBuff(id, ldsstat), false);
|
|
}
|
|
|
|
public ScheduledFuture<?> getPqMapleMap() {
|
|
return pqMapleMap;
|
|
}
|
|
|
|
public void setPqMapleMap(ScheduledFuture<?> pqMapleMap) {
|
|
this.pqMapleMap = pqMapleMap;
|
|
}
|
|
|
|
public ScheduledFuture<?> getAriantScoreBord() {
|
|
return ariantScore;
|
|
}
|
|
|
|
public void setAriantScore(ScheduledFuture<?> ariantScore) {
|
|
this.ariantScore = ariantScore;
|
|
}
|
|
|
|
}
|