Load Powers.cfg at world boot
PowerParser loads Powers.cfg into PowerTemplate._powers. The 22 header fields are named after the client loader's error messages (0x56E410); ACTION= lines keep trains, duration, curve and stack fields; prereqs, curves, flags, messages and CONDITION blocks are kept. Keys compare case-insensitively, as in the client. Four keys the client ignores (APPLYDAMAGESELF=, APPLYEFFECTCASTER=, APPLYEFFECTTARGET= and a POWEREND= typo) are skipped; any other unknown key fails the load. Numbers are read like the client's atoi/atof with a warning when a token isn't clean: PRL-033 init time "2.9.0" -> 2.9 and SGE-001 precision "10.0" -> 10, which matches the DB. Ten EQPREREQ lines end without a required flag, which the client reads as 0. Needs the fixed Powers.cfg (SRDX-DB header had "SPELL 15 Unknown" pasted twice; fixed in MBDATA, findings section 6). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
623f7c4ae6
commit
bd145b1e55
5 files changed
+497
-3
No files matched your search
@@ -0,0 +1,132 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// 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;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
// One POWERBEGIN block of Powers.cfg (client loader 0x56E410).
|
||||
// Header field names follow the client's "<field> Error with line" messages.
|
||||
// KEY= lines map to the lower-case fields named after them.
|
||||
//
|
||||
// Read only once loaded.
|
||||
|
||||
public class PowerTemplate {
|
||||
|
||||
public static HashMap<String, PowerTemplate> _powers = new HashMap<>();
|
||||
|
||||
// Header: 22 tokens
|
||||
|
||||
public String power_id;
|
||||
public String power_name;
|
||||
public mbEnums.PowerType power_type;
|
||||
public int icon_number;
|
||||
public String focus;
|
||||
public mbEnums.PowerTargetType target_type;
|
||||
public float range;
|
||||
public mbEnums.AreaType area_type;
|
||||
public float area_range;
|
||||
public mbEnums.ExcludeType exclude_type;
|
||||
public mbEnums.CostType cost_type;
|
||||
public float cost_amt;
|
||||
public int difficulty;
|
||||
public int precision;
|
||||
public float init_time;
|
||||
public float release_time;
|
||||
public float recycle_time;
|
||||
public boolean hit_roll_yn;
|
||||
public mbEnums.CastingModeType casting_mode;
|
||||
public int init_anim;
|
||||
public int release_anim;
|
||||
public mbEnums.TargetSelectType target_sel;
|
||||
|
||||
// Keys
|
||||
|
||||
public final ArrayList<ActionEntry> actions = new ArrayList<>();
|
||||
public int maxlevel;
|
||||
public float hatevalue;
|
||||
public String hatevalue_curve;
|
||||
public String grantoverridevar;
|
||||
public mbEnums.PowerCategoryType category;
|
||||
public final ArrayList<EquipPrereq> eqprereqs = new ArrayList<>();
|
||||
public final ArrayList<mbEnums.MonsterType> monstertypeprereqs = new ArrayList<>();
|
||||
public final ArrayList<mbEnums.MonsterType> monstertyperestricts = new ArrayList<>();
|
||||
public final ArrayList<EffectPrereq> targeteffectprereqs_anded = new ArrayList<>();
|
||||
public final ArrayList<EffectPrereq> targeteffectprereqs_ored = new ArrayList<>();
|
||||
public final ArrayList<EffectPrereq> effectprereqs = new ArrayList<>();
|
||||
|
||||
// CURVE= <COSTAMT|INITTIME|RECYCLETIME> <curve>: the curve applied to that header value
|
||||
public final LinkedHashMap<String, String> curves = new LinkedHashMap<>();
|
||||
|
||||
public final ArrayList<String> descriptions = new ArrayList<>();
|
||||
public int loopanimid;
|
||||
public boolean bladetrails;
|
||||
public boolean cancastwhilemoving;
|
||||
public boolean cancastwhileflying;
|
||||
public boolean shouldcheckpath;
|
||||
public boolean isadminpower;
|
||||
public boolean cantargetadmin;
|
||||
public boolean isprojectile;
|
||||
public boolean sticky;
|
||||
public int maxnummobtargets;
|
||||
public int maxnumplayertargets;
|
||||
public float pulseinfo_duration;
|
||||
public int pulseinfo_iterations;
|
||||
public final ArrayList<Integer> sounds = new ArrayList<>();
|
||||
public int casterspulseparticle;
|
||||
public final ArrayList<EffectTemplate.ConditionEntry> conditions = new ArrayList<>();
|
||||
|
||||
public String initstring;
|
||||
public String successself;
|
||||
public String successother;
|
||||
public String fizzleself;
|
||||
public String fizzleother;
|
||||
public String applyeffectself;
|
||||
public String applyeffectother;
|
||||
public String wearoffeffectself;
|
||||
public String wearoffeffectother;
|
||||
public String applydamagecaster;
|
||||
public String applydamagetarget;
|
||||
public String applydamageother;
|
||||
|
||||
// ACTION= <action_id> <min_trains> <max_trains> <duration> <duration_curve> <stack_type>
|
||||
// <stack_order> <stack_priority>, then up to four optional pairs kept only in args
|
||||
|
||||
public static class ActionEntry {
|
||||
|
||||
public String action_id;
|
||||
public int min_trains;
|
||||
public int max_trains;
|
||||
public float duration;
|
||||
public String duration_curve;
|
||||
public String stack_type;
|
||||
public int stack_order;
|
||||
public mbEnums.CategoryToPowerType stack_priority;
|
||||
public final ArrayList<String> args = new ArrayList<>();
|
||||
}
|
||||
|
||||
// EQPREREQ= repeats <slot> <skill> <required>
|
||||
|
||||
public static class EquipPrereq {
|
||||
|
||||
public mbEnums.EquipSlotType slot;
|
||||
public String skill;
|
||||
public boolean required;
|
||||
}
|
||||
|
||||
public static class EffectPrereq {
|
||||
|
||||
public String effect_id;
|
||||
public int value;
|
||||
public String message;
|
||||
}
|
||||
}
|
||||
@@ -208,7 +208,7 @@ public class EffectParser {
|
||||
mod.curve = WpakParserUtil.curveToken(line, 3);
|
||||
}
|
||||
|
||||
private static EffectTemplate.ConditionEntry parseCondition(WpakLine line) {
|
||||
static EffectTemplate.ConditionEntry parseCondition(WpakLine line) {
|
||||
|
||||
EffectTemplate.ConditionEntry condition = new EffectTemplate.ConditionEntry();
|
||||
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// Magicbane Emulator Project © 2013 - 2026
|
||||
// https://www.magicbane.com
|
||||
|
||||
package engine.parsers.wpak;
|
||||
|
||||
import engine.mbEnums;
|
||||
import engine.parsers.objects.wpak.PowerTemplate;
|
||||
import org.pmw.tinylog.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
// Loads Powers.cfg (client loader 0x56E410). CompoundCurves.cfg must be loaded first.
|
||||
|
||||
public class PowerParser {
|
||||
|
||||
private static final String fileName = "Powers.cfg";
|
||||
private static final int headerSize = 22;
|
||||
|
||||
// Keys the client's loader doesn't know and skips; "POWEREND=" is a typo for CURVE= on one power
|
||||
private static final HashSet<String> clientIgnoredKeys = new HashSet<>(Arrays.asList(
|
||||
"APPLYDAMAGESELF=", "APPLYEFFECTCASTER=", "APPLYEFFECTTARGET=", "POWEREND="));
|
||||
|
||||
public static HashMap<String, PowerTemplate> parsePowersFile() {
|
||||
|
||||
HashMap<String, PowerTemplate> powers = new HashMap<>();
|
||||
WpakBlock root;
|
||||
PowerTemplate power;
|
||||
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 POWERBEGIN/END");
|
||||
failedCount++;
|
||||
}
|
||||
|
||||
for (WpakBlock block : root.blocks()) {
|
||||
try {
|
||||
if (!block.name.equals("POWER"))
|
||||
throw block.beginLine.error("unexpected " + block.name + "BEGIN");
|
||||
|
||||
blockCount++;
|
||||
power = parsePowerBlock(block);
|
||||
|
||||
if (powers.containsKey(power.power_id))
|
||||
Logger.warn(block.location() + ": duplicate power " + power.power_id + "; the later entry wins");
|
||||
|
||||
powers.put(power.power_id, power);
|
||||
} catch (WpakFormatException e) {
|
||||
Logger.error(e.getMessage());
|
||||
failedCount++;
|
||||
} catch (Exception e) {
|
||||
Logger.error(block.location() + ": " + e);
|
||||
failedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.info("Loaded " + powers.size() + " of " + blockCount + " powers");
|
||||
|
||||
if (failedCount > 0)
|
||||
return null;
|
||||
|
||||
return powers;
|
||||
}
|
||||
|
||||
private static PowerTemplate parsePowerBlock(WpakBlock block) {
|
||||
|
||||
PowerTemplate power = new PowerTemplate();
|
||||
ArrayList<WpakLine> lines = block.lines();
|
||||
|
||||
if (lines.isEmpty())
|
||||
throw block.beginLine.error("power has no header line");
|
||||
|
||||
parseHeader(power, lines.get(0));
|
||||
|
||||
for (int i = 1; i < lines.size(); i++)
|
||||
parseKey(power, lines.get(i));
|
||||
|
||||
for (WpakBlock child : block.blocks()) {
|
||||
if (!child.name.equals("CONDITION"))
|
||||
throw child.beginLine.error("unexpected " + child.name + "BEGIN in a power");
|
||||
|
||||
for (WpakLine line : child.lines())
|
||||
power.conditions.add(EffectParser.parseCondition(line));
|
||||
}
|
||||
|
||||
return power;
|
||||
}
|
||||
|
||||
private static void parseHeader(PowerTemplate power, WpakLine header) {
|
||||
|
||||
if (header.size() != headerSize)
|
||||
throw header.error("power header has " + header.size() + " tokens, expected " + headerSize);
|
||||
|
||||
power.power_id = header.token(0);
|
||||
power.power_name = header.token(1);
|
||||
power.power_type = header.enumToken(mbEnums.PowerType.class, 2);
|
||||
power.icon_number = header.intToken(3);
|
||||
power.focus = header.token(4);
|
||||
power.target_type = header.enumToken(mbEnums.PowerTargetType.class, 5);
|
||||
power.range = header.floatToken(6);
|
||||
power.area_type = header.enumToken(mbEnums.AreaType.class, 7);
|
||||
power.area_range = header.floatToken(8);
|
||||
power.exclude_type = header.enumToken(mbEnums.ExcludeType.class, 9);
|
||||
power.cost_type = header.enumToken(mbEnums.CostType.class, 10);
|
||||
power.cost_amt = header.floatToken(11);
|
||||
power.difficulty = header.intToken(12);
|
||||
power.precision = header.intToken(13);
|
||||
power.init_time = header.floatToken(14);
|
||||
power.release_time = header.floatToken(15);
|
||||
power.recycle_time = header.floatToken(16);
|
||||
power.hit_roll_yn = header.boolToken(17);
|
||||
power.casting_mode = header.enumToken(mbEnums.CastingModeType.class, 18);
|
||||
power.init_anim = header.intToken(19);
|
||||
power.release_anim = header.intToken(20);
|
||||
power.target_sel = header.enumToken(mbEnums.TargetSelectType.class, 21);
|
||||
}
|
||||
|
||||
// The client compares keys case-insensitively (cfg has MaxLevel= and HateValue=)
|
||||
|
||||
private static void parseKey(PowerTemplate power, WpakLine line) {
|
||||
|
||||
String key = line.token(0).toUpperCase();
|
||||
|
||||
switch (key) {
|
||||
case "ACTION=":
|
||||
power.actions.add(parseAction(line));
|
||||
break;
|
||||
case "MAXLEVEL=":
|
||||
power.maxlevel = line.intToken(1);
|
||||
break;
|
||||
case "HATEVALUE=":
|
||||
power.hatevalue = line.floatToken(1);
|
||||
power.hatevalue_curve = line.has(2) ? WpakParserUtil.curveToken(line, 2) : null;
|
||||
break;
|
||||
case "GRANTOVERRIDEVAR=":
|
||||
power.grantoverridevar = line.token(1);
|
||||
break;
|
||||
case "CATEGORY=":
|
||||
power.category = line.enumToken(mbEnums.PowerCategoryType.class, 1);
|
||||
break;
|
||||
case "EQPREREQ=":
|
||||
parseEquipPrereqs(power, line);
|
||||
break;
|
||||
case "MONSTERTYPEPREREQS=":
|
||||
for (int i = 1; i < line.size(); i++)
|
||||
power.monstertypeprereqs.add(line.enumToken(mbEnums.MonsterType.class, i));
|
||||
break;
|
||||
case "MONSTERTYPERESTRICTS=":
|
||||
for (int i = 1; i < line.size(); i++)
|
||||
power.monstertyperestricts.add(line.enumToken(mbEnums.MonsterType.class, i));
|
||||
break;
|
||||
case "TARGETEFFECTPREREQS_ANDED=":
|
||||
parseEffectPrereqPairs(power.targeteffectprereqs_anded, line);
|
||||
break;
|
||||
case "TARGETEFFECTPREREQS_ORED=":
|
||||
parseEffectPrereqPairs(power.targeteffectprereqs_ored, line);
|
||||
break;
|
||||
case "EFFECTPREREQ=":
|
||||
power.effectprereqs.add(parseEffectPrereq(line));
|
||||
break;
|
||||
case "CURVE=":
|
||||
power.curves.put(line.token(1), WpakParserUtil.curveToken(line, 2));
|
||||
break;
|
||||
case "DESCRIPTION=":
|
||||
power.descriptions.add(line.token(1));
|
||||
break;
|
||||
case "LOOPANIMID=":
|
||||
power.loopanimid = line.intToken(1);
|
||||
break;
|
||||
case "BLADETRAILS=":
|
||||
power.bladetrails = line.optionalBoolToken(1);
|
||||
break;
|
||||
case "CANCASTWHILEMOVING=":
|
||||
power.cancastwhilemoving = line.optionalBoolToken(1);
|
||||
break;
|
||||
case "CANCASTWHILEFLYING=":
|
||||
power.cancastwhileflying = line.optionalBoolToken(1);
|
||||
break;
|
||||
case "SHOULDCHECKPATH=":
|
||||
power.shouldcheckpath = line.optionalBoolToken(1);
|
||||
break;
|
||||
case "ISADMINPOWER=":
|
||||
power.isadminpower = line.optionalBoolToken(1);
|
||||
break;
|
||||
case "CANTARGETADMIN=":
|
||||
power.cantargetadmin = line.optionalBoolToken(1);
|
||||
break;
|
||||
case "ISPROJECTILE=":
|
||||
power.isprojectile = line.optionalBoolToken(1);
|
||||
break;
|
||||
case "STICKY=":
|
||||
power.sticky = line.optionalBoolToken(1);
|
||||
break;
|
||||
case "MAXNUMMOBTARGETS=":
|
||||
power.maxnummobtargets = line.intToken(1);
|
||||
break;
|
||||
case "MAXNUMPLAYERTARGETS=":
|
||||
power.maxnumplayertargets = line.intToken(1);
|
||||
break;
|
||||
case "PULSEINFO=":
|
||||
power.pulseinfo_duration = line.floatToken(1);
|
||||
power.pulseinfo_iterations = line.intToken(2);
|
||||
break;
|
||||
case "SOUNDS=":
|
||||
power.sounds.addAll(line.intTokensFrom(1));
|
||||
break;
|
||||
case "CASTERSPULSEPARTICLE=":
|
||||
power.casterspulseparticle = line.intToken(1);
|
||||
break;
|
||||
case "INITSTRING=":
|
||||
power.initstring = line.token(1);
|
||||
break;
|
||||
case "SUCCESSSELF=":
|
||||
power.successself = line.token(1);
|
||||
break;
|
||||
case "SUCCESSOTHER=":
|
||||
power.successother = line.token(1);
|
||||
break;
|
||||
case "FIZZLESELF=":
|
||||
power.fizzleself = line.token(1);
|
||||
break;
|
||||
case "FIZZLEOTHER=":
|
||||
power.fizzleother = line.token(1);
|
||||
break;
|
||||
case "APPLYEFFECTSELF=":
|
||||
power.applyeffectself = line.token(1);
|
||||
break;
|
||||
case "APPLYEFFECTOTHER=":
|
||||
power.applyeffectother = line.token(1);
|
||||
break;
|
||||
case "WEAROFFEFFECTSELF=":
|
||||
power.wearoffeffectself = line.token(1);
|
||||
break;
|
||||
case "WEAROFFEFFECTOTHER=":
|
||||
power.wearoffeffectother = line.token(1);
|
||||
break;
|
||||
case "APPLYDAMAGECASTER=":
|
||||
power.applydamagecaster = line.token(1);
|
||||
break;
|
||||
case "APPLYDAMAGETARGET=":
|
||||
power.applydamagetarget = line.token(1);
|
||||
break;
|
||||
case "APPLYDAMAGEOTHER=":
|
||||
power.applydamageother = line.token(1);
|
||||
break;
|
||||
default:
|
||||
if (!clientIgnoredKeys.contains(key))
|
||||
throw line.error("unknown power key " + line.token(0));
|
||||
}
|
||||
}
|
||||
|
||||
private static PowerTemplate.ActionEntry parseAction(WpakLine line) {
|
||||
|
||||
PowerTemplate.ActionEntry action = new PowerTemplate.ActionEntry();
|
||||
|
||||
action.action_id = line.token(1);
|
||||
action.min_trains = line.intToken(2);
|
||||
action.max_trains = line.intToken(3);
|
||||
action.duration = line.floatToken(4);
|
||||
action.duration_curve = WpakParserUtil.curveToken(line, 5);
|
||||
action.stack_type = line.token(6);
|
||||
action.stack_order = line.intToken(7);
|
||||
action.stack_priority = line.enumToken(mbEnums.CategoryToPowerType.class, 8);
|
||||
action.args.addAll(line.tokens.subList(1, line.size()));
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
// Ten lines end in "LHELD Bow" with no flag; the client reads the missing flag as 0
|
||||
|
||||
private static void parseEquipPrereqs(PowerTemplate power, WpakLine line) {
|
||||
|
||||
PowerTemplate.EquipPrereq prereq;
|
||||
|
||||
for (int i = 1; i < line.size(); i += 3) {
|
||||
prereq = new PowerTemplate.EquipPrereq();
|
||||
prereq.slot = line.enumToken(mbEnums.EquipSlotType.class, i);
|
||||
prereq.skill = line.token(i + 1);
|
||||
prereq.required = line.optionalBoolToken(i + 2);
|
||||
power.eqprereqs.add(prereq);
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseEffectPrereqPairs(ArrayList<PowerTemplate.EffectPrereq> prereqs, WpakLine line) {
|
||||
|
||||
PowerTemplate.EffectPrereq prereq;
|
||||
|
||||
for (int i = 1; i < line.size(); i += 2) {
|
||||
prereq = new PowerTemplate.EffectPrereq();
|
||||
prereq.effect_id = line.token(i);
|
||||
prereq.value = line.intToken(i + 1);
|
||||
prereqs.add(prereq);
|
||||
}
|
||||
}
|
||||
|
||||
// The message is the rest of the line; two lines wrap it in doubled quotes (""text""),
|
||||
// which split it into words with empty tokens at each end
|
||||
|
||||
private static PowerTemplate.EffectPrereq parseEffectPrereq(WpakLine line) {
|
||||
|
||||
PowerTemplate.EffectPrereq prereq = new PowerTemplate.EffectPrereq();
|
||||
ArrayList<String> words = new ArrayList<>();
|
||||
|
||||
prereq.effect_id = line.token(1);
|
||||
prereq.value = line.intToken(2);
|
||||
|
||||
for (int i = 3; i < line.size(); i++)
|
||||
if (!line.token(i).isEmpty())
|
||||
words.add(line.token(i));
|
||||
|
||||
prereq.message = String.join(" ", words);
|
||||
|
||||
return prereq;
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,22 @@
|
||||
package engine.parsers.wpak;
|
||||
|
||||
import engine.parsers.cache.CacheParserUtil;
|
||||
import org.pmw.tinylog.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
// One non-comment line of a wpak cfg file, split into tokens.
|
||||
// Every accessor that fails names the file and line.
|
||||
|
||||
public class WpakLine {
|
||||
|
||||
// The client reads numbers like C atoi/atof: the leading number counts, the rest is dropped
|
||||
// (Powers.cfg has "2.9.0" for a float and "10.0" for an int)
|
||||
private static final Pattern intPrefix = Pattern.compile("[-+]?\\d+");
|
||||
private static final Pattern floatPrefix = Pattern.compile("[-+]?(\\d+\\.?\\d*|\\.\\d+)");
|
||||
|
||||
public final String fileName;
|
||||
public final int lineNumber;
|
||||
public final String text;
|
||||
@@ -56,23 +64,37 @@ public class WpakLine {
|
||||
public int intToken(int index) {
|
||||
|
||||
String value = token(index);
|
||||
Matcher prefix;
|
||||
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw error("token " + index + " '" + value + "' is not an integer");
|
||||
prefix = intPrefix.matcher(value);
|
||||
}
|
||||
|
||||
if (!prefix.lookingAt())
|
||||
throw error("token " + index + " '" + value + "' is not an integer");
|
||||
|
||||
Logger.warn(location() + ": token " + index + " '" + value + "' read as " + prefix.group() + ", as the client does");
|
||||
return Integer.parseInt(prefix.group());
|
||||
}
|
||||
|
||||
public float floatToken(int index) {
|
||||
|
||||
String value = token(index);
|
||||
Matcher prefix;
|
||||
|
||||
try {
|
||||
return Float.parseFloat(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw error("token " + index + " '" + value + "' is not a number");
|
||||
prefix = floatPrefix.matcher(value);
|
||||
}
|
||||
|
||||
if (!prefix.lookingAt())
|
||||
throw error("token " + index + " '" + value + "' is not a number");
|
||||
|
||||
Logger.warn(location() + ": token " + index + " '" + value + "' read as " + prefix.group() + ", as the client does");
|
||||
return Float.parseFloat(prefix.group());
|
||||
}
|
||||
|
||||
// The client reads a missing trailing number as 0 (String::ToInt on an empty token)
|
||||
|
||||
@@ -43,10 +43,12 @@ import engine.parsers.objects.wpak.CompoundCurveTemplate;
|
||||
import engine.parsers.objects.wpak.EffectTemplate;
|
||||
import engine.parsers.objects.wpak.ModTypeTemplate;
|
||||
import engine.parsers.objects.wpak.PowerActionTemplate;
|
||||
import engine.parsers.objects.wpak.PowerTemplate;
|
||||
import engine.parsers.wpak.CompoundCurveParser;
|
||||
import engine.parsers.wpak.EffectParser;
|
||||
import engine.parsers.wpak.ModTypeParser;
|
||||
import engine.parsers.wpak.PowerActionParser;
|
||||
import engine.parsers.wpak.PowerParser;
|
||||
import engine.powers.RunePowerEntry;
|
||||
import engine.powers.RuneSkillAdjustEntry;
|
||||
import engine.server.MBServerStatics;
|
||||
@@ -374,6 +376,14 @@ public class WorldServer {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Logger.info("Loading Powers");
|
||||
PowerTemplate._powers = PowerParser.parsePowersFile();
|
||||
|
||||
if (PowerTemplate._powers == null || PowerTemplate._powers.isEmpty()) {
|
||||
Logger.error("ABORT! Powers failed to load!");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Logger.info("Loading PromotionClasses");
|
||||
PromotionClass.LoadAllPromotions();
|
||||
|
||||
|
||||
Reference in new issue
Block a user