83 lines
2.2 KiB
Go
83 lines
2.2 KiB
Go
package pncdsl
|
|
|
|
// Trigger fires Do when its When condition first becomes true after the
|
|
// trigger arms (on scene enter). Rising-edge semantics: the Do action is
|
|
// queued only on a false→true transition, so a condition that becomes
|
|
// true and stays true fires exactly once per arming. Once=true makes the
|
|
// trigger fire at most once per arming regardless of further toggles.
|
|
type Trigger struct {
|
|
Name string
|
|
When Condition
|
|
Do Action
|
|
Once bool
|
|
}
|
|
|
|
// triggerState is the per-trigger bookkeeping for the active scene.
|
|
// Cleared on scene enter and on Load.
|
|
type triggerState struct {
|
|
lastTrue bool
|
|
fired bool
|
|
}
|
|
|
|
// resetTriggers re-arms every trigger on the current scene — called on
|
|
// scene change and after Load. Triggers are not pre-armed: the first
|
|
// frame samples their condition fresh, so a Flag that's already true
|
|
// will fire the trigger on the rising-edge of the FIRST sample (we treat
|
|
// "no prior sample" as false).
|
|
func (g *Game) resetTriggers() {
|
|
g.triggerStates = make(map[string]*triggerState)
|
|
if g.currentScene == "" {
|
|
return
|
|
}
|
|
s, ok := g.SceneManager.Get(g.currentScene)
|
|
if !ok {
|
|
return
|
|
}
|
|
for _, t := range s.Triggers {
|
|
if t.Name == "" {
|
|
continue
|
|
}
|
|
g.triggerStates[t.Name] = &triggerState{}
|
|
}
|
|
}
|
|
|
|
// tickTriggers evaluates every armed trigger on the current scene. The
|
|
// engine calls this only when the script slot is idle (so we never
|
|
// preempt a running cutscene). The first matching rising-edge fires the
|
|
// trigger's Do action and consumes the script slot for this frame.
|
|
func (g *Game) tickTriggers() {
|
|
if g.scriptRunner != nil || g.currentScene == "" {
|
|
return
|
|
}
|
|
s, ok := g.SceneManager.Get(g.currentScene)
|
|
if !ok {
|
|
return
|
|
}
|
|
ctx := g.makeCtx()
|
|
for _, t := range s.Triggers {
|
|
if t.Name == "" || t.When == nil {
|
|
continue
|
|
}
|
|
st := g.triggerStates[t.Name]
|
|
if st == nil {
|
|
st = &triggerState{}
|
|
g.triggerStates[t.Name] = st
|
|
}
|
|
if st.fired && t.Once {
|
|
continue
|
|
}
|
|
now := t.When.Eval(ctx)
|
|
// Rising edge: was false, now true.
|
|
if now && !st.lastTrue {
|
|
st.fired = true
|
|
st.lastTrue = true
|
|
if t.Do != nil {
|
|
g.queueAction(t.Do, "trigger "+t.Name)
|
|
return // one trigger per frame keeps ordering deterministic
|
|
}
|
|
continue
|
|
}
|
|
st.lastTrue = now
|
|
}
|
|
}
|