Files
Server/src/engine/powers/ActionsBase.java
T
MagicBotandClaude Opus 5.5 2a19a85108 Build power action lists from Powers.cfg ACTION= lines
ActionsBase(ActionEntry, powerID, id, powerAction) with the duration ramp
from CurveRamp; PowersManager.loadActionLists fills PowersBase.actions in
Powers.cfg order (ids 1..2278 + 10000), then the OPENGATE actions
(5001-5008, unchanged). StackType gains the 10 cfg stack types it lacked
and loses the DB-only ResistanceDebuff spelling.

Parity (JSON-Refactor-Plan/scripts/p49.py, dev export, every column):
2287 DB rows vs 2278 cfg lines; 1453/1455 powers have the same action
list; all curves have a ramp form, every action id exists.

Value changes (cfg wins):
- TRA-003..010 lose their DB OPENGATE row; the synthetic OPENGATE action
  was already added, so each gate power applied OPENGATE twice
- HFG-001 loses the DB-only third action HFG-001B (Str +25, attack
  delay -15, melee damage +15)
- SCT-004 applies SCT-004A (Speed SL0125Up, Buff/Effect) instead of
  RNG-033A (Speed SL0100Up, also Chant)
- durations: ALF-001A 180->60, COM-001A 60->30, FGM-002A/003A
  1800->900, RNG-033A 60->30
- stack priority: DEF/OFF/PRE/NOSTANCE A and B Always->GreaterThanOrEqualTo,
  BRD-016A and PRS-031A GreaterThan->GreaterThanOrEqualTo
- stack types: the 16 resist debuffs split from ResistanceDeBuff/
  ResistanceDebuff into Cold/Fire/Lightning/Holy/Unholy/Magic/Mental
  DeBuff (FUR-029A IgnoreStack->ElementalDeBuff), so debuffs of different
  elements now stack; HNT-023A..C StackableAttr*Buff->PersonalAttr*Buff
- IgnoreStack effect keys: action line ids follow cfg order (runtime only)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 10:20:34 -04:00

313 lines
10 KiB
Java

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.powers;
import engine.gameManager.DbManager;
import engine.gameManager.PowersManager;
import engine.mbEnums;
import engine.mbEnums.ModType;
import engine.mbEnums.SourceType;
import engine.mbEnums.StackType;
import engine.objects.*;
import engine.parsers.objects.wpak.CurveRamp;
import engine.parsers.objects.wpak.PowerTemplate;
import engine.powers.poweractions.AbstractPowerAction;
import org.pmw.tinylog.Logger;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
public class ActionsBase {
// Effect maps key both effects and IgnoreStack action lines by Integer.toString(id); keep the ranges apart
private static final int ACTION_ID_OFFSET = 10000;
public int UUID;
public String IDString;
public String effectID;
public int minTrains;
public int maxTrains;
public float duration;
public float ramp;
public boolean addFormula;
public String stackType;
public StackType stackTypeType;
public int stackOrder;
public boolean greaterThanEqual = false;
public boolean always = false;
public boolean greaterThan = false;
public String stackPriority;
private AbstractPowerAction powerAction;
/**
* No Table ID Constructor
*/
public ActionsBase() {
}
/**
* ResultSet Constructor
*/
public ActionsBase(ResultSet rs, HashMap<String, AbstractPowerAction> apa) throws SQLException {
this.UUID = rs.getInt("ID") + ACTION_ID_OFFSET;
this.IDString = rs.getString("powerID");
this.effectID = rs.getString("effectID");
this.minTrains = rs.getInt("minTrains");
this.maxTrains = rs.getInt("maxTrains");
this.duration = rs.getFloat("duration");
this.ramp = rs.getFloat("ramp");
this.addFormula = (rs.getInt("useAddFormula") == 1) ? true : false;
this.stackType = rs.getString("stackType");
this.stackTypeType = StackType.GetStackType(this.stackType);
this.stackOrder = rs.getInt("stackOrder");
this.stackPriority = rs.getString("stackPriority");
switch (stackPriority) {
case "GreaterThanOrEqualTo":
this.greaterThanEqual = true;
break;
case "Always":
this.always = true;
break;
case "GreaterThan":
this.greaterThan = true;
break;
}
this.powerAction = apa.get(this.effectID);
}
public ActionsBase(PowerTemplate.ActionEntry actionEntry, String powerID, int id, AbstractPowerAction powerAction) {
CurveRamp durationRamp = CurveRamp.fromCurve(actionEntry.duration_curve);
if (durationRamp == null) {
Logger.warn("Power " + powerID + " action " + actionEntry.action_id + " duration curve "
+ actionEntry.duration_curve + " has no ramp form");
durationRamp = CurveRamp.NONE;
}
this.UUID = id + ACTION_ID_OFFSET;
this.IDString = powerID;
this.effectID = actionEntry.action_id;
this.minTrains = actionEntry.min_trains;
this.maxTrains = actionEntry.max_trains;
this.duration = actionEntry.duration;
this.ramp = durationRamp.ramp;
this.addFormula = durationRamp.useAdd;
this.stackType = actionEntry.stack_type;
this.stackTypeType = StackType.GetStackType(this.stackType);
this.stackOrder = actionEntry.stack_order;
this.stackPriority = actionEntry.stack_priority.name();
switch (actionEntry.stack_priority) {
case GreaterThanOrEqualTo:
this.greaterThanEqual = true;
break;
case Always:
this.always = true;
break;
case GreaterThan:
this.greaterThan = true;
break;
}
this.powerAction = powerAction;
}
protected ActionsBase(int uUID, String effectID, int minTrains, int maxTrains, float duration, float ramp,
boolean addFormula, String stackType, int stackOrder, boolean greaterThanEqual, boolean always,
boolean greaterThan, AbstractPowerAction powerAction) {
super();
UUID = uUID;
this.effectID = effectID;
this.minTrains = minTrains;
this.maxTrains = maxTrains;
this.duration = duration;
this.ramp = ramp;
this.addFormula = addFormula;
this.stackType = stackType;
this.stackTypeType = StackType.GetStackType(this.stackType);
if (this.stackTypeType == null)
Logger.info("Invalid Stack Type " + this.stackTypeType + " for " + this.effectID);
this.stackOrder = stackOrder;
this.greaterThanEqual = greaterThanEqual;
this.always = always;
this.greaterThan = greaterThan;
this.powerAction = powerAction;
if (this.greaterThanEqual)
this.stackPriority = "GreaterThanOrEqualTo";
else if (this.always)
this.stackPriority = "Always";
else if (this.greaterThan)
this.stackPriority = "GreaterThan";
}
// public static ArrayList<ActionsBase> getActionsBase(String ID) {
// PreparedStatementShared ps = null;
// ArrayList<ActionsBase> out = new ArrayList<ActionsBase>();
// try {
// ps = new PreparedStatementShared("SELECT * FROM actions where powerID = ?");
// ps.setString(1, ID);
// ResultSet rs = ps.executeQuery();
// while (rs.next()) {
// ActionsBase toAdd = new ActionsBase(rs);
// out.add(toAdd);
// }
// rs.close();
// } catch (Exception e) {
// Logger.error("ActionsBase", e);
// } finally {
// ps.release();
// }
// return out;
// }
public static void getActionsBase(HashMap<String, PowersBase> powers, HashMap<String, AbstractPowerAction> apa) {
try (Connection connection = DbManager.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM static_power_action")) {
ResultSet rs = preparedStatement.executeQuery();
String IDString;
ActionsBase toAdd;
PowersBase pb;
while (rs.next()) {
IDString = rs.getString("powerID");
pb = powers.get(IDString);
if (pb != null) {
toAdd = new ActionsBase(rs, apa);
pb.getActions().add(toAdd);
}
}
rs.close();
} catch (Exception e) {
Logger.error(e.toString());
}
addOpenGateActions(powers);
}
public static void addOpenGateActions(HashMap<String, PowersBase> powers) {
int gateID = 5000;
for (String IDString : Runegate.GetAllOpenGateIDStrings()) {
gateID++;
ActionsBase openGateActionBase = new ActionsBase(gateID, "OPENGATE", 5, 9999, 0, 0, true, "IgnoreStack", 0, true, false, false, PowersManager.getPowerActionByIDString("OPENGATE"));
PowersBase openGatePower = powers.get(IDString);
if (openGatePower == null) {
Logger.error("no powerbase for action " + IDString);
break;
}
openGatePower.getActions().add(openGateActionBase);
}
}
public int getUUID() {
return this.UUID;
}
public String getEffectID() {
return this.effectID;
}
public int getMinTrains() {
return this.minTrains;
}
public int getMaxTrains() {
return this.maxTrains;
}
public float getDuration() {
return this.duration;
}
public AbstractPowerAction getPowerAction() {
return this.powerAction;
}
public int getDuration(int trains) {
if (this.addFormula)
return (int) ((this.duration + (this.ramp * trains)) * 1000);
else
return (int) ((this.duration * (1 + (this.ramp * trains))) * 1000);
}
public float getDurationAsFloat(int trains) {
if (this.addFormula)
return ((this.duration + (this.ramp * trains)) * 1000);
else
return ((this.duration * (1 + (this.ramp * trains))) * 1000);
}
public int getDurationInSeconds(int trains) {
if (this.addFormula)
return (int) (this.duration + (this.ramp * trains));
else
return (int) (this.duration * (1 + (this.ramp * trains)));
}
public String getStackType() {
return this.stackType;
}
public int getStackOrder() {
return this.stackOrder;
}
public boolean greaterThanEqual() {
return this.greaterThanEqual;
}
public boolean greaterThan() {
return this.greaterThan;
}
public boolean always() {
return this.always;
}
//Add blocked types here
public boolean blocked(AbstractWorldObject awo, boolean vampDrain) {
//Check for immunities
if (AbstractCharacter.IsAbstractCharacter(awo)){
AbstractCharacter pcTarget = (AbstractCharacter) awo;
PlayerBonuses bonus = pcTarget.getBonuses();
if(vampDrain)
return bonus.getBool(ModType.BlockedPowerType, SourceType.VAMPDRAIN);
if ((this.stackType.equals("Flight") && bonus.getBool(ModType.NoMod, SourceType.Fly)) ||
(this.stackType.equals("Track") && bonus.getBool(ModType.CannotTrack, SourceType.None))) {
return true;
}
mbEnums.DamageType damageType = mbEnums.DamageType.getDamageType(this.stackType.toUpperCase());
return pcTarget.getResists().immuneTo(damageType);
}
return false;
}
}