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 <noreply@anthropic.com>
This commit is contained in:
MagicBotandClaude Opus 5.5 committed 2026-10-23 13:12:12 -04:00
1 parent 0c7b6f6c46
commit c74cb127dc
1 file changed
+33 -13
+33 -13
View File
@@ -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");