package game import ( "fmt" "math/rand" "strconv" "strings" "thehouseoficarus/internal/net" "thehouseoficarus/internal/player" "thehouseoficarus/internal/world" ) func plural(n int) string { if n == 1 { return "" } return "s" } func parseQty(input string) (int, string) { parts := strings.Fields(input) if len(parts) > 1 { if n, err := strconv.Atoi(parts[0]); err == nil && n > 0 { return n, strings.Join(parts[1:], " ") } } return 0, input } func parseChoiceIndex(s string) (int, error) { var idx int _, err := fmt.Sscanf(s, "%d", &idx) return idx, err } func mobDisplayName(m *world.MobInstance, definite bool) string { if m.Unique { return m.Name } if definite { return "the " + m.Name } return "a " + m.Name } // mobDisplayNameCap is like mobDisplayName but capitalizes the article for use // at the start of a sentence (e.g. "The goblin hits you."). func mobDisplayNameCap(m *world.MobInstance, definite bool) string { if m.Unique { return m.Name } if definite { return "The " + m.Name } return "A " + m.Name } // itemDisplayName returns an item's display name for the given id, falling back // to the id itself if the item cannot be loaded. func (g *Game) itemDisplayName(itemID string) string { if def, err := g.ItemStore.Load(itemID); err == nil && def != nil { return def.Name } return itemID } func mobCombatLevel(m *world.MobInstance) int { base := float64(m.Defense+m.MaxHP) / 4.0 melee := float64(m.Attack+m.Strength) / 4.0 ranged := float64(m.Ranged) * 3.0 / 8.0 science := float64(m.Science) * 3.0 / 8.0 best := melee if ranged > best { best = ranged } if science > best { best = science } return int(base + best + 0.5) } func uniqueItemNames(matches []itemMatch) []string { seen := make(map[string]bool) var out []string for _, m := range matches { if !seen[m.Name] { seen[m.Name] = true out = append(out, m.Name) } } return out } func (g *Game) showWhichOne(sess *net.Session, matches []itemMatch) { sess.WriteLine("Which one?") seen := make(map[string]bool) for _, m := range matches { if seen[m.Name] { continue } seen[m.Name] = true def, _ := g.ItemStore.Load(m.ID) coloredName := g.itemColorize(sess, def, m.Name) sess.WriteLine(fmt.Sprintf(" - %s", coloredName)) } } func randInt(max int) int { if max <= 0 { return 0 } return rand.Intn(max) } func formatPickupList(sess *net.Session, picked []string) { for i, name := range picked { if i == len(picked)-1 { sess.WriteLine(name) } else if i == len(picked)-2 { sess.Write(name + " and ") } else { sess.Write(name + ", ") } } } func (g *Game) broadcastAction(sess *net.Session, format string, args ...interface{}) { if g.Hub == nil { return } p := sess.Player if p == nil { return } msg := fmt.Sprintf(format, args...) for _, other := range g.Hub.PlayersInRoom(p.RoomID) { if other != sess && other.Player != nil { other.WriteLine(g.colorize(other, "broadcast", msg)) } } } func (g *Game) newInventorySlot(itemID string, qty int) *player.InventorySlot { slot := &player.InventorySlot{ItemID: itemID, Quantity: qty} if def, err := g.ItemStore.Load(itemID); err == nil && def.MaxQuality > 0 { slot.Quality = def.Quality slot.MaxQuality = def.MaxQuality } return slot }