diff options
| author | historia <[not public]> | 2026-06-10 01:48:28 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-10 01:48:28 -0400 |
| commit | a226d72e51eecb768b13600303f73483118d9104 (patch) | |
| tree | 458d15ef049732c15dacc591a830d3beddbedfde /internal/game/cmd_look.go | |
| parent | 6a0f3d7a252de4b1741cfe5e1c561412c602becc (diff) | |
| download | thehouseoficarus-a226d72e51eecb768b13600303f73483118d9104.tar.gz | |
feat: implemented janky object interaction model
Diffstat (limited to 'internal/game/cmd_look.go')
| -rw-r--r-- | internal/game/cmd_look.go | 403 |
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 +} |
