Files
Server/src/engine/objects/MobBase.java
T
MagicBotandClaude Opus 5.5 c74cb127dc 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>
2026-10-23 13:12:12 -04:00

301 lines
8.7 KiB
Java

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.objects;
import engine.gameManager.DbManager;
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;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.concurrent.ConcurrentHashMap;
public class MobBase extends AbstractGameObject {
private final int loadID;
private final String firstName;
private final byte level;
private final float scale;
public final ArrayList<MobBaseEffects> effectsList;
public int bootySet;
public mbEnums.MobBehaviourType fsm;
public EnumSet<mbEnums.MonsterType> notEnemy;
public EnumSet<mbEnums.MonsterType> enemy;
private float healthMax;
private int attackRating;
private int defenseRating;
private float damageMin;
private float damageMax;
private float hitBoxRadius;
private EnumSet<mbEnums.MobFlagType> flags;
private int mask;
private int goldMod;
private int seeInvis;
private int spawnTime = 0;
private float attackRange;
private boolean isNecroPet = false;
private MobBaseStats mobBaseStats;
private float walk = 0;
private float run = 0;
private float walkCombat = 0;
private float runCombat = 0;
/**
* ResultSet Constructor
*/
public MobBase(ResultSet rs) throws SQLException {
super(rs, rs.getInt("ID"));
RuneTemplate rune = null;
String fsmName;
this.loadID = rs.getInt("loadID");
// 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.attackRange = rs.getFloat("attackRange");
this.bootySet = rs.getInt("bootySet");
this.flags = mbEnums.fromLong(rs.getLong("flags"), mbEnums.MobFlagType.class);
this.seeInvis = rs.getInt("seeInvis");
this.scale = rs.getFloat("scale");
this.hitBoxRadius = 5f;
this.mask = 0;
if (this.getObjectUUID() == 12021 || this.getObjectUUID() == 12022)
this.isNecroPet = true;
if (this.flags.contains(mbEnums.MobFlagType.HUMANOID))
this.mask += MBServerStatics.MASK_HUMANOID;
if (this.flags.contains(mbEnums.MobFlagType.UNDEAD))
this.mask += MBServerStatics.MASK_UNDEAD;
if (this.flags.contains(mbEnums.MobFlagType.BEAST))
this.mask += MBServerStatics.MASK_BEAST;
if (this.flags.contains(mbEnums.MobFlagType.DRAGON))
this.mask += MBServerStatics.MASK_DRAGON;
if (this.flags.contains(mbEnums.MobFlagType.RAT))
this.mask += MBServerStatics.MASK_RAT;
this.mobBaseStats = DbManager.MobBaseQueries.LOAD_STATS(this.loadID);
DbManager.MobBaseQueries.LOAD_ALL_MOBBASE_SPEEDS(this);
//load effects for mobbase
this.effectsList = DbManager.MobBaseQueries.GET_RUNEBASE_EFFECTS(this.loadID);
}
public static ConcurrentHashMap<mbEnums.EquipSlotType, Item> loadEquipmentSet(int equipmentSetID) {
ArrayList<BootySetEntry> equipList;
ConcurrentHashMap<mbEnums.EquipSlotType, Item> equip = new ConcurrentHashMap<>();
if (equipmentSetID == 0)
return equip;
equipList = LootManager._bootySetMap.get(equipmentSetID);
if (equipList == null)
return equip;
for (BootySetEntry equipmentSetEntry : equipList) {
Item item = new Item(equipmentSetEntry.templateID);
if (item.template.getItemEqSlotsOr().isEmpty())
continue;
item.objectUUID = ItemManager.lastNegativeID.decrementAndGet();
item.drop_chance = equipmentSetEntry.dropChance;
item.equipSlot = item.template.getItemEqSlotsOr().iterator().next();
equip.put(item.equipSlot, item);
}
return equip;
}
public static MobBase getMobBase(int id) {
return DbManager.MobBaseQueries.GET_MOBBASE(id);
}
public static int GetClassType(int mobbaseID) {
switch (mobbaseID) {
case 17235:
case 17233:
case 17256:
case 17259:
case 17260:
case 17261:
return 2518;
case 17258:
case 17257:
case 17237:
case 17234:
return 2521;
default:
return 2518;
}
}
public void updateSpeeds(float walk, float walkCombat, float run, float runCombat) {
this.walk = walk;
this.walkCombat = walkCombat;
this.run = run;
this.runCombat = runCombat;
}
/*
* Getters
*/
public String getFirstName() {
return this.firstName;
}
public int getLoadID() {
return this.loadID;
}
public int getLevel() {
return this.level;
}
public float getHealthMax() {
return this.healthMax;
}
public float getDamageMin() {
return this.damageMin;
}
public float getDamageMax() {
return this.damageMax;
}
public int getAttackRating() {
return this.attackRating;
}
public int getDefenseRating() {
return this.defenseRating;
}
public EnumSet<mbEnums.MobFlagType> getFlags() {
return this.flags;
}
public float getScale() {
return this.scale;
}
public int getTypeMasks() {
return this.mask;
}
public int getSeeInvis() {
return this.seeInvis;
}
public int getSpawnTime() {
return this.spawnTime;
}
@Override
public void updateDatabase() {
// TODO Create update logic.
}
public float getHitBoxRadius() {
if (this.hitBoxRadius < 0f) {
return 0f;
} else {
return this.hitBoxRadius;
}
}
public MobBaseStats getMobBaseStats() {
return mobBaseStats;
}
public float getAttackRange() {
return attackRange;
}
public boolean isNecroPet() {
return isNecroPet;
}
public float getWalk() {
return walk;
}
public void setWalk(float walk) {
this.walk = walk;
}
public float getRun() {
return run;
}
public void setRun(float run) {
this.run = run;
}
public float getWalkCombat() {
return walkCombat;
}
public float getRunCombat() {
return runCombat;
}
}