Files
Server/src/engine/objects/RuneBase.java
T

337 lines
11 KiB
Java
Raw Normal View History

2026-07-26 19:08:44 -04:00
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
2022-04-30 09:41:17 -04:00
package engine.objects;
import engine.gameManager.DbManager;
2024-05-12 13:36:47 -04:00
import engine.mbEnums;
import engine.parsers.cache.CObjectIndex;
import engine.parsers.objects.RuneTemplate;
2022-04-30 09:41:17 -04:00
import engine.server.MBServerStatics;
import engine.util.Hasher;
import org.pmw.tinylog.Logger;
2022-04-30 09:41:17 -04:00
import java.util.ArrayList;
import java.util.Arrays;
2022-04-30 09:41:17 -04:00
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
2022-04-30 09:41:17 -04:00
import java.util.concurrent.ConcurrentHashMap;
public class RuneBase extends AbstractGameObject {
2023-07-15 09:23:48 -04:00
public static HashMap<Integer, ArrayList<Integer>> AllowedBaseClassRunesMap = new HashMap<>();
public static HashMap<Integer, ArrayList<Integer>> AllowedRaceRunesMap = new HashMap<>();
// Flagged rune_is_standard_character_creation but missing from CharCreateRuneList.cfg ATTRRUNES:
// admin runes 2910/2911, unused duplicates, and Proficient with Bows (commented out in the cfg).
private static final Set<Integer> NON_CREATION_TALENTS = new HashSet<>(Arrays.asList(2910, 2911, 250081, 250116, 250117, 250123, 250124, 250125));
2023-07-15 09:23:48 -04:00
private final String name;
private final String description;
private final ConcurrentHashMap<Integer, Boolean> race = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
private final ConcurrentHashMap<Integer, Boolean> baseClass = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
private final ConcurrentHashMap<Integer, Boolean> promotionClass = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
private final ConcurrentHashMap<Integer, Boolean> discipline = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
private int levelRequired = 1;
private ArrayList<MobBaseEffects> effectsList = new ArrayList<>();
private mbEnums.RuneType runeType;
private int subTypeToken;
private int rank;
private boolean raceRestrict;
private boolean classRestrict;
private boolean discRestrict;
2022-04-30 09:41:17 -04:00
/**
* Rune Template Constructor
* Talents with the same sub type token replace each other by rank (client 0x4695D0), so no overwrite list.
*/
public RuneBase(RuneTemplate rune) {
super(rune.template_id);
Integer classRuneID;
Integer discRuneID;
ArrayList<Integer> raceRuneIDs;
RuneTemplate classRune;
this.name = rune.item_base_name;
this.description = rune.rune_dsc;
this.effectsList = MobBaseEffects.getEffectsForRune(rune.template_id);
this.runeType = rune.rune_type;
this.rank = rune.rune_rank;
this.levelRequired = rune.item_level_req;
this.raceRestrict = rune.item_race_req_restrict;
this.classRestrict = rune.item_class_req_restrict;
this.discRestrict = rune.item_disc_req_restrict;
for (mbEnums.MonsterType raceType : rune.item_race_req) {
raceRuneIDs = CObjectIndex.getRaceRuneIds(raceType);
if (raceRuneIDs.isEmpty())
Logger.warn("RuneBase " + rune.template_id + ": race " + raceType + " has no race rune");
for (int raceRuneID : raceRuneIDs)
this.race.put(raceRuneID, true);
}
for (mbEnums.ClassType classType : rune.item_class_req) {
classRuneID = CObjectIndex.getClassRuneId(classType);
if (classRuneID == null) {
Logger.warn("RuneBase " + rune.template_id + ": class " + classType + " has no class rune");
continue;
}
classRune = RuneTemplate._runes.get(classRuneID);
if (classRune.rune_is_standard_character_creation)
this.baseClass.put(classRuneID, true);
else
this.promotionClass.put(classRuneID, true);
}
for (mbEnums.DisciplineType discType : rune.item_disc_req) {
discRuneID = CObjectIndex.getDisciplineRuneId(discType);
if (discRuneID == null) {
Logger.warn("RuneBase " + rune.template_id + ": discipline " + discType + " has no discipline rune");
continue;
}
this.discipline.put(discRuneID, true);
}
// Same token as CObjectIndex: unresolved sub types are stored as their hash
if (!rune.rune_sub_type.isEmpty())
this.subTypeToken = Hasher.SBStringHash(rune.rune_sub_type);
else
this.subTypeToken = rune.rune_sub_type_hash;
}
2023-07-15 09:23:48 -04:00
public static RuneBase getRuneBase(int tableId) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (tableId == 0)
return null;
2022-04-30 09:41:17 -04:00
return (RuneBase) DbManager.getFromCache(mbEnums.GameObjectType.RuneBase, tableId);
2023-07-15 09:23:48 -04:00
}
2022-04-30 09:41:17 -04:00
// Rune bases are every non-RACE rune: the classes, disciplines and talents (the 315 former static_rune_runebase rows).
2023-07-15 09:23:48 -04:00
public static void LoadAllRuneBases() {
ArrayList<RuneBaseAttribute> attributes;
int loadedCount = 0;
RuneBaseAttribute.runeBaseAttributeMap.clear();
for (RuneTemplate rune : RuneTemplate._runes.values()) {
if (rune.rune_type == mbEnums.RuneType.RACE)
continue;
DbManager.addToCache(new RuneBase(rune));
attributes = RuneBaseAttribute.getAttributesForRune(rune);
// getAttrs() returns null for a rune without attributes
if (!attributes.isEmpty())
RuneBaseAttribute.runeBaseAttributeMap.put(rune.template_id, attributes);
loadedCount++;
}
Logger.info("Loaded " + loadedCount + " rune bases from runes");
loadCreationTalents();
}
// Every creation race and class gets an explicit list; there is no "any" entry.
private static void loadCreationTalents() {
ArrayList<Integer> creationRaceIDs = new ArrayList<>();
ArrayList<Integer> creationClassIDs = new ArrayList<>();
ArrayList<Integer> listedRaceIDs;
ArrayList<Integer> listedClassIDs;
Integer classRuneID;
boolean listed;
boolean allowed;
int talentCount = 0;
AllowedRaceRunesMap = new HashMap<>();
AllowedBaseClassRunesMap = new HashMap<>();
for (RuneTemplate rune : RuneTemplate._runes.values()) {
if (!rune.rune_is_standard_character_creation)
continue;
if (rune.rune_type == mbEnums.RuneType.RACE) {
creationRaceIDs.add(rune.template_id);
AllowedRaceRunesMap.put(rune.template_id, new ArrayList<>());
}
if (rune.rune_type == mbEnums.RuneType.CLASS) {
creationClassIDs.add(rune.template_id);
AllowedBaseClassRunesMap.put(rune.template_id, new ArrayList<>());
}
}
for (RuneTemplate rune : RuneTemplate._runes.values()) {
if (!rune.rune_is_standard_character_creation || rune.rune_type != mbEnums.RuneType.TALENT)
continue;
if (NON_CREATION_TALENTS.contains(rune.template_id))
continue;
listedRaceIDs = new ArrayList<>();
listedClassIDs = new ArrayList<>();
for (mbEnums.MonsterType raceType : rune.item_race_req)
listedRaceIDs.addAll(CObjectIndex.getRaceRuneIds(raceType));
for (mbEnums.ClassType classType : rune.item_class_req) {
classRuneID = CObjectIndex.getClassRuneId(classType);
if (classRuneID != null)
listedClassIDs.add(classRuneID);
}
// restrict true = blacklist, false = whitelist (ArcItemRestrict_Check 0x4BAC60)
for (int raceRuneID : creationRaceIDs) {
listed = listedRaceIDs.contains(raceRuneID);
allowed = rune.item_race_req_restrict ? !listed : listed;
if (allowed)
AllowedRaceRunesMap.get(raceRuneID).add(rune.template_id);
}
for (int baseClassID : creationClassIDs) {
listed = listedClassIDs.contains(baseClassID);
allowed = rune.item_class_req_restrict ? !listed : listed;
if (allowed)
AllowedBaseClassRunesMap.get(baseClassID).add(rune.template_id);
}
talentCount++;
}
Logger.info("Loaded " + talentCount + " creation talents for " + creationRaceIDs.size() + " races and "
+ creationClassIDs.size() + " classes from runes");
2023-07-15 09:23:48 -04:00
}
@Override
public boolean equals(Object obj) {
if (!super.equals(obj)) {
return false;
}
if (obj instanceof RuneBase) {
RuneBase rbObj = (RuneBase) obj;
if (!this.name.equals(rbObj.name)) {
return false;
}
if (!this.description.equals(rbObj.description)) {
return false;
}
return true;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
return false;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
/*
* Getters
*/
public String getName() {
return name;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public String getDescription() {
return description;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
/**
* @return the attrs
*/
public ArrayList<RuneBaseAttribute> getAttrs() {
return RuneBaseAttribute.runeBaseAttributeMap.get(this.getObjectUUID());
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public ConcurrentHashMap<Integer, Boolean> getRace() {
return this.race;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public ConcurrentHashMap<Integer, Boolean> getBaseClass() {
return this.baseClass;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public ConcurrentHashMap<Integer, Boolean> getPromotionClass() {
return this.promotionClass;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public ConcurrentHashMap<Integer, Boolean> getDiscipline() {
return this.discipline;
}
2022-04-30 09:41:17 -04:00
public mbEnums.RuneType getRuneType() {
return this.runeType;
}
// 0 when the rune has no sub type; talents sharing a non-zero token replace each other
public int getSubTypeToken() {
return this.subTypeToken;
}
public int getRank() {
return this.rank;
}
// restrict true: the list is a blacklist; false: a whitelist
public boolean isRaceRestrict() {
return this.raceRestrict;
}
public boolean isClassRestrict() {
return this.classRestrict;
}
public boolean isDiscRestrict() {
return this.discRestrict;
}
2023-07-15 09:23:48 -04:00
/*
* Serializing
*/
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public int getLevelRequired() {
return this.levelRequired;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public void setLevelRequired(int levelRequired) {
this.levelRequired = levelRequired;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
@Override
public void updateDatabase() {
// TODO Auto-generated method stub
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
/**
* @return the effectsList
*/
public ArrayList<MobBaseEffects> getEffectsList() {
return effectsList;
}
2022-04-30 09:41:17 -04:00
}