aboutsummaryrefslogtreecommitdiff
path: root/internal/game/act_interaction_seq.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/act_interaction_seq.go')
-rw-r--r--internal/game/act_interaction_seq.go97
1 files changed, 97 insertions, 0 deletions
diff --git a/internal/game/act_interaction_seq.go b/internal/game/act_interaction_seq.go
new file mode 100644
index 0000000..8b515c4
--- /dev/null
+++ b/internal/game/act_interaction_seq.go
@@ -0,0 +1,97 @@
+package game
+
+import (
+ "thehouseoficarus/internal/behavior"
+ "thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/player"
+)
+
+// interactionSeqData is the payload carried by p.Action.Data for an in-flight
+// delayed-message interaction sequence (on_use, on_look, on_kill, or
+// on_traverse). Messages are emitted one-by-one with per-message delays; the
+// trailing StepAction effects fire only after every message has been shown. If
+// the player is interrupted (move, quit, new action, combat) cancelAction
+// clears the pending Action and the trailing effects never run.
+type interactionSeqData struct {
+ Msgs []behavior.DelayedMessage
+ Idx int
+ Step *behavior.StepAction
+ RoomID int
+ Scope effectScope
+ FlagVal any
+}
+
+// startInteractionSeq fires the first delayed message (on the next tick,
+// matching the trigger scheduler's "delay = ticks to wait before this step
+// fires" rule with a floor of 1 so the first message ALWAYS arrives next tick
+// even when delay is 0). The trailing non-message effects run once the last
+// message has been emitted.
+func (g *Game) startInteractionSeq(sess *net.Session, p *player.Player, step *behavior.StepAction, roomID int, sc effectScope, flagValue any) {
+ g.cancelAction(p)
+
+ firstDelay := 0
+ if len(step.Messages) > 0 {
+ firstDelay = step.Messages[0].Delay
+ }
+ if firstDelay < 1 {
+ firstDelay = 1
+ }
+
+ // Strip Messages from step before stashing — applyStepAction would otherwise
+ // re-emit them when the trailing effects run.
+ clone := *step
+ clone.Messages = nil
+
+ p.Action = &behavior.Action{
+ Type: behavior.TypeInteractionSeq,
+ WaitLeft: firstDelay,
+ Data: &interactionSeqData{
+ Msgs: step.Messages,
+ Idx: 0,
+ Step: &clone,
+ RoomID: roomID,
+ Scope: sc,
+ FlagVal: flagValue,
+ },
+ }
+}
+
+// advanceInteractionSeq is the per-tick handler for TypeInteractionSeq
+// actions. Each invocation drains any zero-delay messages that are ready, emits
+// the current delayed message, then sets the wait for the next message (if any).
+// After the last message the trailing effects fire and the action is cancelled.
+func (g *Game) advanceInteractionSeq(sess *net.Session, p *player.Player) {
+ d, ok := p.Action.Data.(*interactionSeqData)
+ if !ok {
+ g.cancelAction(p)
+ return
+ }
+
+ var playerName string
+ if p != nil {
+ playerName = p.Name
+ }
+
+ // Drain zero-delay messages that have accumulated, then emit the current
+ // (delayed) one. Matches the trigger scheduler's drain-zero-delay pattern.
+ for d.Idx < len(d.Msgs) {
+ msg := d.Msgs[d.Idx]
+ d.Idx++
+ g.writePlayerMessage(sess, msg.Message, playerName, d.FlagVal)
+
+ if d.Idx >= len(d.Msgs) {
+ break
+ }
+ next := d.Msgs[d.Idx].Delay
+ if next > 0 {
+ p.Action.WaitLeft = next
+ return
+ }
+ }
+
+ // All messages emitted — fire trailing effects.
+ if d.Step != nil {
+ g.applyStepAction(sess, p, d.Step, d.RoomID, d.Scope, d.FlagVal)
+ }
+ g.cancelAction(p)
+}