Add wpak cfg reader and PowerActions.cfg loader

New engine.parsers.wpak importer (replaces engine.wpak, which stays unused).
WpakFileReader tokenizes cfg lines (quotes, '#'/'*' comments, trailing '#')
and builds XxxBEGIN/XxxEND blocks; every error names file:line.

PowerActionParser loads PowerActions.cfg into PowerActionTemplate._powerActions,
keyed by action id, at world boot. Header layouts per action type come from
the client's ArcPowerAction*_ParseHeader functions: the trailing 0/1 on
ApplyEffect and friends is the aggressive flag, not a level. effect_ids holds
the action -> effect map needed by Task 2.10b. Three ids (PRE-235..237)
repeat identically; the later entry wins, as in the client. No consumers change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MagicBotandClaude Opus 5.5 committed 2026-10-22 11:08:09 -04:00
1 parent 76dcdc0f72
commit 6c820940d3
7 files changed
+711

No files matched your search

@@ -0,0 +1,86 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.parsers.objects.wpak;
import engine.mbEnums;
import java.util.ArrayList;
import java.util.HashMap;
// One POWERACTIONBEGIN block of PowerActions.cfg.
//
// Header: <action_id> <action_type> <type-specific args>. The per-type layouts
// come from the client's ArcPowerAction*_ParseHeader functions. header_args keeps
// every arg as written, including ones the client reads but nothing names yet.
// KEY= lines map to the lower-case fields named after them.
//
// Read only once loaded.
public class PowerActionTemplate {
public static HashMap<String, PowerActionTemplate> _powerActions = new HashMap<>();
public String action_id;
public mbEnums.PowerActionType action_type;
public final ArrayList<String> header_args = new ArrayList<>();
// Header
// ApplyEffect, ApplyEffects, Block, Charm, DamageOverTime, DeferredPower,
// DirectDamage, Invis, SpireDisable, Track, Transform, TransferStatOT
public final ArrayList<String> effect_ids = new ArrayList<>();
public String deferred_power_id;
public int num_iterations;
public int mob_id;
public int mob_level;
public String effect_source_to_remove;
public mbEnums.ItemFlags item_flag;
public boolean item_flag_value;
public String track_filter;
public int max_track;
public mbEnums.CostType transfer_from_type;
public mbEnums.CostType transfer_to_type;
// The client reads both with String::ToInt, so 31.5 acts as 31 (0x56ADF0)
public float transfer_amount;
public String transfer_amount_curve;
public float transfer_efficiency;
public String transfer_efficiency_curve;
// Keys, plus the header's trailing aggressive flag (both set the same client field)
public boolean isaggressive;
public boolean bladetrails;
public boolean isresistable;
public String applyeffectself;
public String applyeffectother;
public String wearoffeffectself;
public String wearoffeffectother;
public boolean applyeffectblank;
public boolean wearoffeffectblank;
public boolean is_available_for_formulae;
public boolean removeall;
public final ArrayList<Integer> bodyparts = new ArrayList<>();
public final ArrayList<Integer> femalebodyparts = new ArrayList<>();
public boolean shouldshowweapons;
public boolean shouldshowarmor;
public final ArrayList<Integer> attackanims = new ArrayList<>();
public float[] scalefactor;
public String effectid;
public int levelcap;
public String levelcap_curve;
public boolean clearaggro;
public boolean targetbecomespet;
public boolean destroyoldpet;
public String rootfsmid;
public mbEnums.DamageType damagetype;
public final ArrayList<Integer> splashdamage = new ArrayList<>();
}
@@ -0,0 +1,257 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.parsers.wpak;
import engine.mbEnums;
import engine.parsers.objects.wpak.PowerActionTemplate;
import org.pmw.tinylog.Logger;
import java.util.ArrayList;
import java.util.HashMap;
// Loads PowerActions.cfg (client loader PowerActions_LoadCfg 0x578EC0).
public class PowerActionParser {
private static final String fileName = "PowerActions.cfg";
public static HashMap<String, PowerActionTemplate> parsePowerActionsFile() {
HashMap<String, PowerActionTemplate> powerActions = new HashMap<>();
WpakBlock root;
PowerActionTemplate powerAction;
int blockCount = 0;
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 POWERACTIONBEGIN/END");
failedCount++;
}
for (WpakBlock block : root.blocks()) {
try {
if (!block.name.equals("POWERACTION"))
throw block.beginLine.error("unexpected " + block.name + "BEGIN");
blockCount++;
powerAction = parsePowerActionBlock(block);
// The client's map assigns on insert, so a repeated id keeps the later entry
if (powerActions.containsKey(powerAction.action_id))
Logger.warn(block.location() + ": duplicate power action " + powerAction.action_id + "; the later entry wins");
powerActions.put(powerAction.action_id, powerAction);
} catch (WpakFormatException e) {
Logger.error(e.getMessage());
failedCount++;
} catch (Exception e) {
Logger.error(block.location() + ": " + e);
failedCount++;
}
}
Logger.info("Loaded " + powerActions.size() + " of " + blockCount + " power actions");
if (failedCount > 0)
return null;
return powerActions;
}
private static PowerActionTemplate parsePowerActionBlock(WpakBlock block) {
PowerActionTemplate powerAction = new PowerActionTemplate();
ArrayList<WpakLine> lines = block.lines();
WpakLine header;
if (!block.blocks().isEmpty())
throw block.blocks().get(0).beginLine.error("nested block in a power action");
if (lines.isEmpty())
throw block.beginLine.error("empty power action");
header = lines.get(0);
powerAction.action_id = header.token(0);
powerAction.action_type = header.enumToken(mbEnums.PowerActionType.class, 1);
powerAction.header_args.addAll(header.tokens.subList(2, header.size()));
parseHeader(powerAction, header);
for (int i = 1; i < lines.size(); i++)
parseKey(powerAction, lines.get(i));
return powerAction;
}
// Layouts from the ArcPowerAction*_ParseHeader functions; args start at token 2
private static void parseHeader(PowerActionTemplate powerAction, WpakLine header) {
switch (powerAction.action_type) {
case ApplyEffect:
case Block:
case Charm:
case DirectDamage:
case Invis:
case SpireDisable:
powerAction.effect_ids.add(header.token(2));
powerAction.isaggressive = header.optionalIntToken(3) != 0;
break;
case ApplyEffects:
powerAction.isaggressive = header.intToken(2) != 0;
for (int i = 3; i < header.size(); i++)
powerAction.effect_ids.add(header.token(i));
break;
case DeferredPower:
powerAction.effect_ids.add(header.token(2));
powerAction.isaggressive = header.intToken(3) != 0;
powerAction.deferred_power_id = header.token(4);
break;
case DamageOverTime:
powerAction.effect_ids.add(header.token(2));
powerAction.num_iterations = header.intToken(3);
powerAction.isaggressive = header.optionalIntToken(4) != 0;
break;
case CreateMob:
powerAction.mob_id = header.intToken(2);
powerAction.mob_level = header.intToken(3);
break;
case RemoveEffect:
powerAction.effect_source_to_remove = header.token(2);
break;
case SetItemFlag:
powerAction.item_flag = header.enumToken(mbEnums.ItemFlags.class, 2);
powerAction.item_flag_value = header.optionalBoolToken(3);
break;
case Track:
powerAction.effect_ids.add(header.token(2));
powerAction.track_filter = header.token(5);
powerAction.max_track = header.intToken(7);
break;
case Transform:
powerAction.effect_ids.add(header.token(2));
break;
case TransferStat:
parseTransferStat(powerAction, header);
break;
case TransferStatOT:
parseTransferStat(powerAction, header);
powerAction.effect_ids.add(header.token(10));
powerAction.num_iterations = header.intToken(11);
break;
default:
break;
}
}
private static void parseTransferStat(PowerActionTemplate powerAction, WpakLine header) {
powerAction.transfer_from_type = header.enumToken(mbEnums.CostType.class, 2);
powerAction.transfer_to_type = header.enumToken(mbEnums.CostType.class, 3);
powerAction.transfer_amount = header.floatToken(4);
powerAction.transfer_amount_curve = header.token(5);
powerAction.transfer_efficiency = header.floatToken(6);
powerAction.transfer_efficiency_curve = header.token(7);
powerAction.isaggressive = header.optionalIntToken(9) != 0;
}
// Shared keys from ArcPowerAction_ParseKey 0x579820; the rest are per action type.
// The client logs an unknown key and goes on; here it fails the load so no data is dropped silently.
private static void parseKey(PowerActionTemplate powerAction, WpakLine line) {
switch (line.token(0)) {
case "ISAGGRESSIVE=":
powerAction.isaggressive = line.optionalBoolToken(1);
break;
case "BLADETRAILS=":
powerAction.bladetrails = line.optionalBoolToken(1);
break;
case "ISRESISTABLE=":
powerAction.isresistable = line.optionalBoolToken(1);
break;
case "APPLYEFFECTSELF=":
powerAction.applyeffectself = line.token(1);
break;
case "APPLYEFFECTOTHER=":
powerAction.applyeffectother = line.token(1);
break;
case "WEAROFFEFFECTSELF=":
powerAction.wearoffeffectself = line.token(1);
break;
case "WEAROFFEFFECTOTHER=":
powerAction.wearoffeffectother = line.token(1);
break;
case "APPLYEFFECTBLANK=":
powerAction.applyeffectblank = line.optionalBoolToken(1);
break;
case "WEAROFFEFFECTBLANK=":
powerAction.wearoffeffectblank = line.optionalBoolToken(1);
break;
case "IS_AVAILABLE_FOR_FORMULAE=":
powerAction.is_available_for_formulae = line.optionalBoolToken(1);
break;
case "REMOVEALL=":
powerAction.removeall = line.optionalBoolToken(1);
break;
case "BODYPARTS=":
powerAction.bodyparts.addAll(line.intTokensFrom(1));
break;
case "FEMALEBODYPARTS=":
powerAction.femalebodyparts.addAll(line.intTokensFrom(1));
break;
case "SHOULDSHOWWEAPONS=":
powerAction.shouldshowweapons = line.optionalBoolToken(1);
break;
case "SHOULDSHOWARMOR=":
powerAction.shouldshowarmor = line.optionalBoolToken(1);
break;
case "ATTACKANIMS=":
powerAction.attackanims.addAll(line.intTokensFrom(1));
break;
case "SCALEFACTOR=":
powerAction.scalefactor = new float[]{line.floatToken(1), line.floatToken(2), line.floatToken(3)};
break;
case "EFFECTID=":
powerAction.effectid = line.token(1);
break;
case "LEVELCAP=":
powerAction.levelcap = line.intToken(1);
powerAction.levelcap_curve = line.has(2) ? line.token(2) : null;
break;
case "CLEARAGGRO=":
powerAction.clearaggro = line.optionalBoolToken(1);
break;
case "TARGETBECOMESPET=":
powerAction.targetbecomespet = line.optionalBoolToken(1);
break;
case "DESTROYOLDPET=":
powerAction.destroyoldpet = line.optionalBoolToken(1);
break;
case "ROOTFSMID=":
powerAction.rootfsmid = line.token(1);
break;
case "DAMAGETYPE=":
powerAction.damagetype = line.enumToken(mbEnums.DamageType.class, 1);
break;
case "SPLASHDAMAGE=":
powerAction.splashdamage.addAll(line.intTokensFrom(1));
break;
default:
throw line.error("unknown power action key " + line.token(0));
}
}
}
+52
View File
@@ -0,0 +1,52 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.parsers.wpak;
import java.util.ArrayList;
// A XxxBEGIN/XxxEND block (name "Xxx"), or the whole file for the root block.
// Lines and nested blocks keep their file order in children.
public class WpakBlock {
public final String name;
public final WpakLine beginLine;
public final ArrayList<Object> children = new ArrayList<>();
public WpakBlock(String name, WpakLine beginLine) {
this.name = name;
this.beginLine = beginLine;
}
public String location() {
return beginLine == null ? name : beginLine.location();
}
public ArrayList<WpakLine> lines() {
ArrayList<WpakLine> lines = new ArrayList<>();
for (Object child : children)
if (child instanceof WpakLine)
lines.add((WpakLine) child);
return lines;
}
public ArrayList<WpakBlock> blocks() {
ArrayList<WpakBlock> blocks = new ArrayList<>();
for (Object child : children)
if (child instanceof WpakBlock)
blocks.add((WpakBlock) child);
return blocks;
}
}
+152
View File
@@ -0,0 +1,152 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.parsers.wpak;
import engine.gameManager.ConfigManager;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
// Reads the client's wpak cfg files (an mbEditorPro export of Config.wpak) into
// tokenized lines and XxxBEGIN/XxxEND blocks. Each parser maps the tokens itself.
public class WpakFileReader {
public static final String wpakPath = ConfigManager.DEFAULT_DATA_DIR + "wpak/";
// Lines starting with '#' or '*' are comments (PowerActions_LoadCfg 0x578EC0).
// A '#' outside quotes ends the line, as in PowActionCostInfo.cfg.
public static ArrayList<WpakLine> readLines(String fileName) throws IOException {
ArrayList<WpakLine> lines = new ArrayList<>();
List<String> fileLines = Files.readAllLines(Paths.get(wpakPath + fileName), StandardCharsets.ISO_8859_1);
String text;
String trimmed;
ArrayList<String> tokens;
for (int i = 0; i < fileLines.size(); i++) {
text = fileLines.get(i);
trimmed = text.trim();
if (trimmed.isEmpty() || trimmed.charAt(0) == '#' || trimmed.charAt(0) == '*')
continue;
tokens = tokenize(fileName, i + 1, text);
if (!tokens.isEmpty())
lines.add(new WpakLine(fileName, i + 1, text, tokens));
}
return lines;
}
// A one-token line XxxBEGIN opens block "Xxx"; XxxEND closes the innermost one.
public static WpakBlock readBlocks(String fileName) throws IOException {
WpakBlock root = new WpakBlock(fileName, null);
ArrayDeque<WpakBlock> openBlocks = new ArrayDeque<>();
WpakBlock block;
String token;
openBlocks.push(root);
for (WpakLine line : readLines(fileName)) {
token = line.token(0);
if (line.size() == 1 && token.length() > 5 && token.endsWith("BEGIN")) {
block = new WpakBlock(token.substring(0, token.length() - 5), line);
openBlocks.peek().children.add(block);
openBlocks.push(block);
continue;
}
if (line.size() == 1 && token.equals(openBlocks.peek().name + "END")) {
openBlocks.pop();
continue;
}
if (line.size() == 1 && closesOuterBlock(openBlocks, token))
throw line.error(token + " before " + openBlocks.peek().name + "END (opened at " + openBlocks.peek().location() + ")");
openBlocks.peek().children.add(line);
}
if (openBlocks.size() > 1)
throw new WpakFormatException(openBlocks.peek().location() + ": " + openBlocks.peek().name + "BEGIN is never closed");
return root;
}
private static boolean closesOuterBlock(ArrayDeque<WpakBlock> openBlocks, String token) {
for (WpakBlock block : openBlocks)
if (block.beginLine != null && token.equals(block.name + "END"))
return true;
return false;
}
// Whitespace separates tokens; double quotes group one token (quotes removed, no escapes).
private static ArrayList<String> tokenize(String fileName, int lineNumber, String text) {
ArrayList<String> tokens = new ArrayList<>();
StringBuilder token = new StringBuilder();
boolean inToken = false;
boolean inQuotes = false;
char c;
for (int i = 0; i < text.length(); i++) {
c = text.charAt(i);
if (inQuotes) {
if (c == '"') {
inQuotes = false;
tokens.add(token.toString());
token.setLength(0);
} else
token.append(c);
continue;
}
if (c == '"' || c == '#' || Character.isWhitespace(c)) {
if (inToken) {
tokens.add(token.toString());
token.setLength(0);
inToken = false;
}
if (c == '#')
break;
if (c == '"')
inQuotes = true;
continue;
}
token.append(c);
inToken = true;
}
if (inQuotes)
throw new WpakFormatException(fileName + ":" + lineNumber + ": unclosed quote");
if (inToken)
tokens.add(token.toString());
return tokens;
}
}
@@ -0,0 +1,18 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.parsers.wpak;
// A malformed wpak cfg entry. The message starts with file:line.
public class WpakFormatException extends RuntimeException {
public WpakFormatException(String message) {
super(message);
}
}
+136
View File
@@ -0,0 +1,136 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.parsers.wpak;
import engine.parsers.cache.CacheParserUtil;
import java.util.ArrayList;
// One non-comment line of a wpak cfg file, split into tokens.
// Every accessor that fails names the file and line.
public class WpakLine {
public final String fileName;
public final int lineNumber;
public final String text;
public final ArrayList<String> tokens;
public WpakLine(String fileName, int lineNumber, String text, ArrayList<String> tokens) {
this.fileName = fileName;
this.lineNumber = lineNumber;
this.text = text;
this.tokens = tokens;
}
public String location() {
return fileName + ":" + lineNumber;
}
public WpakFormatException error(String message) {
return new WpakFormatException(location() + ": " + message);
}
public int size() {
return tokens.size();
}
public boolean has(int index) {
return index < tokens.size();
}
public String token(int index) {
if (!has(index))
throw error("missing token " + index + " in '" + text.trim() + "'");
return tokens.get(index);
}
public int intToken(int index) {
String value = token(index);
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw error("token " + index + " '" + value + "' is not an integer");
}
}
public float floatToken(int index) {
String value = token(index);
try {
return Float.parseFloat(value);
} catch (NumberFormatException e) {
throw error("token " + index + " '" + value + "' is not a number");
}
}
// The client reads a missing trailing number as 0 (String::ToInt on an empty token)
public int optionalIntToken(int index) {
if (!has(index))
return 0;
return intToken(index);
}
// TRUE/FALSE, or a number where non-zero is true
public boolean boolToken(int index) {
String value = token(index);
if (value.equalsIgnoreCase("TRUE"))
return true;
if (value.equalsIgnoreCase("FALSE"))
return false;
try {
return Integer.parseInt(value) != 0;
} catch (NumberFormatException e) {
throw error("token " + index + " '" + value + "' is not a boolean");
}
}
public boolean optionalBoolToken(int index) {
if (!has(index))
return false;
return boolToken(index);
}
public ArrayList<Integer> intTokensFrom(int index) {
ArrayList<Integer> values = new ArrayList<>();
for (int i = index; i < tokens.size(); i++)
values.add(intToken(i));
return values;
}
public <E extends Enum<E>> E enumToken(Class<E> enumClass, int index) {
String value = token(index);
String name = CacheParserUtil.enumName(value);
for (E constant : enumClass.getEnumConstants())
if (constant.name().equalsIgnoreCase(name))
return constant;
throw error("token " + index + " '" + value + "' has no " + enumClass.getSimpleName());
}
}
+10
View File
@@ -39,6 +39,8 @@ 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.PowerActionTemplate;
import engine.parsers.wpak.PowerActionParser;
import engine.powers.RunePowerEntry;
import engine.powers.RuneSkillAdjustEntry;
import engine.server.MBServerStatics;
@@ -334,6 +336,14 @@ public class WorldServer {
Logger.info("Indexing Cache Templates");
CObjectIndex.build();
Logger.info("Loading Power Actions");
PowerActionTemplate._powerActions = PowerActionParser.parsePowerActionsFile();
if (PowerActionTemplate._powerActions == null || PowerActionTemplate._powerActions.isEmpty()) {
Logger.error("ABORT! Power actions failed to load!");
exit(1);
}
Logger.info("Loading PromotionClasses");
PromotionClass.LoadAllPromotions();