From 3ae1c72f46e081dafc236fa272546c70811540b2 Mon Sep 17 00:00:00 2001 From: MagicBot Date: Thu, 22 Oct 2026 11:22:27 -0400 Subject: [PATCH] Load Skills, StartingKitTable, CharCreateRuneList and PowActionCostInfo SkillParser: uid, name, icon, attribute weights and description per Skills.cfg line (94 skills; the server's ids 1-63 stay in the DB). StartingKitParser: each kit line is a list of up to 10 item ids under its RACE= line, as the client reads it; the file's tab columns are not used (451 kits, same count as static_rune_validkit). CharCreateRuneParser: race (with sex), class, promotion and attribute creation rune lists. PowActionCostParser: resource costs per id, the source of static_effect_costmaps (524 entries). No consumers change. Co-Authored-By: Claude Opus 5.5 --- .../objects/wpak/CharCreateRuneList.java | 27 ++++++ .../objects/wpak/PowActionCostTemplate.java | 26 ++++++ .../parsers/objects/wpak/SkillTemplate.java | 28 ++++++ .../objects/wpak/StartingKitTemplate.java | 29 ++++++ .../parsers/wpak/CharCreateRuneParser.java | 90 +++++++++++++++++++ .../parsers/wpak/PowActionCostParser.java | 59 ++++++++++++ src/engine/parsers/wpak/SkillParser.java | 80 +++++++++++++++++ .../parsers/wpak/StartingKitParser.java | 78 ++++++++++++++++ src/engine/server/world/WorldServer.java | 40 +++++++++ 9 files changed, 457 insertions(+) create mode 100644 src/engine/parsers/objects/wpak/CharCreateRuneList.java create mode 100644 src/engine/parsers/objects/wpak/PowActionCostTemplate.java create mode 100644 src/engine/parsers/objects/wpak/SkillTemplate.java create mode 100644 src/engine/parsers/objects/wpak/StartingKitTemplate.java create mode 100644 src/engine/parsers/wpak/CharCreateRuneParser.java create mode 100644 src/engine/parsers/wpak/PowActionCostParser.java create mode 100644 src/engine/parsers/wpak/SkillParser.java create mode 100644 src/engine/parsers/wpak/StartingKitParser.java diff --git a/src/engine/parsers/objects/wpak/CharCreateRuneList.java b/src/engine/parsers/objects/wpak/CharCreateRuneList.java new file mode 100644 index 00000000..940703a7 --- /dev/null +++ b/src/engine/parsers/objects/wpak/CharCreateRuneList.java @@ -0,0 +1,27 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2026 +// https://www.magicbane.com + +package engine.parsers.objects.wpak; + +import engine.mbEnums; + +import java.util.ArrayList; +import java.util.LinkedHashMap; + +// CharCreateRuneList.cfg: the rune ids the client offers at character creation, +// in file order. Race runes carry the sex they are offered to (NONE = both). + +public class CharCreateRuneList { + + public static CharCreateRuneList _runeList; + + public final LinkedHashMap race_runes = new LinkedHashMap<>(); + public final ArrayList class_runes = new ArrayList<>(); + public final ArrayList promotion_runes = new ArrayList<>(); + public final ArrayList attr_runes = new ArrayList<>(); +} diff --git a/src/engine/parsers/objects/wpak/PowActionCostTemplate.java b/src/engine/parsers/objects/wpak/PowActionCostTemplate.java new file mode 100644 index 00000000..17f90d3d --- /dev/null +++ b/src/engine/parsers/objects/wpak/PowActionCostTemplate.java @@ -0,0 +1,26 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2026 +// https://www.magicbane.com + +package engine.parsers.objects.wpak; + +import engine.mbEnums; + +import java.util.HashMap; +import java.util.LinkedHashMap; + +// One line of PowActionCostInfo.cfg: ("" )*. +// The resource cost of a prefix/suffix (client EffectCostMap 0x6E00C0); the source of +// the server's static_effect_costmaps. + +public class PowActionCostTemplate { + + public static HashMap _costs = new HashMap<>(); + + public String id; + public final LinkedHashMap costs = new LinkedHashMap<>(); +} diff --git a/src/engine/parsers/objects/wpak/SkillTemplate.java b/src/engine/parsers/objects/wpak/SkillTemplate.java new file mode 100644 index 00000000..296b90ef --- /dev/null +++ b/src/engine/parsers/objects/wpak/SkillTemplate.java @@ -0,0 +1,28 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2026 +// https://www.magicbane.com + +package engine.parsers.objects.wpak; + +import engine.mbEnums; + +import java.util.HashMap; +import java.util.LinkedHashMap; + +// One line of Skills.cfg: "" "" ( )* = "". +// The cfg has no numeric ids; the server's skill ids 1-63 stay in static_skill_skillsbase. + +public class SkillTemplate { + + public static HashMap _skills = new HashMap<>(); + + public String skill_uid; + public String skill_name; + public int icon_number; + public final LinkedHashMap attr_weights = new LinkedHashMap<>(); + public String description; +} diff --git a/src/engine/parsers/objects/wpak/StartingKitTemplate.java b/src/engine/parsers/objects/wpak/StartingKitTemplate.java new file mode 100644 index 00000000..136ea3b9 --- /dev/null +++ b/src/engine/parsers/objects/wpak/StartingKitTemplate.java @@ -0,0 +1,29 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2026 +// https://www.magicbane.com + +package engine.parsers.objects.wpak; + +import engine.mbEnums; + +import java.util.ArrayList; + +// One kit of StartingKitTable.cfg: a kit line under RACE= "". +// The client reads a kit as a plain list of up to 10 item template ids; the tab +// columns in the file (Legs, Torso, ...) are not used (0x620250). +// kit_number counts the kits of one race/sex/class from 0. + +public class StartingKitTemplate { + + public static ArrayList _kits = new ArrayList<>(); + + public String race; + public mbEnums.SexType sex; + public String base_class; + public int kit_number; + public final ArrayList item_ids = new ArrayList<>(); +} diff --git a/src/engine/parsers/wpak/CharCreateRuneParser.java b/src/engine/parsers/wpak/CharCreateRuneParser.java new file mode 100644 index 00000000..7990f7fa --- /dev/null +++ b/src/engine/parsers/wpak/CharCreateRuneParser.java @@ -0,0 +1,90 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2026 +// https://www.magicbane.com + +package engine.parsers.wpak; + +import engine.mbEnums; +import engine.parsers.objects.wpak.CharCreateRuneList; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + +// Loads CharCreateRuneList.cfg (client loaders at 0xBA4C20 and three siblings, one per section). + +public class CharCreateRuneParser { + + private static final String fileName = "CharCreateRuneList.cfg"; + + public static CharCreateRuneList parseCharCreateRuneFile() { + + CharCreateRuneList runeList = new CharCreateRuneList(); + WpakBlock root; + int failedCount = 0; + + try { + root = WpakFileReader.readBlocks(fileName); + } catch (Exception e) { + Logger.error(fileName + ": " + e.getMessage()); + return null; + } + + for (WpakLine line : root.lines()) { + Logger.error(line.location() + ": line outside a XxxRUNESBEGIN/END section"); + failedCount++; + } + + for (WpakBlock block : root.blocks()) { + try { + switch (block.name) { + case "RACERUNES": + for (WpakLine line : block.lines()) + runeList.race_runes.put(line.intToken(0), parseSex(line)); + break; + case "CLASSRUNES": + readRuneIds(block, runeList.class_runes); + break; + case "PROMOTIONRUNES": + readRuneIds(block, runeList.promotion_runes); + break; + case "ATTRRUNES": + readRuneIds(block, runeList.attr_runes); + break; + default: + throw block.beginLine.error("unexpected " + block.name + "BEGIN"); + } + } catch (WpakFormatException e) { + Logger.error(e.getMessage()); + failedCount++; + } + } + + Logger.info("Loaded " + runeList.race_runes.size() + " race, " + runeList.class_runes.size() + " class, " + + runeList.promotion_runes.size() + " promotion and " + runeList.attr_runes.size() + " attribute creation runes"); + + if (failedCount > 0) + return null; + + return runeList; + } + + // The client keeps the previous line's sex when one is missing; every race line in the file has one + + private static mbEnums.SexType parseSex(WpakLine line) { + + if (!line.has(1)) + return mbEnums.SexType.NONE; + + return line.enumToken(mbEnums.SexType.class, 1); + } + + private static void readRuneIds(WpakBlock block, ArrayList runeIds) { + + for (WpakLine line : block.lines()) + runeIds.add(line.intToken(0)); + } +} diff --git a/src/engine/parsers/wpak/PowActionCostParser.java b/src/engine/parsers/wpak/PowActionCostParser.java new file mode 100644 index 00000000..5560997b --- /dev/null +++ b/src/engine/parsers/wpak/PowActionCostParser.java @@ -0,0 +1,59 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2026 +// https://www.magicbane.com + +package engine.parsers.wpak; + +import engine.mbEnums; +import engine.parsers.objects.wpak.PowActionCostTemplate; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.HashMap; + +// Loads PowActionCostInfo.cfg (client loader EffectCostMap_LoadPowActionCostInfo 0x6E00C0). + +public class PowActionCostParser { + + private static final String fileName = "PowActionCostInfo.cfg"; + + public static HashMap parsePowActionCostFile() { + + HashMap costs = new HashMap<>(); + ArrayList lines; + PowActionCostTemplate cost; + int failedCount = 0; + + try { + lines = WpakFileReader.readLines(fileName); + } catch (Exception e) { + Logger.error(fileName + ": " + e.getMessage()); + return null; + } + + for (WpakLine line : lines) { + try { + // A repeated id adds its pairs to the existing entry, as the client does + cost = costs.computeIfAbsent(line.token(0), id -> new PowActionCostTemplate()); + cost.id = line.token(0); + + for (int i = 1; i < line.size(); i += 2) + cost.costs.put(line.enumToken(mbEnums.ResourceType.class, i), line.intToken(i + 1)); + } catch (WpakFormatException e) { + Logger.error(e.getMessage()); + failedCount++; + } + } + + Logger.info("Loaded " + costs.size() + " power action resource costs from " + lines.size() + " lines"); + + if (failedCount > 0) + return null; + + return costs; + } +} diff --git a/src/engine/parsers/wpak/SkillParser.java b/src/engine/parsers/wpak/SkillParser.java new file mode 100644 index 00000000..ca7ddbcf --- /dev/null +++ b/src/engine/parsers/wpak/SkillParser.java @@ -0,0 +1,80 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2026 +// https://www.magicbane.com + +package engine.parsers.wpak; + +import engine.mbEnums; +import engine.parsers.objects.wpak.SkillTemplate; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.HashMap; + +// Loads Skills.cfg (client loader ArcSkill_LoadSkillsCfg 0x584430). + +public class SkillParser { + + private static final String fileName = "Skills.cfg"; + + public static HashMap parseSkillsFile() { + + HashMap skills = new HashMap<>(); + ArrayList lines; + SkillTemplate skill; + int failedCount = 0; + + try { + lines = WpakFileReader.readLines(fileName); + } catch (Exception e) { + Logger.error(fileName + ": " + e.getMessage()); + return null; + } + + for (WpakLine line : lines) { + try { + skill = parseSkillLine(line); + + if (skills.containsKey(skill.skill_uid)) + Logger.warn(line.location() + ": duplicate skill " + skill.skill_uid + "; the later entry wins"); + + skills.put(skill.skill_uid, skill); + } catch (WpakFormatException e) { + Logger.error(e.getMessage()); + failedCount++; + } + } + + Logger.info("Loaded " + skills.size() + " of " + lines.size() + " skills"); + + if (failedCount > 0) + return null; + + return skills; + } + + // Attribute/weight pairs run until the "=" token; the description follows it + + private static SkillTemplate parseSkillLine(WpakLine line) { + + SkillTemplate skill = new SkillTemplate(); + int index = 3; + + skill.skill_uid = line.token(0); + skill.skill_name = line.token(1); + skill.icon_number = line.intToken(2); + + while (line.has(index) && !line.token(index).equals("=")) { + skill.attr_weights.put(line.enumToken(mbEnums.AttributeType.class, index), line.intToken(index + 1)); + index += 2; + } + + skill.description = line.has(index + 1) ? line.token(index + 1) : ""; + + return skill; + } +} diff --git a/src/engine/parsers/wpak/StartingKitParser.java b/src/engine/parsers/wpak/StartingKitParser.java new file mode 100644 index 00000000..878d5c3d --- /dev/null +++ b/src/engine/parsers/wpak/StartingKitParser.java @@ -0,0 +1,78 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2026 +// https://www.magicbane.com + +package engine.parsers.wpak; + +import engine.mbEnums; +import engine.parsers.objects.wpak.StartingKitTemplate; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + +// Loads StartingKitTable.cfg (client loader ArcBaseKitManager_LoadStartingKitTable 0x620250). + +public class StartingKitParser { + + private static final String fileName = "StartingKitTable.cfg"; + private static final int maxKitItems = 10; + + public static ArrayList parseStartingKitFile() { + + ArrayList kits = new ArrayList<>(); + ArrayList lines; + StartingKitTemplate kit; + String race = null; + mbEnums.SexType sex = null; + String baseClass = null; + int kitNumber = 0; + int failedCount = 0; + + try { + lines = WpakFileReader.readLines(fileName); + } catch (Exception e) { + Logger.error(fileName + ": " + e.getMessage()); + return null; + } + + for (WpakLine line : lines) { + try { + if (line.token(0).equals("RACE=")) { + race = line.token(1); + sex = line.enumToken(mbEnums.SexType.class, 2); + baseClass = line.token(3); + kitNumber = 0; + continue; + } + + if (race == null) + throw line.error("kit line before the first RACE="); + + if (line.size() > maxKitItems) + throw line.error("kit has " + line.size() + " items; the client reads at most " + maxKitItems); + + kit = new StartingKitTemplate(); + kit.race = race; + kit.sex = sex; + kit.base_class = baseClass; + kit.kit_number = kitNumber++; + kit.item_ids.addAll(line.intTokensFrom(0)); + kits.add(kit); + } catch (WpakFormatException e) { + Logger.error(e.getMessage()); + failedCount++; + } + } + + Logger.info("Loaded " + kits.size() + " starting kits"); + + if (failedCount > 0) + return null; + + return kits; + } +} diff --git a/src/engine/server/world/WorldServer.java b/src/engine/server/world/WorldServer.java index 5c4edebb..995e3f47 100644 --- a/src/engine/server/world/WorldServer.java +++ b/src/engine/server/world/WorldServer.java @@ -39,16 +39,24 @@ import engine.parsers.cache.RuneParser; import engine.parsers.objects.DeedTemplate; import engine.parsers.objects.ItemTemplate; import engine.parsers.objects.RuneTemplate; +import engine.parsers.objects.wpak.CharCreateRuneList; import engine.parsers.objects.wpak.CompoundCurveTemplate; import engine.parsers.objects.wpak.EffectTemplate; import engine.parsers.objects.wpak.ModTypeTemplate; +import engine.parsers.objects.wpak.PowActionCostTemplate; import engine.parsers.objects.wpak.PowerActionTemplate; import engine.parsers.objects.wpak.PowerTemplate; +import engine.parsers.objects.wpak.SkillTemplate; +import engine.parsers.objects.wpak.StartingKitTemplate; +import engine.parsers.wpak.CharCreateRuneParser; import engine.parsers.wpak.CompoundCurveParser; import engine.parsers.wpak.EffectParser; import engine.parsers.wpak.ModTypeParser; +import engine.parsers.wpak.PowActionCostParser; import engine.parsers.wpak.PowerActionParser; import engine.parsers.wpak.PowerParser; +import engine.parsers.wpak.SkillParser; +import engine.parsers.wpak.StartingKitParser; import engine.powers.RunePowerEntry; import engine.powers.RuneSkillAdjustEntry; import engine.server.MBServerStatics; @@ -384,6 +392,38 @@ public class WorldServer { exit(1); } + Logger.info("Loading Skills"); + SkillTemplate._skills = SkillParser.parseSkillsFile(); + + if (SkillTemplate._skills == null || SkillTemplate._skills.isEmpty()) { + Logger.error("ABORT! Skills failed to load!"); + exit(1); + } + + Logger.info("Loading Starting Kits"); + StartingKitTemplate._kits = StartingKitParser.parseStartingKitFile(); + + if (StartingKitTemplate._kits == null || StartingKitTemplate._kits.isEmpty()) { + Logger.error("ABORT! Starting kits failed to load!"); + exit(1); + } + + Logger.info("Loading Character Creation Runes"); + CharCreateRuneList._runeList = CharCreateRuneParser.parseCharCreateRuneFile(); + + if (CharCreateRuneList._runeList == null) { + Logger.error("ABORT! Character creation runes failed to load!"); + exit(1); + } + + Logger.info("Loading Power Action Resource Costs"); + PowActionCostTemplate._costs = PowActionCostParser.parsePowActionCostFile(); + + if (PowActionCostTemplate._costs == null || PowActionCostTemplate._costs.isEmpty()) { + Logger.error("ABORT! Power action resource costs failed to load!"); + exit(1); + } + Logger.info("Loading PromotionClasses"); PromotionClass.LoadAllPromotions();