aboutsummaryrefslogtreecommitdiff
path: root/internal/game/look_entities.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/look_entities.go')
-rw-r--r--internal/game/look_entities.go347
1 files changed, 347 insertions, 0 deletions
diff --git a/internal/game/look_entities.go b/internal/game/look_entities.go
new file mode 100644
index 0000000..f9412d4
--- /dev/null
+++ b/internal/game/look_entities.go
@@ -0,0 +1,347 @@
+package game
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "thehouseoficarus/internal/color"
+ "thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/player"
+ "thehouseoficarus/internal/world"
+)
+
+func (g *Game) showRoomMobs(sess *net.Session, p *player.Player, room *world.Room) []string {
+ mobs := g.MobStore.MobsInRoom(p.RoomID)
+ if len(mobs) == 0 {
+ return nil
+ }
+ sort.Slice(mobs, func(i, j int) bool {
+ iDamaged := mobs[i].HP < mobs[i].MaxHP
+ jDamaged := mobs[j].HP < mobs[j].MaxHP
+ if iDamaged != jDamaged {
+ return iDamaged
+ }
+ return mobs[i].InstanceID < mobs[j].InstanceID
+ })
+ var lines []string
+ lines = append(lines, "")
+ playerLevel := p.CombatLevel()
+ for _, m := range mobs {
+ hp := ""
+ if m.HP < m.MaxHP {
+ if m.IsTask() {
+ pct := 0
+ if m.MaxHP > 0 {
+ pct = (m.MaxHP - m.HP) * 100 / m.MaxHP
+ }
+ hp = fmt.Sprintf(" [%d%% complete]", pct)
+ } else {
+ hp = fmt.Sprintf(" [%s/%dhp]", color.Render(g.colorMode(sess), color.Parse("167"), fmt.Sprint(m.HP)), m.MaxHP)
+ }
+ }
+ var desc string
+ if g.Combat.IsMobInCombat(m.InstanceID) {
+ def, err := g.MobStore.LoadDef(m.DefID)
+ if err == nil && len(def.CombatDescriptions) > 0 {
+ target := g.Combat.MobTarget(m.InstanceID)
+ pattern := def.CombatDescriptions[randInt(len(def.CombatDescriptions))]
+ desc = " " + fmt.Sprintf(pattern, target)
+ }
+ } else if m.IdleDescription != "" {
+ desc = fmt.Sprintf(" %s", m.IdleDescription)
+ }
+ displayName := m.Name
+ if !m.Unique {
+ displayName = "A " + m.Name
+ }
+ mobColor := "mob"
+ if m.Protected {
+ mobColor = "protected_mob"
+ }
+ mobLevel := mobCombatLevel(m)
+ levelStr := g.levelColorize(sess, playerLevel, mobLevel, fmt.Sprintf("(level %d)", mobLevel))
+ lines = append(lines, fmt.Sprintf("%s %s%s%s", g.colorize(sess, mobColor, displayName), levelStr, hp, desc))
+ }
+ return lines
+}
+
+func (g *Game) showRoomObjects(sess *net.Session, p *player.Player, room *world.Room) []string {
+ objs := g.World.AllObjInstances(p.RoomID)
+ if len(objs) == 0 {
+ return g.showFarmPatches(sess, p)
+ }
+ var lines []string
+ lines = append(lines, "")
+ grouped := make(map[string]int)
+ var order []string
+ for _, o := range objs {
+ if _, ok := grouped[o.DefID]; !ok {
+ order = append(order, o.DefID)
+ }
+ grouped[o.DefID]++
+ }
+ for _, objID := range order {
+ count := grouped[objID]
+ def, err := g.ObjectStore.Load(objID)
+ if err != nil {
+ continue
+ }
+
+ if def.Hidden {
+ continue
+ }
+
+ if farmPatchDefIDs[objID] {
+ continue
+ }
+
+ type instInfo struct {
+ idx int
+ depleted bool
+ sharedMax int
+ sharedCur int
+ respawnIn int
+ quality int
+ }
+ var instances []instInfo
+ for i := 0; i < count; i++ {
+ st := g.World.GetObjState(p.RoomID, objID, i)
+ if st == nil {
+ continue
+ }
+ instances = append(instances, instInfo{
+ idx: i + 1,
+ depleted: st.Depleted,
+ sharedMax: int(st.SharedMax),
+ sharedCur: st.SharedTimer,
+ respawnIn: int(st.DepleteTimer),
+ quality: int(st.Quality),
+ })
+ }
+ multi := len(instances) > 1
+ showTimers := p.OptionBool("depletion")
+
+ var freshIdxs []int
+ var timed, depleted []instInfo
+ for _, ins := range instances {
+ if ins.depleted {
+ depleted = append(depleted, ins)
+ } else if ins.sharedMax > 0 && ins.sharedCur < ins.sharedMax {
+ timed = append(timed, ins)
+ } else {
+ freshIdxs = append(freshIdxs, ins.idx)
+ }
+ }
+
+ roomDesc := def.InRoomDescription
+ if roomDesc != "" {
+ roomDesc = color.ExpandTags(g.colorMode(sess), roomDesc)
+ }
+ coloredName := g.objColorize(sess, def, def.Name)
+ coloredPlural := g.objColorize(sess, def, def.Name+"s")
+
+ if len(freshIdxs) > 0 {
+ var line string
+ if roomDesc != "" {
+ line = roomDesc
+ } else if len(freshIdxs) == 1 {
+ line = fmt.Sprintf("A %s is here.", coloredName)
+ } else {
+ line = fmt.Sprintf("%d %s are here.", len(freshIdxs), coloredPlural)
+ }
+ var suffix string
+ if multi && (len(timed) > 0 || len(depleted) > 0) {
+ suffix = fmt.Sprintf(" [%s]", joinInts(freshIdxs))
+ }
+ qualityTimer := ""
+ if showTimers && len(instances) > 0 && instances[0].quality > 0 {
+ qualityTimer = fmt.Sprintf(" (Burning for %d more ticks)", instances[0].quality)
+ }
+ lines = append(lines, fmt.Sprintf("%s%s%s", line, suffix, qualityTimer))
+ }
+
+ for _, ins := range timed {
+ var line string
+ if roomDesc != "" {
+ line = roomDesc
+ } else {
+ line = fmt.Sprintf("A %s is here.", coloredName)
+ }
+ tag := ""
+ if multi {
+ tag = fmt.Sprintf(" [%d]", ins.idx)
+ }
+ timer := ""
+ if showTimers {
+ timer = fmt.Sprintf(" (despawn: %d/%d)", ins.sharedCur, ins.sharedMax)
+ }
+ lines = append(lines, fmt.Sprintf("%s%s%s", line, tag, timer))
+ }
+
+ for _, ins := range depleted {
+ var line string
+ if roomDesc != "" {
+ line = roomDesc
+ } else {
+ line = fmt.Sprintf("A %s is here.", coloredName)
+ }
+ tag := ""
+ if multi {
+ tag = fmt.Sprintf(" [%d]", ins.idx)
+ }
+ timer := ""
+ if showTimers {
+ timer = fmt.Sprintf(" (respawns in %d ticks)", ins.respawnIn)
+ }
+ lines = append(lines, fmt.Sprintf("%s (depleted)%s%s", line, tag, timer))
+ }
+ }
+
+ lines = append(lines, g.showFarmPatches(sess, p)...)
+ return lines
+}
+
+func (g *Game) showGroundItems(sess *net.Session, p *player.Player, room *world.Room) []string {
+ ground := g.World.GroundItemsDetailed(p.RoomID)
+ if len(ground) == 0 {
+ return nil
+ }
+ showDespawn := p.OptionBool("despawn")
+ showReserve := p.OptionBool("reserve")
+
+ type displayLine struct {
+ name string
+ quantity int
+ colorName string
+ annotation string
+ }
+
+ type groupKey struct {
+ itemID string
+ despawnTimer int
+ }
+
+ var lines []displayLine
+ groups := make(map[groupKey]*displayLine)
+ var groupOrder []groupKey
+
+ for _, info := range ground {
+ def, err := g.ItemStore.Load(info.ItemID)
+ name := info.ItemID
+ if err == nil {
+ name = def.Name
+ }
+ coloredName := g.itemColorize(sess, def, name)
+
+ if info.ReservedFor != "" {
+ var parts []string
+ if showReserve {
+ parts = append(parts, fmt.Sprintf("reserved for %s %dt", info.ReservedFor, info.ReserveTimer))
+ } else {
+ parts = append(parts, "reserved")
+ }
+ if showDespawn && !info.IsSpawn {
+ parts = append(parts, fmt.Sprintf("despawns %dt", info.DespawnTimer))
+ }
+ annotation := ""
+ if len(parts) > 0 {
+ annotation = fmt.Sprintf(" (%s)", strings.Join(parts, ", "))
+ }
+ lines = append(lines, displayLine{
+ name: name,
+ quantity: info.Quantity,
+ colorName: coloredName,
+ annotation: annotation,
+ })
+ continue
+ }
+
+ timerKey := -1
+ if showDespawn && !info.IsSpawn {
+ timerKey = info.DespawnTimer
+ }
+ key := groupKey{itemID: info.ItemID, despawnTimer: timerKey}
+
+ if existing, ok := groups[key]; ok {
+ existing.quantity += info.Quantity
+ } else {
+ annotation := ""
+ if showDespawn && !info.IsSpawn {
+ annotation = fmt.Sprintf(" (despawns %dt)", info.DespawnTimer)
+ }
+ dl := &displayLine{
+ name: name,
+ quantity: info.Quantity,
+ colorName: coloredName,
+ annotation: annotation,
+ }
+ groups[key] = dl
+ groupOrder = append(groupOrder, key)
+ }
+ }
+
+ for _, key := range groupOrder {
+ lines = append(lines, *groups[key])
+ }
+
+ sort.Slice(lines, func(i, j int) bool {
+ return strings.ToLower(lines[i].name) < strings.ToLower(lines[j].name)
+ })
+
+ var out []string
+ out = append(out, "")
+ out = append(out, "On the ground:")
+
+ type fmtLine struct {
+ prefix string
+ annotation string
+ }
+ var fmtLines []fmtLine
+ maxPrefix := 0
+
+ for _, dl := range lines {
+ prefix := ""
+ if dl.quantity > 1 {
+ prefix = fmt.Sprintf(" %d x %s", dl.quantity, dl.colorName)
+ } else {
+ prefix = fmt.Sprintf(" %s", dl.colorName)
+ }
+ if visibleLen(prefix) > maxPrefix {
+ maxPrefix = visibleLen(prefix)
+ }
+ fmtLines = append(fmtLines, fmtLine{prefix, dl.annotation})
+ }
+
+ for _, l := range fmtLines {
+ if l.annotation != "" {
+ pad := maxPrefix + 1 + (len(l.prefix) - visibleLen(l.prefix))
+ out = append(out, fmt.Sprintf("%-*s%s", pad, l.prefix, l.annotation))
+ } else {
+ out = append(out, l.prefix)
+ }
+ }
+ return out
+}
+
+func (g *Game) showRoomPlayers(sess *net.Session, p *player.Player, room *world.Room) {
+ others := g.Hub.PlayersInRoom(p.RoomID)
+ for _, other := range others {
+ if other != sess && other.Player != nil {
+ op := other.Player
+ line := fmt.Sprintf("\n%s is here", g.colorize(sess, "player_name", op.Name))
+ if cs := g.Combat.Get(op.Name); cs != nil {
+ if mob := g.MobStore.GetInstance(cs.MobID); mob != nil && mob.HP > 0 {
+ name := mobDisplayName(mob, false)
+ if idx := mobInstanceIdx(mob, g.MobStore.MobsInRoom(p.RoomID)); idx > 0 {
+ name += fmt.Sprintf(" [%d]", idx)
+ }
+ line += fmt.Sprintf(" (fighting %s)", name)
+ }
+ } else if desc := g.playerActionDisplay(op); desc != "" {
+ line += ", " + desc
+ }
+ sess.WriteLine(line + ".")
+ }
+ }
+}