diff options
| author | historia <[not public]> | 2026-06-25 15:40:48 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-25 15:40:48 -0400 |
| commit | abd612c15799f604e671e83dc7c410ed2b44185f (patch) | |
| tree | 0597ca92350de73aac2a7cf26b2f2d6595dac0cc /internal/game/look_target.go | |
| parent | 2725e2927a1595c7b100d942d1f14146252adeb7 (diff) | |
| download | thehouseoficarus-abd612c15799f604e671e83dc7c410ed2b44185f.tar.gz | |
slop refactor
Diffstat (limited to 'internal/game/look_target.go')
| -rw-r--r-- | internal/game/look_target.go | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/internal/game/look_target.go b/internal/game/look_target.go new file mode 100644 index 0000000..4819411 --- /dev/null +++ b/internal/game/look_target.go @@ -0,0 +1,343 @@ +package game + +import ( + "fmt" + "strings" + + "thehouseoficarus/internal/color" + "thehouseoficarus/internal/item" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" +) + +func (g *Game) doLookTarget(sess *net.Session, input string) { + p := sess.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 + } + if exitDef.Condition != nil && !g.checkCondition(sess, exitDef.Condition) { + msg := exitDef.BlockedMessage + if msg == "" { + msg = fmt.Sprintf("The way %s is blocked.", exitDir) + } + sess.WriteLine(msg) + 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 { + mobColor := "mob" + if best.Protected { + mobColor = "protected_mob" + } + mobLevel := mobCombatLevel(best) + levelStr := g.levelColorize(sess, p.CombatLevel(), mobLevel, fmt.Sprintf("(level %d)", mobLevel)) + sess.WriteLines( + "", + fmt.Sprintf("%s %s", g.colorize(sess, mobColor, best.Name), levelStr), + ) + if best.IdleDescription != "" { + sess.WriteLine(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), + ) + if best.StabDefense != 0 || best.SlashDefense != 0 || best.CrushDefense != 0 || + best.ScienceDefense != 0 || best.RangedDefense != 0 { + sess.WriteLines( + "", + "Defense bonuses:", + fmt.Sprintf(" Stab: %+d Slash: %+d Crush: %+d", best.StabDefense, best.SlashDefense, best.CrushDefense), + fmt.Sprintf(" Science: %+d Ranged: %+d", best.ScienceDefense, best.RangedDefense), + ) + } + if best.Weakness != "" { + sess.WriteLine(fmt.Sprintf("Weakness: %s", best.Weakness)) + } + return + } + + rawInstances := g.World.FindObjInstances(p.RoomID, lower) + + // Group matched object instances by definition, keeping only those visible + // to this player (an object whose conditional descriptions all fail is + // absent). If more than one distinct object matches, disambiguate. + var presentDefs []string + byDef := map[string][]world.ObjState{} + descByDef := map[string]string{} + for _, ist := range rawInstances { + def, err := g.ObjectStore.Load(ist.DefID) + if err != nil { + continue + } + text, present := g.resolveObjDesc(sess, def) + if !present { + continue + } + if _, seen := byDef[ist.DefID]; !seen { + presentDefs = append(presentDefs, ist.DefID) + descByDef[ist.DefID] = text + } + byDef[ist.DefID] = append(byDef[ist.DefID], ist) + } + + if len(presentDefs) > 1 { + sess.WriteLine("That's ambiguous, which one?") + for _, defID := range presentDefs { + if def, err := g.ObjectStore.Load(defID); err == nil { + sess.WriteLine(fmt.Sprintf(" %s", def.Name)) + } + } + return + } + + if len(presentDefs) == 1 { + instances := byDef[presentDefs[0]] + objDescText := descByDef[presentDefs[0]] + st := &instances[0] + def, _ := g.ObjectStore.Load(st.DefID) + + if st.DefID == "estate_directory" { + g.lookEstateDirectory(sess) + return + } + + sess.WriteLine("") + if len(instances) > 1 { + sess.WriteLine(fmt.Sprintf("%d %ss:", len(instances), def.Name)) + } + + if objDescText != "" && !strings.Contains(objDescText, "{quality}") { + sess.WriteLine(color.ExpandTags(g.colorMode(sess), objDescText)) + } + + if def.Safespot != nil { + realSt := g.World.GetObjState(p.RoomID, st.DefID, st.Index) + if realSt != nil { + levelStr := "intact" + if realSt.SafespotLevel <= 0 { + levelStr = fmt.Sprintf("intact (level %d/%d)", len(def.Safespot.Levels), len(def.Safespot.Levels)) + } else if realSt.SafespotLevel < len(def.Safespot.Levels) { + levelStr = fmt.Sprintf("degraded (level %d/%d)", realSt.SafespotLevel, len(def.Safespot.Levels)) + } else { + levelStr = fmt.Sprintf("intact (level %d/%d)", realSt.SafespotLevel, len(def.Safespot.Levels)) + } + blockInfo := "anything" + if def.Safespot.MaxBlockSize != "" { + blockInfo = fmt.Sprintf("up to %s mobs", def.Safespot.MaxBlockSize) + } + sess.WriteLine(fmt.Sprintf("Safespot: %s — blocks %s", levelStr, blockInfo)) + if len(realSt.SafespotOccupants) > 0 { + sess.WriteLine(fmt.Sprintf("Occupied by: %s", strings.Join(realSt.SafespotOccupants, ", "))) + } + } + } + + if p.OptionBool("depletion") { + for _, ist := range instances { + if ist.Depleted { + if len(instances) > 1 { + sess.WriteLine(fmt.Sprintf("%s %d: depleted, respawns in %d ticks.", def.Name, ist.Index+1, int(ist.DepleteTimer))) + } else { + sess.WriteLine(fmt.Sprintf("Depleted, respawns in %d ticks.", int(ist.DepleteTimer))) + } + } else if ist.SharedMax > 0 && float64(ist.SharedTimer) < ist.SharedMax { + if len(instances) > 1 { + sess.WriteLine(fmt.Sprintf("%s %d: despawn timer %d/%.0f.", def.Name, ist.Index+1, ist.SharedTimer, ist.SharedMax)) + } else { + sess.WriteLine(fmt.Sprintf("Despawn timer: %d/%.0f ticks.", ist.SharedTimer, ist.SharedMax)) + } + } + } + } + for _, ist := range instances { + if ist.Quality > 0 && strings.Contains(objDescText, "{quality}") { + desc := strings.ReplaceAll(objDescText, "{quality}", fmt.Sprintf("%.0f", ist.Quality)) + sess.WriteLine(color.ExpandTags(g.colorMode(sess), desc)) + } + } + + farmSuffix := g.farmObjSuffix(p, st.DefID, st.Index) + if farmSuffix != "" { + sess.WriteLine(farmSuffix) + } + 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( + "", + g.itemColorize(sess, def, def.Name), + color.ExpandTags(g.colorMode(sess), def.Description), + fmt.Sprintf("Value: %d credits", def.Value), + ) + g.showItemStats(sess, def) + 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 + } + lines := []string{ + "", + g.itemColorize(sess, def, def.Name), + color.ExpandTags(g.colorMode(sess), def.Description), + fmt.Sprintf("Value: %d credits", def.Value), + } + if slot.MaxQuality > 0 { + lines = append(lines, fmt.Sprintf("Has %d units of butane left.", slot.Quality)) + } + sess.WriteLines(lines...) + g.showItemStats(sess, def) + return + } + + others := g.Hub.PlayersInRoom(p.RoomID) + for _, other := range others { + if other == sess || other.Player == nil { + continue + } + op := other.Player + if strings.ToLower(op.Name) != lower { + continue + } + showPlayerInfo(g, sess, op) + return + } + + sess.WriteLine(fmt.Sprintf("There's no '%s' here.", input)) +} + +func showPlayerInfo(g *Game, sess *net.Session, p *player.Player) { + myP := sess.Player + theirLevel := p.CombatLevel() + levelStr := g.levelColorize(sess, myP.CombatLevel(), theirLevel, fmt.Sprint(theirLevel)) + sess.WriteLines( + "", + p.Name, + fmt.Sprintf("Combat Level: %s", levelStr), + 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 + } + name := itemID + var itemDef *item.ItemDef + if def, err := g.ItemStore.Load(itemID); err == nil { + name = def.Name + itemDef = def + } + sess.WriteLine(fmt.Sprintf(" %-12s %s", slot, g.itemColorize(sess, itemDef, name))) + } + + if p.Description != "" { + sess.WriteLine("") + sess.WriteLine(p.Description) + } +} + +func (g *Game) showItemStats(sess *net.Session, def *item.ItemDef) { + s := def.Stats + hasAttack := s.StabAttack != 0 || s.SlashAttack != 0 || s.CrushAttack != 0 || + s.ScienceAttack != 0 || s.RangedAttack != 0 + hasDefense := s.StabDefense != 0 || s.SlashDefense != 0 || s.CrushDefense != 0 || + s.ScienceDefense != 0 || s.RangedDefense != 0 + hasOther := s.StrengthBonus != 0 || s.RangedStrength != 0 || + s.ScienceDamage != 0 || s.TechnologyBonus != 0 + + if !hasAttack && !hasDefense && !hasOther { + return + } + + sess.WriteLine("") + if hasAttack || hasDefense { + sess.WriteLine("Attack bonuses: Defense bonuses:") + sess.WriteLine(fmt.Sprintf(" Stab: %+4d Stab: %+4d", s.StabAttack, s.StabDefense)) + sess.WriteLine(fmt.Sprintf(" Slash: %+4d Slash: %+4d", s.SlashAttack, s.SlashDefense)) + sess.WriteLine(fmt.Sprintf(" Crush: %+4d Crush: %+4d", s.CrushAttack, s.CrushDefense)) + sess.WriteLine(fmt.Sprintf(" Science:%+4d Science:%+4d", s.ScienceAttack, s.ScienceDefense)) + sess.WriteLine(fmt.Sprintf(" Ranged: %+4d Ranged: %+4d", s.RangedAttack, s.RangedDefense)) + } + if hasOther { + sess.WriteLine("") + sess.WriteLine("Other bonuses:") + if s.StrengthBonus != 0 { + sess.WriteLine(fmt.Sprintf(" Melee strength: %+d", s.StrengthBonus)) + } + if s.RangedStrength != 0 { + sess.WriteLine(fmt.Sprintf(" Ranged strength: %+d", s.RangedStrength)) + } + if s.ScienceDamage != 0 { + sess.WriteLine(fmt.Sprintf(" Science damage: %+d", s.ScienceDamage)) + } + if s.TechnologyBonus != 0 { + sess.WriteLine(fmt.Sprintf(" Technology: %+d", s.TechnologyBonus)) + } + } + if def.AttackType != "" { + sess.WriteLine(fmt.Sprintf("Attack type: %s", def.AttackType)) + } + if def.Speed > 0 { + sess.WriteLine(fmt.Sprintf("Speed: %.0f", def.Speed)) + } + if len(def.Requirements) > 0 { + sess.WriteLine("") + sess.WriteLine("Requirements:") + for skill, level := range def.Requirements { + sess.WriteLine(fmt.Sprintf(" %s: %d", skill, level)) + } + } +} |
