From 27c59749fa3e03f71c208b395ce9e6f79903d0b5 Mon Sep 17 00:00:00 2001 From: MagicBot Date: Mon, 26 Oct 2026 04:37:58 -0400 Subject: [PATCH] Build EffectsBase from Effects.cfg Effects, their fail conditions and source types come from EffectTemplate. IDs are assigned at boot in file order. The power actions that the server also keys as effects (item enchantment parents and the action tokens sent in effect messages) are built from the ApplyEffect/ApplyEffects/Invis actions with no Effects.cfg block: 776 keys, the 712 old registry rows plus 64 such as BRD-014A, which live also sent. Value changes (cfg wins): BRD-016A/PRS-031A lose the Chant source, RUC-002A..004A lose Buff/Effect, and PRS-031A/RNG-037A no longer break on Attack. Effect modifiers still load from the DB. Co-Authored-By: Claude Opus 5.5 --- src/engine/gameManager/PowersManager.java | 55 ++++++--- src/engine/mbEnums.java | 19 +++- src/engine/parsers/wpak/EffectParser.java | 3 +- src/engine/powers/EffectsBase.java | 129 ++++++++++++++++++++++ 4 files changed, 189 insertions(+), 17 deletions(-) diff --git a/src/engine/gameManager/PowersManager.java b/src/engine/gameManager/PowersManager.java index 6e833886..91c0bd2a 100644 --- a/src/engine/gameManager/PowersManager.java +++ b/src/engine/gameManager/PowersManager.java @@ -11,7 +11,6 @@ package engine.gameManager; import engine.InterestManagement.Terrain; 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; @@ -28,7 +27,9 @@ 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.AbstractPowerAction; @@ -41,6 +42,7 @@ 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; @@ -103,6 +105,42 @@ public enum PowersManager { 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"); + } + + 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() { @@ -153,24 +191,11 @@ public enum PowersManager { // 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); + loadEffects(); // Add Modifiers to Effects dbEffectsBaseHandler.cacheAllEffectModifiers(); - // Add Source Types to Effects - dbPowerHandler.addAllSourceTypes(); - // Add PowerActions AbstractPowerAction.getAllPowerActions(PowersManager.powerActionsByIDString, PowersManager.powerActionsByID, PowersManager.effectsBaseByIDString); diff --git a/src/engine/mbEnums.java b/src/engine/mbEnums.java index e51f78e0..c2438ccd 100644 --- a/src/engine/mbEnums.java +++ b/src/engine/mbEnums.java @@ -1097,11 +1097,28 @@ public class mbEnums { public static EffectSourceType GetEffectSourceType(String modName) { EffectSourceType returnMod; + String name; + if (modName.isEmpty()) return EffectSourceType.None; + name = modName.replace(",", "").replace("-", "").trim(); + + // Effects.cfg SOURCE blocks use the short damage names + switch (name) { + case "Crush": + name = "Crushing"; + break; + case "Pierce": + name = "Piercing"; + break; + case "Slash": + name = "Slashing"; + break; + } + try { - returnMod = EffectSourceType.valueOf(modName.replace(",", "")); + returnMod = EffectSourceType.valueOf(name); } catch (Exception e) { Logger.error(e); return EffectSourceType.None; diff --git a/src/engine/parsers/wpak/EffectParser.java b/src/engine/parsers/wpak/EffectParser.java index 426ec872..c7ac4bcb 100644 --- a/src/engine/parsers/wpak/EffectParser.java +++ b/src/engine/parsers/wpak/EffectParser.java @@ -15,6 +15,7 @@ import org.pmw.tinylog.Logger; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; // Loads Effects.cfg. ModTypes.cfg and CompoundCurves.cfg must be loaded first: // a mod's argument layout comes from its behavior, and curve names are checked. @@ -25,7 +26,7 @@ public class EffectParser { public static HashMap parseEffectsFile() { - HashMap effects = new HashMap<>(); + HashMap effects = new LinkedHashMap<>(); WpakBlock root; EffectTemplate effect; int blockCount = 0; diff --git a/src/engine/powers/EffectsBase.java b/src/engine/powers/EffectsBase.java index 5861718d..af5dd9cf 100644 --- a/src/engine/powers/EffectsBase.java +++ b/src/engine/powers/EffectsBase.java @@ -28,8 +28,12 @@ import engine.objects.AbstractCharacter; import engine.objects.AbstractWorldObject; import engine.objects.Effect; import engine.objects.PlayerCharacter; +import engine.parsers.objects.wpak.CurveRamp; +import engine.parsers.objects.wpak.EffectTemplate; +import engine.parsers.objects.wpak.PowerActionTemplate; import engine.powers.effectmodifiers.AbstractEffectModifier; import engine.server.MBServerStatics; +import engine.util.Hasher; import org.pmw.tinylog.Logger; import java.sql.Connection; @@ -123,6 +127,131 @@ public class EffectsBase { // getFailConditions(); } + public EffectsBase(EffectTemplate effectTemplate, int id) { + + this.UUID = id; + this.IDString = effectTemplate.effect_id; + this.name = effectTemplate.effect_name; + this.token = effectToken(this.IDString); + this.isItemEffect = effectTemplate.isitemeffect; + this.isSpireEffect = effectTemplate.isspireeffect; + this.ignoreMod = effectTemplate.ignorenomod; + this.dontSave = effectTemplate.dontsave; + this.isPrefix = this.IDString.startsWith("PRE-"); + this.isSuffix = this.IDString.startsWith("SUF-"); + + loadFailConditions(effectTemplate.conditions); + loadSourceTypes(effectTemplate.sources); + } + + // A power action with no Effects.cfg block that the server still keys as an effect: + // item enchantment parents (PRE-/SUF-/ARM-) and the action tokens sent in effect messages. + public EffectsBase(PowerActionTemplate powerActionTemplate, int id) { + + this.UUID = id; + this.IDString = powerActionTemplate.action_id; + this.token = effectToken(this.IDString); + this.isPrefix = this.IDString.startsWith("PRE-"); + this.isSuffix = this.IDString.startsWith("SUF-"); + } + + // INVIS-D and SafeMode show as the SafeModeB/A action tokens on the client + private static int effectToken(String IDString) { + + switch (IDString) { + case "INVIS-D": + return Hasher.SBStringHash("SafeModeB"); + case "SafeMode": + return Hasher.SBStringHash("SafeModeA"); + default: + return Hasher.SBStringHash(IDString); + } + } + + private void loadFailConditions(ArrayList conditions) { + + CurveRamp curveRamp; + + if (conditions.isEmpty()) + return; + + if (!EffectsBase.EffectFailConditions.containsKey(this.IDString)) + EffectsBase.EffectFailConditions.put(this.IDString, new HashSet<>()); + + for (EffectTemplate.ConditionEntry condition : conditions) { + + EffectsBase.EffectFailConditions.get(this.IDString).add(condition.condition); + + switch (condition.condition) { + case TakeDamage: + this.cancelOnTakeDamage = true; + this.amount = condition.amount; + + curveRamp = (condition.curve == null) ? null : CurveRamp.fromCurve(condition.curve); + + if (condition.curve != null && curveRamp == null) + Logger.warn("Effect " + this.IDString + " TakeDamage curve " + condition.curve + " has no ramp form"); + + this.amountRamp = (curveRamp == null) ? 0 : curveRamp.ramp; + this.useRampAdd = (curveRamp == null) || curveRamp.useAdd; + + if (condition.damage_types.isEmpty()) + break; + + if (!EffectsBase.EffectDamageTypes.containsKey(this.token)) + EffectsBase.EffectDamageTypes.put(this.token, new HashSet<>()); + + EffectsBase.EffectDamageTypes.get(this.token).addAll(condition.damage_types); + break; + case Attack: + this.cancelOnAttack = true; + break; + case AttackSwing: + this.cancelOnAttackSwing = true; + break; + case Cast: + this.cancelOnCast = true; + break; + case CastSpell: + this.cancelOnCastSpell = true; + break; + case EquipChange: + this.cancelOnEquipChange = true; + break; + case Logout: + this.cancelOnLogout = true; + break; + case Move: + this.cancelOnMove = true; + break; + case NewCharm: + this.cancelOnNewCharm = true; + break; + case Sit: + this.cancelOnSit = true; + break; + case TerritoryClaim: + this.cancelOnTerritoryClaim = true; + break; + case UnEquip: + this.cancelOnUnEquip = true; + break; + } + } + } + + private void loadSourceTypes(ArrayList sources) { + + if (sources.isEmpty()) + return; + + if (!EffectsBase.effectSourceTypeMap.containsKey(this.token)) + EffectsBase.effectSourceTypeMap.put(this.token, new HashSet<>()); + + for (String source : sources) + EffectsBase.effectSourceTypeMap.get(this.token).add(EffectSourceType.GetEffectSourceType(source)); + } + public static void getFailConditions(HashMap effects) { try (Connection connection = DbManager.getConnection();