aboutsummaryrefslogtreecommitdiff
path: root/internal/game/act_room.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-25 21:23:34 -0400
committerhistoria <[not public]>2026-06-25 21:23:34 -0400
commite2d5b081f27141bb3dd89dbe595860b8a1345d55 (patch)
tree3090f8150580fe85a872caa04165ff70613bbaa8 /internal/game/act_room.go
parent84d74ca4351a97148ad8339676f9df7946bc21fb (diff)
downloadthehouseoficarus-e2d5b081f27141bb3dd89dbe595860b8a1345d55.tar.gz
feat: on_enter features ported over to triggers, cosmetic fixes with trigger messages
Diffstat (limited to 'internal/game/act_room.go')
-rw-r--r--internal/game/act_room.go104
1 files changed, 78 insertions, 26 deletions
diff --git a/internal/game/act_room.go b/internal/game/act_room.go
index fec7304..ff7a780 100644
--- a/internal/game/act_room.go
+++ b/internal/game/act_room.go
@@ -4,13 +4,14 @@ import (
"fmt"
"strings"
+ "thehouseoficarus/internal/color"
"thehouseoficarus/internal/net"
"thehouseoficarus/internal/player"
"thehouseoficarus/internal/world"
)
-// enterSeq is an in-flight on_enter cutscene for a single player. It is driven
-// one step at a time by EnterSeqTick.
+// enterSeq is an in-flight on_enter stepped sequence for a single player. It is
+// driven one step at a time by EnterSeqTick.
type enterSeq struct {
sess *net.Session
playerName string
@@ -22,8 +23,8 @@ type enterSeq struct {
// 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, exactly
-// as before. Otherwise the surviving steps become a scheduled cutscene driven
+// (no delay, no flag mutation) 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) {
room, err := g.World.LoadRoom(roomID)
@@ -36,21 +37,21 @@ func (g *Game) runEnterSteps(sess *net.Session, roomID int) {
}
var steps []world.EnterStep
- cutscene := false
+ sequenced := false
for _, step := range room.OnEnter {
if step.Condition != nil && !g.checkCondition(sess, step.Condition) {
continue
}
steps = append(steps, step)
if step.IsTimed() {
- cutscene = true
+ sequenced = true
}
}
if len(steps) == 0 {
return
}
- if !cutscene {
+ if !sequenced {
for _, step := range steps {
if step.Message != "" {
sess.WriteLine(step.Message)
@@ -59,16 +60,16 @@ func (g *Game) runEnterSteps(sess *net.Session, roomID int) {
return
}
- g.startEnterCutscene(sess, p, roomID, steps)
+ g.startEnterSeq(sess, p, roomID, steps)
}
-// startEnterCutscene registers a scheduled on_enter sequence and persists a
-// marker on the player so the cutscene can be resumed on reconnect if it is
-// interrupted (disconnect, server restart).
-func (g *Game) startEnterCutscene(sess *net.Session, p *player.Player, roomID int, steps []world.EnterStep) {
+// 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) {
g.cancelEnterSeq(p.Name)
- if p.CutsceneRoom != roomID {
- p.CutsceneRoom = roomID
+ if p.EnterSeqRoom != roomID {
+ p.EnterSeqRoom = roomID
g.AccountStore.SaveCharacter(p)
}
g.enterMu.Lock()
@@ -82,9 +83,9 @@ func (g *Game) startEnterCutscene(sess *net.Session, p *player.Player, roomID in
g.enterMu.Unlock()
}
-// EnterSeqTick advances every in-flight cutscene by one tick. Sequences for a
-// player who is no longer connected are paused (left for resume); sequences are
-// removed from the map on completion or cancellation.
+// EnterSeqTick advances every in-flight enter sequence by one tick. Sequences
+// for a player who is no longer connected are paused (left for resume);
+// sequences are removed from the map on completion or cancellation.
func (g *Game) EnterSeqTick() {
g.enterMu.Lock()
seqs := make([]*enterSeq, 0, len(g.enterSeqs))
@@ -137,20 +138,71 @@ func (g *Game) fireEnterStep(seq *enterSeq, step world.EnterStep) {
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 != "" {
- // Break from the prompt on the first line only, so a multi-line
- // cutscene reads as tight consecutive lines.
+ msg := expandTemplate(step.Message, p.Name, nil)
+ msg = color.ExpandTagsDefault(mode, seqSpec, msg)
if !seq.started {
- seq.sess.WriteLine("\n" + step.Message)
+ seq.sess.WriteLine("\n" + msg)
seq.started = true
} else {
- seq.sess.WriteLine(step.Message)
+ 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", "\n"+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", "\n"+msg))
+ }
}
}
if len(step.SetFlags) > 0 || len(step.SetPlayerFlags) > 0 {
g.applyFlagMutations(p, step.SetFlags, 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)
+ }
}
func (g *Game) completeEnterSeq(seq *enterSeq) {
@@ -161,16 +213,16 @@ func (g *Game) completeEnterSeq(seq *enterSeq) {
if p == nil {
return
}
- if p.CutsceneRoom == seq.roomID {
- p.CutsceneRoom = 0
+ if p.EnterSeqRoom == seq.roomID {
+ p.EnterSeqRoom = 0
g.AccountStore.SaveCharacter(p)
}
g.writePrompt(seq.sess)
}
-// cancelEnterSeq drops any in-flight cutscene for the player. It does not touch
-// the persisted CutsceneRoom marker, so a cutscene cancelled by disconnect can
-// still be resumed on reconnect.
+// cancelEnterSeq drops any in-flight enter sequence for the player. It does not
+// touch the persisted EnterSeqRoom marker, so a sequence cancelled by
+// disconnect can still be resumed on reconnect.
func (g *Game) cancelEnterSeq(name string) {
g.enterMu.Lock()
delete(g.enterSeqs, name)