aboutsummaryrefslogtreecommitdiff
path: root/internal/game/act_talk.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/act_talk.go
parent2725e2927a1595c7b100d942d1f14146252adeb7 (diff)
downloadthehouseoficarus-abd612c15799f604e671e83dc7c410ed2b44185f.tar.gz
slop refactor
Diffstat (limited to 'internal/game/act_talk.go')
-rw-r--r--internal/game/act_talk.go277
1 files changed, 277 insertions, 0 deletions
diff --git a/internal/game/act_talk.go b/internal/game/act_talk.go
new file mode 100644
index 0000000..e9047b2
--- /dev/null
+++ b/internal/game/act_talk.go
@@ -0,0 +1,277 @@
+package game
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "thehouseoficarus/internal/behavior"
+ "thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/object"
+ "thehouseoficarus/internal/player"
+ "thehouseoficarus/internal/world"
+)
+
+func (g *Game) startMobTalk(sess *net.Session, p *player.Player, mob *world.MobInstance) {
+ cfg := mob.TalkConfig
+ if cfg == nil {
+ sess.WriteLine("This person has nothing to say.")
+ return
+ }
+
+ if node, ok := cfg.Nodes["start"]; ok {
+ p.Action = &behavior.Action{
+ Type: behavior.TypeTalk,
+ TargetID: mob.DefID,
+ TargetName: mob.Name,
+ Data: &behavior.TalkData{Node: "start", Cfg: cfg},
+ }
+ 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 := obj.Talk
+ if cfg == nil {
+ sess.WriteLine("This person has nothing to say.")
+ return
+ }
+
+ if node, ok := cfg.Nodes["start"]; ok {
+ p.Action = &behavior.Action{
+ Type: behavior.TypeTalk,
+ TargetID: obj.ID,
+ TargetName: obj.Name,
+ Data: &behavior.TalkData{Node: "start", Cfg: cfg},
+ }
+ g.showTalkNode(sess, node)
+ } else {
+ sess.WriteLine("This person has nothing to say.")
+ }
+}
+
+func (g *Game) showTalkNode(sess *net.Session, node behavior.TalkNode) {
+ sess.WriteLine(fmt.Sprintf("%s", g.colorize(sess, "dialog", node.Message)))
+
+ if node.Action != nil {
+ if node.Action.Shop != nil {
+ g.applyNodeAction(sess, node.Action)
+ g.enterShop(sess, node.Action.Shop)
+ return
+ }
+ g.applyNodeAction(sess, node.Action)
+
+ if v, ok := g.Flags.Get("guard_hostile"); ok && v != nil {
+ g.Flags.Delete("guard_hostile")
+ p := sess.Player
+ if p != nil && p.Action != nil {
+ if td, ok := p.Action.Data.(*behavior.TalkData); ok && td.StealGuard {
+ guardMob := g.findGuardInRoom(p.RoomID, "guard")
+ if guardMob != nil {
+ sess.State = net.StateGame
+ g.cancelAction(p)
+ guardMob.Protected = false
+ g.startCombat(sess, p, guardMob)
+ }
+ return
+ }
+ }
+ }
+ }
+
+ if len(node.Options) == 0 {
+ sess.State = net.StateGame
+ g.writePrompt(sess)
+ 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 := sess.Player
+ if p == nil || p.Action == nil || p.Action.Type != behavior.TypeTalk {
+ sess.State = net.StateGame
+ g.writePrompt(sess)
+ 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
+ g.writePrompt(sess)
+ return
+ }
+
+ if td, ok := p.Action.Data.(*behavior.TalkData); ok && td.EstateDirectory {
+ g.handleEstateDirectoryTalk(sess, input)
+ return
+ }
+
+ td, ok := p.Action.Data.(*behavior.TalkData)
+ if !ok || td.Cfg == nil {
+ g.cancelAction(p)
+ sess.State = net.StateGame
+ g.writePrompt(sess)
+ return
+ }
+ cfg := td.Cfg
+
+ nodeKey := td.Node
+ node, ok := cfg.Nodes[nodeKey]
+ if !ok {
+ g.cancelAction(p)
+ sess.State = net.StateGame
+ g.writePrompt(sess)
+ return
+ }
+
+ idx, err := parseChoiceIndex(input)
+ if err != nil || idx <= 0 {
+ g.cancelAction(p)
+ sess.State = net.StateGame
+ g.writePrompt(sess)
+ return
+ }
+
+ validIdx := 0
+ var chosen *behavior.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
+ g.writePrompt(sess)
+ return
+ }
+
+ if chosen.End {
+ g.cancelAction(p)
+ sess.State = net.StateGame
+ g.writePrompt(sess)
+ return
+ }
+
+ if chosen.Goto != "" {
+ if nextNode, ok := cfg.Nodes[chosen.Goto]; ok {
+ td.Node = chosen.Goto
+ g.showTalkNode(sess, nextNode)
+ return
+ }
+ }
+
+ g.cancelAction(p)
+ sess.State = net.StateGame
+ g.writePrompt(sess)
+}
+
+func (g *Game) enterShop(sess *net.Session, cfg *behavior.ShopConfig) {
+ sess.Shop = cfg
+ sess.State = net.StateShop
+ g.showShopBrowse(sess)
+ g.writeShopPrompt(sess)
+}
+
+func (g *Game) applyNodeAction(sess *net.Session, na *behavior.NodeAction) {
+ p := sess.Player
+ if p == nil {
+ return
+ }
+
+ if na.AssignTask {
+ g.assignAssassinTask(sess, p)
+ }
+ if na.SkipTask {
+ g.skipAssassinTask(sess, p)
+ }
+ if na.ExtendTask {
+ g.extendAssassinTask(sess, p)
+ }
+ if na.ReputationCost > 0 {
+ rep := getPlayerFlagInt(p, "assassin_reputation")
+ if rep < na.ReputationCost {
+ sess.WriteLine(fmt.Sprintf("You don't have enough Reputation. (Need %d, have %d)", na.ReputationCost, rep))
+ return
+ }
+ setPlayerFlag(p, "assassin_reputation", rep-na.ReputationCost)
+ g.AccountStore.SaveCharacter(p)
+ }
+
+ for k, v := range na.SetFlags {
+ g.Flags.Set(k, v)
+ }
+ for k, v := range na.SetPlayerFlags {
+ p.EnsureFlags()
+ 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
+ p.Stats.RecordRoomVisit(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))
+ }
+ if na.Cost > 0 {
+ if p.Credits >= na.Cost {
+ p.Credits -= na.Cost
+ g.AccountStore.SaveCharacter(p)
+ }
+ }
+ if na.Sawmill {
+ g.processSawmill(sess, p)
+ }
+ if na.ApsNode {
+ setPlayerFlag(p, "aps_node_"+strconv.Itoa(p.RoomID), true)
+ }
+}