Public Repository for the Magicbane Shadowbane Emulator
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.

2726 lines
104 KiB

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.gameManager;
import engine.Enum.*;
import engine.InterestManagement.HeightMap;
import engine.InterestManagement.WorldGrid;
import engine.db.handlers.dbEffectsBaseHandler;
import engine.db.handlers.dbPowerHandler;
import engine.db.handlers.dbSkillReqHandler;
import engine.job.AbstractJob;
import engine.job.AbstractScheduleJob;
import engine.job.JobContainer;
import engine.job.JobScheduler;
import engine.jobs.*;
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.objects.*;
import engine.powers.*;
import engine.powers.poweractions.AbstractPowerAction;
import engine.powers.poweractions.TrackPowerAction;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import static engine.math.FastMath.sqr;
public enum PowersManager {
POWERMANAGER;
public static HashMap<String, PowersBase> powersBaseByIDString = new HashMap<>();
public static HashMap<Integer, PowersBase> powersBaseByToken = new HashMap<>();
public static HashMap<String, EffectsBase> effectsBaseByIDString = new HashMap<>();
public static HashMap<Integer, EffectsBase> effectsBaseByToken = new HashMap<>();
public static HashMap<String, AbstractPowerAction> powerActionsByIDString = new HashMap<>();
public static HashMap<Integer, AbstractPowerAction> powerActionsByID = new HashMap<>();
public static HashMap<String, Integer> ActionTokenByIDString = new HashMap<>();
public static HashMap<String, Integer> AnimationOverrides = new HashMap<>();
public static HashMap<Integer, ArrayList<MobPowerEntry>> AllMobPowers;
private static JobScheduler js;
private PowersManager() {
}
public static void initPowersManager(boolean fullPowersLoad) {
if (fullPowersLoad)
PowersManager.InitializePowers();
else
PowersManager.InitializeLoginPowers();
PowersManager.js = JobScheduler.getInstance();
}
public static PowersBase getPowerByToken(int token) {
return PowersManager.powersBaseByToken.get(token);
}
public static PowersBase getPowerByIDString(String IDString) {
return PowersManager.powersBaseByIDString.get(IDString);
}
public static EffectsBase getEffectByIDString(String IDString) {
return PowersManager.effectsBaseByIDString.get(IDString);
}
public static AbstractPowerAction getPowerActionByIDString(String IDString) {
return PowersManager.powerActionsByIDString.get(IDString);
}
public static EffectsBase getEffectByToken(int token) {
return PowersManager.effectsBaseByToken.get(token);
}
// This pre-loads only PowersBase for login
public static void InitializeLoginPowers() {
// get all PowersBase
ArrayList<PowersBase> pbList = dbSkillReqHandler.getAllPowersBase();
for (PowersBase pb : pbList) {
if (pb.getToken() != 0)
PowersManager.powersBaseByToken.put(pb.getToken(), pb);
}
}
// This pre-loads all powers and effects
public static void InitializePowers() {
// Add EffectsBase
ArrayList<EffectsBase> ebList = dbEffectsBaseHandler.getAllEffectsBase();
for (EffectsBase eb : ebList) {
PowersManager.effectsBaseByToken.put(eb.getToken(), eb);
PowersManager.effectsBaseByIDString.put(eb.getIDString(), eb);
}
// Add Fail Conditions
EffectsBase.getFailConditions(PowersManager.effectsBaseByIDString);
// Add Modifiers to Effects
dbEffectsBaseHandler.cacheAllEffectModifiers();
// Add Source Types to Effects
dbPowerHandler.addAllSourceTypes();
dbPowerHandler.addAllAnimationOverrides();
// Add PowerActions
AbstractPowerAction.getAllPowerActions(PowersManager.powerActionsByIDString, PowersManager.powerActionsByID, PowersManager.effectsBaseByIDString);
// Load valid Item Flags
// AbstractPowerAction.loadValidItemFlags(PowersManager.powerActionsByIDString);
// get all PowersBase
ArrayList<PowersBase> pbList = dbSkillReqHandler.getAllPowersBase();
for (PowersBase pb : pbList) {
if (pb.getToken() != 0) {
PowersManager.powersBaseByIDString.put(pb.getIDString(), pb);
PowersManager.powersBaseByToken.put(pb.getToken(), pb);
}
}
// Add Power Prereqs
PowerPrereq.getAllPowerPrereqs(PowersManager.powersBaseByIDString);
// Add Fail Conditions
dbSkillReqHandler.getFailConditions(PowersManager.powersBaseByIDString);
// Add Actions Base
ActionsBase.getActionsBase(PowersManager.powersBaseByIDString,
PowersManager.powerActionsByIDString);
}
public static EffectsBase setEffectToken(int ID, int token) {
for (EffectsBase eb : PowersManager.effectsBaseByIDString.values()) {
if (eb.getUUID() == ID) {
eb.setToken(token);
return eb;
}
}
return null;
}
public static void usePower(final PerformActionMsg msg, ClientConnection origin,
boolean sendCastToSelf) {
if (usePowerA(msg, origin, sendCastToSelf)) {
// Cast failed for some reason, reset timer
RecyclePowerMsg recyclePowerMsg = new RecyclePowerMsg(msg.getPowerUsedID());
Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), recyclePowerMsg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
// Send Fail to cast message
PlayerCharacter pc = SessionManager
.getPlayerCharacter(origin);
if (pc != null) {
sendPowerMsg(pc, 2, msg);
if (pc.isCasting()) {
pc.update();
}
pc.setIsCasting(false);
}
}
}
public static void useMobPower(Mob caster, AbstractCharacter target, PowersBase pb, int rank) {
PerformActionMsg msg = createPowerMsg(pb, rank, caster, target);
msg.setUnknown04(1);
if (useMobPowerA(msg, caster)) {
//sendMobPowerMsg(caster,2,msg); //Lol wtf was i thinking sending msg's to mobs... ZZZZ
}
}
public static boolean usePowerA(final PerformActionMsg msg, ClientConnection origin,
boolean sendCastToSelf) {
PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter(
origin);
if (playerCharacter == null)
return false;
boolean CSRCast = false;
if (MBServerStatics.POWERS_DEBUG) {
ChatManager.chatSayInfo(
playerCharacter,
"Using Power: " + Integer.toHexString(msg.getPowerUsedID())
+ " (" + msg.getPowerUsedID() + ')');
Logger.info("Using Power: "
+ Integer.toHexString(msg.getPowerUsedID()) + " ("
+ msg.getPowerUsedID() + ')');
}
//Sending recycle message to player if died while casting.
if (!playerCharacter.isAlive() && msg.getPowerUsedID() != 428589216) { //succor
RecyclePowerMsg recyclePowerMsg = new RecyclePowerMsg(msg.getPowerUsedID());
Dispatch dispatch = Dispatch.borrow(playerCharacter, recyclePowerMsg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
return false;
}
// if (!pc.getPowers().contains(msg.getPowerUsedID())) {
// sendPowerMsg(pc, 10, msg);
// return false;
// }
// verify recycle timer is not active for power, skip for a CSR
if (playerCharacter.getRecycleTimers().containsKey(msg.getPowerUsedID()) && !playerCharacter.isCSR()) {
// ChatManager.chatSayInfo(pc, "Recycle timer not finished!");
Logger.warn("usePowerA(): Cheat attempted? '" + msg.getPowerUsedID() + "' recycle timer not finished " + playerCharacter.getName());
return false;
}
// get power
PowersBase pb = PowersManager.powersBaseByToken.get(msg.getPowerUsedID());
if (pb == null) {
ChatManager.chatSayInfo(playerCharacter,
"This power is not implemented yet.");
// Logger.error("usePowerA(): Power '" +
// msg.getPowerUsedID()
// + "' was not found on powersBaseByToken map.");
return true;
// return false;
}
if (playerCharacter.getLastPower() != null)
return true;
//Check if Power Target is allowed to cast.
// Check powers for normal users
if (playerCharacter.getPowers() == null || !playerCharacter.getPowers().containsKey(msg.getPowerUsedID()))
if (!playerCharacter.isCSR()) {
if (!MBServerStatics.POWERS_DEBUG) {
// ChatManager.chatSayInfo(pc, "You may not cast that spell!");
Logger.info("usePowerA(): Cheat attempted? '" + msg.getPowerUsedID() + "' was not associated with " + playerCharacter.getName());
return true;
}
} else
CSRCast = true;
// get numTrains for power
int trains = msg.getNumTrains();
// can't go over the max trains for the power, unless CSR
if (trains > pb.getMaxTrains() && !playerCharacter.isCSR()) {
trains = pb.getMaxTrains();
msg.setNumTrains(trains);
}
// can't go over total trains by player
if (playerCharacter.getPowers() != null && playerCharacter.getPowers().containsKey(msg.getPowerUsedID())) {
CharacterPower cp = playerCharacter.getPowers().get(msg.getPowerUsedID());
if (cp != null) {
int tot = cp.getTotalTrains();
if (tot == 0 && !playerCharacter.isCSR())
return false;
if (trains != tot && !playerCharacter.isCSR()) {
trains = tot;
msg.setNumTrains(trains);
}
}
}
// get recycle time in ms
int time = pb.getRecycleTime(trains);
// verify player is in correct mode (combat/nonCombat)
if (playerCharacter.isCombat()) {
if (!pb.allowedInCombat())
// ChatManager.chatPowerError(pc,
// "This power is not allowed in combat mode.");
return true;
} else if (!pb.allowedOutOfCombat())
// ChatManager.chatPowerError(pc,
// "You must be in combat mode to use this power.");
return true;
// verify player is not stunned or prohibited from casting
PlayerBonuses bonus = playerCharacter.getBonuses();
SourceType sourceType = SourceType.GetSourceType(pb.getCategory());
if (bonus != null && (bonus.getBool(ModType.Stunned, SourceType.None) || bonus.getBool(ModType.CannotCast, SourceType.None) || bonus.getBool(ModType.BlockedPowerType, sourceType)))
return true;
// if moving make sure spell valid for movement
Vector3fImmutable endLoc = playerCharacter.getEndLoc();
if (!pb.canCastWhileMoving())
if (playerCharacter.isMoving()) {
// if movement left is less then 1 seconds worth then let cast
// go through.
float distanceLeftSquared = endLoc.distanceSquared2D(playerCharacter.getLoc());
if (distanceLeftSquared > sqr(playerCharacter.getSpeed()))
return true;
}
// if flying, make sure spell valid for flying.
// if (pc.getAltitude() > 0)
// if (!pb.canCastWhileFlying())
// return true;
int targetLiveCounter = -1;
// get target based on targetType;
if (pb.targetFromLastTarget() || pb.targetPet()) // use msg's target
if (pb.isAOE()) {
if (!pb.usePointBlank()) {
AbstractWorldObject target = getTarget(msg);
if (target != null && target.getObjectType() == GameObjectType.Building && !pb.targetBuilding()) {
PowersManager.sendPowerMsg(playerCharacter, 9, new PerformActionMsg(msg));
return true;
}
if (target == null) {
if (playerCharacter.getLoc().distanceSquared2D(msg.getTargetLoc()) > sqr(pb
.getRange()))
return true;
} else if (verifyInvalidRange(playerCharacter, target, pb.getRange()))
// pc.getLoc().distance(target.getLoc()) >
// pb.getRange())
return true;
}
} else {
// get target
AbstractWorldObject target = getTarget(msg);
if (target == null)
return true;
if (!target.isAlive() && target.getObjectType().equals(GameObjectType.Building) == false && msg.getPowerUsedID() != 428589216)
return true;
float range = pb.getRange();
// verify target is in range
if (verifyInvalidRange(playerCharacter, target, range))
// (pc.getLoc().distance(target.getLoc()) > pb.getRange()) {
// TODO send message that target is out of range
return true;
// verify target is valid type
if (!validateTarget(target, playerCharacter, pb))
return true;
if (AbstractWorldObject.IsAbstractCharacter(target))
targetLiveCounter = ((AbstractCharacter) target).getLiveCounter();
}
// verify regular player can cast spell, otherwise authenticate
if (!pb.regularPlayerCanCast()) {
Account a = SessionManager.getAccount(playerCharacter);
if (a.status.equals(AccountStatus.ADMIN) == false) {
Logger.warn("Player '" + playerCharacter.getName()
+ "' is attempting to cast a spell outside of their own access level.");
return true;
}
}
// verify player has proper effects applied to use power
if (pb.getEffectPrereqs().size() > 0 && playerCharacter.getEffects() != null) {
boolean passed = false;
for (PowerPrereq pp : pb.getEffectPrereqs()) {
EffectsBase eb = PowersManager.getEffectByIDString(pp.getEffect());
if (playerCharacter.containsEffect(eb.getToken())) {
passed = true;
break;
}
}
if (!passed)
return true;
}
//verify player has proper equipment to use power
if (pb.getEquipPrereqs().size() > 0) {
boolean passed = false;
for (PowerPrereq pp : pb.getEquipPrereqs()) {
int slot = pp.mainHand() ? MBServerStatics.SLOT_MAINHAND : MBServerStatics.SLOT_OFFHAND;
if (playerCharacter.validEquip(slot, pp.getMessage())) {
passed = true; //should have item in slot
break;
} else if (!pp.isRequired()) {
passed = true; //should not have item in slot
break;
}
}
if (!passed)
return true;
}
// TODO if target immune to powers, cancel unless aoe
// Also make sure No.ImmuneToPowers is false for target
// if there's a different power waiting to finish, stop here
//get power cost and calculate any power cost modifiers
float cost = pb.getCost(trains);
if (playerCharacter.isCSR())
cost = 0;
if (bonus != null)
cost *= (1 + bonus.getFloatPercentAll(ModType.PowerCost, SourceType.None));
if (playerCharacter.getAltitude() > 0)
cost *= 1.5f;
// Verify player can afford to use power
//CCR toons dont use cost
if (!playerCharacter.isCSR()) {
if (cost > 0)
if ((playerCharacter.getObjectTypeMask() & MBServerStatics.MASK_UNDEAD) != 0)
if (playerCharacter.getHealth() <= cost)
return true;
else {
playerCharacter.modifyHealth(-cost, playerCharacter, true);
ModifyHealthMsg mhm = new ModifyHealthMsg(playerCharacter, playerCharacter, -cost,
0f, 0f, 0, null,
9999, 0);
mhm.setOmitFromChat(1);
DispatchMessage.dispatchMsgToInterestArea(playerCharacter, mhm, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
}
else if (pb.useMana())
if (playerCharacter.getMana() < cost)
return true;
else
playerCharacter.modifyMana(-cost, playerCharacter, true);
else if (pb.useStamina())
if (playerCharacter.getStamina() < cost)
return true;
else
playerCharacter.modifyStamina(-cost, playerCharacter, true);
else if (playerCharacter.getHealth() <= cost)
return true;
else
playerCharacter.modifyHealth(-cost, playerCharacter, true);
}
// Validity checks passed, move on to casting spell
//get caster's live counter
int casterLiveCounter = playerCharacter.getLiveCounter();
// run recycle job for when cast is available again, don't bother adding the timer for CSRs
if (time > 0) {
FinishRecycleTimeJob frtj = new FinishRecycleTimeJob(playerCharacter, msg);
playerCharacter.getRecycleTimers().put(msg.getPowerUsedID(), js.scheduleJob(frtj, time));
} else {
// else send recycle message to unlock power
RecyclePowerMsg recyclePowerMsg = new RecyclePowerMsg(msg.getPowerUsedID());
Dispatch dispatch = Dispatch.borrow(playerCharacter, recyclePowerMsg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
}
//what the fuck?
// // Send cast to other players
// if ((playerCharacter.getObjectTypeMask() & MBServerStatics.MASK_UNDEAD) != 0)
// msg.setUnknown04(2); // Vampire Race, use health?
// else
// msg.setUnknown04(1); // Regular Race, use mana?
int tr = msg.getNumTrains();
DispatchMessage.dispatchMsgToInterestArea(playerCharacter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, sendCastToSelf, false);
//Make new msg..
PerformActionMsg copyMsg = new PerformActionMsg(msg);
copyMsg.setNumTrains(tr);
// make person casting stand up if spell (unless they're casting a chant which does not make them stand up)
if (pb.isSpell() && !pb.isChant() && playerCharacter.isSit()) {
playerCharacter.update();
playerCharacter.setSit(false);
UpdateStateMsg updateStateMsg = new UpdateStateMsg(playerCharacter);
DispatchMessage.dispatchMsgToInterestArea(playerCharacter, updateStateMsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
}
// update cast (use skill) fail condition
playerCharacter.cancelOnCast();
// update castSpell (use spell) fail condition if spell
if (pb.isSpell())
playerCharacter.cancelOnSpell();
// get cast time in ms.
time = pb.getCastTime(trains);
// set player is casting for regens
if (time > 100) {
playerCharacter.update();
playerCharacter.setIsCasting(true);
}
playerCharacter.setLastMovementState(playerCharacter.getMovementState());
// update used power timer
playerCharacter.setLastUsedPowerTime();
// run timer job to end cast
if (time < 1) // run immediately
finishUsePower(copyMsg, playerCharacter, casterLiveCounter, targetLiveCounter);
else {
UsePowerJob upj = new UsePowerJob(playerCharacter, copyMsg, copyMsg.getPowerUsedID(), pb, casterLiveCounter, targetLiveCounter);
JobContainer jc = js.scheduleJob(upj, time);
// make lastPower
playerCharacter.setLastPower(jc);
}
if (CSRCast)
Logger.info("CSR " + playerCharacter.getName() + " cast power " + msg.getPowerUsedID() + '.');
return false;
}
public static void testPowers(ByteBufferWriter writer) {
writer.putInt(powersBaseByToken.size());
for (int token : powersBaseByToken.keySet()) {
writer.putInt(token);
writer.putInt(40);
}
}
public static boolean useMobPowerA(PerformActionMsg msg, Mob caster) {
if (caster == null)
return false;
if (!caster.isAlive() && msg.getPowerUsedID() != 428589216) //succor
return false;
// get power
PowersBase pb = PowersManager.powersBaseByToken.get(msg.getPowerUsedID());
if (pb == null)
return true;
// Check powers for normal users
// get numTrains for power
int trains = msg.getNumTrains();
// can't go over the max trains for the power, unless CSR
// can't go over total trains by player
// get recycle time in ms
int time = pb.getRecycleTime(trains);
// verify player is in correct mode (combat/nonCombat)
// verify player is not stunned or prohibited from casting
PlayerBonuses bonus = caster.getBonuses();
SourceType sourceType = SourceType.GetSourceType(pb.getCategory());
if (bonus != null && (bonus.getBool(ModType.Stunned, SourceType.None) || bonus.getBool(ModType.CannotCast, SourceType.None) || bonus.getBool(ModType.BlockedPowerType, sourceType)))
return true;
// if moving make sure spell valid for movement
// if flying, make sure spell valid for flying.
// if (pc.getAltitude() > 0)
// if (!pb.canCastWhileFlying())
// return true;
int targetLiveCounter = -1;
// get target based on targetType;
if (pb.targetFromLastTarget() || pb.targetPet()) // use msg's target
if (pb.isAOE()) {
if (!pb.usePointBlank()) {
AbstractWorldObject target = getTarget(msg);
if (target == null) {
if (caster.getLoc().distanceSquared2D(msg.getTargetLoc()) > sqr(pb
.getRange()))
return true;
} else if (verifyInvalidRange(caster, target, pb.getRange()))
// pc.getLoc().distance(target.getLoc()) >
// pb.getRange())
return true;
}
} else {
// get target
AbstractWorldObject target = getTarget(msg);
if (target == null)
return true;
// verify target is in range
if (verifyInvalidRange(caster, target, pb.getRange()))
// (pc.getLoc().distance(target.getLoc()) > pb.getRange()) {
// TODO send message that target is out of range
return true;
// verify target is valid type
if (AbstractWorldObject.IsAbstractCharacter(target))
targetLiveCounter = ((AbstractCharacter) target).getLiveCounter();
}
// TODO if target immune to powers, cancel unless aoe
// Also make sure No.ImmuneToPowers is false for target
// if there's a different power waiting to finish, stop here
if (caster.getLastMobPowerToken() != 0)
return true;
//get power cost and calculate any power cost modifiers
// Validity checks passed, move on to casting spell
//get caster's live counter
int casterLiveCounter = caster.getLiveCounter();
// run recycle job for when cast is available again, don't bother adding the timer for CSRs
// Send cast to other players
if (caster.getObjectTypeMask() == MBServerStatics.MASK_UNDEAD)
msg.setUnknown05(0); // Regular Race, use mana?
else
msg.setUnknown05(0);
int tr = msg.getNumTrains();
msg.setNumTrains(9999);
DispatchMessage.sendToAllInRange(caster, msg);
DispatchMessage.sendToAllInRange(caster, msg);
msg.setNumTrains(tr);
// make person casting stand up if spell (unless they're casting a chant which does not make them stand up)
// update cast (use skill) fail condition
caster.cancelOnCast();
// update castSpell (use spell) fail condition if spell
if (pb.isSpell())
caster.cancelOnSpell();
// get cast time in ms.
time = pb.getCastTime(trains);
// set player is casting for regens
caster.setIsCasting(true);
caster.setLastMobPowerToken(pb.getToken());
// run timer job to end cast
if (time < 1 || pb.getToken() == -1994153779) {
// run immediately
finishUseMobPower(msg, caster, casterLiveCounter, targetLiveCounter);
caster.setLastMobPowerToken(0);
} else {
caster.setLastMobPowerToken(pb.getToken());
caster.setTimeStamp("FinishCast", System.currentTimeMillis() + (pb.getCastTime(trains)));
}
// finishUseMobPower(msg, caster, casterLiveCounter, targetLiveCounter); // UseMobPowerJob upj = new UseMobPowerJob(caster, msg, msg.getPowerUsedID(), pb, casterLiveCounter, targetLiveCounter);
// JobContainer jc = js.scheduleJob(upj, time);
// // make lastPower
return false;
}
// called when a spell finishes casting. perform actions
public static void finishUsePower(final PerformActionMsg msg, PlayerCharacter playerCharacter, int casterLiveCounter, int targetLiveCounter) {
PerformActionMsg performActionMsg;
Dispatch dispatch;
if (playerCharacter == null || msg == null)
return;
if (playerCharacter.isCasting()) {
playerCharacter.update();
playerCharacter.updateStamRegen(-100);
}
playerCharacter.resetLastSetLocUpdate();
playerCharacter.setIsCasting(false);
// can't go over total trains by player
if (!playerCharacter.isAlive() || playerCharacter.getLiveCounter() != casterLiveCounter) {
playerCharacter.clearLastPower();
finishRecycleTime(msg.getPowerUsedID(), playerCharacter, true);
// Let's do good OO. Clone message don't modify it.
performActionMsg = new PerformActionMsg(msg);
performActionMsg.setNumTrains(9999);
performActionMsg.setUnknown04(2);
dispatch = Dispatch.borrow(playerCharacter, performActionMsg);
DispatchMessage.dispatchMsgToInterestArea(playerCharacter, performActionMsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
return;
}
// set player is not casting for regens
// clear power.
playerCharacter.clearLastPower();
PowersBase pb = PowersManager.powersBaseByToken.get(msg.getPowerUsedID());
if (pb == null) {
Logger.error(
"finishUsePower(): Power '" + msg.getPowerUsedID()
+ "' was not found on powersBaseByToken map.");
return;
}
int trains = msg.getNumTrains();
// update used power timer
playerCharacter.setLastUsedPowerTime();
// verify player is not stunned or power type is blocked
PlayerBonuses bonus = playerCharacter.getBonuses();
if (bonus != null) {
if (bonus.getBool(ModType.Stunned, SourceType.None))
return;
SourceType sourceType = SourceType.GetSourceType(pb.getCategory());
if (bonus.getBool(ModType.BlockedPowerType, sourceType)) {
finishRecycleTime(msg.getPowerUsedID(), playerCharacter, true);
return;
}
}
// get target loc
Vector3fImmutable targetLoc = msg.getTargetLoc();
if (pb.targetFromLastTarget() || pb.targetPet()) // use msg's target
if (pb.isAOE()) {
if (!pb.usePointBlank()) {
AbstractWorldObject mainTarget = getTarget(msg);
float speedRange = 0;
if (AbstractWorldObject.IsAbstractCharacter(mainTarget)) {
speedRange = ((AbstractCharacter) mainTarget).getSpeed() * (pb.getCastTime(trains) * .001f);
}
if (mainTarget != null && mainTarget.getObjectType() == GameObjectType.Building && !pb.targetBuilding()) {
PowersManager.sendPowerMsg(playerCharacter, 8, new PerformActionMsg(msg));
return;
}
if (mainTarget == null) {
if (playerCharacter.getLoc().distanceSquared2D(msg.getTargetLoc()) > sqr(pb
.getRange())) {
sendPowerMsg(playerCharacter, 8, msg);
return;
}
} else if (verifyInvalidRange(playerCharacter, mainTarget, speedRange + pb.getRange())) {
sendPowerMsg(playerCharacter, 8, msg);
return;
}
}
} else {
// get target
AbstractWorldObject mainTarget = getTarget(msg);
if (mainTarget == null)
return;
float speedRange = 0;
if (AbstractWorldObject.IsAbstractCharacter(mainTarget)) {
speedRange = ((AbstractCharacter) mainTarget).getSpeed() * (pb.getCastTime(trains) * .001f);
}
float range = pb.getRange() + speedRange;
if (verifyInvalidRange(playerCharacter, mainTarget, range)) {
sendPowerMsg(playerCharacter, 8, msg);
return;
}
// (pc.getLoc().distance(target.getLoc()) > pb.getRange()) {
// TODO send message that target is out of range
}
if (targetLoc.x == 0f || targetLoc.z == 0f) {
AbstractWorldObject tar = getTarget(msg);
if (tar != null)
targetLoc = tar.getLoc();
}
// get list of targets
HashSet<AbstractWorldObject> allTargets = getAllTargets(
getTarget(msg), msg.getTargetLoc(), playerCharacter, pb);
// no targets found. send error message
if (allTargets.size() == 0) {
sendPowerMsg(playerCharacter, 9, msg);
return;
}
playerCharacter.setHateValue(pb.getHateValue(trains));
//Send Cast Message.
// PerformActionMsg castMsg = new PerformActionMsg(msg);
// castMsg.setNumTrains(9999);
// castMsg.setUnknown04(3);
// DispatchMessage.dispatchMsgToInterestArea(playerCharacter, castMsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
//
boolean msgCasted = false;
for (AbstractWorldObject target : allTargets) {
if (target == null)
continue;
//Hacky Pyschic healing cross heal
//make sure target hasn't respawned since we began casting
//skip this if liveCounter = -1 (from aoe)
if (targetLiveCounter != -1)
if (AbstractWorldObject.IsAbstractCharacter(target))
if (targetLiveCounter != ((AbstractCharacter) target).getLiveCounter())
continue;
if (!target.isAlive() && target.getObjectType() != GameObjectType.Building && pb.getToken() != 428589216 && pb.getToken() != 429425915)
continue;
//make sure mob is awake to respond.
//if (target instanceof AbstractIntelligenceAgent)
//((AbstractIntelligenceAgent)target).enableIntelligence();
// If Hit roll required, test hit
boolean miss = false;
if (pb.requiresHitRoll() && !pb.isWeaponPower() && testAttack(playerCharacter, target, pb, msg)) {
miss = true;
//aggro mob even on a miss
if (target.getObjectType() == GameObjectType.Mob) {
Mob mobTarget = (Mob) target;
if (pb.isHarmful())
mobTarget.handleDirectAggro(playerCharacter);
}
continue;
}
if (target.getObjectType() == GameObjectType.Mob) {
Mob mobTarget = (Mob) target;
if (pb.isHarmful())
mobTarget.handleDirectAggro(playerCharacter);
}
//Power is aiding a target, handle aggro if combat target is a Mob.
if (!pb.isHarmful() && target.getObjectType() == GameObjectType.PlayerCharacter) {
PlayerCharacter pcTarget = (PlayerCharacter) target;
if (!pb.isHarmful())
Mob.HandleAssistedAggro(playerCharacter, pcTarget);
}
// update target of used power timer
if (pb.isHarmful())
if (target.getObjectType().equals(GameObjectType.PlayerCharacter) && target.getObjectUUID() != playerCharacter.getObjectUUID()) {
((PlayerCharacter) target).setLastTargetOfUsedPowerTime();
((PlayerCharacter) target).setTimeStamp("LastCombatPlayer", System.currentTimeMillis());
playerCharacter.setTimeStamp("LastCombatPlayer", System.currentTimeMillis());
}
//Player didn't miss power, send finish cast. Miss cast already sent.
// finally Apply actions
for (ActionsBase ab : pb.getActions()) {
// get numTrains for power, skip action if invalid
if (trains < ab.getMinTrains() || trains > ab.getMaxTrains())
continue;
// If something blocks the action, then stop
if (ab.blocked(target, pb, trains)) {
PowersManager.sendEffectMsg(playerCharacter, 5, ab, pb);
continue;
}
// TODO handle overwrite stack order here
String stackType = ab.getStackType();
stackType = (stackType.equals("IgnoreStack")) ? Integer.toString(ab.getUUID()) : stackType;
// if (!stackType.equals("IgnoreStack")) {
if (target.getEffects().containsKey(stackType)) {
// remove any existing power that overrides
Effect ef = target.getEffects().get(stackType);
AbstractEffectJob effect = null;
if (ef != null) {
JobContainer jc = ef.getJobContainer();
if (jc != null)
effect = (AbstractEffectJob) jc.getJob();
}
ActionsBase overwrite = effect.getAction();
if (overwrite == null) {
Logger.error("NULL ACTION FOR POWER " + effect.getPowerToken());
continue;
}
if (ab.getStackOrder() < overwrite.getStackOrder())
continue; // not high enough to overwrite
else if (ab.getStackOrder() > overwrite.getStackOrder()) {
effect.setNoOverwrite(true);
removeEffect(target, overwrite, true, false);
} else if (ab.getStackOrder() == overwrite.getStackOrder())
if (ab.greaterThanEqual()
&& (trains >= effect.getTrains())) {
effect.setNoOverwrite(true);
removeEffect(target, overwrite, true, false);
} else if (ab.always())
removeEffect(target, overwrite, true, false);
else if (ab.greaterThan()
&& (trains > effect.getTrains())) {
effect.setNoOverwrite(true);
removeEffect(target, overwrite, true, false);
} else if (ab.greaterThan() && pb.getToken() == effect.getPowerToken())
removeEffect(target, overwrite, true, false);
else
continue; // trains not high enough to overwrite
}
// }
runPowerAction(playerCharacter, target, targetLoc, ab, trains, pb);
if (!miss && !msgCasted) {
PerformActionMsg castMsg = new PerformActionMsg(msg);
castMsg.setNumTrains(9999);
castMsg.setUnknown04(2);
DispatchMessage.dispatchMsgToInterestArea(playerCharacter, castMsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
msgCasted = true;
}
}
}
if (!msgCasted) {
PerformActionMsg castMsg = new PerformActionMsg(msg);
castMsg.setNumTrains(9999);
castMsg.setUnknown04(2);
DispatchMessage.dispatchMsgToInterestArea(playerCharacter, castMsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
msgCasted = true;
}
//Handle chant
if (pb != null && pb.isChant())
for (ActionsBase ab : pb.getActions()) {
AbstractPowerAction pa = ab.getPowerAction();
if (pa != null)
if (pb.getToken() != 428950414 && pb.getToken() != 428884878)
pa.handleChant(playerCharacter, playerCharacter, targetLoc, trains, ab, pb);
else if (PowersManager.getTarget(msg) != null && PowersManager.getTarget(msg).isAlive())
pa.handleChant(playerCharacter, PowersManager.getTarget(msg), targetLoc, trains, ab, pb);
else
pa.handleChant(playerCharacter, null, targetLoc, trains, ab, pb);
}
//DispatchMessage.dispatchMsgToInterestArea(playerCharacter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
}
public static void finishUseMobPower(PerformActionMsg msg, Mob caster, int casterLiveCounter, int targetLiveCounter) {
if (caster == null || msg == null)
return;
if (!caster.isAlive() || caster.getLiveCounter() != casterLiveCounter)
return;
// set player is not casting for regens
caster.setIsCasting(false);
PowersBase pb = PowersManager.powersBaseByToken.get(msg.getPowerUsedID());
// clear power.
caster.setLastMobPowerToken(0);
if (pb == null) {
Logger.error(
"finishUsePower(): Power '" + msg.getPowerUsedID()
+ "' was not found on powersBaseByToken map.");
return;
}
int trains = msg.getNumTrains();
// update used power timer
// verify player is not stunned or power type is blocked
PlayerBonuses bonus = caster.getBonuses();
if (bonus != null) {
if (bonus.getBool(ModType.Stunned, SourceType.None))
return;
SourceType sourceType = SourceType.GetSourceType(pb.getCategory());
if (bonus.getBool(ModType.BlockedPowerType, sourceType))
return;
}
msg.setNumTrains(9999);
msg.setUnknown04(2);
DispatchMessage.sendToAllInRange(caster, msg);
// get target loc
Vector3fImmutable targetLoc = msg.getTargetLoc();
if (targetLoc.x == 0f || targetLoc.z == 0f) {
AbstractWorldObject tar = getTarget(msg);
if (tar != null)
targetLoc = tar.getLoc();
}
// get list of targets
HashSet<AbstractWorldObject> allTargets = getAllTargets(
getTarget(msg), msg.getTargetLoc(), caster, pb);
for (AbstractWorldObject target : allTargets) {
if (target == null)
continue;
//make sure target hasn't respawned since we began casting
//skip this if liveCounter = -1 (from aoe)
if (targetLiveCounter != -1)
if (AbstractWorldObject.IsAbstractCharacter(target))
if (targetLiveCounter != ((AbstractCharacter) target).getLiveCounter())
continue;
//make sure mob is awake to respond.
//if (target instanceof AbstractIntelligenceAgent)
//((AbstractIntelligenceAgent)target).enableIntelligence();
// If Hit roll required, test hit
if (pb.requiresHitRoll() && !pb.isWeaponPower() && testAttack(caster, target, pb, msg))
//aggro mob even on a miss
continue;
// update target of used power timer
if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) {
((PlayerCharacter) target).setLastTargetOfUsedPowerTime();
((PlayerCharacter) target).setTimeStamp("LastCombatPlayer", System.currentTimeMillis());
}
// finally Apply actions
for (ActionsBase ab : pb.getActions()) {
// get numTrains for power, skip action if invalid
if (trains < ab.getMinTrains() || trains > ab.getMaxTrains())
continue;
// If something blocks the action, then stop
if (ab.blocked(target, pb, trains))
continue;
// TODO handle overwrite stack order here
String stackType = ab.getStackType();
stackType = (stackType.equals("IgnoreStack")) ? Integer.toString(ab.getUUID()) : stackType;
// if (!stackType.equals("IgnoreStack")) {
if (target.getEffects().containsKey(stackType)) {
// remove any existing power that overrides
Effect ef = target.getEffects().get(stackType);
AbstractEffectJob effect = null;
if (ef != null) {
JobContainer jc = ef.getJobContainer();
if (jc != null)
effect = (AbstractEffectJob) jc.getJob();
}
ActionsBase overwrite = effect.getAction();
if (overwrite == null) {
Logger.error("NULL ACTION FOR EFFECT " + effect.getPowerToken());
continue;
}
if (ab.getStackOrder() < overwrite.getStackOrder())
continue; // not high enough to overwrite
else if (ab.getStackOrder() > overwrite.getStackOrder()) {
effect.setNoOverwrite(true);
removeEffect(target, overwrite, true, false);
} else if (ab.getStackOrder() == overwrite.getStackOrder())
if (ab.greaterThanEqual()
&& (trains >= effect.getTrains())) {
effect.setNoOverwrite(true);
removeEffect(target, overwrite, true, false);
} else if (ab.always())
removeEffect(target, overwrite, true, false);
else if (ab.greaterThan()
&& (trains > effect.getTrains())) {
effect.setNoOverwrite(true);
removeEffect(target, overwrite, true, false);
} else if (ab.greaterThan() && pb.getToken() == effect.getPowerToken())
removeEffect(target, overwrite, true, false);
else
continue; // trains not high enough to overwrite
}
// }
runPowerAction(caster, target, targetLoc, ab, trains, pb);
}
}
//Handle chant
if (pb != null && pb.isChant())
for (ActionsBase ab : pb.getActions()) {
AbstractPowerAction pa = ab.getPowerAction();
if (pa != null)
pa.handleChant(caster, caster, targetLoc, trains, ab, pb);
}
// TODO echo power use to everyone else
msg.setNumTrains(9999);
msg.setUnknown04(2);
DispatchMessage.sendToAllInRange(caster, msg);
}
// *** Refactor : Wtf is this mess?
private static boolean validMonsterType(AbstractWorldObject target, PowersBase pb) {
if (pb == null || target == null)
return false;
String mtp = pb.getMonsterTypePrereq();
if (mtp.length() == 0)
return true;
if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) {
PlayerCharacter pc = (PlayerCharacter) target;
int raceID = 0;
if (pc.getRace() != null)
raceID = pc.getRace().getRaceRuneID();
switch (mtp) {
case "Shade":
return raceID == 2015 || raceID == 2016;
case "Elf":
return raceID == 2008 || raceID == 2009;
case "Dwarf":
return raceID == 2006;
case "Aracoix":
return raceID == 2002 || raceID == 2003;
case "Irekei":
return raceID == 2013 || raceID == 2014;
case "Vampire":
return raceID == 2028 || raceID == 2029;
}
} else if (target.getObjectType().equals(GameObjectType.Mob)) {
Mob mob = (Mob) target;
if (pb.targetMob() && !(mob.agentType.equals(AIAgentType.MOBILE)) && !mob.isSiege())
return false;
else if (pb.targetPet() && !mob.isPet() && !mob.isSiege())
return false;
switch (mtp) {
case "Animal":
if ((mob.getObjectTypeMask() & MBServerStatics.MASK_BEAST) == 0)
return false;
break;
case "NPC":
if ((mob.getObjectTypeMask() & MBServerStatics.MASK_HUMANOID) == 0)
return false;
break;
case "Rat":
if ((mob.getObjectTypeMask() & MBServerStatics.MASK_RAT) == 0)
return false;
break;
case "Siege":
if (!mob.isSiege())
return false;
break;
case "Undead":
if ((mob.getObjectTypeMask() & MBServerStatics.MASK_UNDEAD) == 0)
return false;
break;
}
return true;
} else
return target.getObjectType().equals(GameObjectType.Building) && mtp.equals("Siege");
return false;
}
public static void summon(SendSummonsRequestMsg msg, ClientConnection origin) {
PlayerCharacter pc = SessionManager.getPlayerCharacter(
origin);
if (pc == null)
return;
PlayerCharacter target = SessionManager
.getPlayerCharacterByLowerCaseName(msg.getTargetName());
if (target == null || target.equals(pc) || target.isCombat()) {
if (target == null) // Player not found. Send not found message
ChatManager.chatInfoError(pc,
"Cannot find that player to summon.");
else if (target.isCombat())
ChatManager.chatInfoError(pc,
"Cannot summon player in combat.");
// else trying to summon self, just fail
// recycle summon
sendRecyclePower(msg.getPowerToken(), origin);
// TODO: client already subtracted 200 mana.. need to correct it
// end cast
PerformActionMsg pam = new PerformActionMsg(msg.getPowerToken(),
msg.getTrains(), msg.getSourceType(), msg.getSourceID(), 0,
0, 0f, 0f, 0f, 1, 0);
sendPowerMsg(pc, 2, pam);
return;
}
PerformActionMsg pam = new PerformActionMsg(msg.getPowerToken(), msg
.getTrains(), msg.getSourceType(), msg.getSourceID(), target
.getObjectType().ordinal(), target.getObjectUUID(), 0f, 0f, 0f, 1, 0);
// Client removes 200 mana on summon use.. so don't send message to self
target.addSummoner(pc.getObjectUUID(), System.currentTimeMillis() + MBServerStatics.FOURTYFIVE_SECONDS);
usePower(pam, origin, false);
}
public static void recvSummon(RecvSummonsRequestMsg msg, ClientConnection origin) {
PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
if (pc == null)
return;
PlayerCharacter source = PlayerCharacter.getFromCache(msg.getSourceID());
if (source == null)
return;
long tooLate = pc.getSummoner(source.getObjectUUID());
if (tooLate < System.currentTimeMillis()) {
ChatManager.chatInfoError(pc, "You waited too long to " + (msg.accepted() ? "accept" : "decline") + " the summons.");
pc.removeSummoner(source.getObjectUUID());
return;
}
if (pc.getBonuses() != null && pc.getBonuses().getBool(ModType.BlockedPowerType, SourceType.SUMMON)) {
ErrorPopupMsg.sendErrorMsg(pc, "You have been blocked from receiving summons!");
ErrorPopupMsg.sendErrorMsg(source, "Target is blocked from receiving summons!");
pc.removeSummoner(source.getObjectUUID());
return;
}
pc.removeSummoner(source.getObjectUUID());
// Handle Accepting or Denying a summons.
// set timer based on summon type.
boolean wentThrough = false;
if (msg.accepted())
// summons accepted, let's move the player if within time
if (source.isAlive()) {
// //make sure summons handled in time
ConcurrentHashMap<String, JobContainer> timers = source.getTimers();
// if (timers == null || !timers.containsKey("SummonSend")) {
// ChatManager.chatInfoError(pc, "You waited too long to " + (msg.accepted() ? "accept" : "decline") + " the summons.");
// return;
// }
// // clear last summons accept timer
// timers.get("SummonSend").cancelJob();
//timers.remove("SummonSend");
// cancel any other summons waiting
timers = pc.getTimers();
if (timers != null && timers.containsKey("Summon"))
timers.get("Summon").cancelJob();
// get time to wait before summons goes through
BaseClass base = source.getBaseClass();
PromotionClass promo = source.getPromotionClass();
int duration;
//determine if in combat with another player
//comment out this block to disable combat timer
// if (lastAttacked < 60000) {
// if (pc.inSafeZone()) //player in safe zone, no need for combat timer
// combat = false;
// else if (source.inSafeZone()) //summoner in safe zone, apply combat timer
// combat = true;
// else if ((source.getLoc().distance2D(pc.getLoc())) > 6144f)
// combat = true; //more than 1.5x width of zone, not tactical summons
// }
if (promo != null && promo.getObjectUUID() == 2519)
duration = 10000; // Priest summons, 10 seconds
else if (base != null && base.getObjectUUID() == 2501)
duration = 15000; // Healer Summons, 15 seconds
else
duration = 45000; // Belgosh Summons, 45 seconds
// Teleport to summoners location
FinishSummonsJob fsj = new FinishSummonsJob(source, pc);
JobContainer jc = JobScheduler.getInstance().scheduleJob(fsj,
duration);
if (timers != null)
timers.put("Summon", jc);
wentThrough = true;
}
// Summons failed
if (!wentThrough)
// summons refused. Let's be nice and reset recycle timer
if (source != null) {
// Send summons refused Message
ErrorPopupMsg.sendErrorPopup(source, 29);
// recycle summons power
//finishRecycleTime(428523680, source, true);
}
}
public static void trackWindow(TrackWindowMsg msg, ClientConnection origin) {
PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter(
origin);
if (playerCharacter == null)
return;
if (MBServerStatics.POWERS_DEBUG) {
ChatManager.chatSayInfo(
playerCharacter,
"Using Power: " + Integer.toHexString(msg.getPowerToken())
+ " (" + msg.getPowerToken() + ')');
Logger.info("Using Power: "
+ Integer.toHexString(msg.getPowerToken()) + " ("
+ msg.getPowerToken() + ')');
}
// get track power used
PowersBase pb = PowersManager.powersBaseByToken.get(msg.getPowerToken());
if (pb == null || !pb.isTrack())
return;
//check track threshold timer to prevent spam
long currentTime = System.currentTimeMillis();
long timestamp = playerCharacter.getTimeStamp("trackWindow");
long dif = currentTime - timestamp;
if (dif < MBServerStatics.TRACK_WINDOW_THRESHOLD)
return;
playerCharacter.setTimeStamp("trackWindow", currentTime);
ArrayList<ActionsBase> ablist = pb.getActions();
if (ablist == null)
return;
TrackPowerAction tpa = null;
for (ActionsBase ab : ablist) {
AbstractPowerAction apa = ab.getPowerAction();
if (apa != null && apa instanceof TrackPowerAction)
tpa = (TrackPowerAction) apa;
}
if (tpa == null)
return;
// Check powers for normal users
if (playerCharacter.getPowers() == null || !playerCharacter.getPowers().containsKey(msg.getPowerToken()))
if (!playerCharacter.isCSR())
if (!MBServerStatics.POWERS_DEBUG) {
// ChatManager.chatSayInfo(pc, "You may not cast that spell!");
// this.logEXPLOIT("usePowerA(): Cheat attempted? '" + msg.getPowerToken() + "' was not associated with " + pc.getName());
return;
}
// Get search mask for track
int mask = 0;
if (pb.targetPlayer())
if (tpa.trackVampire()) // track vampires
mask = MBServerStatics.MASK_PLAYER | MBServerStatics.MASK_UNDEAD;
else
// track all players
mask = MBServerStatics.MASK_PLAYER;
else if (pb.targetCorpse()) // track corpses
mask = MBServerStatics.MASK_CORPSE;
else if (tpa.trackNPC()) // Track NPCs
mask = MBServerStatics.MASK_NPC;
else if (tpa.trackUndead()) // Track Undead
mask = MBServerStatics.MASK_MOB | MBServerStatics.MASK_UNDEAD;
else
// Track All
mask = MBServerStatics.MASK_MOB | MBServerStatics.MASK_NPC;
// Find characters in range
HashSet<AbstractWorldObject> allTargets;
allTargets = WorldGrid.getObjectsInRangeContains(playerCharacter.getLoc(),
pb.getRange(), mask);
//remove anyone who can't be tracked
Iterator<AbstractWorldObject> it = allTargets.iterator();
while (it.hasNext()) {
AbstractWorldObject awo = it.next();
if (awo == null)
continue;
else if (!awo.isAlive())
it.remove();
else if (awo.getObjectType().equals(GameObjectType.PlayerCharacter)) {
PlayerBonuses bonus = ((PlayerCharacter) awo).getBonuses();
if (bonus != null && bonus.getBool(ModType.CannotTrack, SourceType.None))
it.remove();
}
}
// get max charcters for window
int maxTargets = 20;
PromotionClass promo = playerCharacter.getPromotionClass();
if (promo != null) {
int tableID = promo.getObjectUUID();
if (tableID == 2512 || tableID == 2514 || tableID == 2515)
maxTargets = 40;
}
// create list of characters
HashSet<AbstractCharacter> trackChars = RangeBasedAwo.getTrackList(
allTargets, playerCharacter, maxTargets);
TrackWindowMsg trackWindowMsg = new TrackWindowMsg(msg);
// send track window
trackWindowMsg.setSource(playerCharacter);
trackWindowMsg.setCharacters(trackChars);
Dispatch dispatch = Dispatch.borrow(playerCharacter, trackWindowMsg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
}
private static void sendRecyclePower(int token, ClientConnection origin) {
RecyclePowerMsg recyclePowerMsg = new RecyclePowerMsg(token);
Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), recyclePowerMsg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
}
public static boolean verifyInvalidRange(AbstractCharacter ac,
AbstractWorldObject target, float range) {
Vector3fImmutable sl = ac.getLoc();
Vector3fImmutable tl = target.getLoc();
if (target.getObjectType().equals(GameObjectType.Item)) {
Item item = (Item) target;
AbstractGameObject owner = item.getOwner();
if (owner == null || owner.getObjectType().equals(GameObjectType.Account))
return true;
if (owner.getObjectType().equals(GameObjectType.PlayerCharacter) || owner.getObjectType().equals(GameObjectType.Mob)) {
AbstractCharacter acOwner = (AbstractCharacter) owner;
CharacterItemManager itemMan = acOwner.getCharItemManager();
if (itemMan == null)
return true;
if (itemMan.inventoryContains(item)) {
tl = acOwner.getLoc();
return !(sl.distanceSquared(tl) <= sqr(range));
}
return true;
}
return true;
}
range += (calcHitBox(ac) + calcHitBox(target));
float distanceToTarget = sl.distanceSquared(tl);//distance to center of target
return distanceToTarget > range * range;
}
public static float calcHitBox(AbstractWorldObject ac) {
//TODO Figure out how Str Affects HitBox
float hitBox = 1;
switch (ac.getObjectType()) {
case PlayerCharacter:
PlayerCharacter pc = (PlayerCharacter) ac;
if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG)
Logger.info("Hit box radius for " + pc.getFirstName() + " is " + ((int) pc.statStrBase / 200f));
hitBox = 2f + (int) ((PlayerCharacter) ac).statStrBase / 50f;
break;
case Mob:
Mob mob = (Mob) ac;
if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG)
Logger.info("Hit box radius for " + mob.getFirstName()
+ " is " + ((Mob) ac).getMobBase().getHitBoxRadius());
hitBox = ((Mob) ac).getMobBase().getHitBoxRadius();
break;
case Building:
Building building = (Building) ac;
if (building.getBlueprint() == null)
return 32;
hitBox = Math.max(building.getBlueprint().getBuildingGroup().getExtents().x,
building.getBlueprint().getBuildingGroup().getExtents().y);
if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG)
Logger.info("Hit box radius for " + building.getName() + " is " + hitBox);
break;
}
return hitBox;
}
// Apply a power based on it's IDString
public static void applyPower(AbstractCharacter ac, AbstractWorldObject target,
Vector3fImmutable targetLoc, String ID, int trains, boolean fromItem) {
if (ac == null || target == null || !ac.isAlive())
return;
PowersBase pb = powersBaseByIDString.get(ID);
if (pb == null) {
Logger.error(
"applyPower(): Got NULL on powersBaseByIDString table lookup for: "
+ ID);
return;
}
applyPowerA(ac, target, targetLoc, pb, trains, fromItem);
}
// Apply a power based on it's Token
public static void applyPower(AbstractCharacter ac, AbstractWorldObject target,
Vector3fImmutable targetLoc, int token, int trains, boolean fromItem) {
if (ac == null || target == null)
return;
//Don't apply power if ac is dead, unless death shroud or safe mode
if (!ac.isAlive())
if (!(token == -1661758934 || token == 1672601862))
return;
PowersBase pb = powersBaseByToken.get(token);
if (pb == null) {
Logger.error(
"applyPower(): Got NULL on powersBaseByToken table lookup for: "
+ token);
return;
}
applyPowerA(ac, target, targetLoc, pb, trains, fromItem);
}
private static void applyPowerA(AbstractCharacter ac, AbstractWorldObject target,
Vector3fImmutable targetLoc, PowersBase pb, int trains,
boolean fromItem) {
int time = pb.getCastTime(trains);
if (!fromItem)
finishApplyPowerA(ac, target, targetLoc, pb, trains, false);
else if (time == 0)
finishApplyPower(ac, target, targetLoc, pb, trains, ac.getLiveCounter());
else {
ac.setItemCasting(true);
int tarType = (target == null) ? 0 : target.getObjectType().ordinal();
int tarID = (target == null) ? 0 : target.getObjectUUID();
// start the action animation
PerformActionMsg msg = new PerformActionMsg(pb.getToken(),
trains, ac.getObjectType().ordinal(), ac.getObjectUUID(), tarType, tarID, 0,
0, 0, 1, 0);
DispatchMessage.sendToAllInRange(target, msg);
ConcurrentHashMap<String, JobContainer> timers = ac.getTimers();
if (timers.containsKey(Integer.toString(pb.getToken()))) {
JobContainer jc = timers.get(Integer.toString(pb.getToken()));
if (jc != null)
jc.cancelJob();
}
// // clear any other items being used
// JobContainer jc = ac.getLastItem();
// if (jc != null) {
// jc.cancelJob();
// ac.clearLastItem();
// }
// run timer job to end cast
UseItemJob uij = new UseItemJob(ac, target, pb, trains, ac.getLiveCounter());
JobContainer jc = js.scheduleJob(uij, time);
// make lastItem
timers.put(Integer.toString(pb.getToken()), jc);
}
}
public static void finishApplyPower(AbstractCharacter ac,
AbstractWorldObject target, Vector3fImmutable targetLoc,
PowersBase pb, int trains, int liveCounter) {
if (ac != null)
ac.setItemCasting(false);
if (ac == null || target == null || pb == null)
return;
ac.clearTimer(Integer.toString(pb.getToken()));
if (liveCounter == ac.getLiveCounter())
finishApplyPowerA(ac, target, targetLoc, pb, trains, false);
}
public static void finishApplyPowerA(AbstractCharacter ac,
AbstractWorldObject target, Vector3fImmutable targetLoc,
PowersBase pb, int trains, boolean fromChant) {
// finally Apply actions
ArrayList<ActionsBase> actions = pb.getActions();
for (ActionsBase ab : actions) {
// get numTrains for power, skip action if invalid
if (trains < ab.getMinTrains() || trains > ab.getMaxTrains())
continue;
// If something blocks the action, then stop
if (ab.blocked(target, pb, trains))
// sendPowerMsg(pc, 5, msg);
continue;
// TODO handle overwrite stack order here
String stackType = ab.getStackType();
stackType = (stackType.equals("IgnoreStack")) ? Integer.toString(ab.getUUID()) : stackType;
if (target.getEffects().containsKey(stackType)) {
// remove any existing power that overrides
Effect ef = target.getEffects().get(stackType);
AbstractEffectJob effect = null;
if (ef != null) {
JobContainer jc = ef.getJobContainer();
if (jc != null)
effect = (AbstractEffectJob) jc.getJob();
}
ActionsBase overwrite = effect.getAction();
PowersBase pbOverwrite = effect.getPower();
if (pbOverwrite != null && pbOverwrite.equals(pb)
&& (trains >= effect.getTrains()))
removeEffect(target, overwrite, true, fromChant);
else if (ab.getStackOrder() < overwrite.getStackOrder())
continue; // not high enough to overwrite
else if (ab.getStackOrder() > overwrite.getStackOrder())
removeEffect(target, overwrite, true, false);
else if (ab.getStackOrder() == overwrite.getStackOrder())
if (ab.greaterThanEqual()
&& (trains >= effect.getTrains()))
removeEffect(target, overwrite, true, false);
else if (ab.always())
removeEffect(target, overwrite, true, false);
else if (ab.greaterThan()
&& (trains > effect.getTrains()))
removeEffect(target, overwrite, true, false);
else if (ab.greaterThan() && pb.getToken() == effect.getPowerToken())
removeEffect(target, overwrite, true, false);
else
continue; // trains not high enough to overwrite
}
if (fromChant)
targetLoc = Vector3fImmutable.ZERO;
runPowerAction(ac, target, targetLoc, ab, trains, pb);
}
//Handle chant
if (pb != null && pb.isChant())
for (ActionsBase ab : pb.getActions()) {
AbstractPowerAction pa = ab.getPowerAction();
if (pa != null)
pa.handleChant(ac, target, targetLoc, trains, ab, pb);
}
// for chants, only send the animation if the character is not is not moving or casting
boolean doAnimation = true;
if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) {
PlayerCharacter pc = (PlayerCharacter) target;
if (pb != null && pb.isChant() && (pc.isMoving() || pc.isCasting()))
doAnimation = false;
}
if (pb.getToken() == 428950414)
doAnimation = true;
if (doAnimation) {
PerformActionMsg msg = new PerformActionMsg(pb.getToken(), 9999, ac
.getObjectType().ordinal(), ac.getObjectUUID(), target.getObjectType().ordinal(),
target.getObjectUUID(), 0, 0, 0, 2, 0);
DispatchMessage.sendToAllInRange(ac, msg);
}
}
public static void runPowerAction(AbstractCharacter source,
AbstractWorldObject awo, Vector3fImmutable targetLoc,
ActionsBase ab, int trains, PowersBase pb) {
AbstractPowerAction pa = ab.getPowerAction();
if (pa == null) {
Logger.error(
"runPowerAction(): PowerAction not found of IDString: "
+ ab.getEffectID());
return;
}
pa.startAction(source, awo, targetLoc, trains, ab, pb);
}
public static void runPowerAction(AbstractCharacter source,
AbstractWorldObject awo, Vector3fImmutable targetLoc,
ActionsBase ab, int trains, PowersBase pb, int duration) {
AbstractPowerAction pa = ab.getPowerAction();
if (pa == null) {
Logger.error(
"runPowerAction(): PowerAction not found of IDString: "
+ ab.getEffectID());
return;
}
pa.startAction(source, awo, targetLoc, trains, ab, pb, duration);
}
public static HashSet<AbstractWorldObject> getAllTargets(
AbstractWorldObject target, Vector3fImmutable tl,
PlayerCharacter pc, PowersBase pb) {
HashSet<AbstractWorldObject> allTargets;
if (pb.isAOE()) {
Vector3fImmutable targetLoc = null;
if (pb.usePointBlank()) {
targetLoc = pc.getLoc();
} else {
if (target != null) {
targetLoc = target.getLoc();
} else {
targetLoc = tl;
try {
targetLoc = targetLoc.setY(HeightMap.getWorldHeight(targetLoc)); //on ground
} catch (Exception e) {
Logger.error(e);
targetLoc = tl;
}
}
}
if (targetLoc.x == 0f || targetLoc.z == 0f)
return new HashSet<>(); // invalid loc,
// return
// nothing
//first find targets in range quickly with QTree
if (pb.targetPlayer() && pb.targetMob())
// Player and mobs
allTargets = WorldGrid.getObjectsInRangePartial(
targetLoc, pb.getRadius(), MBServerStatics.MASK_MOBILE);
else if (pb.targetPlayer())
// Player only
allTargets = WorldGrid.getObjectsInRangePartial(
targetLoc, pb.getRadius(), MBServerStatics.MASK_PLAYER);
else if (pb.targetMob())
// Mob only
allTargets = WorldGrid.getObjectsInRangePartial(
targetLoc, pb.getRadius(), MBServerStatics.MASK_MOB
| MBServerStatics.MASK_PET);
else if (pb.targetPet())
//Pet only
allTargets = WorldGrid.getObjectsInRangePartial(
targetLoc, pb.getRadius(), MBServerStatics.MASK_PET);
else if (pb.targetNecroPet())
allTargets = WorldGrid.getObjectsInRangePartialNecroPets(
targetLoc, pb.getRadius());
else
allTargets = WorldGrid.getObjectsInRangePartial(
targetLoc, pb.getRadius(), 0);
// cleanup self, group and nation targets if needed
Iterator<AbstractWorldObject> awolist = allTargets.iterator();
while (awolist.hasNext()) {
AbstractWorldObject awo = awolist.next();
if (awo == null) {
awolist.remove(); // won't hit a null
continue;
}
//see if targets are within 3D range of each other
Vector3fImmutable tloc = awo.getLoc();
if (tloc.distanceSquared(targetLoc) > sqr(pb.getRadius())) {
awolist.remove(); // too far away
continue;
}
if (pb.isCasterFriendly() && pc.equals(awo)) {
awolist.remove(); // won't hit self
continue;
}
if (!awo.isAlive()) {
awolist.remove(); // too far away
continue;
}
if (awo.getObjectType().equals(GameObjectType.PlayerCharacter)) {
PlayerCharacter pcc = (PlayerCharacter) awo;
if (pb.isGroupFriendly() && GroupManager.getGroup(pc) != null && GroupManager.getGroup(pcc) != null)
if (GroupManager.getGroup(pc).equals(GroupManager.getGroup(pcc))) {
awolist.remove(); // Won't hit group members
continue;
}
if (pb.isNationFriendly() && pc.getGuild() != null &&
pc.getGuild().getNation() != null && pcc.getGuild() != null &&
pc.getGuild().getNation() != null)
if (pc.getGuild().getNation().equals(pcc.getGuild().getNation())) {
awolist.remove(); // Won't hit nation members
continue;
}
// Remove players for non-friendly spells in safe zone
if (pb.isHarmful() && (pcc.inSafeZone() || pc.inSafeZone())) {
awolist.remove();
continue;
}
}
}
// Trim list down to max size closest targets, limited by max
// Player/Mob amounts
allTargets = RangeBasedAwo.getSortedList(allTargets, targetLoc, pb
.getMaxNumPlayerTargets(), pb.getMaxNumMobTargets());
} else if (pb.targetGroup()) {
if (GroupManager.getGroup(pc) != null) {
allTargets = WorldGrid.getObjectsInRangePartial(pc
.getLoc(), pb.getRange(), MBServerStatics.MASK_PLAYER);
Iterator<AbstractWorldObject> awolist = allTargets.iterator();
while (awolist.hasNext()) {
AbstractWorldObject awo = awolist.next();
if (!(awo.getObjectType().equals(GameObjectType.PlayerCharacter))) {
awolist.remove(); // remove non players if there are any
continue;
}
PlayerCharacter pcc = (PlayerCharacter) awo;
if (GroupManager.getGroup(pcc) == null)
awolist.remove(); // remove players not in a group
else if (!GroupManager.getGroup(pcc).equals(GroupManager.getGroup(pc)))
awolist.remove(); // remove if not same group
}
} else {
allTargets = new HashSet<>();
allTargets.add(pc); // no group, use only self
}
} else {
allTargets = new HashSet<>();
if (pb.targetSelf())
allTargets.add(pc);
else if (pb.targetFromLastTarget())
allTargets.add(target);
else if (pb.targetFromNearbyMobs())
allTargets.add(target); // need better way to do this later
else
// targetByName
allTargets.add(target); // need to get name later
// can't target self if caster friendly
if (pb.isCasterFriendly() && allTargets.contains(pc))
allTargets.remove(0);
}
Iterator<AbstractWorldObject> awolist = allTargets.iterator();
while (awolist.hasNext()) {
AbstractWorldObject awo = awolist.next();
//See if target is valid type
if (!validMonsterType(awo, pb)) {
awolist.remove();
continue;
}
if (awo != null && awo.getObjectType().equals(GameObjectType.PlayerCharacter)) {
// Remove players who are in safe mode
PlayerCharacter pcc = (PlayerCharacter) awo;
PlayerBonuses bonuses = pcc.getBonuses();
if (bonuses != null && bonuses.getBool(ModType.ImmuneToPowers, SourceType.None)) {
awolist.remove();
continue;
}
//remove if power is harmful and caster or target is in safe zone
if (pb.isHarmful() && (pcc.inSafeZone() || pc.inSafeZone())) {
awolist.remove();
continue;
}
}
}
// verify target has proper effects applied to receive power
if (pb.getTargetEffectPrereqs().size() > 0) {
Iterator<AbstractWorldObject> it = allTargets.iterator();
while (it.hasNext()) {
boolean passed = false;
AbstractWorldObject awo = it.next();
if (awo.getEffects() != null) {
for (PowerPrereq pp : pb.getTargetEffectPrereqs()) {
EffectsBase eb = PowersManager.getEffectByIDString(pp.getEffect());
if (awo.containsEffect(eb.getToken())) {
passed = true;
break;
}
}
if (!passed)
it.remove();
} else
it.remove(); //awo is missing it's effects list
}
}
return allTargets;
}
public static HashSet<AbstractWorldObject> getAllTargets(
AbstractWorldObject target, Vector3fImmutable tl,
AbstractCharacter caster, PowersBase pb) {
HashSet<AbstractWorldObject> allTargets;
if (pb.isAOE()) {
Vector3fImmutable targetLoc = tl;
if (pb.usePointBlank()) {
targetLoc = caster.getLoc();
} else {
if (target != null) {
targetLoc = target.getLoc();
} else {
targetLoc = tl;
try {
targetLoc = targetLoc.setY(HeightMap.getWorldHeight(targetLoc)); //on ground
} catch (Exception e) {
Logger.error(e);
}
}
}
if (targetLoc.x == 0f || targetLoc.z == 0f)
return new HashSet<>(); // invalid loc,
//first find targets in range quickly with QTree
if (pb.targetPlayer() && pb.targetMob())
// Player and mobs
allTargets = WorldGrid.getObjectsInRangePartial(
targetLoc, pb.getRadius(), MBServerStatics.MASK_MOBILE);
else if (pb.targetPlayer())
// Player only
allTargets = WorldGrid.getObjectsInRangePartial(
targetLoc, pb.getRadius(), MBServerStatics.MASK_PLAYER);
else if (pb.targetMob())
// Mob only
allTargets = WorldGrid.getObjectsInRangePartial(
targetLoc, pb.getRadius(), MBServerStatics.MASK_MOB
| MBServerStatics.MASK_PET);
else if (pb.targetPet())
//Pet only
allTargets = WorldGrid.getObjectsInRangePartial(
targetLoc, pb.getRadius(), MBServerStatics.MASK_PET);
else if (pb.targetNecroPet())
allTargets = WorldGrid.getObjectsInRangePartialNecroPets(
targetLoc, pb.getRadius());
else
allTargets = WorldGrid.getObjectsInRangePartial(
targetLoc, pb.getRadius(), 0);
// cleanup self, group and nation targets if needed
Iterator<AbstractWorldObject> awolist = allTargets.iterator();
while (awolist.hasNext()) {
AbstractWorldObject awo = awolist.next();
if (awo == null) {
awolist.remove(); // won't hit a null
continue;
}
//see if targets are within 3D range of each other
Vector3fImmutable tloc = awo.getLoc();
if (tloc.distanceSquared(targetLoc) > sqr(pb.getRadius())) {
awolist.remove(); // too far away
continue;
}
if (pb.isCasterFriendly() && caster.equals(awo)) {
awolist.remove(); // won't hit self
continue;
}
if (awo.getObjectType() == GameObjectType.Mob) {
awolist.remove(); // Won't hit other mobs.
continue;
}
}
// Trim list down to max size closest targets, limited by max
// Player/Mob amounts
allTargets = RangeBasedAwo.getSortedList(allTargets, targetLoc, pb
.getMaxNumPlayerTargets(), pb.getMaxNumMobTargets());
} else if (pb.targetGroup()) {
allTargets = new HashSet<>();
allTargets.add(caster); // no group, use only self
} else {
allTargets = new HashSet<>();
if (pb.targetSelf())
allTargets.add(caster);
else if (pb.targetFromLastTarget())
allTargets.add(target);
else if (pb.targetFromNearbyMobs())
allTargets.add(target); // need better way to do this later
else
// targetByName
allTargets.add(target); // need to get name later
// can't target self if caster friendly
if (pb.isCasterFriendly() && allTargets.contains(caster))
allTargets.remove(caster);
}
Iterator<AbstractWorldObject> awolist = allTargets.iterator();
while (awolist.hasNext()) {
AbstractWorldObject awo = awolist.next();
//See if target is valid type
if (!validMonsterType(awo, pb)) {
awolist.remove();
continue;
}
if (awo != null && awo.getObjectType().equals(GameObjectType.PlayerCharacter)) {
// Remove players who are in safe mode
PlayerCharacter pcc = (PlayerCharacter) awo;
PlayerBonuses bonuses = pcc.getBonuses();
if (bonuses != null && bonuses.getBool(ModType.ImmuneToPowers, SourceType.None)) {
awolist.remove();
continue;
}
}
}
// verify target has proper effects applied to receive power
if (pb.getTargetEffectPrereqs().size() > 0) {
Iterator<AbstractWorldObject> it = allTargets.iterator();
while (it.hasNext()) {
boolean passed = false;
AbstractWorldObject awo = it.next();
if (awo.getEffects() != null) {
for (PowerPrereq pp : pb.getTargetEffectPrereqs()) {
EffectsBase eb = PowersManager.getEffectByIDString(pp.getEffect());
if (awo.containsEffect(eb.getToken())) {
passed = true;
break;
}
}
if (!passed)
it.remove();
} else
it.remove(); //awo is missing it's effects list
}
}
return allTargets;
}
// removes an effect before time is finished
public static void removeEffect(AbstractWorldObject awo, ActionsBase toRemove,
boolean overwrite, boolean fromChant) {
if (toRemove == null)
return;
String stackType = toRemove.getStackType();
stackType = (stackType.equals("IgnoreStack")) ? Integer
.toString(toRemove.getUUID()) : stackType;
if (fromChant) {
Effect eff = awo.getEffects().get(stackType);
if (eff != null)
eff.cancelJob(true);
} else
awo.cancelEffect(stackType, overwrite);
}
// removes an effect when timer finishes
public static void finishEffectTime(AbstractWorldObject source,
AbstractWorldObject awo, ActionsBase toRemove, int trains) {
if (awo == null || toRemove == null)
return;
// remove effect from player
String stackType = toRemove.getStackType();
if (stackType.equals("IgnoreStack"))
stackType = Integer.toString(toRemove.getUUID());
awo.endEffect(stackType);
}
// removes an effect when timer is canceled
public static void cancelEffectTime(AbstractWorldObject source,
AbstractWorldObject awo, PowersBase pb, EffectsBase eb,
ActionsBase toRemove, int trains, AbstractEffectJob efj) {
if (awo == null || pb == null || eb == null || toRemove == null)
return;
eb.endEffect(source, awo, trains, pb, efj);
}
// called when cooldown ends letting player cast next spell
public static void finishCooldownTime(PerformActionMsg msg, PlayerCharacter pc) {
// clear spell so player can cast again
// if (pc != null)
// pc.clearLastPower();
}
// called when recycle time ends letting player cast spell again
public static void finishRecycleTime(PerformActionMsg msg, PlayerCharacter pc,
boolean canceled) {
finishRecycleTime(msg.getPowerUsedID(), pc, canceled);
}
public static void finishRecycleTime(int token, PlayerCharacter pc,
boolean canceled) {
if (pc == null)
return;
ConcurrentHashMap<Integer, JobContainer> recycleTimers = pc
.getRecycleTimers();
// clear recycle time
if (recycleTimers != null)
if (recycleTimers.containsKey(token)) {
if (canceled) {
JobContainer jc = recycleTimers.get(token);
if (jc != null)
jc.cancelJob();
}
recycleTimers.remove(token);
}
// send recycle message to unlock power
RecyclePowerMsg recyclePowerMsg = new RecyclePowerMsg(token);
Dispatch dispatch = Dispatch.borrow(pc, recyclePowerMsg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
}
// Called when a fail condition is met by player
// such as moving, taking damage, ect.
public static void cancelUseLastPower(PlayerCharacter pc) {
if (pc == null)
return;
// set player is not casting for regens
if (pc.isCasting()) {
pc.update();
}
pc.setIsCasting(false);
UsePowerJob lastPower = null;
JobContainer jc = pc.getLastPower();
if (jc != null)
lastPower = ((UsePowerJob) jc.getJob());
if (lastPower == null)
return;
// clear recycle timer
int token = lastPower.getToken();
if (pc.getRecycleTimers().contains(token))
finishRecycleTime(token, pc, true);
// pc.getRecycleTimers().remove(token);
// Cancel power
js.cancelScheduledJob(lastPower);
// clear last power
pc.clearLastPower();
}
private static AbstractWorldObject getTarget(PerformActionMsg msg) {
int type = msg.getTargetType();
int UUID = msg.getTargetID();
if (type == -1 || type == 0 || UUID == -1 || UUID == 0)
return null;
return (AbstractWorldObject) DbManager.getObject(GameObjectType.values()[type], UUID);
}
public static boolean testAttack(PlayerCharacter pc, AbstractWorldObject awo,
PowersBase pb, PerformActionMsg msg) {
// Get defense for target
float atr = CharacterSkill.getATR(pc, pb.getSkillName());
float defense;
if (AbstractWorldObject.IsAbstractCharacter(awo)) {
AbstractCharacter tar = (AbstractCharacter) awo;
defense = tar.getDefenseRating();
} else
defense = 0f;
// Get hit chance
if (pc.getDebug(16)) {
String smsg = "ATR: " + atr + ", Defense: " + defense;
ChatManager.chatSystemInfo(pc, smsg);
}
int chance;
if (atr > defense || defense == 0)
chance = 94;
else {
float dif = atr / defense;
if (dif <= 0.8f)
chance = 4;
else
chance = ((int) (450 * (dif - 0.8f)) + 4);
}
// calculate hit/miss
int roll = ThreadLocalRandom.current().nextInt(100);
boolean disable = true;
if (roll < chance) {
// Hit, check if dodge kicked in
if (awo instanceof AbstractCharacter) {
AbstractCharacter tarAc = (AbstractCharacter) awo;
// Handle Dodge passive
if (testPassive(pc, tarAc, "Dodge")) {
// Dodge fired, send dodge message
PerformActionMsg dodgeMsg = new PerformActionMsg(msg);
dodgeMsg.setTargetType(awo.getObjectType().ordinal());
dodgeMsg.setTargetID(awo.getObjectUUID());
sendPowerMsg(pc, 4, dodgeMsg);
return true;
}
}
return false;
} else {
// Miss. Send miss message
PerformActionMsg missMsg = new PerformActionMsg(msg);
missMsg.setTargetType(awo.getObjectType().ordinal());
missMsg.setTargetID(awo.getObjectUUID());
sendPowerMsg(pc, 3, missMsg);
return true;
}
}
public static boolean testAttack(Mob caster, AbstractWorldObject awo,
PowersBase pb, PerformActionMsg msg) {
// Get defense for target
float atr = 2000;
float defense;
if (AbstractWorldObject.IsAbstractCharacter(awo)) {
AbstractCharacter tar = (AbstractCharacter) awo;
defense = tar.getDefenseRating();
} else
defense = 0f;
// Get hit chance
int chance;
if (atr > defense || defense == 0)
chance = 94;
else {
float dif = atr / defense;
if (dif <= 0.8f)
chance = 4;
else
chance = ((int) (450 * (dif - 0.8f)) + 4);
}
// calculate hit/miss
int roll = ThreadLocalRandom.current().nextInt(100);
if (roll < chance) {
// Hit, check if dodge kicked in
if (AbstractWorldObject.IsAbstractCharacter(awo)) {
AbstractCharacter tarAc = (AbstractCharacter) awo;
// Handle Dodge passive
return testPassive(caster, tarAc, "Dodge");
}
return false;
} else
return true;
}
public static void sendPowerMsg(PlayerCharacter playerCharacter, int type, PerformActionMsg msg) {
if (playerCharacter == null)
return;
msg.setUnknown05(type);
switch (type) {
case 3:
case 4:
msg.setUnknown04(2);
DispatchMessage.dispatchMsgToInterestArea(playerCharacter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
break;
default:
msg.setUnknown04(1);
Dispatch dispatch = Dispatch.borrow(playerCharacter, msg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
}
}
public static void sendEffectMsg(PlayerCharacter pc, int type, ActionsBase ab, PowersBase pb) {
if (pc == null)
return;
try {
EffectsBase eb = PowersManager.effectsBaseByIDString.get(ab.getEffectID());
if (eb == null)
return;
ApplyEffectMsg aem = new ApplyEffectMsg(pc, pc, 0, eb.getToken(), 9, pb.getToken(), pb.getName());
aem.setUnknown03(type);
DispatchMessage.dispatchMsgToInterestArea(pc, aem, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
} catch (Exception e) {
Logger.error(e.getMessage());
}
}
public static void sendEffectMsg(PlayerCharacter pc, int type, EffectsBase eb) {
if (pc == null)
return;
try {
if (eb == null)
return;
ApplyEffectMsg aem = new ApplyEffectMsg(pc, pc, 0, eb.getToken(), 0, eb.getToken(), "");
aem.setUnknown03(type);
aem.setUnknown05(1);
DispatchMessage.dispatchMsgToInterestArea(pc, aem, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
} catch (Exception e) {
Logger.error(e.getMessage());
}
}
public static void sendMobPowerMsg(Mob mob, int type, PerformActionMsg msg) {
msg.setUnknown05(type);
switch (type) {
case 3:
case 4:
DispatchMessage.sendToAllInRange(mob, msg);
}
}
private static boolean testPassive(AbstractCharacter source,
AbstractCharacter target, String type) {
float chance = target.getPassiveChance(type, source.getLevel(), false);
if (chance == 0f)
return false;
// max 75% chance of passive to fire
if (chance > 75f)
chance = 75f;
int roll = ThreadLocalRandom.current().nextInt(100);
// Passive fired
// TODO send message
// Passive did not fire
return roll < chance;
}
private static boolean validateTarget(AbstractWorldObject target,
PlayerCharacter pc, PowersBase pb) {
//group target. uses pbaoe rules
if (pb.targetGroup())
return true;
// target is player
else if ((target.getObjectTypeMask() & MBServerStatics.MASK_PLAYER) != 0) {
if (pb.targetPlayer())
if (pb.isGroupOnly()) { //single target group only power
PlayerCharacter trg = (PlayerCharacter) target;
if (GroupManager.getGroup(trg) != null && GroupManager.getGroup(pc) != null)
if (GroupManager.getGroup(trg).getObjectUUID() == GroupManager.getGroup(pc).getObjectUUID())
return true; // both in same group, good to go
return trg != null && pc.getObjectUUID() == trg.getObjectUUID();
} else
return true; // can target player, good to go.
else if (target.getObjectUUID() == pc.getObjectUUID() && pb.targetSelf())
return true; // can target self, good to go
else if (pb.targetCorpse()) {
//target is dead player
PlayerCharacter trg = (PlayerCharacter) target;
return !trg.isAlive();
} else {
PlayerCharacter trg = (PlayerCharacter) target;
if (pb.targetGroup())
if (GroupManager.getGroup(trg) != null && GroupManager.getGroup(pc) != null)
if (GroupManager.getGroup(trg).getObjectUUID() == GroupManager.getGroup(pc)
.getObjectUUID())
return true; // both in same group, good to go
if (pb.targetGuildLeader())
if (pc.getGuild() != null)
if (pc.getGuild().getGuildLeaderUUID() == trg.getObjectUUID())
return true; // can hit guild leader, good to go
}
String outmsg = "Invalid Target";
ChatManager.chatSystemInfo(pc, outmsg);
return false; // can't target player, stop here
} // target is mob
else if ((target.getObjectTypeMask() & MBServerStatics.MASK_MOB) != 0)
return pb.targetMob();
// target is pet
else if ((target.getObjectTypeMask() & MBServerStatics.MASK_PET) != 0)
return pb.targetPet();
// target is Building
else if ((target.getObjectTypeMask() & MBServerStatics.MASK_BUILDING) != 0)
return pb.targetBuilding();
else if (target.getObjectType().equals(GameObjectType.Item)) {
Item item = (Item) target;
if (pb.targetItem())
return true;
// TODO add these checks later
else if (pb.targetArmor() && item.getItemBase().getType().equals(ItemType.ARMOR))
return true;
else if (pb.targetJewelry() && item.getItemBase().getType().equals(ItemType.JEWELRY))
return true;
else
return pb.targetWeapon() && item.getItemBase().getType().equals(ItemType.WEAPON);
} // How did we get here? all valid targets have been covered
else
return false;
}
/*
* Cancel spell upon actions
*/
public static void cancelOnAttack(AbstractCharacter ac) {
ac.cancelTimer("Stuck");
}
public static void cancelOnAttackSwing(AbstractCharacter ac) {
}
public static void cancelOnCast(AbstractCharacter ac) {
}
public static void cancelOnSpell(AbstractCharacter ac) {
PowersBase power = getLastPower(ac);
if (power != null && power.cancelOnCastSpell())
cancelPower(ac, false);
ac.cancelLastChant();
}
public static void cancelOnEquipChange(AbstractCharacter ac) {
}
public static void cancelOnLogout(AbstractCharacter ac) {
}
public static void cancelOnMove(AbstractCharacter ac) {
PowersBase power = getLastPower(ac);
if (power != null && !power.canCastWhileMoving())
cancelPower(ac, false);
//cancel items
cancelItems(ac, true, false);
ac.cancelTimer("Stuck");
}
public static void cancelOnNewCharm(AbstractCharacter ac) {
}
public static void cancelOnSit(AbstractCharacter ac) {
cancelPower(ac, false); // Always cancel casts on sit
}
public static void cancelOnTakeDamage(AbstractCharacter ac) {
PowersBase power = getLastPower(ac);
if (power != null && power.cancelOnTakeDamage())
cancelPower(ac, true);
cancelItems(ac, false, true);
ac.cancelTimer("Stuck");
}
public static void cancelOnTerritoryClaim(AbstractCharacter ac) {
}
public static void cancelOnUnEquip(AbstractCharacter ac) {
}
public static void cancelOnStun(AbstractCharacter ac) {
}
private static PowersBase getLastPower(AbstractCharacter ac) {
if (ac == null)
return null;
JobContainer jc = ac.getLastPower();
if (jc == null)
return null;
AbstractJob aj = jc.getJob();
if (aj == null)
return null;
if (aj instanceof UsePowerJob) {
UsePowerJob upj = (UsePowerJob) aj;
return upj.getPowersBase();
}
return null;
}
private static PowersBase getLastItem(AbstractCharacter ac) {
if (ac == null)
return null;
JobContainer jc = ac.getLastItem();
if (jc == null)
return null;
AbstractJob aj = jc.getJob();
if (aj == null)
return null;
if (aj instanceof UseItemJob) {
UseItemJob uij = (UseItemJob) aj;
return uij.getPowersBase();
}
return null;
}
//cancels last casted power
private static void cancelPower(AbstractCharacter ac, boolean cancelCastAnimation) {
if (ac == null)
return;
JobContainer jc = ac.getLastPower();
if (jc == null)
return;
AbstractJob aj = jc.getJob();
if (aj == null)
return;
if (aj instanceof AbstractScheduleJob)
((AbstractScheduleJob) aj).cancelJob();
ac.clearLastPower();
//clear cast animation for everyone in view range
if (aj instanceof UsePowerJob && cancelCastAnimation) {
PerformActionMsg pam = ((UsePowerJob) aj).getMsg();
if (pam != null) {
pam.setNumTrains(9999);
pam.setUnknown04(2);
DispatchMessage.sendToAllInRange(ac, pam);
}
}
}
public static PerformActionMsg createPowerMsg(PowersBase pb, int trains, AbstractCharacter source, AbstractCharacter target) {
return new PerformActionMsg(pb.getToken(), trains, source.getObjectType().ordinal(), source.getObjectUUID(), target.getObjectType().ordinal(), target.getObjectUUID(), target.getLoc().x, target.getLoc().y, target.getLoc().z, 0, 0);
}
//cancels any casts from using an item
private static void cancelItems(AbstractCharacter ac, boolean cancelOnMove, boolean cancelOnTakeDamage) {
JobContainer jc;
AbstractJob aj;
ConcurrentHashMap<String, JobContainer> timers;
UseItemJob uij;
PowersBase pb;
AbstractWorldObject target;
if (ac == null)
return;
timers = ac.getTimers();
if (timers == null)
return;
for (String name : timers.keySet()) {
jc = timers.get(name);
if (jc == null)
continue;
aj = jc.getJob();
if (aj != null && aj instanceof UseItemJob) {
uij = (UseItemJob) aj;
pb = uij.getPowersBase();
if (pb == null)
continue;
if (!pb.canCastWhileMoving() && cancelOnMove) {
uij.cancelJob();
timers.remove(name);
continue;
}
if ((pb.cancelOnTakeDamage() == false) &&
(cancelOnTakeDamage == false))
continue;
uij.cancelJob();
timers.remove(name);
//clear cast animation for everyone in view range
target = uij.getTarget();
if (target != null) {
PerformActionMsg pam = new PerformActionMsg(pb.getToken(), 9999, ac
.getObjectType().ordinal(), ac.getObjectUUID(), target.getObjectType().ordinal(),
target.getObjectUUID(), 0, 0, 0, 2, 0);
DispatchMessage.sendToAllInRange(ac, pam);
}
}
}
}
}