package game import ( "fmt" "strings" "thirdcollapse/internal/action" "thirdcollapse/internal/net" "thirdcollapse/internal/object" "thirdcollapse/internal/player" "thirdcollapse/internal/world" ) func (g *Game) startMobTalk(sess *net.Session, p *player.Player, mob *world.MobInstance) { cfg, err := g.BehaviorStore.LoadTalk(mob.BehaviorID) if err != nil { sess.WriteLine("This person has nothing to say.") return } if node, ok := cfg.Nodes["start"]; ok { p.Action = &action.Action{ Type: "talk", TargetID: mob.DefID, TargetName: mob.Name, Data: map[string]any{"node": "start", "behavior_id": mob.BehaviorID}, } g.showTalkNode(sess, node) } else { sess.WriteLine("This person has nothing to say.") } } func (g *Game) startTalk(sess *net.Session, p *player.Player, obj *object.ObjectDef) { cfg, err := g.BehaviorStore.LoadTalk(obj.BehaviorID) if err != nil { sess.WriteLine("This person has nothing to say.") return } if node, ok := cfg.Nodes["start"]; ok { p.Action = &action.Action{ Type: "talk", TargetID: obj.ID, TargetName: obj.Name, Data: map[string]any{"node": "start", "behavior_id": obj.BehaviorID}, } g.showTalkNode(sess, node) } else { sess.WriteLine("This person has nothing to say.") } } func (g *Game) showTalkNode(sess *net.Session, node action.TalkNode) { sess.WriteLine(fmt.Sprintf("\n%s", node.Message)) if node.Action != nil { g.applyNodeAction(sess, node.Action) } if len(node.Options) == 0 { sess.State = net.StateGame sess.Write("\r\n> ") return } visible := 0 for _, opt := range node.Options { if opt.Condition != nil && !g.checkCondition(sess, opt.Condition) { continue } visible++ sess.WriteLine(fmt.Sprintf(" %d. %s", visible, opt.Text)) } sess.State = net.StateTalk sess.Write("\nChoice: ") } func (g *Game) handleTalkInput(sess *net.Session, input string) { p, ok := sess.Player.(*player.Player) if !ok || p.Action == nil || p.Action.Type != "talk" { sess.State = net.StateGame sess.Write("\r\n> ") return } input = strings.TrimSpace(input) lower := strings.ToLower(input) if lower == "bye" || lower == "exit" || lower == "end" || lower == "quit" { g.CancelAction(p) sess.State = net.StateGame sess.Write("\r\n> ") return } behaviorID := p.Action.Data["behavior_id"].(string) cfg, err := g.BehaviorStore.LoadTalk(behaviorID) if err != nil { g.CancelAction(p) sess.State = net.StateGame sess.Write("\r\n> ") return } nodeKey := p.Action.Data["node"].(string) node, ok := cfg.Nodes[nodeKey] if !ok { g.CancelAction(p) sess.State = net.StateGame sess.Write("\r\n> ") return } idx, err := parseChoiceIndex(input) if err != nil || idx <= 0 { g.CancelAction(p) sess.State = net.StateGame sess.Write("\r\n> ") return } validIdx := 0 var chosen *action.TalkOption for i := range node.Options { opt := &node.Options[i] if opt.Condition != nil && !g.checkCondition(sess, opt.Condition) { continue } validIdx++ if validIdx == idx { chosen = opt break } } if chosen == nil { g.CancelAction(p) sess.State = net.StateGame sess.Write("\r\n> ") return } if chosen.End { g.CancelAction(p) sess.State = net.StateGame sess.Write("\r\n> ") return } if chosen.Goto != "" { if nextNode, ok := cfg.Nodes[chosen.Goto]; ok { p.Action.Data["node"] = chosen.Goto g.showTalkNode(sess, nextNode) return } } g.CancelAction(p) sess.State = net.StateGame sess.Write("\r\n> ") } func (g *Game) applyNodeAction(sess *net.Session, na *action.NodeAction) { p, ok := sess.Player.(*player.Player) if !ok { return } for k, v := range na.SetFlags { g.WorldFlags[k] = v } for k, v := range na.SetPlayerFlags { if p.Flags == nil { p.Flags = make(map[string]any) } p.Flags[k] = v } if na.TakeItem != "" { if p.HasItem(na.TakeItem) { p.RemoveItem(na.TakeItem, 1) } } if na.GiveItem != "" { slot := p.FirstFreeSlot() if slot == -1 { sess.WriteLine("Your inventory is too full to receive that.") } else { p.SetInvSlot(slot, &player.InventorySlot{ItemID: na.GiveItem, Quantity: 1}) g.AccountStore.SaveCharacter(p) } } if na.Teleport > 0 { p.RoomID = na.Teleport g.AccountStore.SaveCharacter(p) g.World.SeedGroundItems(p.RoomID) g.seedRoomMobs(p.RoomID) g.seedRoomObjects(p.RoomID) if g.Hub != nil { g.Hub.EnterRoom(sess, p.RoomID) } g.doLook(sess) g.RunEnterSteps(sess, p.RoomID) } if na.Heal > 0 { p.HP += na.Heal if maxHP := p.MaxHP(); p.HP > maxHP { p.HP = maxHP } g.AccountStore.SaveCharacter(p) sess.WriteLine(fmt.Sprintf("You regain %d hitpoints.", na.Heal)) } }