From fae15889f7433a8a17b46a0ea8a6ac20c8258f3d Mon Sep 17 00:00:00 2001 From: MagicBot Date: Thu, 22 Oct 2026 08:18:51 -0400 Subject: [PATCH] Load rune powers from item_power_grant instead of static_rune_powers Parity (scripts/p28.py): all 1066 DB runes with a JSON file match in value and order; token = SBStringHash(power_type), rank = power_value, name = obj_name. The 14 Magister Captain guard contract ids (848, 952, 1052, 1182, 1252, 1352, 1452, 1502, 1527, 1552, 1557, 1602, 1652, 980102) have no rune file; guard spells are server-made, so they keep their DB rows. The JSON adds 170 class/discipline/talent runes; none is a mobbase, and the trainer contracts among them never reach the guard contract path in MobAI, so no reachable value change. Co-Authored-By: Claude Opus 5.5 --- src/engine/powers/RunePowerEntry.java | 50 ++++++++++++++++++++++++ src/engine/server/world/WorldServer.java | 3 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/engine/powers/RunePowerEntry.java b/src/engine/powers/RunePowerEntry.java index 486bd31d..e2beb4e7 100644 --- a/src/engine/powers/RunePowerEntry.java +++ b/src/engine/powers/RunePowerEntry.java @@ -8,8 +8,16 @@ package engine.powers; +import engine.parsers.objects.PowerGrant; +import engine.parsers.objects.RuneTemplate; +import engine.util.Hasher; +import org.pmw.tinylog.Logger; + import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; public class RunePowerEntry { @@ -26,4 +34,46 @@ public class RunePowerEntry { this.rank = rs.getInt("rank"); } + public RunePowerEntry(RuneTemplate rune, PowerGrant powerGrant) { + this.name = rune.obj_name; + this.token = Hasher.SBStringHash(powerGrant.power_type); + this.power_type = powerGrant.power_type; + 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) { + + HashMap> runePowers = new HashMap<>(); + ArrayList powerList; + int entryCount = 0; + int fallbackCount = 0; + + for (RuneTemplate rune : RuneTemplate._runes.values()) { + + if (rune.item_power_grant.isEmpty()) + continue; + + powerList = new ArrayList<>(); + + for (PowerGrant powerGrant : rune.item_power_grant) + powerList.add(new RunePowerEntry(rune, powerGrant)); + + runePowers.put(rune.template_id, powerList); + 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"); + return runePowers; + } + } diff --git a/src/engine/server/world/WorldServer.java b/src/engine/server/world/WorldServer.java index 43f56bcb..58684a75 100644 --- a/src/engine/server/world/WorldServer.java +++ b/src/engine/server/world/WorldServer.java @@ -39,6 +39,7 @@ import engine.parsers.cache.RuneParser; import engine.parsers.objects.DeedTemplate; import engine.parsers.objects.ItemTemplate; import engine.parsers.objects.RuneTemplate; +import engine.powers.RunePowerEntry; import engine.powers.RuneSkillAdjustEntry; import engine.server.MBServerStatics; import engine.util.MapLoader; @@ -358,7 +359,7 @@ public class WorldServer { DbManager.MobBaseQueries.GET_ALL_MOBBASES(); Logger.info("Loading Rune Powers"); - PowersManager._allRunePowers = dbRuneBaseHandler.LOAD_RUNE_POWERS(); + PowersManager._allRunePowers = RunePowerEntry.loadAllRunePowers(dbRuneBaseHandler.LOAD_RUNE_POWERS()); Logger.info("Loading Rune Skill Adjusts"); PowersManager._allRuneSkillAdjusts = RuneSkillAdjustEntry.loadAllRuneSkillAdjusts();