diff options
| author | historia <[not public]> | 2026-07-08 19:59:14 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-07-08 19:59:14 -0400 |
| commit | 09d325dcf5e779eab5550d3fd3377bde50101428 (patch) | |
| tree | a433be903aabbf1d2eadce2aacdac12a9eb8dde0 /internal/game/act_room.go | |
| parent | 9184377301c2604e003f36426788c032fb0ca524 (diff) | |
| download | thehouseoficarus-09d325dcf5e779eab5550d3fd3377bde50101428.tar.gz | |
feat: unify on use, on look, and on kill. all support same conditions/actions now.
Diffstat (limited to 'internal/game/act_room.go')
| -rw-r--r-- | internal/game/act_room.go | 92 |
1 files changed, 20 insertions, 72 deletions
diff --git a/internal/game/act_room.go b/internal/game/act_room.go index da74d6e..2b08bfa 100644 --- a/internal/game/act_room.go +++ b/internal/game/act_room.go @@ -4,10 +4,9 @@ import ( "fmt" "strings" - "thehouseoficarus/internal/color" + "thehouseoficarus/internal/behavior" "thehouseoficarus/internal/net" "thehouseoficarus/internal/player" - "thehouseoficarus/internal/world" ) // enterSeq is an in-flight on_enter stepped sequence for a single player. It is @@ -16,13 +15,13 @@ type enterSeq struct { sess *net.Session playerName string roomID int - steps []world.EnterStep + steps []behavior.StepAction wait int } // runEnterSteps evaluates a room's on_enter script for the player. Steps whose // condition fails are dropped. If none of the surviving steps are "timed" -// (no delay, no flag mutation) the messages are printed synchronously. +// (any non-zero effect field) the messages are printed synchronously. // Otherwise the surviving steps become a scheduled enter sequence driven // by EnterSeqTick. func (g *Game) runEnterSteps(sess *net.Session, roomID int) { @@ -35,7 +34,7 @@ func (g *Game) runEnterSteps(sess *net.Session, roomID int) { return } - var steps []world.EnterStep + var steps []behavior.StepAction sequenced := false for _, step := range room.OnEnter { if step.Condition != nil && !g.checkCondition(sess, step.Condition) { @@ -51,6 +50,7 @@ func (g *Game) runEnterSteps(sess *net.Session, roomID int) { } if !sequenced { + // Pure message-only steps — print synchronously, no side effects. for _, step := range steps { if step.Message != "" { sess.WriteLine(step.Message) @@ -65,7 +65,7 @@ func (g *Game) runEnterSteps(sess *net.Session, roomID int) { // startEnterSeq registers a scheduled on_enter sequence and persists a marker // on the player so the sequence can be resumed on reconnect if interrupted // (disconnect, server restart). -func (g *Game) startEnterSeq(sess *net.Session, p *player.Player, roomID int, steps []world.EnterStep) { +func (g *Game) startEnterSeq(sess *net.Session, p *player.Player, roomID int, steps []behavior.StepAction) { g.cancelEnterSeq(p.Name) if p.EnterSeqRoom != roomID { p.EnterSeqRoom = roomID @@ -98,7 +98,7 @@ func (g *Game) EnterSeqTick() { continue } - var jobs []world.EnterStep + var jobs []behavior.StepAction done := false g.enterMu.Lock() @@ -123,8 +123,8 @@ func (g *Game) EnterSeqTick() { } g.enterMu.Unlock() - for _, step := range jobs { - g.fireEnterStep(seq, step) + for i := range jobs { + g.fireEnterStep(seq, &jobs[i]) } if done { g.completeEnterSeq(seq) @@ -132,71 +132,16 @@ func (g *Game) EnterSeqTick() { } } -func (g *Game) fireEnterStep(seq *enterSeq, step world.EnterStep) { +// fireEnterStep runs one on_enter step's effects via the unified +// applyStepAction executor. The room-lock check (player must still be in the +// room that the sequence is being played for) lives here, before dispatch — +// applyStepAction itself doesn't re-check it. +func (g *Game) fireEnterStep(seq *enterSeq, step *behavior.StepAction) { p := seq.sess.Player if p == nil || p.RoomID != seq.roomID { return } - seqSpec := g.resolveColor(seq.sess, "sequence") - broadcastSpec := g.resolveColor(seq.sess, "broadcast") - mode := g.colorMode(seq.sess) - if step.Message != "" { - msg := expandTemplate(step.Message, p.Name, nil) - msg = color.ExpandTagsDefault(mode, seqSpec, msg) - seq.sess.WriteLine(msg) - } - if step.Broadcast != "" && g.Hub != nil { - msg := expandTemplate(step.Broadcast, p.Name, nil) - msg = color.ExpandTagsDefault(mode, broadcastSpec, msg) - for _, other := range g.Hub.PlayersInRoom(seq.roomID) { - other.WriteLine(g.colorize(other, "broadcast", msg)) - } - } - if step.BroadcastGlobal != "" && g.Hub != nil { - msg := expandTemplate(step.BroadcastGlobal, p.Name, nil) - msg = color.ExpandTagsDefault(mode, broadcastSpec, msg) - for _, other := range g.Hub.AllSessions() { - if other.Player != nil { - other.WriteLine(g.colorize(other, "broadcast", msg)) - } - } - } - if len(step.SetGlobalFlags) > 0 || len(step.SetPlayerFlags) > 0 { - g.applyFlagMutations(p, step.SetGlobalFlags, step.SetPlayerFlags) - g.AccountStore.SaveCharacter(p) - } - if step.SpawnMob != nil { - g.spawnTriggerMob(seq.sess, p, step.SpawnMob, seq.roomID) - } - if step.DespawnMob != "" { - g.despawnTriggerMobs(step.DespawnMob, p.Name) - } - if step.GiveItem != "" { - slot := p.FirstFreeSlot() - if slot == -1 { - seq.sess.WriteLine("Your inventory is too full to receive that.") - } else { - p.SetInvSlot(slot, &player.InventorySlot{ItemID: step.GiveItem, Quantity: 1}) - g.AccountStore.SaveCharacter(p) - } - } - if step.TakeItem != "" { - if p.HasItem(step.TakeItem) { - p.RemoveItem(step.TakeItem, 1) - g.AccountStore.SaveCharacter(p) - } - } - if step.Heal > 0 { - p.HP += step.Heal - if maxHP := p.MaxHP(); p.HP > maxHP { - p.HP = maxHP - } - g.AccountStore.SaveCharacter(p) - seq.sess.WriteLine(fmt.Sprintf("You regain %d hitpoints.", step.Heal)) - } - if step.Teleport > 0 { - g.teleportPlayer(seq.sess, p, step.Teleport) - } + g.applyStepAction(seq.sess, p, step, seq.roomID, scopePlayerSeq, nil) } func (g *Game) completeEnterSeq(seq *enterSeq) { @@ -229,8 +174,11 @@ func (g *Game) isCharLive(name string, sess *net.Session) bool { return g.loggedInChars[name] == sess } -// applyFlagMutations sets global and player flags, shared by on_enter steps and -// exit traversal. +// applyFlagMutations sets global and player flags. It is the player-scoped +// (scopePlayer / scopePlayerSeq) flag step of applyStepAction, called once +// per non-global step that carries set_global_flags and/or set_player_flags. +// scopeGlobal sets globals directly in applyStepAction step 4 instead (so +// world-scoped cascades don't reach back into this helper). func (g *Game) applyFlagMutations(p *player.Player, setGlobalFlags, setPlayerFlags map[string]any) { g.GlobalFlags.SetAll(setGlobalFlags) if len(setPlayerFlags) > 0 { |
