package javastory.channel;
import java.awt.Point;
import java.io.Serializable;
import java.rmi.RemoteException;
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.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.EnumMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
import javastory.channel.anticheat.CheatTracker;
import javastory.channel.client.ActivePlayerStats;
import javastory.channel.client.BuddyList;
import javastory.channel.client.BuddyListEntry;
import javastory.channel.client.BuffStat;
import javastory.channel.client.BuffStatValueHolder;
import javastory.channel.client.CancelCooldownAction;
import javastory.channel.client.CooldownValueHolder;
import javastory.channel.client.Disease;
import javastory.channel.client.DiseaseValueHolder;
import javastory.channel.client.ISkill;
import javastory.channel.client.KeyBinding;
import javastory.channel.client.KeyLayout;
import javastory.channel.client.MemberRank;
import javastory.channel.client.MonsterBook;
import javastory.channel.client.Mount;
import javastory.channel.client.MultiInventory;
import javastory.channel.client.Pet;
import javastory.channel.client.Skill;
import javastory.channel.client.SkillEntry;
import javastory.channel.life.MobSkill;
import javastory.channel.life.Monster;
import javastory.channel.maps.AbstractAnimatedGameMapObject;
import javastory.channel.maps.Door;
import javastory.channel.maps.Dragon;
import javastory.channel.maps.FieldLimitType;
import javastory.channel.maps.GameMap;
import javastory.channel.maps.GameMapFactory;
import javastory.channel.maps.GameMapObject;
import javastory.channel.maps.GameMapObjectType;
import javastory.channel.maps.SavedLocationType;
import javastory.channel.maps.Summon;
import javastory.channel.movement.LifeMovementFragment;
import javastory.channel.packet.MTSCSPacket;
import javastory.channel.packet.MobPacket;
import javastory.channel.packet.MonsterCarnivalPacket;
import javastory.channel.packet.PetPacket;
import javastory.channel.server.CarnivalChallenge;
import javastory.channel.server.CarnivalParty;
import javastory.channel.server.InventoryManipulator;
import javastory.channel.server.Portal;
import javastory.channel.server.Shop;
import javastory.channel.server.StatEffect;
import javastory.channel.server.Storage;
import javastory.channel.server.Trade;
import javastory.channel.shops.PlayerShop;
import javastory.client.GameCharacter;
import javastory.client.PlayerRandomStream;
import javastory.db.Database;
import javastory.db.DatabaseException;
import javastory.game.Equip;
import javastory.game.GameConstants;
import javastory.game.Gender;
import javastory.game.Inventory;
import javastory.game.InventoryType;
import javastory.game.Item;
import javastory.game.ItemFlag;
import javastory.game.Jobs;
import javastory.game.Skills;
import javastory.game.Stat;
import javastory.game.StatValue;
import javastory.game.data.ItemInfoProvider;
import javastory.game.data.RandomRewards;
import javastory.game.data.SkillInfoProvider;
import javastory.game.quest.QuestInfoProvider;
import javastory.game.quest.QuestInfoProvider.QuestInfo;
import javastory.game.quest.QuestStatus;
import javastory.io.GamePacket;
import javastory.io.PacketBuilder;
import javastory.rmi.WorldChannelInterface;
import javastory.scripting.EventInstanceManager;
import javastory.scripting.NpcScriptManager;
import javastory.server.BuffStatValue;
import javastory.server.FameLog;
import javastory.server.Notes;
import javastory.server.Notes.Note;
import javastory.server.TimerManager;
import javastory.tools.Randomizer;
import javastory.tools.packets.ChannelPackets;
import javastory.tools.packets.UIPacket;
import javastory.world.core.PartyOperation;
import javastory.world.core.PlayerCooldownValueHolder;
import javastory.world.core.PlayerDiseaseValueHolder;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
public class ChannelCharacter extends AbstractAnimatedGameMapObject implements GameCharacter, Serializable {
/**
*
*/
private static final long serialVersionUID = 2519039181363990945L;
// TODO: Global Damage caps should not be here.
public static int damageCap = 100000000;
public static int magicCap = 999999999;
private transient int linkedMonsterId = 0;
private transient Dragon dragon;
private transient List<LifeMovementFragment> lastMovement;
private transient Set<Monster> controlledMonsters;
private transient Set<GameMapObject> visibleMapObjects;
private transient Map<Integer, Summon> summons;
// TODO: Create a class for these triplet.
private transient Map<Integer, CooldownValueHolder> cooldowns;
private transient Map<Disease, DiseaseValueHolder> diseases;
private transient Map<BuffStat, BuffStatValueHolder> effects;
// TODO: Find a better place for these two.
private transient Deque<CarnivalChallenge> pendingCarnivalRequests;
private transient CarnivalParty carnivalParty;
private transient CheatTracker cheatTracker;
private transient AtomicInteger conversationState;
private transient EventInstanceManager eventInstance;
// TODO: Either add an interface for the map or for the character, circular dependency bad!
private transient GameMap map;
// TODO: This can likely be generalized into a playerInteraction thingie?
private transient Shop shop;
// TODO: This can likely be generalized into a PlayerIntraction thingie?
private transient Trade trade;
// TODO: Move fullness schedules to Pet class.
private transient ScheduledFuture<?> fullnessSchedule, fullnessSchedule_1, fullnessSchedule_2, hpDecreaseTask;
// TODO: Surely these should be buff tasks?
private transient ScheduledFuture<?> beholderHealingSchedule, beholderBuffSchedule, BerserkSchedule;
private transient ScheduledFuture<?> dragonBloodSchedule;
private transient ScheduledFuture<?> mapTimeLimitTask, fishing;
// TODO: Use skill checking instead?
private boolean canDoor, berserk, smega, hidden;
// TODO: Are these really necessary?
private boolean ondmg = true, callgm = true;
// Make this a quest record,
// TODO : Transfer it somehow with the current data
private byte dojoRecord;
//
private int id;
private Gender gender;
private byte gmLevel;
private int hairId, faceId, skinColorId;
private int level, jobId, fame;
// TODO: WorldId is obsolete here.
public int worldId;
private short mulung_energy, combo, availableCP, totalCP;
// TODO: Move into Account class.
public int vpoints;
public int accountId;
public int maplePoints;
public int aCash;
// TODO: Create classes for these arrays.
private int[] wishlist, teleportRocks;
private EnumMap<SavedLocationType, Integer> savedLocations;
public int reborns;
// TODO: Subcategory should be in the Job class.
private int subcategory;
// TODO: Extract HP AP Used, MP AP Used, remaining AP into another class.
private int mpApUsed, hpApUsed, remainingAp;
private int meso, exp, mapId, initialSpawnPoint, bookCover, dojoPoints, fallcounter, chair, itemEffect;
// TODO: Extract SP logic into class.
private int[] remainingSp = new int[10];
private long lastCombo, lastFameTime, keydown_skill;
private String name, chalktext, BlessOfFairy_Origin;
//
private Map<Integer, QuestStatus> quests;
private Map<Integer, String> questInfo;
private final Map<ISkill, SkillEntry> skills;
// TODO: Surely this shouldn't be here?
private transient final List<Door> doors;
private final List<Pet> pets;
// TODO: Extract SkillMacro list into a class.
private SkillMacroSet skillMacros;
// TODO: This stuff should be in GuildMember
private int guildId;
private MemberRank guildRank;
private GuildMember guildMember;
//
private BuddyList buddies;
private MonsterBook monsterBook;
private ActivePlayerStats stats;
private Storage storage;
private Mount mount;
// TODO: MessengerMember instead.
private Messenger messenger;
private int messengerPosition;
// TODO: Probably not the best place.
private PlayerShop playerShop;
// TODO: Remove Party uses from this class completely.
private PartyMember partyMember;
private Party party;
private KeyLayout keylayout;
private MultiInventory inventory;
private transient ChannelClient client;
private transient PlayerRandomStream randomStream;
private ChannelCharacter() {
this.setStance(0);
this.setPosition(new Point(0, 0));
for (int i = 0; i < this.remainingSp.length; i++) {
this.remainingSp[i] = 0;
}
this.lastCombo = 0;
this.mulung_energy = 0;
this.combo = 0;
this.keydown_skill = 0;
this.messengerPosition = 4;
this.canDoor = true;
this.berserk = false;
this.smega = true;
this.wishlist = new int[10];
this.teleportRocks = new int[10];
// 1 = NPC/Quest,
// 2 = Duey,
// 3 = Hired Merch store,
// 4 = Storage
this.conversationState = new AtomicInteger();
this.keylayout = new KeyLayout();
this.inventory = new MultiInventory();
this.stats = new ActivePlayerStats(this);
this.cheatTracker = new CheatTracker(this);
this.cooldowns = Maps.newLinkedHashMap();
this.effects = Maps.newEnumMap(BuffStat.class);
this.diseases = Maps.newEnumMap(Disease.class);
this.doors = Lists.newArrayList();
this.pets = Lists.newArrayList();
this.pendingCarnivalRequests = Lists.newLinkedList();
this.controlledMonsters = Sets.newLinkedHashSet();
this.visibleMapObjects = Sets.newLinkedHashSet();
this.quests = Maps.newLinkedHashMap();
this.questInfo = Maps.newLinkedHashMap();
this.summons = Maps.newLinkedHashMap();
this.skills = Maps.newLinkedHashMap();
// TODO: Get rid of arrays.
this.savedLocations = Maps.newEnumMap(SavedLocationType.class);
}
public static ChannelCharacter reconstructCharacter(final CharacterTransfer ct, final ChannelClient client) {
final ChannelCharacter ret = new ChannelCharacter();
ret.client = client;
ret.id = ct.CharacterId;
ret.name = ct.CharacterName;
ret.level = ct.Level;
ret.fame = ct.Fame;
ret.randomStream = new PlayerRandomStream();
ret.stats.setStr(ct.STR);
ret.stats.setDex(ct.DEX);
ret.stats.setInt(ct.INT);
ret.stats.setLuk(ct.LUK);
ret.stats.setMaxHp(ct.MaxHP);
ret.stats.setMaxMp(ct.MaxMP);
ret.stats.setHp(ct.HP);
ret.stats.setMp(ct.MP);
ret.exp = ct.Exp;
ret.hpApUsed = ct.hpApUsed;
ret.mpApUsed = ct.mpApUsed;
ret.remainingSp = ct.RemainingSP;
ret.remainingAp = ct.RemainingAP;
ret.meso = ct.Meso;
ret.gmLevel = ct.GmLevel;
ret.skinColorId = ct.SkinColorId;
ret.gender = ct.Gender;
ret.jobId = ct.JobId;
ret.hairId = ct.HairId;
ret.faceId = ct.FaceId;
ret.accountId = ct.AccountId;
ret.mapId = ct.MapId;
ret.initialSpawnPoint = ct.InitialSpawnPoint;
ret.worldId = ct.WorldId;
ret.bookCover = ct.MonsterBookCover;
ret.dojoPoints = ct.DojoPoints;
ret.dojoRecord = ct.DojoRecord;
ret.reborns = ct.RebornCount;
ret.guildId = ct.GuildId;
ret.guildRank = ct.GuildRank;
ret.subcategory = ct.Subcategory;
ret.ondmg = ct.ondmg;
ret.callgm = ct.callgm;
if (ret.guildId > 0) {
ret.guildMember = new GuildMember(ret);
}
ret.buddies = new BuddyList(ct.BuddyListCapacity);
final GameMapFactory mapFactory = ChannelServer.getMapFactory();
ret.map = mapFactory.getMap(ret.mapId);
if (ret.map == null) {
// char is on a map that doesn't exist warp it to henesys
ret.map = mapFactory.getMap(100000000);
} else {
if (ret.map.getForcedReturnId() != 999999999) {
ret.map = ret.map.getForcedReturnMap();
}
}
Portal portal = ret.map.getPortal(ret.initialSpawnPoint);
if (portal == null) {
portal = ret.map.getPortal(0); // char is on a spawnpoint that
// doesn't exist - select the first
// spawnpoint instead
ret.initialSpawnPoint = 0;
}
ret.setPosition(portal.getPosition());
final int partyId = ct.PartyId;
if (partyId >= 0) {
try {
final Party party = ChannelServer.getWorldInterface().getParty(partyId);
if (party != null && party.getMemberById(ret.id) != null) {
ret.party = party;
}
} catch (final RemoteException e) {
ChannelServer.pingWorld();
}
}
final int messengerid = ct.MessengerId;
final int position = ct.MessengerPosition;
if (messengerid > 0 && position < 4 && position > -1) {
try {
final WorldChannelInterface wci = ChannelServer.getWorldInterface();
final Messenger messenger = wci.getMessenger(messengerid);
if (messenger != null) {
ret.messenger = messenger;
ret.messengerPosition = position;
}
} catch (final RemoteException e) {
ChannelServer.pingWorld();
}
}
ret.quests = ct.Quests;
// TODO: transfer of active quest information
for (final Map.Entry<Integer, SkillEntry> qs : ct.Skills.entrySet()) {
ret.skills.put(SkillInfoProvider.getSkill(qs.getKey()), qs.getValue());
}
ret.monsterBook = ct.MonsterBook;
ret.inventory = ct.Inventories;
ret.BlessOfFairy_Origin = ct.BlessOfFairy;
ret.skillMacros = ct.SkillMacros;
ret.keylayout = ct.KeyLayout;
ret.questInfo = ct.QuestInfoEntries;
ret.savedLocations = ct.SavedLocations;
ret.wishlist = ct.Wishlist;
ret.teleportRocks = ct.TeleportRocks;
ret.buddies.loadFromTransfer(ct.BuddyListEntries);
ret.keydown_skill = 0; // Keydown skill can't be brought over
ret.lastFameTime = ct.LastFameTime;
ret.storage = ct.Storage;
client.setAccountName(ct.AccountName);
ret.aCash = ct.ACash;
ret.vpoints = ct.vpoints;
ret.maplePoints = ct.MaplePoints;
ret.mount = new Mount(ret, ct.MountItemId, 1004, ct.MountFatigue, ct.MountLevel, ct.MountExp);
ret.stats.recalcLocalStats();
ret.silentEnforceMaxHpMp();
return ret;
}
public static ChannelCharacter loadFromDb(final int characterId, final ChannelClient client) {
final ChannelCharacter ret = new ChannelCharacter();
ret.client = client;
ret.accountId = client.getAccountId();
ret.id = characterId;
final Connection con = Database.getConnection();
try {
ret.loadCharacterData(characterId, con);
ret.quests = loadQuestData(con, characterId);
ret.randomStream = new PlayerRandomStream();
ret.monsterBook = MonsterBook.loadFromDb(characterId);
ret.loadInventoryCapacity(characterId, con);
ret.loadInventoryItems(characterId, con);
ret.loadAccountData(con);
ret.loadQuestData(characterId, con);
ret.loadSkillInfo(characterId, con);
ret.loadBlessOfFairy(characterId, con);
ret.loadSkillMacros(characterId, con);
ret.keylayout = KeyLayout.loadFromDb(characterId);
ret.loadSavedLocations(characterId, con);
ret.lastFameTime = FameLog.getLastTimestamp(characterId);
ret.buddies.loadFromDb(characterId);
ret.storage = Storage.loadStorage(ret.accountId);
ret.loadWishlist(characterId, con);
ret.loadTeleportRockLocations(characterId, con);
ret.loadMountData(characterId, con);
} catch (final SQLException ex) {
System.out.println("Failed to load character: " + ex);
return null;
}
ret.stats.recalcLocalStats();
ret.silentEnforceMaxHpMp();
return ret;
}
private void loadMountData(final int characterId, final Connection con) throws SQLException {
try ( final PreparedStatement ps = getSelectMountData(characterId, con);
final ResultSet rs = ps.executeQuery()) {
if (!rs.next()) {
throw new RuntimeException("No mount data found on SQL column");
}
final Item mount = this.getEquippedItemsInventory().getItem((byte) -22);
this.mount = new Mount(this, mount != null ? mount.getItemId() : 0, 1004, rs.getInt("Fatigue"), rs.getInt("Level"), rs.getInt("Exp"));
}
}
private void loadTeleportRockLocations(final int characterId, final Connection con) throws SQLException {
try ( final PreparedStatement ps = getSelectTeleportRockLocations(characterId, con);
final ResultSet rs = ps.executeQuery()) {
int r = 0;
while (rs.next()) {
this.teleportRocks[r] = rs.getInt("mapid");
r++;
}
while (r < 10) {
this.teleportRocks[r] = 999999999;
r++;
}
}
}
private void loadWishlist(final int characterId, final Connection con) throws SQLException {
try ( final PreparedStatement ps = getSelectWishlistItems(characterId, con);
final ResultSet rs = ps.executeQuery()) {
int i = 0;
while (rs.next()) {
this.wishlist[i] = rs.getInt("sn");
i++;
}
while (i < 10) {
this.wishlist[i] = 0;
i++;
}
}
}
private void loadSavedLocations(final int characterId, final Connection con) throws SQLException {
try ( final PreparedStatement ps = getSelectSavedLocations(characterId, con);
final ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
final int locationType = rs.getInt("locationtype");
final int savedMapId = rs.getInt("map");
this.savedLocations.put(SavedLocationType.fromNumber(locationType), savedMapId);
}
}
}
private void loadSkillMacros(final int characterId, final Connection con) throws SQLException {
try ( final PreparedStatement ps = getSelectSkillMacros(characterId, con);
final ResultSet rs = ps.executeQuery()) {
this.skillMacros = new SkillMacroSet(rs);
}
}
private void loadBlessOfFairy(final int characterId, final Connection con) throws SQLException {
try ( final PreparedStatement ps = getSelectCharactersByLevelDesc(this.accountId, con);
final ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
if (rs.getInt("id") != characterId) { // Not this character
byte maxlevel = (byte) (rs.getInt("level") / 10);
// if (!GameConstants.isKOC(ret.job)) {
if (maxlevel > 20) {
maxlevel = 20;
}
// }
this.BlessOfFairy_Origin = rs.getString("name");
final SkillEntry skillEntry = new SkillEntry(maxlevel, (byte) 0);
final int skillId = Skills.getBlessOfFairyForJob(this.jobId);
this.skills.put(SkillInfoProvider.getSkill(skillId), skillEntry);
break;
}
}
}
}
private void loadSkillInfo(final int characterId, final Connection con) throws SQLException {
try ( final PreparedStatement ps = getSelectSkillInfo(characterId, con);
final ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
final int skillId = rs.getInt("skillid");
if (GameConstants.isApplicableSkill(skillId)) {
final byte masterLevel = rs.getByte("masterlevel");
final byte skillLevel = rs.getByte("skilllevel");
final SkillEntry skillEntry = new SkillEntry(skillLevel, masterLevel);
this.skills.put(SkillInfoProvider.getSkill(skillId), skillEntry);
}
}
}
}
private void loadQuestData(final int characterId, final Connection con) throws SQLException {
try ( final PreparedStatement ps = getSelectQuestInfo(characterId, con);
final ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
final int questId = rs.getInt("quest");
final String customData = rs.getString("customData");
this.questInfo.put(questId, customData);
}
}
}
private void loadAccountData(final Connection con) throws SQLException {
try ( final PreparedStatement ps = getSelectAccount(this.accountId, con);
final ResultSet rs = ps.executeQuery();) {
if (rs.next()) {
this.getClient().setAccountName(rs.getString("name"));
this.aCash = rs.getInt("ACash");
this.vpoints = rs.getInt("vpoints");
this.maplePoints = rs.getInt("mPoints");
}
}
}
private void loadInventoryItems(final int characterId, final Connection con) throws SQLException {
try ( PreparedStatement ps = getSelectEquips(characterId, con);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
final byte inventoryType = rs.getByte("inventorytype");
final InventoryType type = InventoryType.fromNumber(inventoryType);
final int itemId = rs.getInt("itemid");
final byte position = rs.getByte("position");
final short quantity = rs.getShort("quantity");
final byte flag = rs.getByte("flag");
final long expiration = rs.getLong("expiredate");
final String owner = rs.getString("owner");
final String gmLog = rs.getString("GM_Log");
if (type.equals(InventoryType.EQUIP) || type.equals(InventoryType.EQUIPPED)) {
final Equip equip = new Equip(itemId, position, rs.getInt("ringid"), flag);
equip.setOwner(owner);
equip.setQuantity(quantity);
equip.setExpiration(expiration);
equip.setGMLog(gmLog);
equip.setAcc(rs.getShort("acc"));
equip.setAvoid(rs.getShort("avoid"));
equip.setDex(rs.getShort("dex"));
equip.setHands(rs.getShort("hands"));
equip.setHp(rs.getShort("hp"));
equip.setInt(rs.getShort("int"));
equip.setJump(rs.getShort("jump"));
equip.setLuk(rs.getShort("luk"));
equip.setMatk(rs.getShort("matk"));
equip.setMdef(rs.getShort("mdef"));
equip.setMp(rs.getShort("mp"));
equip.setSpeed(rs.getShort("speed"));
equip.setStr(rs.getShort("str"));
equip.setWatk(rs.getShort("watk"));
equip.setWdef(rs.getShort("wdef"));
equip.setItemLevel(rs.getByte("itemLevel"));
equip.setItemEXP(rs.getShort("itemEXP"));
equip.setViciousHammer(rs.getByte("ViciousHammer"));
equip.setUpgradeSlots(rs.getByte("upgradeslots"));
equip.setLevel(rs.getByte("level"));
this.inventory.get(type).addFromDb(equip);
} else {
final Item item = new Item(itemId, position, quantity, flag);
item.setOwner(owner);
item.setExpiration(expiration);
item.setGMLog(gmLog);
this.inventory.get(type).addFromDb(item);
if (rs.getInt("petid") > -1) {
final int petId = rs.getInt("petid");
final Pet pet = Pet.loadFromDb(item.getItemId(), petId, item.getPosition());
this.pets.add(pet);
item.setPet(pet);
}
}
}
}
}
private void loadInventoryCapacity(final int characterId, final Connection con) throws SQLException {
try ( PreparedStatement ps = getSelectInventorySlot(characterId, con);
ResultSet rs = ps.executeQuery()) {
if (!rs.next()) {
throw new RuntimeException("No Inventory slot column found in SQL. [inventoryslot]");
}
final byte equipSlots = rs.getByte("equip");
this.getEquipInventory().setSlotLimit(equipSlots);
final byte useSlots = rs.getByte("use");
this.getUseInventory().setSlotLimit(useSlots);
final byte setupSlots = rs.getByte("setup");
this.getSetupInventory().setSlotLimit(setupSlots);
final byte etcSlots = rs.getByte("etc");
this.getEtcInventory().setSlotLimit(etcSlots);
final byte cashSlots = rs.getByte("cash");
this.getCashInventory().setSlotLimit(cashSlots);
}
}
private static PreparedStatement getSelectMountData(final int characterId, final Connection con) throws SQLException {
final PreparedStatement ps = con.prepareStatement("SELECT * FROM mountdata WHERE characterid = ?");
ps.setInt(1, characterId);
return ps;
}
private static PreparedStatement getSelectTeleportRockLocations(final int characterId, final Connection con) throws SQLException {
final PreparedStatement ps = con.prepareStatement("SELECT mapid FROM trocklocations WHERE characterid = ?");
ps.setInt(1, characterId);
return ps;
}
private static PreparedStatement getSelectWishlistItems(final int characterId, final Connection con) throws SQLException {
final PreparedStatement ps = con.prepareStatement("SELECT sn FROM wishlist WHERE characterid = ?");
ps.setInt(1, characterId);
return ps;
}
private static PreparedStatement getSelectSavedLocations(final int characterId, final Connection con) throws SQLException {
final PreparedStatement ps = con.prepareStatement("SELECT `locationtype`,`map` FROM savedlocations WHERE characterid = ?");
ps.setInt(1, characterId);
return ps;
}
private static PreparedStatement getSelectSkillMacros(final int characterId, final Connection con) throws SQLException {
final PreparedStatement ps = con.prepareStatement("SELECT * FROM skillmacros WHERE characterid = ?");
ps.setInt(1, characterId);
return ps;
}
private static PreparedStatement getSelectCharactersByLevelDesc(final int accountId, final Connection con) throws SQLException {
final PreparedStatement ps = con.prepareStatement("SELECT * FROM characters WHERE accountid = ? ORDER BY level DESC");
ps.setInt(1, accountId);
return ps;
}
private static PreparedStatement getSelectSkillInfo(final int characterId, final Connection con) throws SQLException {
final PreparedStatement ps = con.prepareStatement("SELECT skillid, skilllevel, masterlevel FROM skills WHERE characterid = ?");
ps.setInt(1, characterId);
return ps;
}
private static PreparedStatement getSelectQuestInfo(final int characterId, final Connection con) throws SQLException {
final PreparedStatement ps = con.prepareStatement("SELECT * FROM questinfo WHERE characterid = ?");
ps.setInt(1, characterId);
return ps;
}
private static PreparedStatement getSelectAccount(final int accountId, final Connection con) throws SQLException {
final PreparedStatement ps = con.prepareStatement("SELECT * FROM accounts WHERE id = ?");
ps.setInt(1, accountId);
return ps;
}
private static PreparedStatement getSelectEquips(final int characterId, final Connection con) throws SQLException {
final PreparedStatement ps = con
.prepareStatement("SELECT * FROM inventoryitems LEFT JOIN inventoryequipment USING (inventoryitemid) WHERE characterid = ?");
ps.setInt(1, characterId);
return ps;
}
private static PreparedStatement getSelectInventorySlot(final int characterId, final Connection con) throws SQLException {
final PreparedStatement ps = con.prepareStatement("SELECT * FROM inventoryslot where characterid = ?");
ps.setInt(1, characterId);
return ps;
}
private static PreparedStatement getSelectCharacter(final int characterId, final Connection con) throws SQLException {
final PreparedStatement ps = con.prepareStatement("SELECT * FROM characters WHERE id = ?");
ps.setInt(1, characterId);
return ps;
}
private void loadCharacterData(final int characterId, final Connection con) throws SQLException {
try ( PreparedStatement ps = getSelectCharacter(characterId, con);
ResultSet rs = ps.executeQuery()) {
if (!rs.next()) {
throw new RuntimeException("Loading the Char Failed (char not found)");
}
this.name = rs.getString("name");
this.level = rs.getShort("level");
this.fame = rs.getInt("fame");
this.stats = new ActivePlayerStats(this);
this.stats.setStr(rs.getInt("str"));
this.stats.setDex(rs.getInt("dex"));
this.stats.setInt(rs.getInt("int"));
this.stats.setLuk(rs.getInt("luk"));
this.stats.setMaxHp(rs.getInt("maxhp"));
this.stats.setMaxMp(rs.getInt("maxmp"));
this.stats.setHp(rs.getInt("hp"));
this.stats.setMp(rs.getInt("mp"));
this.exp = rs.getInt("exp");
this.hpApUsed = rs.getInt("hpApUsed");
this.mpApUsed = rs.getInt("mpApUsed");
final String[] sp = rs.getString("sp").split(",");
for (int i = 0; i < this.remainingSp.length; i++) {
this.remainingSp[i] = Integer.parseInt(sp[i]);
}
this.remainingAp = rs.getInt("ap");
this.subcategory = rs.getInt("subcategory");
this.meso = rs.getInt("meso");
this.gmLevel = rs.getByte("gm");
this.skinColorId = rs.getInt("skincolor");
this.gender = Gender.fromNumber(rs.getByte("gender"));
this.jobId = rs.getInt("job");
this.hairId = rs.getInt("hair");
this.faceId = rs.getInt("face");
this.accountId = rs.getInt("accountid");
this.mapId = rs.getInt("map");
this.initialSpawnPoint = rs.getInt("spawnpoint");
this.worldId = rs.getInt("world");
this.guildId = rs.getInt("guildid");
this.guildRank = MemberRank.fromNumber(rs.getInt("guildrank"));
this.reborns = rs.getInt("reborns");
if (this.guildId > 0) {
this.guildMember = new GuildMember(this);
}
this.buddies = new BuddyList(rs.getInt("buddyCapacity"));
final GameMapFactory mapFactory = ChannelServer.getMapFactory();
this.map = mapFactory.getMap(this.mapId);
if (this.map == null) {
// char is on a map that doesn't exist warp it to henesys
this.map = mapFactory.getMap(100000000);
}
Portal portal = this.map.getPortal(this.initialSpawnPoint);
if (portal == null) {
// char is on a spawnpoint that doesn't exist - select the first
// spawnpoint instead
portal = this.map.getPortal(0);
this.initialSpawnPoint = 0;
}
this.setPosition(portal.getPosition());
final int partyid = rs.getInt("party");
if (partyid >= 0) {
try {
final Party party = ChannelServer.getWorldInterface().getParty(partyid);
if (party != null && party.getMemberById(this.id) != null) {
this.party = party;
}
} catch (final RemoteException e) {
ChannelServer.pingWorld();
}
}
final int messengerId = rs.getInt("messengerid");
final int messengerPosition = rs.getInt("messengerposition");
if (messengerId > 0 && messengerPosition < 4 && messengerPosition > -1) {
try {
final WorldChannelInterface wci = ChannelServer.getWorldInterface();
final Messenger messenger = wci.getMessenger(messengerId);
if (messenger != null) {
this.messenger = messenger;
this.messengerPosition = messengerPosition;
}
} catch (final RemoteException e) {
ChannelServer.pingWorld();
}
}
this.bookCover = rs.getInt("monsterbookcover");
this.dojoPoints = rs.getInt("dojo_pts");
this.dojoRecord = rs.getByte("dojoRecord");
}
}
private static Map<Integer, QuestStatus> loadQuestData(final Connection c, final int characterId) throws SQLException {
try ( final PreparedStatement statusQuery = getSelectQuestStatus(c, characterId);
final ResultSet statusResultSet = statusQuery.executeQuery()) {
return loadQuestMobStatus(c, statusResultSet);
}
}
private static Map<Integer, QuestStatus> loadQuestMobStatus(final Connection c, final ResultSet statusResultSet) throws SQLException {
final Map<Integer, QuestStatus> quests = Maps.newHashMap();
try (final PreparedStatement mobsQuery = getSelectQuestMobStatus(c)) {
while (statusResultSet.next()) {
final QuestStatus status = new QuestStatus(statusResultSet);
final int questId = status.getQuestId();
quests.put(questId, status);
final int questStatusId = statusResultSet.getInt("queststatusid");
mobsQuery.setInt(1, questStatusId);
try (ResultSet mobResultSet = mobsQuery.executeQuery()) {
while (mobResultSet.next()) {
final int mobId = mobResultSet.getInt("mob");
final int killCount = mobResultSet.getInt("count");
status.setMobKills(mobId, killCount);
}
}
}
return quests;
}
}
private static PreparedStatement getSelectQuestMobStatus(final Connection c) throws SQLException {
return c.prepareStatement("SELECT * FROM queststatusmobs WHERE queststatusid = ?");
}
private static PreparedStatement getSelectQuestStatus(final Connection c, final int characterId) throws SQLException {
final PreparedStatement statusQuery = c.prepareStatement("SELECT * FROM queststatus WHERE characterid = ?");
statusQuery.setInt(1, characterId);
return statusQuery;
}
public void saveToDb(final boolean forcedDc) {
final Connection con = Database.getConnection();
try {
con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
con.setAutoCommit(false);
this.saveCharacterData(con);
for (final Pet pet : this.pets) {
if (pet.isSummoned()) {
// Only save those summoned :P
pet.saveToDb();
}
}
this.deleteByCharacterId(con, "DELETE FROM skillmacros WHERE characterid = ?");
this.getSkillMacros().saveToDb(this.id, con);
this.deleteByCharacterId(con, "DELETE FROM inventoryslot WHERE characterid = ?");
this.saveInventorySlots(con);
this.deleteByCharacterId(con, "DELETE FROM inventoryitems WHERE characterid = ?");
this.saveInventoryItems(con);
this.deleteByCharacterId(con, "DELETE FROM questinfo WHERE characterid = ?");
this.saveQuestInfo(con);
this.deleteByCharacterId(con, "DELETE FROM queststatus WHERE characterid = ?");
this.saveQuestStatus(con);
this.deleteByCharacterId(con, "DELETE FROM skills WHERE characterid = ?");
this.saveSkillInfo(con);
if (forcedDc && this.getAllCooldowns().size() > 0) {
this.saveSkillCooldowns(con);
}
this.deleteByCharacterId(con, "DELETE FROM savedlocations WHERE characterid = ?");
this.saveSavedLocations(con);
this.deleteByCharacterId(con, "DELETE FROM buddies WHERE characterid = ? AND pending = 0");
this.saveBuddyEntries(con);
this.saveAccountInfo(con);
if (this.storage != null) {
this.storage.saveToDB();
}
this.keylayout.saveKeys(this.id);
this.mount.saveMount(this.id);
this.monsterBook.saveCards(this.id);
this.deleteByCharacterId(con, "DELETE FROM wishlist WHERE characterid = ?");
this.saveWishlist(con);
this.deleteByCharacterId(con, "DELETE FROM trocklocations WHERE characterid = ?");
this.saveTeleportRockLocations(con);
con.commit();
} catch (SQLException | DatabaseException e) {
final String failMessage = ChannelClient.getLogMessage(this, "[charsave] Error saving character data");
System.err.println(failMessage + e);
try {
con.rollback();
} catch (final SQLException ex) {
final String completelyFailMessage = ChannelClient.getLogMessage(this, "[charsave] Error Rolling Back");
System.err.println(completelyFailMessage + e);
}
} finally {
try {
con.setAutoCommit(true);
con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
} catch (final SQLException e) {
System.err.println(ChannelClient.getLogMessage(this, "[charsave] Error going back to autocommit mode") + e);
}
}
}
private void saveCharacterData(final Connection con) throws SQLException {
try (final PreparedStatement ps = this.getUpdateCharacterData(con)) {
final int affectedRows = ps.executeUpdate();
if (affectedRows < 1) {
throw new DatabaseException("Character not in database (" + this.id + ")");
}
}
}
private PreparedStatement getUpdateCharacterData(final Connection con) throws SQLException {
final String sql = "UPDATE characters SET level = ?, fame = ?, str = ?, dex = ?, luk = ?, `int` = ?, exp = ?, hp = ?, mp = ?, maxhp = ?, maxmp = ?, sp = ?, ap = ?, gm = ?, skincolor = ?, gender = ?, job = ?, hair = ?, face = ?, map = ?, meso = ?, hpApUsed = ?, mpApUsed = ?, spawnpoint = ?, party = ?, buddyCapacity = ?, messengerid = ?, messengerposition = ?, monsterbookcover = ?, dojo_pts = ?, dojoRecord = ?, reborns = ?, subcategory = ? WHERE id = ?";
final PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, this.level);
ps.setInt(2, this.fame);
ps.setInt(3, this.stats.getStr());
ps.setInt(4, this.stats.getDex());
ps.setInt(5, this.stats.getLuk());
ps.setInt(6, this.stats.getInt());
ps.setInt(7, this.exp);
final int hp = this.stats.getHp();
final int hpToSave = hp < 1 ? 50 : hp;
ps.setInt(8, hpToSave);
ps.setInt(9, this.stats.getMp());
ps.setInt(10, this.stats.getMaxHp());
ps.setInt(11, this.stats.getMaxMp());
final String joinedSp = Joiner.on(',').join(Lists.newArrayList(this.remainingSp));
ps.setString(12, joinedSp);
ps.setInt(13, this.remainingAp);
ps.setInt(14, this.gmLevel);
ps.setInt(15, this.skinColorId);
ps.setInt(16, this.gender.asNumber());
ps.setInt(17, this.jobId);
ps.setInt(18, this.hairId);
ps.setInt(19, this.faceId);
if (this.map.getForcedReturnId() != 999999999) {
ps.setInt(20, this.map.getForcedReturnId());
} else {
ps.setInt(20, hp < 1 ? this.map.getReturnMapId() : this.map.getId());
}
ps.setInt(21, this.meso);
ps.setInt(22, this.hpApUsed);
ps.setInt(23, this.mpApUsed);
final Portal closest = this.map.findClosestSpawnpoint(this.getPosition());
ps.setInt(24, closest != null ? closest.getId() : 0);
ps.setInt(25, this.party != null ? this.party.getId() : -1);
ps.setInt(26, this.buddies.getCapacity());
if (this.messenger != null) {
ps.setInt(27, this.messenger.getId());
ps.setInt(28, this.messengerPosition);
} else {
ps.setInt(27, 0);
ps.setInt(28, 4);
}
ps.setInt(29, this.bookCover);
ps.setInt(30, this.dojoPoints);
ps.setInt(31, this.dojoRecord);
ps.setInt(32, this.getReborns());
ps.setInt(33, this.subcategory);
ps.setInt(34, this.id);
return ps;
}
private void saveTeleportRockLocations(final Connection con) throws SQLException {
final String sql = "INSERT INTO trocklocations(characterid, mapid) VALUES(?, ?) ";
try (final PreparedStatement ps = con.prepareStatement(sql)) {
for (int i = 0; i < this.getRockSize(); i++) {
if (this.teleportRocks[i] != 999999999) {
ps.setInt(1, this.getId());
ps.setInt(2, this.teleportRocks[i]);
ps.execute();
}
}
}
}
private void saveWishlist(final Connection con) throws SQLException {
final String sql = "INSERT INTO wishlist(characterid, sn) VALUES(?, ?) ";
try (final PreparedStatement ps = con.prepareStatement(sql)) {
for (int i = 0; i < this.getWishlistSize(); i++) {
ps.setInt(1, this.getId());
ps.setInt(2, this.wishlist[i]);
ps.execute();
}
}
}
private void saveAccountInfo(final Connection con) throws SQLException {
try (final PreparedStatement ps = this.getUpdateAccount(con)) {
ps.execute();
}
}
private PreparedStatement getUpdateAccount(final Connection connection) throws SQLException {
final String sql = "UPDATE accounts SET `ACash` = ?, `mPoints` = ?, `vpoints` = ? WHERE id = ?";
final PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1, this.aCash);
ps.setInt(2, this.maplePoints);
ps.setInt(3, this.vpoints);
ps.setInt(4, this.client.getAccountId());
return ps;
}
private void saveBuddyEntries(final Connection connection) throws SQLException {
try (final PreparedStatement ps = this.getInsertBuddyEntry(connection)) {
for (final BuddyListEntry entry : this.buddies.getBuddies()) {
if (entry.isVisible()) {
ps.setInt(2, entry.getCharacterId());
ps.execute();
}
}
}
}
private PreparedStatement getInsertBuddyEntry(final Connection connection) throws SQLException {
final String sql = "INSERT INTO buddies (characterid, `buddyid`, `pending`) VALUES (?, ?, 0)";
final PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1, this.id);
return ps;
}
private void saveSavedLocations(final Connection connection) throws SQLException {
try (final PreparedStatement ps = this.getInsertSavedLocation(connection)) {
for (final Map.Entry<SavedLocationType, Integer> entry : this.savedLocations.entrySet()) {
ps.setInt(2, entry.getKey().asNumber());
ps.setInt(3, entry.getValue());
ps.execute();
}
}
}
private PreparedStatement getInsertSavedLocation(final Connection connection) throws SQLException {
final String sql = "INSERT INTO savedlocations (characterid, `locationtype`, `map`) VALUES (?, ?, ?)";
final PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1, this.id);
return ps;
}
private void saveSkillCooldowns(final Connection connection) throws SQLException {
try (final PreparedStatement ps = this.getInsertSkillCooldown(connection)) {
for (final PlayerCooldownValueHolder cooling : this.getAllCooldowns()) {
ps.setInt(2, cooling.SkillId);
ps.setLong(3, cooling.StartTime);
ps.setLong(4, cooling.Length);
ps.execute();
}
}
}
private PreparedStatement getInsertSkillCooldown(final Connection connection) throws SQLException {
final String sql = "INSERT INTO skills_cooldowns (charid, SkillID, StartTime, length) VALUES (?, ?, ?, ?)";
final PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1, this.id);
return ps;
}
private void saveSkillInfo(final Connection connection) throws SQLException {
try (final PreparedStatement ps = this.getInsertSkillInfo(connection)) {
for (final Entry<ISkill, SkillEntry> skill : this.skills.entrySet()) {
final int skillId = skill.getKey().getId();
ps.setInt(2, skillId);
final SkillEntry entry = skill.getValue();
ps.setInt(3, entry.getCurrentLevel());
ps.setInt(4, entry.getMasterLevel());
ps.execute();
}
}
}
private PreparedStatement getInsertSkillInfo(final Connection connection) throws SQLException {
final String sql = "INSERT INTO skills (characterid, skillid, skilllevel, masterlevel) VALUES (?, ?, ?, ?)";
final PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1, this.id);
return ps;
}
private void saveQuestStatus(final Connection connection) throws SQLException {
try ( final PreparedStatement questStatement = this.getInsertQuestStatus(connection);
final PreparedStatement mobStatement = this.getInsertQuestMobStatus(connection)) {
for (final QuestStatus quest : this.quests.values()) {
this.setQuestStatusData(questStatement, quest);
questStatement.executeUpdate();
if (quest.hasMobKills()) {
try (final ResultSet rs = questStatement.getGeneratedKeys()) {
if (!rs.next()) {
continue;
}
for (final int mob : quest.getMobKills().keySet()) {
mobStatement.setInt(1, rs.getInt(1));
mobStatement.setInt(2, mob);
mobStatement.setInt(3, quest.getMobKills(mob));
mobStatement.executeUpdate();
}
}
}
}
}
}
private void setQuestStatusData(final PreparedStatement statement, final QuestStatus quest) throws SQLException {
statement.setInt(2, quest.getQuestId());
statement.setInt(3, quest.getState());
statement.setInt(4, (int) (quest.getCompletionTime() / 1000));
statement.setInt(5, quest.getForfeited());
statement.setString(6, quest.getCustomData());
}
private PreparedStatement getInsertQuestMobStatus(final Connection connection) throws SQLException {
final String sql = "INSERT INTO queststatusmobs VALUES (DEFAULT, ?, ?, ?)";
return connection.prepareStatement(sql);
}
private PreparedStatement getInsertQuestStatus(final Connection connection) throws SQLException {
final String sql = "INSERT INTO queststatus (`queststatusid`, `characterid`, `quest`, `status`, `time`, `forfeited`, `customData`) VALUES (DEFAULT, ?, ?, ?, ?, ?, ?)";
final PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, this.id);
return ps;
}
private void saveQuestInfo(final Connection connection) throws SQLException {
try (final PreparedStatement ps = this.getInsertQuestInfo(connection)) {
for (final Entry<Integer, String> q : this.questInfo.entrySet()) {
ps.setInt(2, q.getKey());
ps.setString(3, q.getValue());
ps.execute();
}
}
}
private PreparedStatement getInsertQuestInfo(final Connection connection) throws SQLException {
final String sql = "INSERT INTO questinfo (`characterid`, `quest`, `data`) VALUES (?, ?, ?)";
final PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1, this.id);
return ps;
}
private void saveInventoryItems(final Connection connection) throws SQLException {
try (final PreparedStatement items = this.getInsertInventoryItem(connection)) {
this.saveItemInventory(items, this.getUseInventory());
this.saveItemInventory(items, this.getSetupInventory());
this.saveItemInventory(items, this.getEtcInventory());
this.saveItemInventory(items, this.getCashInventory());
try (final PreparedStatement equips = this.getInsertInventoryEquip(connection)) {
this.saveEquipInventory(items, equips, this.getEquipInventory());
this.saveEquipInventory(items, equips, this.getEquippedItemsInventory());
}
}
}
private void saveItemInventory(final PreparedStatement statement, final Inventory inventory) throws SQLException {
statement.setInt(2, inventory.getType().asNumber());
for (final Item item : inventory) {
this.setItemData(statement, item);
statement.executeUpdate();
}
}
private void setItemData(final PreparedStatement statement, final Item item) throws SQLException {
statement.setInt(3, item.getItemId());
statement.setInt(4, item.getPosition());
statement.setInt(5, item.getQuantity());
statement.setString(6, item.getOwner());
statement.setString(7, item.getGMLog());
statement.setInt(8, item.getPet() != null ? item.getPet().getUniqueId() : -1);
statement.setLong(9, item.getExpiration());
statement.setByte(10, item.getFlag());
}
private void saveEquipInventory(final PreparedStatement itemStatement, final PreparedStatement equipStatement, final Inventory inventory)
throws SQLException {
itemStatement.setInt(3, inventory.getType().asNumber());
for (final Item item : inventory) {
this.setItemData(itemStatement, item);
itemStatement.executeUpdate();
final ResultSet rs = itemStatement.getGeneratedKeys();
int itemid;
if (rs.next()) {
itemid = rs.getInt(1);
} else {
throw new DatabaseException("Inserting char failed.");
}
if (inventory.getType().equals(InventoryType.EQUIP) || inventory.getType().equals(InventoryType.EQUIPPED)) {
equipStatement.setInt(1, itemid);
this.setEquipData(equipStatement, (Equip) item);
equipStatement.executeUpdate();
}
}
}
private void setEquipData(final PreparedStatement statement, final Equip equip) throws SQLException {
statement.setInt(2, equip.getUpgradeSlots());
statement.setInt(3, equip.getLevel());
statement.setInt(4, equip.getStr());
statement.setInt(5, equip.getDex());
statement.setInt(6, equip.getInt());
statement.setInt(7, equip.getLuk());
statement.setInt(8, equip.getHp());
statement.setInt(9, equip.getMp());
statement.setInt(10, equip.getWatk());
statement.setInt(11, equip.getMatk());
statement.setInt(12, equip.getWdef());
statement.setInt(13, equip.getMdef());
statement.setInt(14, equip.getAcc());
statement.setInt(15, equip.getAvoid());
statement.setInt(16, equip.getHands());
statement.setInt(17, equip.getSpeed());
statement.setInt(18, equip.getJump());
statement.setInt(19, equip.getRingId());
statement.setInt(20, equip.getViciousHammer());
statement.setInt(21, equip.getItemLevel());
statement.setInt(22, equip.getItemEXP());
}
private PreparedStatement getInsertInventoryEquip(final Connection connection) throws SQLException {
final String sql = "INSERT INTO inventoryequipment VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
return connection.prepareStatement(sql);
}
private PreparedStatement getInsertInventoryItem(final Connection connection) throws SQLException {
final String sql = "INSERT INTO inventoryitems (characterid, inventorytype, itemid, position, quantity, owner, GM_Log, petid, expiredate, flag) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
final PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, this.id);
return ps;
}
private void saveInventorySlots(final Connection connection) throws SQLException {
try (PreparedStatement ps = this.getInsertInventorySlots(connection)) {
ps.execute();
}
}
private PreparedStatement getInsertInventorySlots(final Connection connection) throws SQLException {
final String sql = "INSERT INTO inventoryslot (characterid, `equip`, `use`, `setup`, `etc`, `cash`) VALUES (?, ?, ?, ?, ?, ?)";
final PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1, this.id);
ps.setInt(2, this.getEquipInventory().getSlotLimit());
ps.setInt(3, this.getUseInventory().getSlotLimit());
ps.setInt(4, this.getSetupInventory().getSlotLimit());
ps.setInt(5, this.getEtcInventory().getSlotLimit());
ps.setInt(6, this.getCashInventory().getSlotLimit());
return ps;
}
private void deleteByCharacterId(final Connection connection, final String sql) throws SQLException {
try (PreparedStatement ps = connection.prepareStatement(sql)) {
ps.setInt(1, this.id);
ps.executeUpdate();
}
}
@Override
public final ActivePlayerStats getStats() {
return this.stats;
}
public final PlayerRandomStream getRandomStream() {
return this.randomStream;
}
public final void writeQuestInfoPacket(final PacketBuilder builder) {
builder.writeAsShort(this.questInfo.size());
for (final Entry<Integer, String> q : this.questInfo.entrySet()) {
builder.writeAsShort(q.getKey());
builder.writeLengthPrefixedString(q.getValue() == null ? "" : q.getValue());
}
builder.writeInt(0); // PQ rank and stuff
}
public final void updateInfoQuest(final int questid, final String data) {
this.questInfo.put(questid, data);
this.client.write(ChannelPackets.updateInfoQuest(questid, data));
}
public final String getInfoQuest(final int questid) {
if (this.questInfo.containsKey(questid)) {
return this.questInfo.get(questid);
}
return "";
}
public final int getNumQuest() {
int i = 0;
for (final QuestStatus q : this.quests.values()) {
if (q.getState() == 2) {
i++;
}
}
return i;
}
public final byte getQuestCompletionStatus(final int questId) {
for (final QuestStatus q : this.quests.values()) {
if (q.getQuestId() == questId) {
return q.getState();
}
}
return 0;
}
public final QuestStatus getQuestStatus(final int questId) {
QuestStatus status = this.quests.get(questId);
if (status == null) {
status = new QuestStatus(questId, (byte) 0);
this.quests.put(questId, status);
}
return status;
}
public final QuestStatus getAddQuestStatus(final int questId) {
if (!this.quests.containsKey(questId)) {
final QuestStatus status = new QuestStatus(questId, (byte) 0);
this.quests.put(questId, status);
return status;
}
return this.quests.get(questId);
}
public final void updateQuest(final int questId) {
final QuestStatus status = this.getQuestStatus(questId);
switch (status.getState()) {
case 0:
this.client.write(ChannelPackets.forfeitQuest(this, questId));
break;
case 1:
this.client.write(ChannelPackets.startQuest(this, questId, status.getCustomData()));
this.client.write(ChannelPackets.updateQuestInfo(this, questId, status.getNpc(), (byte) 8));
break;
case 2:
this.client.write(ChannelPackets.completeQuest(questId));
break;
}
}
public final Map<Integer, String> getQuestInfoMap() {
return this.questInfo;
}
public final Map<Integer, QuestStatus> getQuestStatusMap() {
return this.quests;
}
public boolean isActiveBuffedValue(final int skillid) {
final LinkedList<BuffStatValueHolder> allBuffs = Lists.newLinkedList(this.effects.values());
for (final BuffStatValueHolder value : allBuffs) {
if (value.effect.isSkill() && value.effect.getSourceId() == skillid) {
return true;
}
}
return false;
}
public Integer getBuffedValue(final BuffStat effect) {
final BuffStatValueHolder value = this.effects.get(effect);
return value == null ? null : Integer.valueOf(value.value);
}
public final Integer getBuffedSkill_X(final BuffStat effect) {
final BuffStatValueHolder value = this.effects.get(effect);
if (value == null) {
return null;
}
return value.effect.getX();
}
public final Integer getBuffedSkill_Y(final BuffStat effect) {
final BuffStatValueHolder value = this.effects.get(effect);
if (value == null) {
return null;
}
return value.effect.getY();
}
public final StatEffect getBuffedSkillEffect(final BuffStat effect) {
final BuffStatValueHolder value = this.effects.get(effect);
if (value == null) {
return null;
}
return value.effect;
}
public boolean isBuffFrom(final BuffStat stat, final ISkill skill) {
final BuffStatValueHolder value = this.effects.get(stat);
if (value == null) {
return false;
}
return value.effect.isSkill() && value.effect.getSourceId() == skill.getId();
}
public int getBuffSource(final BuffStat stat) {
final BuffStatValueHolder value = this.effects.get(stat);
return value == null ? -1 : value.effect.getSourceId();
}
public int getItemQuantity(final int itemId, final boolean checkEquipped) {
int count = this.inventory.get(GameConstants.getInventoryType(itemId)).countById(itemId);
if (checkEquipped) {
count += this.inventory.get(InventoryType.EQUIPPED).countById(itemId);
}
return count;
}
public int getReborns() {
return this.reborns;
}
public int getVPoints() {
return this.vpoints;
}
public int getSnipeDamage() {
return Math.min(damageCap, 500000 * this.getReborns() + 500000);
}
public int getMaxStats() {
return this.getJobId() > 999 && this.getJobId() < 2000 ? 15000 : 32000;
}
public int getNX() {
return this.aCash;
}
public void gainVPoints(final int gainedpoints) {
this.vpoints += gainedpoints;
}
@Override
public int getWorldId() {
return this.worldId;
}
public void setBuffedValue(final BuffStat effect, final int statValue) {
final BuffStatValueHolder value = this.effects.get(effect);
if (value == null) {
return;
}
value.value = statValue;
}
public Long getBuffedStartTime(final BuffStat effect) {
final BuffStatValueHolder value = this.effects.get(effect);
return value == null ? null : Long.valueOf(value.startTime);
}
public StatEffect getStatForBuff(final BuffStat effect) {
final BuffStatValueHolder value = this.effects.get(effect);
return value == null ? null : value.effect;
}
private void prepareDragonBlood(final StatEffect bloodEffect) {
if (this.dragonBloodSchedule != null) {
this.dragonBloodSchedule.cancel(false);
}
this.dragonBloodSchedule = TimerManager.getInstance().register(new DragonBloodRunnable(bloodEffect), 4000, 4000);
}
private final class DragonBloodRunnable implements Runnable {
private final StatEffect effect;
public DragonBloodRunnable(final StatEffect effect) {
this.effect = effect;
}
@Override
public void run() {
if (ChannelCharacter.this.stats.getHp() - this.effect.getX() > 1) {
ChannelCharacter.this.cancelBuffStats(BuffStat.DRAGONBLOOD);
} else {
ChannelCharacter.this.addHP(-this.effect.getX());
final int bloodEffectSourceId = this.effect.getSourceId();
final GamePacket ownEffectPacket = ChannelPackets.showOwnBuffEffect(bloodEffectSourceId, 5);
ChannelCharacter.this.client.write(ownEffectPacket);
final GamePacket otherEffectPacket = ChannelPackets.showBuffeffect(ChannelCharacter.this.getId(), bloodEffectSourceId, 5);
ChannelCharacter.this.map.broadcastMessage(ChannelCharacter.this, otherEffectPacket, false);
}
}
}
public void startFullnessSchedule(final int decrease, final Pet pet, final int petSlot) {
final ScheduledFuture<?> schedule = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
final int newFullness = pet.getFullness() - decrease;
if (newFullness <= 5) {
pet.setFullness(15);
ChannelCharacter.this.unequipPet(pet, true, true);
} else {
pet.setFullness(newFullness);
ChannelCharacter.this.client.write(PetPacket.updatePet(pet, true));
}
}
}, 60000, 60000);
switch (petSlot) {
case 0:
this.fullnessSchedule = schedule;
break;
case 1:
this.fullnessSchedule_1 = schedule;
break;
case 2:
this.fullnessSchedule_2 = schedule;
break;
}
}
public void cancelFullnessSchedule(final int petSlot) {
switch (petSlot) {
case 0:
if (this.fullnessSchedule != null) {
this.fullnessSchedule.cancel(false);
}
break;
case 1:
if (this.fullnessSchedule_1 != null) {
this.fullnessSchedule_1.cancel(false);
}
break;
case 2:
if (this.fullnessSchedule_2 != null) {
this.fullnessSchedule_2.cancel(false);
}
break;
}
}
public void startMapTimeLimitTask(int time, final GameMap to) {
this.client.write(ChannelPackets.getClock(time));
time *= 1000;
this.mapTimeLimitTask = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
ChannelCharacter.this.changeMap(to, to.getPortal(0));
}
}, time, time);
}
public void startFishingTask(final boolean VIP) {
final int time = VIP ? 30000 : 60000;
this.cancelFishingTask();
this.fishing = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
if (!ChannelCharacter.this.haveItem(2300000, 1, false, true)) {
ChannelCharacter.this.cancelFishingTask();
return;
}
InventoryManipulator.removeById(ChannelCharacter.this.client, ChannelCharacter.this.getUseInventory(), 2300000, 1, false, false);
final int randval = RandomRewards.getInstance().getFishingReward();
switch (randval) {
case 0: // Meso
final int money = Randomizer.rand(10, 50000);
ChannelCharacter.this.gainMeso(money, true);
ChannelCharacter.this.client.write(UIPacket.fishingUpdate((byte) 1, money));
break;
case 1: // EXP
final int experi = Randomizer.nextInt(GameConstants.getExpNeededForLevel(ChannelCharacter.this.level) / 200);
ChannelCharacter.this.gainExp(experi, true, false, true);
ChannelCharacter.this.client.write(UIPacket.fishingUpdate((byte) 2, experi));
break;
default:
InventoryManipulator.addById(ChannelCharacter.this.client, randval, (short) 1);
ChannelCharacter.this.client.write(UIPacket.fishingUpdate((byte) 0, randval));
break;
}
ChannelCharacter.this.map.broadcastMessage(UIPacket.fishingCaught(ChannelCharacter.this.id));
}
}, time, time);
}
public void cancelMapTimeLimitTask() {
if (this.mapTimeLimitTask != null) {
this.mapTimeLimitTask.cancel(false);
}
}
public void cancelFishingTask() {
if (this.fishing != null) {
this.fishing.cancel(false);
}
}
public void registerEffect(final StatEffect effect, final long starttime, final ScheduledFuture<?> schedule) {
if (effect.isHide()) {
this.hidden = true;
this.map.broadcastMessage(this, ChannelPackets.removePlayerFromMap(this.getId()), false);
} else if (effect.isDragonBlood()) {
this.prepareDragonBlood(effect);
} else if (effect.isBerserk()) {
this.checkBerserk();
} else if (effect.isBeholder()) {
this.prepareBeholderEffect();
}
for (final BuffStatValue statup : effect.getStatups()) {
this.effects.put(statup.Stat, new BuffStatValueHolder(effect, starttime, schedule, statup.Value));
}
this.stats.recalcLocalStats();
}
public List<BuffStat> getBuffStats(final StatEffect effect, final long startTime) {
final List<BuffStat> bstats = Lists.newArrayList();
for (final Entry<BuffStat, BuffStatValueHolder> stateffect : this.effects.entrySet()) {
final BuffStatValueHolder value = stateffect.getValue();
if (value.effect.sameSource(effect) && (startTime == -1 || startTime == value.startTime)) {
bstats.add(stateffect.getKey());
}
}
return bstats;
}
private void deregisterBuffStats(final List<BuffStat> stats) {
final List<BuffStatValueHolder> effectsToCancel = Lists.newArrayListWithCapacity(stats.size());
for (final BuffStat stat : stats) {
final BuffStatValueHolder value = this.effects.get(stat);
if (value != null) {
this.effects.remove(stat);
boolean addMbsvh = true;
for (final BuffStatValueHolder contained : effectsToCancel) {
if (value.startTime == contained.startTime && contained.effect == value.effect) {
addMbsvh = false;
}
}
if (addMbsvh) {
effectsToCancel.add(value);
}
if (stat == BuffStat.SUMMON || stat == BuffStat.PUPPET || stat == BuffStat.MIRROR_TARGET) {
final int summonId = value.effect.getSourceId();
final Summon summon = this.summons.get(summonId);
if (summon != null) {
this.map.broadcastMessage(ChannelPackets.removeSummon(summon, true));
this.map.removeMapObject(summon);
this.removeVisibleMapObject(summon);
this.summons.remove(summonId);
if (summon.getSkill() == 1321007) {
if (this.beholderHealingSchedule != null) {
this.beholderHealingSchedule.cancel(false);
this.beholderHealingSchedule = null;
}
if (this.beholderBuffSchedule != null) {
this.beholderBuffSchedule.cancel(false);
this.beholderBuffSchedule = null;
}
}
}
} else if (stat == BuffStat.DRAGONBLOOD) {
if (this.dragonBloodSchedule != null) {
this.dragonBloodSchedule.cancel(false);
this.dragonBloodSchedule = null;
}
}
}
}
for (final BuffStatValueHolder cancelEffectCancelTasks : effectsToCancel) {
if (this.getBuffStats(cancelEffectCancelTasks.effect, cancelEffectCancelTasks.startTime).isEmpty()) {
if (cancelEffectCancelTasks.schedule != null) {
cancelEffectCancelTasks.schedule.cancel(false);
}
}
}
}
/**
* @param effect
* @param overwrite
* when overwrite is set no data is sent and all the Buffstats in
* the StatEffect are deregistered
* @param startTime
*/
public void cancelEffect(final StatEffect effect, final boolean overwrite, final long startTime) {
List<BuffStat> buffstats;
if (!overwrite) {
buffstats = this.getBuffStats(effect, startTime);
} else {
final List<BuffStatValue> statups = effect.getStatups();
buffstats = Lists.newArrayListWithCapacity(statups.size());
for (final BuffStatValue statup : statups) {
buffstats.add(statup.Stat);
}
}
this.deregisterBuffStats(buffstats);
if (effect.isMagicDoor()) {
// remove for all on maps
if (!this.getDoors().isEmpty()) {
final Door door = this.getDoors().iterator().next();
for (final ChannelCharacter chr : door.getTarget().getCharacters()) {
door.sendDestroyData(chr.getClient());
}
for (final ChannelCharacter chr : door.getTown().getCharacters()) {
door.sendDestroyData(chr.getClient());
}
for (final Door destroyDoor : this.getDoors()) {
door.getTarget().removeMapObject(destroyDoor);
door.getTown().removeMapObject(destroyDoor);
}
this.clearDoors();
this.silentPartyUpdate();
}
} else if (effect.isMonsterRiding()) {
// if (effect.getSourceId() != 5221006) {
// getMount().cancelSchedule();
// }
} else if (effect.isAranCombo()) {
this.combo = 0;
}
// check if we are still logged in o.o
if (!overwrite) {
this.cancelPlayerBuffs(buffstats);
if (effect.isHide() && (ChannelCharacter) this.map.getMapObject(this.getObjectId()) != null) {
this.hidden = false;
this.map.broadcastMessage(this, ChannelPackets.spawnPlayerMapObject(this), false);
for (final Pet pet : this.pets) {
if (pet.isSummoned()) {
final GamePacket packet = PetPacket.showPet(this, pet);
this.map.broadcastMessage(this, packet, false);
}
}
}
}
}
public void cancelBuffStats(final BuffStat stat) {
final List<BuffStat> buffStatList = Arrays.asList(stat);
this.deregisterBuffStats(buffStatList);
this.cancelPlayerBuffs(buffStatList);
}
public void cancelEffectFromBuffStat(final BuffStat stat) {
this.cancelEffect(this.effects.get(stat).effect, false, -1);
}
private void cancelPlayerBuffs(final List<BuffStat> buffstats) {
if (ChannelServer.getPlayerStorage().getCharacterById(this.getId()) != null) {
// are we still connected ?
if (buffstats.contains(BuffStat.HOMING_BEACON)) {
this.client.write(ChannelPackets.cancelHoming());
} else {
this.stats.recalcLocalStats();
this.enforceMaxHpMp();
this.client.write(ChannelPackets.cancelBuff(buffstats, buffstats.contains(BuffStat.MONSTER_RIDING)));
this.map.broadcastMessage(this, ChannelPackets.cancelForeignBuff(this.getId(), buffstats), false);
}
}
if (buffstats.contains(BuffStat.MONSTER_RIDING) && Jobs.isEvan(this.jobId) && this.jobId >= 2200) {
this.makeDragon();
this.map.spawnDragon(this.dragon);
}
}
public void dispel() {
if (!this.isHidden()) {
final LinkedList<BuffStatValueHolder> allBuffs = Lists.newLinkedList(this.effects.values());
for (final BuffStatValueHolder value : allBuffs) {
if (value.effect.isSkill() && value.schedule != null && !value.effect.isMorph()) {
this.cancelEffect(value.effect, false, value.startTime);
}
}
}
}
public void dispelSkill(final int skillid) {
final LinkedList<BuffStatValueHolder> allBuffs = Lists.newLinkedList(this.effects.values());
for (final BuffStatValueHolder value : allBuffs) {
if (skillid == 0) {
if (value.effect.isSkill()
&& (value.effect.getSourceId() == 1004 || value.effect.getSourceId() == 10001004 || value.effect.getSourceId() == 20001004
|| value.effect.getSourceId() == 20011004 || value.effect.getSourceId() == 1321007 || value.effect.getSourceId() == 2121005
|| value.effect.getSourceId() == 2221005 || value.effect.getSourceId() == 2311006 || value.effect.getSourceId() == 2321003
|| value.effect.getSourceId() == 3111002 || value.effect.getSourceId() == 3111005 || value.effect.getSourceId() == 3211002
|| value.effect.getSourceId() == 3211005 || value.effect.getSourceId() == 4111002)) {
this.cancelEffect(value.effect, false, value.startTime);
break;
}
} else {
if (value.effect.isSkill() && value.effect.getSourceId() == skillid) {
this.cancelEffect(value.effect, false, value.startTime);
break;
}
}
}
}
public void clearAllBuffEffects() {
this.effects.clear();
}
public void cancelAllBuffs() {
final LinkedList<BuffStatValueHolder> allBuffs = Lists.newLinkedList(this.effects.values());
for (final BuffStatValueHolder value : allBuffs) {
this.cancelEffect(value.effect, false, value.startTime);
}
}
public void cancelMorphs() {
final LinkedList<BuffStatValueHolder> allBuffs = Lists.newLinkedList(this.effects.values());
for (final BuffStatValueHolder value : allBuffs) {
switch (value.effect.getSourceId()) {
case 5111005:
case 5121003:
case 15111002:
case 13111005:
return; // Since we can't have more than 1, save up on loops
default:
if (value.effect.isMorph()) {
this.cancelEffect(value.effect, false, value.startTime);
continue;
}
}
}
}
public int getMorphState() {
final LinkedList<BuffStatValueHolder> allBuffs = Lists.newLinkedList(this.effects.values());
for (final BuffStatValueHolder value : allBuffs) {
if (value.effect.isMorph()) {
return value.effect.getSourceId();
}
}
return -1;
}
public void silentGiveBuffs(final Collection<PlayerBuffValueHolder> buffs) {
for (final PlayerBuffValueHolder value : buffs) {
value.Effect.silentApplyBuff(this, value.StartTime);
}
}
public List<PlayerBuffValueHolder> getAllBuffs() {
final List<PlayerBuffValueHolder> ret = Lists.newArrayList();
for (final BuffStatValueHolder value : this.effects.values()) {
ret.add(new PlayerBuffValueHolder(value.startTime, value.effect));
}
return ret;
}
public void cancelMagicDoor() {
final LinkedList<BuffStatValueHolder> allBuffs = Lists.newLinkedList(this.effects.values());
for (final BuffStatValueHolder value : allBuffs) {
if (value.effect.isMagicDoor()) {
this.cancelEffect(value.effect, false, value.startTime);
break;
}
}
}
public final void handleEnergyCharge(final int skillid, final byte targets) {
final ISkill echskill = SkillInfoProvider.getSkill(skillid);
final byte skilllevel = this.getCurrentSkillLevel(echskill);
if (skilllevel > 0) {
if (targets > 0) {
if (this.getBuffedValue(BuffStat.ENERGY_CHARGE) == null) {
echskill.getEffect(skilllevel).applyEnergyBuff(this, true); // Infinity
// time
} else {
Integer energyLevel = this.getBuffedValue(BuffStat.ENERGY_CHARGE);
if (energyLevel < 10000) {
energyLevel += 100 * targets;
this.setBuffedValue(BuffStat.ENERGY_CHARGE, energyLevel);
this.client.write(ChannelPackets.giveEnergyChargeTest(energyLevel));
if (energyLevel >= 10000) {
energyLevel = 10001;
}
} else if (energyLevel == 10001) {
echskill.getEffect(skilllevel).applyEnergyBuff(this, false); // One
// with
// time
energyLevel = 10002;
}
}
}
}
}
public final void handleOrbgain() {
final int orbcount = this.getBuffedValue(BuffStat.COMBO);
ISkill comboSkill;
ISkill advancedComboSkill;
switch (this.getJobId()) {
case 1110:
case 1111:
comboSkill = SkillInfoProvider.getSkill(11111001);
advancedComboSkill = SkillInfoProvider.getSkill(11110005);
break;
default:
comboSkill = SkillInfoProvider.getSkill(1111002);
advancedComboSkill = SkillInfoProvider.getSkill(1120003);
break;
}
StatEffect ceffect = null;
final int advComboSkillLevel = this.getCurrentSkillLevel(advancedComboSkill);
if (advComboSkillLevel > 0) {
ceffect = advancedComboSkill.getEffect(advComboSkillLevel);
} else {
ceffect = comboSkill.getEffect(this.getCurrentSkillLevel(comboSkill));
}
if (orbcount < ceffect.getX() + 1) {
int neworbcount = orbcount + 1;
if (advComboSkillLevel > 0 && ceffect.makeChanceResult()) {
if (neworbcount < ceffect.getX() + 1) {
neworbcount++;
}
}
final List<BuffStatValue> stat = Collections.singletonList(new BuffStatValue(BuffStat.COMBO, neworbcount));
this.setBuffedValue(BuffStat.COMBO, neworbcount);
int duration = ceffect.getDuration();
duration += (int) (this.getBuffedStartTime(BuffStat.COMBO) - System.currentTimeMillis());
this.client.write(ChannelPackets.giveBuff(1111002, duration, stat, ceffect));
this.map.broadcastMessage(this, ChannelPackets.giveForeignBuff(this.getId(), stat, ceffect), false);
}
}
public void handleOrbconsume() {
ISkill comboSkill;
switch (this.getJobId()) {
case 1110:
case 1111:
comboSkill = SkillInfoProvider.getSkill(11111001);
break;
default:
comboSkill = SkillInfoProvider.getSkill(1111002);
break;
}
final StatEffect ceffect = comboSkill.getEffect(this.getCurrentSkillLevel(comboSkill));
final List<BuffStatValue> stat = Collections.singletonList(new BuffStatValue(BuffStat.COMBO, 1));
this.setBuffedValue(BuffStat.COMBO, 1);
int duration = ceffect.getDuration();
duration += (int) (this.getBuffedStartTime(BuffStat.COMBO) - System.currentTimeMillis());
this.client.write(ChannelPackets.giveBuff(1111002, duration, stat, ceffect));
this.map.broadcastMessage(this, ChannelPackets.giveForeignBuff(this.getId(), stat, ceffect), false);
}
private void silentEnforceMaxHpMp() {
this.stats.setMp(this.stats.getMp());
this.stats.setHp(this.stats.getHp(), true);
}
private void enforceMaxHpMp() {
final List<StatValue> statups = Lists.newArrayListWithCapacity(2);
if (this.stats.getMp() > this.stats.getCurrentMaxMp()) {
this.stats.setMp(this.stats.getMp());
statups.add(new StatValue(Stat.MP, Integer.valueOf(this.stats.getMp())));
}
if (this.stats.getHp() > this.stats.getCurrentMaxHp()) {
this.stats.setHp(this.stats.getHp());
statups.add(new StatValue(Stat.HP, Integer.valueOf(this.stats.getHp())));
}
if (statups.size() > 0) {
this.client.write(ChannelPackets.updatePlayerStats(statups, this.getJobId()));
}
}
public GameMap getMap() {
return this.map;
}
public MonsterBook getMonsterBook() {
return this.monsterBook;
}
public void setMap(final GameMap newMap) {
this.map = newMap;
}
public void setMap(final int newMapId) {
this.mapId = newMapId;
}
@Override
public int getMapId() {
if (this.map != null) {
return this.map.getId();
}
return this.mapId;
}
@Override
public int getInitialSpawnPoint() {
return this.initialSpawnPoint;
}
@Override
public int getId() {
return this.id;
}
@Override
public String getName() {
return this.name;
}
public String getWorldName() {
return this.name;
}
public static int getDamageCap() {
return damageCap;
}
public final String getBlessOfFairyOrigin() {
return this.BlessOfFairy_Origin;
}
@Override
public final int getLevel() {
return this.level;
}
@Override
public int getFame() {
return this.fame;
}
public final int getDojoPoints() {
return this.dojoPoints;
}
public final int getDojoRecord() {
return this.dojoRecord;
}
public final int getFallCounter() {
return this.fallcounter;
}
public final ChannelClient getClient() {
return this.client;
}
public final void setClient(final ChannelClient client) {
this.client = client;
}
@Override
public int getExp() {
return this.exp;
}
@Override
public int getRemainingAp() {
return this.remainingAp;
}
@Override
public int[] getRemainingSps() {
return this.remainingSp;
}
@Override
public int getRemainingSp() {
return this.remainingSp[Skills.getSkillbook(this.jobId)]; // default
}
@Override
public int getRemainingSp(final int skillbook) {
return this.remainingSp[skillbook];
}
@Override
public int getRemainingSpSize() {
int ret = 0;
for (final int element : this.remainingSp) {
if (element > 0) {
ret++;
}
}
return ret;
}
public int getMpApUsed() {
return this.mpApUsed;
}
public void setMpApUsed(final int mpApUsed) {
this.mpApUsed = mpApUsed;
}
public int getHpApUsed() {
return this.hpApUsed;
}
public boolean isHidden() {
return this.hidden;
}
public void setHpApUsed(final int hpApUsed) {
this.hpApUsed = hpApUsed;
}
@Override
public int getSkinColorId() {
return this.skinColorId;
}
public void setSkinColorId(final int skinColorId) {
this.skinColorId = skinColorId;
}
@Override
public int getJobId() {
return this.jobId;
}
@Override
public Gender getGender() {
return this.gender;
}
@Override
public int getHairId() {
return this.hairId;
}
@Override
public int getFaceId() {
return this.faceId;
}
public void setName(final String name) {
this.name = name;
}
public void setExp(final int exp) {
this.exp = exp;
}
public void setHairId(final int hairId) {
this.hairId = hairId;
}
public void setFaceId(final int faceId) {
this.faceId = faceId;
}
public void setFame(final int fame) {
this.fame = fame;
}
public void setDojoPoints(final int points) {
this.dojoPoints = points;
}
public void setDojoRecord(final boolean reset) {
if (reset) {
this.dojoPoints = 0;
this.dojoRecord = 0;
} else {
this.dojoRecord++;
}
}
public void setFallCounter(final int fallcounter) {
this.fallcounter = fallcounter;
}
public void setRemainingAp(final int remainingAp) {
this.remainingAp = remainingAp;
}
public void setRemainingSp(final int remainingSp) {
this.remainingSp[Skills.getSkillbook(this.jobId)] = remainingSp; // default
}
public void setRemainingSp(final int remainingSp, final int skillbook) {
this.remainingSp[skillbook] = remainingSp;
}
public void setGender(final Gender gender) {
this.gender = gender;
}
public CheatTracker getCheatTracker() {
return this.cheatTracker;
}
public BuddyList getBuddyList() {
return this.buddies;
}
public void addFame(final int famechange) {
this.fame += famechange;
}
public void changeMapBanish(final int mapid, final String portal, final String msg) {
this.sendNotice(5, msg);
final GameMap newMap = ChannelServer.getMapFactory().getMap(mapid);
this.changeMap(newMap, newMap.getPortal(portal));
}
public void changeMap(final GameMap to, final Point pos) {
this.changeMapInternal(to, pos, ChannelPackets.getWarpToMap(to, 0x81, this));
}
public void changeMap(final GameMap to, final Portal pto) {
this.changeMapInternal(to, pto.getPosition(), ChannelPackets.getWarpToMap(to, pto.getId(), this));
}
private void changeMapInternal(final GameMap to, final Point pos, final GamePacket warpPacket) {
if (this.eventInstance != null) {
this.eventInstance.changedMap(this, to.getId());
}
this.client.write(warpPacket);
this.map.removePlayer(this);
if (ChannelServer.getPlayerStorage().getCharacterById(this.getId()) != null) {
this.map = to;
this.setPosition(pos);
to.addPlayer(this);
this.stats.relocHeal();
}
}
public void leaveMap() {
this.controlledMonsters.clear();
this.visibleMapObjects.clear();
if (this.chair != 0) {
this.cancelFishingTask();
this.chair = 0;
}
if (this.hpDecreaseTask != null) {
this.hpDecreaseTask.cancel(false);
}
this.cancelMapTimeLimitTask();
}
public void resetStats(final int str, final int dex, final int int_, final int luk) {
final List<StatValue> newStats = Lists.newArrayList();
final ChannelCharacter chr = this;
int total = chr.getStats().getStr() + chr.getStats().getDex() + chr.getStats().getLuk() + chr.getStats().getInt() + chr.getRemainingAp();
total -= str;
chr.getStats().setStr(str);
total -= dex;
chr.getStats().setDex(dex);
total -= int_;
chr.getStats().setInt(int_);
total -= luk;
chr.getStats().setLuk(luk);
chr.setRemainingAp(total);
newStats.add(new StatValue(Stat.STR, str));
newStats.add(new StatValue(Stat.DEX, dex));
newStats.add(new StatValue(Stat.INT, int_));
newStats.add(new StatValue(Stat.LUK, luk));
newStats.add(new StatValue(Stat.AVAILABLE_AP, total));
this.client.write(ChannelPackets.updatePlayerStats(newStats, false, chr.getJobId()));
}
public void startHurtHp() {
this.hpDecreaseTask = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
if (ChannelCharacter.this.map.getHPDec() < 1 || !ChannelCharacter.this.isAlive()) {
return;
} else if (ChannelCharacter.this.getEquippedItemsInventory().findById(ChannelCharacter.this.map.getHPDecProtect()) == null) {
ChannelCharacter.this.addHP(-ChannelCharacter.this.map.getHPDec());
}
}
}, 10000);
}
public void changeJob(final int newJob) {
final boolean wasEvan = Jobs.isEvan(this.jobId);
this.jobId = (short) newJob;
if (Jobs.isBeginner(newJob)) {
if (Jobs.isEvan(newJob)) {
this.remainingSp[Skills.getSkillbook(newJob)] += 3;
} else {
this.remainingSp[Skills.getSkillbook(newJob)]++;
if (newJob % 10 >= 2) {
this.remainingSp[Skills.getSkillbook(newJob)] += 2;
}
}
}
if (!this.isGM()) {
if (newJob % 1000 == 100) { // first job = warrior
this.resetStats(25, 4, 4, 4);
} else if (newJob % 1000 == 200) {
this.resetStats(4, 4, 20, 4);
} else if (newJob % 1000 == 300 || newJob % 1000 == 400) {
this.resetStats(4, 25, 4, 4);
} else if (newJob % 1000 == 500) {
this.resetStats(4, 20, 4, 4);
}
}
this.client.write(ChannelPackets.updateSp(this, false, wasEvan));
this.updateSingleStat(Stat.JOB, newJob);
int maxhp = this.stats.getMaxHp(), maxmp = this.stats.getMaxMp();
switch (this.jobId) {
case 100: // Warrior
case 1100: // Soul Master
case 2100: // Aran
maxhp += Randomizer.rand(200, 250);
break;
case 200: // Magician
case 2200:
case 2210:
maxmp += Randomizer.rand(100, 150);
break;
case 300: // Bowman
case 400: // Thief
case 500: // Pirate
maxhp += Randomizer.rand(100, 150);
maxmp += Randomizer.rand(25, 50);
break;
case 110: // Fighter
maxhp += Randomizer.rand(300, 350);
break;
case 120: // Page
case 130: // Spearman
case 1110: // Soul Master
case 2110: // Aran
maxhp += Randomizer.rand(300, 350);
break;
case 210: // FP
case 220: // IL
case 230: // Cleric
maxmp += Randomizer.rand(400, 450);
break;
case 310: // Bowman
case 320: // Crossbowman
case 410: // Assasin
case 420: // Bandit
case 430:
case 1310: // Wind Breaker
case 1410: // Night Walker
maxhp += Randomizer.rand(300, 350);
maxhp += Randomizer.rand(150, 200);
break;
case 900: // GM
case 800: // Manager
maxhp += 30000;
maxhp += 30000;
break;
}
if (maxhp >= 30000) {
maxhp = 30000;
}
if (maxmp >= 30000) {
maxmp = 30000;
}
this.stats.setMaxHp(maxhp);
this.stats.setMaxMp(maxmp);
this.stats.setHp(maxhp);
this.stats.setMp(maxmp);
final List<StatValue> statup = Lists.newArrayList();
statup.add(new StatValue(Stat.MAX_HP, Integer.valueOf(maxhp)));
statup.add(new StatValue(Stat.MAX_MP, Integer.valueOf(maxmp)));
this.stats.recalcLocalStats();
this.client.write(ChannelPackets.updatePlayerStats(statup, this.getJobId()));
this.map.broadcastMessage(this, ChannelPackets.showForeignEffect(this.getId(), 8), false);
this.silentPartyUpdate();
this.updateJob();
if (this.dragon != null) {
this.map.broadcastMessage(ChannelPackets.removeDragon(this.id));
this.map.removeMapObject(this.dragon);
this.dragon = null;
}
if (newJob >= 2200 && newJob <= 2218) { // make new
if (this.getBuffedValue(BuffStat.MONSTER_RIDING) != null) {
this.cancelBuffStats(BuffStat.MONSTER_RIDING);
}
this.makeDragon();
this.map.spawnDragon(this.dragon);
if (newJob == 2217) {
for (final int id : Skill.EVAN_SKILLS_1) {
final ISkill skill = SkillInfoProvider.getSkill(id);
if (skill != null && this.getCurrentSkillLevel(skill) <= 0 && this.getMasterSkillLevel(skill) <= 0) {
this.changeSkillLevel(skill, skill.getMaxLevel(), skill.getMaxLevel());
}
}
} else if (newJob == 2218) {
for (final int id : Skill.EVAN_SKILLS_2) {
final ISkill skill = SkillInfoProvider.getSkill(id);
if (skill != null && this.getCurrentSkillLevel(skill) <= 0 && this.getMasterSkillLevel(skill) <= 0) {
this.changeSkillLevel(skill, skill.getMaxLevel(), skill.getMaxLevel());
}
}
}
} else if (newJob >= 431 && newJob <= 434) { // master skills
for (final int id : Skill.DUALBLADE_SKILLS) {
final ISkill skill = SkillInfoProvider.getSkill(id);
if (skill != null && this.getCurrentSkillLevel(skill) <= 0 && this.getMasterSkillLevel(skill) <= 0) {
this.changeSkillLevel(skill, (byte) 0, (byte) skill.getMasterLevel());
}
}
}
}
public void makeDragon() {
this.dragon = new Dragon(this);
}
public Dragon getDragon() {
return this.dragon;
}
public void gainAp(final int ap) {
this.remainingAp += ap;
this.updateSingleStat(Stat.AVAILABLE_AP, this.remainingAp);
}
public void gainSP(final int sp) {
this.remainingSp[Skills.getSkillbook(this.jobId)] += sp; // default
this.client.write(ChannelPackets.updateSp(this, false));
this.client.write(UIPacket.getSPMsg((byte) sp));
}
public void changeSkillLevel(final ISkill skill, byte newLevel, byte newMasterlevel) {
if (skill == null || !GameConstants.isApplicableSkill(skill.getId()) && !GameConstants.isApplicableSkill_(skill.getId())) {
return;
}
if (newLevel == 0 && newMasterlevel == 0) {
if (this.skills.containsKey(skill)) {
this.skills.remove(skill);
}
} else {
if (newLevel < 0) {
newLevel = 0;
}
if (newMasterlevel < 0) {
newMasterlevel = 0;
}
this.skills.put(skill, new SkillEntry(newLevel, newMasterlevel));
}
if (Skills.isRecoveryIncSkill(skill.getId())) {
this.stats.relocHeal();
} else if (Skills.isElementAmplification(skill.getId())) {
this.stats.recalcLocalStats();
}
this.client.write(ChannelPackets.updateSkill(skill.getId(), newLevel, newMasterlevel));
}
public void playerDead() {
if (this.getEventInstance() != null) {
this.getEventInstance().playerKilled(this);
}
this.dispelSkill(0);
if (this.getBuffedValue(BuffStat.MORPH) != null) {
this.cancelEffectFromBuffStat(BuffStat.MORPH);
}
if (this.getBuffedValue(BuffStat.MONSTER_RIDING) != null) {
this.cancelEffectFromBuffStat(BuffStat.MONSTER_RIDING);
}
if (this.getBuffedValue(BuffStat.SUMMON) != null) {
this.cancelEffectFromBuffStat(BuffStat.SUMMON);
}
if (this.getBuffedValue(BuffStat.PUPPET) != null) {
this.cancelEffectFromBuffStat(BuffStat.PUPPET);
}
if (this.getBuffedValue(BuffStat.MIRROR_TARGET) != null) {
this.cancelEffectFromBuffStat(BuffStat.MIRROR_TARGET);
}
if (this.jobId != 0 && this.jobId != 1000 && this.jobId != 2000 && this.jobId != 2001) {
int charms = this.getItemQuantity(5130000, false);
if (charms > 0) {
InventoryManipulator.removeById(this.client, this.getCashInventory(), 5130000, 1, true, false);
charms--;
if (charms > 0xFF) {
charms = 0xFF;
}
this.client.write(MTSCSPacket.useCharm((byte) charms, (byte) 0));
} else {
float diepercentage = 0.0f;
final int expforlevel = GameConstants.getExpNeededForLevel(this.level);
if (this.map.isTown() || FieldLimitType.RegularExpLoss.check(this.map.getFieldLimit())) {
diepercentage = 0.01f;
} else {
float v8 = 0.0f;
if (this.jobId / 100 == 3) {
v8 = 0.08f;
} else {
v8 = 0.2f;
}
diepercentage = (float) (v8 / this.stats.getLuk() + 0.05);
}
int v10 = (int) (this.exp - (long) ((double) expforlevel * diepercentage));
if (v10 < 0) {
v10 = 0;
}
this.exp = v10;
}
}
this.updateSingleStat(Stat.EXP, this.exp);
}
public void updatePartyMemberHP() {
if (this.party != null) {
final int channel = this.client.getChannelId();
for (final PartyMember partychar : this.party.getMembers()) {
if (partychar.getMapId() == this.getMapId() && partychar.getChannel() == channel) {
final ChannelCharacter other = ChannelServer.getPlayerStorage().getCharacterByName(partychar.getName());
if (other != null) {
final GamePacket packet = ChannelPackets.updatePartyMemberHP(this.getId(), this.stats.getHp(), this.stats.getCurrentMaxHp());
other.getClient().write(packet);
}
}
}
}
}
public void receivePartyMemberHP() {
final int channel = this.client.getChannelId();
for (final PartyMember partychar : this.party.getMembers()) {
if (partychar.getMapId() == this.getMapId() && partychar.getChannel() == channel) {
final ChannelCharacter other = ChannelServer.getPlayerStorage().getCharacterByName(partychar.getName());
if (other != null) {
final GamePacket packet = ChannelPackets.updatePartyMemberHP(other.getId(), other.getStats().getHp(), other.getStats().getCurrentMaxHp());
this.client.write(packet);
}
}
}
}
/**
* Convenience function which adds the supplied parameter to the current hp
* then directly does a updateSingleStat.
*
* @see ChannelCharacter#setHp(int)
* @param delta
*/
public void addHP(final int delta) {
if (this.stats.setHp(this.stats.getHp() + delta)) {
this.updateSingleStat(Stat.HP, this.stats.getHp());
}
}
/**
* Convenience function which adds the supplied parameter to the current mp
* then directly does a updateSingleStat.
*
* @see ChannelCharacter#setMp(int)
* @param delta
*/
public void addMP(final int delta) {
if (this.stats.setMp(this.stats.getMp() + delta)) {
this.updateSingleStat(Stat.MP, this.stats.getMp());
}
}
public void addMPHP(final int hpDiff, final int mpDiff) {
final List<StatValue> statups = Lists.newArrayList();
if (this.stats.setHp(this.stats.getHp() + hpDiff)) {
statups.add(new StatValue(Stat.HP, Integer.valueOf(this.stats.getHp())));
}
if (this.stats.setMp(this.stats.getMp() + mpDiff)) {
statups.add(new StatValue(Stat.MP, Integer.valueOf(this.stats.getMp())));
}
if (statups.size() > 0) {
this.client.write(ChannelPackets.updatePlayerStats(statups, this.getJobId()));
}
}
public void updateSingleStat(final Stat stat, final int newval) {
this.updateSingleStat(stat, newval, false);
}
/**
* Updates a single stat of this GameCharacter for the client. This method
* only creates and sends an update packet, it does not update the stat
* stored in this GameCharacter instance.
*
* @param stat
* @param newval
* @param itemReaction
*/
public void updateSingleStat(final Stat stat, final int newval, final boolean itemReaction) {
if (stat == Stat.AVAILABLE_SP) {
this.client.write(ChannelPackets.updateSp(this, itemReaction, false));
return;
}
final StatValue value = new StatValue(stat, Integer.valueOf(newval));
this.client.write(ChannelPackets.updatePlayerStats(Collections.singletonList(value), itemReaction, this.getJobId()));
}
public void gainExp(final int total, final boolean show, final boolean inChat, final boolean white) {
if (this.level == 200 || Jobs.isCygnus(this.jobId) && this.level == 120) {
final int needed = GameConstants.getExpNeededForLevel(this.level);
if (this.exp + total > needed) {
this.setExp(needed);
} else {
this.exp += total;
}
} else {
if (this.exp + total >= GameConstants.getExpNeededForLevel(this.level)) {
this.exp += total;
this.levelUp();
final int needed = GameConstants.getExpNeededForLevel(this.level);
if (this.exp > needed) {
this.setExp(needed);
}
} else {
this.exp += total;
}
}
if (total != 0) {
if (this.exp < 0) { // After adding, and negative
if (total > 0) {
this.setExp(GameConstants.getExpNeededForLevel(this.level));
} else if (total < 0) {
this.setExp(0);
}
}
this.updateSingleStat(Stat.EXP, this.getExp());
if (show) { // still show the expgain even if it's not there
this.client.write(ChannelPackets.GainEXP_Others(total, inChat, white));
}
}
}
public void gainExpMonster(final int gain, final boolean show, final boolean white, final byte partyMembers, final int CLASS_EXP) {
final Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getDefault());
final int day = cal.get(Calendar.DAY_OF_WEEK);
final int hour = cal.get(Calendar.HOUR_OF_DAY);
int eventExp = 0;
final int weddingExp = 0;
int partyRingExp = 0;
int partyExp = 0;
int premiumExp = 0;
int itemExp = 0;
int rainbowExp = 0;
int baseExp = gain;
if (this.haveItem(5210006, 1, false, true) && hour > 22 && hour < 2 || this.haveItem(5210007, 1, false, true) && hour > 2 && hour < 6
|| this.haveItem(5210008, 1, false, true) && hour > 6 && hour < 10 || this.haveItem(5210009, 1, false, true) && hour > 10 && hour < 14
|| this.haveItem(5210010, 1, false, true) && hour > 14 && hour < 18 || this.haveItem(5210011, 1, false, true) && hour > 18 && hour < 22) {
baseExp *= 2;
}
if (this.level >= 1 && this.level <= 10) {
eventExp = (int) (baseExp * 0.1);
premiumExp = (int) (baseExp * 0.1);
itemExp = (int) (baseExp * 0.1);
}
if (this.haveItem(1112127, 1, true, true)) {
// Welcome Back Ring | must be equipped in order to work
partyRingExp = (int) (baseExp * 0.8);
}
if (partyMembers > 1) {
partyExp = (int) ((baseExp / 10.0f) * (partyMembers + 1)); // 10%
}
if (day == Calendar.SATURDAY || day == Calendar.SUNDAY) {
// Saturday and Sunday
rainbowExp = (int) (baseExp * 0.1);
}
if (this.level == 200 || Jobs.isCygnus(this.jobId) && this.level == 120) {
final int needed = GameConstants.getExpNeededForLevel(this.level);
if (this.exp + baseExp > needed) {
this.setExp(needed);
} else {
this.exp += baseExp + eventExp + weddingExp + partyRingExp + partyExp + premiumExp + itemExp + rainbowExp + CLASS_EXP;
}
} else {
if (this.exp + baseExp >= GameConstants.getExpNeededForLevel(this.level)) {
this.exp += baseExp;
this.levelUp();
final int needed = GameConstants.getExpNeededForLevel(this.level);
if (this.exp > needed) {
this.setExp(needed);
}
} else {
this.exp += baseExp + eventExp + weddingExp + partyRingExp + partyExp + premiumExp + itemExp + rainbowExp + CLASS_EXP;
}
}
if (gain != 0) {
if (this.exp < 0) {
// After adding, and negative
if (gain > 0) {
this.setExp(GameConstants.getExpNeededForLevel(this.level));
} else if (gain < 0) {
this.setExp(0);
}
}
this.updateSingleStat(Stat.EXP, this.getExp());
if (show) {
// still show the expgain even if it's not there
this.client.write(ChannelPackets.GainEXP_Monster(baseExp, white, eventExp, weddingExp, partyRingExp, partyExp, premiumExp, itemExp, rainbowExp,
CLASS_EXP));
}
}
}
public void silentPartyUpdate() {
if (this.party != null) {
try {
final PartyMember newMember = new PartyMember(this.party.getId(), this);
ChannelServer.getWorldInterface().updateParty(this.party.getId(), PartyOperation.SILENT_UPDATE, newMember);
} catch (final RemoteException e) {
System.err.println("REMOTE THROW, silentPartyUpdate" + e);
ChannelServer.pingWorld();
}
}
}
public boolean isGM() {
return this.gmLevel > 0;
}
@Override
public int getGmLevel() {
return this.gmLevel;
}
public boolean hasGmLevel(final int level) {
return this.gmLevel >= level;
}
public final Inventory getEquipInventory() {
return this.inventory.get(InventoryType.EQUIP);
}
public final Inventory getUseInventory() {
return this.inventory.get(InventoryType.USE);
}
public final Inventory getSetupInventory() {
return this.inventory.get(InventoryType.SETUP);
}
public final Inventory getEtcInventory() {
return this.inventory.get(InventoryType.ETC);
}
public final Inventory getCashInventory() {
return this.inventory.get(InventoryType.CASH);
}
@Override
public final Inventory getEquippedItemsInventory() {
return this.inventory.get(InventoryType.EQUIPPED);
}
public final Inventory getInventoryForItem(final int itemId) {
return this.inventory.get(GameConstants.getInventoryType(itemId));
}
public final Inventory getInventoryByType(final InventoryType type) {
return this.inventory.get(type);
}
public final MultiInventory getInventories() {
return this.inventory;
}
public final void expirationTask() {
long expiration;
final long currenttime = System.currentTimeMillis();
// This is here to prevent deadlock.
final List<Item> toberemove = Lists.newArrayList();
for (final Inventory inv : this.inventory) {
for (final Item item : inv) {
expiration = item.getExpiration();
if (expiration != -1 && !GameConstants.isPet(item.getItemId())) {
final byte flag = item.getFlag();
if (ItemFlag.LOCK.check(flag)) {
if (currenttime > expiration) {
item.setExpiration(-1);
item.setFlag((byte) (flag - ItemFlag.LOCK.getValue()));
this.client.write(ChannelPackets.updateSpecialItemUse(item, item.getType().asNumber()));
}
} else if (currenttime > expiration) {
this.client.write(MTSCSPacket.itemExpired(item.getItemId()));
toberemove.add(item);
}
}
}
for (final Item item : toberemove) {
InventoryManipulator.removeFromSlot(this.client, inv, item.getPosition(), item.getQuantity(), false);
}
}
}
public Shop getShop() {
return this.shop;
}
public void setShop(final Shop shop) {
this.shop = shop;
}
@Override
public int getMeso() {
return this.meso;
}
public final EnumMap<SavedLocationType, Integer> getSavedLocations() {
return Maps.newEnumMap(this.savedLocations);
}
public int getSavedLocation(final SavedLocationType type) {
return this.savedLocations.get(type).intValue();
}
public void saveLocation(final SavedLocationType type) {
this.savedLocations.put(type, this.mapId);
}
public void clearSavedLocation(final SavedLocationType type) {
this.savedLocations.remove(type);
}
public void gainMeso(final int gain, final boolean show) {
this.gainMeso(gain, show, false, false);
}
public void gainMeso(final int gain, final boolean show, final boolean enableActions) {
this.gainMeso(gain, show, enableActions, false);
}
public void gainMeso(final int gain, final boolean show, final boolean enableActions, final boolean inChat) {
if (this.meso + gain < 0) {
this.client.write(ChannelPackets.enableActions());
return;
}
this.meso += gain;
this.updateSingleStat(Stat.MESO, this.meso, enableActions);
if (show) {
this.client.write(ChannelPackets.showMesoGain(gain, inChat));
}
}
public void controlMonster(final Monster monster, final boolean aggro) {
monster.setController(this);
this.controlledMonsters.add(monster);
this.client.write(MobPacket.controlMonster(monster, false, aggro));
}
public void stopControllingMonster(final Monster monster) {
this.controlledMonsters.remove(monster);
}
public void checkMonsterAggro(final Monster monster) {
if (monster.getController() == this) {
monster.setControllerHasAggro(true);
} else {
monster.switchController(this, true);
}
}
public Collection<Monster> getControlledMonsters() {
return Collections.unmodifiableCollection(this.controlledMonsters);
}
public int getAccountId() {
return this.accountId;
}
public void mobKilled(final int id) {
for (final Map.Entry<Integer, QuestStatus> entry : this.quests.entrySet()) {
final QuestStatus status = entry.getValue();
if (status.getState() != 1 || !status.hasMobKills()) {
continue;
}
if (status.mobKilled(id)) {
this.client.write(ChannelPackets.updateQuestMobKills(status));
final int questId = status.getQuestId();
final QuestInfo info = QuestInfoProvider.getInfo(questId);
if (info.canComplete(this, null)) {
this.client.write(ChannelPackets.getShowQuestCompletion(questId));
}
}
}
}
public final List<QuestStatus> getStartedQuests() {
final List<QuestStatus> ret = Lists.newLinkedList();
for (final QuestStatus q : this.quests.values()) {
if (q.getState() == 1) {
ret.add(q);
}
}
return Collections.unmodifiableList(ret);
}
public final List<QuestStatus> getCompletedQuests() {
final List<QuestStatus> ret = Lists.newLinkedList();
for (final QuestStatus q : this.quests.values()) {
if (q.getState() == 2) {
ret.add(q);
}
}
return Collections.unmodifiableList(ret);
}
public Map<ISkill, SkillEntry> getSkills() {
return Collections.unmodifiableMap(this.skills);
}
public byte getCurrentSkillLevel(final ISkill skill) {
final SkillEntry ret = this.skills.get(skill);
if (ret == null) {
return 0;
}
return ret.getCurrentLevel();
}
public byte getMasterSkillLevel(final ISkill skill) {
final SkillEntry ret = this.skills.get(skill);
if (ret == null) {
return 0;
}
return ret.getMasterLevel();
}
public void levelUp() {
if (Jobs.isCygnus(this.jobId)) {
if (this.level <= 70) {
this.remainingAp += 6;
} else {
this.remainingAp += 5;
}
} else {
this.remainingAp += 5;
}
int maxhp = this.stats.getMaxHp();
int maxmp = this.stats.getMaxMp();
if (this.jobId == 0 || this.jobId == 1000 || this.jobId == 2000) {
// Beginner
maxhp += Randomizer.rand(12, 16);
maxmp += Randomizer.rand(10, 12);
} else if (this.jobId >= 100 && this.jobId <= 132) {
// Warrior
final ISkill improvingMaxHP = SkillInfoProvider.getSkill(1000001);
final int slevel = this.getCurrentSkillLevel(improvingMaxHP);
if (slevel > 0) {
maxhp += improvingMaxHP.getEffect(slevel).getX();
}
maxhp += Randomizer.rand(24, 28);
maxmp += Randomizer.rand(4, 6);
} else if (this.jobId >= 200 && this.jobId <= 232) {
// Magician
final ISkill improvingMaxMP = SkillInfoProvider.getSkill(2000001);
final int skillLevel = this.getCurrentSkillLevel(improvingMaxMP);
if (skillLevel > 0) {
maxmp += improvingMaxMP.getEffect(skillLevel).getX() * 2;
}
maxhp += Randomizer.rand(10, 14);
maxmp += Randomizer.rand(22, 24);
} else if (this.jobId >= 300 && this.jobId <= 322 || this.jobId >= 400 && this.jobId <= 434 || this.jobId >= 1300 && this.jobId <= 1311
|| this.jobId >= 1400 && this.jobId <= 1411) {
// Bowman, Thief, Wind Breaker and Night Walker
maxhp += Randomizer.rand(20, 24);
maxmp += Randomizer.rand(14, 16);
} else if (this.jobId >= 500 && this.jobId <= 522) {
// Pirate
final ISkill improvingMaxHP = SkillInfoProvider.getSkill(5100000);
final int slevel = this.getCurrentSkillLevel(improvingMaxHP);
if (slevel > 0) {
maxhp += improvingMaxHP.getEffect(slevel).getX();
}
maxhp += Randomizer.rand(22, 26);
maxmp += Randomizer.rand(18, 22);
} else if (this.jobId >= 1100 && this.jobId <= 1111) {
// Soul Master
final ISkill improvingMaxHP = SkillInfoProvider.getSkill(11000000);
final int slevel = this.getCurrentSkillLevel(improvingMaxHP);
if (slevel > 0) {
maxhp += improvingMaxHP.getEffect(slevel).getX();
}
maxhp += Randomizer.rand(24, 28);
maxmp += Randomizer.rand(4, 6);
} else if (this.jobId >= 1200 && this.jobId <= 1211) {
// Flame Wizard
final ISkill improvingMaxMP = SkillInfoProvider.getSkill(12000000);
final int slevel = this.getCurrentSkillLevel(improvingMaxMP);
if (slevel > 0) {
maxmp += improvingMaxMP.getEffect(slevel).getX() * 2;
}
maxhp += Randomizer.rand(10, 14);
maxmp += Randomizer.rand(22, 24);
} else if (this.jobId >= 2200 && this.jobId <= 2218) {
// Evan
maxhp += Randomizer.rand(12, 16);
maxmp += Randomizer.rand(50, 52);
} else if (this.jobId >= 1500 && this.jobId <= 1512) {
// Pirate
final ISkill improvingMaxHP = SkillInfoProvider.getSkill(15100000);
final int slevel = this.getCurrentSkillLevel(improvingMaxHP);
if (slevel > 0) {
maxhp += improvingMaxHP.getEffect(slevel).getX();
}
maxhp += Randomizer.rand(22, 26);
maxmp += Randomizer.rand(18, 22);
} else if (this.jobId >= 2100 && this.jobId <= 2112) {
// Aran
maxhp += Randomizer.rand(50, 52);
maxmp += Randomizer.rand(4, 6);
} else {
// GameMaster
maxhp += Randomizer.rand(50, 100);
maxmp += Randomizer.rand(50, 100);
}
maxmp += this.stats.getTotalInt() / 10;
this.exp -= GameConstants.getExpNeededForLevel(this.level);
this.level += 1;
if (this.level == 200 && !this.isGM()) {
try {
final StringBuilder sb = new StringBuilder("[Congratulation] ");
final Item medal = this.getEquippedItemsInventory().getItem((byte) -46);
if (medal != null) { // Medal
sb.append("<");
sb.append(ItemInfoProvider.getInstance().getName(medal.getItemId()));
sb.append("> ");
}
sb.append(this.getName());
sb.append(" has achieved Level 200. Let us Celebrate! Maplers!");
ChannelServer.getWorldInterface().broadcastMessage(ChannelPackets.serverNotice(6, sb.toString()));
} catch (final RemoteException e) {
ChannelServer.pingWorld();
}
}
maxhp = Math.min(30000, maxhp);
maxmp = Math.min(30000, maxmp);
final List<StatValue> statup = Lists.newArrayListWithCapacity(8);
statup.add(new StatValue(Stat.MAX_HP, maxhp));
statup.add(new StatValue(Stat.MAX_MP, maxmp));
statup.add(new StatValue(Stat.HP, maxhp));
statup.add(new StatValue(Stat.MP, maxmp));
statup.add(new StatValue(Stat.EXP, this.exp));
statup.add(new StatValue(Stat.LEVEL, this.level));
if (this.jobId != 0 && this.jobId != 1000 && this.jobId != 2000 && this.jobId != 2001) { // Not
// Beginner,
// Nobless
// and
// Legend
this.remainingSp[Skills.getSkillbook(this.jobId)] += 3;
this.client.write(ChannelPackets.updateSp(this, false));
} else {
if (this.level <= 10) {
this.stats.setStr(this.stats.getStr() + this.remainingAp);
this.remainingAp = 0;
statup.add(new StatValue(Stat.STR, this.stats.getStr()));
}
}
statup.add(new StatValue(Stat.AVAILABLE_AP, this.remainingAp));
this.stats.setMaxHp(maxhp);
this.stats.setMaxMp(maxmp);
this.stats.setHp(maxhp);
this.stats.setMp(maxmp);
this.client.write(ChannelPackets.updatePlayerStats(statup, this.getJobId()));
this.map.broadcastMessage(this, ChannelPackets.showForeignEffect(this.getId(), 0), false);
this.stats.recalcLocalStats();
this.silentPartyUpdate();
this.updateLevel();
NpcScriptManager.getInstance().start(this.getClient(), 9105010); // Vavaan
}
public void changeKeybinding(final int key, final KeyBinding keybinding) {
if (keybinding.getType() != 0) {
this.keylayout.Layout().put(Integer.valueOf(key), keybinding);
} else {
this.keylayout.Layout().remove(Integer.valueOf(key));
}
}
public final SkillMacroSet getSkillMacros() {
return this.skillMacros;
}
public void temporaryBan(final String reason, final Calendar duration, final int tempBanReason) {
try {
final Connection con = Database.getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO ipbans VALUES (DEFAULT, ?)");
ps.setString(1, this.client.getSessionIP());
ps.execute();
ps.close();
this.client.disconnect(true);
ps = con.prepareStatement("UPDATE accounts SET tempban = ?, tempban_reason = ? WHERE id = ?");
final Timestamp timestamp = new Timestamp(duration.getTimeInMillis());
ps.setTimestamp(1, timestamp);
ps.setString(2, reason);
ps.setInt(3, tempBanReason);
ps.setInt(4, this.accountId);
ps.execute();
ps.close();
} catch (final SQLException ex) {
System.err.println("Error while tempbanning" + ex);
}
}
public final boolean ban(final String banReason, final boolean isAutoban) {
final Connection con = Database.getConnection();
try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET banned = ?, banreason = ? WHERE id = ?")) {
ps.setInt(1, isAutoban ? 2 : 1);
ps.setString(2, banReason);
ps.setInt(3, this.accountId);
ps.execute();
} catch (final SQLException ex) {
System.err.println("Error while banning" + ex);
return false;
}
return true;
}
@Override
public int getObjectId() {
return this.getId();
}
@Override
public void setObjectId(final int id) {
throw new UnsupportedOperationException();
}
public Storage getStorage() {
return this.storage;
}
public void addVisibleMapObject(final GameMapObject mo) {
this.visibleMapObjects.add(mo);
}
public void removeVisibleMapObject(final GameMapObject mo) {
this.visibleMapObjects.remove(mo);
}
public boolean isMapObjectVisible(final GameMapObject mo) {
return this.visibleMapObjects.contains(mo);
}
public Collection<GameMapObject> getVisibleMapObjects() {
return Collections.unmodifiableCollection(this.visibleMapObjects);
}
public boolean isAlive() {
return this.stats.getHp() > 0;
}
@Override
public void sendDestroyData(final ChannelClient client) {
client.write(ChannelPackets.removePlayerFromMap(this.getObjectId()));
}
@Override
public void sendSpawnData(final ChannelClient client) {
if (!this.isHidden()) {
client.write(ChannelPackets.spawnPlayerMapObject(this));
for (final Pet pet : this.pets) {
if (pet.isSummoned()) {
client.write(PetPacket.showPet(this, pet));
}
}
if (this.dragon != null) {
client.write(ChannelPackets.spawnDragon(this.dragon));
}
}
}
public void setDragon(final Dragon d) {
this.dragon = d;
}
public final void equipChanged() {
this.map.broadcastMessage(this, ChannelPackets.updateCharLook(this), false);
this.stats.recalcLocalStats();
this.enforceMaxHpMp();
final ChannelCharacter player = this.client.getPlayer();
if (player.getMessenger() != null) {
final WorldChannelInterface wci = ChannelServer.getWorldInterface();
try {
wci.updateMessenger(player.getMessenger().getId(), player.getName(), this.client.getChannelId());
} catch (final RemoteException e) {
ChannelServer.pingWorld();
}
}
}
public final Pet getPet(final int index) {
byte count = 0;
for (final Pet pet : this.pets) {
if (pet.isSummoned()) {
if (count == index) {
return pet;
}
count++;
}
}
return null;
}
public void addPet(final Pet pet) {
this.pets.remove(pet);
this.pets.add(pet);
// So that the pet will be at the last
// Pet index logic :(
}
public void removePet(final Pet pet, final boolean shiftLeft) {
pet.setSummoned(false);
/*
* 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 (shiftLeft) { if (slot > -1) { for (int i =
* slot; i < 3; i++) { if (i != 2) { pets[i] = pets[i + 1]; } else {
* pets[i] = null; } } } }
*/
}
public final byte getPetIndex(final Pet petz) {
byte count = 0;
for (final Pet pet : this.pets) {
if (pet.isSummoned()) {
if (pet == petz) {
return count;
}
count++;
}
}
return -1;
}
public final byte getPetIndex(final int petId) {
byte count = 0;
for (final Pet pet : this.pets) {
if (pet.isSummoned()) {
if (pet.getUniqueId() == petId) {
return count;
}
count++;
}
}
return -1;
}
public final List<Pet> getPets() {
return this.pets;
}
public final void unequipAllPets() {
for (final Pet pet : this.pets) {
if (pet != null) {
this.unequipPet(pet, true, false);
}
}
}
public void unequipPet(final Pet pet, final boolean shiftLeft, final boolean hunger) {
this.cancelFullnessSchedule(this.getPetIndex(pet));
pet.saveToDb();
this.map.broadcastMessage(this, PetPacket.removePet(this, pet, hunger), true);
final List<StatValue> petStat = Lists.newArrayList();
petStat.add(new StatValue(Stat.PET, Integer.valueOf(0)));
this.client.write(PetPacket.petStatUpdate(this));
this.client.write(ChannelPackets.enableActions());
this.removePet(pet, shiftLeft);
}
/*
* public void shiftPetsRight() { if (pets[2] == null) { pets[2] = pets[1];
* pets[1] = pets[0]; pets[0] = null; } }
*/
public final long getLastFameTime() {
return this.lastFameTime;
}
public final void setLastFameTime(final long timestamp) {
this.lastFameTime = timestamp;
}
public final boolean hasFamedToday() {
final long day = (long) Math.floor(this.lastFameTime / 86400000.0);
final long today = (long) Math.floor(System.currentTimeMillis() / 86400000.0);
return day < today;
}
public final KeyLayout getKeyLayout() {
return this.keylayout;
}
public boolean hasParty() {
return this.partyMember != null;
}
public PartyMember getPartyMembership() {
return this.partyMember;
}
public PartyMember setPartyMembership(final int partyId) {
this.partyMember = new PartyMember(partyId, this);
return this.partyMember;
}
public void removePartyMembership() {
this.partyMember = null;
}
public void setWorld(final int world) {
this.worldId = world;
}
public void setParty(final Party party) {
this.party = party;
}
public Trade getTrade() {
return this.trade;
}
public void setTrade(final Trade trade) {
this.trade = trade;
}
public EventInstanceManager getEventInstance() {
return this.eventInstance;
}
public void setEventInstance(final EventInstanceManager eventInstance) {
this.eventInstance = eventInstance;
}
public void addDoor(final Door door) {
this.doors.add(door);
}
public void clearDoors() {
this.doors.clear();
}
public ImmutableList<Door> getDoors() {
return ImmutableList.copyOf(this.doors);
}
public void setSmega() {
if (this.smega) {
this.smega = false;
this.sendNotice(5, "You have set megaphone to disabled mode");
} else {
this.smega = true;
this.sendNotice(5, "You have set megaphone to enabled mode");
}
}
public boolean getSmega() {
return this.smega;
}
public boolean canDoor() {
return this.canDoor;
}
public void disableDoor() {
this.canDoor = false;
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
ChannelCharacter.this.canDoor = true;
}
}, 5000);
}
public Map<Integer, Summon> getSummons() {
return this.summons;
}
public int getChair() {
return this.chair;
}
public int getItemEffect() {
return this.itemEffect;
}
public void setChair(final int chair) {
this.chair = chair;
this.stats.relocHeal();
}
public void setItemEffect(final int itemEffect) {
this.itemEffect = itemEffect;
}
@Override
public GameMapObjectType getType() {
return GameMapObjectType.PLAYER;
}
public int getGuildId() {
return this.guildId;
}
public MemberRank getGuildRank() {
return this.guildRank;
}
public void setGuildId(final int _id) {
this.guildId = _id;
if (this.guildId > 0) {
if (this.guildMember == null) {
this.guildMember = new GuildMember(this);
} else {
this.guildMember.setGuildId(this.guildId);
}
} else {
this.guildMember = null;
}
}
public void setGuildRank(final MemberRank newRank) {
this.guildRank = newRank;
if (this.guildMember != null) {
this.guildMember.setGuildRank(newRank);
}
}
public GuildMember getGuildMembership() {
return this.guildMember;
}
public Guild getGuild() {
try {
return ChannelServer.getWorldInterface().getGuild(this.getGuildId());
} catch (final RemoteException e) {
ChannelServer.pingWorld();
}
return null;
}
private void updateJob() {
final WorldChannelInterface world = ChannelServer.getWorldInterface();
if (this.guildMember == null) {
return;
} else {
try {
world.updateGuildMemberJob(this.guildId, this.hairId, this.jobId);
} catch (final RemoteException ex) {
System.err.println("Could not update level: " + ex);
}
}
}
private void updateLevel() {
final WorldChannelInterface world = ChannelServer.getWorldInterface();
if (this.guildMember == null) {
return;
} else {
try {
world.updateGuildMemberLevel(this.guildId, this.hairId, this.level);
} catch (final RemoteException ex) {
System.err.println("Could not update level: " + ex);
}
}
// TODO: more stuff here.
}
public void setReborns(final int reborns) {
this.reborns = reborns;
}
public void saveGuildStatus() {
final Connection con = Database.getConnection();
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET guildid = ?, guildrank = ? WHERE id = ?")) {
ps.setInt(1, this.guildId);
ps.setInt(2, this.guildRank.asNumber());
ps.setInt(3, this.id);
ps.execute();
} catch (final SQLException se) {
System.err.println("SQL error: " + se.getLocalizedMessage() + se);
}
}
public void modifyCSPoints(final int type, final int quantity, final boolean show) {
if (this.getNX() < 0) {
this.aCash = 0;
}
if (this.getNX() > 1000000) {
this.aCash = 900000;
}
if (this.getNX() + quantity < 900000) {
switch (type) {
case 1:
this.aCash += quantity;
break;
case 2:
this.maplePoints += quantity;
break;
default:
break;
}
if (show) {
this.sendNotice(5, "You have gained " + quantity + " cash.");
this.client.write(ChannelPackets.showSpecialEffect(19));
}
} else {
this.sendNotice(5, "You have reached the maximum ammount of @cash");
}
}
public int getCSPoints(final int type) {
switch (type) {
case 1:
return this.aCash;
case 2:
return this.maplePoints;
default:
return 0;
}
}
public final boolean haveItem(final int itemId, final int quantity, final boolean checkEquipped, final boolean greaterOrEquals) {
int count = this.getInventoryForItem(itemId).countById(itemId);
if (checkEquipped) {
count += this.inventory.get(InventoryType.EQUIPPED).countById(itemId);
}
if (greaterOrEquals) {
return count >= quantity;
} else {
return count == quantity;
}
}
public void setLevel(final int level) {
this.level = (short) level;
}
public int getSkillLevel(final int skill) {
final SkillEntry ret = this.skills.get(SkillInfoProvider.getSkill(skill));
if (ret == null) {
return 0;
}
return ret.getCurrentLevel();
}
public void forfeitQuest(final int questId) {
this.getQuestStatus(questId).forfeit();
this.updateQuest(questId);
}
public void completeQuest(final int questId, final int npcId) {
this.getQuestStatus(questId).complete(npcId);
this.updateQuest(questId);
}
public void startQuest(final int questId, final int npcId) {
this.getQuestStatus(questId).start(npcId, "");
this.updateQuest(questId);
}
public static enum FameStatus {
OK, NOT_TODAY, NOT_THIS_MONTH
}
public int getBuddyCapacity() {
return this.buddies.getCapacity();
}
public void setBuddyCapacity(final int capacity) {
this.buddies.setCapacity(capacity);
this.client.write(ChannelPackets.updateBuddyCapacity(capacity));
}
public Messenger getMessenger() {
return this.messenger;
}
public void setMessenger(final Messenger messenger) {
this.messenger = messenger;
}
public int getMessengerPosition() {
return this.messengerPosition;
}
public void setMessengerPosition(final int position) {
this.messengerPosition = position;
}
public void addCooldown(final int skillId, final long startTime, final long length, final ScheduledFuture<?> timer) {
this.cooldowns.put(Integer.valueOf(skillId), new CooldownValueHolder(skillId, startTime, length, timer));
}
public void removeCooldown(final int skillId) {
if (this.cooldowns.containsKey(Integer.valueOf(skillId))) {
this.cooldowns.remove(Integer.valueOf(skillId));
}
}
public boolean isInCooldown(final int skillId) {
return this.cooldowns.containsKey(Integer.valueOf(skillId));
}
public void giveCooldowns(final int skillid, final long starttime, final long length) {
final int time = (int) (length + starttime - System.currentTimeMillis());
final ScheduledFuture<?> timer = TimerManager.getInstance().schedule(new CancelCooldownAction(this, skillid), time);
this.addCooldown(skillid, System.currentTimeMillis(), time, timer);
}
public void giveCooldowns(final Collection<PlayerCooldownValueHolder> cooldowns) {
int time;
if (cooldowns != null) {
for (final PlayerCooldownValueHolder cooldown : cooldowns) {
time = (int) (cooldown.Length + cooldown.StartTime - System.currentTimeMillis());
final ScheduledFuture<?> timer = TimerManager.getInstance().schedule(new CancelCooldownAction(this, cooldown.SkillId), time);
this.addCooldown(cooldown.SkillId, System.currentTimeMillis(), time, timer);
}
} else {
final Connection con = Database.getConnection();
try ( PreparedStatement ps = this.getSelectCooldowns(con);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
final long length = rs.getLong("length");
final long startTime = rs.getLong("StartTime");
if (length + startTime - System.currentTimeMillis() <= 0) {
continue;
}
final int skillId = rs.getInt("SkillID");
this.giveCooldowns(skillId, startTime, length);
}
this.deleteByCharacterId(con, "DELETE FROM skills_cooldowns WHERE charid = ?");
} catch (final SQLException e) {
System.err.println("Error while retriving cooldown from SQL storage");
}
}
}
private PreparedStatement getSelectCooldowns(final Connection con) throws SQLException {
final PreparedStatement ps = con.prepareStatement("SELECT SkillID,StartTime,length FROM skills_cooldowns WHERE charid = ?");
ps.setInt(1, this.getId());
return ps;
}
public List<PlayerCooldownValueHolder> getAllCooldowns() {
final List<PlayerCooldownValueHolder> ret = Lists.newArrayList();
for (final CooldownValueHolder mcdvh : this.cooldowns.values()) {
ret.add(new PlayerCooldownValueHolder(mcdvh.skillId, mcdvh.startTime, mcdvh.length));
}
return ret;
}
public final List<PlayerDiseaseValueHolder> getAllDiseases() {
final List<PlayerDiseaseValueHolder> ret = Lists.newArrayListWithCapacity(5);
DiseaseValueHolder vh;
for (final Entry<Disease, DiseaseValueHolder> disease : this.diseases.entrySet()) {
vh = disease.getValue();
ret.add(new PlayerDiseaseValueHolder(disease.getKey(), vh.startTime, vh.length));
}
return ret;
}
public final boolean hasDisease(final Disease disease) {
for (final Disease current : this.diseases.keySet()) {
if (current == disease) {
return true;
}
}
return false;
}
public void giveDebuff(final Disease disease, final MobSkill skill) {
final List<DiseaseValue> debuff = Lists.newArrayList(new DiseaseValue(disease, skill.getX()));
if (!this.hasDisease(disease) && this.diseases.size() < 2) {
if (!(disease == Disease.SEDUCE || disease == Disease.STUN)) {
if (this.isActiveBuffedValue(2321005)) {
return;
}
}
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
ChannelCharacter.this.dispelDebuff(disease);
}
}, skill.getDuration());
this.diseases.put(disease, new DiseaseValueHolder(System.currentTimeMillis(), skill.getDuration()));
this.client.write(ChannelPackets.giveDebuff(debuff, skill));
this.map.broadcastMessage(this, ChannelPackets.giveForeignDebuff(this.id, debuff, skill), false);
}
}
public final void giveSilentDebuff(final Collection<PlayerDiseaseValueHolder> ld) {
if (ld != null) {
for (final PlayerDiseaseValueHolder disease : ld) {
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
ChannelCharacter.this.dispelDebuff(disease.Disease);
}
}, disease.Length + disease.StartTime - System.currentTimeMillis());
this.diseases.put(disease.Disease, new DiseaseValueHolder(disease.StartTime, disease.Length));
}
}
}
public void dispelDebuff(final Disease debuff) {
if (this.hasDisease(debuff)) {
final long mask = debuff.getValue();
this.client.write(ChannelPackets.cancelDebuff(mask));
this.map.broadcastMessage(this, ChannelPackets.cancelForeignDebuff(this.id, mask), false);
this.diseases.remove(debuff);
}
}
public void dispelDebuffs() {
this.dispelDebuff(Disease.CURSE);
this.dispelDebuff(Disease.DARKNESS);
this.dispelDebuff(Disease.POISON);
this.dispelDebuff(Disease.SEAL);
this.dispelDebuff(Disease.WEAKEN);
}
public void cancelAllDebuffs() {
this.diseases.clear();
}
public void setLevel(final short level) {
this.level = (short) (level - 1);
}
public void sendNote(final String recepientName, final String message) {
Notes.send(this.name, recepientName, message);
}
public void showNote() {
final List<Note> notes = Notes.loadReceived(this.name);
if (notes.isEmpty()) {
return;
}
MTSCSPacket.showNotes(notes);
}
public void deleteNote(final int noteId) {
Notes.delete(noteId);
}
public void mulung_EnergyModify(final boolean inc) {
if (inc) {
if (this.mulung_energy + 100 > 10000) {
this.mulung_energy = 10000;
} else {
this.mulung_energy += 100;
}
} else {
this.mulung_energy = 0;
}
this.client.write(ChannelPackets.MulungEnergy(this.mulung_energy));
}
public void writeMulungEnergy() {
this.client.write(ChannelPackets.MulungEnergy(this.mulung_energy));
}
public final short getCombo() {
return this.combo;
}
public void setCombo(final short combo) {
this.combo = combo;
}
public final long getLastCombo() {
return this.lastCombo;
}
public void setLastCombo(final long combo) {
this.lastCombo = combo;
}
public final long getKeyDownSkill_Time() {
return this.keydown_skill;
}
public void setKeyDownSkill_Time(final long keydown_skill) {
this.keydown_skill = keydown_skill;
}
public void checkBerserk() {
if (this.BerserkSchedule != null) {
this.BerserkSchedule.cancel(false);
this.BerserkSchedule = null;
}
final ISkill berserkX = SkillInfoProvider.getSkill(1320006);
final int skilllevel = this.getCurrentSkillLevel(berserkX);
if (skilllevel >= 1) {
final StatEffect ampStat = berserkX.getEffect(skilllevel);
if (this.stats.getHp() * 100 / this.stats.getMaxHp() > ampStat.getX()) {
this.berserk = false;
} else {
this.berserk = true;
}
this.client.write(ChannelPackets.showOwnBerserk(skilllevel, this.berserk));
this.map.broadcastMessage(this, ChannelPackets.showBerserk(this.getId(), skilllevel, this.berserk), false);
this.BerserkSchedule = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
ChannelCharacter.this.checkBerserk();
}
}, 10000);
}
}
private void prepareBeholderEffect() {
if (this.beholderHealingSchedule != null) {
this.beholderHealingSchedule.cancel(false);
}
if (this.beholderBuffSchedule != null) {
this.beholderBuffSchedule.cancel(false);
}
final ISkill bHealing = SkillInfoProvider.getSkill(1320008);
final int bHealingLvl = this.getCurrentSkillLevel(bHealing);
if (bHealingLvl > 0) {
final StatEffect healEffect = bHealing.getEffect(bHealingLvl);
final int healInterval = healEffect.getX() * 1000;
this.beholderHealingSchedule = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
ChannelCharacter.this.addHP(healEffect.getHp());
ChannelCharacter.this.client.write(ChannelPackets.showOwnBuffEffect(1321007, 2));
ChannelCharacter.this.map.broadcastMessage(ChannelCharacter.this, ChannelPackets.summonSkill(ChannelCharacter.this.getId(), 1321007, 5),
true);
ChannelCharacter.this.map.broadcastMessage(ChannelCharacter.this, ChannelPackets.showBuffeffect(ChannelCharacter.this.getId(), 1321007, 2),
false);
}
}, healInterval, healInterval);
}
final ISkill bBuff = SkillInfoProvider.getSkill(1320009);
final int bBuffLvl = this.getCurrentSkillLevel(bBuff);
if (bBuffLvl > 0) {
final StatEffect buffEffect = bBuff.getEffect(bBuffLvl);
final int buffInterval = buffEffect.getX() * 1000;
this.beholderBuffSchedule = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
buffEffect.applyTo(ChannelCharacter.this);
ChannelCharacter.this.client.write(ChannelPackets.showOwnBuffEffect(1321007, 2));
ChannelCharacter.this.map.broadcastMessage(ChannelCharacter.this, ChannelPackets.summonSkill(ChannelCharacter.this.getId(), 1321007,
(int) (Math.random() * 3) + 6), true);
ChannelCharacter.this.map.broadcastMessage(ChannelCharacter.this, ChannelPackets.showBuffeffect(ChannelCharacter.this.getId(), 1321007, 2),
false);
}
}, buffInterval, buffInterval);
}
}
public void setChalkboard(final String text) {
this.chalktext = text;
this.map.broadcastMessage(MTSCSPacket.useChalkboard(this.getId(), text));
}
public String getChalkboard() {
return this.chalktext;
}
public Mount getMount() {
return this.mount;
}
public int[] getWishlist() {
return this.wishlist;
}
public void clearWishlist() {
for (int i = 0; i < 10; i++) {
this.wishlist[i] = 0;
}
}
public int getWishlistSize() {
int ret = 0;
for (int i = 0; i < 10; i++) {
if (this.wishlist[i] > 0) {
ret++;
}
}
return ret;
}
public int[] getRocks() {
return this.teleportRocks;
}
public int getRockSize() {
int ret = 0;
for (int i = 0; i < 10; i++) {
if (this.teleportRocks[i] > 0) {
ret++;
}
}
return ret;
}
public void deleteFromRocks(final int map) {
for (int i = 0; i < 10; i++) {
if (this.teleportRocks[i] == map) {
this.teleportRocks[i] = -1;
break;
}
}
}
public void addRockMap() {
if (this.getRockSize() >= 10) {
return;
}
this.teleportRocks[this.getRockSize()] = this.getMapId();
}
public boolean isRockMap(final int id) {
for (int i = 0; i < 10; i++) {
if (this.teleportRocks[i] == id) {
return true;
}
}
return false;
}
public List<LifeMovementFragment> getLastRes() {
return this.lastMovement;
}
public void setLastRes(final List<LifeMovementFragment> lastres) {
this.lastMovement = lastres;
}
public void setMonsterBookCover(final int bookCover) {
this.bookCover = bookCover;
}
public int getMonsterBookCover() {
return this.bookCover;
}
public void sendNotice(final int type, final String message) {
this.client.write(ChannelPackets.serverNotice(type, message));
}
public PlayerShop getPlayerShop() {
return this.playerShop;
}
public void setPlayerShop(final PlayerShop playerShop) {
this.playerShop = playerShop;
}
public int getConversationState() {
return this.conversationState.get();
}
public void setConversationState(final int state) {
this.conversationState.set(state);
}
public CarnivalParty getCarnivalParty() {
return this.carnivalParty;
}
public void setCarnivalParty(final CarnivalParty party) {
this.carnivalParty = party;
}
public void addCP(final int ammount) {
this.totalCP += ammount;
this.availableCP += ammount;
}
public void useCP(final int ammount) {
this.availableCP -= ammount;
}
public int getAvailableCP() {
return this.availableCP;
}
public int getTotalCP() {
return this.totalCP;
}
public void resetCP() {
this.totalCP = 0;
this.availableCP = 0;
}
public void addCarnivalRequest(final CarnivalChallenge request) {
this.pendingCarnivalRequests.add(request);
}
public final CarnivalChallenge getNextCarnivalRequest() {
return this.pendingCarnivalRequests.pollLast();
}
public void clearCarnivalRequests() {
this.pendingCarnivalRequests = Lists.newLinkedList();
}
public void startMonsterCarnival(final int enemyavailable, final int enemytotal) {
this.client.write(MonsterCarnivalPacket.startMonsterCarnival(this, enemyavailable, enemytotal));
}
public void CPUpdate(final boolean party, final int available, final int total, final int team) {
this.client.write(MonsterCarnivalPacket.CPUpdate(party, available, total, team));
}
public void playerDiedCPQ(final String name, final int lostCP, final int team) {
this.client.write(MonsterCarnivalPacket.playerDiedMessage(name, lostCP, team));
}
@Override
public int getSubcategory() {
if (this.jobId >= 430 && this.jobId <= 434) {
return 1; // dont set it
}
return this.subcategory;
}
public int getLinkedMonsterId() {
return this.linkedMonsterId;
}
public void setLinkedMonsterId(final int lm) {
this.linkedMonsterId = lm;
}
public boolean isOnDMG() {
return this.ondmg;
}
public boolean isCallGM() {
return this.callgm;
}
public void setCallGM(final boolean b) {
this.callgm = b;
}
public void setOnDMG(final boolean b) {
this.ondmg = b;
}
public Party getParty() {
final PartyMember member = this.getPartyMembership();
if (member != null) {
try {
return ChannelServer.getWorldInterface().getParty(member.getPartyId());
} catch (final RemoteException e) {
ChannelServer.pingWorld();
}
}
return null;
}
}