forked from MagicBane/Server
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2392 lines
91 KiB
2392 lines
91 KiB
package engine.gameManager; |
|
|
|
import engine.Enum; |
|
import engine.InterestManagement.InterestManager; |
|
import engine.InterestManagement.WorldGrid; |
|
import engine.exception.MsgSendException; |
|
import engine.exception.SerializationException; |
|
import engine.job.JobContainer; |
|
import engine.job.JobScheduler; |
|
import engine.jobs.FinishSpireEffectJob; |
|
import engine.jobs.NoTimeJob; |
|
import engine.math.Vector3fImmutable; |
|
import engine.net.ByteBufferWriter; |
|
import engine.net.Dispatch; |
|
import engine.net.DispatchMessage; |
|
import engine.net.client.ClientConnection; |
|
import engine.net.client.msg.*; |
|
import engine.net.client.msg.login.CommitNewCharacterMsg; |
|
import engine.objects.*; |
|
import engine.powers.EffectsBase; |
|
import engine.server.MBServerStatics; |
|
import engine.server.login.LoginServer; |
|
import engine.server.login.LoginServerMsgHandler; |
|
import engine.server.world.WorldServer; |
|
import engine.util.MiscUtils; |
|
import org.joda.time.DateTime; |
|
import org.pmw.tinylog.Logger; |
|
|
|
import java.util.*; |
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
public class PlayerManager { |
|
public static Building getUpdatedBindBuilding(PlayerCharacter player) { |
|
Building returnBuilding = null; |
|
|
|
//update bindBuilding based on Guild or nation TOL; |
|
|
|
if (player.getBindBuildingID() == 0) { |
|
|
|
returnBuilding = getBindBuildingForGuild(player); |
|
|
|
if (returnBuilding != null) |
|
player.setBindBuildingID(returnBuilding.getObjectUUID()); |
|
return returnBuilding; |
|
} |
|
returnBuilding = BuildingManager.getBuildingFromCache(player.getBindBuildingID()); |
|
|
|
if (returnBuilding == null) { |
|
returnBuilding = getBindBuildingForGuild(player); |
|
|
|
if (returnBuilding != null) |
|
player.setBindBuildingID(returnBuilding.getObjectUUID()); |
|
} |
|
return returnBuilding; |
|
} |
|
|
|
public static Building getBindBuildingForGuild(PlayerCharacter player) { |
|
|
|
Building returnBuilding; |
|
|
|
if (player.getGuild() == null || player.getGuild().isEmptyGuild()) |
|
return null; |
|
|
|
if (player.getGuild().getOwnedCity() == null) { |
|
|
|
if (player.getGuild().getNation().getOwnedCity() == null) |
|
return null; |
|
|
|
if (player.getGuild().getNation().getOwnedCity().getTOL() == null) |
|
return null; |
|
|
|
returnBuilding = player.getGuild().getNation().getOwnedCity().getTOL(); |
|
player.setBindBuildingID(returnBuilding.getObjectUUID()); |
|
return returnBuilding; |
|
} |
|
|
|
if (player.getGuild().getOwnedCity().getTOL() == null) |
|
return null; |
|
|
|
returnBuilding = player.getGuild().getOwnedCity().getTOL(); |
|
return returnBuilding; |
|
} |
|
|
|
public static void __serializeForClientMsg(PlayerCharacter playerCharacter, ByteBufferWriter writer) throws SerializationException { |
|
serializeForClientCommon(playerCharacter, writer, true, false, false, false); |
|
} |
|
|
|
public static void serializeForClientMsgLogin(PlayerCharacter playerCharacter, ByteBufferWriter writer) throws SerializationException { |
|
serializeForClientCommon(playerCharacter, writer, true, false, false, false); |
|
} |
|
|
|
public static void serializeForClientMsgCommit(PlayerCharacter playerCharacter, ByteBufferWriter writer) throws SerializationException { |
|
serializeForClientCommon(playerCharacter, writer, true, true, false, false); |
|
} |
|
|
|
public static void serializeForClientMsgFull(PlayerCharacter playerCharacter, ByteBufferWriter writer) throws SerializationException { |
|
serializeForClientCommon(playerCharacter, writer, false, false, false, false); |
|
} |
|
|
|
public static void serializeForClientMsgOtherPlayer(PlayerCharacter playerCharacter, ByteBufferWriter writer) throws SerializationException { |
|
serializeForClientCommon(playerCharacter, writer, false, false, true, false); |
|
} |
|
|
|
public static void serializePlayerForClientMsgOtherPlayer(PlayerCharacter playerCharacter, ByteBufferWriter writer, boolean hideAsciiLastName) throws SerializationException { |
|
serializeForClientCommon(playerCharacter, writer, false, false, true, hideAsciiLastName); |
|
} |
|
|
|
// TODO what is a Fresh Char? |
|
private static void serializeForClientCommon(PlayerCharacter playerCharacter, ByteBufferWriter writer, boolean loginData, boolean freshChar, boolean otherPlayer, boolean hideAsciiLastName) |
|
throws SerializationException { |
|
|
|
/* |
|
* RUNES |
|
*/ |
|
// Handle Applied Runes |
|
writer.putInt(0); // Pad |
|
writer.putInt(0); // Pad |
|
|
|
// Put number of runes |
|
//We need to send all runes to everyone, otherwise playerCharacter will cause major issues |
|
if (playerCharacter.getPromotionClass() != null) |
|
writer.putInt(playerCharacter.getRunes().size() + 3); |
|
else |
|
writer.putInt(playerCharacter.getRunes().size() + 2); |
|
|
|
// Cant forget that Race and baseClass are technically Runes :0 |
|
if (playerCharacter.getSubRaceID() != 0) { |
|
writer.putInt(1); // For Race |
|
writer.putInt(0); // Pad |
|
writer.putInt(playerCharacter.getSubRaceID()); |
|
|
|
writer.putInt(Enum.GameObjectType.Race.ordinal()); |
|
writer.putInt(playerCharacter.getSubRaceID()); |
|
} else |
|
playerCharacter.getRace().serializeForClientMsg(writer); |
|
if (playerCharacter.getPromotionClass() != null) { |
|
BaseClass.serializeForClientMsg(playerCharacter.getBaseClass(), writer, 2); |
|
PromotionClass.serializeForClientMsg(playerCharacter.getPromotionClass(), writer); |
|
} else |
|
BaseClass.serializeForClientMsg(playerCharacter.getBaseClass(), writer, 3); |
|
|
|
// Put runes. |
|
|
|
for (CharacterRune rb : playerCharacter.getRunes()) { |
|
CharacterRune.serializeForClientMsg(rb, writer); |
|
} |
|
|
|
/* |
|
* STATS |
|
*/ |
|
// Number of Stats to follow |
|
writer.putInt(5); |
|
|
|
writer.putInt(MBServerStatics.STAT_STR_ID); // Strength ID |
|
writer.putInt(freshChar ? 0 : playerCharacter.getStrMod()); |
|
|
|
writer.putInt(MBServerStatics.STAT_SPI_ID); // Spirit ID |
|
writer.putInt(freshChar ? 0 : playerCharacter.getSpiMod()); |
|
|
|
writer.putInt(MBServerStatics.STAT_CON_ID); // Constitution ID |
|
writer.putInt(freshChar ? 0 : playerCharacter.getConMod()); |
|
|
|
writer.putInt(MBServerStatics.STAT_DEX_ID); // Dexterity ID |
|
writer.putInt(freshChar ? 0 : playerCharacter.getDexMod()); |
|
|
|
writer.putInt(MBServerStatics.STAT_INT_ID); // Intelligence ID |
|
writer.putInt(freshChar ? 0 : playerCharacter.getIntMod()); |
|
|
|
// Handle Info |
|
playerCharacter.title._serializeFirstName(writer, playerCharacter.getFirstName()); |
|
playerCharacter.title._serializeLastName(writer, playerCharacter.getLastName(), hideAsciiLastName, playerCharacter.asciiLastName); |
|
|
|
// Unknown |
|
writer.putInt(0); |
|
|
|
writer.putString(ConfigManager.MB_WORLD_NAME.getValue()); |
|
writer.putInt(WorldServer.worldMapID); |
|
|
|
writer.put((byte) 1); // End Datablock byte |
|
writer.putInt(0); // Unsure, Pad? |
|
writer.putInt(playerCharacter.getObjectType().ordinal()); |
|
writer.putInt(playerCharacter.getObjectUUID()); |
|
|
|
// Perhaps playerCharacter is loc and the next 3 are Facing dir? |
|
writer.putFloat(1); // Unknown |
|
writer.putFloat(playerCharacter.getRace().getRaceType().getScaleHeight()); // Unknown |
|
writer.putFloat(1); // Unknown |
|
|
|
writer.putVector3f(playerCharacter.getLoc()); |
|
writer.putFloat(playerCharacter.getFaceDir().getRotation()); // Rotation, direction |
|
|
|
// facing |
|
|
|
// Running trains. |
|
|
|
if (otherPlayer) { |
|
CharacterSkill runSkill = playerCharacter.getSkills().get("Running"); |
|
if (runSkill == null) |
|
// Logger.log.log( |
|
// LogEventType.WARNING, |
|
// "Failed to find the 'Running Skill' when serializing PlayerCharacter '" |
|
// + playerCharacter.getCombinedName() + "'"); |
|
// TODO put int=0 for now. |
|
writer.putInt(0); |
|
else |
|
writer.putInt(runSkill.getNumTrains()); |
|
} else |
|
writer.putInt(0); |
|
|
|
|
|
ArrayList<Item> equipped = playerCharacter.getCharItemManager().getEquippedList(); |
|
|
|
writer.putInt(equipped.size()); |
|
for (Item item : equipped) { |
|
Item._serializeForClientMsg(item, writer); |
|
} |
|
writer.putInt(playerCharacter.getRank()); |
|
|
|
writer.putInt(playerCharacter.getLevel()); |
|
if (loginData) |
|
writer.putInt(5); |
|
else |
|
writer.putInt(playerCharacter.getIsSittingAsInt()); // 5 |
|
writer.putInt(playerCharacter.getIsWalkingAsInt()); // 1 |
|
writer.putInt(playerCharacter.getIsCombatAsInt()); // 1 |
|
writer.putInt(playerCharacter.getIsFlightAsInt()); // 2 or 3 |
|
|
|
writer.putInt(playerCharacter.getIsLfGroupAsInt()); // 1 |
|
|
|
// if (loginData) |
|
// writer.putInt(0); |
|
// else |
|
writer.putInt(playerCharacter.getHeadlightsAsInt()); |
|
|
|
|
|
if (playerCharacter.region != null && !loginData) { |
|
Building building = Regions.GetBuildingForRegion(playerCharacter.region); |
|
|
|
if (building == null) { |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
} else { |
|
writer.putInt(Enum.GameObjectType.Building.ordinal()); |
|
writer.putInt(building.getObjectUUID()); |
|
} |
|
|
|
} else { |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
} |
|
|
|
|
|
writer.put((byte) 0); |
|
writer.put((byte) 0); |
|
writer.put((byte) 0); |
|
writer.putInt(0); |
|
writer.put((byte) 0); |
|
writer.put((byte) 0); |
|
writer.put((byte) 0); |
|
|
|
// writer.putInt(0); |
|
// writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
|
|
if (!playerCharacter.isAlive() && otherPlayer) { |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
} |
|
|
|
|
|
//TODO FIGURE OUT THE REAL SEARLIZATION FOR NEXT 2 SHORTS? |
|
writer.putInt(playerCharacter.getSkinColor()); // Skin Color |
|
writer.putFloat(20); |
|
writer.put((byte) 0); //Unknown |
|
|
|
//unknown object |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
|
|
//unknown type |
|
writer.putInt(0); |
|
//0x4080 should be the next short here, instead it wraps 0's down their in for loops.. seriously.. who wrote playerCharacter shit. |
|
// playerCharacter aint right! |
|
// ByteBufferUtils.putString(writer, playerCharacter.guild.getName()); |
|
// writer.putInt(playerCharacter.getGuild().getUUID()); |
|
// ByteBufferUtils.putString(writer, playerCharacter.guild.getNation().getName()); |
|
// writer.putInt(playerCharacter.getGuild().getNation().getUUID()); |
|
Guild.serializeForClientMsg(playerCharacter.getGuild(), writer, playerCharacter, false); |
|
|
|
//Send Tokens for race/class/promotion (disciplines?) |
|
if (playerCharacter.getPromotionClass() != null) |
|
writer.putInt(3); |
|
else |
|
writer.putInt(2); |
|
writer.putInt(playerCharacter.getRace().getToken()); |
|
writer.putInt(playerCharacter.getBaseClass().getToken()); |
|
if (playerCharacter.getPromotionClass() != null) |
|
writer.putInt(playerCharacter.getPromotionClass().getToken()); |
|
|
|
writer.putFloat(playerCharacter.getAltitude()); // altitude? |
|
writer.putFloat(playerCharacter.getAltitude()); // altitude? |
|
writer.put((byte) 0); // End Datablock byte |
|
|
|
writer.putFloat(playerCharacter.healthMax); |
|
writer.putFloat(playerCharacter.health.get()); |
|
|
|
writer.put((byte) 0); // End Datablock byte |
|
//size |
|
|
|
|
|
if (loginData) { |
|
writer.putInt(0); |
|
} else { |
|
int indexPosition = writer.position(); |
|
writer.putInt(0); //placeholder for item cnt |
|
int total = 0; |
|
// Logger.info("",""+ playerCharacter.getEffects().size()); |
|
for (Effect eff : playerCharacter.getEffects().values()) { |
|
if (eff.getPower() == null && otherPlayer) |
|
continue; |
|
if (eff.getPower() != null && eff.getPower().token == 429506619) // Oblivion's Caress |
|
continue; |
|
if (!eff.serializeForLoad(writer)) |
|
continue; |
|
++total; |
|
|
|
} |
|
|
|
writer.putIntAt(total, indexPosition); |
|
} |
|
|
|
if (otherPlayer) { |
|
writer.put((byte) 0); // End Datablock Byte |
|
return; |
|
|
|
} |
|
|
|
//made up for sendalleffects |
|
//writer.putInt(0); // Pad |
|
//writer.put((byte) 0); // End Datablock byte |
|
writer.putInt(playerCharacter.getUnusedStatPoints()); |
|
writer.putInt(playerCharacter.getLevel()); |
|
writer.putInt(playerCharacter.getExp() + playerCharacter.getOverFlowEXP()); |
|
writer.putFloat(playerCharacter.getManaMax()); |
|
writer.putFloat(playerCharacter.mana.get()); |
|
writer.putFloat(playerCharacter.getStaminaMax()); |
|
writer.putFloat(playerCharacter.stamina.get()); |
|
writer.putInt(playerCharacter.getAtrHandOne()); |
|
writer.putInt(playerCharacter.getAtrHandTwo()); |
|
writer.putInt(playerCharacter.getDefenseRating()); |
|
|
|
if (MBServerStatics.POWERS_DEBUG) //debug mode, grant lots of trains |
|
writer.putInt(1000); |
|
else |
|
writer.putInt(playerCharacter.trainsAvailable.get()); |
|
|
|
/* |
|
* Skills |
|
*/ |
|
if (loginData) |
|
writer.putInt(0); // Skip skills |
|
else { |
|
writer.putInt(playerCharacter.getSkills().size()); |
|
Iterator<String> it = playerCharacter.getSkills().keySet().iterator(); |
|
while (it.hasNext()) { |
|
String name = it.next(); |
|
CharacterSkill.serializeForClientMsg(playerCharacter.getSkills().get(name), writer); |
|
} |
|
} |
|
|
|
/* |
|
* Powers |
|
*/ |
|
if (loginData) |
|
writer.putInt(0); // Skip Powers |
|
else if (MBServerStatics.POWERS_DEBUG) //debug mode, grant all powers |
|
PowersManager.testPowers(writer); |
|
else { |
|
writer.putInt(playerCharacter.getPowers().size()); |
|
for (CharacterPower sp : playerCharacter.getPowers().values()) { |
|
CharacterPower.serializeForClientMsg(sp, writer); |
|
} |
|
} |
|
|
|
/* |
|
* Inventory |
|
*/ |
|
if (loginData) { |
|
writer.putInt(0); // Skip Inventory |
|
writer.putInt(playerCharacter.getInventoryCapacity()); // Inventory Capacity |
|
|
|
} else { |
|
ArrayList<Item> inv = playerCharacter.getCharItemManager().getInventory(true); |
|
Item.putList(writer, inv, false, playerCharacter.getObjectUUID()); |
|
writer.putInt(playerCharacter.getInventoryCapacityRemaining()); |
|
} |
|
|
|
/* |
|
* Bank |
|
*/ |
|
if (loginData) { |
|
writer.putInt(0); // Skip Bank |
|
writer.putInt(AbstractCharacter.getBankCapacity()); // Bank Capacity |
|
|
|
} else { |
|
ArrayList<Item> bank = playerCharacter.getCharItemManager().getBank(); |
|
|
|
Item.putList(writer, bank, false, playerCharacter.getObjectUUID()); |
|
writer.putInt(playerCharacter.getBankCapacityRemaining()); |
|
} |
|
//load player friends. |
|
if (loginData) |
|
writer.putInt(0); |
|
else { |
|
HashSet<Integer> friendMap = PlayerFriends.PlayerFriendsMap.get(playerCharacter.getObjectUUID()); |
|
if (friendMap == null) |
|
writer.putInt(0); |
|
else { |
|
writer.putInt(friendMap.size()); |
|
for (int friendID : friendMap) { |
|
PlayerCharacter friend = getFromCache(friendID); |
|
//shouldn't get here, but if null serialize blank friend. |
|
if (friend == null) { |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
} else { |
|
writer.putInt(friend.getObjectType().ordinal()); |
|
writer.putInt(friend.getObjectUUID()); |
|
writer.putString(friend.getName()); |
|
boolean online = SessionManager.getPlayerCharacterByID(friend.getObjectUUID()) != null ? true : false; |
|
writer.putInt(online ? 0 : 1); |
|
writer.putInt(friend.friendStatus.ordinal()); |
|
} |
|
|
|
} |
|
} |
|
} |
|
|
|
|
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
writer.putInt(0); |
|
|
|
writer.putShort((short) 0); |
|
writer.put((byte) 0); |
|
// playerCharacter is for send self in enter world (full character) |
|
if (!loginData && !freshChar) { |
|
int size = playerCharacter.getRecycleTimers().size(); |
|
writer.putInt(size); |
|
if (size > 0) |
|
for (int token : playerCharacter.getRecycleTimers().keySet()) { |
|
|
|
JobContainer frtj = playerCharacter.getRecycleTimers().get(token); |
|
long timeLeft = frtj.timeOfExection() - System.currentTimeMillis(); |
|
writer.putInt(token); |
|
writer.putInt((int) timeLeft / 1000); |
|
} |
|
DateTime enterWorld = new DateTime(playerCharacter.getTimestamps().get("EnterWorld")); |
|
writer.putDateTime(enterWorld); |
|
|
|
writer.putInt(0x49EF1E98); //DUnno what playerCharacter is. |
|
writer.putFloat(DateTime.now().hourOfDay().get()); //daylight in float. |
|
writer.putFloat(6); //interval of light to change per game hour //float |
|
//writer.putInt(1637194901); //playerCharacter is actually an opcode taht is in recordings, no clue what it is, dumped it and it changes nothing |
|
} else { |
|
writer.put((byte) 0); //added to compensate the cooldown check. |
|
|
|
//add server up or down |
|
int serverUp = LoginServer.worldServerRunning ? 1 : 0; |
|
|
|
if (playerCharacter.getAccount() == null) |
|
serverUp = 0; |
|
|
|
if ((playerCharacter.getAccount().status.equals(Enum.AccountStatus.ADMIN) == false) && |
|
(playerCharacter.getAccount().status.equals(WorldServer.worldAccessLevel) == false)) |
|
serverUp = 0; |
|
|
|
writer.putInt(serverUp); |
|
writer.putInt(0); // effects, not sure used by players |
|
writer.put((byte) 0); // End Player Datablock |
|
} |
|
|
|
} |
|
|
|
public static PlayerCharacter generatePCFromCommitNewCharacterMsg(Account a, CommitNewCharacterMsg msg, ClientConnection clientConnection) { |
|
|
|
String firstName = msg.getFirstName().trim(); |
|
String lastName = msg.getLastName().trim(); |
|
|
|
if (firstName.length() < 3) { |
|
LoginServerMsgHandler.sendInvalidNameMsg(firstName, lastName, MBServerStatics.INVALIDNAME_FIRSTNAME_MUST_BE_LONGER, |
|
clientConnection); |
|
return null; |
|
} |
|
|
|
// Ensure names are below required length |
|
if (firstName.length() > 15 || lastName.length() > 15) { |
|
LoginServerMsgHandler.sendInvalidNameMsg(firstName, lastName, MBServerStatics.INVALIDNAME_FIRSTANDLAST_MUST_BE_SHORTER, |
|
clientConnection); |
|
return null; |
|
} |
|
|
|
// Check if firstname is valid |
|
if (MiscUtils.checkIfFirstNameInvalid(firstName)) { |
|
LoginServerMsgHandler.sendInvalidNameMsg(firstName, lastName, MBServerStatics.INVALIDNAME_PLEASE_CHOOSE_ANOTHER_FIRSTNAME, |
|
clientConnection); |
|
return null; |
|
} |
|
|
|
// Check if last name is valid |
|
if (MiscUtils.checkIfLastNameInvalid(lastName)) { |
|
LoginServerMsgHandler.sendInvalidNameMsg(firstName, lastName, MBServerStatics.INVALIDNAME_LASTNAME_UNAVAILABLE, |
|
clientConnection); |
|
return null; |
|
} |
|
|
|
// Verify Race |
|
int raceID = msg.getRace(); |
|
|
|
if(raceID == 0) |
|
raceID = 1999; |
|
|
|
Race race = Race.getRace(raceID); |
|
|
|
if (race == null) { |
|
Logger.info("Invalid RaceID: " + raceID); |
|
return null; |
|
} |
|
|
|
// Verify BaseClass Object. |
|
int baseClassID = msg.getBaseClass(); |
|
BaseClass baseClass = DbManager.BaseClassQueries.GET_BASE_CLASS(baseClassID); |
|
|
|
if (baseClass == null) { |
|
Logger.info("Invalid BaseClasID: " + baseClassID); |
|
return null; |
|
} |
|
|
|
// Verify Race/baseClass combo. |
|
boolean valid = false; |
|
|
|
for (BaseClass bc : race.getValidBaseClasses()) { |
|
|
|
if (bc.getObjectUUID() == baseClassID) { |
|
valid = true; |
|
break; |
|
} |
|
} |
|
|
|
if (!valid) { |
|
Logger.info("Invalid BaseClass/Race Combo"); |
|
return null; |
|
} |
|
|
|
// Verify HairStyle/BeardStyle/SkinColor/HairColor/BeardColor |
|
int hairStyleID = msg.getHairStyle(); |
|
int beardStyleID = msg.getBeardStyle(); |
|
int skinColorID = msg.getSkinColor(); |
|
int hairColorID = msg.getHairColor(); |
|
int beardColorID = msg.getBeardColor(); |
|
|
|
if(raceID != 1999) { |
|
if (!race.isValidHairStyle(hairStyleID)) { |
|
Logger.info("Invalid HairStyleID: " + hairStyleID + " for race: " + race.getName()); |
|
return null; |
|
} |
|
|
|
if (!race.isValidSkinColor(skinColorID)) { |
|
Logger.info("Invalid skinColorID: " + skinColorID + " for race: " + race.getName()); |
|
return null; |
|
} |
|
|
|
if (!race.isValidHairColor(hairColorID)) { |
|
Logger.info("Invalid hairColorID: " + hairColorID + " for race: " + race.getName()); |
|
return null; |
|
} |
|
|
|
if (!race.isValidBeardColor(beardColorID)) { |
|
Logger.info("Invalid beardColorID: " + beardColorID + " for race: " + race.getName()); |
|
return null; |
|
} |
|
} |
|
// Get stat modifiers |
|
int strMod = msg.getStrengthMod(); |
|
int dexMod = msg.getDexterityMod(); |
|
int conMod = msg.getConstitutionMod(); |
|
int intMod = msg.getIntelligenceMod(); |
|
int spiMod = msg.getSpiritMod(); |
|
|
|
|
|
if (intMod < -5 || dexMod < -5 || conMod < -5 || strMod < -5 || spiMod < -5) { |
|
Logger.error("NEGATIVE STAT CHEAT ATTEMPTED! ACCOUNT: " + a.getUname() + "(" + a.getObjectUUID() + ") IP ADDRESS: " + clientConnection.getClientIpAddress()); |
|
return null; |
|
} |
|
|
|
// calculate current stats: |
|
short strCur = (short) (race.getStrStart() + baseClass.getStrMod() + strMod); |
|
short dexCur = (short) (race.getDexStart() + baseClass.getDexMod() + dexMod); |
|
short conCur = (short) (race.getConStart() + baseClass.getConMod() + conMod); |
|
short intCur = (short) (race.getIntStart() + baseClass.getIntMod() + intMod); |
|
short spiCur = (short) (race.getSpiStart() + baseClass.getSpiMod() + spiMod); |
|
|
|
// calculate max stats: |
|
short strMax = race.getStrMax(); |
|
short dexMax = race.getDexMax(); |
|
short conMax = race.getConMax(); |
|
short intMax = race.getIntMax(); |
|
short spiMax = race.getSpiMax(); |
|
|
|
// Verify not too many runes applied |
|
int numRunes = msg.getNumRunes(); |
|
|
|
if (numRunes > 16) { |
|
Logger.info("Too many Runes applied"); |
|
return null; |
|
} |
|
|
|
// Get Runes |
|
// ArrayList<RuneBase> characterRunesUsed = new ArrayList<RuneBase>(); |
|
// ArrayList<Byte> subtypesUsed = new ArrayList<Byte>(); |
|
int remainingPoints = race.getStartingPoints() - strMod - dexMod - conMod - intMod - spiMod; |
|
|
|
int[] characterRunes = msg.getRunes(); |
|
|
|
HashSet<Byte> usedRunesSubType = new HashSet<>(); |
|
HashSet<RuneBase> usedRunes = new HashSet<>(); |
|
|
|
// So that all the penalties can be added at the end. |
|
ConcurrentHashMap<String, Integer> penalties = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); |
|
|
|
penalties.put("StrCur", 0); |
|
penalties.put("StrMax", 0); |
|
penalties.put("DexCur", 0); |
|
penalties.put("DexMax", 0); |
|
penalties.put("ConCur", 0); |
|
penalties.put("ConMax", 0); |
|
penalties.put("IntCur", 0); |
|
penalties.put("IntMax", 0); |
|
penalties.put("SpiCur", 0); |
|
penalties.put("SpiMax", 0); |
|
|
|
PriorityQueue<Map.Entry<Integer, RuneBase>> orderedRunes = new PriorityQueue<>(14, |
|
new Comparator<Map.Entry<Integer, RuneBase>>() { |
|
|
|
@Override |
|
public int compare(Map.Entry<Integer, RuneBase> o1, Map.Entry<Integer, RuneBase> o2) { |
|
return o1.getKey() - o2.getKey(); |
|
} |
|
}); |
|
|
|
// Figure out which Runes we are adding. |
|
for (int i : characterRunes) { |
|
// Zero skip |
|
if (i == 0) |
|
continue; |
|
|
|
// Skip the Race and BaseClass runes... already dealt with. |
|
if (i == raceID || i == baseClassID) |
|
continue; |
|
|
|
RuneBase runeBase = RuneBase.getRuneBase(i); |
|
|
|
// Null check |
|
if (runeBase == null) { |
|
Logger.info("GOM returned NULL RuneBase"); |
|
return null; |
|
} |
|
|
|
// Validate Rune against Race |
|
if(raceID != 1999) { |
|
if (!race.isAllowedRune(runeBase)) { |
|
Logger.info("Trait Not valid for Race"); |
|
return null; |
|
} |
|
} |
|
// Validate BaseClass against Race |
|
if (!baseClass.isAllowedRune(runeBase)) { |
|
Logger.info("Trait Not valid for BaseClass"); |
|
return null; |
|
} |
|
|
|
int previous_size = usedRunes.size(); |
|
int previous_subtype = usedRunesSubType.size(); |
|
|
|
usedRunes.add(runeBase); |
|
usedRunesSubType.add(runeBase.getSubtype()); |
|
|
|
// Duplicate Rune check |
|
if (usedRunes.size() <= previous_size) { |
|
Logger.info("Duplicate RuneBase"); |
|
return null; |
|
} |
|
|
|
// Duplicate Subtype check |
|
if (runeBase.getSubtype() != 0 && usedRunesSubType.size() <= previous_subtype) { |
|
Logger.info("Duplicate RuneBase Subtype"); |
|
return null; |
|
} |
|
|
|
int maxValue = 0; |
|
|
|
// Every attempt is made to load MIN_NEEDED_ATTRIBUTES first. |
|
|
|
if (runeBase.getAttrs() != null) |
|
for (RuneBaseAttribute rba : runeBase.getAttrs()) { |
|
if (rba.getAttributeID() == MBServerStatics.RUNE_STR_MIN_NEEDED_ATTRIBUTE_ID |
|
|| rba.getAttributeID() == MBServerStatics.RUNE_DEX_MIN_NEEDED_ATTRIBUTE_ID |
|
|| rba.getAttributeID() == MBServerStatics.RUNE_CON_MIN_NEEDED_ATTRIBUTE_ID |
|
|| rba.getAttributeID() == MBServerStatics.RUNE_INT_MIN_NEEDED_ATTRIBUTE_ID |
|
|| rba.getAttributeID() == MBServerStatics.RUNE_SPI_MIN_NEEDED_ATTRIBUTE_ID) { |
|
maxValue = rba.getModValue(); |
|
if (runeBase.getName().equals("Giant's Blood")) |
|
maxValue = 45; // Take care of the Giant's Blood special |
|
// case. |
|
break; |
|
} |
|
} |
|
|
|
orderedRunes.add(new AbstractMap.SimpleEntry<>(maxValue, runeBase)); |
|
} |
|
|
|
while (orderedRunes.size() > 0) { |
|
RuneBase rb = orderedRunes.remove().getValue(); |
|
ArrayList<RuneBaseAttribute> attrs = rb.getAttrs(); |
|
|
|
if (attrs != null) |
|
for (RuneBaseAttribute abr : attrs) { |
|
|
|
int attrID = abr.getAttributeID(); |
|
int value = abr.getModValue(); |
|
|
|
switch (attrID) { |
|
case MBServerStatics.RUNE_COST_ATTRIBUTE_ID: |
|
|
|
Logger.info("Bought " + rb.getName() + " for " + value + " points. " |
|
+ (remainingPoints - value) + " left."); |
|
|
|
if ((remainingPoints - value) >= 0) { |
|
remainingPoints -= value; |
|
continue; |
|
} |
|
Logger.info("Not enough points left"); |
|
return null; |
|
case MBServerStatics.RUNE_STR_MIN_NEEDED_ATTRIBUTE_ID: |
|
|
|
if (strCur >= value) |
|
continue; |
|
|
|
Logger.info("STR fails to meet Rune Minimum --> " + rb.getName()); |
|
return null; |
|
case MBServerStatics.RUNE_DEX_MIN_NEEDED_ATTRIBUTE_ID: |
|
|
|
if (dexCur >= value) |
|
continue; |
|
|
|
Logger.info("DEX fails to meet Rune Minimum --> " + rb.getName()); |
|
return null; |
|
case MBServerStatics.RUNE_CON_MIN_NEEDED_ATTRIBUTE_ID: |
|
|
|
if (conCur >= value) |
|
continue; |
|
|
|
Logger.info("CON fails to meet Rune Minimum --> " + rb.getName()); |
|
return null; |
|
case MBServerStatics.RUNE_INT_MIN_NEEDED_ATTRIBUTE_ID: |
|
|
|
if (intCur >= value) |
|
continue; |
|
|
|
Logger.info("INT fails to meet Rune Minimum --> " + rb.getName()); |
|
return null; |
|
case MBServerStatics.RUNE_SPI_MIN_NEEDED_ATTRIBUTE_ID: |
|
|
|
if (spiCur >= value) |
|
continue; |
|
|
|
Logger.info("SPI fails to meet Rune Minimum --> " + rb.getName()); |
|
return null; |
|
case MBServerStatics.RUNE_STR_ATTRIBUTE_ID: |
|
|
|
if (value < 0) |
|
penalties.put("StrCur", (penalties.get("StrCur") + value)); |
|
else |
|
strCur += value; |
|
continue; |
|
|
|
case MBServerStatics.RUNE_DEX_ATTRIBUTE_ID: |
|
if (value < 0) |
|
penalties.put("DexCur", (penalties.get("DexCur") + value)); |
|
else |
|
dexCur += value; |
|
continue; |
|
case MBServerStatics.RUNE_CON_ATTRIBUTE_ID: |
|
if (value < 0) |
|
penalties.put("ConCur", (penalties.get("ConCur") + value)); |
|
else |
|
conCur += value; |
|
continue; |
|
case MBServerStatics.RUNE_INT_ATTRIBUTE_ID: |
|
if (value < 0) |
|
penalties.put("IntCur", (penalties.get("IntCur") + value)); |
|
else |
|
intCur += value; |
|
continue; |
|
case MBServerStatics.RUNE_SPI_ATTRIBUTE_ID: |
|
if (value < 0) |
|
penalties.put("SpiCur", (penalties.get("SpiCur") + value)); |
|
else |
|
spiCur += value; |
|
continue; |
|
case MBServerStatics.RUNE_STR_MAX_ATTRIBUTE_ID: |
|
if (value < 0) |
|
penalties.put("StrMax", (penalties.get("StrMax") + value)); |
|
else |
|
strMax += value; |
|
continue; |
|
case MBServerStatics.RUNE_DEX_MAX_ATTRIBUTE_ID: |
|
if (value < 0) |
|
penalties.put("DexMax", (penalties.get("DexMax") + value)); |
|
else |
|
dexMax += value; |
|
continue; |
|
case MBServerStatics.RUNE_CON_MAX_ATTRIBUTE_ID: |
|
if (value < 0) |
|
penalties.put("ConMax", (penalties.get("ConMax") + value)); |
|
else |
|
conMax += value; |
|
continue; |
|
case MBServerStatics.RUNE_INT_MAX_ATTRIBUTE_ID: |
|
if (value < 0) |
|
penalties.put("IntMax", (penalties.get("IntMax") + value)); |
|
else |
|
intMax += value; |
|
continue; |
|
case MBServerStatics.RUNE_SPI_MAX_ATTRIBUTE_ID: |
|
if (value < 0) |
|
penalties.put("SpiMax", (penalties.get("SpiMax") + value)); |
|
else |
|
spiMax += value; |
|
continue; |
|
|
|
default: |
|
Logger.info("Unknown ATTRIBUTE_ID while checking RuneBaseAttributes: " + attrID); |
|
return null; |
|
} |
|
} |
|
} |
|
|
|
// Add in all of the penalties. |
|
strCur += penalties.get("StrCur"); |
|
strMax += penalties.get("StrMax"); |
|
dexCur += penalties.get("DexCur"); |
|
dexMax += penalties.get("DexMax"); |
|
conCur += penalties.get("ConCur"); |
|
conMax += penalties.get("ConMax"); |
|
intCur += penalties.get("IntCur"); |
|
intMax += penalties.get("IntMax"); |
|
spiCur += penalties.get("SpiCur"); |
|
spiMax += penalties.get("SpiMax"); |
|
|
|
int kitID = msg.getKit(); |
|
|
|
// get the correctKit |
|
int raceClassID; |
|
if(raceID != 1999){ |
|
raceClassID = Kit.GetKitIDByRaceClass(raceID, baseClassID); |
|
}else{ |
|
raceClassID = Kit.GetKitIDByRaceClass(2011, baseClassID); |
|
} |
|
ArrayList<Kit> allKits = Kit.RaceClassIDMap.get(raceClassID); |
|
|
|
|
|
Kit kit = null; |
|
|
|
for (Kit k : allKits) { |
|
if (k.getKitNumber() == kitID) { |
|
kit = k; |
|
break; |
|
} |
|
} |
|
|
|
if (kit == null) { |
|
Logger.info("Unable to find matching kitID: " + kitID); |
|
return null; |
|
} |
|
|
|
byte runningTrains = 0; |
|
PlayerCharacter playerCharacter; |
|
|
|
//Synchronized block to allow exclusive access when confirming |
|
//uniqueness of FirstName and subsequently saving the new record |
|
//to the database with that FirstName |
|
synchronized (PlayerCharacter.FirstNameLock) { |
|
// Test if FirstName already exists. |
|
// This must be the very last check before calling the |
|
// DB to create the character record |
|
if (DbManager.PlayerCharacterQueries.IS_CHARACTER_NAME_UNIQUE(firstName) == false) { |
|
LoginServerMsgHandler.sendInvalidNameMsg(firstName, lastName, MBServerStatics.INVALIDNAME_FIRSTNAME_UNAVAILABLE, |
|
clientConnection); |
|
return null; |
|
} |
|
if(raceID == 1999){ |
|
hairStyleID = 0; |
|
beardStyleID = 0; |
|
skinColorID = 0; |
|
hairColorID = 0; |
|
beardColorID = 0; |
|
} |
|
|
|
// Make PC |
|
PlayerCharacter pcWithoutID = new PlayerCharacter(firstName, lastName, (short) strMod, (short) dexMod, (short) conMod, |
|
(short) intMod, (short) spiMod, Guild.getErrantGuild(), runningTrains, a, race, baseClass, (byte) skinColorID, (byte) hairColorID, |
|
(byte) beardColorID, (byte) beardStyleID, (byte) hairStyleID); |
|
|
|
try { |
|
playerCharacter = DbManager.PlayerCharacterQueries.ADD_PLAYER_CHARACTER(pcWithoutID); |
|
} catch (Exception e) { |
|
Logger.error("generatePCFromCommitNewCharacterMsg", "An error occurred while saving new PlayerCharacter to DB", e); |
|
return null; |
|
} |
|
|
|
if (playerCharacter == null) { |
|
Logger.info("GOM Failed to create PlayerCharacter"); |
|
return null; |
|
} |
|
|
|
} // END synchronized(FirstNameLock) |
|
|
|
// Add creation runes |
|
for (RuneBase rb : usedRunes) { |
|
CharacterRune runeWithoutID = new CharacterRune(rb, playerCharacter.getObjectUUID()); |
|
CharacterRune characterRune; |
|
try { |
|
characterRune = DbManager.CharacterRuneQueries.ADD_CHARACTER_RUNE(runeWithoutID); |
|
} catch (Exception e) { |
|
characterRune = null; |
|
} |
|
|
|
if (characterRune == null) { |
|
playerCharacter.deactivateCharacter(); |
|
Logger.info("GOM Failed to create CharacterRune"); |
|
return null; |
|
} |
|
|
|
playerCharacter.addRune(characterRune); |
|
} |
|
|
|
if (hairStyleID != 0) { |
|
// Create Hair |
|
Item tempHair = new Item(ItemBase.getItemBase(hairStyleID), playerCharacter.getObjectUUID(), Enum.OwnerType.PlayerCharacter, |
|
(byte) 0, (byte) 0, (short) 1, (short) 1, false, false, Enum.ItemContainerType.EQUIPPED, |
|
(byte) MBServerStatics.SLOT_HAIRSTYLE, new ArrayList<>(), ""); |
|
|
|
Item hair; |
|
|
|
try { |
|
hair = DbManager.ItemQueries.ADD_ITEM(tempHair); |
|
} catch (Exception e) { |
|
hair = null; |
|
} |
|
|
|
if (hair == null) { |
|
playerCharacter.deactivateCharacter(); |
|
Logger.info("GameObjectManager failed to create Hair:" + hairStyleID + " in Slot:" |
|
+ MBServerStatics.SLOT_HAIRSTYLE); |
|
return null; |
|
} |
|
} |
|
|
|
if (beardStyleID != 0) { |
|
// Create Beard |
|
Item tempBeard = new Item(ItemBase.getItemBase(beardStyleID), playerCharacter.getObjectUUID(), Enum.OwnerType.PlayerCharacter, |
|
(byte) 0, (byte) 0, (short) 1, (short) 1, false, false, Enum.ItemContainerType.EQUIPPED, |
|
(byte) MBServerStatics.SLOT_BEARDSTYLE, new ArrayList<>(), ""); |
|
Item beard; |
|
try { |
|
beard = DbManager.ItemQueries.ADD_ITEM(tempBeard); |
|
} catch (Exception e) { |
|
beard = null; |
|
} |
|
|
|
if (beard == null) { |
|
playerCharacter.deactivateCharacter(); |
|
Logger.info("GameObjectManager failed to create Beard:" + beardStyleID + " in Slot:" |
|
+ MBServerStatics.SLOT_BEARDSTYLE); |
|
return null; |
|
} |
|
} |
|
// Create items from Kit and equip on character. |
|
try { |
|
kit.equipPCwithKit(playerCharacter); |
|
} catch (Exception e) { |
|
Logger.info("Unable to find KIT ID for Race: " + raceID + "||" + "Class:" + baseClassID); |
|
playerCharacter.deactivateCharacter(); |
|
return null; |
|
} |
|
|
|
// Get any new skills that belong to the player |
|
playerCharacter.calculateSkills(); |
|
|
|
a.setLastCharacter(playerCharacter.getObjectUUID()); |
|
playerCharacter.getCharItemManager().load(); |
|
|
|
playerCharacter.activateCharacter(); |
|
|
|
return playerCharacter; |
|
} |
|
|
|
public static boolean isIgnoreListFull() { |
|
return false; //Why were we setting a limit on ignores? - |
|
//return (ignoredPlayerIDs.size() >= MBServerStatics.IGNORE_LIST_MAX); |
|
} |
|
|
|
public static boolean isIgnorable() { |
|
return true; |
|
// // if (account == null) return false; |
|
// if (account.getAccessLevel() > 0) { |
|
// return false; |
|
// } |
|
// return true; |
|
} |
|
|
|
/** |
|
* @ Initialize player upon creation |
|
*/ |
|
public static void initializePlayer(PlayerCharacter player) { |
|
|
|
if (player.initialized) |
|
return; |
|
// Logger.info("", " Initializing " + player.getCombinedName()); |
|
player.skills = DbManager.CharacterSkillQueries.GET_SKILLS_FOR_CHARACTER(player); |
|
player.powers = player.initializePowers(); |
|
|
|
|
|
if (ConfigManager.serverType.equals(Enum.ServerType.WORLDSERVER)) |
|
player.setLoc(player.getBindLoc()); |
|
player.endLoc = Vector3fImmutable.ZERO; |
|
|
|
//get level based on experience |
|
player.level = (short) Experience.getLevel(player.getExp()); |
|
|
|
player.setHealth(999999f); |
|
player.mana.set(999999f); |
|
player.stamina.set(999999f); |
|
player.bonuses = new PlayerBonuses(player); |
|
PlayerBonuses.InitializeBonuses(player); |
|
player.setResists(new Resists(player)); |
|
player.getCharItemManager().load(); |
|
|
|
if (ConfigManager.serverType.equals(Enum.ServerType.WORLDSERVER)) { |
|
|
|
//CharacterSkill.updateAllBaseAmounts(this); |
|
CharacterPower.grantTrains(player); |
|
|
|
// calculate skills. Make sure none are missing. |
|
AbstractCharacter.runBonusesOnLoad(player); |
|
|
|
InitializeSkillsOnLoad(player); |
|
|
|
//apply all bonuses |
|
player.recalculatePlayerStats(true); |
|
player.trainsAvailable.set(CharacterSkill.getTrainsAvailable(player)); |
|
|
|
if (player.trainsAvailable.get() < 0) |
|
player.recalculateTrains(); |
|
|
|
//this.resists.calculateResists(this); |
|
player.newChar = true; |
|
|
|
//check current guild valid for player |
|
checkGuildStatus(player); |
|
|
|
player.setHealth(player.getHealthMax()); |
|
player.mana.set(player.getManaMax()); |
|
player.stamina.set(player.getStaminaMax()); |
|
} else |
|
player.setBindLoc(Vector3fImmutable.ZERO); |
|
|
|
player.initialized = true; |
|
|
|
String lastAscii = player.getLastName().replaceAll("[^\\p{ASCII}]", ""); |
|
player.asciiLastName = lastAscii.equals(player.getLastName()); |
|
} |
|
|
|
public static void recalculatePlayerStatsOnLoad(PlayerCharacter pc) { |
|
|
|
//calculate base stats |
|
pc.calculateBaseStats(); |
|
|
|
//calculate base skills |
|
CharacterSkill.updateAllBaseAmounts(pc); |
|
pc.calculateModifiedStats(); |
|
|
|
//calculate modified skills |
|
CharacterSkill.updateAllModifiedAmounts(pc); |
|
|
|
|
|
//calculate modified stats |
|
|
|
|
|
//calculate ATR, damage and defense |
|
pc.calculateAtrDefenseDamage(); |
|
|
|
//calculate movement bonus |
|
calculateSpeedMod(pc); |
|
|
|
// recalculate Max Health/Mana/Stamina |
|
pc.calculateMaxHealthManaStamina(); |
|
|
|
// recalculate Resists |
|
Resists.calculateResists(pc); |
|
|
|
} |
|
|
|
public static boolean hideNonAscii() { |
|
|
|
return false; |
|
} |
|
|
|
public static float getDexPenalty(Item armor) { |
|
if (armor == null) |
|
return 0f; |
|
ItemBase ab = armor.getItemBase(); |
|
if (ab == null) |
|
return 0f; |
|
return ab.getDexPenalty(); |
|
} |
|
|
|
public static float getModifiedAmount(CharacterSkill skill) { |
|
if (skill == null) |
|
return 0f; |
|
return skill.getModifiedAmount(); |
|
} |
|
|
|
public static void InitializeSkillsOnLoad(PlayerCharacter pc) { |
|
try { |
|
{ |
|
|
|
//see if any new skills or powers granted |
|
CharacterSkill.calculateSkills(pc); |
|
|
|
// calculate granted Trains in powers. |
|
CharacterPower.grantTrains(pc); |
|
|
|
//see if any new powers unlocked from previous check |
|
CharacterPower.calculatePowers(pc); |
|
} |
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
} |
|
|
|
} |
|
|
|
public static String getFirstName(int tableId) { |
|
|
|
PlayerCharacter player; |
|
|
|
if (tableId == 0) |
|
return ""; |
|
|
|
player = (PlayerCharacter) DbManager.getObject(Enum.GameObjectType.PlayerCharacter, tableId); |
|
|
|
return player.getFirstName(); |
|
} |
|
|
|
public static PlayerCharacter getFromCache(int id) { |
|
return (PlayerCharacter) DbManager.getFromCache(Enum.GameObjectType.PlayerCharacter, id); |
|
} |
|
|
|
public static PlayerCharacter getByFirstName(String name) { |
|
|
|
PlayerCharacter returnPlayer = null; |
|
for (AbstractGameObject ago : DbManager.getList(Enum.GameObjectType.PlayerCharacter)) { |
|
PlayerCharacter cachePlayer = (PlayerCharacter) ago; |
|
if (!name.equalsIgnoreCase(cachePlayer.getFirstName())) |
|
continue; |
|
if (cachePlayer.isDeleted()) |
|
continue; |
|
returnPlayer = cachePlayer; |
|
break; |
|
} |
|
|
|
return returnPlayer; |
|
} |
|
|
|
public static PlayerCharacter getPlayerCharacter(int uuid) { |
|
|
|
PlayerCharacter outPlayer; |
|
|
|
outPlayer = DbManager.PlayerCharacterQueries.GET_PLAYER_CHARACTER(uuid); |
|
|
|
if (outPlayer != null) |
|
return outPlayer; |
|
|
|
return (PlayerCharacter) DbManager.getFromCache(Enum.GameObjectType.PlayerCharacter, uuid); |
|
} |
|
|
|
public static int GetPlayerRealmTitle(PlayerCharacter player) { |
|
|
|
if (player.getGuild().isEmptyGuild()) |
|
return 0; |
|
if (!player.getGuild().isGuildLeader(player.getObjectUUID())) |
|
return 0; |
|
if (player.getGuild().getOwnedCity() == null) |
|
return 10; |
|
if (player.getGuild().getOwnedCity().getRealm() == null) |
|
return 10; |
|
if (player.getGuild().getOwnedCity().getRealm().getRulingCity() == null) |
|
return 10; |
|
|
|
if (player.getGuild().getOwnedCity().getRealm().getRulingCity().getObjectUUID() != player.getGuild().getOwnedCity().getObjectUUID()) |
|
return 10; |
|
int realmTitle = 1; |
|
if (player.getGuild().getSubGuildList() == null || player.getGuild().getSubGuildList().isEmpty()) |
|
return 11; |
|
for (Guild subGuild : player.getGuild().getSubGuildList()) { |
|
if (subGuild.getOwnedCity() == null) |
|
continue; |
|
if (subGuild.getOwnedCity().getRealm() == null) |
|
continue; |
|
if (subGuild.getOwnedCity().getRealm().getRulingCity() == null) |
|
continue; |
|
if (subGuild.getOwnedCity().getRealm().getRulingCity().getObjectUUID() != subGuild.getOwnedCity().getObjectUUID()) |
|
continue; |
|
realmTitle++; |
|
} |
|
|
|
if (realmTitle < 3) |
|
return 11; |
|
else if (realmTitle < 5) |
|
return 12; |
|
else |
|
return 13; |
|
} |
|
|
|
public static void UpdateClientPlayerRank(PlayerCharacter pc) { |
|
if (pc == null) |
|
return; |
|
boolean disable = true; |
|
|
|
if (disable) |
|
return; |
|
UpdateCharOrMobMessage ucm = new UpdateCharOrMobMessage(pc, 2, pc.getRank()); |
|
DispatchMessage.sendToAllInRange(pc, ucm); |
|
} |
|
|
|
public static void GroundPlayer(PlayerCharacter groundee) { |
|
if (groundee.getDesiredAltitude() == 0 && groundee.getAltitude() == 0) |
|
return; |
|
groundee.setAltitude(groundee.getAltitude()); |
|
groundee.setDesiredAltitude(0); |
|
groundee.setTakeOffTime(System.currentTimeMillis()); |
|
|
|
ChangeAltitudeMsg msg = ChangeAltitudeMsg.GroundPlayerMsg(groundee); |
|
// force a landing |
|
DispatchMessage.dispatchMsgToInterestArea(groundee, msg, Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); |
|
|
|
} |
|
|
|
public static boolean CanBreathe(PlayerCharacter breather) { |
|
try { |
|
if (isFlying(breather)) |
|
return true; |
|
Zone zone = ZoneManager.findSmallestZone(breather.getLoc()); |
|
|
|
if (zone.getSeaLevel() != 0) { |
|
|
|
float localAltitude = breather.getLoc().y; |
|
|
|
|
|
if (localAltitude + breather.getCharacterHeight() < zone.getSeaLevel() - 2) |
|
return false; |
|
|
|
if (breather.isMoving()) { |
|
if (localAltitude + breather.getCharacterHeight() < zone.getSeaLevel()) |
|
return false; |
|
} |
|
} else { |
|
if (breather.getLoc().y + breather.getCharacterHeight() < -2) |
|
return false; |
|
|
|
if (breather.isMoving()) { |
|
if (breather.getLoc().y + breather.getCharacterHeight() < 0) |
|
return false; |
|
} |
|
} |
|
|
|
|
|
} catch (Exception e) { |
|
Logger.info(breather.getName() + e); |
|
} |
|
|
|
|
|
return true; |
|
} |
|
|
|
public static boolean enterWater(PlayerCharacter enterer) { |
|
|
|
try { |
|
if (isFlying(enterer)) |
|
return false; |
|
|
|
|
|
Zone zone = ZoneManager.findSmallestZone(enterer.getLoc()); |
|
|
|
if (zone.getSeaLevel() != 0) { |
|
|
|
float localAltitude = enterer.getLoc().y + enterer.getCharacterHeight(); |
|
|
|
|
|
if (localAltitude < zone.getSeaLevel()) |
|
return true; |
|
} else { |
|
if (enterer.getLoc().y + enterer.getCharacterHeight() < 0) |
|
return true; |
|
} |
|
} catch (Exception e) { |
|
Logger.info(enterer.getName() + e); |
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
public static boolean LeaveWater(PlayerCharacter leaver) { |
|
|
|
try { |
|
|
|
|
|
Zone zone = ZoneManager.findSmallestZone(leaver.getLoc()); |
|
|
|
float leaveWater = leaver.centerHeight; |
|
|
|
if (leaver.isMoving()) |
|
leaveWater = 1f; |
|
|
|
|
|
if (zone.getSeaLevel() != 0) { |
|
|
|
float localAltitude = leaver.getLoc().y; |
|
|
|
|
|
if (localAltitude + leaveWater < zone.getSeaLevel()) |
|
return false; |
|
} else { |
|
if (leaver.getLoc().y + leaveWater < 0) |
|
return false; |
|
} |
|
} catch (Exception e) { |
|
Logger.info(leaver.getName() + e); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public static boolean CanBindToBuilding(PlayerCharacter player, int buildingID) { |
|
if (buildingID == 0) |
|
return false; |
|
|
|
Building bindBuilding = BuildingManager.getBuildingFromCache(buildingID); |
|
|
|
if (bindBuilding == null) |
|
return false; |
|
|
|
if (!BuildingManager.playerCanManage(player, bindBuilding)) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
public static void forceRespawn(PlayerCharacter sourcePlayer) throws MsgSendException { |
|
|
|
if (sourcePlayer == null) |
|
return; |
|
try { |
|
sourcePlayer.getClientConnection().disconnect(); |
|
} catch (Exception e) { |
|
|
|
} |
|
|
|
} |
|
|
|
public static void unboxPlayer(PlayerCharacter player){ |
|
String machineID = player.getClientConnection().machineID; |
|
ArrayList<PlayerCharacter> sameMachine = new ArrayList<>(); |
|
for(PlayerCharacter pc : SessionManager.getAllActivePlayerCharacters()){ |
|
if(!pc.equals(player) && pc.isActive() && pc.isEnteredWorld() && pc.getClientConnection().machineID.equals(machineID)){ |
|
sameMachine.add(pc); |
|
} |
|
} |
|
|
|
for(PlayerCharacter pc : sameMachine) |
|
pc.isBoxed = true; |
|
|
|
player.isBoxed = false; |
|
if(player.containsEffect(1672601862)) { |
|
player.removeEffectBySource(Enum.EffectSourceType.DeathShroud,41,false); |
|
} |
|
|
|
} |
|
|
|
public static boolean checkIfBoxed(PlayerCharacter player){ |
|
if(ConfigManager.MB_WORLD_TESTMODE.getValue().equals("true")) { |
|
return false; |
|
} |
|
try { |
|
String machineID = player.getClientConnection().machineID; |
|
ArrayList<PlayerCharacter> sameMachine = new ArrayList<>(); |
|
for (PlayerCharacter pc : SessionManager.getAllActivePlayerCharacters()) { |
|
if (!pc.equals(player) && pc.isActive() && pc.isEnteredWorld() && pc.getClientConnection().machineID.equals(machineID)) { |
|
sameMachine.add(pc); |
|
} |
|
} |
|
|
|
boolean boxed = false; |
|
for (PlayerCharacter pc : sameMachine) |
|
if (!pc.isBoxed) |
|
boxed = true; |
|
|
|
return boxed; |
|
}catch(Exception e){ |
|
return false; |
|
} |
|
} |
|
|
|
public static void setGuildTitle(PlayerCharacter playerCharacter, int value) { |
|
if (GuildStatusController.getTitle(playerCharacter.getGuildStatus()) == value) |
|
return; |
|
DbManager.PlayerCharacterQueries.SET_GUILD_TITLE(playerCharacter, value); |
|
GuildStatusController.setTitle(playerCharacter.getGuildStatus(), value); |
|
} |
|
|
|
public static void setFullMember(PlayerCharacter playerCharacter, boolean value) { |
|
if (GuildStatusController.isFullMember(playerCharacter.getGuildStatus()) == value) |
|
return; |
|
DbManager.PlayerCharacterQueries.SET_FULL_MEMBER(playerCharacter, value); |
|
GuildStatusController.setFullMember(playerCharacter.getGuildStatus(), value); |
|
} |
|
|
|
public static void setRecruiter(PlayerCharacter playerCharacter, boolean value) { |
|
if (GuildStatusController.isRecruiter(playerCharacter.getGuildStatus()) == value) |
|
return; |
|
DbManager.PlayerCharacterQueries.SET_RECRUITER(playerCharacter, value); |
|
GuildStatusController.setRecruiter(playerCharacter.getGuildStatus(), value); |
|
} |
|
|
|
public static void setTaxCollector(PlayerCharacter playerCharacter, boolean value) { |
|
if (GuildStatusController.isTaxCollector(playerCharacter.getGuildStatus()) == value) |
|
return; |
|
DbManager.PlayerCharacterQueries.SET_TAX_COLLECTOR(playerCharacter, value); |
|
GuildStatusController.setTaxCollector(playerCharacter.getGuildStatus(), value); |
|
} |
|
|
|
public static void setInnerCouncil(PlayerCharacter playerCharacter, boolean value) { |
|
|
|
// dont update if its the same. |
|
if (GuildStatusController.isInnerCouncil(playerCharacter.getGuildStatus()) == value) |
|
return; |
|
|
|
DbManager.PlayerCharacterQueries.SET_INNERCOUNCIL(playerCharacter, value); |
|
GuildStatusController.setInnerCouncil(playerCharacter.getGuildStatus(), value); |
|
} |
|
|
|
public static void setGuildLeader(PlayerCharacter playerCharacter, boolean value) { |
|
if (GuildStatusController.isGuildLeader(playerCharacter.getGuildStatus()) == value) |
|
return; |
|
|
|
GuildStatusController.setGuildLeader(playerCharacter.getGuildStatus(), value); |
|
if (value == true) { |
|
setInnerCouncil(playerCharacter, true); |
|
setFullMember(playerCharacter, true); |
|
} |
|
} |
|
|
|
//END -> Guild Status Interface |
|
public static void resetGuildStatuses(PlayerCharacter playerCharacter) { |
|
setInnerCouncil(playerCharacter, false); |
|
setFullMember(playerCharacter, false); |
|
setGuildTitle(playerCharacter, 0); |
|
setTaxCollector(playerCharacter, false); |
|
setRecruiter(playerCharacter, false); |
|
setGuildLeader(playerCharacter, false); |
|
} |
|
|
|
public static void setEnteredWorld(PlayerCharacter playerCharacter, boolean enteredWorld) { |
|
playerCharacter.enteredWorld = enteredWorld; |
|
} |
|
|
|
public static boolean isLastSwimming(PlayerCharacter playerCharacter) { |
|
return playerCharacter.lastSwimming; |
|
} |
|
|
|
public static void teleport(PlayerCharacter playerCharacter, final Vector3fImmutable targetLoc) { |
|
|
|
Regions targetRegion = Regions.GetRegionForTeleport(targetLoc); |
|
playerCharacter.locationLock.writeLock().lock(); |
|
|
|
try { |
|
MovementManager.translocate(playerCharacter, targetLoc, targetRegion); |
|
} catch (Exception e) { |
|
Logger.error(e); |
|
} finally { |
|
playerCharacter.locationLock.writeLock().unlock(); |
|
} |
|
} |
|
|
|
public static float getBargain(PlayerCharacter playerCharacter) { |
|
float bargain = 0; |
|
|
|
CharacterSkill bargainSkill = playerCharacter.getSkills().get(Enum.CharacterSkills.Bargaining.name()); |
|
|
|
if (bargainSkill != null) |
|
bargain = bargainSkill.getModifiedAmountBeforeMods(); |
|
|
|
if (bargain > 100) |
|
bargain = 100; |
|
|
|
bargain *= .01f; |
|
|
|
return bargain; |
|
} |
|
|
|
public static boolean isDirtyLoad(PlayerCharacter playerCharacter) { |
|
boolean dirtyValue; |
|
playerCharacter.dirtyLock.readLock().lock(); |
|
dirtyValue = playerCharacter.dirtyLoad; |
|
playerCharacter.dirtyLock.readLock().unlock(); |
|
return dirtyValue; |
|
} |
|
|
|
public static void setDirtyLoad(PlayerCharacter playerCharacter, boolean dirtyLoad) { |
|
playerCharacter.dirtyLock.writeLock().lock(); |
|
playerCharacter.dirtyLoad = dirtyLoad; |
|
playerCharacter.dirtyLock.writeLock().unlock(); |
|
} |
|
|
|
public static void ResetLevel(PlayerCharacter playerCharacter, short targetLevel) { |
|
|
|
if (targetLevel > 13) { |
|
ChatManager.chatSystemError(playerCharacter, "Please choose a level between 1 and 13."); |
|
return; |
|
} |
|
playerCharacter.promotionClass = null; |
|
if (targetLevel > 10) { |
|
playerCharacter.level = 10; |
|
playerCharacter.exp = Experience.getBaseExperience(11); |
|
int maxEXP = Experience.getBaseExperience(targetLevel); //target level exp; |
|
playerCharacter.setOverFlowEXP(maxEXP - playerCharacter.exp); |
|
} else { |
|
playerCharacter.level = targetLevel; |
|
playerCharacter.exp = Experience.getBaseExperience(playerCharacter.level); |
|
playerCharacter.setOverFlowEXP(0); |
|
} |
|
|
|
|
|
for (CharacterSkill skill : playerCharacter.getSkills().values()) { |
|
skill.reset(playerCharacter, true); |
|
} |
|
|
|
for (CharacterPower power : playerCharacter.getPowers().values()) { |
|
power.reset(playerCharacter); |
|
} |
|
|
|
playerCharacter.recalculatePlayerStats(playerCharacter.initialized); |
|
playerCharacter.recalculate(); |
|
|
|
ChatManager.chatSystemInfo(playerCharacter, "Character reset to " + targetLevel + ". All training points have been refunded. Relog to update changes on client."); |
|
|
|
} |
|
|
|
public static void updateSkillsAndPowersToDatabase(PlayerCharacter playerCharacter) { |
|
if (playerCharacter.skills != null) |
|
for (CharacterSkill skill : playerCharacter.skills.values()) { |
|
DbManager.CharacterSkillQueries.UPDATE_TRAINS(skill); |
|
if (playerCharacter.powers != null) |
|
for (CharacterPower power : playerCharacter.powers.values()) { |
|
DbManager.CharacterPowerQueries.UPDATE_TRAINS(power); |
|
} |
|
} |
|
} |
|
|
|
public static boolean commandSiegeMinion(PlayerCharacter playerCharacter, Mob toCommand) { |
|
if (!toCommand.isSiege()) |
|
return false; |
|
if (toCommand.isPet() || !toCommand.isAlive()) |
|
return false; |
|
|
|
if (toCommand.getGuild().getNation() != playerCharacter.getGuild().getNation()) |
|
return false; |
|
|
|
if (playerCharacter.getPet() != null) { |
|
Mob currentPet = playerCharacter.getPet(); |
|
if (!currentPet.isSiege()) { |
|
|
|
currentPet.setCombatTarget(null); |
|
|
|
if (currentPet.getParentZone() != null) |
|
|
|
currentPet.getParentZone().zoneMobSet.remove(currentPet); |
|
|
|
try { |
|
currentPet.clearEffects(); |
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
} |
|
currentPet.playerAgroMap.clear(); |
|
WorldGrid.RemoveWorldObject(currentPet); |
|
DbManager.removeFromCache(currentPet); |
|
|
|
} else if (currentPet.isSiege()) { |
|
currentPet.agentType = Enum.AIAgentType.MOBILE; |
|
currentPet.setOwner(null); |
|
currentPet.setCombatTarget(null); |
|
if (currentPet.isAlive()) |
|
WorldGrid.updateObject(currentPet); |
|
} |
|
} |
|
|
|
toCommand.setPet(playerCharacter, false); |
|
playerCharacter.setPet(toCommand); |
|
toCommand.setCombatTarget(null); |
|
PetMsg petMsg = new PetMsg(6, toCommand); |
|
Dispatch dispatch = Dispatch.borrow(playerCharacter, petMsg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); |
|
|
|
if (toCommand.isAlive()) |
|
WorldGrid.updateObject(toCommand); |
|
return true; |
|
} |
|
|
|
public static boolean isFlying(PlayerCharacter playerCharacter) { |
|
|
|
return playerCharacter.getAltitude() > 0; |
|
|
|
} |
|
|
|
public static boolean isSwimming(PlayerCharacter playerCharacter) { |
|
|
|
// If char is flying they aren't quite swimming |
|
try { |
|
if (isFlying(playerCharacter)) |
|
return false; |
|
|
|
Zone zone = ZoneManager.findSmallestZone(playerCharacter.getLoc()); |
|
|
|
if (zone.getSeaLevel() != 0) { |
|
|
|
float localAltitude = playerCharacter.getLoc().y + playerCharacter.centerHeight; |
|
if (localAltitude < zone.getSeaLevel()) |
|
return true; |
|
} else { |
|
if (playerCharacter.getLoc().y + playerCharacter.centerHeight < 0) |
|
return true; |
|
} |
|
} catch (Exception e) { |
|
Logger.info(playerCharacter.getName() + e); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
public static boolean hasBoon(PlayerCharacter playerCharacter) { |
|
for (Effect eff : playerCharacter.getEffects().values()) { |
|
if (eff.getPowerToken() == -587743986 || eff.getPowerToken() == -1660519801 || eff.getPowerToken() == -1854683250) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
public static void updateBlessingMessage(PlayerCharacter playerCharacter) { |
|
|
|
if (playerCharacter.getTimeStamp("RealmClaim") > System.currentTimeMillis()) |
|
return; |
|
|
|
int count = 0; |
|
|
|
for (Effect eff : playerCharacter.getEffects().values()) { |
|
if (eff.getPowerToken() == -587743986 || eff.getPowerToken() == -1660519801 || eff.getPowerToken() == -1854683250) |
|
count++; |
|
} |
|
|
|
if (count > 0) { |
|
playerCharacter.getTimestamps().put("RealmClaim", DateTime.now().plusMinutes(3).getMillis()); |
|
for (PlayerCharacter toSend : SessionManager.getAllActivePlayerCharacters()) { |
|
ChatManager.chatSystemInfo(toSend, playerCharacter.getCombinedName() + " is seeking to claim a realm and already has " + count + " blessngs!"); |
|
} |
|
} |
|
} |
|
|
|
public static void respawn(PlayerCharacter playerCharacter, boolean setAlive, boolean enterWorld, boolean makeCorpse) { |
|
|
|
// Recalculate everything |
|
if(playerCharacter.getTimestamps().containsKey("DeathTime")) |
|
playerCharacter.getTimestamps().remove("DeathTime"); |
|
|
|
playerCharacter.recalculatePlayerStats(true); |
|
playerCharacter.setCombat(false); |
|
|
|
// Set Health to 1/4 max |
|
|
|
|
|
Corpse corpse = null; |
|
|
|
if (makeCorpse) { |
|
try { |
|
corpse = Corpse.makeCorpse(playerCharacter, enterWorld); |
|
} catch (Exception e) { |
|
Logger.error(e); |
|
} |
|
//if we're not making corpse, just purge inventory. used for characters dead while logged out. |
|
} |
|
|
|
if (!setAlive) { |
|
if (corpse == null && makeCorpse) { |
|
Logger.error("Corpse not created."); |
|
} else { |
|
if (makeCorpse && corpse != null) { |
|
InterestManager.forceLoad(corpse); |
|
} |
|
} |
|
return; |
|
} |
|
|
|
playerCharacter.setHealth((float) (playerCharacter.healthMax * .25)); |
|
playerCharacter.isAlive.set(true); |
|
|
|
|
|
// Put player in safe mode |
|
// Teleport the player to his bind loc |
|
// or to a ruin as apporpriate. |
|
|
|
Building bindBuilding = BuildingManager.getBuildingFromCache(playerCharacter.getBindBuildingID()); |
|
|
|
if (enterWorld) { |
|
playerCharacter.stopMovement(playerCharacter.getBindLoc()); |
|
} else if (bindBuilding != null) { |
|
if (bindBuilding.getParentZone().equals(ZoneManager.findSmallestZone(playerCharacter.getLoc()))) |
|
teleport(playerCharacter, Enum.Ruins.getRandomRuin().getLocation()); |
|
else |
|
teleport(playerCharacter, playerCharacter.getBindLoc()); |
|
} else // no bind building found for player, teleport to ruins. |
|
teleport(playerCharacter, Enum.Ruins.getRandomRuin().getLocation()); |
|
|
|
playerCharacter.lastUpdateTime = System.currentTimeMillis(); |
|
playerCharacter.lastStamUpdateTime = System.currentTimeMillis(); |
|
|
|
playerCharacter.update(false); |
|
|
|
PowersManager.applyPower(playerCharacter, playerCharacter, Vector3fImmutable.ZERO, -1661758934, 40, false); |
|
|
|
if (corpse == null && makeCorpse) { |
|
Logger.error("Corpse not created."); |
|
} else { |
|
if (makeCorpse && corpse != null) { |
|
InterestManager.forceLoad(corpse); |
|
} |
|
} |
|
} |
|
|
|
public static Effect addCityEffect(PlayerCharacter playerCharacter, String name, EffectsBase eb, int trains, int duration, boolean onEnter, City city) { |
|
JobContainer jc = null; |
|
if (onEnter) { |
|
NoTimeJob ntj = new NoTimeJob(playerCharacter, name, eb, trains); //infinite timer |
|
ntj.setEffectSourceType(city.getObjectType().ordinal()); |
|
ntj.setEffectSourceID(city.getObjectUUID()); |
|
jc = new JobContainer(ntj); |
|
} else { |
|
FinishSpireEffectJob fsej = new FinishSpireEffectJob(playerCharacter, name, eb, trains); |
|
fsej.setEffectSourceType(city.getObjectType().ordinal()); |
|
fsej.setEffectSourceID(city.getObjectUUID()); |
|
jc = JobScheduler.getInstance().scheduleJob(fsej, duration); |
|
} |
|
|
|
if (playerCharacter.effects.get(name) != null) |
|
playerCharacter.effects.get(name).cancelJob(); |
|
|
|
Effect eff = new Effect(jc, eb, trains); |
|
playerCharacter.effects.put(name, eff); |
|
playerCharacter.applyAllBonuses(); |
|
eff.sendSpireEffect(playerCharacter.getClientConnection(), onEnter); |
|
return eff; |
|
} |
|
|
|
public static void addStr(PlayerCharacter playerCharacter, int amount) { |
|
|
|
boolean worked = false; |
|
short newStr = (short) 0; |
|
while (!worked) { |
|
|
|
if ((playerCharacter.getUnusedStatPoints() - playerCharacter.trainedStatPoints) <= 0) |
|
return; |
|
|
|
newStr = (short) (playerCharacter.statStrBase + amount); |
|
short mod = (short) playerCharacter.strMod.get(); |
|
short newStrMod = (short) (mod + amount); |
|
|
|
if (newStr > playerCharacter.statStrMax) { |
|
newStrMod += (playerCharacter.statStrMax - newStr); |
|
newStr = playerCharacter.statStrMax; |
|
} |
|
worked = playerCharacter.strMod.compareAndSet(mod, newStrMod); |
|
} |
|
playerCharacter.trainedStatPoints++; |
|
playerCharacter.statStrBase = newStr; |
|
playerCharacter.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); |
|
playerCharacter.applyBonuses(); |
|
playerCharacter.calculateSkills(); |
|
} |
|
|
|
public static void addDex(PlayerCharacter playerCharacter, int amount) { |
|
|
|
boolean worked = false; |
|
short newDex = (short) 0; |
|
|
|
while (!worked) { |
|
|
|
if ((playerCharacter.getUnusedStatPoints() - playerCharacter.trainedStatPoints) <= 0) |
|
return; |
|
|
|
newDex = (short) (playerCharacter.statDexBase + amount); |
|
short mod = (short) playerCharacter.dexMod.get(); |
|
short newDexMod = (short) (mod + amount); |
|
|
|
if (newDex > playerCharacter.statDexMax) { |
|
newDexMod += (playerCharacter.statDexMax - newDex); |
|
newDex = playerCharacter.statDexMax; |
|
} |
|
|
|
worked = playerCharacter.dexMod.compareAndSet(mod, newDexMod); |
|
} |
|
playerCharacter.trainedStatPoints++; |
|
playerCharacter.statDexBase = newDex; |
|
playerCharacter.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); |
|
playerCharacter.applyBonuses(); |
|
playerCharacter.calculateSkills(); |
|
} |
|
|
|
public static void addCon(PlayerCharacter playerCharacter, int amount) { |
|
boolean worked = false; |
|
short newCon = (short) 0; |
|
while (!worked) { |
|
|
|
if ((playerCharacter.getUnusedStatPoints() - playerCharacter.trainedStatPoints) <= 0) |
|
return; |
|
|
|
newCon = (short) (playerCharacter.statConBase + amount); |
|
short mod = (short) playerCharacter.conMod.get(); |
|
short newConMod = (short) (mod + amount); |
|
|
|
if (newCon > playerCharacter.statConMax) { |
|
newConMod += (playerCharacter.statConMax - newCon); |
|
newCon = playerCharacter.statConMax; |
|
} |
|
worked = playerCharacter.conMod.compareAndSet(mod, newConMod); |
|
} |
|
playerCharacter.trainedStatPoints++; |
|
playerCharacter.statConBase = newCon; |
|
playerCharacter.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); |
|
playerCharacter.applyBonuses(); |
|
playerCharacter.calculateSkills(); |
|
} |
|
|
|
public static void addInt(PlayerCharacter playerCharacter, int amount) { |
|
boolean worked = false; |
|
short newInt = (short) 0; |
|
while (!worked) { |
|
|
|
if ((playerCharacter.getUnusedStatPoints() - playerCharacter.trainedStatPoints) <= 0) |
|
return; |
|
|
|
newInt = (short) (playerCharacter.statIntBase + amount); |
|
short mod = (short) playerCharacter.intMod.get(); |
|
short newIntMod = (short) (mod + amount); |
|
|
|
if (newInt > playerCharacter.statIntMax) { |
|
newIntMod += (playerCharacter.statIntMax - newInt); |
|
newInt = playerCharacter.statIntMax; |
|
} |
|
worked = playerCharacter.intMod.compareAndSet(mod, newIntMod); |
|
} |
|
playerCharacter.trainedStatPoints++; |
|
playerCharacter.statIntBase = newInt; |
|
playerCharacter.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); |
|
playerCharacter.applyBonuses(); |
|
playerCharacter.calculateSkills(); |
|
} |
|
|
|
public static void addSpi(PlayerCharacter playerCharacter, int amount) { |
|
boolean worked = false; |
|
short newSpi = (short) 0; |
|
|
|
while (!worked) { |
|
|
|
if ((playerCharacter.getUnusedStatPoints() - playerCharacter.trainedStatPoints) <= 0) |
|
return; |
|
|
|
newSpi = (short) (playerCharacter.statSpiBase + amount); |
|
short mod = (short) playerCharacter.spiMod.get(); |
|
short newSpiMod = (short) (mod + amount); |
|
|
|
if (newSpi > playerCharacter.statSpiMax) { |
|
newSpiMod += (playerCharacter.statSpiMax - newSpi); |
|
newSpi = playerCharacter.statSpiMax; |
|
} |
|
worked = playerCharacter.spiMod.compareAndSet(mod, newSpiMod); |
|
} |
|
playerCharacter.trainedStatPoints++; |
|
playerCharacter.statSpiBase = newSpi; |
|
playerCharacter.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); |
|
playerCharacter.applyBonuses(); |
|
playerCharacter.calculateSkills(); |
|
} |
|
|
|
public static boolean refineStr(PlayerCharacter playerCharacter) { |
|
boolean worked = false; |
|
short newStr = (short) 0; |
|
|
|
while (!worked) { |
|
|
|
newStr = (short) (playerCharacter.statStrBase - 1); |
|
short mod = (short) playerCharacter.strMod.get(); |
|
|
|
if (mod == 0) |
|
return false; |
|
|
|
short newStrMod = (short) (mod - 1); |
|
|
|
if (newStr < playerCharacter.statStrMin) |
|
return false; |
|
|
|
if (!canRefineLower(playerCharacter, MBServerStatics.STAT_STR_ID)) |
|
return false; |
|
|
|
worked = playerCharacter.strMod.compareAndSet(mod, newStrMod); |
|
} |
|
playerCharacter.trainedStatPoints--; |
|
playerCharacter.statStrBase = newStr; |
|
playerCharacter.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); |
|
playerCharacter.applyBonuses(); |
|
playerCharacter.calculateSkills(); |
|
return true; |
|
} |
|
|
|
public static boolean refineDex(PlayerCharacter playerCharacter) { |
|
boolean worked = false; |
|
short newDex = (short) 0; |
|
|
|
while (!worked) { |
|
newDex = (short) (playerCharacter.statDexBase - 1); |
|
short mod = (short) playerCharacter.dexMod.get(); |
|
|
|
if (mod == 0) |
|
return false; |
|
|
|
short newDexMod = (short) (mod - 1); |
|
|
|
if (newDex < playerCharacter.statDexMin) |
|
return false; |
|
|
|
if (!canRefineLower(playerCharacter, MBServerStatics.STAT_DEX_ID)) |
|
return false; |
|
|
|
worked = playerCharacter.dexMod.compareAndSet(mod, newDexMod); |
|
} |
|
playerCharacter.trainedStatPoints--; |
|
playerCharacter.statDexBase = newDex; |
|
playerCharacter.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); |
|
playerCharacter.applyBonuses(); |
|
playerCharacter.calculateSkills(); |
|
return true; |
|
} |
|
|
|
public static boolean refineCon(PlayerCharacter playerCharacter) { |
|
boolean worked = false; |
|
short newCon = (short) 0; |
|
|
|
while (!worked) { |
|
newCon = (short) (playerCharacter.statConBase - 1); |
|
short mod = (short) playerCharacter.conMod.get(); |
|
|
|
if (mod == 0) |
|
return false; |
|
|
|
short newConMod = (short) (mod - 1); |
|
|
|
if (newCon < playerCharacter.statConMin) |
|
return false; |
|
|
|
if (!canRefineLower(playerCharacter, MBServerStatics.STAT_CON_ID)) |
|
return false; |
|
|
|
worked = playerCharacter.conMod.compareAndSet(mod, newConMod); |
|
} |
|
playerCharacter.trainedStatPoints--; |
|
playerCharacter.statConBase = newCon; |
|
playerCharacter.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); |
|
playerCharacter.applyBonuses(); |
|
playerCharacter.calculateSkills(); |
|
return true; |
|
} |
|
|
|
public static boolean refineInt(PlayerCharacter playerCharacter, RefineMsg msg) { |
|
boolean worked = false; |
|
short newInt = (short) 0; |
|
|
|
while (!worked) { |
|
newInt = (short) (playerCharacter.statIntBase - 1); |
|
short mod = (short) playerCharacter.intMod.get(); |
|
|
|
if (mod == 0) |
|
return false; |
|
short newIntMod = (short) (mod |
|
- 1); |
|
|
|
if (newInt < playerCharacter.statIntMin) |
|
return false; |
|
|
|
if (!canRefineLower(playerCharacter, MBServerStatics.STAT_INT_ID)) |
|
return false; |
|
|
|
worked = playerCharacter.intMod.compareAndSet(mod, newIntMod); |
|
} |
|
playerCharacter.trainedStatPoints--; |
|
playerCharacter.statIntBase = newInt; |
|
playerCharacter.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); |
|
|
|
verifySkillMax(playerCharacter, msg); |
|
|
|
playerCharacter.applyBonuses(); |
|
playerCharacter.calculateSkills(); |
|
return true; |
|
} |
|
|
|
public static boolean refineSpi(PlayerCharacter playerCharacter) { |
|
boolean worked = false; |
|
short newSpi = (short) 0; |
|
while (!worked) { |
|
newSpi = (short) (playerCharacter.statSpiBase - 1); |
|
short mod = (short) playerCharacter.spiMod.get(); |
|
if (mod == 0) |
|
return false; |
|
short newSpiMod = (short) (mod - 1); |
|
if (newSpi < playerCharacter.statSpiMin) |
|
return false; |
|
if (!canRefineLower(playerCharacter, MBServerStatics.STAT_SPI_ID)) |
|
return false; |
|
worked = playerCharacter.spiMod.compareAndSet(mod, newSpiMod); |
|
} |
|
playerCharacter.trainedStatPoints--; |
|
playerCharacter.statSpiBase = newSpi; |
|
playerCharacter.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); |
|
playerCharacter.applyBonuses(); |
|
playerCharacter.calculateSkills(); |
|
return true; |
|
} |
|
|
|
//this verifies stat doesn't fall too low to keep runes applied while refining |
|
private static boolean canRefineLower(PlayerCharacter playerCharacter, int stat) { |
|
for (CharacterRune cr : playerCharacter.getRunes()) { |
|
if (cr != null) { |
|
RuneBase rb = cr.getRuneBase(); |
|
if (rb != null) { |
|
ArrayList<RuneBaseAttribute> attrs = rb.getAttrs(); |
|
|
|
if (attrs != null) |
|
for (RuneBaseAttribute rba : attrs) { |
|
int attrID = rba.getAttributeID(); |
|
int mod = rba.getModValue(); |
|
if (stat == MBServerStatics.STAT_STR_ID) { |
|
if (attrID == MBServerStatics.RUNE_STR_MIN_NEEDED_ATTRIBUTE_ID && ((int) playerCharacter.statStrBase <= mod)) |
|
return false; |
|
} else if (stat == MBServerStatics.STAT_DEX_ID) { |
|
if (attrID == MBServerStatics.RUNE_DEX_MIN_NEEDED_ATTRIBUTE_ID && ((int) playerCharacter.statDexBase <= mod)) |
|
return false; |
|
} else if (stat == MBServerStatics.STAT_CON_ID) { |
|
if (attrID == MBServerStatics.RUNE_CON_MIN_NEEDED_ATTRIBUTE_ID && ((int) playerCharacter.statConBase <= mod)) |
|
return false; |
|
} else if (stat == MBServerStatics.STAT_INT_ID) { |
|
if (attrID == MBServerStatics.RUNE_INT_MIN_NEEDED_ATTRIBUTE_ID && ((int) playerCharacter.statIntBase <= mod)) |
|
return false; |
|
} else if (stat == MBServerStatics.STAT_SPI_ID) |
|
if (attrID == MBServerStatics.RUNE_SPI_MIN_NEEDED_ATTRIBUTE_ID && ((int) playerCharacter.statSpiBase <= mod)) |
|
return false; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//checked on refining int to see if skills need refined also. |
|
private static void verifySkillMax(PlayerCharacter playerCharacter, RefineMsg msg) { |
|
|
|
ConcurrentHashMap<String, CharacterSkill> skills = playerCharacter.getSkills(); |
|
|
|
//make sure no skills are over the max number of trains |
|
int maxTrains = CharacterSkill.getMaxTrains((int) playerCharacter.statIntBase); |
|
|
|
RefineMsg rm = new RefineMsg(msg.getNpcType(), msg.getNpcID(), 0, 0); |
|
|
|
for (CharacterSkill skill : skills.values()) { |
|
|
|
while (skill.getNumTrains() > maxTrains) { |
|
boolean worked = skill.refine(playerCharacter, false); //refine skill, do not recalculate everything |
|
if (worked) { |
|
rm.setToken(skill.getToken()); |
|
|
|
Dispatch dispatch = Dispatch.borrow(playerCharacter, rm); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); |
|
|
|
} else { |
|
Logger.error("Failed to force refine of skill " + skill.getObjectUUID() + " by character " + playerCharacter.getObjectUUID()); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
public static int getClassToken(PlayerCharacter playerCharacter) { |
|
if (playerCharacter.promotionClass != null) |
|
return playerCharacter.promotionClass.getToken(); |
|
else if (playerCharacter.getBaseClass() != null) |
|
return playerCharacter.getBaseClass().getToken(); |
|
return 0; |
|
} |
|
|
|
public static int getRaceToken(PlayerCharacter playerCharacter) { |
|
|
|
if (playerCharacter.getRace() == null) |
|
return 0; |
|
|
|
return playerCharacter.getRace().getToken(); |
|
} |
|
|
|
public static synchronized void grantXP(PlayerCharacter playerCharacter, int xp) { |
|
if(playerCharacter.promotionClass == null && playerCharacter.level == 10){ |
|
playerCharacter.setOverFlowEXP(0); |
|
playerCharacter.update(false); |
|
playerCharacter.incVer(); |
|
playerCharacter.recalculate(); |
|
playerCharacter.calculateMaxHealthManaStamina(); |
|
playerCharacter.setHealth(playerCharacter.healthMax); |
|
playerCharacter.mana.set(playerCharacter.getManaMax()); |
|
playerCharacter.stamina.set(playerCharacter.getStaminaMax()); |
|
//LoadJob.reloadCharacter(this); |
|
DbManager.PlayerCharacterQueries.SET_PROPERTY(playerCharacter, "char_experience", playerCharacter.exp); |
|
// updateDatabase(); |
|
DbManager.AccountQueries.INVALIDATE_LOGIN_CACHE(playerCharacter.getObjectUUID(), "character"); |
|
return; |
|
} |
|
// Stop players from getting experience past the cap |
|
if (playerCharacter.exp + xp >= Experience.getBaseExperience(MBServerStatics.LEVELCAP)) |
|
xp = Experience.getBaseExperience(MBServerStatics.LEVELCAP) - playerCharacter.exp + 1; |
|
|
|
if (xp == 0) |
|
xp = 1; |
|
|
|
boolean isNewLevel = false; |
|
boolean charReloadRequired = false; |
|
int remainingXP = xp; |
|
int neededXP = 0; |
|
|
|
// handle players that have not yet promoted. |
|
ClientConnection origin = playerCharacter.getClientConnection(); |
|
|
|
//not promoted at level 10, start checking for negative EXP |
|
if (playerCharacter.promotionClass == null && playerCharacter.getLevel() == 10) { |
|
|
|
if (playerCharacter.getExp() == Experience.getBaseExperience(11)) { |
|
if (playerCharacter.overFlowEXP == 110000) |
|
return; |
|
|
|
if (playerCharacter.overFlowEXP + xp > 110000) { |
|
remainingXP = 110000 - playerCharacter.overFlowEXP; |
|
playerCharacter.overFlowEXP = 110000; |
|
|
|
|
|
} else { |
|
playerCharacter.overFlowEXP += remainingXP; |
|
} |
|
|
|
GrantExperienceMsg gem = new GrantExperienceMsg(playerCharacter, remainingXP); |
|
Dispatch dispatch = Dispatch.borrow(playerCharacter, gem); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); |
|
|
|
playerCharacter.addDatabaseJob("EXP", MBServerStatics.FIVE_MINUTES); |
|
return; |
|
//didnt reach level 11 EXP to start overflow, add exp normally till we get here; |
|
} else { |
|
|
|
//Player exp begins negative exp, add remaing exp after level 11 to overflow |
|
if (playerCharacter.getExp() + remainingXP >= Experience.getBaseExperience(11)) { |
|
|
|
playerCharacter.overFlowEXP = remainingXP - (Experience.getBaseExperience(11) - playerCharacter.getExp()); |
|
playerCharacter.exp = Experience.getBaseExperience(11); |
|
|
|
GrantExperienceMsg grantExperienceMsg = new GrantExperienceMsg(playerCharacter, remainingXP); |
|
Dispatch dispatch = Dispatch.borrow(playerCharacter, grantExperienceMsg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); |
|
|
|
playerCharacter.addDatabaseJob("EXP", MBServerStatics.FIVE_MINUTES); |
|
return; |
|
|
|
//didnt reach negative exp yet, just do normal exp gain. |
|
} else { |
|
playerCharacter.exp += remainingXP; |
|
GrantExperienceMsg grantExperienceMsg = new GrantExperienceMsg(playerCharacter, remainingXP); |
|
remainingXP = 0; |
|
Dispatch dispatch = Dispatch.borrow(playerCharacter, grantExperienceMsg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); |
|
|
|
playerCharacter.addDatabaseJob("EXP", MBServerStatics.FIVE_MINUTES); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
if (playerCharacter.overFlowEXP > 0) { |
|
|
|
int nextLevel; |
|
|
|
if (playerCharacter.level == 10) |
|
nextLevel = 12; |
|
else |
|
nextLevel = playerCharacter.level + 2; |
|
|
|
int nextLevelEXP = Experience.getBaseExperience(nextLevel); |
|
|
|
// if overflow > 0, u have level 11 experience + overflow, but level is still 10 due to just promoting. |
|
//Use level + 2 experience for next level. |
|
playerCharacter.overFlowEXP += 1; |
|
|
|
if (playerCharacter.getExp() + playerCharacter.overFlowEXP >= nextLevelEXP) { |
|
|
|
int expToNextLevel = nextLevelEXP - playerCharacter.getExp(); |
|
playerCharacter.overFlowEXP -= expToNextLevel; |
|
playerCharacter.exp += expToNextLevel; |
|
playerCharacter.level++; |
|
charReloadRequired = true; |
|
|
|
GrantExperienceMsg grantExperienceMsg = new GrantExperienceMsg(playerCharacter, 1); |
|
Dispatch dispatch = Dispatch.borrow(playerCharacter, grantExperienceMsg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); |
|
|
|
SetObjectValueMsg upm = new SetObjectValueMsg(playerCharacter, 9); |
|
DispatchMessage.dispatchMsgToInterestArea(playerCharacter, upm, Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); |
|
checkGuildStatus(playerCharacter); |
|
playerCharacter.addDatabaseJob("EXP", MBServerStatics.FIVE_MINUTES); |
|
// double overflow exp used up, remaining overflow will just add to level + 1. |
|
} else if (playerCharacter.getExp() + playerCharacter.overFlowEXP >= Experience.getBaseExperience(playerCharacter.level + 1)) { |
|
int nextExperience = Experience.getBaseExperience(playerCharacter.level + 1) + playerCharacter.overFlowEXP; |
|
playerCharacter.exp = nextExperience; |
|
playerCharacter.level++; |
|
charReloadRequired = true; |
|
playerCharacter.overFlowEXP = 0; |
|
GrantExperienceMsg grantExperienceMsg = new GrantExperienceMsg(playerCharacter, 1); |
|
Dispatch dispatch = Dispatch.borrow(playerCharacter, grantExperienceMsg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); |
|
SetObjectValueMsg upm = new SetObjectValueMsg(playerCharacter, 9); |
|
DispatchMessage.dispatchMsgToInterestArea(playerCharacter, upm, Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); |
|
checkGuildStatus(playerCharacter); |
|
playerCharacter.addDatabaseJob("EXP", MBServerStatics.FIVE_MINUTES); |
|
} |
|
|
|
} else { |
|
// Hand out each Level one at a time. |
|
isNewLevel = Experience.getLevel(playerCharacter.exp + remainingXP) > playerCharacter.getLevel(); |
|
|
|
if (isNewLevel) { |
|
neededXP = Experience.getBaseExperience(playerCharacter.getLevel() + 1) - playerCharacter.exp; |
|
|
|
charReloadRequired = true; |
|
playerCharacter.exp += neededXP; |
|
playerCharacter.level++; |
|
|
|
GrantExperienceMsg grantExperienceMsg = new GrantExperienceMsg(playerCharacter, neededXP); |
|
Dispatch dispatch = Dispatch.borrow(playerCharacter, grantExperienceMsg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); |
|
|
|
remainingXP -= neededXP; |
|
|
|
//Send newLevel. |
|
SetObjectValueMsg upm = new SetObjectValueMsg(playerCharacter, 9); |
|
DispatchMessage.dispatchMsgToInterestArea(playerCharacter, upm, Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); |
|
checkGuildStatus(playerCharacter); |
|
} else { |
|
|
|
playerCharacter.exp += remainingXP; |
|
GrantExperienceMsg grantExperienceMsg = new GrantExperienceMsg(playerCharacter, remainingXP); |
|
remainingXP = 0; |
|
Dispatch dispatch = Dispatch.borrow(playerCharacter, grantExperienceMsg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); |
|
|
|
playerCharacter.addDatabaseJob("EXP", MBServerStatics.FIVE_MINUTES); |
|
} |
|
} |
|
|
|
if (charReloadRequired) { |
|
playerCharacter.update(false); |
|
playerCharacter.incVer(); |
|
playerCharacter.recalculate(); |
|
playerCharacter.calculateMaxHealthManaStamina(); |
|
playerCharacter.setHealth(playerCharacter.healthMax); |
|
playerCharacter.mana.set(playerCharacter.getManaMax()); |
|
playerCharacter.stamina.set(playerCharacter.getStaminaMax()); |
|
//LoadJob.reloadCharacter(this); |
|
DbManager.PlayerCharacterQueries.SET_PROPERTY(playerCharacter, "char_experience", playerCharacter.exp); |
|
// updateDatabase(); |
|
DbManager.AccountQueries.INVALIDATE_LOGIN_CACHE(playerCharacter.getObjectUUID(), "character"); |
|
} |
|
} |
|
|
|
//This checks if a player meets the requirements to be in current guild. |
|
public static void checkGuildStatus(PlayerCharacter playerCharacter) { |
|
|
|
Guild g = playerCharacter.guild; |
|
|
|
if (g == null || g.isEmptyGuild() || GuildStatusController.isGuildLeader(playerCharacter.getGuildStatus())) |
|
return; |
|
|
|
//check level |
|
int curLevel = (int) playerCharacter.getPCLevel(); |
|
if (curLevel < g.getRepledgeMin() || curLevel >= g.getRepledgeKick()) { |
|
//TODO kick from guild |
|
g.removePlayer(playerCharacter, Enum.GuildHistoryType.LEAVE); |
|
ChatManager.chatGuildInfo(playerCharacter, "You no longer meet the level requirements for the guild."); |
|
} |
|
} |
|
|
|
public static void calculateSpeedMod(PlayerCharacter playerCharacter) { |
|
// get base race speed modifer |
|
|
|
|
|
//this is retarded. *** Refactor |
|
// if (this.race != null) { |
|
// int ID = this.race.getObjectUUID(); |
|
// if (ID == 2004 || ID == 2005) |
|
// this.raceRunMod = 1.21f; // centaur run bonus 22% |
|
//// else if (ID == 2017) |
|
//// this.raceRunMod = 1.14f; // mino run bonus 15% |
|
// else |
|
// this.raceRunMod = 1; |
|
// } else |
|
// this.raceRunMod = 1; |
|
|
|
|
|
float bonus = 1f; |
|
|
|
// // TODO: hardcoded, as didnt have time to introduce DB column to base object |
|
// if (baseClass.getName().equals("Fighter") || baseClass.getName().equals("Rogue")) |
|
// bonus += .05f; |
|
|
|
// get running skill |
|
if (playerCharacter.skills != null) { |
|
CharacterSkill running = playerCharacter.skills.get("Running"); |
|
if (running != null) { |
|
|
|
float runningBonus = (float) (Math.log(Math.round(running.getModifiedAmount()) * .01f) / Math.log(2) * .50f); |
|
runningBonus = (float) (Math.pow(2, runningBonus) - 1); |
|
runningBonus += 1; |
|
runningBonus *= .25f; |
|
bonus += runningBonus; |
|
|
|
} |
|
} |
|
|
|
if (playerCharacter.bonuses != null) |
|
// get rune and effect bonuses |
|
bonus += playerCharacter.bonuses.getFloatPercentNullZero(Enum.ModType.Speed, Enum.SourceType.None); |
|
|
|
// TODO get equip bonus |
|
playerCharacter.update(false); |
|
playerCharacter.speedMod = bonus; |
|
} |
|
}
|
|
|