schedule
The storm's clock. Every strike is a pure function of the storm phase, so the storm graph (as expressions over its own time) and the director (as Luau over the generator's clock) agree exactly, and the whole storm loops every `PERIOD` seconds.
schedule
The storm's clock. Every strike is a pure function of the storm phase, so the storm graph (as
expressions over its own time) and the director (as Luau over the generator's clock) agree exactly,
and the whole storm loops every PERIOD seconds.
Two assets require it: the storm graph beside it (storm.procGraph, which builds its flash, bolt and burst
expressions from STRIKES, flashExpr, envelopeExpr and windowOne) and the StormDirector
component of the Procgen Effects Showcase world (lights, materials and thunder from phase, envelope, afterglow and STRIKES).
Exports
Values
-
M.PERIOD:13.6, the loop length in seconds. -
M.OFFSET:5.006, the phase the storm is in at graph time 0. A strike has just landed there, so a paused frame is a hero frame. -
M.STRIKES: the four strikes, each{ t, kind, target, top, variant, weight }.tis the phase the strike lands at,targetwhere it lands,topwhere the bolt starts, andweightits brightness weight.kindis"ground"(throws sparks and smoke where it hits) or"tower"(hits the finial).targetandtopare in the storm graph's own space, where the tower stands at the origin; in the world they stand where the entity playing the graph does.tkindtargettopvariantweight1.6 ground { 15, 0, -8 }{ 3, 135, -2 }1 1.0 5.0 tower { 0.05, 48.6, 0.1 }{ -7, 135, 5 }2 1.2 8.6 ground { -19, 0, 7 }{ -30, 135, 12 }3 1.0 11.0 tower { 0.05, 48.6, 0.1 }{ 9, 135, 6 }4 1.3 -
M.PULSES: the return strokes of a strike, each{ delay after the leader, strength, decay rate per second }:{ 0.00, 1.00, 30 },{ 0.11, 0.80, 26 },{ 0.24, 0.62, 20 },{ 0.43, 0.36, 11 }.
Functions
M.phase(t): the storm phase at graph timet,(t + OFFSET) % PERIOD.M.envelope(ph, s): how bright strikesis at phaseph: 0 before the strike, about 1 at the return stroke. It sums every pulse whose delay has passed, each decaying exponentially at its rate.M.afterglow(ph, s): the cloud glow that lingers after strikes, at phaseph:0.55 * exp(-d * 1.7) * min(d * 20, 1)fordseconds after the strike, 0 before it.M.envelopeExpr(s): the same envelope asenvelope, written as an anim expression string overt.M.afterglowExpr(s): the same afterglow asafterglow, as an expression string overt.M.flashExpr(): the scene-wide flash as an expression string: the sum of every strike's envelope times its weight, capped at 3.M.windowExpr(only, lag, rise, fall): a burst of activitylagseconds after each strike, as a bump expression in [0, 1]: it rises withmin(d * rise, 1)and falls withexp(-d * fall).onlylimits it to strikes of one kind ("ground"or"tower");nilincludes every strike.M.windowOne(s, lag, rise, fall): the same bump for strikesalone.
Usage
local S = require("~.storm.schedule")
local generator = entity.find("storm").component("Generator")
local ph = S.phase(generator:getTime())
local flash = 0
for _, strike in ipairs(S.STRIKES) do
flash += S.envelope(ph, strike) * strike.weight
end
-- the same flash, as an anim expression for a procGraph
local flashExpr = proc.anim.expr(S.flashExpr())
-- a burst 0.15 s after each ground strike
local bursts = S.windowExpr("ground", 0.15, 12, 1.0)
Scoped to this part · feeds back into the world's score.