forked from MagicBane/Server
257 lines
10 KiB
Java
257 lines
10 KiB
Java
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|||
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||
|
|
// 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));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|