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

+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());
}
}