Files
Server/src/engine/objects/BaseClass.java
T
MagicBotandClaude Opus 5.5 13e02707eb Load player rune effects from item_user_power_action instead of static_npc_mobbase_effects
MobBaseEffects.getEffectsForRune builds each rune's list from the RUNE JSON
on first use and caches it: exact repeats are dropped (54 rune files list
their effects twice), the action id resolves to its effect through
PowerActions.cfg, token = SBStringHash(effect id), rank = arg 0,
reqLvl = arg 1. Used by Race, BaseClass, PromotionClass and RuneBase;
MobBase stays on GET_RUNEBASE_EFFECTS until Task 3.4.
Parity (scripts/p210b.py, token level): 1074 runes match. Differences:
- Tireless, Tough as Nails, Taint of Madness: JSON action TRT-* resolves
  to the same effect the DB stores; no change
- CSR 2901-2904, 2906, 2908, 2910, 2911, Non-Aggro Rune 252624: DB token
  was the NoNPCAggro action (no effect found, skipped); now NONPCAGGRO,
  which has no mods; no gameplay change
- Mob-only 12021/12022 NECROPETSTATS and 13236 PEACE are server rows on
  the MobBase path, untouched

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 12:10:29 -04:00

257 lines
7.9 KiB
Java

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.objects;
import engine.gameManager.DbManager;
import engine.mbEnums;
import engine.net.ByteBufferWriter;
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;
public class BaseClass extends AbstractGameObject {
private final String name;
private final String description;
private final byte strMod;
private final byte dexMod;
private final byte conMod;
private final byte intMod;
private final byte spiMod;
private final float healthMod;
private final float manaMod;
private final float staminaMod;
private final ArrayList<SkillReq> skillsGranted;
private final ArrayList<PowerReq> powersGranted;
private int token = 0;
private ArrayList<MobBaseEffects> effectsList = new ArrayList<>();
/**
* No Table ID Constructor
*/
public BaseClass(String name, String description, byte strMod, byte dexMod, byte conMod, byte intMod, byte spiMod,
ArrayList<RuneBase> allowedRunes, ArrayList<SkillReq> skillsGranted, ArrayList<PowerReq> powersGranted) {
super();
this.name = name;
this.description = description;
this.strMod = strMod;
this.dexMod = dexMod;
this.conMod = conMod;
this.intMod = intMod;
this.spiMod = spiMod;
this.healthMod = 1;
this.manaMod = 1;
this.staminaMod = 1;
this.skillsGranted = skillsGranted;
this.powersGranted = powersGranted;
}
/**
* Normal Constructor
*/
public BaseClass(String name, String description, byte strMod, byte dexMod, byte conMod, byte intMod, byte spiMod,
ArrayList<RuneBase> allowedRunes, ArrayList<SkillReq> skillsGranted, ArrayList<PowerReq> powersGranted, int newUUID) {
super(newUUID);
this.name = name;
this.description = description;
this.strMod = strMod;
this.dexMod = dexMod;
this.conMod = conMod;
this.intMod = intMod;
this.spiMod = spiMod;
this.healthMod = 1;
this.manaMod = 1;
this.staminaMod = 1;
this.skillsGranted = skillsGranted;
this.powersGranted = powersGranted;
}
/**
* ResultSet Constructor
*/
public BaseClass(ResultSet rs) throws SQLException {
super(rs);
this.name = rs.getString("name");
this.description = rs.getString("description");
this.strMod = rs.getByte("strMod");
this.dexMod = rs.getByte("dexMod");
this.conMod = rs.getByte("conMod");
this.intMod = rs.getByte("intMod");
this.spiMod = rs.getByte("spiMod");
this.token = rs.getInt("token");
this.healthMod = rs.getInt("healthMod");
this.manaMod = rs.getInt("manaMod");
this.staminaMod = rs.getInt("staminaMod");
this.skillsGranted = SkillReq.getSkillReqsForRune(this.getObjectUUID());
this.powersGranted = PowerReq.getPowerReqsForRune(this.getObjectUUID());
this.effectsList = MobBaseEffects.getEffectsForRune(this.getObjectUUID());
}
/**
* Rune Template Constructor
*/
public BaseClass(RuneTemplate rune) {
super(rune.template_id);
this.name = rune.obj_name;
this.description = rune.rune_dsc;
this.strMod = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Strength, 0).byteValue();
this.dexMod = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Dexterity, 0).byteValue();
this.conMod = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Constitution, 0).byteValue();
this.intMod = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Intelligence, 0).byteValue();
this.spiMod = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Spirit, 0).byteValue();
this.token = Hasher.SBStringHash(rune.rune_sub_type);
this.healthMod = rune.rune_health;
this.manaMod = rune.rune_mana;
this.staminaMod = rune.rune_stamina;
this.skillsGranted = SkillReq.getSkillReqsForRune(this.getObjectUUID());
this.powersGranted = PowerReq.getPowerReqsForRune(this.getObjectUUID());
this.effectsList = MobBaseEffects.getEffectsForRune(this.getObjectUUID());
}
public static void LoadAllBaseClasses() {
int loadedCount = 0;
// Promotion classes and the Pet class are also CLASS runes, but not standard creation runes
for (RuneTemplate rune : RuneTemplate._runes.values()) {
if (rune.rune_type != mbEnums.RuneType.CLASS || !rune.rune_is_standard_character_creation)
continue;
DbManager.addToCache(new BaseClass(rune));
loadedCount++;
}
Logger.info("Loaded " + loadedCount + " base classes from runes");
}
public static void serializeForClientMsg(BaseClass baseClass, ByteBufferWriter writer) {
serializeForClientMsg(baseClass, writer, 3);
}
public static void serializeForClientMsg(BaseClass baseClass, ByteBufferWriter writer, int type) {
writer.putInt(type); // For BaseClass
writer.putInt(0); // Pad
writer.putInt(baseClass.getObjectUUID());
writer.putInt(baseClass.getObjectType().ordinal()); // Is this correct?
writer.putInt(baseClass.getObjectUUID());
}
public static BaseClass getBaseClass(final int UUID) {
return DbManager.BaseClassQueries.GET_BASE_CLASS(UUID);
}
/*
* Getters
*/
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public byte getStrMod() {
return strMod;
}
public byte getDexMod() {
return dexMod;
}
public byte getConMod() {
return conMod;
}
public byte getIntMod() {
return intMod;
}
public byte getSpiMod() {
return spiMod;
}
public int getToken() {
return this.token;
}
public float getHealthMod() {
return this.healthMod;
}
public float getManaMod() {
return this.manaMod;
}
public float getStaminaMod() {
return this.staminaMod;
}
public ArrayList<Integer> getRuneList() {
return RuneBase.AllowedBaseClassRunesMap.get(this.getObjectUUID());
}
public ArrayList<SkillReq> getSkillsGranted() {
return this.skillsGranted;
}
/*
* Serializing
*/
public ArrayList<PowerReq> getPowersGranted() {
return this.powersGranted;
}
public ArrayList<RuneBaseEffect> getEffectsGranted() {
return RuneBaseEffect.RuneIDBaseEffectMap.get(this.getObjectUUID());
}
/*
* Utils
*/
public boolean isAllowedRune(RuneBase rb) {
if (this.getRuneList().contains(rb.getObjectUUID()))
return true;
if (RuneBase.AllowedBaseClassRunesMap.containsKey(111111)) {
if (RuneBase.AllowedBaseClassRunesMap.get(111111).contains(rb.getObjectUUID()))
return true;
}
return false;
}
@Override
public void updateDatabase() {
; //Never update..
}
public ArrayList<MobBaseEffects> getEffectsList() {
return effectsList;
}
}