Files
prestonbane/src/engine/parsers/wpak/EffectParser.java
T

224 lines
8.1 KiB
Java
Raw Normal View History

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.parsers.wpak;
import engine.mbEnums;
import engine.parsers.objects.wpak.EffectTemplate;
import engine.parsers.objects.wpak.ModTypeTemplate;
import org.pmw.tinylog.Logger;
import java.util.ArrayList;
import java.util.HashMap;
// Loads Effects.cfg. ModTypes.cfg and CompoundCurves.cfg must be loaded first:
// a mod's argument layout comes from its behavior, and curve names are checked.
public class EffectParser {
private static final String fileName = "Effects.cfg";
public static HashMap<String, EffectTemplate> parseEffectsFile() {
HashMap<String, EffectTemplate> effects = new HashMap<>();
WpakBlock root;
EffectTemplate effect;
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 EFFECTBEGIN/END");
failedCount++;
}
for (WpakBlock block : root.blocks()) {
try {
if (!block.name.equals("EFFECT"))
throw block.beginLine.error("unexpected " + block.name + "BEGIN");
blockCount++;
effect = parseEffectBlock(block);
if (effects.containsKey(effect.effect_id))
Logger.warn(block.location() + ": duplicate effect " + effect.effect_id + "; the later entry wins");
effects.put(effect.effect_id, effect);
} catch (WpakFormatException e) {
Logger.error(e.getMessage());
failedCount++;
} catch (Exception e) {
Logger.error(block.location() + ": " + e);
failedCount++;
}
}
Logger.info("Loaded " + effects.size() + " of " + blockCount + " effects");
if (failedCount > 0)
return null;
return effects;
}
private static EffectTemplate parseEffectBlock(WpakBlock block) {
EffectTemplate effect = new EffectTemplate();
ArrayList<WpakLine> lines = block.lines();
WpakLine header;
if (lines.isEmpty())
throw block.beginLine.error("effect has no header line");
header = lines.get(0);
effect.effect_id = header.token(0);
effect.effect_name = header.token(1);
effect.icon = header.optionalIntToken(2);
for (int i = 1; i < lines.size(); i++)
parseKey(effect, lines.get(i));
for (WpakBlock child : block.blocks()) {
switch (child.name) {
case "SOURCE":
for (WpakLine line : child.lines())
effect.sources.add(line.token(0));
break;
case "MODS":
for (WpakLine line : child.lines())
effect.mods.add(parseMod(line));
break;
case "CONDITION":
for (WpakLine line : child.lines())
effect.conditions.add(parseCondition(line));
break;
default:
throw child.beginLine.error("unexpected " + child.name + "BEGIN in an effect");
}
if (!child.blocks().isEmpty())
throw child.blocks().get(0).beginLine.error("nested block in " + child.name);
}
return effect;
}
// The client compares these case-insensitively (cfg has IsItemEffect, client ISITEMEFFECT)
private static void parseKey(EffectTemplate effect, WpakLine line) {
switch (line.token(0).toUpperCase()) {
case "ISITEMEFFECT":
effect.isitemeffect = true;
break;
case "DONTSAVE":
effect.dontsave = true;
break;
case "IGNORENOMOD":
effect.ignorenomod = true;
break;
case "ISSPIREEFFECT":
effect.isspireeffect = true;
break;
default:
throw line.error("unknown effect key " + line.token(0));
}
}
private static EffectTemplate.ModEntry parseMod(WpakLine line) {
EffectTemplate.ModEntry mod = new EffectTemplate.ModEntry();
ModTypeTemplate modType = ModTypeTemplate._modTypes.get(line.token(0));
if (modType == null)
throw line.error("mod type " + line.token(0) + " is not in ModTypes.cfg");
mod.mod_type = line.enumToken(mbEnums.ModType.class, 0);
mod.behavior = modType.behavior;
mod.args.addAll(line.tokens.subList(1, line.size()));
switch (mod.behavior) {
case "Flag":
break;
case "Standard":
parseStandard(mod, line);
break;
case "FPSubTypeAttr":
case "FPSubTypeSkill":
case "FPSubTypeDmg":
case "FPSubTypeMonster":
parseStandard(mod, line);
mod.type = line.token(4);
break;
case "DD":
mod.min_mod = line.floatToken(1);
mod.max_mod = line.floatToken(2);
mod.percent_mod = line.floatToken(3);
mod.curve = WpakParserUtil.curveToken(line, 5);
mod.type = line.token(6);
break;
case "ProcInfo":
mod.min_mod = line.floatToken(1);
mod.proc_power_id = line.token(2);
mod.percent_mod = line.floatToken(3);
break;
case "SubTypeSourceType":
case "SubTypePowerType":
mod.type = line.token(1);
mod.string1 = line.has(2) ? String.join(" ", line.tokens.subList(2, line.size())) : null;
break;
case "SubTypeSkill":
case "SubTypeDmg":
case "SubTypeMod":
case "SubTypePower":
mod.type = line.token(1);
break;
case "MapIntToInts":
mod.map_key = line.intToken(1);
mod.map_values.addAll(line.intTokensFrom(2));
break;
case "String":
mod.string1 = line.token(1);
mod.string2 = line.token(2);
break;
default:
throw line.error("unknown mod behavior " + mod.behavior + " for " + line.token(0));
}
return mod;
}
// Tokens after the curve are ignored by the client (some lines end in "Unknown")
private static void parseStandard(EffectTemplate.ModEntry mod, WpakLine line) {
mod.min_mod = line.floatToken(1);
mod.percent_mod = line.floatToken(2);
mod.curve = WpakParserUtil.curveToken(line, 3);
}
2026-10-22 11:19:14 -04:00
static EffectTemplate.ConditionEntry parseCondition(WpakLine line) {
EffectTemplate.ConditionEntry condition = new EffectTemplate.ConditionEntry();
condition.condition = line.enumToken(mbEnums.PowerFailCondition.class, 0);
condition.amount = line.floatToken(1);
condition.curve = line.has(2) ? WpakParserUtil.curveToken(line, 2) : null;
for (int i = 3; i < line.size(); i++)
condition.damage_types.add(line.enumToken(mbEnums.DamageType.class, i));
return condition;
}
}