// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // 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 allowedRunes; private final ArrayList skillsGranted; private final ArrayList powersGranted; private int token = 0; private ArrayList effectsList = new ArrayList<>(); /** * No Table ID Constructor */ public PromotionClass(String name, String description, ArrayList allowedRunes, ArrayList skillsGranted, ArrayList 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; } /** * Normal Constructor */ public PromotionClass(String name, String description, ArrayList allowedRunes, ArrayList skillsGranted, ArrayList 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; } /** * ResultSet Constructor * Only the CSR/admin rows 2901+ load from the DB; they have no promotion rune and allow no runes. */ 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 = new ArrayList<>(); this.skillsGranted = SkillReq.getSkillReqsForRune(this.getObjectUUID()); this.powersGranted = PowerReq.getPowerReqsForRune(this.getObjectUUID()); this.effectsList = MobBaseEffects.getEffectsForRune(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 = SkillReq.getSkillReqsForRune(this.getObjectUUID()); this.powersGranted = PowerReq.getPowerReqsForRune(this.getObjectUUID()); this.effectsList = MobBaseEffects.getEffectsForRune(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 getAllowedRuneTokens(RuneTemplate rune) { ArrayList 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 getRuneList() { return this.allowedRunes; } public ArrayList getSkillsGranted() { return this.skillsGranted; } public ArrayList getPowersGranted() { return this.powersGranted; } /* * Serializing */ @Override public void updateDatabase() { // TODO Create update logic. } public ArrayList getEffectsList() { return effectsList; } public void setEffectsList(ArrayList effectsList) { this.effectsList = effectsList; } }