|
|
|
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
|
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
|
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
|
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
|
|
|
// Magicbane Emulator Project © 2013 - 2022
|
|
|
|
// www.magicbane.com
|
|
|
|
|
|
|
|
|
|
|
|
package engine.objects;
|
|
|
|
|
|
|
|
import engine.Enum;
|
|
|
|
import engine.Enum.*;
|
|
|
|
import engine.InterestManagement.HeightMap;
|
|
|
|
import engine.InterestManagement.RealmMap;
|
|
|
|
import engine.InterestManagement.WorldGrid;
|
|
|
|
import engine.db.archive.CharacterRecord;
|
|
|
|
import engine.db.archive.DataWarehouse;
|
|
|
|
import engine.db.archive.PvpRecord;
|
|
|
|
import engine.gameManager.*;
|
|
|
|
import engine.jobs.DeferredPowerJob;
|
|
|
|
import engine.math.Bounds;
|
|
|
|
import engine.math.FastMath;
|
|
|
|
import engine.math.Vector3fImmutable;
|
|
|
|
import engine.net.Dispatch;
|
|
|
|
import engine.net.DispatchMessage;
|
|
|
|
import engine.net.client.ClientConnection;
|
|
|
|
import engine.net.client.msg.*;
|
|
|
|
import engine.server.MBServerStatics;
|
|
|
|
import org.pmw.tinylog.Logger;
|
|
|
|
|
|
|
|
import java.sql.ResultSet;
|
|
|
|
import java.sql.SQLException;
|
|
|
|
import java.util.*;
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
import java.util.concurrent.locks.ReadWriteLock;
|
|
|
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
|
|
|
|
|
|
|
|
|
|
public class PlayerCharacter extends AbstractCharacter {
|
|
|
|
|
|
|
|
//This object is to be used as the lock in a synchronized statement
|
|
|
|
//any time the name of a PlayerCharacter needs to be set or
|
|
|
|
//changed. It ensures the uniqueness check and subsequent
|
|
|
|
//database update can happen exclusively.
|
|
|
|
public static final Object FirstNameLock = new Object();
|
|
|
|
public final ReadWriteLock respawnLock = new ReentrantReadWriteLock(true);
|
|
|
|
public final ArrayList<Mob> necroPets = new ArrayList<>();
|
|
|
|
private final Account account;
|
|
|
|
private final Race race;
|
|
|
|
private final byte skinColor;
|
|
|
|
private final byte hairColor;
|
|
|
|
private final byte beardColor;
|
|
|
|
private final byte hairStyle;
|
|
|
|
private final byte beardStyle;
|
|
|
|
//All Guild information should be held here
|
|
|
|
private final AtomicInteger guildStatus;
|
|
|
|
public final AtomicInteger strMod = new AtomicInteger(); // Stat Modifiers
|
|
|
|
public final AtomicInteger dexMod = new AtomicInteger();
|
|
|
|
public final AtomicInteger conMod = new AtomicInteger();
|
|
|
|
public final AtomicInteger intMod = new AtomicInteger();
|
|
|
|
public final AtomicInteger spiMod = new AtomicInteger();
|
|
|
|
private final ReadWriteLock teleportLock = new ReentrantReadWriteLock(true);
|
|
|
|
private final HashMap<Integer, Long> summoners = new HashMap<>();
|
|
|
|
private final HashSet<AbstractWorldObject> loadedObjects = new HashSet<>();
|
|
|
|
private final ConcurrentHashMap<Integer, LinkedList<Long>> chatChanFloodList = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
|
|
|
|
private final ConcurrentHashMap<Integer, Long> killMap = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
|
|
|
|
public final AtomicInteger trainsAvailable = new AtomicInteger(0); // num skill trains not used
|
|
|
|
|
|
|
|
public boolean notDeleted; // <-Use this for deleting character
|
|
|
|
// ===========================================
|
|
|
|
// Variables NOT to put into the database!!!! (session only)
|
|
|
|
// ===========================================
|
|
|
|
public short statStrMax; // Max Base Stats
|
|
|
|
public short statDexMax;
|
|
|
|
public short statConMax;
|
|
|
|
public short statIntMax;
|
|
|
|
public short statSpiMax;
|
|
|
|
public short statStrMin; // Min Base Stats
|
|
|
|
public short statDexMin;
|
|
|
|
public short statConMin;
|
|
|
|
public short statIntMin;
|
|
|
|
public short statSpiMin;
|
|
|
|
// Current Stats before Equip and Effect
|
|
|
|
// Modifiers
|
|
|
|
public short statStrBase;
|
|
|
|
public short statDexBase;
|
|
|
|
public short statConBase;
|
|
|
|
public short statIntBase;
|
|
|
|
public short statSpiBase;
|
|
|
|
public short trainedStatPoints = 0;
|
|
|
|
public boolean isCSR = false;
|
|
|
|
//TODO Public fields break OO!!!
|
|
|
|
public boolean newChar;
|
|
|
|
public LinkedList<Integer> pvpKills;
|
|
|
|
public LinkedList<Integer> pvpDeaths;
|
|
|
|
public int lastBuildingAccessed = 0;
|
|
|
|
public boolean RUN_MAGICTREK = true;
|
|
|
|
public float centerHeight = 0;
|
|
|
|
public float landingAltitude = 0;
|
|
|
|
public int bindBuilding = 0;
|
|
|
|
public FriendStatus friendStatus = FriendStatus.Available;
|
|
|
|
protected ArrayList<CharacterRune> runes;
|
|
|
|
private BaseClass baseClass;
|
|
|
|
public PromotionClass promotionClass;
|
|
|
|
private ConcurrentHashMap<Integer, String> ignoredPlayerIDs = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
|
|
|
|
private boolean lfGroup;
|
|
|
|
private boolean lfGuild;
|
|
|
|
private boolean recruiting = false;
|
|
|
|
private MovementState movementState = MovementState.IDLE;
|
|
|
|
private MovementState lastMovementState = MovementState.IDLE;
|
|
|
|
public int overFlowEXP = 0;
|
|
|
|
private int lastGuildToInvite;
|
|
|
|
private int lastGroupToInvite;
|
|
|
|
private boolean follow = false;
|
|
|
|
private HashSet<AbstractWorldObject> loadedStaticObjects = new HashSet<>();
|
|
|
|
private Vector3fImmutable lastStaticLoc = new Vector3fImmutable(0.0f, 0.0f, 0.0f);
|
|
|
|
private GameObjectType lastTargetType;
|
|
|
|
private int lastTargetID;
|
|
|
|
private int hidden = 0; // current rank of hide/sneak/invis
|
|
|
|
private int seeInvis = 0; // current rank of see invis
|
|
|
|
public float speedMod;
|
|
|
|
private boolean teleportMode = false; // Teleport on MoveToPoint
|
|
|
|
private long lastPlayerAttackTime = 0;
|
|
|
|
public long lastUpdateTime = System.currentTimeMillis();
|
|
|
|
public long lastStamUpdateTime = System.currentTimeMillis();
|
|
|
|
private boolean safeZone = false;
|
|
|
|
private int bindBuildingID;
|
|
|
|
|
|
|
|
/*
|
|
|
|
DataWarehouse based kill/death tracking.
|
|
|
|
These sets contain the last 10 UUID's
|
|
|
|
*/
|
|
|
|
private int lastRealmID = -2;
|
|
|
|
private int subRaceID = 0;
|
|
|
|
private DeferredPowerJob weaponPower;
|
|
|
|
private NPC lastNPCDialog;
|
|
|
|
private Mob pet;
|
|
|
|
//Used for skill/Power calculation optimization
|
|
|
|
public CharacterTitle title = CharacterTitle.NONE;
|
|
|
|
public boolean asciiLastName = true;
|
|
|
|
public boolean initialized = false;
|
|
|
|
public boolean enteredWorld = false;
|
|
|
|
private boolean canBreathe = true;
|
|
|
|
private String hash;
|
|
|
|
private ArrayList<GuildHistory> guildHistory = new ArrayList<>();
|
|
|
|
private boolean wasTripped75 = false;
|
|
|
|
private boolean wasTripped50 = false;
|
|
|
|
private boolean wasTripped25 = false;
|
|
|
|
private float characterHeight = 0;
|
|
|
|
public boolean lastSwimming = false;
|
|
|
|
public boolean dirtyLoad = true;
|
|
|
|
public final ReadWriteLock dirtyLock = new ReentrantReadWriteLock(true);
|
|
|
|
|
|
|
|
public float ZergMultiplier = 1.0f;
|
|
|
|
|
|
|
|
public boolean isBoxed = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* No Id Constructor
|
|
|
|
*/
|
|
|
|
public PlayerCharacter(String firstName, String lastName, short strMod, short dexMod, short conMod, short intMod,
|
|
|
|
short spiMod, Guild guild, byte runningTrains, Account account, Race race, BaseClass baseClass, byte skinColor, byte hairColor,
|
|
|
|
byte beardColor, byte hairStyle, byte beardStyle) {
|
|
|
|
super(firstName, lastName, (short) 1, (short) 1, (short) 1, (short) 1, (short) 1, (short) 1, 0,
|
|
|
|
Vector3fImmutable.ZERO, Vector3fImmutable.ZERO,
|
|
|
|
guild, runningTrains);
|
|
|
|
|
|
|
|
this.runes = new ArrayList<>();
|
|
|
|
this.account = account;
|
|
|
|
this.notDeleted = true;
|
|
|
|
this.race = race;
|
|
|
|
this.baseClass = baseClass;
|
|
|
|
this.skinColor = skinColor;
|
|
|
|
this.hairColor = hairColor;
|
|
|
|
this.beardColor = beardColor;
|
|
|
|
this.hairStyle = hairStyle;
|
|
|
|
this.beardStyle = beardStyle;
|
|
|
|
this.lfGroup = false;
|
|
|
|
this.lfGuild = false;
|
|
|
|
|
|
|
|
this.strMod.set(strMod);
|
|
|
|
this.dexMod.set(dexMod);
|
|
|
|
this.conMod.set(conMod);
|
|
|
|
this.intMod.set(intMod);
|
|
|
|
this.spiMod.set(spiMod);
|
|
|
|
|
|
|
|
this.guildStatus = new AtomicInteger(0);
|
|
|
|
this.bindBuildingID = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ResultSet Constructor
|
|
|
|
*/
|
|
|
|
public PlayerCharacter(ResultSet rs) throws SQLException {
|
|
|
|
super(rs, true);
|
|
|
|
|
|
|
|
this.runes = DbManager.CharacterRuneQueries.GET_RUNES_FOR_CHARACTER(this.getObjectUUID());
|
|
|
|
int accountID = rs.getInt("parent");
|
|
|
|
this.account = DbManager.AccountQueries.GET_ACCOUNT(accountID);
|
|
|
|
this.gridObjectType = GridObjectType.DYNAMIC;
|
|
|
|
|
|
|
|
this.notDeleted = rs.getBoolean("char_isActive");
|
|
|
|
|
|
|
|
int raceID = rs.getInt("char_raceID");
|
|
|
|
this.race = Race.getRace(raceID);
|
|
|
|
|
|
|
|
int baseClassID = rs.getInt("char_baseClassID");
|
|
|
|
this.baseClass = DbManager.BaseClassQueries.GET_BASE_CLASS(baseClassID);
|
|
|
|
|
|
|
|
int promotionClassID = rs.getInt("char_promotionClassID");
|
|
|
|
this.promotionClass = DbManager.PromotionQueries.GET_PROMOTION_CLASS(promotionClassID);
|
|
|
|
|
|
|
|
this.skinColor = rs.getByte("char_skinColor");
|
|
|
|
this.hairColor = rs.getByte("char_hairColor");
|
|
|
|
this.beardColor = rs.getByte("char_beardColor");
|
|
|
|
this.hairStyle = rs.getByte("char_hairStyle");
|
|
|
|
this.beardStyle = rs.getByte("char_beardStyle");
|
|
|
|
|
|
|
|
this.lfGroup = false;
|
|
|
|
this.lfGuild = false;
|
|
|
|
|
|
|
|
//TODO Unify game object with database after DB overhaul
|
|
|
|
this.guildStatus = new AtomicInteger(0);
|
|
|
|
|
|
|
|
Guild guild = Guild.getGuild(this.getGuildUUID());
|
|
|
|
PlayerManager.setGuildLeader(this, guild != null && guild.isGuildLeader(this.getObjectUUID()));
|
|
|
|
|
|
|
|
boolean hasAnniversery = rs.getBoolean("anniversery");
|
|
|
|
|
|
|
|
PlayerManager.setInnerCouncil(this, rs.getBoolean("guild_isInnerCouncil"));
|
|
|
|
PlayerManager.setFullMember(this, rs.getBoolean("guild_isFullMember"));
|
|
|
|
PlayerManager.setTaxCollector(this, rs.getBoolean("guild_isTaxCollector"));
|
|
|
|
PlayerManager.setRecruiter(this, rs.getBoolean("guild_isRecruiter"));
|
|
|
|
PlayerManager.setGuildTitle(this, rs.getInt("guild_title"));
|
|
|
|
|
|
|
|
if (this.account != null)
|
|
|
|
this.ignoredPlayerIDs = DbManager.PlayerCharacterQueries.GET_IGNORE_LIST(this.account.getObjectUUID(), false);
|
|
|
|
|
|
|
|
this.strMod.set(rs.getShort("char_strMod"));
|
|
|
|
this.dexMod.set(rs.getShort("char_dexMod"));
|
|
|
|
this.conMod.set(rs.getShort("char_conMod"));
|
|
|
|
this.intMod.set(rs.getShort("char_intMod"));
|
|
|
|
this.spiMod.set(rs.getShort("char_spiMod"));
|
|
|
|
|
|
|
|
this.bindBuildingID = rs.getInt("char_bindBuilding");
|
|
|
|
|
|
|
|
this.hash = rs.getString("hash");
|
|
|
|
|
|
|
|
|
|
|
|
// For debugging skills
|
|
|
|
// CharacterSkill.printSkills(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Getters
|
|
|
|
*/
|
|
|
|
public byte getHairStyle() {
|
|
|
|
return hairStyle;
|
|
|
|
}
|
|
|
|
|
|
|
|
public byte getBeardStyle() {
|
|
|
|
return beardStyle;
|
|
|
|
}
|
|
|
|
|
|
|
|
public DeferredPowerJob getWeaponPower() {
|
|
|
|
return this.weaponPower;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setWeaponPower(DeferredPowerJob value) {
|
|
|
|
this.weaponPower = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setSafeZone(boolean value) {
|
|
|
|
this.safeZone = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean inSafeZone() {
|
|
|
|
return this.safeZone;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isInSafeZone() {
|
|
|
|
|
|
|
|
Zone zone = ZoneManager.findSmallestZone(this.getLoc());
|
|
|
|
|
|
|
|
if (zone != null) {
|
|
|
|
return zone.getSafeZone() == (byte) 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
//return this.safeZone;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the account
|
|
|
|
*/
|
|
|
|
public Account getAccount() {
|
|
|
|
return account;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void deactivateCharacter() {
|
|
|
|
this.notDeleted = false;
|
|
|
|
DbManager.PlayerCharacterQueries.SET_DELETED(this);
|
|
|
|
DbManager.removeFromCache(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void activateCharacter() {
|
|
|
|
this.notDeleted = true;
|
|
|
|
DbManager.PlayerCharacterQueries.SET_DELETED(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isDeleted() {
|
|
|
|
return !this.notDeleted;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ArrayList<CharacterRune> getRunes() {
|
|
|
|
return this.runes;
|
|
|
|
}
|
|
|
|
|
|
|
|
public CharacterRune getRune(int runeID) {
|
|
|
|
if (this.runes == null)
|
|
|
|
return null;
|
|
|
|
for (CharacterRune cr : this.runes) {
|
|
|
|
if (cr.getRuneBase() != null && cr.getRuneBase().getObjectUUID() == runeID)
|
|
|
|
return cr;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean addRune(CharacterRune value) {
|
|
|
|
if (this.runes.size() > 12) // Max Runes
|
|
|
|
return false;
|
|
|
|
if (this.runes.indexOf(value) != -1) // Already contains rune
|
|
|
|
return false;
|
|
|
|
this.runes.add(value);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean removeRune(CharacterRune value) {
|
|
|
|
int index = this.runes.indexOf(value);
|
|
|
|
if (index == -1)
|
|
|
|
return false;
|
|
|
|
this.runes.remove(index);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public CharacterRune removeRune(int runeID) {
|
|
|
|
Iterator<CharacterRune> it = this.runes.iterator();
|
|
|
|
while (it.hasNext()) {
|
|
|
|
CharacterRune cr = it.next();
|
|
|
|
if (cr != null) {
|
|
|
|
RuneBase rb = cr.getRuneBase();
|
|
|
|
if (rb != null)
|
|
|
|
if (runeID == rb.getObjectUUID()) {
|
|
|
|
it.remove();
|
|
|
|
DbManager.CharacterRuneQueries.DELETE_CHARACTER_RUNE(cr);
|
|
|
|
return cr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ Kill this Character
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public void killCharacter(AbstractCharacter attacker) {
|
|
|
|
|
|
|
|
killCleanup();
|
|
|
|
|
|
|
|
// *** Mobs have a separate combat path? Crazy shit!
|
|
|
|
// *** Mobs don't get Experience for killing players. everything else is done in killCleanup();
|
|
|
|
|
|
|
|
if (attacker.getObjectType().equals(GameObjectType.PlayerCharacter) == false) {
|
|
|
|
|
|
|
|
Zone zone = ZoneManager.findSmallestZone(this.getLoc());
|
|
|
|
|
|
|
|
//DeathShroud
|
|
|
|
|
|
|
|
if (zone.getSafeZone() == 0)
|
|
|
|
PowersManager.applyPower(this, this, Vector3fImmutable.ZERO, 1672601862, 40, false);
|
|
|
|
|
|
|
|
//enable this to give players deathshroud if mobs kill player.
|
|
|
|
|
|
|
|
// Zone zone = ZoneManager.findSmallestZone(this.getLoc());
|
|
|
|
// if (zone.getSafeZone() == 0)
|
|
|
|
// PowersManager.applyPower(this, this, Vector3fImmutable.ZERO, 1672601862, 40, false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Death to other player.
|
|
|
|
// TODO Send PvP and guild/nation message
|
|
|
|
PlayerCharacter att = (PlayerCharacter) attacker;
|
|
|
|
String message = this.getFirstName();
|
|
|
|
if (this.guild != null && (!(this.guild.getName().equals("Errant"))))
|
|
|
|
message += " of " + this.guild.getName();
|
|
|
|
message += " was killed by " + att.getFirstName();
|
|
|
|
if (att.guild != null && (!(att.guild.getName().equals("Errant"))))
|
|
|
|
message += " of " + att.guild.getName();
|
|
|
|
message += "!";
|
|
|
|
|
|
|
|
|
|
|
|
//see if we shold grant xp to attacker
|
|
|
|
boolean doPVPEXP = false;
|
|
|
|
long lastKill = att.getLastKillOfTarget(this.getObjectUUID());
|
|
|
|
//if ((System.currentTimeMillis() - lastKill) > MBServerStatics.PLAYER_KILL_XP_TIMER)
|
|
|
|
//if (attacker.getLevel() > 39 && this.getLevel() > 39) {
|
|
|
|
//Guild aN = null;
|
|
|
|
//Guild tN = null;
|
|
|
|
//if (attacker.getGuild() != null)
|
|
|
|
// aN = attacker.getGuild().getNation();
|
|
|
|
//if (this.getGuild() != null)
|
|
|
|
// tN = this.getGuild().getNation();
|
|
|
|
//if (aN == null || tN == null || aN.isEmptyGuild() || Guild.sameGuild(aN, tN) || this.isDeathShroud()) {
|
|
|
|
//skip giving xp if same guild or attacker is errant, or target is in death shroud.
|
|
|
|
//} else {
|
|
|
|
doPVPEXP = true;
|
|
|
|
//}
|
|
|
|
// }
|
|
|
|
//apply death shroud to non safeholds.
|
|
|
|
Zone zone = ZoneManager.findSmallestZone(this.getLoc());
|
|
|
|
|
|
|
|
//DeathShroud
|
|
|
|
|
|
|
|
if (zone != null && zone.getSafeZone() == 0)
|
|
|
|
PowersManager.applyPower(this, this, Vector3fImmutable.ZERO, 1672601862, 40, false);
|
|
|
|
|
|
|
|
if (doPVPEXP) {
|
|
|
|
Group g = GroupManager.getGroup((PlayerCharacter) attacker);
|
|
|
|
Experience.doExperience((PlayerCharacter) attacker, this, g);
|
|
|
|
}
|
|
|
|
|
|
|
|
ChatManager.chatPVP(message);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Update kill / death tracking lists
|
|
|
|
Each character on list is unique. Only once!
|
|
|
|
*/
|
|
|
|
|
|
|
|
PlayerCharacter aggressorCharacter = (PlayerCharacter) attacker;
|
|
|
|
|
|
|
|
boolean containsVictim = true;
|
|
|
|
boolean containsAttacker = true;
|
|
|
|
|
|
|
|
containsVictim = aggressorCharacter.pvpKills.contains(this.getObjectUUID());
|
|
|
|
containsAttacker = aggressorCharacter.pvpKills.contains(this.getObjectUUID());
|
|
|
|
|
|
|
|
// Rorate attacker's kill list
|
|
|
|
|
|
|
|
if ((aggressorCharacter.pvpKills.size() == 10) && containsVictim == false)
|
|
|
|
aggressorCharacter.pvpKills.removeLast();
|
|
|
|
|
|
|
|
if (containsVictim == false)
|
|
|
|
aggressorCharacter.pvpKills.addFirst(this.getObjectUUID());
|
|
|
|
|
|
|
|
// Rotate the poor victim's deathlist
|
|
|
|
|
|
|
|
if ((this.pvpDeaths.size() == 10) && containsAttacker == false)
|
|
|
|
this.pvpDeaths.removeLast();
|
|
|
|
|
|
|
|
if (containsAttacker == false)
|
|
|
|
this.pvpDeaths.addFirst(this.getObjectUUID());
|
|
|
|
|
|
|
|
// DataWarehouse: store pvp event
|
|
|
|
|
|
|
|
PvpRecord pvpRecord = PvpRecord.borrow((PlayerCharacter) attacker, this, this.getLoc(), doPVPEXP);
|
|
|
|
DataWarehouse.pushToWarehouse(pvpRecord);
|
|
|
|
|
|
|
|
// Mark kill time in killmap
|
|
|
|
|
|
|
|
att.updateKillMap(this.getObjectUUID());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void killCharacter(String reason) {
|
|
|
|
|
|
|
|
killCleanup();
|
|
|
|
Zone zone = ZoneManager.findSmallestZone(this.getLoc());
|
|
|
|
|
|
|
|
if (zone.getSafeZone() == 0)
|
|
|
|
PowersManager.applyPower(this, this, Vector3fImmutable.ZERO, 1672601862, 40, false);
|
|
|
|
|
|
|
|
// Send death message if needed
|
|
|
|
if (reason.equals("Water")) {
|
|
|
|
|
|
|
|
TargetedActionMsg targetedActionMsg = new TargetedActionMsg(this, true);
|
|
|
|
Dispatch dispatch = Dispatch.borrow(this, targetedActionMsg);
|
|
|
|
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
|
|
|
|
|
|
|
|
String message = this.getFirstName();
|
|
|
|
|
|
|
|
if (this.guild != null && (!(this.guild.getName().equals("Errant"))))
|
|
|
|
message += " of " + this.guild.getName();
|
|
|
|
else
|
|
|
|
message += this.getLastName();
|
|
|
|
message += " was killed by water!";
|
|
|
|
|
|
|
|
ChatManager.chatPVP(message);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void killCleanup() {
|
|
|
|
this.stopMovement(this.getLoc());
|
|
|
|
|
|
|
|
this.health.set(-1);
|
|
|
|
//remove pet
|
|
|
|
if (this.pet != null)
|
|
|
|
this.dismissPet();
|
|
|
|
|
|
|
|
NPCManager.dismissNecroPets(this);
|
|
|
|
// remove flight job.
|
|
|
|
|
|
|
|
this.setTakeOffTime(0);
|
|
|
|
this.setDesiredAltitude(0);
|
|
|
|
this.altitude = (float) 0;
|
|
|
|
|
|
|
|
// Release Mine Claims
|
|
|
|
|
|
|
|
Mine.releaseMineClaims(this);
|
|
|
|
|
|
|
|
this.getCharItemManager().closeTradeWindow();
|
|
|
|
|
|
|
|
//increment live counter. This is to prevent double kills from casts
|
|
|
|
this.liveCounter++;
|
|
|
|
|
|
|
|
//remove any effects
|
|
|
|
try {
|
|
|
|
this.clearEffects();
|
|
|
|
} catch (Exception e) {
|
|
|
|
Logger.error("PlayerCharacter.KillCleanup", e.getMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
//remove the SIT flag
|
|
|
|
this.setSit(false);
|
|
|
|
|
|
|
|
|
|
|
|
// sends a kill message to ensure the Player falls over.
|
|
|
|
|
|
|
|
this.respawnLock.writeLock().lock();
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (SessionManager.getPlayerCharacterByID(this.getObjectUUID()) == null && !this.enteredWorld) {
|
|
|
|
WorldGrid.RemoveWorldObject(this);
|
|
|
|
PlayerManager.respawn(this, false, false, true);
|
|
|
|
} else {
|
|
|
|
TargetedActionMsg killmsg = new TargetedActionMsg(this, true);
|
|
|
|
DispatchMessage.dispatchMsgToInterestArea(this, killmsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
Logger.error(e);
|
|
|
|
} finally {
|
|
|
|
this.respawnLock.writeLock().unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO damage equipped items
|
|
|
|
if (this.charItemManager != null)
|
|
|
|
this.charItemManager.damageAllGear();
|
|
|
|
|
|
|
|
// TODO cleanup any timers
|
|
|
|
//recalculate inventory weights
|
|
|
|
if (this.charItemManager != null) {
|
|
|
|
this.charItemManager.endTrade(true);
|
|
|
|
this.charItemManager.calculateWeights();
|
|
|
|
this.charItemManager.updateInventory();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public void updateKillMap(int target) {
|
|
|
|
this.killMap.put(target, System.currentTimeMillis());
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getLastKillOfTarget(int target) {
|
|
|
|
if (this.killMap.containsKey(target))
|
|
|
|
return this.killMap.get(target);
|
|
|
|
return 0L;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isDeathShroud() {
|
|
|
|
return this.effects != null && this.effects.containsKey("DeathShroud");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setSafeMode() {
|
|
|
|
PowersManager.applyPower(this, this, Vector3fImmutable.ZERO, -1661758934, 40, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean safemodeInvis() {
|
|
|
|
|
|
|
|
if (!this.effects.containsKey("Invisible"))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Effect eff = this.effects.get("Invisible");
|
|
|
|
|
|
|
|
if (eff == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return eff.getEffectToken() == -1661751254;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the race
|
|
|
|
*/
|
|
|
|
public Race getRace() {
|
|
|
|
return race;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getRaceID() {
|
|
|
|
if (race != null)
|
|
|
|
return race.getRaceRuneID();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the baseClass
|
|
|
|
*/
|
|
|
|
public BaseClass getBaseClass() {
|
|
|
|
return baseClass;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getBaseClassID() {
|
|
|
|
if (baseClass != null)
|
|
|
|
return baseClass.getObjectUUID();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getBaseClassToken() {
|
|
|
|
if (this.baseClass == null)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return this.baseClass.getToken();
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean setBaseClass(int value) {
|
|
|
|
BaseClass bs = BaseClass.getBaseClass(value);
|
|
|
|
if (bs != null) {
|
|
|
|
this.baseClass = bs;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Vector3fImmutable getBindLoc() {
|
|
|
|
|
|
|
|
Vector3fImmutable bindLocation;
|
|
|
|
|
|
|
|
// Return garbage and early exit if this is the login server.
|
|
|
|
// getBindLoc() does a TOL lookup, which also then loads the
|
|
|
|
// city and other garbage not needed on the login server.
|
|
|
|
|
|
|
|
if (ConfigManager.serverType.equals(ServerType.LOGINSERVER))
|
|
|
|
return Vector3fImmutable.ZERO;
|
|
|
|
|
|
|
|
Building bindBuilding = PlayerManager.getUpdatedBindBuilding(this);
|
|
|
|
|
|
|
|
//handle rented room binds.
|
|
|
|
|
|
|
|
if (bindBuilding == null) {
|
|
|
|
bindLocation = Enum.Ruins.getRandomRuin().getLocation();
|
|
|
|
return bindLocation;
|
|
|
|
}
|
|
|
|
|
|
|
|
bindLocation = BuildingManager.GetBindLocationForBuilding(bindBuilding);
|
|
|
|
|
|
|
|
if (bindLocation == null)
|
|
|
|
bindLocation = Enum.Ruins.getRandomRuin().getLocation();
|
|
|
|
|
|
|
|
if(this.guild.getNation().equals(Guild.getErrantGuild())){
|
|
|
|
bindLocation = Vector3fImmutable.getRandomPointOnCircle(BuildingManager.getBuilding(27977).loc,20f);
|
|
|
|
}
|
|
|
|
|
|
|
|
return bindLocation;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getInventoryCapacity() {
|
|
|
|
return statStrBase * 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getInventoryCapacityRemaining() {
|
|
|
|
return (this.getInventoryCapacity() - this.charItemManager.getInventoryWeight());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the PromotionClass
|
|
|
|
*/
|
|
|
|
public PromotionClass getPromotionClass() {
|
|
|
|
return promotionClass;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getPromotionClassID() {
|
|
|
|
if (promotionClass != null)
|
|
|
|
return promotionClass.getObjectUUID();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean setPromotionClass(int value) {
|
|
|
|
|
|
|
|
PromotionClass promotionClass = PromotionClass.GetPromtionClassFromCache(value);
|
|
|
|
|
|
|
|
if (promotionClass == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
if (!DbManager.PlayerCharacterQueries.SET_PROMOTION_CLASS(this, value))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
this.promotionClass = promotionClass;
|
|
|
|
|
|
|
|
// Warehouse this event
|
|
|
|
CharacterRecord.updatePromotionClass(this);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the skinColor
|
|
|
|
*/
|
|
|
|
public byte getSkinColor() {
|
|
|
|
return skinColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the hairColor
|
|
|
|
*/
|
|
|
|
public byte getHairColor() {
|
|
|
|
return hairColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the beardColor
|
|
|
|
*/
|
|
|
|
public byte getBeardColor() {
|
|
|
|
return beardColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getIsLfGroupAsInt() {
|
|
|
|
if (lfGroup)
|
|
|
|
return 2;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final void toggleLFGroup() {
|
|
|
|
this.lfGroup = !this.lfGroup;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final void toggleLFGuild() {
|
|
|
|
this.lfGuild = !this.lfGuild;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final void toggleRecruiting() {
|
|
|
|
this.recruiting = !this.recruiting;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final boolean isLFGroup() {
|
|
|
|
return this.lfGroup;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final boolean isLFGuild() {
|
|
|
|
return this.lfGuild;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final boolean isRecruiting() {
|
|
|
|
return this.recruiting;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final int getHeadlightsAsInt() {
|
|
|
|
if (this.lfGroup)
|
|
|
|
if (this.lfGuild)
|
|
|
|
if (this.recruiting)
|
|
|
|
return 14; // LFGroup + LFGuild + Recruiting
|
|
|
|
else
|
|
|
|
return 6; // LFGroup + LFGuild
|
|
|
|
else if (this.recruiting)
|
|
|
|
return 10; // LFGroup + Recruiting
|
|
|
|
else
|
|
|
|
return 2; // LFGroup only
|
|
|
|
else if (this.lfGuild)
|
|
|
|
if (this.recruiting)
|
|
|
|
return 12; // LFGuild + Recruiting
|
|
|
|
else
|
|
|
|
return 4; // LFGuild only
|
|
|
|
else if (this.recruiting)
|
|
|
|
return 8; // Recruiting only
|
|
|
|
else
|
|
|
|
return 0; // No Headlights
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getStrMax() {
|
|
|
|
return this.statStrMax;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getDexMax() {
|
|
|
|
return this.statDexMax;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getConMax() {
|
|
|
|
return this.statConMax;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getIntMax() {
|
|
|
|
return this.statIntMax;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getSpiMax() {
|
|
|
|
return this.statSpiMax;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setLastTarget(GameObjectType type, int id) {
|
|
|
|
this.lastTargetType = type;
|
|
|
|
this.lastTargetID = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
public GameObjectType getLastTargetType() {
|
|
|
|
return this.lastTargetType;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getLastTargetID() {
|
|
|
|
return this.lastTargetID;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Serializing
|
|
|
|
*/
|
|
|
|
|
|
|
|
public synchronized int getBindBuildingID() {
|
|
|
|
return this.bindBuildingID;
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized void setBindBuildingID(int value) {
|
|
|
|
DbManager.PlayerCharacterQueries.SET_BIND_BUILDING(this, value);
|
|
|
|
this.bindBuildingID = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public AbstractGameObject getLastTarget() {
|
|
|
|
if (this.lastTargetType == GameObjectType.unknown)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
switch (this.lastTargetType) {
|
|
|
|
// Make sure these only return an object that is
|
|
|
|
// already in the GOM, and doesn't reload from the DB
|
|
|
|
case PlayerCharacter:
|
|
|
|
return DbManager.getFromCache(GameObjectType.PlayerCharacter, this.lastTargetID);
|
|
|
|
|
|
|
|
case Building:
|
|
|
|
return DbManager.getFromCache(GameObjectType.Building, this.lastTargetID);
|
|
|
|
|
|
|
|
case NPC:
|
|
|
|
return NPC.getFromCache(this.lastTargetID);
|
|
|
|
|
|
|
|
case Mob:
|
|
|
|
return Mob.getFromCache(this.lastTargetID);
|
|
|
|
|
|
|
|
case Item:
|
|
|
|
return DbManager.getFromCache(GameObjectType.Item, this.lastTargetID);
|
|
|
|
|
|
|
|
case Corpse:
|
|
|
|
return DbManager.getFromCache(GameObjectType.Corpse, this.lastTargetID);
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
// Ignore exception for MobLoot? ***Check
|
|
|
|
if (this.lastTargetType != GameObjectType.MobLoot)
|
|
|
|
Logger.error("getLastTarget() unhandled object type: "
|
|
|
|
+ this.lastTargetType.toString());
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Vector3fImmutable getLastStaticLoc() {
|
|
|
|
return this.lastStaticLoc;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setLastStaticLoc(Vector3fImmutable value) {
|
|
|
|
this.lastStaticLoc = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getHidden() {
|
|
|
|
return this.hidden;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setHidden(int value) {
|
|
|
|
this.hidden = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getSeeInvis() {
|
|
|
|
if (this.getDebug(8)) //<-added for see invis debug devcmd
|
|
|
|
return 10000;
|
|
|
|
return this.seeInvis;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setSeeInvis(int value) {
|
|
|
|
this.seeInvis = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getLastPlayerAttackTime() {
|
|
|
|
return this.lastPlayerAttackTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setLastPlayerAttackTime() {
|
|
|
|
this.lastPlayerAttackTime = System.currentTimeMillis();
|
|
|
|
}
|
|
|
|
|
|
|
|
public NPC getLastNPCDialog() {
|
|
|
|
return this.lastNPCDialog;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setLastNPCDialog(NPC value) {
|
|
|
|
this.lastNPCDialog = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public Mob getPet() {
|
|
|
|
return this.pet;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setPet(Mob mob) {
|
|
|
|
|
|
|
|
if (mob == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.pet = mob;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void dismissPet() {
|
|
|
|
|
|
|
|
if (this.pet != null) {
|
|
|
|
this.pet.dismiss();
|
|
|
|
this.pet = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//called to verify player has correct item equipped for casting.
|
|
|
|
public boolean validEquip(int slot, String type) {
|
|
|
|
|
|
|
|
if (this.charItemManager == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Item item = this.charItemManager.getEquipped(slot);
|
|
|
|
|
|
|
|
if (item == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ItemBase ib = item.getItemBase();
|
|
|
|
if (ib != null) {
|
|
|
|
|
|
|
|
if ((ib.getType().equals(ItemType.WEAPON))
|
|
|
|
&& (ib.getSkillRequired().equals(type) || ib.getMastery().equals(type)))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return (ib.getType().equals(ItemType.ARMOR))
|
|
|
|
&& (ib.getSkillRequired().equals(type));
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public short getPCLevel() {
|
|
|
|
short level = (short) Experience.getLevel(this.exp);
|
|
|
|
if (this.promotionClass == null && level >= 10)
|
|
|
|
return (short) 10;
|
|
|
|
|
|
|
|
if (this.overFlowEXP > 0)
|
|
|
|
return this.level;
|
|
|
|
|
|
|
|
return level;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public float getSpeed() {
|
|
|
|
|
|
|
|
float speed;
|
|
|
|
|
|
|
|
if (PlayerManager.isFlying(this))
|
|
|
|
if (this.walkMode) {
|
|
|
|
speed = race.getRaceType().getRunSpeed().getFlyWalk();
|
|
|
|
} else {
|
|
|
|
speed = race.getRaceType().getRunSpeed().getFlyRun();
|
|
|
|
}
|
|
|
|
else if (this.lastSwimming == true)
|
|
|
|
speed = MBServerStatics.SWIMSPEED;
|
|
|
|
else if (this.walkMode) {
|
|
|
|
if (this.isCombat())
|
|
|
|
speed = race.getRaceType().getRunSpeed().getWalkCombat();
|
|
|
|
else
|
|
|
|
speed = race.getRaceType().getRunSpeed().getWalkStandard();
|
|
|
|
} else {
|
|
|
|
if (this.isCombat())
|
|
|
|
speed = race.getRaceType().getRunSpeed().getRunCombat();
|
|
|
|
else
|
|
|
|
speed = race.getRaceType().getRunSpeed().getRunStandard();
|
|
|
|
}
|
|
|
|
|
|
|
|
float mod = this.speedMod;
|
|
|
|
float endSpeed = speed * mod;
|
|
|
|
|
|
|
|
if (endSpeed > 41 && !this.isCSR)
|
|
|
|
endSpeed = 41;
|
|
|
|
|
|
|
|
return endSpeed;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ClientConnection getClientConnection() {
|
|
|
|
return SessionManager.getClientConnection(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getCombinedName() {
|
|
|
|
return this.getName();
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getLastGuildToInvite() {
|
|
|
|
return this.lastGuildToInvite;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setLastGuildToInvite(int value) {
|
|
|
|
this.lastGuildToInvite = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean getFollow() {
|
|
|
|
return this.follow;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setFollow(boolean value) {
|
|
|
|
this.follow = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean toggleFollow() {
|
|
|
|
this.follow = !this.follow;
|
|
|
|
return this.follow;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getLastGroupToInvite() {
|
|
|
|
return this.lastGroupToInvite;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setLastGroupToInvite(int value) {
|
|
|
|
this.lastGroupToInvite = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public float getAltitude() {
|
|
|
|
if (this.altitude < 0)
|
|
|
|
this.altitude = 0;
|
|
|
|
|
|
|
|
//player has reached desired altitude, return normal altitude.
|
|
|
|
if (this.getTakeOffTime() == 0)
|
|
|
|
return this.altitude;
|
|
|
|
|
|
|
|
//sanity check if desired altitude is the same as current altitude. return desired altitude.
|
|
|
|
if (this.altitude == this.getDesiredAltitude()) {
|
|
|
|
return this.getDesiredAltitude();
|
|
|
|
}
|
|
|
|
|
|
|
|
//calculate how much the player has moved up
|
|
|
|
float amountMoved = (System.currentTimeMillis() - this.getTakeOffTime()) * MBServerStatics.FLY_RATE; //FUCK DIVIDING
|
|
|
|
|
|
|
|
//Player is moving up
|
|
|
|
if (this.getDesiredAltitude() > this.altitude) {
|
|
|
|
|
|
|
|
//if amount moved passed desiredAltitude, return the desired altitude.
|
|
|
|
if (this.altitude + amountMoved >= this.getDesiredAltitude())
|
|
|
|
return this.getDesiredAltitude();
|
|
|
|
|
|
|
|
return this.altitude + amountMoved;
|
|
|
|
//Player is moving down
|
|
|
|
} else {
|
|
|
|
//if amount moved passed desiredAltitude, return the desired altitude.
|
|
|
|
if (this.altitude - amountMoved <= this.getDesiredAltitude())
|
|
|
|
return this.getDesiredAltitude();
|
|
|
|
return this.altitude - amountMoved;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setAltitude(float value) {
|
|
|
|
this.altitude = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public HashSet<AbstractWorldObject> getLoadedObjects() {
|
|
|
|
return this.loadedObjects;
|
|
|
|
}
|
|
|
|
|
|
|
|
public HashSet<AbstractWorldObject> getLoadedStaticObjects() {
|
|
|
|
return this.loadedStaticObjects;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isTeleportMode() {
|
|
|
|
return teleportMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setTeleportMode(boolean teleportMode) {
|
|
|
|
this.teleportMode = teleportMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long chatFloodTime(int chatOpcode, long chatTimeMilli, int qtyToSave) {
|
|
|
|
if (qtyToSave < 1)
|
|
|
|
return 0L; // disabled
|
|
|
|
LinkedList<Long> times = null;
|
|
|
|
long oldestTime;
|
|
|
|
synchronized (chatChanFloodList) {
|
|
|
|
if (!chatChanFloodList.containsKey(chatOpcode)) {
|
|
|
|
times = new LinkedList<>();
|
|
|
|
for (int i = 0; i < qtyToSave; i++) {
|
|
|
|
times.add(0L);
|
|
|
|
}
|
|
|
|
chatChanFloodList.put(chatOpcode, times);
|
|
|
|
} else
|
|
|
|
times = chatChanFloodList.get(chatOpcode);
|
|
|
|
oldestTime = times.getLast();
|
|
|
|
times.removeLast();
|
|
|
|
times.addFirst(chatTimeMilli);
|
|
|
|
}
|
|
|
|
return oldestTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addIgnoredPlayer(Account ac, String name) {
|
|
|
|
if (ac == null)
|
|
|
|
return;
|
|
|
|
int acID = ac.getObjectUUID();
|
|
|
|
if (acID < 1)
|
|
|
|
return;
|
|
|
|
if (ignoredPlayerIDs == null)
|
|
|
|
return;
|
|
|
|
if (acID == getObjectUUID())
|
|
|
|
return; // yourself
|
|
|
|
|
|
|
|
ignoredPlayerIDs.put(acID, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void removeIgnoredPlayer(Account ac) {
|
|
|
|
if (ac == null)
|
|
|
|
return;
|
|
|
|
int acID = ac.getObjectUUID();
|
|
|
|
if (acID < 1)
|
|
|
|
return;
|
|
|
|
if (ignoredPlayerIDs == null)
|
|
|
|
return;
|
|
|
|
if (acID == getObjectUUID())
|
|
|
|
return; // yourself
|
|
|
|
|
|
|
|
ignoredPlayerIDs.remove(acID);
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isIgnoringPlayer(PlayerCharacter pc) {
|
|
|
|
|
|
|
|
if (pc == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (pc.account == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return isIgnoringPlayer(pc.account);
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isIgnoringPlayer(Account ac) {
|
|
|
|
if (ac == null)
|
|
|
|
return false;
|
|
|
|
int acID = ac.getObjectUUID();
|
|
|
|
if (acID < 1)
|
|
|
|
return false;
|
|
|
|
return ignoredPlayerIDs.containsKey(acID);
|
|
|
|
}
|
|
|
|
|
|
|
|
public String[] getIgnoredPlayerNames() {
|
|
|
|
int size = ignoredPlayerIDs.size();
|
|
|
|
String[] ary = new String[size];
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
// ary[i] = PlayerCharacter.getFirstName(ignoredPlayerIDs.get(i));
|
|
|
|
ary[i] = ignoredPlayerIDs.get(i);
|
|
|
|
}
|
|
|
|
return ary;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getStrMod() {
|
|
|
|
return this.strMod.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getDexMod() {
|
|
|
|
return this.dexMod.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getConMod() {
|
|
|
|
return this.conMod.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getIntMod() {
|
|
|
|
return this.intMod.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getSpiMod() {
|
|
|
|
return this.spiMod.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isMale() {
|
|
|
|
if (this.race == null)
|
|
|
|
return true;
|
|
|
|
return (this.race.getRaceType().getCharacterSex().equals(CharacterSex.MALE));
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean canSee(PlayerCharacter tar) {
|
|
|
|
|
|
|
|
if (tar == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (this.equals(tar))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return this.getSeeInvis() >= tar.hidden && !tar.safemodeInvis();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void recalculatePlayerStats(boolean initialized) {
|
|
|
|
|
|
|
|
//calculate base stats
|
|
|
|
this.calculateBaseStats();
|
|
|
|
|
|
|
|
//calculate base skills
|
|
|
|
CharacterSkill.updateAllBaseAmounts(this);
|
|
|
|
this.calculateModifiedStats();
|
|
|
|
|
|
|
|
//calculate modified skills
|
|
|
|
CharacterSkill.updateAllModifiedAmounts(this);
|
|
|
|
this.updateScaleHeight();
|
|
|
|
|
|
|
|
//calculate modified stats
|
|
|
|
|
|
|
|
|
|
|
|
//calculate ATR, damage and defense
|
|
|
|
this.calculateAtrDefenseDamage();
|
|
|
|
|
|
|
|
//calculate movement bonus
|
|
|
|
PlayerManager.calculateSpeedMod(this);
|
|
|
|
|
|
|
|
// recalculate Max Health/Mana/Stamina
|
|
|
|
this.calculateMaxHealthManaStamina();
|
|
|
|
|
|
|
|
// recalculate Resists
|
|
|
|
Resists.calculateResists(this);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ Recalculate player after promoting or gaining a level
|
|
|
|
*/
|
|
|
|
public void recalculate() {
|
|
|
|
this.applyBonuses();
|
|
|
|
this.trainsAvailable.set(CharacterSkill.getTrainsAvailable(this));
|
|
|
|
if (this.trainsAvailable.get() < 0)
|
|
|
|
recalculateTrains();
|
|
|
|
//this.resists.calculateResists(this);
|
|
|
|
|
|
|
|
// calculate skills and powers. Make sure none are missing.
|
|
|
|
this.calculateSkills();
|
|
|
|
|
|
|
|
// calculate powers again. See if any new powers unlocked
|
|
|
|
this.calculateSkills();
|
|
|
|
}
|
|
|
|
|
|
|
|
//This is run to auto-fix any overage on skill training.
|
|
|
|
public void recalculateTrains() {
|
|
|
|
int trainsAvailable = CharacterSkill.getTrainsAvailable(this);
|
|
|
|
if (trainsAvailable < 0) {
|
|
|
|
|
|
|
|
//refine powers first, run twice to catch any prereqs
|
|
|
|
ConcurrentHashMap<Integer, CharacterPower> powers = this.getPowers();
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
for (CharacterPower p : powers.values()) {
|
|
|
|
if (trainsAvailable >= 0)
|
|
|
|
return;
|
|
|
|
while (p.getTrains() > 0 && p.refine(this)) {
|
|
|
|
trainsAvailable++;
|
|
|
|
if (trainsAvailable >= 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//refine skills
|
|
|
|
ConcurrentHashMap<String, CharacterSkill> skills = this.getSkills();
|
|
|
|
for (CharacterSkill s : skills.values()) {
|
|
|
|
if (trainsAvailable >= 0)
|
|
|
|
return;
|
|
|
|
while (s.getNumTrains() > 0 && s.refine(this)) {
|
|
|
|
if (CharacterSkill.getTrainsAvailable(this) >= 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ Calculates Base Stats Call this when modifying stats or adding/removing
|
|
|
|
* runes
|
|
|
|
*/
|
|
|
|
public void calculateBaseStats() {
|
|
|
|
if (this.race == null || this.baseClass == null)
|
|
|
|
// Logger.getInstance().log( LogEventType.ERROR,
|
|
|
|
// "PlayerCharacter.updateBaseStats: Missing race or baseclass for Player "
|
|
|
|
// + this.getUUID());
|
|
|
|
return;
|
|
|
|
|
|
|
|
// get base stats and total available
|
|
|
|
int strMin = this.race.getStrStart() + this.baseClass.getStrMod() - 5;
|
|
|
|
int dexMin = this.race.getDexStart() + this.baseClass.getDexMod() - 5;
|
|
|
|
int conMin = this.race.getConStart() + this.baseClass.getConMod() - 5;
|
|
|
|
int intMin = this.race.getIntStart() + this.baseClass.getIntMod() - 5;
|
|
|
|
int spiMin = this.race.getSpiStart() + this.baseClass.getSpiMod() - 5;
|
|
|
|
int str = this.race.getStrStart() + this.baseClass.getStrMod() + this.strMod.get();
|
|
|
|
int dex = this.race.getDexStart() + this.baseClass.getDexMod() + this.dexMod.get();
|
|
|
|
int con = this.race.getConStart() + this.baseClass.getConMod() + this.conMod.get();
|
|
|
|
int intt = this.race.getIntStart() + this.baseClass.getIntMod() + this.intMod.get();
|
|
|
|
int spi = this.race.getSpiStart() + this.baseClass.getSpiMod() + this.spiMod.get();
|
|
|
|
int strMax = this.race.getStrMax();
|
|
|
|
int dexMax = this.race.getDexMax();
|
|
|
|
int conMax = this.race.getConMax();
|
|
|
|
int intMax = this.race.getIntMax();
|
|
|
|
int spiMax = this.race.getSpiMax();
|
|
|
|
int available = this.race.getStartingPoints() - this.strMod.get() - this.dexMod.get() - this.conMod.get() - this.intMod.get() - this.spiMod.get();
|
|
|
|
if (level < 20)
|
|
|
|
available += (level - 1) * 5;
|
|
|
|
else if (level < 30)
|
|
|
|
available += 90 + (level - 19) * 4;
|
|
|
|
else if (level < 40)
|
|
|
|
available += 130 + (level - 29) * 3;
|
|
|
|
else if (level < 50)
|
|
|
|
available += 160 + (level - 39) * 2;
|
|
|
|
else
|
|
|
|
available += 180 + (level - 49);
|
|
|
|
|
|
|
|
// modify for any runes applied.
|
|
|
|
for (CharacterRune rune : this.runes) {
|
|
|
|
if (rune.getRuneBase() == null)
|
|
|
|
// Logger.getInstance().log( LogEventType.ERROR,
|
|
|
|
// "PlayerCharacter.updateBaseStats: Missing runebase for rune "
|
|
|
|
// + rune.getUUID());
|
|
|
|
continue;
|
|
|
|
ArrayList<RuneBaseAttribute> attrs = rune.getRuneBase().getAttrs();
|
|
|
|
if (attrs == null)
|
|
|
|
// Logger.getInstance().log( LogEventType.ERROR,
|
|
|
|
// "PlayerCharacter.updateBaseStats: Missing attributes for runebase "
|
|
|
|
// + rune.getRuneBase().getUUID());
|
|
|
|
continue;
|
|
|
|
for (RuneBaseAttribute abr : attrs) {
|
|
|
|
int attrID = abr.getAttributeID();
|
|
|
|
int value = abr.getModValue();
|
|
|
|
switch (attrID) {
|
|
|
|
case MBServerStatics.RUNE_COST_ATTRIBUTE_ID:
|
|
|
|
available -= value;
|
|
|
|
break;
|
|
|
|
case MBServerStatics.RUNE_STR_ATTRIBUTE_ID:
|
|
|
|
str += value;
|
|
|
|
strMin += value;
|
|
|
|
break;
|
|
|
|
case MBServerStatics.RUNE_DEX_ATTRIBUTE_ID:
|
|
|
|
dex += value;
|
|
|
|
dexMin += value;
|
|
|
|
break;
|
|
|
|
case MBServerStatics.RUNE_CON_ATTRIBUTE_ID:
|
|
|
|
con += value;
|
|
|
|
conMin += value;
|
|
|
|
break;
|
|
|
|
case MBServerStatics.RUNE_INT_ATTRIBUTE_ID:
|
|
|
|
intt += value;
|
|
|
|
intMin += value;
|
|
|
|
break;
|
|
|
|
case MBServerStatics.RUNE_SPI_ATTRIBUTE_ID:
|
|
|
|
spi += value;
|
|
|
|
spiMin += value;
|
|
|
|
break;
|
|
|
|
case MBServerStatics.RUNE_STR_MAX_ATTRIBUTE_ID:
|
|
|
|
strMax += value;
|
|
|
|
break;
|
|
|
|
case MBServerStatics.RUNE_DEX_MAX_ATTRIBUTE_ID:
|
|
|
|
dexMax += value;
|
|
|
|
break;
|
|
|
|
case MBServerStatics.RUNE_CON_MAX_ATTRIBUTE_ID:
|
|
|
|
conMax += value;
|
|
|
|
break;
|
|
|
|
case MBServerStatics.RUNE_INT_MAX_ATTRIBUTE_ID:
|
|
|
|
intMax += value;
|
|
|
|
break;
|
|
|
|
case MBServerStatics.RUNE_SPI_MAX_ATTRIBUTE_ID:
|
|
|
|
spiMax += value;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Set titles based on rune..
|
|
|
|
switch (rune.getRuneBaseID()) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2901: //CSR 1
|
|
|
|
this.title = CharacterTitle.CSR_1;
|
|
|
|
break;
|
|
|
|
case 2902: //CSR 1
|
|
|
|
this.title = CharacterTitle.CSR_2;
|
|
|
|
break;
|
|
|
|
case 2903: //CSR 1
|
|
|
|
this.title = CharacterTitle.CSR_3;
|
|
|
|
break;
|
|
|
|
case 2904: //CSR 1
|
|
|
|
this.title = CharacterTitle.CSR_4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2910: //Wolfpack Developer
|
|
|
|
this.title = CharacterTitle.DEVELOPER;
|
|
|
|
break;
|
|
|
|
case 2911: //QA Test Rune
|
|
|
|
this.title = CharacterTitle.QA;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//hack check. Make sure available does not go below 0.
|
|
|
|
//subtract from each stat until available is 0 or greater.
|
|
|
|
if (available < 0) {
|
|
|
|
while (this.spiMod.get() > 0 && available < 0) {
|
|
|
|
this.spiMod.decrementAndGet();
|
|
|
|
spi--;
|
|
|
|
available++;
|
|
|
|
}
|
|
|
|
while (this.conMod.get() > 0 && available < 0) {
|
|
|
|
this.conMod.decrementAndGet();
|
|
|
|
con--;
|
|
|
|
available++;
|
|
|
|
}
|
|
|
|
while (this.strMod.get() > 0 && available < 0) {
|
|
|
|
this.strMod.decrementAndGet();
|
|
|
|
str--;
|
|
|
|
available++;
|
|
|
|
}
|
|
|
|
while (this.dexMod.get() > 0 && available < 0) {
|
|
|
|
this.dexMod.decrementAndGet();
|
|
|
|
dex--;
|
|
|
|
available++;
|
|
|
|
}
|
|
|
|
while (this.intMod.get() > 0 && available < 0) {
|
|
|
|
this.intMod.decrementAndGet();
|
|
|
|
intt--;
|
|
|
|
available++;
|
|
|
|
}
|
|
|
|
|
|
|
|
//update database
|
|
|
|
this.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.statStrBase = (short) str;
|
|
|
|
this.statDexBase = (short) dex;
|
|
|
|
this.statConBase = (short) con;
|
|
|
|
this.statIntBase = (short) intt;
|
|
|
|
this.statSpiBase = (short) spi;
|
|
|
|
this.statStrMax = (short) (strMax);
|
|
|
|
this.statDexMax = (short) (dexMax);
|
|
|
|
this.statConMax = (short) (conMax);
|
|
|
|
this.statIntMax = (short) (intMax);
|
|
|
|
this.statSpiMax = (short) (spiMax);
|
|
|
|
this.statStrMin = (short) strMin;
|
|
|
|
this.statDexMin = (short) dexMin;
|
|
|
|
this.statConMin = (short) conMin;
|
|
|
|
this.statIntMin = (short) intMin;
|
|
|
|
this.statSpiMin = (short) spiMin;
|
|
|
|
this.unusedStatPoints = (short) available;
|
|
|
|
this.trainedStatPoints = 0;
|
|
|
|
|
|
|
|
// Testing, allow characters to have more stats then normal for formula checking
|
|
|
|
if (this.statStrBase > this.statStrMax)
|
|
|
|
this.statStrMax = this.statStrBase;
|
|
|
|
if (this.statDexBase > this.statDexMax)
|
|
|
|
this.statDexMax = this.statDexBase;
|
|
|
|
if (this.statConBase > this.statConMax)
|
|
|
|
this.statConMax = this.statConBase;
|
|
|
|
if (this.statIntBase > this.statIntMax)
|
|
|
|
this.statIntMax = this.statIntBase;
|
|
|
|
if (this.statSpiBase > this.statSpiMax)
|
|
|
|
this.statSpiMax = this.statSpiBase;
|
|
|
|
|
|
|
|
// Modified stats must be recalculated when base stats are
|
|
|
|
//calculateModifiedStats();
|
|
|
|
//update hide and seeInvis levels
|
|
|
|
if (this.bonuses != null) {
|
|
|
|
this.hidden = (int) bonuses.getFloat(ModType.Invisible, SourceType.None);
|
|
|
|
this.seeInvis = (int) bonuses.getFloat(ModType.SeeInvisible, SourceType.None);
|
|
|
|
} else {
|
|
|
|
this.hidden = (byte) 0;
|
|
|
|
this.seeInvis = (byte) 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//check is player is a CSR
|
|
|
|
this.isCSR = this.containsCSRRune();
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean containsCSRRune() {
|
|
|
|
|
|
|
|
if (this.race != null && this.race.getRaceType().equals(RaceType.CSRMALE))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (this.baseClass != null && this.baseClass.getObjectUUID() > 2900 && this.baseClass.getObjectUUID() < 2905)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (this.promotionClass != null && this.promotionClass.getObjectUUID() > 2900 && this.promotionClass.getObjectUUID() < 2905)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (this.runes == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (CharacterRune rune : this.runes) {
|
|
|
|
|
|
|
|
if (rune == null || rune.getRuneBase() == null)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
RuneBase rb = rune.getRuneBase();
|
|
|
|
|
|
|
|
if (rb.getObjectUUID() > 2900 && rb.getObjectUUID() < 2905)
|
|
|
|
return true;
|
|
|
|
if (rb.getObjectUUID() == 2910)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isCSR() {
|
|
|
|
return this.isCSR;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setAsciiLastName(boolean value) {
|
|
|
|
this.asciiLastName = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean _asciiLastName() {
|
|
|
|
return this.asciiLastName;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ Calculates Modified Stats Call this when changing equipment or
|
|
|
|
* add/removing effect. skips base stat modification.
|
|
|
|
*/
|
|
|
|
public void calculateModifiedStats() {
|
|
|
|
float strVal = this.statStrBase;
|
|
|
|
float dexVal = this.statDexBase;
|
|
|
|
float conVal = this.statConBase;
|
|
|
|
float intVal = this.statIntBase;
|
|
|
|
float spiVal = this.statSpiBase;
|
|
|
|
|
|
|
|
float dexPenalty = getDexPenalty();
|
|
|
|
|
|
|
|
// TODO modify for equipment
|
|
|
|
if (this.bonuses != null) {
|
|
|
|
// modify for effects
|
|
|
|
strVal += Math.round(this.bonuses.getFloat(ModType.Attr, SourceType.Strength));
|
|
|
|
dexVal += Math.round(this.bonuses.getFloat(ModType.Attr, SourceType.Dexterity));
|
|
|
|
conVal += Math.round(this.bonuses.getFloat(ModType.Attr, SourceType.Constitution));
|
|
|
|
intVal += Math.round(this.bonuses.getFloat(ModType.Attr, SourceType.Intelligence));
|
|
|
|
spiVal += Math.round(this.bonuses.getFloat(ModType.Attr, SourceType.Spirit));
|
|
|
|
|
|
|
|
// apply dex penalty for armor
|
|
|
|
dexVal *= dexPenalty;
|
|
|
|
|
|
|
|
// modify percent amounts. DO THIS LAST!
|
|
|
|
strVal *= (1 + this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Strength));
|
|
|
|
dexVal *= (1 + this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Dexterity));
|
|
|
|
conVal *= (1 + this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Constitution));
|
|
|
|
intVal *= (1 + this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Intelligence));
|
|
|
|
spiVal *= (1 + this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Spirit));
|
|
|
|
|
|
|
|
} else
|
|
|
|
// apply dex penalty for armor
|
|
|
|
dexVal *= dexPenalty;
|
|
|
|
|
|
|
|
// Set current stats
|
|
|
|
this.statStrCurrent = (strVal < 1) ? (short) 1 : (short) strVal;
|
|
|
|
this.statDexCurrent = (dexVal < 1) ? (short) 1 : (short) dexVal;
|
|
|
|
this.statConCurrent = (conVal < 1) ? (short) 1 : (short) conVal;
|
|
|
|
this.statIntCurrent = (intVal < 1) ? (short) 1 : (short) intVal;
|
|
|
|
this.statSpiCurrent = (spiVal < 1) ? (short) 1 : (short) spiVal;
|
|
|
|
|
|
|
|
// recalculate skills
|
|
|
|
//CharacterSkill.updateAllBaseAmounts(this);
|
|
|
|
// recalculate Max Health/Mana/Stamina
|
|
|
|
//calculateMaxHealthManaStamina();
|
|
|
|
// recalculate Resists
|
|
|
|
//this.resists.calculateResists(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getDexPenalty() {
|
|
|
|
|
|
|
|
if (this.charItemManager == null || this.charItemManager.getEquipped() == null) {
|
|
|
|
Logger.error("Player " + this.getObjectUUID() + " missing equipment");
|
|
|
|
return 1f;
|
|
|
|
}
|
|
|
|
|
|
|
|
ConcurrentHashMap<Integer, Item> equipped = this.charItemManager.getEquipped();
|
|
|
|
float dexPenalty = 0f;
|
|
|
|
dexPenalty += PlayerManager.getDexPenalty(equipped.get(MBServerStatics.SLOT_HELMET));
|
|
|
|
dexPenalty += PlayerManager.getDexPenalty(equipped.get(MBServerStatics.SLOT_CHEST));
|
|
|
|
dexPenalty += PlayerManager.getDexPenalty(equipped.get(MBServerStatics.SLOT_ARMS));
|
|
|
|
dexPenalty += PlayerManager.getDexPenalty(equipped.get(MBServerStatics.SLOT_GLOVES));
|
|
|
|
dexPenalty += PlayerManager.getDexPenalty(equipped.get(MBServerStatics.SLOT_LEGGINGS));
|
|
|
|
dexPenalty += PlayerManager.getDexPenalty(equipped.get(MBServerStatics.SLOT_FEET));
|
|
|
|
return (1 - (dexPenalty / 100));
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getStrForClient() {
|
|
|
|
return this.statStrCurrent - this.race.getStrStart() - this.baseClass.getStrMod();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getDexForClient() {
|
|
|
|
return this.statDexCurrent - this.race.getDexStart() - this.baseClass.getDexMod();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getConForClient() {
|
|
|
|
return this.statConCurrent - this.race.getConStart() - this.baseClass.getConMod();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getIntForClient() {
|
|
|
|
return this.statIntCurrent - this.race.getIntStart() - this.baseClass.getIntMod();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getSpiForClient() {
|
|
|
|
return this.statSpiCurrent - this.race.getSpiStart() - this.baseClass.getSpiMod();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getTrainsAvailable() {
|
|
|
|
return this.trainsAvailable.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void modifyTrainsAvailable(int amount) {
|
|
|
|
boolean worked = false;
|
|
|
|
while (!worked) {
|
|
|
|
int old = this.trainsAvailable.get();
|
|
|
|
int newVal = old + amount;
|
|
|
|
// if (newVal < 0)
|
|
|
|
// newVal = 0;
|
|
|
|
worked = this.trainsAvailable.compareAndSet(old, newVal);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset any data that should not persist from a previous session
|
|
|
|
public void resetDataAtLogin() {
|
|
|
|
loadedObjects.clear();
|
|
|
|
loadedStaticObjects.clear();
|
|
|
|
lastStaticLoc = Vector3fImmutable.ZERO;
|
|
|
|
setLastTarget(GameObjectType.unknown, 0);
|
|
|
|
this.follow = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ Calculates Atr (both hands) Defense, and Damage for pc
|
|
|
|
*/
|
|
|
|
public void calculateAtrDefenseDamage() {
|
|
|
|
if (this.charItemManager == null || this.charItemManager.getEquipped() == null || this.skills == null) {
|
|
|
|
Logger.error("Player " + this.getObjectUUID() + " missing skills or equipment");
|
|
|
|
defaultAtrAndDamage(true);
|
|
|
|
defaultAtrAndDamage(false);
|
|
|
|
this.defenseRating = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ConcurrentHashMap<Integer, Item> equipped = this.charItemManager.getEquipped();
|
|
|
|
|
|
|
|
// // Reset passives
|
|
|
|
// if (this.bonuses != null) {
|
|
|
|
// this.bonuses.setBool("Block", false);
|
|
|
|
// this.bonuses.setBool("Parry", false);
|
|
|
|
// if (this.baseClass != null && this.baseClass.getUUID() == 2502)
|
|
|
|
// this.bonuses.setBool("Dodge", true);
|
|
|
|
// else
|
|
|
|
// this.bonuses.setBool("Dodge", false);
|
|
|
|
// }
|
|
|
|
// calculate atr and damage for each hand
|
|
|
|
calculateAtrDamageForWeapon(equipped.get(MBServerStatics.SLOT_MAINHAND), true, equipped.get(MBServerStatics.SLOT_OFFHAND));
|
|
|
|
calculateAtrDamageForWeapon(equipped.get(MBServerStatics.SLOT_OFFHAND), false, equipped.get(MBServerStatics.SLOT_MAINHAND));
|
|
|
|
|
|
|
|
// No Defense while in DeathShroud
|
|
|
|
if (this.effects != null && this.effects.containsKey("DeathShroud"))
|
|
|
|
this.defenseRating = (short) 0;
|
|
|
|
else {
|
|
|
|
// calculate defense for equipment
|
|
|
|
float defense = this.statDexCurrent * 2;
|
|
|
|
defense += getShieldDefense(equipped.get(MBServerStatics.SLOT_OFFHAND));
|
|
|
|
defense += getArmorDefense(equipped.get(MBServerStatics.SLOT_HELMET));
|
|
|
|
defense += getArmorDefense(equipped.get(MBServerStatics.SLOT_CHEST));
|
|
|
|
defense += getArmorDefense(equipped.get(MBServerStatics.SLOT_ARMS));
|
|
|
|
defense += getArmorDefense(equipped.get(MBServerStatics.SLOT_GLOVES));
|
|
|
|
defense += getArmorDefense(equipped.get(MBServerStatics.SLOT_LEGGINGS));
|
|
|
|
defense += getArmorDefense(equipped.get(MBServerStatics.SLOT_FEET));
|
|
|
|
defense += getWeaponDefense(equipped);
|
|
|
|
|
|
|
|
if (this.bonuses != null) {
|
|
|
|
// add any bonuses
|
|
|
|
defense += (short) this.bonuses.getFloat(ModType.DCV, SourceType.None);
|
|
|
|
|
|
|
|
// Finally multiply any percent modifiers. DO THIS LAST!
|
|
|
|
float pos_Bonus = this.bonuses.getFloatPercentPositive(ModType.DCV, SourceType.None);
|
|
|
|
defense = (short) (defense * (1 + pos_Bonus));
|
|
|
|
|
|
|
|
//Lucky rune applies next
|
|
|
|
//applied runes will be calculated and added to the normal bonuses. no need for this garbage anymore
|
|
|
|
//defense = (short) (defense * (1 + ((float) this.bonuses.getShort("rune.Defense") / 100)));
|
|
|
|
|
|
|
|
//and negative percent modifiers
|
|
|
|
//already done...
|
|
|
|
float neg_Bonus = this.bonuses.getFloatPercentNegative(ModType.DCV, SourceType.None);
|
|
|
|
defense = (short) (defense * (1 + neg_Bonus));
|
|
|
|
|
|
|
|
} else
|
|
|
|
// TODO add error log here
|
|
|
|
Logger.error("Error: missing bonuses");
|
|
|
|
|
|
|
|
defense = (defense < 1) ? 1 : defense;
|
|
|
|
this.defenseRating = (short) (defense + 0.5f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ Calculates Atr, and Damage for each weapon
|
|
|
|
*/
|
|
|
|
private void calculateAtrDamageForWeapon(Item weapon, boolean mainHand, Item otherHand) {
|
|
|
|
|
|
|
|
// make sure weapon exists
|
|
|
|
boolean noWeapon = false;
|
|
|
|
ItemBase wb = null;
|
|
|
|
if (weapon == null)
|
|
|
|
noWeapon = true;
|
|
|
|
else {
|
|
|
|
ItemBase ib = weapon.getItemBase();
|
|
|
|
if (ib == null)
|
|
|
|
noWeapon = true;
|
|
|
|
else if (!ib.getType().equals(ItemType.WEAPON)) {
|
|
|
|
defaultAtrAndDamage(mainHand);
|
|
|
|
return;
|
|
|
|
} else
|
|
|
|
wb = ib;
|
|
|
|
}
|
|
|
|
float skillPercentage, masteryPercentage;
|
|
|
|
float mastDam;
|
|
|
|
float min, max;
|
|
|
|
float speed = 20f;
|
|
|
|
boolean strBased = false;
|
|
|
|
|
|
|
|
ItemBase wbMain = (weapon != null) ? weapon.getItemBase() : null;
|
|
|
|
ItemBase wbOff = (otherHand != null) ? otherHand.getItemBase() : null;
|
|
|
|
|
|
|
|
// get skill percentages and min and max damage for weapons
|
|
|
|
if (noWeapon) {
|
|
|
|
if (mainHand) {
|
|
|
|
Item off = this.charItemManager.getEquipped().get(MBServerStatics.SLOT_OFFHAND);
|
|
|
|
if (off != null && off.getItemBase() != null && off.getItemBase().getType().equals(ItemType.WEAPON))
|
|
|
|
this.rangeHandOne = 10 * (1 + (this.statStrBase / 600)); // Set
|
|
|
|
// to
|
|
|
|
// no
|
|
|
|
// weapon
|
|
|
|
// range
|
|
|
|
else
|
|
|
|
this.rangeHandOne = -1; // set to do not attack
|
|
|
|
} else
|
|
|
|
this.rangeHandTwo = -1; // set to do not attack
|
|
|
|
|
|
|
|
skillPercentage = PlayerManager.getModifiedAmount(this.skills.get("Unarmed Combat"));
|
|
|
|
masteryPercentage = PlayerManager.getModifiedAmount(this.skills.get("Unarmed Combat Mastery"));
|
|
|
|
if (masteryPercentage == 0f)
|
|
|
|
mastDam = CharacterSkill.getQuickMastery(this, "Unarmed Combat Mastery");
|
|
|
|
else
|
|
|
|
mastDam = masteryPercentage;
|
|
|
|
// TODO Correct these
|
|
|
|
min = 1;
|
|
|
|
max = 3;
|
|
|
|
} else {
|
|
|
|
if (mainHand)
|
|
|
|
this.rangeHandOne = weapon.getItemBase().getRange() * (1 + (this.statStrBase / 600));
|
|
|
|
else
|
|
|
|
this.rangeHandTwo = weapon.getItemBase().getRange() * (1 + (this.statStrBase / 600));
|
|
|
|
|
|
|
|
if (this.bonuses != null) {
|
|
|
|
float range_bonus = 1 + this.bonuses.getFloatPercentAll(ModType.WeaponRange, SourceType.None);
|
|
|
|
|
|
|
|
if (mainHand)
|
|
|
|
this.rangeHandOne *= range_bonus;
|
|
|
|
else
|
|
|
|
this.rangeHandTwo *= range_bonus;
|
|
|
|
|
|
|
|
}
|
|
|
|
skillPercentage = PlayerManager.getModifiedAmount(this.skills.get(wb.getSkillRequired()));
|
|
|
|
masteryPercentage = PlayerManager.getModifiedAmount(this.skills.get(wb.getMastery()));
|
|
|
|
if (masteryPercentage == 0f)
|
|
|
|
mastDam = 0f;
|
|
|
|
// mastDam = CharacterSkill.getQuickMastery(this, wb.getMastery());
|
|
|
|
else
|
|
|
|
mastDam = masteryPercentage;
|
|
|
|
min = (float) wb.getMinDamage();
|
|
|
|
max = (float) wb.getMaxDamage();
|
|
|
|
strBased = wb.isStrBased();
|
|
|
|
|
|
|
|
//
|
|
|
|
// Add parry bonus for weapon and allow parry if needed
|
|
|
|
|
|
|
|
// // Only Fighters and Thieves can Parry
|
|
|
|
// if ((this.baseClass != null && this.baseClass.getUUID() == 2500)
|
|
|
|
// || (this.promotionClass != null && this.promotionClass.getUUID() == 2520)) {
|
|
|
|
// if (wbMain == null || wbMain.getRange() < MBServerStatics.RANGED_WEAPON_RANGE)
|
|
|
|
// if (wbOff == null || wbOff.getRange() < MBServerStatics.RANGED_WEAPON_RANGE)
|
|
|
|
// this.bonuses.setBool("Parry", true);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.effects != null && this.effects.containsKey("DeathShroud"))
|
|
|
|
// No Atr in deathshroud.
|
|
|
|
if (mainHand)
|
|
|
|
this.atrHandOne = (short) 0;
|
|
|
|
else
|
|
|
|
this.atrHandTwo = (short) 0;
|
|
|
|
else {
|
|
|
|
// calculate atr
|
|
|
|
float atr = 0;
|
|
|
|
atr += (int) skillPercentage * 4f; //<-round down skill% -
|
|
|
|
atr += (int) masteryPercentage * 3f;
|
|
|
|
if (this.statStrCurrent > this.statDexCurrent)
|
|
|
|
atr += statStrCurrent / 2;
|
|
|
|
else
|
|
|
|
atr += statDexCurrent / 2;
|
|
|
|
|
|
|
|
// add in any bonuses to atr
|
|
|
|
if (this.bonuses != null) {
|
|
|
|
// Add any base bonuses
|
|
|
|
atr += this.bonuses.getFloat(ModType.OCV, SourceType.None);
|
|
|
|
|
|
|
|
// Finally use any multipliers. DO THIS LAST!
|
|
|
|
float pos_Bonus = (1 + this.bonuses.getFloatPercentPositive(ModType.OCV, SourceType.None));
|
|
|
|
atr *= pos_Bonus;
|
|
|
|
|
|
|
|
// next precise
|
|
|
|
//runes will have their own bonuses.
|
|
|
|
// atr *= (1 + ((float) this.bonuses.getShort("rune.Attack") / 100));
|
|
|
|
|
|
|
|
//and negative percent modifiers
|
|
|
|
float neg_Bonus = this.bonuses.getFloatPercentNegative(ModType.OCV, SourceType.None);
|
|
|
|
|
|
|
|
atr *= (1 + neg_Bonus);
|
|
|
|
}
|
|
|
|
|
|
|
|
atr = (atr < 1) ? 1 : atr;
|
|
|
|
|
|
|
|
// set atr
|
|
|
|
if (mainHand)
|
|
|
|
this.atrHandOne = (short) (atr + 0.5f);
|
|
|
|
else
|
|
|
|
this.atrHandTwo = (short) (atr + 0.5f);
|
|
|
|
}
|
|
|
|
|
|
|
|
//calculate speed
|
|
|
|
if (wb != null)
|
|
|
|
speed = wb.getSpeed();
|
|
|
|
else
|
|
|
|
speed = 20f; //unarmed attack speed
|
|
|
|
if (weapon != null)
|
|
|
|
speed *= (1 + this.bonuses.getFloatPercentAll(ModType.WeaponSpeed, SourceType.None));
|
|
|
|
speed *= (1 + this.bonuses.getFloatPercentAll(ModType.AttackDelay, SourceType.None));
|
|
|
|
if (speed < 10)
|
|
|
|
speed = 10;
|
|
|
|
|
|
|
|
//add min/max damage bonuses for weapon
|
|
|
|
if (weapon != null) {
|
|
|
|
// Add any base bonuses
|
|
|
|
|
|
|
|
min += weapon.getBonus(ModType.MinDamage, SourceType.None);
|
|
|
|
max += weapon.getBonus(ModType.MaxDamage, SourceType.None);
|
|
|
|
|
|
|
|
min += weapon.getBonus(ModType.MeleeDamageModifier, SourceType.None);
|
|
|
|
max += weapon.getBonus(ModType.MeleeDamageModifier, SourceType.None);
|
|
|
|
// Finally use any multipliers. DO THIS LAST!
|
|
|
|
|
|
|
|
float percentMinDamage = 1;
|
|
|
|
float percentMaxDamage = 1;
|
|
|
|
|
|
|
|
percentMinDamage += weapon.getBonusPercent(ModType.MinDamage, SourceType.None);
|
|
|
|
percentMinDamage += weapon.getBonusPercent(ModType.MeleeDamageModifier, SourceType.None);
|
|
|
|
|
|
|
|
percentMaxDamage += weapon.getBonusPercent(ModType.MaxDamage, SourceType.None);
|
|
|
|
percentMaxDamage += weapon.getBonusPercent(ModType.MeleeDamageModifier, SourceType.None);
|
|
|
|
|
|
|
|
|
|
|
|
min *= percentMinDamage;
|
|
|
|
max *= percentMaxDamage;
|
|
|
|
}
|
|
|
|
|
|
|
|
//if duel wielding, cut damage by 30%
|
|
|
|
if (otherHand != null) {
|
|
|
|
ItemBase ibo = otherHand.getItemBase();
|
|
|
|
if (ibo != null && ibo.getType().equals(ItemType.WEAPON)) {
|
|
|
|
min *= 0.7f;
|
|
|
|
max *= 0.7f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// calculate damage
|
|
|
|
float minDamage;
|
|
|
|
float maxDamage;
|
|
|
|
float pri = (strBased) ? (float) this.statStrCurrent : (float) this.statDexCurrent;
|
|
|
|
float sec = (strBased) ? (float) this.statDexCurrent : (float) this.statStrCurrent;
|
|
|
|
minDamage = (float) (min * ((0.0315f * Math.pow(pri, 0.75f)) + (0.042f * Math.pow(sec, 0.75f)) + (0.01f * ((int) skillPercentage + (int) mastDam))));
|
|
|
|
maxDamage = (float) (max * ((0.0785f * Math.pow(pri, 0.75f)) + (0.016f * Math.pow(sec, 0.75f)) + (0.0075f * ((int) skillPercentage + (int) mastDam))));
|
|
|
|
minDamage = (float) ((int) (minDamage + 0.5f)); //round to nearest decimal
|
|
|
|
maxDamage = (float) ((int) (maxDamage + 0.5f)); //round to nearest decimal
|
|
|
|
|
|
|
|
// Half damage if in death shroud
|
|
|
|
if (this.effects != null && this.effects.containsKey("DeathShroud")) {
|
|
|
|
minDamage *= 0.5f;
|
|
|
|
maxDamage *= 0.5f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add in any bonuses to damage
|
|
|
|
if (this.bonuses != null) {
|
|
|
|
// Add any base bonuses
|
|
|
|
minDamage += this.bonuses.getFloat(ModType.MinDamage, SourceType.None);
|
|
|
|
maxDamage += this.bonuses.getFloat(ModType.MaxDamage, SourceType.None);
|
|
|
|
|
|
|
|
minDamage += this.bonuses.getFloat(ModType.MeleeDamageModifier, SourceType.None);
|
|
|
|
maxDamage += this.bonuses.getFloat(ModType.MeleeDamageModifier, SourceType.None);
|
|
|
|
// Finally use any multipliers. DO THIS LAST!
|
|
|
|
|
|
|
|
float percentMinDamage = 1;
|
|
|
|
float percentMaxDamage = 1;
|
|
|
|
|
|
|
|
percentMinDamage += this.bonuses.getFloatPercentAll(ModType.MinDamage, SourceType.None);
|
|
|
|
percentMinDamage += this.bonuses.getFloatPercentAll(ModType.MeleeDamageModifier, SourceType.None);
|
|
|
|
|
|
|
|
percentMaxDamage += this.bonuses.getFloatPercentAll(ModType.MaxDamage, SourceType.None);
|
|
|
|
percentMaxDamage += this.bonuses.getFloatPercentAll(ModType.MeleeDamageModifier, SourceType.None);
|
|
|
|
|
|
|
|
minDamage *= percentMinDamage;
|
|
|
|
maxDamage *= percentMaxDamage;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// set damages
|
|
|
|
if (mainHand) {
|
|
|
|
this.minDamageHandOne = (int) minDamage;
|
|
|
|
this.maxDamageHandOne = (int) maxDamage;
|
|
|
|
this.speedHandOne = speed;
|
|
|
|
} else {
|
|
|
|
this.minDamageHandTwo = (int) minDamage;
|
|
|
|
this.maxDamageHandTwo = (int) maxDamage;
|
|
|
|
this.speedHandTwo = speed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ Calculates Defense for shield
|
|
|
|
*/
|
|
|
|
private float getShieldDefense(Item shield) {
|
|
|
|
if (shield == null)
|
|
|
|
return 0;
|
|
|
|
ItemBase ab = shield.getItemBase();
|
|
|
|
if (ab == null || !ab.isShield())
|
|
|
|
return 0;
|
|
|
|
CharacterSkill blockSkill = this.skills.get("Block");
|
|
|
|
float skillMod;
|
|
|
|
if (blockSkill == null) {
|
|
|
|
skillMod = 0;
|
|
|
|
} else
|
|
|
|
skillMod = blockSkill.getModifiedAmount();
|
|
|
|
|
|
|
|
float def = ab.getDefense();
|
|
|
|
//apply item defense bonuses
|
|
|
|
if (shield != null) {
|
|
|
|
def += shield.getBonus(ModType.DR, SourceType.None);
|
|
|
|
def *= (1 + shield.getBonusPercent(ModType.DR, SourceType.None));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// float val = ((float)ab.getDefense()) * (1 + (skillMod / 100));
|
|
|
|
return (def * (1 + ((int) skillMod / 100f)));
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setPassives() {
|
|
|
|
if (this.bonuses != null) {
|
|
|
|
ConcurrentHashMap<Integer, Item> equipped = this.charItemManager.getEquipped();
|
|
|
|
Item off = equipped.get(MBServerStatics.SLOT_OFFHAND);
|
|
|
|
Item main = equipped.get(MBServerStatics.SLOT_MAINHAND);
|
|
|
|
ItemBase wbMain = null;
|
|
|
|
ItemBase wbOff = null;
|
|
|
|
if (main != null)
|
|
|
|
wbMain = main.getItemBase();
|
|
|
|
if (off != null)
|
|
|
|
wbOff = off.getItemBase();
|
|
|
|
|
|
|
|
//set block if block found
|
|
|
|
this.bonuses.setBool(ModType.Block, SourceType.None, false);
|
|
|
|
if (this.baseClass != null && (this.baseClass.getObjectUUID() == 2500 || this.baseClass.getObjectUUID() == 2501))
|
|
|
|
if (off != null && off.getItemBase() != null && off.getItemBase().isShield())
|
|
|
|
this.bonuses.setBool(ModType.Block, SourceType.None, true);
|
|
|
|
|
|
|
|
//set dodge if rogue
|
|
|
|
if (this.baseClass != null && this.baseClass.getObjectUUID() == 2502)
|
|
|
|
this.bonuses.setBool(ModType.Dodge, SourceType.None, true);
|
|
|
|
else
|
|
|
|
this.bonuses.setBool(ModType.Dodge, SourceType.None, false);
|
|
|
|
|
|
|
|
//set parry if fighter or thief and no invalid weapon found
|
|
|
|
this.bonuses.setBool(ModType.Parry, SourceType.None, false);
|
|
|
|
if ((this.baseClass != null && this.baseClass.getObjectUUID() == 2500)
|
|
|
|
|| (this.promotionClass != null && this.promotionClass.getObjectUUID() == 2520))
|
|
|
|
if (wbMain == null || wbMain.getRange() < MBServerStatics.RANGED_WEAPON_RANGE)
|
|
|
|
if (wbOff == null || wbOff.getRange() < MBServerStatics.RANGED_WEAPON_RANGE)
|
|
|
|
this.bonuses.setBool(ModType.Parry, SourceType.None, true);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ Calculates Defense for armor
|
|
|
|
*/
|
|
|
|
private float getArmorDefense(Item armor) {
|
|
|
|
|
|
|
|
if (armor == null)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ItemBase ib = armor.getItemBase();
|
|
|
|
|
|
|
|
if (ib == null)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!ib.getType().equals(ItemType.ARMOR))
|
|
|
|
return 0;
|
|
|
|
if (ib.getSkillRequired().isEmpty())
|
|
|
|
return ib.getDefense();
|
|
|
|
CharacterSkill armorSkill = this.skills.get(ib.getSkillRequired());
|
|
|
|
if (armorSkill == null) {
|
|
|
|
Logger.error("Player " + this.getObjectUUID()
|
|
|
|
+ " has armor equipped without the nescessary skill to equip it");
|
|
|
|
return ib.getDefense();
|
|
|
|
}
|
|
|
|
|
|
|
|
float def = ib.getDefense();
|
|
|
|
//apply item defense bonuses
|
|
|
|
if (armor != null) {
|
|
|
|
def += armor.getBonus(ModType.DR, SourceType.None);
|
|
|
|
def *= (1 + armor.getBonusPercent(ModType.DR, SourceType.None));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (def * (1 + ((int) armorSkill.getModifiedAmount() / 50f)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ Calculates Defense for weapon
|
|
|
|
*/
|
|
|
|
private float getWeaponDefense(ConcurrentHashMap<Integer, Item> equipped) {
|
|
|
|
Item weapon = equipped.get(MBServerStatics.SLOT_MAINHAND);
|
|
|
|
ItemBase wb = null;
|
|
|
|
CharacterSkill skill, mastery;
|
|
|
|
float val = 0;
|
|
|
|
boolean unarmed = false;
|
|
|
|
if (weapon == null) {
|
|
|
|
weapon = equipped.get(MBServerStatics.SLOT_OFFHAND);
|
|
|
|
if (weapon == null || weapon.getItemBase().isShield())
|
|
|
|
unarmed = true;
|
|
|
|
else
|
|
|
|
wb = weapon.getItemBase();
|
|
|
|
} else
|
|
|
|
wb = weapon.getItemBase();
|
|
|
|
if (wb == null)
|
|
|
|
unarmed = true;
|
|
|
|
if (unarmed) {
|
|
|
|
skill = this.skills.get("Unarmed Combat");
|
|
|
|
mastery = this.skills.get("Unarmed Combat Mastery");
|
|
|
|
} else {
|
|
|
|
skill = this.skills.get(wb.getSkillRequired());
|
|
|
|
mastery = this.skills.get(wb.getMastery());
|
|
|
|
}
|
|
|
|
if (skill != null)
|
|
|
|
val += (int) skill.getModifiedAmount() / 2f;
|
|
|
|
if (mastery != null)
|
|
|
|
val += (int) mastery.getModifiedAmount() / 2f;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Call this function to recalculate granted skills and powers for player
|
|
|
|
public synchronized void calculateSkills() {
|
|
|
|
//tell the player to applyBonuses because something has changed
|
|
|
|
|
|
|
|
runSkillCalc();
|
|
|
|
|
|
|
|
//start running the skill/power calculations
|
|
|
|
}
|
|
|
|
|
|
|
|
//Don't call this function directly. linked from pc.calculateSkills()
|
|
|
|
//through SkillCalcJob. Designed to only run from one worker thread
|
|
|
|
public void runSkillCalc() {
|
|
|
|
try {
|
|
|
|
|
|
|
|
//see if any new skills or powers granted
|
|
|
|
CharacterSkill.calculateSkills(this);
|
|
|
|
// calculate granted Trains in powers.
|
|
|
|
CharacterPower.grantTrains(this);
|
|
|
|
//see if any new powers unlocked from previous check
|
|
|
|
CharacterPower.calculatePowers(this);
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//calculate item bonuses here
|
|
|
|
public void calculateItemBonuses() {
|
|
|
|
if (this.charItemManager == null || this.bonuses == null)
|
|
|
|
return;
|
|
|
|
ConcurrentHashMap<Integer, Item> equipped = this.charItemManager.getEquipped();
|
|
|
|
for (Item item : equipped.values()) {
|
|
|
|
ItemBase ib = item.getItemBase();
|
|
|
|
if (ib == null)
|
|
|
|
continue;
|
|
|
|
//TODO add effect bonuses in here for equipped items
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ Defaults ATR, Defense and Damage for player
|
|
|
|
*/
|
|
|
|
private void defaultAtrAndDamage(boolean mainHand) {
|
|
|
|
if (mainHand) {
|
|
|
|
this.atrHandOne = 0;
|
|
|
|
this.minDamageHandOne = 0;
|
|
|
|
this.maxDamageHandOne = 0;
|
|
|
|
this.rangeHandOne = -1;
|
|
|
|
this.speedHandOne = 20;
|
|
|
|
} else {
|
|
|
|
this.atrHandTwo = 0;
|
|
|
|
this.minDamageHandTwo = 0;
|
|
|
|
this.maxDamageHandTwo = 0;
|
|
|
|
this.rangeHandTwo = -1;
|
|
|
|
this.speedHandTwo = 20;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void calculateMaxHealthManaStamina() {
|
|
|
|
float h = 1f;
|
|
|
|
float m = 0f;
|
|
|
|
float s = 0f;
|
|
|
|
float baseHealth = 15f;
|
|
|
|
float baseMana = 5f;
|
|
|
|
float baseStamina = 1f;
|
|
|
|
float promoHealth = 0f;
|
|
|
|
float promoMana = 0f;
|
|
|
|
float promoStamina = 0f;
|
|
|
|
float raceHealth = 0f;
|
|
|
|
float raceMana = 0f;
|
|
|
|
float raceStamina = 0f;
|
|
|
|
float toughness = 0f;
|
|
|
|
float athletics = 0f;
|
|
|
|
|
|
|
|
//get baseclass modifiers
|
|
|
|
if (this.baseClass != null) {
|
|
|
|
baseHealth = this.baseClass.getHealthMod();
|
|
|
|
baseMana = this.baseClass.getManaMod();
|
|
|
|
baseStamina = this.baseClass.getStaminaMod();
|
|
|
|
} else {
|
|
|
|
//TODO log error here
|
|
|
|
}
|
|
|
|
|
|
|
|
//get promotion modifiers
|
|
|
|
if (this.promotionClass != null) {
|
|
|
|
promoHealth = this.promotionClass.getHealthMod();
|
|
|
|
promoMana = this.promotionClass.getManaMod();
|
|
|
|
promoStamina = this.promotionClass.getStaminaMod();
|
|
|
|
}
|
|
|
|
|
|
|
|
// next get racial modifer
|
|
|
|
if (this.race != null) {
|
|
|
|
raceHealth += this.race.getHealthBonus();
|
|
|
|
raceMana += this.race.getManaBonus();
|
|
|
|
raceStamina += this.race.getStaminaBonus();
|
|
|
|
} else {
|
|
|
|
//TODO log error here
|
|
|
|
}
|
|
|
|
|
|
|
|
//Get level modifers
|
|
|
|
float f = 0;
|
|
|
|
float g = 0;
|
|
|
|
if (this.level < 10 || this.promotionClass == null)
|
|
|
|
f = this.level;
|
|
|
|
else if (this.level < 20) {
|
|
|
|
f = this.level;
|
|
|
|
g = this.level - 9;
|
|
|
|
} else if (level < 30) {
|
|
|
|
f = (float) (19 + (this.level - 19) * 0.8);
|
|
|
|
g = (float) (10 + (this.level - 19) * 0.8);
|
|
|
|
} else if (level < 40) {
|
|
|
|
f = (float) (27 + (this.level - 29) * 0.6);
|
|
|
|
g = (float) (18 + (this.level - 29) * 0.6);
|
|
|
|
} else if (level < 50) {
|
|
|
|
f = (float) (33 + (this.level - 39) * 0.4);
|
|
|
|
g = (float) (24 + (this.level - 39) * 0.4);
|
|
|
|
} else if (level < 60) {
|
|
|
|
f = (float) (37 + (this.level - 49) * 0.2);
|
|
|
|
g = (float) (28 + (this.level - 49) * 0.2);
|
|
|
|
} else {
|
|
|
|
f = (float) (39 + (this.level - 59) * 0.1);
|
|
|
|
g = (float) (30 + (this.level - 59) * 0.1);
|
|
|
|
}
|
|
|
|
|
|
|
|
//get toughness and athletics amount
|
|
|
|
if (this.skills != null) {
|
|
|
|
if (this.skills.containsKey("Toughness"))
|
|
|
|
toughness = this.skills.get("Toughness").getModifiedAmount();
|
|
|
|
if (this.skills.containsKey("Athletics"))
|
|
|
|
athletics = this.skills.get("Athletics").getModifiedAmount();
|
|
|
|
}
|
|
|
|
|
|
|
|
h = (((f * baseHealth) + (g * promoHealth)) * (0.3f + (0.005f * this.statConCurrent)) + (this.statConCurrent + raceHealth)) * (1 + (int) toughness / 400f);
|
|
|
|
m = ((f * baseMana) + (g * promoMana)) * (0.3f + (0.005f * this.statSpiCurrent)) + (this.statSpiCurrent + raceMana);
|
|
|
|
s = (((f * baseStamina) + (g * promoStamina)) * (0.3f + (0.005f * this.statConCurrent)) + (this.statConCurrent + raceStamina)) * (1 + (int) athletics / 300f);
|
|
|
|
|
|
|
|
// s = f * (baseStamina + 1.75f) * .5f + this.statConCurrent + raceStamina;
|
|
|
|
// Apply any bonuses from runes and effects
|
|
|
|
if (this.bonuses != null) {
|
|
|
|
|
|
|
|
|
|
|
|
//apply effects
|
|
|
|
h += this.bonuses.getFloat(ModType.HealthFull, SourceType.None);
|
|
|
|
m += this.bonuses.getFloat(ModType.ManaFull, SourceType.None);
|
|
|
|
s += this.bonuses.getFloat(ModType.StaminaFull, SourceType.None);
|
|
|
|
|
|
|
|
h *= (1 + this.bonuses.getFloatPercentAll(ModType.HealthFull, SourceType.None));
|
|
|
|
m *= (1 + this.bonuses.getFloatPercentAll(ModType.ManaFull, SourceType.None));
|
|
|
|
s *= (1 + this.bonuses.getFloatPercentAll(ModType.StaminaFull, SourceType.None));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set max health, mana and stamina
|
|
|
|
if (h > 0)
|
|
|
|
this.healthMax = h;
|
|
|
|
else
|
|
|
|
this.healthMax = 1;
|
|
|
|
if (m > -1)
|
|
|
|
this.manaMax = m;
|
|
|
|
else
|
|
|
|
this.manaMax = 0;
|
|
|
|
if (s > -1)
|
|
|
|
this.staminaMax = s;
|
|
|
|
else
|
|
|
|
this.staminaMax = 0;
|
|
|
|
|
|
|
|
// Update health, mana and stamina if needed
|
|
|
|
if (this.getCurrentHitpoints() > this.healthMax)
|
|
|
|
this.setHealth(this.healthMax);
|
|
|
|
if (this.mana.get() > this.manaMax)
|
|
|
|
this.mana.set(this.manaMax);
|
|
|
|
if (this.stamina.get() > this.staminaMax)
|
|
|
|
this.stamina.set(staminaMax);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public float getPassiveChance(String type, int attackerLevel, boolean fromCombat) {
|
|
|
|
if (this.skills == null || this.bonuses == null)
|
|
|
|
return 0f;
|
|
|
|
|
|
|
|
ModType modType = ModType.GetModType(type);
|
|
|
|
|
|
|
|
// must be allowed to use this passive
|
|
|
|
if (!this.bonuses.getBool(modType, SourceType.None))
|
|
|
|
return 0f;
|
|
|
|
|
|
|
|
// must not be stunned
|
|
|
|
if (this.bonuses.getBool(ModType.Stunned, SourceType.None))
|
|
|
|
return 0f;
|
|
|
|
|
|
|
|
// Get base skill amount
|
|
|
|
CharacterSkill sk = this.skills.get(type);
|
|
|
|
float amount;
|
|
|
|
if (sk == null)
|
|
|
|
amount = CharacterSkill.getQuickMastery(this, type);
|
|
|
|
else
|
|
|
|
amount = sk.getModifiedAmount();
|
|
|
|
|
|
|
|
// Add bonuses
|
|
|
|
amount += this.bonuses.getFloat(modType, SourceType.None);
|
|
|
|
|
|
|
|
// Add item bonuses and return
|
|
|
|
if (type.equals(ModType.Dodge) && !fromCombat)
|
|
|
|
return ((amount / 4) - attackerLevel + this.getLevel()) / 4;
|
|
|
|
else
|
|
|
|
return (amount - attackerLevel + this.getLevel()) / 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getPassiveChance1(ModType modType, SourceType sourceType, int attackerLevel, boolean fromCombat) {
|
|
|
|
if (this.skills == null || this.bonuses == null)
|
|
|
|
return 0f;
|
|
|
|
|
|
|
|
// must be allowed to use this passive
|
|
|
|
if (!this.bonuses.getBool(modType, sourceType))
|
|
|
|
return 0f;
|
|
|
|
|
|
|
|
// must not be stunned
|
|
|
|
if (this.bonuses.getBool(ModType.Stunned, SourceType.None))
|
|
|
|
return 0f;
|
|
|
|
|
|
|
|
// Get base skill amount
|
|
|
|
CharacterSkill sk = this.skills.get(sourceType.name());
|
|
|
|
float amount;
|
|
|
|
if (sk == null)
|
|
|
|
amount = CharacterSkill.getQuickMastery(this, modType.name());
|
|
|
|
else
|
|
|
|
amount = sk.getModifiedAmount();
|
|
|
|
|
|
|
|
// Add bonuses
|
|
|
|
amount += this.bonuses.getFloat(modType, sourceType);
|
|
|
|
|
|
|
|
// Add item bonuses and return
|
|
|
|
if (sourceType.equals(SourceType.Dodge) && !fromCombat)
|
|
|
|
return ((amount / 4) - attackerLevel + this.getLevel()) / 4;
|
|
|
|
else
|
|
|
|
return (amount - attackerLevel + this.getLevel()) / 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getRegenModifier(ModType type) {
|
|
|
|
float regen = 1f;
|
|
|
|
|
|
|
|
if (this.bonuses != null)
|
|
|
|
// get regen bonus from effects
|
|
|
|
regen = this.bonuses.getRegen(type);
|
|
|
|
return regen;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean canBeLooted() {
|
|
|
|
return !this.isAlive();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void removeFromCache() {
|
|
|
|
Logger.info("Removing " + this.getName() + " from Object Cache.");
|
|
|
|
|
|
|
|
for (Item e : this.charItemManager.getEquipped().values()) {
|
|
|
|
e.removeFromCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (Item i : this.charItemManager.getInventory(true)) {
|
|
|
|
i.removeFromCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (Item b : this.charItemManager.getBank()) {
|
|
|
|
b.removeFromCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.account.getLastCharIDUsed() == this.getObjectUUID())
|
|
|
|
for (Item v : this.charItemManager.getVault()) {
|
|
|
|
v.removeFromCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (CharacterSkill cs : this.getSkills().values()) {
|
|
|
|
cs.removeFromCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (CharacterPower ps : this.getPowers().values()) {
|
|
|
|
ps.removeFromCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (CharacterRune cr : this.runes) {
|
|
|
|
cr.removeFromCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
super.removeFromCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void updateDatabase() {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void runAfterLoad() {
|
|
|
|
|
|
|
|
// Init inventory
|
|
|
|
|
|
|
|
this.charItemManager = new CharacterItemManager(this);
|
|
|
|
|
|
|
|
Bounds playerBounds = Bounds.borrow();
|
|
|
|
playerBounds.setBounds(this.getLoc());
|
|
|
|
this.setBounds(playerBounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ConcurrentHashMap<Integer, CharacterPower> initializePowers() {
|
|
|
|
return DbManager.CharacterPowerQueries.GET_POWERS_FOR_CHARACTER(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public final void setFirstName(final String name) {
|
|
|
|
super.setFirstName(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setLastName(final String name) {
|
|
|
|
super.setLastName(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public short getLevel() {
|
|
|
|
return this.getPCLevel();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setLevel(short targetLevel) {
|
|
|
|
|
|
|
|
short tmpLevel;
|
|
|
|
|
|
|
|
tmpLevel = targetLevel;
|
|
|
|
|
|
|
|
tmpLevel = (short) Math.min(tmpLevel, 75);
|
|
|
|
|
|
|
|
while (this.level < tmpLevel) {
|
|
|
|
PlayerManager.grantXP(this, Experience.getBaseExperience(tmpLevel) - this.exp);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean asciiLastName() {
|
|
|
|
return this._asciiLastName();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setGuild(Guild value) {
|
|
|
|
|
|
|
|
if (value == null)
|
|
|
|
value = Guild.getErrantGuild();
|
|
|
|
|
|
|
|
int guildID = 0;
|
|
|
|
|
|
|
|
if (!value.isEmptyGuild())
|
|
|
|
guildID = value.getObjectUUID();
|
|
|
|
DbManager.PlayerCharacterQueries.UPDATE_GUILD(this, guildID);
|
|
|
|
super.setGuild(value);
|
|
|
|
|
|
|
|
// Player changed guild so let's invalidate the login server
|
|
|
|
// cache to reflect this event.
|
|
|
|
|
|
|
|
//Update player bind location;
|
|
|
|
|
|
|
|
Building cityTol = null;
|
|
|
|
|
|
|
|
if (value.getOwnedCity() != null)
|
|
|
|
cityTol = value.getOwnedCity().getTOL();
|
|
|
|
|
|
|
|
this.setBindBuildingID(cityTol != null ? cityTol.getObjectUUID() : 0);
|
|
|
|
//update binds, checks for nation tol if guild tol == null;
|
|
|
|
PlayerManager.getUpdatedBindBuilding(this);
|
|
|
|
|
|
|
|
|
|
|
|
DbManager.AccountQueries.INVALIDATE_LOGIN_CACHE(this.getObjectUUID(), "character");
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getSummoner(int summoner) {
|
|
|
|
synchronized (this.summoners) {
|
|
|
|
if (!this.summoners.containsKey(summoner))
|
|
|
|
return 0;
|
|
|
|
return this.summoners.get(summoner);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addSummoner(int summoner, long time) {
|
|
|
|
synchronized (this.summoners) {
|
|
|
|
this.summoners.put(summoner, time);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void removeSummoner(int summoner) {
|
|
|
|
synchronized (this.summoners) {
|
|
|
|
if (this.summoners.containsKey(summoner))
|
|
|
|
this.summoners.remove(summoner);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private double getDeltaTime() {
|
|
|
|
|
|
|
|
return (System.currentTimeMillis() - lastUpdateTime) * .001f;
|
|
|
|
}
|
|
|
|
|
|
|
|
private double getStamDeltaTime() {
|
|
|
|
|
|
|
|
return (System.currentTimeMillis() - lastStamUpdateTime) * .001f;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void update(Boolean newSystem) {
|
|
|
|
|
|
|
|
//if(!newSystem)
|
|
|
|
// return;
|
|
|
|
|
|
|
|
if (this.updateLock.writeLock().tryLock()) {
|
|
|
|
try {
|
|
|
|
|
|
|
|
if (!this.isAlive() && this.isEnteredWorld()) {
|
|
|
|
if(!this.timestamps.containsKey("DeathTime")){
|
|
|
|
this.timestamps.put("DeathTime",System.currentTimeMillis());
|
|
|
|
}else if((System.currentTimeMillis() - this.timestamps.get("DeathTime")) > 600000)
|
|
|
|
PlayerManager.forceRespawn(this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.updateLocation();
|
|
|
|
this.updateMovementState();
|
|
|
|
this.updateRegen();
|
|
|
|
|
|
|
|
if (this.getStamina() < 10) {
|
|
|
|
if (this.getAltitude() > 0 || this.getDesiredAltitude() > 0) {
|
|
|
|
PlayerManager.GroundPlayer(this);
|
|
|
|
this.updateRegen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RealmMap.updateRealm(this);
|
|
|
|
PlayerManager.updateBlessingMessage(this);
|
|
|
|
|
|
|
|
this.safeZone = this.isInSafeZone();
|
|
|
|
if(!this.timestamps.containsKey("nextBoxCheck"))
|
|
|
|
this.timestamps.put("nextBoxCheck", System.currentTimeMillis() + 10000);
|
|
|
|
|
|
|
|
if(!this.isBoxed && this.timestamps.get("nextBoxCheck") < System.currentTimeMillis()) {
|
|
|
|
this.isBoxed = PlayerManager.checkIfBoxed(this);
|
|
|
|
this.timestamps.put("nextBoxCheck", System.currentTimeMillis() + 10000);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.level < 10 && this.enteredWorld) {
|
|
|
|
// this.setLevel((short) 10);
|
|
|
|
while (this.level < 10) {
|
|
|
|
PlayerManager.grantXP(this, Experience.getBaseExperience(this.level + 1) - this.exp);
|
|
|
|
}
|
|
|
|
if(this.charItemManager != null && this.charItemManager.getGoldInventory() != null && this.charItemManager.getGoldInventory().getNumOfItems() < 1000) {
|
|
|
|
this.getCharItemManager().addGoldToInventory(1000, false);
|
|
|
|
this.getCharItemManager().addItemToInventory(new MobLoot(this, ItemBase.getItemBase(980066), 1, false).promoteToItem(this));
|
|
|
|
this.getCharItemManager().updateInventory();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.isBoxed && !this.containsEffect(1672601862)) {
|
|
|
|
PowersManager.applyPower(this, this, Vector3fImmutable.ZERO, 1672601862, 40, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(PlayerManager.isFlying(this)){
|
|
|
|
//if (!AbstractCharacter.CanFly(this)) {
|
|
|
|
if(this.effects.containsKey("MoveBuff")){
|
|
|
|
PlayerManager.GroundPlayer(this);
|
|
|
|
//ChatManager.chatSystemInfo(this, "You Cannot Fly While Having A MovementBuff");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
Logger.error(e);
|
|
|
|
} finally {
|
|
|
|
this.updateLock.writeLock().unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void updateFlight() {
|
|
|
|
|
|
|
|
if (this.getAltitude() == 0 && this.getTakeOffTime() == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (this.getTakeOffTime() == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (this.getAltitude() == this.getDesiredAltitude()) {
|
|
|
|
if (this.getDesiredAltitude() == 0)
|
|
|
|
this.syncClient();
|
|
|
|
//landing in a building, mark altitude to 0 as player is no longer flying.
|
|
|
|
if (this.landingRegion != null) {
|
|
|
|
this.altitude = 0;
|
|
|
|
this.region = this.landingRegion;
|
|
|
|
this.loc = this.loc.setY(this.landingRegion.lerpY(this));
|
|
|
|
} else
|
|
|
|
this.altitude = this.getDesiredAltitude();
|
|
|
|
|
|
|
|
this.loc = this.loc.setY(HeightMap.getWorldHeight(this) + this.getAltitude());
|
|
|
|
|
|
|
|
this.setTakeOffTime(0);
|
|
|
|
MovementManager.finishChangeAltitude(this, this.getDesiredAltitude());
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.loc = this.loc.setY(HeightMap.getWorldHeight(this) + this.getAltitude());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void updateLocation() {
|
|
|
|
|
|
|
|
|
|
|
|
if (!this.isMoving())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!this.isActive)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Vector3fImmutable newLoc = this.getMovementLoc();
|
|
|
|
|
|
|
|
if (this.isAlive() == false || this.getBonuses().getBool(ModType.Stunned, SourceType.None) || this.getBonuses().getBool(ModType.CannotMove, SourceType.None)) {
|
|
|
|
//Target is stunned or rooted. Don't move
|
|
|
|
this.stopMovement(newLoc);
|
|
|
|
this.region = AbstractWorldObject.GetRegionByWorldObject(this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (newLoc.equals(this.getEndLoc())) {
|
|
|
|
this.stopMovement(newLoc);
|
|
|
|
this.region = AbstractWorldObject.GetRegionByWorldObject(this);
|
|
|
|
if (this.getDebug(1))
|
|
|
|
ChatManager.chatSystemInfo(this,
|
|
|
|
"Arrived at End location. " + this.getEndLoc());
|
|
|
|
return;
|
|
|
|
//Next upda
|
|
|
|
}
|
|
|
|
|
|
|
|
setLoc(newLoc);
|
|
|
|
this.region = AbstractWorldObject.GetRegionByWorldObject(this);
|
|
|
|
|
|
|
|
if (this.getDebug(1))
|
|
|
|
ChatManager.chatSystemInfo(this,
|
|
|
|
"Distance to target " + this.getEndLoc().distance2D(this.getLoc()) + " speed " + this.getSpeed());
|
|
|
|
|
|
|
|
if (this.getStamina() < 10)
|
|
|
|
MovementManager.sendOOS(this);
|
|
|
|
|
|
|
|
// if (MBServerStatics.MOVEMENT_SYNC_DEBUG || this.getDebug(1))
|
|
|
|
// Logger.info("MovementManager", "Updating movement current loc:" + this.getLoc().getX() + " " + this.getLoc().getZ()
|
|
|
|
// + " end loc: " + this.getEndLoc().getX() + " " + this.getEndLoc().getZ() + " distance " + this.getEndLoc().distance2D(this.getLoc()));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void updateMovementState() {
|
|
|
|
|
|
|
|
|
|
|
|
if (this.enteredWorld) {
|
|
|
|
if (!this.lastSwimming) {
|
|
|
|
boolean enterWater = PlayerManager.enterWater(this);
|
|
|
|
|
|
|
|
if (enterWater) {
|
|
|
|
this.lastSwimming = enterWater;
|
|
|
|
MovementManager.sendRWSSMsg(this);
|
|
|
|
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (PlayerManager.LeaveWater(this)) {
|
|
|
|
this.lastSwimming = false;
|
|
|
|
if (!this.isMoving())
|
|
|
|
MovementManager.sendRWSSMsg(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean breathe = PlayerManager.CanBreathe(this);
|
|
|
|
|
|
|
|
if (breathe != this.canBreathe) {
|
|
|
|
this.canBreathe = breathe;
|
|
|
|
// ChatManager.chatSystemInfo(this, "Breathe : " + this.canBreathe);
|
|
|
|
this.syncClient();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//char is flying
|
|
|
|
if (PlayerManager.isFlying(this) == true) {
|
|
|
|
this.movementState = MovementState.FLYING;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Char is not moving. Set sitting or idle
|
|
|
|
if (!this.isMoving()) {
|
|
|
|
|
|
|
|
if (this.sit == true)
|
|
|
|
this.movementState = MovementState.SITTING;
|
|
|
|
else
|
|
|
|
this.movementState = MovementState.IDLE;
|
|
|
|
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
this.movementState = MovementState.RUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Char is swimming // we now are saving lastSwimstate boolean, use this instead of calling getSwimming again.
|
|
|
|
if (this.lastSwimming == true) {
|
|
|
|
this.movementState = MovementState.SWIMMING;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Char is moving, yet not swimming or flying he must be running
|
|
|
|
this.movementState = MovementState.RUNNING;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void updateRegen() {
|
|
|
|
|
|
|
|
float healthRegen = 0f;
|
|
|
|
float manaRegen = 0f;
|
|
|
|
float stamRegen = 0f;
|
|
|
|
|
|
|
|
boolean updateClient = false;
|
|
|
|
|
|
|
|
// Early exit if char is dead or disconnected
|
|
|
|
if ((this.isAlive() == false)
|
|
|
|
|| (this.isActive() == false) || this.getLoc().x == 0 && this.getLoc().z == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Calculate Regen amount from last simulation tick
|
|
|
|
switch (this.movementState) {
|
|
|
|
|
|
|
|
case IDLE:
|
|
|
|
|
|
|
|
healthRegen = ((this.healthMax * MBServerStatics.HEALTH_REGEN_IDLE) + MBServerStatics.HEALTH_REGEN_IDLE_STATIC) * (getRegenModifier(ModType.HealthRecoverRate));
|
|
|
|
|
|
|
|
if (this.isCasting() || this.isItemCasting())
|
|
|
|
healthRegen *= .75f;
|
|
|
|
// Characters regen mana when in only walk mode and idle
|
|
|
|
if (this.walkMode)
|
|
|
|
manaRegen = ((this.manaMax * MBServerStatics.MANA_REGEN_IDLE) * getRegenModifier(ModType.ManaRecoverRate));
|
|
|
|
else if (!this.isCasting() && !this.isItemCasting())
|
|
|
|
manaRegen = ((this.manaMax * MBServerStatics.MANA_REGEN_IDLE) * getRegenModifier(ModType.ManaRecoverRate));
|
|
|
|
else
|
|
|
|
manaRegen = 0;
|
|
|
|
|
|
|
|
if (!PlayerManager.CanBreathe(this))
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_SWIM;
|
|
|
|
else if ((!this.isCasting() && !this.isItemCasting()) || this.lastMovementState.equals(MovementState.FLYING))
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_IDLE * getRegenModifier(ModType.StaminaRecoverRate);
|
|
|
|
else
|
|
|
|
stamRegen = 0;
|
|
|
|
break;
|
|
|
|
case SITTING:
|
|
|
|
healthRegen = ((this.healthMax * MBServerStatics.HEALTH_REGEN_SIT) + MBServerStatics.HEALTH_REGEN_SIT_STATIC) * getRegenModifier(ModType.HealthRecoverRate);
|
|
|
|
manaRegen = (this.manaMax * MBServerStatics.MANA_REGEN_SIT) * (getRegenModifier(ModType.ManaRecoverRate));
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_SIT * getRegenModifier(ModType.StaminaRecoverRate);
|
|
|
|
break;
|
|
|
|
case RUNNING:
|
|
|
|
if (this.walkMode == true) {
|
|
|
|
healthRegen = ((this.healthMax * MBServerStatics.HEALTH_REGEN_WALK) + MBServerStatics.HEALTH_REGEN_IDLE_STATIC) * getRegenModifier(ModType.HealthRecoverRate);
|
|
|
|
manaRegen = this.manaMax * MBServerStatics.MANA_REGEN_WALK * getRegenModifier(ModType.ManaRecoverRate);
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_WALK;
|
|
|
|
} else {
|
|
|
|
healthRegen = 0;
|
|
|
|
manaRegen = 0;
|
|
|
|
|
|
|
|
if (this.combat == true)
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_RUN_COMBAT;
|
|
|
|
else
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_RUN_NONCOMBAT;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FLYING:
|
|
|
|
|
|
|
|
float seventyFive = this.staminaMax * .75f;
|
|
|
|
float fifty = this.staminaMax * .5f;
|
|
|
|
float twentyFive = this.staminaMax * .25f;
|
|
|
|
|
|
|
|
if (this.getDesiredAltitude() == 0 && this.getAltitude() <= 10) {
|
|
|
|
if (this.isCombat())
|
|
|
|
stamRegen = 0;
|
|
|
|
else
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_IDLE * getRegenModifier(ModType.StaminaRecoverRate);
|
|
|
|
} else if (!this.useFlyMoveRegen()) {
|
|
|
|
|
|
|
|
healthRegen = ((this.healthMax * MBServerStatics.HEALTH_REGEN_IDLE) + MBServerStatics.HEALTH_REGEN_IDLE_STATIC) * (getRegenModifier(ModType.HealthRecoverRate));
|
|
|
|
|
|
|
|
if (this.isCasting() || this.isItemCasting())
|
|
|
|
healthRegen *= .75f;
|
|
|
|
// Characters regen mana when in only walk mode and idle
|
|
|
|
if (this.walkMode)
|
|
|
|
manaRegen = (this.manaMax * MBServerStatics.MANA_REGEN_IDLE + (this.getSpiMod() * .015f)) * (getRegenModifier(ModType.ManaRecoverRate));
|
|
|
|
else if (!this.isCasting() && !this.isItemCasting())
|
|
|
|
manaRegen = (this.manaMax * MBServerStatics.MANA_REGEN_IDLE + (this.getSpiMod() * .015f)) * (getRegenModifier(ModType.ManaRecoverRate));
|
|
|
|
else
|
|
|
|
manaRegen = 0;
|
|
|
|
|
|
|
|
if (!this.isItemCasting() && !this.isCasting() || this.getTakeOffTime() != 0)
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_FLY_IDLE;
|
|
|
|
else
|
|
|
|
stamRegen = -1f;
|
|
|
|
} else if (this.walkMode == true) {
|
|
|
|
healthRegen = ((this.healthMax * MBServerStatics.HEALTH_REGEN_WALK) + MBServerStatics.HEALTH_REGEN_IDLE_STATIC) * getRegenModifier(ModType.HealthRecoverRate);
|
|
|
|
manaRegen = ((this.manaMax * MBServerStatics.MANA_REGEN_WALK) + (this.getSpiMod() * .015f)) * (getRegenModifier(ModType.ManaRecoverRate));
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_FLY_WALK;
|
|
|
|
} else {
|
|
|
|
healthRegen = 0;
|
|
|
|
manaRegen = 0;
|
|
|
|
if (this.isCombat())
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_FLY_RUN_COMBAT;
|
|
|
|
else
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_FLY_RUN;
|
|
|
|
}
|
|
|
|
|
|
|
|
float oldStamina = this.stamina.get();
|
|
|
|
|
|
|
|
if (FastMath.between(oldStamina, 0, twentyFive) && !this.wasTripped25) {
|
|
|
|
updateClient = true;
|
|
|
|
this.wasTripped25 = true;
|
|
|
|
this.wasTripped50 = false;
|
|
|
|
this.wasTripped75 = false;
|
|
|
|
} else if (FastMath.between(oldStamina, twentyFive, fifty) && !this.wasTripped50) {
|
|
|
|
updateClient = true;
|
|
|
|
this.wasTripped25 = false;
|
|
|
|
this.wasTripped50 = true;
|
|
|
|
this.wasTripped75 = false;
|
|
|
|
} else if (FastMath.between(oldStamina, fifty, seventyFive) && !this.wasTripped75) {
|
|
|
|
updateClient = true;
|
|
|
|
this.wasTripped25 = false;
|
|
|
|
this.wasTripped50 = false;
|
|
|
|
this.wasTripped75 = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SWIMMING:
|
|
|
|
if (this.walkMode == true) {
|
|
|
|
healthRegen = ((this.healthMax * MBServerStatics.HEALTH_REGEN_WALK) + MBServerStatics.HEALTH_REGEN_IDLE_STATIC) * getRegenModifier(ModType.HealthRecoverRate);
|
|
|
|
manaRegen = ((this.manaMax * MBServerStatics.MANA_REGEN_WALK) + (this.getSpiMod() * .015f)) * (getRegenModifier(ModType.ManaRecoverRate));
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_SWIM;
|
|
|
|
} else {
|
|
|
|
healthRegen = 0;
|
|
|
|
manaRegen = 0;
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_SWIM;
|
|
|
|
|
|
|
|
if (this.combat == true)
|
|
|
|
stamRegen += MBServerStatics.STAMINA_REGEN_RUN_COMBAT;
|
|
|
|
else
|
|
|
|
stamRegen += MBServerStatics.STAMINA_REGEN_RUN_NONCOMBAT;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Are we drowning?
|
|
|
|
if ((this.getStamina() <= 0)
|
|
|
|
&& (PlayerManager.CanBreathe(this) == false))
|
|
|
|
healthRegen = (this.healthMax * -.03f);
|
|
|
|
|
|
|
|
// Multiple regen values by current deltaTime
|
|
|
|
// Logger.info("", healthRegen + "");
|
|
|
|
healthRegen *= getDeltaTime();
|
|
|
|
manaRegen *= getDeltaTime();
|
|
|
|
stamRegen *= getStamDeltaTime();
|
|
|
|
|
|
|
|
boolean workedHealth = false;
|
|
|
|
boolean workedMana = false;
|
|
|
|
boolean workedStamina = false;
|
|
|
|
|
|
|
|
float old, mod;
|
|
|
|
while (!workedHealth || !workedMana || !workedStamina) {
|
|
|
|
if (!this.isAlive() || !this.isActive())
|
|
|
|
return;
|
|
|
|
if (!workedHealth) {
|
|
|
|
old = this.health.get();
|
|
|
|
mod = old + healthRegen;
|
|
|
|
if (mod > this.healthMax)
|
|
|
|
mod = healthMax;
|
|
|
|
else if (mod <= 0) {
|
|
|
|
if (this.isAlive.compareAndSet(true, false))
|
|
|
|
killCharacter("Water");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
workedHealth = this.health.compareAndSet(old, mod);
|
|
|
|
}
|
|
|
|
if (!workedStamina) {
|
|
|
|
old = this.stamina.get();
|
|
|
|
mod = old + stamRegen;
|
|
|
|
if (mod > this.staminaMax)
|
|
|
|
mod = staminaMax;
|
|
|
|
else if (mod < 0)
|
|
|
|
mod = 0;
|
|
|
|
workedStamina = this.stamina.compareAndSet(old, mod);
|
|
|
|
}
|
|
|
|
if (!workedMana) {
|
|
|
|
old = this.mana.get();
|
|
|
|
mod = old + manaRegen;
|
|
|
|
if (mod > this.manaMax)
|
|
|
|
mod = manaMax;
|
|
|
|
else if (mod < 0)
|
|
|
|
mod = 0;
|
|
|
|
workedMana = this.mana.compareAndSet(old, mod);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (updateClient)
|
|
|
|
this.syncClient();
|
|
|
|
|
|
|
|
// Reset this char's frame time.
|
|
|
|
this.lastUpdateTime = System.currentTimeMillis();
|
|
|
|
this.lastStamUpdateTime = System.currentTimeMillis();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized void updateStamRegen(long time) {
|
|
|
|
|
|
|
|
boolean disable = true;
|
|
|
|
|
|
|
|
if (disable)
|
|
|
|
return;
|
|
|
|
|
|
|
|
float stamRegen = 0f;
|
|
|
|
|
|
|
|
// Early exit if char is dead or disconnected
|
|
|
|
if ((this.isAlive() == false)
|
|
|
|
|| (this.isActive() == false) || this.getLoc().x == 0 && this.getLoc().z == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Calculate Regen amount from last simulation tick
|
|
|
|
switch (this.movementState) {
|
|
|
|
|
|
|
|
case IDLE:
|
|
|
|
if (!PlayerManager.CanBreathe(this))
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_SWIM;
|
|
|
|
else if ((!this.isCasting() && !this.isItemCasting()) || this.lastMovementState.equals(MovementState.FLYING))
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_IDLE * getRegenModifier(ModType.StaminaRecoverRate);
|
|
|
|
else
|
|
|
|
stamRegen = 0;
|
|
|
|
break;
|
|
|
|
case SITTING:
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_SIT * getRegenModifier(ModType.StaminaRecoverRate);
|
|
|
|
break;
|
|
|
|
case RUNNING:
|
|
|
|
if (this.walkMode == true) {
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_WALK;
|
|
|
|
} else {
|
|
|
|
if (this.combat == true)
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_RUN_COMBAT;
|
|
|
|
else
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_RUN_NONCOMBAT;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FLYING:
|
|
|
|
|
|
|
|
if (this.getDesiredAltitude() == 0 && this.getAltitude() <= 10) {
|
|
|
|
if (this.isCombat())
|
|
|
|
stamRegen = 0;
|
|
|
|
else
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_IDLE * getRegenModifier(ModType.StaminaRecoverRate);
|
|
|
|
} else if (!this.isMoving()) {
|
|
|
|
|
|
|
|
|
|
|
|
if (!this.isItemCasting() && !this.isCasting() || this.getTakeOffTime() != 0)
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_FLY_IDLE;
|
|
|
|
else
|
|
|
|
stamRegen = -1f;
|
|
|
|
|
|
|
|
} else if (this.walkMode == true) {
|
|
|
|
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_FLY_WALK;
|
|
|
|
} else {
|
|
|
|
if (this.isCombat())
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_FLY_RUN_COMBAT;
|
|
|
|
else
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_FLY_RUN;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SWIMMING:
|
|
|
|
if (this.walkMode == true) {
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_SWIM;
|
|
|
|
} else {
|
|
|
|
stamRegen = MBServerStatics.STAMINA_REGEN_SWIM;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Multiple regen values by current deltaTime
|
|
|
|
// Logger.info("", healthRegen + "");
|
|
|
|
|
|
|
|
stamRegen *= (time * .001f);
|
|
|
|
|
|
|
|
|
|
|
|
boolean workedStamina = false;
|
|
|
|
|
|
|
|
|
|
|
|
float old, mod;
|
|
|
|
while (!workedStamina) {
|
|
|
|
if (!this.isAlive() || !this.isActive())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!workedStamina) {
|
|
|
|
old = this.stamina.get();
|
|
|
|
mod = old + stamRegen;
|
|
|
|
if (mod > this.staminaMax)
|
|
|
|
mod = staminaMax;
|
|
|
|
else if (mod < 0)
|
|
|
|
mod = 0;
|
|
|
|
workedStamina = this.stamina.compareAndSet(old, mod);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public void syncClient() {
|
|
|
|
|
|
|
|
ModifyHealthMsg modifyHealthMsg = new ModifyHealthMsg(null, this, 0, 1, 1, -1984683793, "", 0, 652920987);
|
|
|
|
//mhm.setOmitFromChat(0);
|
|
|
|
Dispatch dispatch = Dispatch.borrow(this, modifyHealthMsg);
|
|
|
|
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public MovementState getMovementState() {
|
|
|
|
return movementState;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getHash() {
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setHash() {
|
|
|
|
|
|
|
|
this.hash = DataWarehouse.hasher.encrypt(this.getObjectUUID());
|
|
|
|
|
|
|
|
// Write hash to player character table
|
|
|
|
|
|
|
|
DataWarehouse.writeHash(DataRecordType.CHARACTER, this.getObjectUUID());
|
|
|
|
}
|
|
|
|
|
|
|
|
public AtomicInteger getGuildStatus() {
|
|
|
|
return guildStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getLastRealmID() {
|
|
|
|
return lastRealmID;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setLastRealmID(int lastRealmID) {
|
|
|
|
this.lastRealmID = lastRealmID;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getSubRaceID() {
|
|
|
|
return subRaceID;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setSubRaceID(int subRaceID) {
|
|
|
|
this.subRaceID = subRaceID;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ArrayList<GuildHistory> getGuildHistory() {
|
|
|
|
return guildHistory;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setGuildHistory(ArrayList<GuildHistory> guildHistory) {
|
|
|
|
this.guildHistory = guildHistory;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void updateScaleHeight() {
|
|
|
|
|
|
|
|
float strengthScale = 0;
|
|
|
|
float unknownScale1 = 0;
|
|
|
|
float unknownScale2 = 0;
|
|
|
|
float unknownScale3 = 0;
|
|
|
|
|
|
|
|
float scaleHeight = 0;
|
|
|
|
|
|
|
|
if ((int) this.statStrBase > 40)
|
|
|
|
strengthScale = ((int) this.statStrBase - 40) * 0.0024999999f; //Y scale ?
|
|
|
|
|
|
|
|
unknownScale1 = (float) (((int) this.statStrBase * 0.0024999999f + strengthScale + 0.89999998) * race.getRaceType().getScaleHeight());
|
|
|
|
strengthScale = (int) this.statStrBase * 0.0037499999f + strengthScale + 0.85000002f; //strengthScale is different for x and z
|
|
|
|
|
|
|
|
unknownScale2 = strengthScale * race.getRaceType().getScaleHeight(); //x scale?
|
|
|
|
unknownScale3 = strengthScale * race.getRaceType().getScaleHeight(); //z Scale?
|
|
|
|
|
|
|
|
|
|
|
|
scaleHeight = (1.5f + unknownScale1);
|
|
|
|
|
|
|
|
|
|
|
|
this.characterHeight = scaleHeight;
|
|
|
|
|
|
|
|
this.centerHeight = scaleHeight;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getOverFlowEXP() {
|
|
|
|
return overFlowEXP;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setOverFlowEXP(int overFlowEXP) {
|
|
|
|
this.overFlowEXP = overFlowEXP;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setLastMovementState(MovementState lastMovementState) {
|
|
|
|
this.lastMovementState = lastMovementState;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public final void setIsCasting(final boolean isCasting) {
|
|
|
|
if (this.isCasting != isCasting)
|
|
|
|
this.update(false);
|
|
|
|
this.isCasting = isCasting;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setItemCasting(boolean itemCasting) {
|
|
|
|
if (this.itemCasting != itemCasting)
|
|
|
|
this.dynamicUpdate(UpdateType.REGEN);
|
|
|
|
this.itemCasting = itemCasting;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void resetRegenUpdateTime() {
|
|
|
|
this.lastUpdateTime = System.currentTimeMillis();
|
|
|
|
this.lastStamUpdateTime = System.currentTimeMillis();
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getCharacterHeight() {
|
|
|
|
return characterHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isEnteredWorld() {
|
|
|
|
return enteredWorld;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ReadWriteLock getTeleportLock() {
|
|
|
|
return teleportLock;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|