From dff53d5c76473a57bf3676d84c22245c73fb8d74 Mon Sep 17 00:00:00 2001 From: MagicBot Date: Sat, 24 Oct 2026 12:34:02 -0400 Subject: [PATCH] Move guard captain powers from static_rune_powers to MinionType The 14 Magister Captain contracts' powers have no client rune or deed (Task 3.9), so they live on the MinionType mage entries instead of the DB fallback in loadAllRunePowers. canCast, mobCast and aiInfoCmd read them through MinionType.getCaptainPowers. No value change: same 56 power ids, rank 40, and all 56 DB tokens equal the powerbase token of their power id (checked on mbdb_108). Co-Authored-By: Claude Opus 5.5 --- src/engine/devcmd/cmds/aiInfoCmd.java | 7 ++-- src/engine/mbEnums.java | 48 ++++++++++++++++-------- src/engine/mobileAI/MobAI.java | 9 ++--- src/engine/powers/RunePowerEntry.java | 24 +++++------- src/engine/server/world/WorldServer.java | 2 +- 5 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/engine/devcmd/cmds/aiInfoCmd.java b/src/engine/devcmd/cmds/aiInfoCmd.java index f148f56c..ea04332b 100644 --- a/src/engine/devcmd/cmds/aiInfoCmd.java +++ b/src/engine/devcmd/cmds/aiInfoCmd.java @@ -118,18 +118,17 @@ public class aiInfoCmd extends AbstractDevCmd { ArrayList powerEntries = new ArrayList<>(PowersManager.getPowersForRune(mob.getMobBaseID())); - // Additional powers may come from the contract ID. This is to support - // powers for player guards irrespective of the mobbase used. + // Player guards also cast their captain contract's powers, irrespective of the mobbase used. if (mob.isPlayerGuard()) { ArrayList contractEntries = new ArrayList<>(); if (mob.contract != null) - contractEntries = new ArrayList<>(PowersManager.getPowersForRune(mob.contractUUID)); + contractEntries = new ArrayList<>(mbEnums.MinionType.getCaptainPowers(mob.contractUUID)); if (mob.agentType.equals(mbEnums.AIAgentType.GUARDMINION)) - contractEntries = new ArrayList<>(PowersManager.getPowersForRune(mob.guardCaptain.contractUUID)); + contractEntries = new ArrayList<>(mbEnums.MinionType.getCaptainPowers(mob.guardCaptain.contractUUID)); powerEntries.addAll(contractEntries); diff --git a/src/engine/mbEnums.java b/src/engine/mbEnums.java index 3be3dd7d..e51f78e0 100644 --- a/src/engine/mbEnums.java +++ b/src/engine/mbEnums.java @@ -18,6 +18,7 @@ import engine.objects.AbstractCharacter; import engine.objects.Shrine; import engine.objects.Zone; import engine.powers.EffectsBase; +import engine.powers.RunePowerEntry; import org.pmw.tinylog.Logger; import java.util.ArrayList; @@ -2633,55 +2634,61 @@ public class mbEnums { public enum MinionType { AELFBORNGUARD(951, 1637, MinionClass.MELEE, "Guard", "Aelfborn"), - AELFBORNMAGE(952, 1635, MinionClass.MAGE, "Adept", "Aelfborn"), + AELFBORNMAGE(952, 1635, MinionClass.MAGE, "Adept", "Aelfborn", "WIZ-016", "WIZ-061", "WIZ-025", "WIZ-020"), AMAZONGUARD(1500, 1670, MinionClass.MELEE, "Guard", "Amazon"), - AMAZONMAGE(1502, 1638, MinionClass.MAGE, "Fury", "Amazon"), + AMAZONMAGE(1502, 1638, MinionClass.MAGE, "Fury", "Amazon", "FUR-003", "FUR-011", "FUR-009", "FUR-005"), ARACOIXGUARD(1600, 1672, MinionClass.MELEE, "Guard", "Aracoix"), //used guard captain equipset. - ARACOIXMAGE(1602, 885, MinionClass.MAGE, "Adept", "Aracoix"), + ARACOIXMAGE(1602, 885, MinionClass.MAGE, "Adept", "Aracoix", "WLR-029", "WLR-019", "WLR-027", "WLR-012"), CENTAURGUARD(1650, 1642, MinionClass.MELEE, "Guard", "Centaur"), - CENTAURMAGE(1652, 1640, MinionClass.MAGE, "Druid", "Centaur"), + CENTAURMAGE(1652, 1640, MinionClass.MAGE, "Druid", "Centaur", "PRL-017", "PRL-046", "PRL-033", "PRL-009"), DWARVENARCHER(845, 1644, MinionClass.ARCHER, "Marksman", "Dwarven"), DWARVENGUARD(1050, 1666, MinionClass.MELEE, "Guard", "Dwarven"), - DWARVENMAGE(1052, 1643, MinionClass.MAGE, "War Priest", "Dwarven"), + DWARVENMAGE(1052, 1643, MinionClass.MAGE, "War Priest", "Dwarven", "PRL-017", "PRL-046", "PRL-033", "PRL-009"), ELFGUARD(1180, 1671, MinionClass.MELEE, "Guard", "Elven"), //old 1645 - ELFMAGE(1182, 1667, MinionClass.MAGE, "Adept", "Elven"), + ELFMAGE(1182, 1667, MinionClass.MAGE, "Adept", "Elven", "WIZ-020", "WIZ-025", "WIZ-016", "WIZ-061"), FORESTGUARD(1550, 1668, MinionClass.MELEE, "Guard", "Forest"), //captain changed to guard equipset - FORESTMAGE(1552, 436, MinionClass.MAGE, "Adept", "Forest"), + FORESTMAGE(1552, 436, MinionClass.MAGE, "Adept", "Forest", "DRU-009", "DRU-023", "DRU-036", "DRU-015"), HOLYGUARD(1525, 1658, MinionClass.MELEE, "Guard", "Holy Church"), - HOLYMAGE(1527, 1646, MinionClass.MAGE, "Prelate", "Holy Church"), + HOLYMAGE(1527, 1646, MinionClass.MAGE, "Prelate", "Holy Church", "PRL-017", "PRL-046", "PRL-033", "PRL-009"), HUMANARCHER(846, 1654, MinionClass.ARCHER, "Archer", "Human"), HUMANGUARD(840, 1665, MinionClass.MELEE, "Guard", "Human"), - HUMANMAGE(848, 1655, MinionClass.MAGE, "Adept", "Human"), + HUMANMAGE(848, 1655, MinionClass.MAGE, "Adept", "Human", "WIZ-016", "WIZ-025", "WIZ-020", "WIZ-061"), IREKEIGUARD(1350, 1659, MinionClass.MELEE, "Guard", "Irekei"), - IREKEIMAGE(1352, 1660, MinionClass.MAGE, "Adept", "Irekei"), + IREKEIMAGE(1352, 1660, MinionClass.MAGE, "Adept", "Irekei", "WIZ-061", "WIZ-016", "WIZ-025", "WIZ-020"), MINOTAURARCHER(1701, 0, MinionClass.ARCHER, "Archer", "Minotaur"), MINOTAURGUARD(1700, 1673, MinionClass.MELEE, "Guard", "Minotaur"), NORTHMANGUARD(1250, 1669, MinionClass.MELEE, "Guard", "Northman"), - NORTHMANMAGE(1252, 1650, MinionClass.MAGE, "Runecaster", "Northman"), + NORTHMANMAGE(1252, 1650, MinionClass.MAGE, "Runecaster", "Northman", "WIZ-061", "WIZ-016", "WIZ-025", "WIZ-020"), SHADEGUARD(1450, 1662, MinionClass.MELEE, "Guard", "Shade"), - SHADEMAGE(1452, 1664, MinionClass.MAGE, "Adept", "Shade"), + SHADEMAGE(1452, 1664, MinionClass.MAGE, "Adept", "Shade", "NEC-001", "NEC-003", "MAG-013", "MAG-025"), TEMPLARGUARD(841, 1564, MinionClass.MELEE, "Marksman", "Templar"), TEMPLEGUARD(1575, 1652, MinionClass.MELEE, "Guard", "Temple"), - TEMPLEMAGE(1577, 1656, MinionClass.MAGE, "Confessor", "Temple"), + TEMPLEMAGE(1577, 1656, MinionClass.MAGE, "Confessor", "Temple", "CON-048", "CON-015", "CON-012", "CON-051"), UNDEADGUARD(980100, 1674, MinionClass.MELEE, "Guard", "Undead"), - UNDEADMAGE(980102, 1675, MinionClass.MAGE, "Adept", "Undead"), + UNDEADMAGE(980102, 1675, MinionClass.MAGE, "Adept", "Undead", "NEC-001", "NEC-003", "MAG-013", "MAG-025"), WEREWOLFGUARD(980104, 0, MinionClass.MELEE, "Guard", "Werewolf"), WEREBEARGUARD(980103, 0, MinionClass.MELEE, "Guard", "Werebear"); public static HashMap ContractToMinionMap = new HashMap<>(); + private static final int CAPTAIN_POWER_RANK = 40; private final int captainContractID; private final int equipSetID; private final MinionClass minionClass; private final String name; private final String race; + private final ArrayList captainPowers; - MinionType(int captainContractID, int equipSetID, MinionClass minionClass, String name, String race) { + // Captain powers were hand-made by the server team; no client rune or deed lists them + MinionType(int captainContractID, int equipSetID, MinionClass minionClass, String name, String race, String... captainPowerTypes) { this.captainContractID = captainContractID; this.equipSetID = equipSetID; this.minionClass = minionClass; this.name = name; this.race = race; + this.captainPowers = new ArrayList<>(); + for (String powerType : captainPowerTypes) + this.captainPowers.add(new RunePowerEntry(race + " " + name, powerType, CAPTAIN_POWER_RANK)); } public static void InitializeMinions() { @@ -2690,6 +2697,17 @@ public class mbEnums { ContractToMinionMap.put(minionType.captainContractID, minionType); } + // The powers a guard captain's contract casts, shared by its minions; empty for non-caster contracts + public static ArrayList getCaptainPowers(int captainContractID) { + + MinionType minionType = ContractToMinionMap.get(captainContractID); + + if (minionType == null) + return new ArrayList<>(); + + return minionType.captainPowers; + } + public String getName() { return name; } diff --git a/src/engine/mobileAI/MobAI.java b/src/engine/mobileAI/MobAI.java index bc0c8dc0..a4010e1a 100644 --- a/src/engine/mobileAI/MobAI.java +++ b/src/engine/mobileAI/MobAI.java @@ -276,7 +276,7 @@ public class MobAI { // Mobile has no powers defined in mobbase or contract.. - if (PowersManager.getPowersForRune(mob.getMobBaseID()).isEmpty() && PowersManager.getPowersForRune(contractID).isEmpty()) + if (PowersManager.getPowersForRune(mob.getMobBaseID()).isEmpty() && mbEnums.MinionType.getCaptainPowers(contractID).isEmpty()) return false; if (mob.nextCastTime == 0) @@ -309,18 +309,17 @@ public class MobAI { powerEntries = new ArrayList<>(PowersManager.getPowersForRune(mob.getMobBaseID())); purgeEntries = new ArrayList<>(); - // Additional powers may come from the contract ID. This is to support - // powers for player guards irrespective of the mobbase used. + // Player guards also cast their captain contract's powers, irrespective of the mobbase used. if (mob.isPlayerGuard()) { ArrayList contractEntries = new ArrayList<>(); if (mob.contract != null) - contractEntries = PowersManager.getPowersForRune(mob.contractUUID); + contractEntries = mbEnums.MinionType.getCaptainPowers(mob.contractUUID); if (mob.agentType.equals(mbEnums.AIAgentType.GUARDMINION)) - contractEntries = PowersManager.getPowersForRune(mob.guardCaptain.contractUUID); + contractEntries = mbEnums.MinionType.getCaptainPowers(mob.guardCaptain.contractUUID); powerEntries.addAll(contractEntries); diff --git a/src/engine/powers/RunePowerEntry.java b/src/engine/powers/RunePowerEntry.java index e2beb4e7..36307803 100644 --- a/src/engine/powers/RunePowerEntry.java +++ b/src/engine/powers/RunePowerEntry.java @@ -17,7 +17,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; -import java.util.Map; public class RunePowerEntry { @@ -41,13 +40,19 @@ public class RunePowerEntry { this.rank = powerGrant.power_value; } - // Guard contract powers have no client source, so ids without a rune keep their DB rows. - public static HashMap> loadAllRunePowers(HashMap> dbRunePowers) { + public RunePowerEntry(String name, String powerType, int rank) { + this.name = name; + this.token = Hasher.SBStringHash(powerType); + this.power_type = powerType; + this.rank = rank; + } + + // Guard captain contract powers have no rune; they live on mbEnums.MinionType + public static HashMap> loadAllRunePowers() { HashMap> runePowers = new HashMap<>(); ArrayList powerList; int entryCount = 0; - int fallbackCount = 0; for (RuneTemplate rune : RuneTemplate._runes.values()) { @@ -63,16 +68,7 @@ public class RunePowerEntry { entryCount += powerList.size(); } - for (Map.Entry> dbEntry : dbRunePowers.entrySet()) { - - if (RuneTemplate._runes.containsKey(dbEntry.getKey())) - continue; - - runePowers.put(dbEntry.getKey(), dbEntry.getValue()); - fallbackCount++; - } - - Logger.info("Loaded " + entryCount + " rune powers from runes, " + fallbackCount + " ids from the DB; " + runePowers.size() + " ids"); + Logger.info("Loaded " + entryCount + " rune powers for " + runePowers.size() + " runes"); return runePowers; } diff --git a/src/engine/server/world/WorldServer.java b/src/engine/server/world/WorldServer.java index a60126ce..d798ca94 100644 --- a/src/engine/server/world/WorldServer.java +++ b/src/engine/server/world/WorldServer.java @@ -450,7 +450,7 @@ public class WorldServer { DbManager.MobBaseQueries.GET_ALL_MOBBASES(); Logger.info("Loading Rune Powers"); - PowersManager._allRunePowers = RunePowerEntry.loadAllRunePowers(dbRuneBaseHandler.LOAD_RUNE_POWERS()); + PowersManager._allRunePowers = RunePowerEntry.loadAllRunePowers(); Logger.info("Loading Rune Skill Adjusts"); PowersManager._allRuneSkillAdjusts = RuneSkillAdjustEntry.loadAllRuneSkillAdjusts();