[CPLUS plus ex] SkillEffectModifier Base Class - Redacted-Rice/ITB-RedactedRiceExts GitHub Wiki
SkillEffectModifier Base Class
Base class for pilot skills that modify weapon damage and effects. Automatically intercepts skill effect creation to let you add, modify, or react to SpaceDamage objects.
Note: SkillEffectModifier inherits from SkillActive , so you can also use setupEffect() to register additional event handlers.
Basic Usage
local mySkill = cplus_plus_ex.baseClasses.SkillEffectModifier:new{
id = "MyDamageSkill",
shortName = "Bonus Dmg",
fullName = "Bonus Damage",
description = "Adds +1 damage to all attacks",
priority = 100, -- Optional: Lower values run first (default: 100)
modifiesKillDamage = true, -- Optional, default true. Set false to skip IsDeadly registration
}
-- Override modifySpaceDamage to modify weapon effects
function mySkill:modifySpaceDamage(source, attackingPawn, phase, spaceDamage, indexes, targetPawn)
local newDamage = self:modifyKillDamage(source, attackingPawn, spaceDamage, indexes, targetPawn, spaceDamage.iDamage)
if newDamage == spaceDamage.iDamage then
return
end
if source == self.SOURCE_ATTACKER then
spaceDamage.iDamage = newDamage
-- Side effects (icons, sScript, etc.) only belong here, not in modifyKillDamage
end
end
-- No side effects - used for Board:IsDeadly / GetModifiedDamage preview
function mySkill:modifyKillDamage(source, attackingPawn, spaceDamage, indexes, targetPawn, currentDamage)
if source == self.SOURCE_ATTACKER then
return currentDamage + 1
end
return currentDamage
end
function mod:init()
cplus_plus_ex:registerSkill("Custom", mySkill)
end
Methods to Override
modifySpaceDamage
modifySpaceDamage(source, attackingPawn, phase, spaceDamage, indexes, targetPawn)
Called for each SpaceDamage during skill/final effect builds. Mutate spaceDamage in place. Do not return anything (returns are ignored).
Parameters:
source(string): EitherSOURCE_ATTACKERorSOURCE_TARGETSOURCE_ATTACKER: The attacking pawn's pilot has this skillSOURCE_TARGET: The target pawn's pilot (atspaceDamage.locafter push/move tracking) has this skill
attackingPawn(Pawn): The pawn using the weaponphase(number): One ofcplus_plus_ex.damageModifierLib.PHASE_*(see Phases below)spaceDamage(SpaceDamage): The damage object being processed - modify this directlyindexes(table): Array of skill slot numbers where this skill appears (e.g.,{1},{2}, or{1, 2})targetPawn(Pawn or nil): The pawn atspaceDamage.loc, if any
Example - Modifying existing damage:
function mySkill:modifySpaceDamage(source, attackingPawn, phase, spaceDamage, indexes, targetPawn)
if source == self.SOURCE_ATTACKER then
spaceDamage.iDamage = spaceDamage.iDamage * 2
end
end
To add new SpaceDamages after the full pass, override SkillEffectEvaluated (not modifySpaceDamage).
modifyKillDamage
modifyKillDamage(source, attackingPawn, spaceDamage, indexes, targetPawn, currentDamage)
Called from DamageModifierLib / Board:IsDeadly preview (and typically from your modifySpaceDamage for consistent totals).
Returns: Adjusted damage total (number). No side effects (no icons, sScript, etc.).
Default implementation returns currentDamage unchanged.
Set modifiesKillDamage = false on the skill table to skip onEvaluatingDeadly registration entirely.
function mySkill:modifyKillDamage(source, attackingPawn, spaceDamage, indexes, targetPawn, currentDamage)
if source == self.SOURCE_ATTACKER and spaceDamage.iDamage > 0 then
return currentDamage + 1
end
return currentDamage
end
SkillEffectEvaluated
SkillEffectEvaluated(phase)
Optional. Called after all SpaceDamages in a pass have been processed by all modifiers.
Returns: nil, a single SpaceDamage, or an array of SpaceDamage to append. Derived effects are modifier-processed again until the chain settles (see DamageModifierLib settle before add behavior). Avoid infinite loops.
function mySkill:SkillEffectEvaluated(phase)
-- After seeing the full pass, queue a follow-up effect
return SpaceDamage(Point(0, 0), 1)
end
setupEffect()
Optional override. Inherits from SkillActive. Use this if you need to register additional event handlers beyond weapon modification.
Example:
function mySkill:setupEffect()
-- Call parent to register with weapon modification system
cplus_plus_ex.baseClasses.SkillEffectModifier.setupEffect(self)
-- Add custom event handlers
table.insert(self.events, modApi.events.onMissionStart:subscribe(function()
LOG("Mission started!")
end))
end
Helper Methods
:getPawnAt(loc)
Gets the pawn at a location, accounting for pending moves tracked by DamageModifierLib during the weapon preview. Prefer this over Board:GetPawn when checking tiles during skill builds.
Parameters:
loc(Point): Location to check
Returns: Pawn at that location, or nil
Example:
function mySkill:modifySpaceDamage(source, attackingPawn, phase, spaceDamage, indexes, targetPawn)
for dir = DIR_START, DIR_END do
local adjLoc = spaceDamage.loc + DIR_VECTORS[dir]
local adjPawn = self:getPawnAt(adjLoc)
if adjPawn and adjPawn:IsEnemy() then
-- Found an adjacent enemy
end
end
end
Note: You usually don't need to call this directly since targetPawn is provided. Use it when checking tiles other than spaceDamage.loc.
:getPawnSpace(pawn)
Returns the current tracked board space for pawn during a skill build (DamageModifierLib move tracking). Useful when the pawn has already moved as part of the effect.
Parameters:
pawn(Pawn): Pawn to resolve
Returns: Point (or lib-defined equivalent) for the pawn's tracked space
Constants
Source Constants
Indicate which pawn has the skill:
SkillEffectModifier.SOURCE_ATTACKER = "attacker"- Attacking pawn has the skillSkillEffectModifier.SOURCE_TARGET = "target"- Target pawn has the skill
Phase Constants
Indicate the current weapon preview phase. These live on DamageModifierLib, not on SkillEffectModifier:
cplus_plus_ex.damageModifierLib.PHASE_NONE = 0cplus_plus_ex.damageModifierLib.PHASE_SKILL_EFFECT = 1cplus_plus_ex.damageModifierLib.PHASE_TARGET_AREA = 2cplus_plus_ex.damageModifierLib.PHASE_QUEUED_SKILL = 3cplus_plus_ex.damageModifierLib.PHASE_SECOND_TARGET_AREA = 4cplus_plus_ex.damageModifierLib.PHASE_FINAL_EFFECT = 5cplus_plus_ex.damageModifierLib.PHASE_QUEUED_FINAL_EFFECT = 6
SkillEffectModifier only defines source constants (SOURCE_ATTACKER / SOURCE_TARGET).
Priority System
Skills are processed in order of their priority value (lower runs first). Default is 100.
This allows for predictable ordering and interaction of skills. For example this can allow all +dmg skills to run before any skill that multiplies damage.
Example priorities:
- 50: Run early (before most skills)
- 80: Before damage doubling
- 100: Default (runs with most skills)
- 180: After kill effects
Set priority when creating your skill:
local mySkill = cplus_plus_ex.baseClasses.SkillEffectModifier:new{
id = "MySkill",
shortName = "My Skill",
fullName = "My Skill",
description = "Does something",
priority = 50 -- Run before other skills
}
Skill Definition Properties
Registering with SkillEffectModifier:new{}, you can include any of the standard properties as supported by Skill Registration . See that page for full details on supported properties.
How It Works Internally
SkillEffectModifier does not walk skill effects itself. Effect walking, push/move tracking, chaining, and Board:IsDeadly preview live in DamageModifierLib.
On setupEffect(), this class bridges CPLUS+ pilot-skill state into that lib by subscribing to:
damageModifierLib.events.onSkillEffectModify-> callsmodifySpaceDamage(...)damageModifierLib.events.onEvaluatingDeadly-> callsmodifyKillDamage(...)(unlessmodifiesKillDamage = false)damageModifierLib.events.onSkillEffectEvaluated-> callsSkillEffectEvaluated(phase)and appends returned damages
Subscriptions use the skill's priority (lower runs first). clearEvents() unsubscribes those handlers and then clears SkillActive events.
You don't need these internals to use the base class; they explain why getPawnAt / settle-before-add behavior comes from the lib.
Common Pitfalls
-
Checking source incorrectly: Always check if
source == self.SOURCE_ATTACKERorsource == self.SOURCE_TARGETto know which pawn has the skill. -
Not checking damage type: Check
spaceDamage.iDamage > 0andspaceDamage.iDamage ~= DAMAGE_ZEROto avoid processing non-damage effects. -
Forgetting nil checks:
targetPawncan be nil if there's no pawn at the damage location. Always check before using it. -
Using Board:GetSpace: This will not account for pushed pawns
-
Priority matters: If your skill depends on another skill's modifications, set appropriate priorities.