From b944847b8a70d9aa14a7d93f4a9b8cf9aa913e80 Mon Sep 17 00:00:00 2001 From: arnonos Date: Sun, 17 Aug 2025 17:14:17 -0500 Subject: [PATCH] Added fear mechanic. --- .gitignore | 2 + prestonbane.code-workspace | 8 --- src/engine/Enum.java | 1 + .../db/handlers/dbEffectsBaseHandler.java | 3 + src/engine/gameManager/CombatManager.java | 5 ++ src/engine/gameManager/MovementManager.java | 9 +++ src/engine/gameManager/PowersManager.java | 18 ++++++ src/engine/jobs/FearWanderJob.java | 42 ++++++++++++++ .../mobileAI/utilities/CombatUtilities.java | 2 +- .../mobileAI/utilities/MovementUtilities.java | 2 +- src/engine/objects/AbstractCharacter.java | 24 ++++++++ src/engine/objects/Effect.java | 9 +++ src/engine/objects/Mob.java | 2 +- src/engine/objects/PlayerCharacter.java | 14 +++++ src/engine/powers/ActionsBase.java | 2 + .../effectmodifiers/FearEffectModifier.java | 58 +++++++++++++++++++ 16 files changed, 190 insertions(+), 11 deletions(-) delete mode 100644 prestonbane.code-workspace create mode 100644 src/engine/jobs/FearWanderJob.java create mode 100644 src/engine/powers/effectmodifiers/FearEffectModifier.java diff --git a/.gitignore b/.gitignore index 9154f4c7..f1cc71df 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ hs_err_pid* replay_pid* +*.code-workspace + diff --git a/prestonbane.code-workspace b/prestonbane.code-workspace deleted file mode 100644 index 876a1499..00000000 --- a/prestonbane.code-workspace +++ /dev/null @@ -1,8 +0,0 @@ -{ - "folders": [ - { - "path": "." - } - ], - "settings": {} -} \ No newline at end of file diff --git a/src/engine/Enum.java b/src/engine/Enum.java index e98fcabc..d6dcc8a1 100644 --- a/src/engine/Enum.java +++ b/src/engine/Enum.java @@ -1196,6 +1196,7 @@ public class Enum { Durability, ExclusiveDamageCap, Fade, + Fear, Fly, Health, HealthFull, diff --git a/src/engine/db/handlers/dbEffectsBaseHandler.java b/src/engine/db/handlers/dbEffectsBaseHandler.java index 4836ec6e..7d32f0e5 100644 --- a/src/engine/db/handlers/dbEffectsBaseHandler.java +++ b/src/engine/db/handlers/dbEffectsBaseHandler.java @@ -280,6 +280,9 @@ public class dbEffectsBaseHandler extends dbHandlerBase { case Stunned: abstractEffectModifier = new StunnedEffectModifier(rs); break; + case Fear: + abstractEffectModifier = new FearEffectModifier(rs); + break; case Value: abstractEffectModifier = new ValueEffectModifier(rs); if (effectBase != null) { diff --git a/src/engine/gameManager/CombatManager.java b/src/engine/gameManager/CombatManager.java index 0587e384..1282eb04 100644 --- a/src/engine/gameManager/CombatManager.java +++ b/src/engine/gameManager/CombatManager.java @@ -433,6 +433,11 @@ public enum CombatManager { if (bonus != null && bonus.getBool(ModType.Stunned, SourceType.None)) attackFailure = true; + //see if attacker is feared. If so, stop here + + if (bonus != null && bonus.getBool(ModType.Fear, SourceType.None)) + attackFailure = true; + //Get Range of weapon float range; diff --git a/src/engine/gameManager/MovementManager.java b/src/engine/gameManager/MovementManager.java index f38b502e..6ca7fd88 100644 --- a/src/engine/gameManager/MovementManager.java +++ b/src/engine/gameManager/MovementManager.java @@ -79,6 +79,11 @@ public enum MovementManager { return; } + // Will this prevent movement at all or only player input for movement? + if (toMove.getBonuses().getBool(ModType.Fear, SourceType.None)) { + return; + } + if (msg.getEndLat() > MBServerStatics.MAX_WORLD_WIDTH) msg.setEndLat((float) MBServerStatics.MAX_WORLD_WIDTH); @@ -407,6 +412,10 @@ public enum MovementManager { PlayerBonuses bonus = member.getBonuses(); if (bonus.getBool(ModType.Stunned, SourceType.None) || bonus.getBool(ModType.CannotMove, SourceType.None)) continue; + + //don't move if player is feared + if (bonus.getBool(ModType.Fear, SourceType.None)) + continue; member.update(); diff --git a/src/engine/gameManager/PowersManager.java b/src/engine/gameManager/PowersManager.java index 9e15abc2..18a0ab30 100644 --- a/src/engine/gameManager/PowersManager.java +++ b/src/engine/gameManager/PowersManager.java @@ -313,6 +313,9 @@ public enum PowersManager { if (bonus != null && (bonus.getBool(ModType.Stunned, SourceType.None) || bonus.getBool(ModType.CannotCast, SourceType.None) || bonus.getBool(ModType.BlockedPowerType, sourceType))) return true; + if (bonus != null && bonus.getBool(ModType.Fear, SourceType.None)) + return true; + // if moving make sure spell valid for movement Vector3fImmutable endLoc = playerCharacter.getEndLoc(); @@ -595,6 +598,9 @@ public enum PowersManager { 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 (bonus != null && bonus.getBool(ModType.Fear, SourceType.None)) + return true; // if moving make sure spell valid for movement // if flying, make sure spell valid for flying. @@ -761,6 +767,11 @@ public enum PowersManager { finishRecycleTime(msg.getPowerUsedID(), playerCharacter, true); return; } + + if (bonus.getBool(ModType.Fear, SourceType.None)) { + finishRecycleTime(msg.getPowerUsedID(), playerCharacter, true); + return; + } } // get target loc @@ -1042,6 +1053,9 @@ public enum PowersManager { return; } + if (bonus != null && bonus.getBool(ModType.Fear, SourceType.None)) + return; + msg.setNumTrains(9999); msg.setUnknown04(2); DispatchMessage.sendToAllInRange(caster, msg); @@ -2565,6 +2579,10 @@ public enum PowersManager { } + public static void cancelOnFear(AbstractCharacter ac) { + + } + private static PowersBase getLastPower(AbstractCharacter ac) { if (ac == null) return null; diff --git a/src/engine/jobs/FearWanderJob.java b/src/engine/jobs/FearWanderJob.java new file mode 100644 index 00000000..bd4739f6 --- /dev/null +++ b/src/engine/jobs/FearWanderJob.java @@ -0,0 +1,42 @@ +package engine.jobs; + +import engine.job.AbstractJob; +import engine.objects.AbstractCharacter; +import engine.math.Vector3fImmutable; +import engine.server.MBServerStatics; + +import java.util.Random; + +public class FearWanderJob extends AbstractJob { + private final AbstractCharacter target; + private final Random rand = new Random(); + + public FearWanderJob(AbstractCharacter target) { + super(); + this.target = target; + } + + @Override + public void doJob() { + // Only wander if still feared + if (target == null || !target.getBonuses().getBool(engine.Enum.ModType.Fear, engine.Enum.SourceType.None)) + return; + + // Pick a random direction and distance + float angle = rand.nextFloat() * (float)Math.PI * 2f; + float distance = 5f + rand.nextFloat() * 10f; // 5-15 units + + Vector3fImmutable current = target.getLoc(); + float newX = current.x + (float)Math.cos(angle) * distance; + float newZ = current.z + (float)Math.sin(angle) * distance; + + // Clamp to world bounds if needed + newX = (float) Math.max(0, Math.min(newX, MBServerStatics.MAX_WORLD_WIDTH)); + newZ = (float) Math.max(0, Math.min(newZ, MBServerStatics.MAX_WORLD_HEIGHT)); + + Vector3fImmutable dest = new Vector3fImmutable(newX, current.y, newZ); + + // Issue movement (implement setEndLoc or similar in your AbstractCharacter) + target.setEndLoc(dest); + } +} diff --git a/src/engine/mobileAI/utilities/CombatUtilities.java b/src/engine/mobileAI/utilities/CombatUtilities.java index fd52a2e3..95006750 100644 --- a/src/engine/mobileAI/utilities/CombatUtilities.java +++ b/src/engine/mobileAI/utilities/CombatUtilities.java @@ -124,7 +124,7 @@ public class CombatUtilities { } public static boolean canSwing(Mob agent) { - return (agent.isAlive() && !agent.getBonuses().getBool(ModType.Stunned, SourceType.None)); + return (agent.isAlive() && !agent.getBonuses().getBool(ModType.Stunned, SourceType.None) && !agent.getBonuses().getBool(ModType.Fear, SourceType.None)); } public static void swingIsMiss(Mob agent, AbstractWorldObject target, int animation) { diff --git a/src/engine/mobileAI/utilities/MovementUtilities.java b/src/engine/mobileAI/utilities/MovementUtilities.java index 85affdca..afe1dd8b 100644 --- a/src/engine/mobileAI/utilities/MovementUtilities.java +++ b/src/engine/mobileAI/utilities/MovementUtilities.java @@ -169,7 +169,7 @@ public class MovementUtilities { if (agent.getMobBase() != null && Enum.MobFlagType.SENTINEL.elementOf(agent.getMobBase().getFlags())) return false; - return (agent.isAlive() && !agent.getBonuses().getBool(ModType.Stunned, SourceType.None) && !agent.getBonuses().getBool(ModType.CannotMove, SourceType.None)); + return (agent.isAlive() && !agent.getBonuses().getBool(ModType.Stunned, SourceType.None) && !agent.getBonuses().getBool(ModType.Fear, SourceType.None) && !agent.getBonuses().getBool(ModType.CannotMove, SourceType.None)); } public static Vector3fImmutable randomPatrolLocation(Mob agent, Vector3fImmutable center, float radius) { diff --git a/src/engine/objects/AbstractCharacter.java b/src/engine/objects/AbstractCharacter.java index e43da320..d3dd1b80 100644 --- a/src/engine/objects/AbstractCharacter.java +++ b/src/engine/objects/AbstractCharacter.java @@ -1653,6 +1653,30 @@ public abstract class AbstractCharacter extends AbstractWorldObject { PowersManager.cancelOnStun(this); } + public final void cancelOnFear() { + boolean changed = false; + for (String s : this.effects.keySet()) { + Effect eff = this.effects.get(s); + if (eff == null) + continue; + if (eff.cancelOnFear() && eff.cancel()) { + eff.cancelJob(); + this.effects.remove(s); + changed = true; + } + } + + JobContainer wanderJob = this.getTimers().get("FearWander"); + if (wanderJob != null) { + wanderJob.cancelJob(); + this.getTimers().remove("FearWander"); + } + if (changed) { + applyBonuses(); + } + PowersManager.cancelOnFear(this); + } + //Call to apply any new effects to player public synchronized void applyBonuses() { PlayerCharacter player; diff --git a/src/engine/objects/Effect.java b/src/engine/objects/Effect.java index 60e991c8..df3f410e 100644 --- a/src/engine/objects/Effect.java +++ b/src/engine/objects/Effect.java @@ -48,6 +48,7 @@ public class Effect { private boolean cancelOnTerritoryClaim; private boolean cancelOnUnEquip; private boolean cancelOnStun; + private boolean cancelOnFear; private boolean bakedInStat = false; private boolean isStatic = false; private int effectSourceType = 0; @@ -80,6 +81,7 @@ public class Effect { this.cancelOnTerritoryClaim = false; this.cancelOnUnEquip = false; this.cancelOnStun = false; + this.cancelOnFear = false; this.eb = eb; this.trains = trains; } @@ -99,6 +101,7 @@ public class Effect { this.cancelOnTerritoryClaim = false; this.cancelOnUnEquip = false; this.cancelOnStun = false; + this.cancelOnFear = false; this.eb = eb; this.trains = trains; this.isStatic = isStatic; @@ -581,6 +584,12 @@ public class Effect { return this.cancelOnStun; } + public boolean cancelOnFear() { + if (this.eb == null) + return true; + return this.cancelOnFear; + } + public boolean cancelOnTakeDamage() { if (this.eb == null) return true; diff --git a/src/engine/objects/Mob.java b/src/engine/objects/Mob.java index 70602b02..46e00076 100644 --- a/src/engine/objects/Mob.java +++ b/src/engine/objects/Mob.java @@ -1249,7 +1249,7 @@ public class Mob extends AbstractIntelligenceAgent { if (!this.isMoving()) return; - if (this.isAlive() == false || this.getBonuses().getBool(ModType.Stunned, SourceType.None) || this.getBonuses().getBool(ModType.CannotMove, SourceType.None)) { + if (this.isAlive() == false || this.getBonuses().getBool(ModType.Stunned, SourceType.None) || this.getBonuses().getBool(ModType.Fear, SourceType.None) || this.getBonuses().getBool(ModType.CannotMove, SourceType.None)) { //Target is stunned or rooted. Don't move this.stopMovement(this.getMovementLoc()); diff --git a/src/engine/objects/PlayerCharacter.java b/src/engine/objects/PlayerCharacter.java index 35dd8c5a..06c2a3ac 100644 --- a/src/engine/objects/PlayerCharacter.java +++ b/src/engine/objects/PlayerCharacter.java @@ -4407,6 +4407,10 @@ public class PlayerCharacter extends AbstractCharacter { if (this.bonuses.getBool(ModType.Stunned, SourceType.None)) return 0f; + if (this.bonuses.getBool( + ModType.Fear, SourceType.None)) + return 0f; + // Get base skill amount CharacterSkill sk = this.skills.get(type); float amount; @@ -4436,6 +4440,10 @@ public class PlayerCharacter extends AbstractCharacter { // must not be stunned if (this.bonuses.getBool(ModType.Stunned, SourceType.None)) return 0f; + + if (this.bonuses.getBool(ModType.Fear, SourceType.None)) + return 0f; + // Get base skill amount CharacterSkill sk = this.skills.get(sourceType.name()); @@ -4897,6 +4905,12 @@ public class PlayerCharacter extends AbstractCharacter { this.region = AbstractWorldObject.GetRegionByWorldObject(this); return; } + if (this.isAlive() == false || this.getBonuses().getBool(ModType.Fear, SourceType.None)) { + //Target is feared. Don't move + this.stopMovement(newLoc); + this.region = AbstractWorldObject.GetRegionByWorldObject(this); + return; + } if (newLoc.equals(this.getEndLoc())) { this.stopMovement(newLoc); this.region = AbstractWorldObject.GetRegionByWorldObject(this); diff --git a/src/engine/powers/ActionsBase.java b/src/engine/powers/ActionsBase.java index 5264d18b..045228f6 100644 --- a/src/engine/powers/ActionsBase.java +++ b/src/engine/powers/ActionsBase.java @@ -247,6 +247,8 @@ public class ActionsBase { //TODO make this more efficient then testing strings if (this.stackType.equals("Stun") && bonus.getBool(ModType.ImmuneTo, SourceType.Stun)) return true; //Currently stun immune. Skip stun + else if (this.stackType.equals("Fear") && bonus.getBool(ModType.ImmuneTo, SourceType.Fear)) + return true; //Currently fear immune. Skip fear else if (this.stackType.equals("Snare") && bonus.getBool(ModType.ImmuneTo, SourceType.Snare)) return true; //Currently snare immune. Skip snare else if (this.stackType.equals("Blindness") && bonus.getBool(ModType.ImmuneTo, SourceType.Blind)) diff --git a/src/engine/powers/effectmodifiers/FearEffectModifier.java b/src/engine/powers/effectmodifiers/FearEffectModifier.java new file mode 100644 index 00000000..e0fb481e --- /dev/null +++ b/src/engine/powers/effectmodifiers/FearEffectModifier.java @@ -0,0 +1,58 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum.GameObjectType; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.jobs.FearWanderJob; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class FearEffectModifier extends AbstractEffectModifier { + + public FearEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + // Optional: custom logic when fear is applied + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + + bonus.setBool(this.modType, this.sourceType, true); + ac.cancelOnFear(); + ac.setIsCasting(false); + ac.stopMovement(ac.getLoc()); + + // Schedule the wander job if not already scheduled + if (ac.getTimers().get("FearWander") == null) { + JobContainer wanderJob = JobScheduler.getInstance().scheduleJob( + new FearWanderJob(ac), 2000 + ); + + ac.getTimers().put("FearWander", wanderJob); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +}