From 72dcf07aab6191883fb2a4bbc575479a48f8ce66 Mon Sep 17 00:00:00 2001 From: MagicBot Date: Fri, 23 Oct 2026 15:49:39 -0400 Subject: [PATCH] Take power target types from Powers.cfg PowersBase gets setTargetType (the old inline parse, now resetting the flags first) and the world server re-applies every power's target type from PowerTemplate after the wpak load. The login server only uses power tokens and keeps the DB value. Parity vs static_power_powerbase (1465 powers): 1460 identical, area, radius type, exclude and target effect prereqs identical for all. Value changes: - NEC-018 Sepulchre's Bounty: PET -> MOBILE. Its AoE now collects mobs and pets; the NECROPETSTATS target prereq keeps only necro pets. - DRU-066 Braialla's Judgement: NECROPET -> PET. Kept on the DB (user, behaviour change, for the Phase 4 power task): HLR-025 Succor PC (cfg CORPSE), HNT-023 Phaedra's Fury PC (cfg SELF), RNG-033 Fleet of Foot GROUP (cfg SELF). Client: ArcPower_IsValidTargetForType 0x49E440 accepts any live ArcMobile for Mobile and has no case for Pet (server decides). Co-Authored-By: Claude Opus 5.5 --- src/engine/gameManager/PowersManager.java | 29 ++++++ src/engine/powers/PowersBase.java | 108 +++++++++++++--------- src/engine/server/world/WorldServer.java | 2 + 3 files changed, 94 insertions(+), 45 deletions(-) diff --git a/src/engine/gameManager/PowersManager.java b/src/engine/gameManager/PowersManager.java index eec72eb2..c196da99 100644 --- a/src/engine/gameManager/PowersManager.java +++ b/src/engine/gameManager/PowersManager.java @@ -28,6 +28,7 @@ import engine.net.Dispatch; import engine.net.client.ClientConnection; import engine.net.client.msg.*; import engine.objects.*; +import engine.parsers.objects.wpak.PowerTemplate; import engine.powers.*; import engine.powers.poweractions.AbstractPowerAction; import engine.server.MBServerStatics; @@ -37,6 +38,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; @@ -57,6 +59,8 @@ public enum PowersManager { public static HashMap> _allRunePowers; public static HashMap> _allRuneSkillAdjusts; public static HashMap> _effect_costMaps = new HashMap<>(); + // Succor, Phaedra's Fury and Fleet of Foot keep their DB target type until the Phase 4 power task decides them + private static final Set DB_TARGET_TYPE_POWERS = Set.of("HLR-025", "HNT-023", "RNG-033"); private static JobScheduler js; public static void initPowersManager(boolean fullPowersLoad) { @@ -90,6 +94,31 @@ public enum PowersManager { return PowersManager.effectsBaseByToken.get(token); } + // Powers.cfg target type wins over static_power_powerbase; needs PowerTemplate._powers, so the world server calls it after the wpak load + public static void applyClientTargetTypes() { + + PowerTemplate powerTemplate; + int applied = 0; + + for (PowersBase powersBase : PowersManager.powersBaseByIDString.values()) { + + if (DB_TARGET_TYPE_POWERS.contains(powersBase.getIDString())) + continue; + + powerTemplate = PowerTemplate._powers.get(powersBase.getIDString()); + + if (powerTemplate == null || powerTemplate.target_type == null) { + Logger.warn("Power " + powersBase.getIDString() + " has no Powers.cfg target type; keeping the DB value"); + continue; + } + + powersBase.setTargetType(powerTemplate.target_type.name()); + applied++; + } + + Logger.info("Applied Powers.cfg target types to " + applied + " powers"); + } + // This pre-loads only PowersBase for login public static void InitializeLoginPowers() { diff --git a/src/engine/powers/PowersBase.java b/src/engine/powers/PowersBase.java index 056d01bd..77fd431b 100644 --- a/src/engine/powers/PowersBase.java +++ b/src/engine/powers/PowersBase.java @@ -137,51 +137,7 @@ public class PowersBase { this.useMana = true; else if (ct.equals("STAMINA")) this.useStamina = true; - ct = rs.getString("targetType").trim(); - if (ct.equals("BUILDING")) - this.targetBuilding = true; - else if (ct.equals("CORPSE")) - this.targetCorpse = true; - else if (ct.equals("GROUP")) - this.targetGroup = true; - else if (ct.equals("GUILDLEADER")) - this.targetGuildLeader = true; - else if (ct.equals("MOBILE")) { - this.targetMob = true; - this.targetPet = true; // sure on this one? - } else if (ct.equals("PC")) - this.targetPlayer = true; - else if (ct.equals("SELF")) - this.targetSelf = true; - else if (ct.equals("PET")) - this.targetPet = true; - else if (ct.equals("NECROPET")) - this.targetNecroPet = true; - else if (ct.equals("ARMOR")) - this.targetArmor = true; - else if (ct.equals("WEAPON")) - this.targetWeapon = true; - else if (ct.equals("JEWELRY")) - this.targetJewelry = true; - else if (ct.equals("ITEM")) { - this.targetItem = true; - this.targetJewelry = true; - this.targetArmor = true; - this.targetWeapon = true; - } else if (ct.equals("ARMORWEAPONJEWELRY")) { - this.targetArmor = true; - this.targetWeapon = true; - this.targetJewelry = true; - } else if (ct.equals("PCMOBILE")) { - this.targetPlayer = true; - this.targetMob = true; - this.targetPet = true; // sure on this one? - } else if (ct.equals("WEAPONARMOR")) { - this.targetWeapon = true; - this.targetArmor = true; - } else { - Logger.info("Missed " + ct + " targetType"); - } + this.setTargetType(rs.getString("targetType").trim()); String cat = rs.getString("category").trim(); this.category = cat; @@ -327,6 +283,68 @@ public class PowersBase { this.monsterTypeRestrictions.add(ct); } + public void setTargetType(String targetType) { + + this.targetPlayer = false; + this.targetMob = false; + this.targetPet = false; + this.targetNecroPet = false; + this.targetSelf = false; + this.targetWeapon = false; + this.targetCorpse = false; + this.targetBuilding = false; + this.targetGroup = false; + this.targetGuildLeader = false; + this.targetJewelry = false; + this.targetArmor = false; + this.targetItem = false; + + if (targetType.equals("BUILDING")) + this.targetBuilding = true; + else if (targetType.equals("CORPSE")) + this.targetCorpse = true; + else if (targetType.equals("GROUP")) + this.targetGroup = true; + else if (targetType.equals("GUILDLEADER")) + this.targetGuildLeader = true; + else if (targetType.equals("MOBILE")) { + this.targetMob = true; + this.targetPet = true; // sure on this one? + } else if (targetType.equals("PC")) + this.targetPlayer = true; + else if (targetType.equals("SELF")) + this.targetSelf = true; + else if (targetType.equals("PET")) + this.targetPet = true; + else if (targetType.equals("NECROPET")) + this.targetNecroPet = true; + else if (targetType.equals("ARMOR")) + this.targetArmor = true; + else if (targetType.equals("WEAPON")) + this.targetWeapon = true; + else if (targetType.equals("JEWELRY")) + this.targetJewelry = true; + else if (targetType.equals("ITEM")) { + this.targetItem = true; + this.targetJewelry = true; + this.targetArmor = true; + this.targetWeapon = true; + } else if (targetType.equals("ARMORWEAPONJEWELRY")) { + this.targetArmor = true; + this.targetWeapon = true; + this.targetJewelry = true; + } else if (targetType.equals("PCMOBILE")) { + this.targetPlayer = true; + this.targetMob = true; + this.targetPet = true; // sure on this one? + } else if (targetType.equals("WEAPONARMOR")) { + this.targetWeapon = true; + this.targetArmor = true; + } else { + Logger.info("Missed " + targetType + " targetType"); + } + } + public String getName() { return this.name; diff --git a/src/engine/server/world/WorldServer.java b/src/engine/server/world/WorldServer.java index 9ca41018..a60126ce 100644 --- a/src/engine/server/world/WorldServer.java +++ b/src/engine/server/world/WorldServer.java @@ -392,6 +392,8 @@ public class WorldServer { exit(1); } + PowersManager.applyClientTargetTypes(); + Logger.info("Loading Skills"); SkillTemplate._skills = SkillParser.parseSkillsFile();