aboutsummaryrefslogtreecommitdiff
path: root/internal/game/cmd_hide.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-21 22:19:17 -0400
committerhistoria <[not public]>2026-06-21 22:19:17 -0400
commita3ae1e6aad696f584d138d9363c05dedff136a65 (patch)
tree81e615f7227bc9d1ec79ceefc7311ecde917b074 /internal/game/cmd_hide.go
parent84072ffe6365ad57c4976e9272762aa6db41de7e (diff)
downloadthehouseoficarus-a3ae1e6aad696f584d138d9363c05dedff136a65.tar.gz
feat: safespot system implemented
Diffstat (limited to 'internal/game/cmd_hide.go')
-rw-r--r--internal/game/cmd_hide.go171
1 files changed, 171 insertions, 0 deletions
diff --git a/internal/game/cmd_hide.go b/internal/game/cmd_hide.go
new file mode 100644
index 0000000..9445949
--- /dev/null
+++ b/internal/game/cmd_hide.go
@@ -0,0 +1,171 @@
+package game
+
+import (
+ "fmt"
+ "strings"
+
+ "thehouseoficarus/internal/combat"
+ "thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/world"
+)
+
+func (g *Game) executeHide(sess *net.Session, args []string, rawInput string) {
+ p := sess.Player
+
+ if len(args) == 0 {
+ sess.WriteLine("Hide behind what?")
+ return
+ }
+
+ // Skip optional "behind"
+ target := strings.Join(args, " ")
+ if args[0] == "behind" {
+ if len(args) < 2 {
+ sess.WriteLine("Hide behind what?")
+ return
+ }
+ target = strings.Join(args[1:], " ")
+ }
+
+ instances := g.World.FindObjInstances(p.RoomID, target)
+ if len(instances) == 0 {
+ sess.WriteLine("You don't see that here.")
+ return
+ }
+
+ if len(instances) > 1 && objectInstancesNeedDisambiguation(instances) {
+ sess.WriteLines(
+ "That's ambiguous, which one?",
+ formatObjInstanceChoices(instances),
+ )
+ return
+ }
+
+ objDef, err := g.ObjectStore.Load(instances[0].DefID)
+ if err != nil || !objDef.IsSafespot() {
+ sess.WriteLine("You can't hide behind that.")
+ return
+ }
+
+ cfg := objDef.Safespot
+ st := g.World.GetObjState(p.RoomID, objDef.ID, instances[0].Index)
+ if st == nil {
+ sess.WriteLine("You can't hide behind that.")
+ return
+ }
+
+ g.safespotMu.Lock()
+ existingSS, alreadyHiding := g.safespotStates[p.Name]
+ g.safespotMu.Unlock()
+
+ if alreadyHiding && existingSS.Active {
+ if existingSS.ObjectDefID == objDef.ID && existingSS.ObjectIndex == instances[0].Index {
+ if existingSS.HideCountdown > 0 {
+ sess.WriteLine("You're already moving into position there.")
+ } else {
+ sess.WriteLine("You're already hiding there.")
+ }
+ return
+ }
+ g.leaveSafespot(sess, p)
+ }
+
+ if g.safespotTier(p) < cfg.Tier {
+ sess.WriteLine("You can't figure out how to use this as effective cover.")
+ return
+ }
+
+ if cfg.MaxOccupants > 0 && len(st.SafespotOccupants) >= cfg.MaxOccupants {
+ sess.WriteLines(
+ "",
+ fmt.Sprintf("%s already hiding there!",
+ joinSafespotOccupants(st.SafespotOccupants)),
+ )
+ return
+ }
+
+ if st.SafespotLevel <= 0 {
+ if cfg.RespawnTicks > 0 {
+ sess.WriteLine(fmt.Sprintf("The %s has been destroyed and hasn't reformed yet.", objDef.Name))
+ } else {
+ sess.WriteLine(fmt.Sprintf("The %s has been completely destroyed.", objDef.Name))
+ }
+ return
+ }
+
+ inCombat := combat.GetCombat(p.Name) != nil
+ hideCountdown := 1
+ if inCombat {
+ hideCountdown = 4
+ }
+
+ g.safespotMu.Lock()
+ g.safespotStates[p.Name] = &SafespotState{
+ Active: true,
+ ObjectDefID: objDef.ID,
+ ObjectIndex: instances[0].Index,
+ RoomID: p.RoomID,
+ HideCountdown: hideCountdown,
+ }
+ g.safespotMu.Unlock()
+
+ p.ActionState = &ActionState{Type: ActionHiding, TargetName: objDef.Name}
+
+ if inCombat {
+ sess.WriteLine(fmt.Sprintf("\nYou try to get behind the %s for cover...", objDef.Name))
+ } else {
+ sess.WriteLine(fmt.Sprintf("You position yourself behind the %s.", objDef.Name))
+ }
+}
+
+func (g *Game) executeUnhide(sess *net.Session, args []string, rawInput string) {
+ p := sess.Player
+
+ g.safespotMu.Lock()
+ ss, ok := g.safespotStates[p.Name]
+ g.safespotMu.Unlock()
+
+ if !ok || !ss.Active {
+ sess.WriteLine("You're not hiding behind anything.")
+ return
+ }
+
+ if ss.HideCountdown > 0 {
+ g.safespotMu.Lock()
+ delete(g.safespotStates, p.Name)
+ g.safespotMu.Unlock()
+ p.ActionState = nil
+ sess.WriteLine("You stop trying to hide.")
+ return
+ }
+
+ g.leaveSafespot(sess, p)
+}
+
+func objectInstancesNeedDisambiguation(instances []world.ObjState) bool {
+ if len(instances) < 2 {
+ return false
+ }
+ first := instances[0].DefID
+ for i := 1; i < len(instances); i++ {
+ if instances[i].DefID != first {
+ return true
+ }
+ }
+ return false
+}
+
+func formatObjInstanceChoices(instances []world.ObjState) string {
+ var lines []string
+ for _, inst := range instances {
+ lines = append(lines, fmt.Sprintf(" %d.%s", inst.Index+1, inst.Name))
+ }
+ return strings.Join(lines, "\n")
+}
+
+func joinSafespotOccupants(names []string) string {
+ if len(names) == 1 {
+ return fmt.Sprintf("%s is", names[0])
+ }
+ return fmt.Sprintf("%s are", strings.Join(names, ", "))
+}