From 2a19a85108cc42939f6143c1a5f3565f6f560763 Mon Sep 17 00:00:00 2001 From: MagicBot Date: Mon, 26 Oct 2026 10:20:34 -0400 Subject: [PATCH] Build power action lists from Powers.cfg ACTION= lines ActionsBase(ActionEntry, powerID, id, powerAction) with the duration ramp from CurveRamp; PowersManager.loadActionLists fills PowersBase.actions in Powers.cfg order (ids 1..2278 + 10000), then the OPENGATE actions (5001-5008, unchanged). StackType gains the 10 cfg stack types it lacked and loses the DB-only ResistanceDebuff spelling. Parity (JSON-Refactor-Plan/scripts/p49.py, dev export, every column): 2287 DB rows vs 2278 cfg lines; 1453/1455 powers have the same action list; all curves have a ramp form, every action id exists. Value changes (cfg wins): - TRA-003..010 lose their DB OPENGATE row; the synthetic OPENGATE action was already added, so each gate power applied OPENGATE twice - HFG-001 loses the DB-only third action HFG-001B (Str +25, attack delay -15, melee damage +15) - SCT-004 applies SCT-004A (Speed SL0125Up, Buff/Effect) instead of RNG-033A (Speed SL0100Up, also Chant) - durations: ALF-001A 180->60, COM-001A 60->30, FGM-002A/003A 1800->900, RNG-033A 60->30 - stack priority: DEF/OFF/PRE/NOSTANCE A and B Always->GreaterThanOrEqualTo, BRD-016A and PRS-031A GreaterThan->GreaterThanOrEqualTo - stack types: the 16 resist debuffs split from ResistanceDeBuff/ ResistanceDebuff into Cold/Fire/Lightning/Holy/Unholy/Magic/Mental DeBuff (FUR-029A IgnoreStack->ElementalDeBuff), so debuffs of different elements now stack; HNT-023A..C StackableAttr*Buff->PersonalAttr*Buff - IgnoreStack effect keys: action line ids follow cfg order (runtime only) Co-Authored-By: Claude Opus 5.5 --- src/engine/gameManager/PowersManager.java | 33 +++++++++++++++-- src/engine/mbEnums.java | 11 +++++- src/engine/powers/ActionsBase.java | 44 +++++++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/src/engine/gameManager/PowersManager.java b/src/engine/gameManager/PowersManager.java index 6ca102f8..99001b87 100644 --- a/src/engine/gameManager/PowersManager.java +++ b/src/engine/gameManager/PowersManager.java @@ -253,6 +253,35 @@ public enum PowersManager { Logger.info("Loaded " + PowersManager.powersBaseByIDString.size() + " powers from Powers.cfg"); } + // ACTION= lines in Powers.cfg order, ids 1..n (offset in ActionsBase), then the OPENGATE actions + private static void loadActionLists() { + + int actionID = 0; + PowersBase powersBase; + AbstractPowerAction powerAction; + + for (PowerTemplate powerTemplate : PowerTemplate._powers.values()) { + + powersBase = PowersManager.powersBaseByIDString.get(powerTemplate.power_id); + + for (PowerTemplate.ActionEntry actionEntry : powerTemplate.actions) { + + powerAction = PowersManager.powerActionsByIDString.get(actionEntry.action_id); + + if (powerAction == null) { + Logger.error("Power " + powerTemplate.power_id + " lists unknown action " + actionEntry.action_id); + continue; + } + + powersBase.getActions().add(new ActionsBase(actionEntry, powerTemplate.power_id, ++actionID, powerAction)); + } + } + + ActionsBase.addOpenGateActions(PowersManager.powersBaseByIDString); + + Logger.info("Loaded " + actionID + " power action lines from Powers.cfg"); + } + // The login server needs only the token map public static void InitializeLoginPowers() { loadPowers(); @@ -277,9 +306,7 @@ public enum PowersManager { loadPowers(); - // Add Actions Base - ActionsBase.getActionsBase(PowersManager.powersBaseByIDString, - PowersManager.powerActionsByIDString); + loadActionLists(); } public static EffectsBase setEffectToken(int ID, int token) { diff --git a/src/engine/mbEnums.java b/src/engine/mbEnums.java index c2438ccd..90918c5d 100644 --- a/src/engine/mbEnums.java +++ b/src/engine/mbEnums.java @@ -1148,6 +1148,7 @@ public class mbEnums { BMHealing, Charm, ClassBoon, + ColdDeBuff, Confusion, DamageAbsorber, DamageDebuff, @@ -1162,6 +1163,7 @@ public class mbEnums { ElementalDeBuff, EnchantWeapon, Fear, + FireDeBuff, Flight, Frenzy, GroupHeal, @@ -1169,12 +1171,16 @@ public class mbEnums { HealOverTime, HealResBuff, HealthPotion, + HolyDeBuff, IgnoreStack, Invisible, + LightningDeBuff, + MagicDeBuff, ManaPotion, MangonelFire, MeleeDamageDeBuff, MeleeDeBuff, + MentalDeBuff, MoveBuff, MoveDebuff, NoFear, @@ -1187,6 +1193,9 @@ public class mbEnums { NoTrack, PassiveDefense, PersAttrSPRBuff, + PersonalAttrCONBuff, + PersonalAttrDEXBuff, + PersonalAttrSTRBuff, PetBuff, PierceResistanceDebuff, PoisonBuchinine, @@ -1209,7 +1218,6 @@ public class mbEnums { RecoveryStaminaDeBuff, ResistanceBuff, ResistanceDeBuff, - ResistanceDebuff, Root, SafeMode, SelfOneAttrBuff, @@ -1233,6 +1241,7 @@ public class mbEnums { Stun, Track, Transform, + UnholyDeBuff, WeaponMove; public static StackType GetStackType(String modName) { diff --git a/src/engine/powers/ActionsBase.java b/src/engine/powers/ActionsBase.java index d0c053d8..1bd805d0 100644 --- a/src/engine/powers/ActionsBase.java +++ b/src/engine/powers/ActionsBase.java @@ -16,6 +16,8 @@ import engine.mbEnums.ModType; import engine.mbEnums.SourceType; import engine.mbEnums.StackType; import engine.objects.*; +import engine.parsers.objects.wpak.CurveRamp; +import engine.parsers.objects.wpak.PowerTemplate; import engine.powers.poweractions.AbstractPowerAction; import org.pmw.tinylog.Logger; @@ -89,6 +91,43 @@ public class ActionsBase { this.powerAction = apa.get(this.effectID); } + public ActionsBase(PowerTemplate.ActionEntry actionEntry, String powerID, int id, AbstractPowerAction powerAction) { + + CurveRamp durationRamp = CurveRamp.fromCurve(actionEntry.duration_curve); + + if (durationRamp == null) { + Logger.warn("Power " + powerID + " action " + actionEntry.action_id + " duration curve " + + actionEntry.duration_curve + " has no ramp form"); + durationRamp = CurveRamp.NONE; + } + + this.UUID = id + ACTION_ID_OFFSET; + this.IDString = powerID; + this.effectID = actionEntry.action_id; + this.minTrains = actionEntry.min_trains; + this.maxTrains = actionEntry.max_trains; + this.duration = actionEntry.duration; + this.ramp = durationRamp.ramp; + this.addFormula = durationRamp.useAdd; + this.stackType = actionEntry.stack_type; + this.stackTypeType = StackType.GetStackType(this.stackType); + this.stackOrder = actionEntry.stack_order; + this.stackPriority = actionEntry.stack_priority.name(); + + switch (actionEntry.stack_priority) { + case GreaterThanOrEqualTo: + this.greaterThanEqual = true; + break; + case Always: + this.always = true; + break; + case GreaterThan: + this.greaterThan = true; + break; + } + this.powerAction = powerAction; + } + protected ActionsBase(int uUID, String effectID, int minTrains, int maxTrains, float duration, float ramp, boolean addFormula, String stackType, int stackOrder, boolean greaterThanEqual, boolean always, boolean greaterThan, AbstractPowerAction powerAction) { @@ -162,6 +201,11 @@ public class ActionsBase { Logger.error(e.toString()); } + addOpenGateActions(powers); + } + + public static void addOpenGateActions(HashMap powers) { + int gateID = 5000; for (String IDString : Runegate.GetAllOpenGateIDStrings()) {