LoadAllPromotions builds one PromotionClass per promotion CLASS rune (22, 2504-2526 without 2522) and caches it, then runs the DB loader, which skips cached ids, so only the CSR/admin rows 2901-2911 still come from static_rune_promotion (decision: DB fallback). The world server calls it in place of GET_ALL_PROMOTIONS; the login server now preloads promotions too, so character load there gets the same cached objects. The SQL loader stays until task 2.11. Value changes: Templar 2516 manaMod 14 -> 15 (decision: JSON, D2). description NULL -> rune_dsc blurb (unread). All other fields unchanged; final parity before the switch (scripts/p22.py): 22 promotion rows, 0 differences in name, token, health, stamina and the 163 allowed rune tokens. The static_rune_promotion comparator is removed; it would only compare the JSON with itself now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
280 lines
10 KiB
Java
280 lines
10 KiB
Java
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
|
// Magicbane Emulator Project © 2013 - 2026
|
|
// https://www.magicbane.com
|
|
|
|
|
|
package engine.objects;
|
|
|
|
import engine.gameManager.DbManager;
|
|
import engine.mbEnums;
|
|
import engine.mbEnums.GameObjectType;
|
|
import engine.net.ByteBufferWriter;
|
|
import engine.parsers.cache.CObjectIndex;
|
|
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 PromotionClass extends AbstractGameObject {
|
|
|
|
private final String name;
|
|
private final String description;
|
|
private final float healthMod;
|
|
private final float manaMod;
|
|
private final float staminaMod;
|
|
private final ArrayList<Integer> allowedRunes;
|
|
private final ArrayList<SkillReq> skillsGranted;
|
|
private final ArrayList<PowerReq> powersGranted;
|
|
private final ArrayList<RuneBaseEffect> effectsGranted;
|
|
private final ArrayList<RuneBaseEffect> effectsGrantedFighter;
|
|
private final ArrayList<RuneBaseEffect> effectsGrantedHealer;
|
|
private final ArrayList<RuneBaseEffect> effectsGrantedRogue;
|
|
private final ArrayList<RuneBaseEffect> effectsGrantedMage;
|
|
private int token = 0;
|
|
private ArrayList<MobBaseEffects> effectsList = new ArrayList<>();
|
|
|
|
/**
|
|
* No Table ID Constructor
|
|
*/
|
|
public PromotionClass(String name, String description,
|
|
ArrayList<Integer> allowedRunes, ArrayList<SkillReq> skillsGranted, ArrayList<PowerReq> powersGranted) {
|
|
super();
|
|
this.name = name;
|
|
this.description = description;
|
|
this.allowedRunes = allowedRunes;
|
|
this.skillsGranted = skillsGranted;
|
|
this.powersGranted = powersGranted;
|
|
this.healthMod = 0f;
|
|
this.manaMod = 0f;
|
|
this.staminaMod = 0f;
|
|
this.effectsGranted = new ArrayList<>();
|
|
this.effectsGrantedFighter = new ArrayList<>();
|
|
this.effectsGrantedHealer = new ArrayList<>();
|
|
this.effectsGrantedRogue = new ArrayList<>();
|
|
this.effectsGrantedMage = new ArrayList<>();
|
|
}
|
|
|
|
/**
|
|
* Normal Constructor
|
|
*/
|
|
public PromotionClass(String name, String description,
|
|
ArrayList<Integer> allowedRunes, ArrayList<SkillReq> skillsGranted, ArrayList<PowerReq> powersGranted, int newUUID) {
|
|
super(newUUID);
|
|
this.name = name;
|
|
this.description = description;
|
|
this.allowedRunes = allowedRunes;
|
|
this.skillsGranted = skillsGranted;
|
|
this.powersGranted = powersGranted;
|
|
this.healthMod = 0f;
|
|
this.manaMod = 0f;
|
|
this.staminaMod = 0f;
|
|
this.effectsGranted = new ArrayList<>();
|
|
this.effectsGrantedFighter = new ArrayList<>();
|
|
this.effectsGrantedHealer = new ArrayList<>();
|
|
this.effectsGrantedRogue = new ArrayList<>();
|
|
this.effectsGrantedMage = new ArrayList<>();
|
|
}
|
|
|
|
/**
|
|
* ResultSet Constructor
|
|
*/
|
|
public PromotionClass(ResultSet rs) throws SQLException {
|
|
super(rs);
|
|
|
|
this.name = rs.getString("name");
|
|
this.description = rs.getString("description");
|
|
this.token = rs.getInt("token");
|
|
this.healthMod = rs.getFloat("healthMod");
|
|
this.manaMod = rs.getFloat("manaMod");
|
|
this.staminaMod = rs.getFloat("staminaMod");
|
|
this.allowedRunes = DbManager.PromotionQueries.GET_ALLOWED_RUNES(this);
|
|
this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(this.getObjectUUID());
|
|
this.powersGranted = PowerReq.getPowerReqsForRune(this.getObjectUUID());
|
|
this.effectsGranted = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE(this.getObjectUUID());
|
|
this.effectsGrantedFighter = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2500);
|
|
this.effectsGrantedHealer = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2501);
|
|
this.effectsGrantedRogue = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2502);
|
|
this.effectsGrantedMage = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2503);
|
|
this.effectsList = DbManager.MobBaseQueries.GET_RUNEBASE_EFFECTS(this.getObjectUUID());
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* Rune Template Constructor
|
|
*/
|
|
public PromotionClass(RuneTemplate rune) {
|
|
super(rune.template_id);
|
|
|
|
this.name = rune.obj_name;
|
|
this.description = rune.rune_dsc;
|
|
this.token = Hasher.SBStringHash(rune.rune_sub_type);
|
|
this.healthMod = rune.rune_health;
|
|
this.manaMod = rune.rune_mana;
|
|
this.staminaMod = rune.rune_stamina;
|
|
this.allowedRunes = getAllowedRuneTokens(rune);
|
|
this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(this.getObjectUUID());
|
|
this.powersGranted = PowerReq.getPowerReqsForRune(this.getObjectUUID());
|
|
this.effectsGranted = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE(this.getObjectUUID());
|
|
this.effectsGrantedFighter = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2500);
|
|
this.effectsGrantedHealer = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2501);
|
|
this.effectsGrantedRogue = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2502);
|
|
this.effectsGrantedMage = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2503);
|
|
this.effectsList = DbManager.MobBaseQueries.GET_RUNEBASE_EFFECTS(this.getObjectUUID());
|
|
}
|
|
|
|
// Promotions are the CLASS runes that require a base class; this skips the creation classes and Pet 2522.
|
|
|
|
public static boolean isPromotionRune(RuneTemplate rune) {
|
|
return rune.rune_type == mbEnums.RuneType.CLASS && !rune.rune_is_standard_character_creation
|
|
&& !rune.item_class_req.isEmpty();
|
|
}
|
|
|
|
// Race and base class tokens are SBStringHash(rune_sub_type) of the runes named in the req lists.
|
|
// Every promotion rune's lists are whitelists (restrict false).
|
|
|
|
private static ArrayList<Integer> getAllowedRuneTokens(RuneTemplate rune) {
|
|
|
|
ArrayList<Integer> tokens = new ArrayList<>();
|
|
Integer classRuneId;
|
|
int token;
|
|
|
|
for (mbEnums.MonsterType race : rune.item_race_req)
|
|
for (int raceRuneId : CObjectIndex.getRaceRuneIds(race)) {
|
|
token = Hasher.SBStringHash(RuneTemplate._runes.get(raceRuneId).rune_sub_type);
|
|
if (!tokens.contains(token))
|
|
tokens.add(token);
|
|
}
|
|
|
|
for (mbEnums.ClassType classType : rune.item_class_req) {
|
|
classRuneId = CObjectIndex.getClassRuneId(classType);
|
|
if (classRuneId != null)
|
|
tokens.add(Hasher.SBStringHash(RuneTemplate._runes.get(classRuneId).rune_sub_type));
|
|
}
|
|
|
|
return tokens;
|
|
}
|
|
|
|
public static void LoadAllPromotions() {
|
|
|
|
int loadedCount = 0;
|
|
|
|
for (RuneTemplate rune : RuneTemplate._runes.values()) {
|
|
if (!isPromotionRune(rune))
|
|
continue;
|
|
|
|
DbManager.addToCache(new PromotionClass(rune));
|
|
loadedCount++;
|
|
}
|
|
|
|
Logger.info("Loaded " + loadedCount + " promotion classes from runes");
|
|
|
|
// The CSR/admin rows 2901+ have no promotion rune; the DB loader skips ids already cached
|
|
DbManager.PromotionQueries.GET_ALL_PROMOTIONS();
|
|
}
|
|
|
|
public static void serializeForClientMsg(PromotionClass promotionClass, ByteBufferWriter writer) {
|
|
writer.putInt(3); // For BaseClass
|
|
writer.putInt(0); // Pad
|
|
writer.putInt(promotionClass.getObjectUUID());
|
|
writer.putInt(promotionClass.getObjectType().ordinal());
|
|
writer.putInt(promotionClass.getObjectUUID());
|
|
}
|
|
|
|
public static PromotionClass GetPromtionClassFromCache(int runeID) {
|
|
if (runeID == 0)
|
|
return null;
|
|
return (PromotionClass) DbManager.getFromCache(GameObjectType.PromotionClass, runeID);
|
|
}
|
|
|
|
/*
|
|
* Getters
|
|
*/
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
public String getDescription() {
|
|
return description;
|
|
}
|
|
|
|
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 boolean isAllowedRune(int token) {
|
|
for (int b : this.allowedRunes) {
|
|
if (token == b) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public ArrayList<Integer> getRuneList() {
|
|
return this.allowedRunes;
|
|
}
|
|
|
|
public ArrayList<SkillReq> getSkillsGranted() {
|
|
return this.skillsGranted;
|
|
}
|
|
|
|
public ArrayList<PowerReq> getPowersGranted() {
|
|
return this.powersGranted;
|
|
}
|
|
|
|
/*
|
|
* Serializing
|
|
*/
|
|
|
|
public ArrayList<RuneBaseEffect> getEffectsGranted() {
|
|
return this.effectsGranted;
|
|
}
|
|
|
|
public ArrayList<RuneBaseEffect> getEffectsGranted(int baseClassID) {
|
|
if (baseClassID == 2500)
|
|
return this.effectsGrantedFighter;
|
|
else if (baseClassID == 2501)
|
|
return this.effectsGrantedHealer;
|
|
else if (baseClassID == 2502)
|
|
return this.effectsGrantedRogue;
|
|
else if (baseClassID == 2503)
|
|
return this.effectsGrantedMage;
|
|
else
|
|
return new ArrayList<>();
|
|
}
|
|
|
|
@Override
|
|
public void updateDatabase() {
|
|
// TODO Create update logic.
|
|
}
|
|
|
|
public ArrayList<MobBaseEffects> getEffectsList() {
|
|
return effectsList;
|
|
}
|
|
|
|
public void setEffectsList(ArrayList<MobBaseEffects> effectsList) {
|
|
this.effectsList = effectsList;
|
|
}
|
|
}
|