aboutsummaryrefslogtreecommitdiff
path: root/internal/game/action.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-25 15:40:48 -0400
committerhistoria <[not public]>2026-06-25 15:40:48 -0400
commitabd612c15799f604e671e83dc7c410ed2b44185f (patch)
tree0597ca92350de73aac2a7cf26b2f2d6595dac0cc /internal/game/action.go
parent2725e2927a1595c7b100d942d1f14146252adeb7 (diff)
downloadthehouseoficarus-abd612c15799f604e671e83dc7c410ed2b44185f.tar.gz
slop refactor
Diffstat (limited to 'internal/game/action.go')
-rw-r--r--internal/game/action.go377
1 files changed, 0 insertions, 377 deletions
diff --git a/internal/game/action.go b/internal/game/action.go
deleted file mode 100644
index 19e79cf..0000000
--- a/internal/game/action.go
+++ /dev/null
@@ -1,377 +0,0 @@
-package game
-
-import (
- "fmt"
- "sort"
- "strconv"
- "strings"
-
- "thehouseoficarus/internal/action"
- "thehouseoficarus/internal/combat"
- "thehouseoficarus/internal/net"
- "thehouseoficarus/internal/object"
- "thehouseoficarus/internal/player"
- "thehouseoficarus/internal/world"
-)
-
-var verbAliases = map[string]string{
- "mine": "gather",
- "chop": "gather",
- "fish": "gather",
- "cut": "gather",
- "shear": "gather",
- "talk": "talk",
- "speak": "talk",
- "ask": "talk",
- "steal": "steal",
- "thieve": "steal",
- "hide": "safespot",
-}
-
-var verbSkill = map[string]string{
- "mine": "mining",
- "chop": "woodcutting",
- "cut": "woodcutting",
- "fish": "fishing",
- "shear": "crafting",
- "steal": "thieving",
- "thieve": "thieving",
-}
-
-func normalizeVerb(v string) string {
- if a, ok := verbAliases[v]; ok {
- return a
- }
- return v
-}
-
-func (g *Game) startAction(sess *net.Session, verb, target string) {
- p := sess.Player
-
- verb = normalizeVerb(verb)
-
- if combat.GetCombat(p.Name) != nil {
- sess.WriteLine("You can't do that during combat!")
- return
- }
-
- if p.Action != nil {
- g.cancelAction(p)
- }
- g.cancelBgAction(p)
-
- lower := strings.ToLower(target)
- instanceIdx := -1
- if dotPos := strings.Index(lower, "."); dotPos > 0 {
- if n, err := strconv.Atoi(lower[:dotPos]); err == nil && n > 0 {
- instanceIdx = n - 1
- lower = lower[dotPos+1:]
- }
- }
-
- instances := g.World.FindObjInstances(p.RoomID, lower)
- sort.Slice(instances, func(i, j int) bool {
- return instances[i].Index < instances[j].Index
- })
-
- var obj *object.ObjectDef
- var mob *world.MobInstance
- var chosen *world.ObjState
-
- if len(instances) > 0 {
- uniqueDefs := make(map[string]bool)
- for _, ist := range instances {
- uniqueDefs[ist.DefID] = true
- }
- if len(uniqueDefs) > 1 {
- sess.WriteLine("That's ambiguous, which one?")
- return
- }
-
- if instanceIdx >= 0 && instanceIdx < len(instances) {
- chosen = &instances[instanceIdx]
- } else {
- for i := range instances {
- if !instances[i].Depleted {
- chosen = &instances[i]
- break
- }
- }
- if chosen == nil {
- chosen = &instances[0]
- }
- }
- if chosen == nil {
- sess.WriteLine(fmt.Sprintf("There's nothing here to %s.", verb))
- return
- }
- var err error
- obj, err = g.ObjectStore.Load(chosen.DefID)
- if err != nil || !obj.IsInteractable() {
- sess.WriteLine(fmt.Sprintf("You can't %s the %s.", verb, obj.Name))
- return
- }
- } else {
- mobs := g.MobStore.MobsInRoom(p.RoomID)
- var candidates []*world.MobInstance
- for _, m := range mobs {
- if !m.IsTalkable() {
- continue
- }
- if q := m.MatchQuality(lower); q >= world.MatchPrefix {
- candidates = append(candidates, m)
- }
- }
- if len(candidates) == 0 {
- sess.WriteLine(fmt.Sprintf("There's nothing here to %s.", verb))
- return
- }
- sort.Slice(candidates, func(i, j int) bool {
- return candidates[i].InstanceID < candidates[j].InstanceID
- })
- if instanceIdx >= 0 && instanceIdx < len(candidates) {
- mob = candidates[instanceIdx]
- } else if len(candidates) == 1 {
- mob = candidates[0]
- } else {
- sess.WriteLine("Which one?")
- return
- }
- }
-
- if obj != nil && obj.ID == "estate_directory" {
- g.startEstateDirectory(sess, verb)
- return
- }
-
- bhType := ""
- if obj != nil {
- bhType = obj.BehaviorType()
- } else if mob != nil {
- if mob.IsTalkable() {
- bhType = "talk"
- }
- }
-
- if bhType != verb {
- if obj != nil {
- sess.WriteLine(fmt.Sprintf("You can't %s the %s.", verb, obj.Name))
- } else {
- sess.WriteLine(fmt.Sprintf("You can't %s that.", verb))
- }
- return
- }
-
- switch bhType {
- case "gather":
- if obj == nil || chosen == nil {
- sess.WriteLine(fmt.Sprintf("You can't %s that.", verb))
- return
- }
- g.startGather(sess, p, obj, chosen)
- case "talk":
- if mob != nil {
- g.startMobTalk(sess, p, mob)
- } else {
- g.startTalk(sess, p, obj)
- }
- case "use":
- g.startUse(sess, p, obj)
- default:
- sess.WriteLine(fmt.Sprintf("You can't %s that.", verb))
- }
-}
-
-func (g *Game) cancelAction(p *player.Player) {
- p.Action = nil
- p.WalkSequence = nil
- p.ClearMoveState()
-}
-
-func (g *Game) cancelBgAction(p *player.Player) {
- p.BackgroundAction = nil
-}
-
-func (g *Game) AdvanceActions() {
- if g.Hub == nil {
- return
- }
- for _, sess := range g.Hub.AllSessions() {
- p := sess.Player
- if p == nil || p.Action == nil {
- continue
- }
- if !p.Action.Advance() {
- continue
- }
- switch p.Action.Type {
- case "gather":
- g.advanceGather(sess, p)
- case "use":
- g.advanceUse(sess, p)
- case "burn":
- g.advanceBurn(sess, p)
- case "stoke":
- g.advanceStoke(sess, p)
- case "search":
- g.advanceSearch(sess, p)
- case "identify":
- g.advanceIdentify(sess, p)
- case "steal":
- g.advanceSteal(sess, p)
- case "plant":
- g.advancePlant(sess, p)
- case "harvest":
- g.advanceHarvest(sess, p)
- case "rake":
- g.advanceRake(sess, p)
- case "water":
- g.advanceWater(sess, p)
- case "cure":
- g.advanceCure(sess, p)
- case "obstacle":
- g.advanceObstacle(sess, p)
- default:
- if productionActionTypes[string(p.Action.Type)] {
- g.advanceProduction(sess, p)
- }
- }
- if p.Action == nil {
- g.writePrompt(sess)
- }
- }
-
- for _, sess := range g.Hub.AllSessions() {
- p := sess.Player
- if p == nil || p.BackgroundAction == nil {
- continue
- }
- if !p.BackgroundAction.Advance() {
- continue
- }
- switch p.BackgroundAction.Type {
- case "fletch":
- g.advanceFletch(sess, p)
- case "clean":
- g.advanceClean(sess, p)
- }
- if p.BackgroundAction == nil {
- g.writePrompt(sess)
- }
- }
-}
-
-func (g *Game) checkCondition(sess *net.Session, c *action.Condition) bool {
- p := sess.Player
-
- if c.AllOf != nil {
- for _, sub := range c.AllOf {
- if !g.checkCondition(sess, &sub) {
- return false
- }
- }
- return true
- }
- if c.AnyOf != nil {
- for _, sub := range c.AnyOf {
- if g.checkCondition(sess, &sub) {
- return true
- }
- }
- return false
- }
-
- if c.PlayerFlag != "" {
- var val any
- present := false
- if p != nil && p.Flags != nil {
- val, present = p.Flags[c.PlayerFlag]
- }
- return flagMatches(present, val, c.Value, c.Not)
- }
-
- if c.Flag != "" {
- val, present := g.Flags.Get(c.Flag)
- return flagMatches(present, val, c.Value, c.Not)
- }
- if c.HasItem != "" {
- has := p != nil && p.HasItem(c.HasItem)
- if c.Not {
- return !has
- }
- return has
- }
- if c.MinCredits > 0 {
- has := p != nil && p.Credits >= c.MinCredits
- if c.Not {
- return !has
- }
- return has
- }
- return true
-}
-
-// flagMatches evaluates a flag/player_flag condition.
-//
-// If the condition specifies no `value`, the flag matches when it is present
-// and truthy (so `{player_flag: x}` means "x is set" and
-// `{player_flag: x, not: true}` means "x is not set"). If a `value` is given,
-// the flag must be present and equal to it. `not` inverts the result. Numeric
-// values are compared by coercing both sides through a common numeric type so
-// that an int flag set in code matches an int64/float64 decoded from YAML.
-func flagMatches(present bool, val, want any, not bool) bool {
- var match bool
- if want == nil {
- match = present && isTruthy(val)
- } else {
- match = present && valuesEqual(val, want)
- }
- if not {
- return !match
- }
- return match
-}
-
-// valuesEqual compares two any-typed values, normalizing numeric types (int,
-// int64, float64) so that e.g. int(3) == int64(3) == float64(3). Non-numeric
-// types fall back to ==.
-func valuesEqual(a, b any) bool {
- ai, aok := numericValue(a)
- bi, bok := numericValue(b)
- if aok && bok {
- return ai == bi
- }
- return a == b
-}
-
-// numericValue returns the value as a float64 if it is a numeric type.
-func numericValue(v any) (float64, bool) {
- switch x := v.(type) {
- case int:
- return float64(x), true
- case int64:
- return float64(x), true
- case float64:
- return x, true
- }
- return 0, false
-}
-
-func isTruthy(v any) bool {
- switch x := v.(type) {
- case nil:
- return false
- case bool:
- return x
- case int:
- return x != 0
- case int64:
- return x != 0
- case float64:
- return x != 0
- case string:
- return x != ""
- default:
- return true
- }
-}