Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/Constants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ Constants.VFXType = {
-- Defense effects
SHIELD = "shield",
REFLECT = "reflect",
SHIELD_BREAK = "shield_break",

-- Spell timing effects
SPELL_ACCELERATE = "spell_accelerate",
Expand Down
1 change: 1 addition & 0 deletions docs/Visual_Language.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ The visual language is organized by:
| Generic | Acceleration | Slot | `SPELL_ACCELERATE` | sparkle | LIME | Speed-up animation |
| Generic | Cancel | Slot | `SPELL_CANCEL` | impactRing | CRIMSON | Spell interruption effect |
| Generic | Echo | Slot | `SPELL_ECHO` | sparkle | BONE | Spell replication effect |
| Generic | Shield Break | Self | `SHIELD_BREAK` | impactRing | SMOKE | Flash when a shield collapses |

## Shield Visual Language

Expand Down
1 change: 1 addition & 0 deletions docs/wizard.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Each `Wizard` instance maintains a comprehensive set of state variables:
* Shield Spells: Calls local `createShield` helper, potentially applies elevation, leaves slot active with tokens (`isShield=true`).
* Normal Spells: Applies damage (`target.health`), status effects (burn, stun), position changes (range, elevation), mana manipulation (lock, delay) based on `effect` table. Returns caster's tokens (`requestReturnAnimation`/`manaPool:returnToken`), resets caster's slot (`resetSpellSlot`).
* **`Wizard:handleShieldBlock(slotIndex, blockedSpell)`:** (Called on the target wizard). Consumes tokens from the specified shield slot based on `blockedSpell.shieldBreaker`, returns consumed tokens, and calls `resetSpellSlot` if the shield breaks (runs out of tokens).
* When a shield collapses from losing its last token, `EventRunner` plays the `SHIELD_BREAK` visual effect at the defender's position for clarity.
* **`Wizard:resetSpellSlot(slotIndex)`:** Utility function to reset all properties of a spell slot to default/inactive state and clear its token list. Used after normal casts, cancellations, or shield breaks.
* **`Wizard:canPayManaCost(costOrFn[, target])`:** Checks if a mana cost can be paid without consuming tokens. `costOrFn` may be a static table or the `getCost` function from a spell definition. The function returns a reservation detail table or `nil` if the cost can't currently be met.
* **`Wizard:freeAllSpells()`:** Cancels all active spells/shields, returns their tokens, and resets the corresponding slots.
Expand Down
17 changes: 17 additions & 0 deletions systems/TokenManager.lua
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,23 @@ function TokenManager.checkFizzleCondition(wizard, slotIndex, removedToken)
-- If slot has no tokens left, reset it
if not slot.tokens or #slot.tokens == 0 then
print("[TOKEN MANAGER] Spell in slot " .. slotIndex .. " fizzled - no tokens left")

if slot.isShield and wizard.gameState and wizard.gameState.eventRunner then
local EventRunner = require("systems.EventRunner")
local Constants = require("core.Constants")
local breakEvent = {
type = "EFFECT",
source = Constants.TargetType.TARGET,
target = Constants.TargetType.TARGET,
effectType = "shield_break",
shieldType = slot.defenseType,
vfxParams = { x = wizard.x, y = wizard.y },
rangeBand = wizard.rangeBand,
elevation = wizard.elevation,
}
wizard.gameState.eventRunner.processEvents({breakEvent}, wizard, nil)
end

wizard:resetSpellSlot(slotIndex)
return true
end
Expand Down
31 changes: 31 additions & 0 deletions systems/VisualResolver.lua
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,37 @@ function VisualResolver.pick(event)

return baseTemplate, opts
end

-- PRIORITY 3b: Handle shield break event
if event.effectType == "shield_break" then
baseTemplate = Constants.VFXType.SHIELD_BREAK
selectionPath = "SHIELD_BREAK"

VisualResolver.debug("Handling shield break effect")

local color = DEFAULT_COLOR
if event.shieldType == Constants.ShieldType.BARRIER then
color = {1.0, 1.0, 0.3, 1.0}
elseif event.shieldType == Constants.ShieldType.WARD then
color = {0.3, 0.3, 1.0, 1.0}
elseif event.shieldType == Constants.ShieldType.FIELD then
color = {0.3, 1.0, 0.3, 1.0}
end

local opts = {
color = color,
scale = 1.5,
motion = Constants.MotionStyle.PULSE,
addons = {},
rangeBand = event.rangeBand,
elevation = event.elevation,
}

VisualResolver.debug(string.format(
"Resolved shield break event to base=%s via %s", baseTemplate, selectionPath))

return baseTemplate, opts
end

-- PRIORITY 4: Legacy manual VFX: If the event has manualVfx flag and effectType
if event.manualVfx and event.effectType then
Expand Down
14 changes: 14 additions & 0 deletions vfx.lua
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,18 @@ function VFX.init()
sound = "shield", -- Use shield sound
criticalAssets = {"impactRing", "sparkle"} -- Assets needed for shield hit
},

shield_break = {
type = "shield_break",
duration = 1.0,
particleCount = 40,
startScale = 0.6,
endScale = 1.6,
color = Constants.Color.GRAY,
radius = 80,
sound = "shield_break",
criticalAssets = {"impactRing", "sparkle"}
},

-- Existing effects
-- General impact effect (used for many spell interactions)
Expand Down Expand Up @@ -1748,6 +1760,7 @@ VFX.updaters["conjure_base"] = ConjureEffect.update -- Conjuration template
VFX.updaters["remote_base"] = RemoteEffect.update -- Remote effect template
VFX.updaters["warp_base"] = RemoteEffect.update -- Warp uses remote logic
VFX.updaters["shield_hit_base"] = ImpactEffect.update -- Shield hit template
VFX.updaters["shield_break"] = ImpactEffect.update -- Shield break uses impact logic

-- Specific effect templates
VFX.updaters["meteor"] = MeteorEffect.update -- Meteor effect
Expand Down Expand Up @@ -1778,6 +1791,7 @@ VFX.drawers["conjure_base"] = ConjureEffect.draw -- Conjuration template
VFX.drawers["remote_base"] = RemoteEffect.draw -- Remote effect template
VFX.drawers["warp_base"] = RemoteEffect.draw -- Warp uses remote logic
VFX.drawers["shield_hit_base"] = ImpactEffect.draw -- Shield hit template
VFX.drawers["shield_break"] = ImpactEffect.draw -- Shield break template

-- Specific effect templates
VFX.drawers["meteor"] = MeteorEffect.draw -- Meteor effect
Expand Down
1 change: 1 addition & 0 deletions vfx/initializeParticles.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ local function initializeParticles(effect)
["remote_base"] = "remote",
["warp_base"] = "remote",
["shield_hit_base"] = "impact",
["shield_break"] = "impact",

-- Specific effect templates
["meteor"] = "meteor",
Expand Down