Files
lakebane/src/engine/gameManager/PowersManager.java
T
MagicBotandClaude Opus 5.5 3fc5e7253a Build power actions from PowerActions.cfg
PowersManager.loadPowerActions builds every AbstractPowerAction from
PowerActionTemplate (template ctor on all subclasses), in id order with IDs
1..3046; OPENGATE keeps 5000. The ResultSet loader stays until removal.
The parser now reads the SimpleDamage (min, max), Teleport (bool), Track
(two bools) and TransferStat (bool at token 8) header fields.

Parity (JSON-Refactor-Plan/scripts/p47.py, every column): ids 3046/3046 plus
the DB-only AELFBORN row, which nothing references (the cfg has
BOON-AELFBORN). Effect ids, deferred powers, RemoveEffect source/REMOVEALL,
track filter/max, transfer types/ramps/ramp-add, mob level identical. The old
flags match the new header fields: Track 1024/2048 = tokens 3/4,
TransferStat 16384 = token 8, Teleport 32768 = SAB-001A's TRUE.

Value changes (PowerActions.cfg wins):
- DamageOverTime/TransferStatOT: the cfg value is the tick count; tick length
  = action duration / ticks. The DB stored cfg+1 as tick seconds, which only
  worked for 30s DoTs (5 x 6s, unchanged). HOLY-DOT-17 (60s) goes 10 -> 5
  ticks, BLEED-DOT (4s) 0 -> 4, TransferStatOT 30s 6 -> 5 ticks.
- TransferStat amounts are read with ToInt as the client does: 16 actions
  drop by 0.5 (BEL-001A 31.5 -> 31, CON-001A 13.5 -> 13, ...).
- PRE-334..337 become ApplyEffects with one effect; ApplyEffectsPowerAction
  now allows a missing second effect on baked-in items.
- Charm level cap ramp from the LEVELCAP= curve (same values as the DB).
- Charm actions with effect FEAR/CONFUSION (12, DB type Confusion) stay
  ConfusionPowerAction no-ops; CHARM ones use CharmPowerAction (decided).
- CreateMob: the cfg client mob id (49..74, 3500, 3501) maps to the mobbase
  through a map in CreateMobPowerAction, from the old DB values.
- The 50 Charm/Block/SpireDisable rows with no DB effect id get the cfg id
  (unread by those classes); isAggressive differs on 330 rows (unread).
- SimpleDamage keeps min/max instead of the always-0 simpleDamage (unread).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:37:36 -04:00

2674 lines
103 KiB
Java

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.gameManager;
import engine.InterestManagement.Terrain;
import engine.InterestManagement.WorldGrid;
import engine.db.handlers.dbSkillReqHandler;
import engine.job.AbstractJob;
import engine.job.AbstractScheduleJob;
import engine.job.JobContainer;
import engine.job.JobScheduler;
import engine.jobs.AbstractEffectJob;
import engine.jobs.FinishRecycleTimeJob;
import engine.jobs.UseItemJob;
import engine.jobs.UsePowerJob;
import engine.math.Vector3fImmutable;
import engine.mbEnums.*;
import engine.net.ByteBufferWriter;
import engine.net.Dispatch;
import engine.net.client.ClientConnection;
import engine.net.client.msg.*;
import engine.objects.*;
import engine.parsers.objects.wpak.EffectTemplate;
import engine.parsers.objects.wpak.PowActionCostTemplate;
import engine.parsers.objects.wpak.PowerActionTemplate;
import engine.parsers.objects.wpak.PowerTemplate;
import engine.powers.*;
import engine.powers.poweractions.*;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
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<Integer, ArrayList<RunePowerEntry>> _allRunePowers;
public static HashMap<Integer, ArrayList<RuneSkillAdjustEntry>> _allRuneSkillAdjusts;
public static HashMap<String, HashMap<ResourceType, Integer>> _effect_costMaps = new HashMap<>();
// Succor, Phaedra's Fury and Fleet of Foot keep their DB target type until the Phase 4 power task decides them
private static final Set<String> DB_TARGET_TYPE_POWERS = new HashSet<>(Arrays.asList("HLR-025", "HNT-023", "RNG-033"));
private static JobScheduler js;
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);
}
public static void loadEffectCostMaps() {
for (PowActionCostTemplate costTemplate : PowActionCostTemplate._costs.values())
PowersManager._effect_costMaps.put(costTemplate.id, new HashMap<>(costTemplate.costs));
Logger.info("Loaded " + PowersManager._effect_costMaps.size() + " effect cost maps from PowActionCostInfo.cfg");
}
// Effects.cfg in file order, then the ApplyEffect/ApplyEffects/Invis actions with no Effects.cfg block
private static void loadEffects() {
int effectID = 0;
int actionKeyCount = 0;
PowerActionTemplate powerActionTemplate;
for (EffectTemplate effectTemplate : EffectTemplate._effects.values())
addEffect(new EffectsBase(effectTemplate, ++effectID));
for (String actionID : new TreeSet<>(PowerActionTemplate._powerActions.keySet())) {
if (EffectTemplate._effects.containsKey(actionID))
continue;
powerActionTemplate = PowerActionTemplate._powerActions.get(actionID);
switch (powerActionTemplate.action_type) {
case ApplyEffect:
case ApplyEffects:
case Invis:
addEffect(new EffectsBase(powerActionTemplate, ++effectID));
actionKeyCount++;
break;
}
}
Logger.info("Loaded " + EffectTemplate._effects.size() + " effects from Effects.cfg and "
+ actionKeyCount + " effect keys from PowerActions.cfg");
}
// PowerActions.cfg in id order; ids 1..n, OPENGATE keeps 5000
private static void loadPowerActions() {
int powerActionID = 0;
PowerActionTemplate powerActionTemplate;
AbstractPowerAction powerAction;
AbstractPowerAction openGateAction;
for (String actionID : new TreeSet<>(PowerActionTemplate._powerActions.keySet())) {
powerActionTemplate = PowerActionTemplate._powerActions.get(actionID);
powerAction = buildPowerAction(powerActionTemplate, ++powerActionID);
if (powerAction == null) {
Logger.error("Power action " + actionID + " has unsupported type " + powerActionTemplate.action_type);
continue;
}
PowersManager.ActionTokenByIDString.put(actionID, DbManager.hasher.SBStringHash(actionID));
PowersManager.powerActionsByIDString.put(actionID, powerAction);
PowersManager.powerActionsByID.put(powerAction.getUUID(), powerAction);
}
openGateAction = new OpenGatePowerAction(5000, "OPENGATE", "OpenGate", false);
PowersManager.powerActionsByIDString.put("OPENGATE", openGateAction);
PowersManager.powerActionsByID.put(openGateAction.getUUID(), openGateAction);
Logger.info("Loaded " + PowersManager.powerActionsByIDString.size() + " power actions from PowerActions.cfg (incl. OPENGATE)");
}
private static AbstractPowerAction buildPowerAction(PowerActionTemplate powerActionTemplate, int id) {
HashMap<String, EffectsBase> effects = PowersManager.effectsBaseByIDString;
switch (powerActionTemplate.action_type) {
case ApplyEffect:
return new ApplyEffectPowerAction(powerActionTemplate, id, effects);
case ApplyEffects:
return new ApplyEffectsPowerAction(powerActionTemplate, id, effects);
case Block:
return new BlockPowerAction(powerActionTemplate, id);
case Charm:
// FEAR and CONFUSION Charm actions stay no-ops, as when the DB typed them Confusion
if (powerActionTemplate.effect_ids.get(0).equals("CHARM"))
return new CharmPowerAction(powerActionTemplate, id);
return new ConfusionPowerAction(powerActionTemplate, id);
case ClaimMine:
return new ClaimMinePowerAction(powerActionTemplate, id);
case ClearAggro:
return new ClearAggroPowerAction(powerActionTemplate, id);
case ClearNearbyAggro:
return new ClearNearbyAggroPowerAction(powerActionTemplate, id);
case Confusion:
return new ConfusionPowerAction(powerActionTemplate, id);
case CreateMob:
return new CreateMobPowerAction(powerActionTemplate, id);
case DamageOverTime:
return new DamageOverTimePowerAction(powerActionTemplate, id, effects);
case DeferredPower:
return new DeferredPowerPowerAction(powerActionTemplate, id, effects);
case DirectDamage:
return new DirectDamagePowerAction(powerActionTemplate, id, effects);
case Invis:
return new InvisPowerAction(powerActionTemplate, id, effects);
case MobRecall:
return new MobRecallPowerAction(powerActionTemplate, id);
case Peek:
return new PeekPowerAction(powerActionTemplate, id);
case Recall:
return new RecallPowerAction(powerActionTemplate, id);
case RemoveEffect:
return new RemoveEffectPowerAction(powerActionTemplate, id);
case Resurrect:
return new ResurrectPowerAction(powerActionTemplate, id);
case RunegateTeleport:
return new RunegateTeleportPowerAction(powerActionTemplate, id);
case SetItemFlag:
return new SetItemFlagPowerAction(powerActionTemplate, id);
case SimpleDamage:
return new SimpleDamagePowerAction(powerActionTemplate, id);
case SpireDisable:
return new SpireDisablePowerAction(powerActionTemplate, id);
case Steal:
return new StealPowerAction(powerActionTemplate, id);
case Summon:
return new SummonPowerAction(powerActionTemplate, id);
case Teleport:
return new TeleportPowerAction(powerActionTemplate, id);
case Track:
return new TrackPowerAction(powerActionTemplate, id, effects);
case TransferStat:
return new TransferStatPowerAction(powerActionTemplate, id, effects);
case TransferStatOT:
return new TransferStatOTPowerAction(powerActionTemplate, id, effects);
case Transform:
return new TransformPowerAction(powerActionTemplate, id, effects);
case TreeChoke:
return new TreeChokePowerAction(powerActionTemplate, id);
default:
return null;
}
}
private static void addEffect(EffectsBase effectsBase) {
PowersManager.effectsBaseByToken.put(effectsBase.getToken(), effectsBase);
PowersManager.effectsBaseByIDString.put(effectsBase.getIDString(), effectsBase);
}
// Powers.cfg target type wins over static_power_powerbase
private static void applyClientTargetTypes() {
PowerTemplate powerTemplate;
int applied = 0;
for (PowersBase powersBase : PowersManager.powersBaseByIDString.values()) {
if (DB_TARGET_TYPE_POWERS.contains(powersBase.getIDString()))
continue;
powerTemplate = PowerTemplate._powers.get(powersBase.getIDString());
if (powerTemplate == null || powerTemplate.target_type == null) {
Logger.warn("Power " + powersBase.getIDString() + " has no Powers.cfg target type; keeping the DB value");
continue;
}
powersBase.setTargetType(powerTemplate.target_type.name());
applied++;
}
Logger.info("Applied Powers.cfg target types to " + applied + " powers");
}
// 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);
}
}
public static ArrayList<RunePowerEntry> getPowersForRune(int rune_id) {
ArrayList<RunePowerEntry> powerEntries = PowersManager._allRunePowers.get(rune_id);
if (powerEntries == null)
powerEntries = new ArrayList<>();
return powerEntries;
}
// This pre-loads all powers and effects
public static void InitializePowers() {
loadEffects();
loadPowerActions();
// 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);
applyClientTargetTypes();
}
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 (ConfigManager.MB_RULESET.getValue().equals("LORE")) {
PowersBase pb = PowersManager.powersBaseByToken.get(msg.getPowerUsedID());
PlayerCharacter caster = origin.getPlayerCharacter();
PlayerCharacter target = PlayerCharacter.getFromCache(msg.getTargetID());
if (pb != null && pb.enforceLore()) {
//if (caster.guild.equals(Guild.getErrantGuild()))
// return;
if (target != null && caster.guild.getGuildType().equals(target.guild.getGuildType()) == false && target.getObjectType().equals(GameObjectType.Building) == false) {
RecyclePowerMsg recyclePowerMsg = new RecyclePowerMsg(msg.getPowerUsedID());
Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), recyclePowerMsg);
DispatchManager.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);
}
return;
}
}
}
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);
DispatchManager.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);
DispatchManager.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()) {
EquipSlotType slot = pp.mainHand() ? EquipSlotType.RHELD : EquipSlotType.LHELD;
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);
DispatchManager.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);
DispatchManager.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();
DispatchManager.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);
DispatchManager.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());
// 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);
DispatchManager.sendToAllInRange(caster, msg);
DispatchManager.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);
DispatchManager.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();
// 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;
}
//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);
}
// update target of used power timer
if (pb.isHarmful())
if (target.getObjectType().equals(GameObjectType.PlayerCharacter) && target.getObjectUUID() != playerCharacter.getObjectUUID()) {
((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.vampDrain)) {
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);
DispatchManager.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);
DispatchManager.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);
DispatchManager.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).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.vampDrain))
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);
DispatchManager.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.race != null)
raceID = pc.race.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;
}
// MONSTERTYPERESTRICTS: mobs use their race rune's monster type, players their RaceType's
private static boolean isRestrictedMonsterType(AbstractWorldObject target, PowersBase pb) {
MonsterType monsterType = null;
PlayerCharacter player;
if (target == null || pb.monsterTypeRestrictions.isEmpty())
return false;
if (target.getObjectType() == GameObjectType.Mob && ((Mob) target).getMobBase() != null)
monsterType = ((Mob) target).getMobBase().monsterType;
else if (target.getObjectType() == GameObjectType.PlayerCharacter) {
player = (PlayerCharacter) target;
if (player.race != null && player.race.getRaceType() != null)
monsterType = player.race.getRaceType().getMonsterType();
}
return monsterType != null && pb.monsterTypeRestrictions.contains(monsterType);
}
public static void sendRecyclePower(int token, ClientConnection origin) {
RecyclePowerMsg recyclePowerMsg = new RecyclePowerMsg(token);
Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), recyclePowerMsg);
DispatchManager.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.charItemManager;
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);
DispatchManager.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.vampDrain))
// 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);
DispatchManager.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(Terrain.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
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 (isRestrictedMonsterType(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(Terrain.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
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 (isRestrictedMonsterType(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);
DispatchManager.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
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);
DispatchManager.dispatchMsgToInterestArea(playerCharacter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
break;
default:
msg.setUnknown04(1);
Dispatch dispatch = Dispatch.borrow(playerCharacter, msg);
DispatchManager.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);
DispatchManager.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);
DispatchManager.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:
DispatchManager.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 pet; pets also carry MASK_MOB, so test them first. A pet is still a Mobile (client 0x49E440)
else if ((target.getObjectTypeMask() & MBServerStatics.MASK_PET) != 0)
return pb.targetPet() || pb.targetMob();
// target is mob
else if ((target.getObjectTypeMask() & MBServerStatics.MASK_MOB) != 0)
return pb.targetMob();
// 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.template.getItemType().equals(ItemType.ARMOR))
return true;
else if (pb.targetJewelry() && item.template.getItemType().equals(ItemType.JEWELRY))
return true;
else
return pb.targetWeapon() && item.template.getItemType().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);
DispatchManager.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);
DispatchManager.sendToAllInRange(ac, pam);
}
}
}
}
}