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", "talk": "talk", "speak": "talk", "ask": "talk", "pull": "toggle", "push": "toggle", } var verbSkill = map[string]string{ "mine": "mining", "chop": "woodcutting", "cut": "woodcutting", "fish": "fishing", } 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.(*player.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) } 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 behaviorID string 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 } var chosen *world.ObjState 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.BehaviorID == "" { sess.WriteLine(fmt.Sprintf("You can't %s the %s.", verb, obj.Name)) return } behaviorID = obj.BehaviorID } else { mobs := g.MobStore.MobsInRoom(p.RoomID) var candidates []*world.MobInstance for _, m := range mobs { if m.BehaviorID == "" { 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 } behaviorID = mob.BehaviorID } bh, err := g.BehaviorStore.Load(behaviorID) if err != nil { if obj != nil { sess.WriteLine(fmt.Sprintf("Something is wrong with the %s.", obj.Name)) } else { sess.WriteLine("Something is wrong with that.") } return } if bh.Type != 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 bh.Type { case "gather": if obj == nil { sess.WriteLine(fmt.Sprintf("You can't %s that.", verb)) return } chosenObj := g.World.FindObjInstances(p.RoomID, obj.ID) sort.Slice(chosenObj, func(i, j int) bool { return chosenObj[i].Index < chosenObj[j].Index }) var st *world.ObjState for i := range chosenObj { if !chosenObj[i].Depleted { st = &chosenObj[i] break } } if st == nil && len(chosenObj) > 0 { st = &chosenObj[0] } if st == nil { sess.WriteLine(fmt.Sprintf("There's nothing here to %s.", verb)) return } g.startGather(sess, p, obj, st) case "talk": if mob != nil { g.startMobTalk(sess, p, mob) } else { g.startTalk(sess, p, obj) } case "use": g.startUse(sess, p, obj) case "toggle": g.startToggle(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.ActionState = nil p.WalkSequence = nil p.ClearMoveState() } func (g *Game) AdvanceActions() { if g.Hub == nil { return } for _, sess := range g.Hub.AllSessions() { p, ok := sess.Player.(*player.Player) if !ok || 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 "cook": g.advanceProduction(sess, p) case "smelt": g.advanceProduction(sess, p) case "smith": g.advanceProduction(sess, p) case "combine": g.advanceProduction(sess, p) } if p.Action == nil { g.writePrompt(sess) } } } func (g *Game) checkCondition(sess *net.Session, c *action.Condition) bool { p, _ := sess.Player.(*player.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 != "" { if p == nil || p.Flags == nil { return c.Not } val, ok := p.Flags[c.PlayerFlag] if c.Not { return !ok || val != c.Value } return ok && val == c.Value } if c.Flag != "" { val, ok := g.WorldFlags[c.Flag] if c.Not { return !ok || val != c.Value } return ok && val == c.Value } if c.HasItem != "" { has := p != nil && p.HasItem(c.HasItem) if c.Not { return !has } return has } return true }