Build the creation talent maps from the runes

AllowedRaceRunesMap and AllowedBaseClassRunesMap now come from TALENT runes
with rune_is_standard_character_creation, using item_race_req and
item_class_req (restrict true = blacklist, false = whitelist) over the
creation race runes (2000-2029) and class runes (2500-2503). Every creation
race and class gets an explicit list, so the 111111 sentinel is no longer
used. static_rune_racerune / baseclassrune loaders stay until 2.11.

Eight flagged runes that are not in CharCreateRuneList.cfg ATTRRUNES are left
out: 2910 Wolfpack Developer, 2911 QA Test Rune, the unused duplicates
250116/250117/250123/250124/250125, and 250081 Proficient with Bows
(commented out in the cfg).

Parity (offline, scripts/p26.py): 84 talents, the same as the DB apart from
these value changes, all decided JSON (2026-10-22):
- 250081 Proficient with Bows: no longer accepted at creation (DB: any race/class)
- 250088 Raised by Barbarians: Elf, Irekei lose it
- 250089 Raised by Centaurs: Elf loses it
- 250092 Raised by Thieves' Guild: Dwarf loses it
- 250093 Raised in the Woods: female Irekei loses it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MagicBotandClaude Opus 5.5 committed 2026-10-22 06:49:55 -04:00
1 parent 26aa8de86c
commit 85bc807260
1 file changed
+82 -2
+82 -2
View File
@@ -22,6 +22,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -29,6 +30,9 @@ public class RuneBase extends AbstractGameObject {
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 = Set.of(2910, 2911, 250081, 250116, 250117, 250123, 250124, 250125);
private final String name;
private final String description;
private final int type;
@@ -215,8 +219,84 @@ public class RuneBase extends AbstractGameObject {
Logger.info("Loaded " + loadedCount + " rune bases from runes");
RuneBase.AllowedBaseClassRunesMap = DbManager.RuneBaseQueries.LOAD_ALLOWED_STARTING_RUNES_FOR_BASECLASS();
RuneBase.AllowedRaceRunesMap = DbManager.RuneBaseQueries.LOAD_ALLOWED_STARTING_RUNES_FOR_RACE();
loadCreationTalents();
}
// Every creation race and class gets a list, so the 111111 "any" sentinel of the DB tables is no longer used.
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");
}
@Override