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)) return } st.SafespotLevel = len(cfg.Levels) st.SafespotTicksAtLevel = 0 } 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("You try to get behind the %s for cover...", 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, ", ")) }