Files
Server/src/engine/objects/SkillReq.java
T
MagicBotandClaude Opus 5.5 4170749a59 Load rune skill reqs from rune_skill_grant instead of static_skill_skillreq
SkillReq.getSkillReqsForRune builds each rune's list from the RUNE JSON
on first use and caches it: skill_value is the grant level and
skill_granted_skills are the prereqs. The prereq amounts (80, Warrior 50,
Sun Dancer 60) are loaded but not yet read; CharacterSkill keeps its
hard-coded 80 and validForWarrior.
Parity (scripts/p29.py): 47 of 79 DB runes match. Value changes (JSON
wins, Task 2.9 decisions):
- Thief Dagger Mastery, Throwing, Unarmed Combat Mastery: level 15 -> 20
- Sentinel Axe/Great Axe/Hammer/Great Hammer/Sword Mastery: 20 -> 15;
  gains Great Sword Mastery (15, Sword); loses Wear Armor, Medium
- Channeler loses Sword; Storm Lord loses Stormcalling
- Blood Horn gains Hammer Mastery (20, Hammer)
- No weapon prereq any more on Archer Archery, Blade Weaver Blade Weaving,
  Sun Dancer Sun Dancing and the 11 Master of X talents
- No effect: Druid/Prelate Sword and Fury Spear 10 -> 1 (promotion is at
  10), discipline/talent level changes (they need level 20), Throwing
  prereqs (always granted), Blade Master Sword prereq (granted at 1)
- JSON-only runes (Archon, Duelist, War Correspondent, Pet, mob/guard
  races) gain lists; Archon resistances and Duelist Dueling are not
  skills and are skipped with a warning

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

172 lines
5.6 KiB
Java

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.objects;
import engine.gameManager.DbManager;
import engine.parsers.objects.RuneTemplate;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SkillReq extends AbstractGameObject {
private static final int DB_SKILL_REQ_AMOUNT = 80;
private static final ConcurrentHashMap<Integer, ArrayList<SkillReq>> skillReqsByRune = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
private int skillID;
private short level;
private ArrayList<Byte> skillReqs;
private HashMap<Byte, Integer> skillReqAmounts;
/* This shouldn't be used
public SkillReq(SkillsBase skillsBase, short level, ArrayList<Byte>skillReqs) {
super();
this.skillsBase = skillsBase;
this.level = level;
this.skillReqs = skillReqs;
}
*/
/* This shouldn't be used
public SkillReq(SkillsBase skillsBase, short level, ArrayList<Byte>skillReqs, int newUUID) {
super(newUUID);
this.skillsBase = skillsBase;
this.level = level;
this.skillReqs = skillReqs;
}
*/
/* This shouldn't be used
public SkillReq(SkillReq a, int newUUID) {
super(a, newUUID);
this.skillsBase = a.skillsBase;
this.level = a.level;
this.skillReqs = a.skillReqs;
}
*/
/**
* ResultSet Constructor
*/
public SkillReq(ResultSet rs) throws SQLException {
super(rs, 0);
this.skillID = rs.getInt("skillID");
this.level = rs.getShort("level");
skillReqs = new ArrayList<>(0);
skillReqAmounts = new HashMap<>();
int skillReq;
skillReq = rs.getInt("skillreq1");
if (skillReq > 0)
skillReqs.add((byte) skillReq);
skillReq = rs.getInt("skillreq2");
if (skillReq > 0)
skillReqs.add((byte) skillReq);
skillReq = rs.getInt("skillreq3");
if (skillReq > 0)
skillReqs.add((byte) skillReq);
// DB rows carry no amount; the server hard-codes 80
for (byte reqSkillID : skillReqs)
skillReqAmounts.put(reqSkillID, DB_SKILL_REQ_AMOUNT);
}
// skill_value is the level the skill is granted at; skill_granted_skills are prereq skills with amounts
public SkillReq(RuneTemplate.SkillGrant skillGrant, SkillsBase skillsBase, int runeID) {
super();
SkillsBase reqSkillsBase;
this.skillID = skillsBase.getObjectUUID();
this.level = (short) skillGrant.skill_value;
this.skillReqs = new ArrayList<>(0);
this.skillReqAmounts = new HashMap<>();
for (Map.Entry<String, Integer> reqSkill : skillGrant.skill_granted_skills.entrySet()) {
reqSkillsBase = DbManager.SkillsBaseQueries.GET_BASE_BY_NAME(reqSkill.getKey());
if (reqSkillsBase == null) {
Logger.warn("Rune " + runeID + " skill grant " + skillGrant.skill_type + ": unknown prereq skill " + reqSkill.getKey());
continue;
}
this.skillReqs.add((byte) reqSkillsBase.getObjectUUID());
this.skillReqAmounts.put((byte) reqSkillsBase.getObjectUUID(), reqSkill.getValue());
}
}
public static ArrayList<SkillReq> getSkillReqsForRune(int runeID) {
return skillReqsByRune.computeIfAbsent(runeID, SkillReq::loadSkillReqsForRune);
}
private static ArrayList<SkillReq> loadSkillReqsForRune(int runeID) {
ArrayList<SkillReq> runeSkillReqs = new ArrayList<>();
RuneTemplate rune = RuneTemplate._runes.get(runeID);
SkillsBase skillsBase;
if (rune == null)
return runeSkillReqs;
for (RuneTemplate.SkillGrant skillGrant : rune.rune_skill_grant) {
skillsBase = DbManager.SkillsBaseQueries.GET_BASE_BY_NAME(skillGrant.skill_type);
// Archon 2018 grants resistances and Duelist 3010 grants Dueling; neither is a skill
if (skillsBase == null) {
Logger.warn("Rune " + runeID + ": skill grant " + skillGrant.skill_type + " is not a skill");
continue;
}
runeSkillReqs.add(new SkillReq(skillGrant, skillsBase, runeID));
}
return runeSkillReqs;
}
/*
* Getters
*/
public SkillsBase getSkillsBase() {
return DbManager.SkillsBaseQueries.GET_BASE(this.skillID);
}
public int getSkillID() {
return this.skillID;
}
public short getLevel() {
return this.level;
}
public ArrayList<Byte> getSkillReqs() {
return this.skillReqs;
}
public int getSkillReqAmount(byte reqSkillID) {
return this.skillReqAmounts.getOrDefault(reqSkillID, DB_SKILL_REQ_AMOUNT);
}
@Override
public void updateDatabase() {
}
}