From c74cb127dce33d6b1a0f13ca193e0150f86be721 Mon Sep 17 00:00:00 2001 From: MagicBot Date: Fri, 23 Oct 2026 13:12:12 -0400 Subject: [PATCH] Load MobBase core fields from the race rune Mobbases whose ID is their own race rune (2375 of 2781) now take name, level, health, atr, defense, min/max damage, fsm and enemy/notEnemy from the rune; the 406 server-made mobbases keep their DB row. Server-only columns (bootySet, flags, seeInvis, spawnTime, goldMod, scale, attackRange) stay DB. Parity (MBDATA scripts/p31.py vs magicbane.sql), JSON for all (D2, user decision 2026-10-23): - fsm 84: DB None where the rune has no ROOTFSMID; no change - enemy/notEnemy: no change - level 95: rune 0 (player races, guards, pets, golems, necro pets) - level 8: 13314 53->42, 13581 53->40, 13758 78->40, 14034 54->40, 14172 54->40, 14174 55->40, 14177 54->40, 13171 10->1 - health/atr/def/dmg 61: DB defaults 100/10/20 -> 0 (tombstones 2030-2040, pets 12024-12035 and 13266-13301, CSR 2020/2021 atr/def/dmg) - defense 27: DB 100 -> rune 140-780 (clay, risen/doom, skeletal lines, 12011, 13961, 14167, 14168, 14285, 14292); damage/atr there and on 14037/14087/14296/14303 already came from the rune at runtime - name 33: DB names -> obj_name (e.g. Sharduk -> Hell Hound) No ParityCheck comparator: p31.py covers it. Co-Authored-By: Claude Opus 5.5 --- src/engine/objects/MobBase.java | 46 +++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/engine/objects/MobBase.java b/src/engine/objects/MobBase.java index 0bc5a616..91740408 100644 --- a/src/engine/objects/MobBase.java +++ b/src/engine/objects/MobBase.java @@ -14,6 +14,7 @@ import engine.gameManager.ItemManager; import engine.gameManager.LootManager; import engine.loot.BootySetEntry; import engine.mbEnums; +import engine.parsers.objects.RuneTemplate; import engine.server.MBServerStatics; import java.sql.ResultSet; @@ -58,28 +59,47 @@ public class MobBase extends AbstractGameObject { public MobBase(ResultSet rs) throws SQLException { super(rs, rs.getInt("ID")); + RuneTemplate rune = null; + String fsmName; + this.loadID = rs.getInt("loadID"); - this.firstName = rs.getString("name"); - this.level = rs.getByte("level"); + // Server-made mobbases (15000+) borrow a playable race rune as loadID; only their DB row describes them. + + if (this.getObjectUUID() == this.loadID) + rune = RuneTemplate._runes.get(this.loadID); + + if (rune != null) { + this.firstName = rune.obj_name; + this.level = (byte) rune.rune_level; + this.healthMax = rune.rune_health; + this.damageMin = rune.rune_min_damage; + this.damageMax = rune.rune_max_damage; + this.attackRating = rune.rune_attack; + this.defenseRating = rune.rune_defense; + fsmName = rune.obj_sparse_data.getOrDefault("ROOTFSMID", mbEnums.MobBehaviourType.None.name()); + this.fsm = mbEnums.MobBehaviourType.valueOf(fsmName); + this.notEnemy = EnumSet.copyOf(rune.rune_not_enemy_monster_types); + this.enemy = EnumSet.copyOf(rune.rune_enemy_monster_types); + } else { + this.firstName = rs.getString("name"); + this.level = rs.getByte("level"); + this.healthMax = rs.getInt("health"); + this.damageMin = rs.getFloat("minDmg"); + this.damageMax = rs.getFloat("maxDmg"); + this.attackRating = rs.getInt("atr"); + this.defenseRating = rs.getInt("defense"); + this.fsm = mbEnums.MobBehaviourType.valueOf(rs.getString("fsm")); + this.notEnemy = mbEnums.fromLong(rs.getLong("notEnemy"), mbEnums.MonsterType.class); + this.enemy = mbEnums.fromLong(rs.getLong("enemy"), mbEnums.MonsterType.class); + } this.goldMod = rs.getInt("goldMod"); this.spawnTime = rs.getInt("spawnTime"); - - this.healthMax = rs.getInt("health"); - this.damageMin = rs.getFloat("minDmg"); - this.damageMax = rs.getFloat("maxDmg"); - - this.attackRating = rs.getInt("atr"); - this.defenseRating = rs.getInt("defense"); this.attackRange = rs.getFloat("attackRange"); this.bootySet = rs.getInt("bootySet"); - this.fsm = mbEnums.MobBehaviourType.valueOf(rs.getString("fsm")); - this.flags = mbEnums.fromLong(rs.getLong("flags"), mbEnums.MobFlagType.class); - this.notEnemy = mbEnums.fromLong(rs.getLong("notEnemy"), mbEnums.MonsterType.class); - this.enemy = mbEnums.fromLong(rs.getLong("enemy"), mbEnums.MonsterType.class); this.seeInvis = rs.getInt("seeInvis"); this.scale = rs.getFloat("scale");