aboutsummaryrefslogtreecommitdiff
path: root/internal/game/action_talk.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/action_talk.go')
-rw-r--r--internal/game/action_talk.go211
1 files changed, 211 insertions, 0 deletions
diff --git a/internal/game/action_talk.go b/internal/game/action_talk.go
new file mode 100644
index 0000000..f4929e9
--- /dev/null
+++ b/internal/game/action_talk.go
@@ -0,0 +1,211 @@
+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 := parseIndex(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.ensureRoomObjects(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))
+ }
+}