aboutsummaryrefslogtreecommitdiff
path: root/internal/game/cmd_look.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/cmd_look.go')
-rw-r--r--internal/game/cmd_look.go403
1 files changed, 403 insertions, 0 deletions
diff --git a/internal/game/cmd_look.go b/internal/game/cmd_look.go
new file mode 100644
index 0000000..a544ec1
--- /dev/null
+++ b/internal/game/cmd_look.go
@@ -0,0 +1,403 @@
+package game
+
+import (
+ "fmt"
+ "sort"
+ "strconv"
+ "strings"
+
+ "thirdcollapse/internal/combat"
+ "thirdcollapse/internal/net"
+ "thirdcollapse/internal/player"
+ "thirdcollapse/internal/world"
+)
+
+func (g *Game) doLook(sess *net.Session) {
+ p := sess.Player.(*player.Player)
+ room, err := g.World.LoadRoom(p.RoomID)
+ if err != nil {
+ sess.WriteLine("You are in a void.")
+ return
+ }
+
+ sess.WriteLines(
+ "",
+ room.Name,
+ room.Description,
+ )
+
+ mobs := g.MobStore.MobsInRoom(p.RoomID)
+ if len(mobs) > 0 {
+ 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
+ })
+ sess.WriteLine("")
+ for _, m := range mobs {
+ hp := ""
+ if m.HP < m.MaxHP {
+ hp = fmt.Sprintf(" [%d/%dhp]", m.HP, m.MaxHP)
+ }
+ var desc string
+ if combat.IsMobInCombat(m.InstanceID) {
+ def, err := g.MobStore.LoadDef(m.DefID)
+ if err == nil && len(def.CombatDescriptions) > 0 {
+ target := combat.GetMobTarget(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
+ }
+ sess.WriteLine(fmt.Sprintf(" %s (level %d)%s%s", displayName, mobCombatLevel(m), hp, desc))
+ }
+ }
+
+ objs := g.World.AllObjInstances(p.RoomID)
+ if len(objs) > 0 {
+ sess.WriteLine("")
+ 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
+ }
+
+ var activeIdxs, depletedIdxs []int
+ for i := 0; i < count; i++ {
+ st := g.World.GetObjState(p.RoomID, objID, i)
+ if st != nil && st.Depleted {
+ depletedIdxs = append(depletedIdxs, i+1)
+ } else {
+ activeIdxs = append(activeIdxs, i+1)
+ }
+ }
+
+ if len(activeIdxs) > 0 {
+ if len(activeIdxs) == 1 {
+ sess.Write(fmt.Sprintf(" A %s is here.", def.Name))
+ } else {
+ sess.Write(fmt.Sprintf(" %d %ss are here.", len(activeIdxs), def.Name))
+ }
+ if count > 1 {
+ sess.WriteLine(fmt.Sprintf(" [%s]", intsJoin(activeIdxs)))
+ } else {
+ sess.WriteLine("")
+ }
+ }
+ if len(depletedIdxs) > 0 {
+ if len(depletedIdxs) == 1 {
+ sess.Write(fmt.Sprintf(" 1 depleted %s is here.", def.Name))
+ } else {
+ sess.Write(fmt.Sprintf(" %d depleted %ss are here.", len(depletedIdxs), def.Name))
+ }
+ sess.WriteLine(fmt.Sprintf(" [%s]", intsJoin(depletedIdxs)))
+ }
+ }
+ }
+
+ ground := g.World.GroundItemsDetailed(p.RoomID)
+ if len(ground) > 0 {
+ sess.WriteLine("")
+ sess.WriteLine("On the ground:")
+ for _, info := range ground {
+ def, err := g.ItemStore.Load(info.ItemID)
+ name := info.ItemID
+ if err == nil {
+ name = def.Name
+ }
+ line := ""
+ if info.Quantity > 1 {
+ line = fmt.Sprintf(" %d x %s", info.Quantity, name)
+ } else {
+ line = fmt.Sprintf(" %s", name)
+ }
+ if info.ReservedFor != "" {
+ if p.Toggles["reserve"] {
+ line += fmt.Sprintf(" (reserved for %s for %d ticks)", info.ReservedFor, info.ReserveTimer)
+ } else {
+ line += " (reserved)"
+ }
+ }
+ sess.WriteLine(line)
+ }
+ }
+
+ if len(room.Exits) > 0 {
+ sess.WriteLine("")
+ if p.Toggles["exits"] {
+ sess.WriteLine("Exits:")
+ for _, dir := range world.ExitOrder {
+ exitDef, ok := room.Exits[dir]
+ if !ok {
+ continue
+ }
+ targetRoom, err := g.World.LoadRoom(exitDef.Room)
+ targetName := fmt.Sprintf("#%d", exitDef.Room)
+ if err == nil {
+ targetName = targetRoom.Name
+ }
+ if exitDef.Condition != nil && !g.CheckExitCondition(exitDef.Condition) {
+ targetName += " (blocked)"
+ }
+ sess.WriteLine(fmt.Sprintf(" %-6s - %s", dir, targetName))
+ }
+ } else {
+ sess.Write("Exits: ")
+ first := true
+ for _, dir := range world.ExitOrder {
+ if _, ok := room.Exits[dir]; ok {
+ if !first {
+ sess.Write(", ")
+ }
+ sess.Write(string(dir))
+ first = false
+ }
+ }
+ sess.WriteLine("")
+ }
+ }
+
+ others := g.Hub.PlayersInRoom(p.RoomID)
+ for _, other := range others {
+ if other != sess && other.Player != nil {
+ op := other.Player.(*player.Player)
+ line := fmt.Sprintf("\n%s is here", op.Name)
+ if cs := combat.GetCombat(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 := actionDesc(op); desc != "" {
+ line += ", " + desc
+ }
+ sess.WriteLine(line + ".")
+ }
+ }
+}
+
+func (g *Game) doLookTarget(sess *net.Session, input string) {
+ p := sess.Player.(*player.Player)
+ lower := strings.ToLower(input)
+
+ if exitDir := g.World.ResolveExit(lower); exitDir != "" {
+ room, err := g.World.LoadRoom(p.RoomID)
+ if err != nil {
+ sess.WriteLine("You can't see anything that way.")
+ return
+ }
+ exitDef, ok := room.Exits[exitDir]
+ if !ok {
+ sess.WriteLine("You can't see anything that way.")
+ return
+ }
+ g.World.SeedGroundItems(exitDef.Room)
+ g.seedRoomMobs(exitDef.Room)
+ origRoom := p.RoomID
+ p.RoomID = exitDef.Room
+ g.doLook(sess)
+ p.RoomID = origRoom
+ return
+ }
+
+ var best *world.MobInstance
+ bestQ := world.MatchNone
+ for _, m := range g.MobStore.MobsInRoom(p.RoomID) {
+ q := m.MatchQuality(lower)
+ if q > bestQ {
+ bestQ = q
+ best = m
+ }
+ }
+ if best != nil {
+ sess.WriteLines(
+ "",
+ fmt.Sprintf("%s (level %d)", best.Name, mobCombatLevel(best)),
+ )
+ if best.IdleDescription != "" {
+ sess.WriteLine(fmt.Sprintf(" %s", best.IdleDescription))
+ }
+ sess.WriteLines(
+ "",
+ fmt.Sprintf(" Attack: %d", best.Attack),
+ fmt.Sprintf(" Strength: %d", best.Strength),
+ fmt.Sprintf(" Defense: %d", best.Defense),
+ fmt.Sprintf(" HP: %d/%d", best.HP, best.MaxHP),
+ )
+ return
+ }
+
+ instances := g.World.FindObjInstances(p.RoomID, lower)
+ if len(instances) > 0 {
+ st := &instances[0]
+ def, _ := g.ObjectStore.Load(st.DefID)
+ sess.WriteLine("")
+ if len(instances) > 1 {
+ sess.WriteLine(fmt.Sprintf("%d %ss:", len(instances), def.Name))
+ } else {
+ sess.WriteLine(def.Name)
+ }
+
+ if desc, ok := def.Props["description"].(string); ok && desc != "" {
+ sess.WriteLine(fmt.Sprintf(" %s", desc))
+ }
+
+ for _, ist := range instances {
+ if ist.Depleted {
+ if len(instances) > 1 {
+ sess.WriteLine(fmt.Sprintf(" %s %d: depleted for %d more ticks.", def.Name, ist.Index+1, ist.DepleteTimer))
+ } else {
+ sess.WriteLine(fmt.Sprintf(" Depleted for %d more ticks.", ist.DepleteTimer))
+ }
+ }
+ }
+ return
+ }
+
+ ground := g.World.GroundItems(p.RoomID)
+ for itemID := range ground {
+ def, err := g.ItemStore.Load(itemID)
+ if err != nil || !def.MatchesName(input) {
+ continue
+ }
+ sess.WriteLines(
+ "",
+ def.Name,
+ fmt.Sprintf(" %s", def.Description),
+ fmt.Sprintf(" Value: %d credits", def.Value),
+ )
+ return
+ }
+
+ for i := 0; i < 28; i++ {
+ slot := p.InvSlot(i)
+ if slot == nil {
+ continue
+ }
+ def, err := g.ItemStore.Load(slot.ItemID)
+ if err != nil || !def.MatchesName(input) {
+ continue
+ }
+ sess.WriteLines(
+ "",
+ def.Name,
+ fmt.Sprintf(" %s", def.Description),
+ fmt.Sprintf(" Value: %d credits", def.Value),
+ )
+ return
+ }
+
+ others := g.Hub.PlayersInRoom(p.RoomID)
+ for _, other := range others {
+ if other == sess || other.Player == nil {
+ continue
+ }
+ op := other.Player.(*player.Player)
+ if strings.ToLower(op.Name) != lower {
+ continue
+ }
+ showPlayerInfo(sess, op)
+ return
+ }
+
+ sess.WriteLine(fmt.Sprintf("There's no '%s' here.", input))
+}
+
+func showPlayerInfo(sess *net.Session, p *player.Player) {
+ sess.WriteLines(
+ "",
+ p.Name,
+ fmt.Sprintf(" Combat Level: %d", p.CombatLevel()),
+ fmt.Sprintf(" HP: %d/%d", p.HP, p.MaxHP()),
+ "",
+ )
+
+ for _, s := range player.AllSkills {
+ level := p.Level(s)
+ sess.WriteLine(fmt.Sprintf(" %-12s Level: %d", s, level))
+ }
+
+ sess.WriteLine("")
+ sess.WriteLine(" Equipment:")
+ for _, slot := range EquipSlots {
+ itemID, ok := p.Equipment[slot]
+ if !ok {
+ continue
+ }
+ sess.WriteLine(fmt.Sprintf(" %-12s %s", slot, itemID))
+ }
+
+ if p.Description != "" {
+ sess.WriteLine("")
+ sess.WriteLine(fmt.Sprintf(" %s", p.Description))
+ }
+}
+
+func (g *Game) doExits(sess *net.Session) {
+ p := sess.Player.(*player.Player)
+ room, err := g.World.LoadRoom(p.RoomID)
+ if err != nil || len(room.Exits) == 0 {
+ sess.WriteLine("There are no exits here.")
+ return
+ }
+ for _, dir := range world.ExitOrder {
+ exitDef, ok := room.Exits[dir]
+ if !ok {
+ continue
+ }
+ targetRoom, err := g.World.LoadRoom(exitDef.Room)
+ targetName := fmt.Sprintf("#%d", exitDef.Room)
+ if err == nil {
+ targetName = targetRoom.Name
+ }
+ sess.WriteLine(fmt.Sprintf(" %-6s - %s", dir, targetName))
+ }
+}
+
+func intsJoin(nums []int) string {
+ var parts []string
+ for _, n := range nums {
+ parts = append(parts, strconv.Itoa(n))
+ }
+ return strings.Join(parts, ",")
+}
+
+func mobInstanceIdx(mob *world.MobInstance, roomMobs []*world.MobInstance) int {
+ var same []*world.MobInstance
+ for _, m := range roomMobs {
+ if m.DefID == mob.DefID {
+ same = append(same, m)
+ }
+ }
+ if len(same) <= 1 {
+ return 0
+ }
+ sort.Slice(same, func(i, j int) bool {
+ return same[i].InstanceID < same[j].InstanceID
+ })
+ for i, m := range same {
+ if m.InstanceID == mob.InstanceID {
+ return i + 1
+ }
+ }
+ return 0
+}