// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // 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 powersBaseByIDString = new HashMap<>(); public static HashMap powersBaseByToken = new HashMap<>(); public static HashMap effectsBaseByIDString = new HashMap<>(); public static HashMap effectsBaseByToken = new HashMap<>(); public static HashMap powerActionsByIDString = new HashMap<>(); public static HashMap powerActionsByID = new HashMap<>(); public static HashMap ActionTokenByIDString = new HashMap<>(); public static HashMap AnimationOverrides = new HashMap<>(); public static HashMap> 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 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 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 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()); // 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(); // 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 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); } //Power is aiding a target, handle aggro if combat target is a Mob. if (!pb.isHarmful() && target.getObjectType() == GameObjectType.PlayerCharacter) { PlayerCharacter pcTarget = (PlayerCharacter) target; } // 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, 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 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, 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 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 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 allTargets; allTargets = WorldGrid.getObjectsInRangeContains(playerCharacter.getLoc(), pb.getRange(), mask); //remove anyone who can't be tracked Iterator 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 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 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 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 getAllTargets( AbstractWorldObject target, Vector3fImmutable tl, PlayerCharacter pc, PowersBase pb) { HashSet 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 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 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 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 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 getAllTargets( AbstractWorldObject target, Vector3fImmutable tl, AbstractCharacter caster, PowersBase pb) { HashSet 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 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 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 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 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 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); } } } } }