aboutsummaryrefslogtreecommitdiff
path: root/internal/game/act_talk.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-26 02:52:31 -0400
committerhistoria <[not public]>2026-06-26 02:52:31 -0400
commit3102525d97aa6f1ad152c74b2ccfbd4b3a9b83f5 (patch)
tree65610fa4fb8abe1dc7a46d00658e85e0525d397c /internal/game/act_talk.go
parente2d5b081f27141bb3dd89dbe595860b8a1345d55 (diff)
downloadthehouseoficarus-3102525d97aa6f1ad152c74b2ccfbd4b3a9b83f5.tar.gz
feat: simplified conversation trees and output won't overwrite prompts now
Diffstat (limited to 'internal/game/act_talk.go')
-rw-r--r--internal/game/act_talk.go228
1 files changed, 163 insertions, 65 deletions
diff --git a/internal/game/act_talk.go b/internal/game/act_talk.go
index 14361f8..c87e3c5 100644
--- a/internal/game/act_talk.go
+++ b/internal/game/act_talk.go
@@ -87,51 +87,63 @@ func (g *Game) handleNodeAction(sess *net.Session, na *behavior.NodeAction) (int
func (g *Game) showTalkNode(sess *net.Session, node behavior.TalkNode) {
p := sess.Player
td, _ := p.Action.Data.(*behavior.TalkData)
-
+ td.CancelTalk = false
+ td.PendingSeq = false
+ td.PendingChoice = ""
+ td.PendingAction = nil
+ td.PendingChosen = false
dialogSpec := g.resolveColor(sess, "dialog")
- msg := node.Message.Pick()
- if msg != "" {
- sess.WriteLine(color.ExpandTagsDefault(g.colorMode(sess), dialogSpec, msg))
- }
-
- if node.Action != nil {
- if g.handleNodeAction(sess, node.Action) {
- return
- }
- }
-
cfg := td.Cfg
+
for {
- visible := g.visibleOptions(sess, node.Options)
- if len(visible) > 0 {
- for i, opt := range visible {
- sess.WriteLine(fmt.Sprintf(" %d. %s", i+1, opt.Text))
+ if node.Condition == nil || g.checkCondition(sess, node.Condition) {
+ if len(node.Sequence) > 0 {
+ if node.Action != nil {
+ if g.handleNodeAction(sess, node.Action) {
+ return
+ }
+ }
+ td.SeqIndex = 0
+ msg := node.Sequence[0]
+ if msg != "" {
+ sess.WriteLine(color.ExpandTagsDefault(g.colorMode(sess), dialogSpec, msg))
+ }
+ td.SeqIndex = 1
+ if td.SeqIndex < len(node.Sequence) {
+ sess.State = net.StateTalkSequence
+ sess.Write("[enter to continue]\n")
+ return
+ }
+ } else {
+ msg := node.Message.Pick()
+ if msg != "" {
+ sess.WriteLine(color.ExpandTagsDefault(g.colorMode(sess), dialogSpec, msg))
+ }
+ if node.Action != nil {
+ if g.handleNodeAction(sess, node.Action) {
+ return
+ }
+ }
+ }
+ visible := g.visibleOptions(sess, node.Options)
+ if len(visible) > 0 {
+ for i, opt := range visible {
+ sess.WriteLine(fmt.Sprintf(" %d. %s", i+1, opt.Text))
+ }
+ sess.State = net.StateTalk
+ g.reprompt(sess)
+ return
}
- sess.State = net.StateTalk
- sess.Write("\nChoice: ")
- return
}
-
- if node.Next != "" && cfg != nil {
- next, ok := cfg.Nodes[node.Next]
+ if node.Goto != "" && cfg != nil {
+ next, ok := cfg.Nodes[node.Goto]
if !ok {
break
}
if td != nil {
- td.Node = node.Next
+ td.Node = node.Goto
}
node = next
-
- msg := node.Message.Pick()
- if msg != "" {
- sess.WriteLine(color.ExpandTagsDefault(g.colorMode(sess), dialogSpec, msg))
- }
-
- if node.Action != nil {
- if g.handleNodeAction(sess, node.Action) {
- return
- }
- }
} else {
break
}
@@ -152,6 +164,31 @@ func (g *Game) visibleOptions(sess *net.Session, options []behavior.TalkOption)
return visible
}
+func (g *Game) finishSequence(sess *net.Session, node behavior.TalkNode) {
+ visible := g.visibleOptions(sess, node.Options)
+ if len(visible) > 0 {
+ for i, opt := range visible {
+ sess.WriteLine(fmt.Sprintf(" %d. %s", i+1, opt.Text))
+ }
+ sess.State = net.StateTalk
+ g.reprompt(sess)
+ return
+ }
+
+ td, _ := sess.Player.Action.Data.(*behavior.TalkData)
+ if node.Goto != "" && td != nil && td.Cfg != nil {
+ nextNode, ok := td.Cfg.Nodes[node.Goto]
+ if ok {
+ td.Node = node.Goto
+ g.showTalkNode(sess, nextNode)
+ return
+ }
+ }
+
+ g.cancelAction(sess.Player)
+ sess.State = net.StateGame
+}
+
func (g *Game) handleTalkInput(sess *net.Session, input string) {
p := sess.Player
if p == nil || p.Action == nil || p.Action.Type != behavior.TypeTalk {
@@ -160,46 +197,57 @@ func (g *Game) handleTalkInput(sess *net.Session, input string) {
return
}
- input = strings.TrimSpace(input)
- if input == "" {
- input = "1"
+ if p.Action.WaitLeft > 0 {
+ return
}
- lower := strings.ToLower(input)
- if lower == "bye" || lower == "exit" || lower == "end" || lower == "quit" {
+
+ td, ok := p.Action.Data.(*behavior.TalkData)
+ if !ok || td.Cfg == nil {
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)
+ input = strings.TrimSpace(input)
+ if input == "" && sess.State != net.StateTalkSequence {
+ input = "1"
+ }
+
+ if sess.State == net.StateTalkSequence {
+ _, ok := td.Cfg.Nodes[td.Node]
+ if !ok {
+ td.CancelTalk = true
+ p.Action.WaitLeft = 1
+ return
+ }
+ if input == "" {
+ td.PendingSeq = true
+ } else {
+ td.CancelTalk = true
+ }
+ p.Action.WaitLeft = 1
return
}
- td, ok := p.Action.Data.(*behavior.TalkData)
- if !ok || td.Cfg == nil {
- g.cancelAction(p)
- sess.State = net.StateGame
- g.writePrompt(sess)
+ if td.EstateDirectory {
+ g.handleEstateDirectoryTalk(sess, input)
return
}
- cfg := td.Cfg
+ cfg := td.Cfg
nodeKey := td.Node
node, ok := cfg.Nodes[nodeKey]
if !ok {
- g.cancelAction(p)
- sess.State = net.StateGame
- g.writePrompt(sess)
+ td.CancelTalk = true
+ p.Action.WaitLeft = 1
return
}
idx, err := parseChoiceIndex(input)
if err != nil || idx <= 0 {
- g.cancelAction(p)
- sess.State = net.StateGame
- g.writePrompt(sess)
+ td.CancelTalk = true
+ p.Action.WaitLeft = 1
return
}
@@ -218,29 +266,79 @@ func (g *Game) handleTalkInput(sess *net.Session, input string) {
}
if chosen == nil {
+ td.CancelTalk = true
+ p.Action.WaitLeft = 1
+ return
+ }
+
+ td.PendingChoice = chosen.Goto
+ td.PendingAction = chosen.Action
+ td.PendingChosen = true
+ p.Action.WaitLeft = 1
+}
+
+func (g *Game) advanceTalk(sess *net.Session, p *player.Player) {
+ td, ok := p.Action.Data.(*behavior.TalkData)
+ if !ok || td.Cfg == nil {
g.cancelAction(p)
sess.State = net.StateGame
- g.writePrompt(sess)
return
}
- if chosen.Action != nil {
- if g.handleNodeAction(sess, chosen.Action) {
- return
- }
+ if td.CancelTalk {
+ sess.WriteLine("You leave the conversation.")
+ g.cancelAction(p)
+ sess.State = net.StateGame
+ return
}
- if chosen.Goto != "" {
- if nextNode, ok := cfg.Nodes[chosen.Goto]; ok {
- td.Node = chosen.Goto
- g.showTalkNode(sess, nextNode)
+ if td.PendingSeq {
+ td.PendingSeq = false
+ node, ok := td.Cfg.Nodes[td.Node]
+ if !ok {
+ g.cancelAction(p)
+ sess.State = net.StateGame
+ return
+ }
+ dialogSpec := g.resolveColor(sess, "dialog")
+ if td.SeqIndex < len(node.Sequence) {
+ msg := node.Sequence[td.SeqIndex]
+ if msg != "" {
+ sess.WriteLine(color.ExpandTagsDefault(g.colorMode(sess), dialogSpec, msg))
+ }
+ td.SeqIndex++
+ }
+ if td.SeqIndex >= len(node.Sequence) {
+ g.finishSequence(sess, node)
return
}
+ sess.State = net.StateTalkSequence
+ sess.Write("[enter to continue]\n")
+ return
}
- g.cancelAction(p)
- sess.State = net.StateGame
- g.writePrompt(sess)
+ if td.PendingChosen {
+ td.PendingChosen = false
+ choice := td.PendingChoice
+ action := td.PendingAction
+ td.PendingChoice = ""
+ td.PendingAction = nil
+ if action != nil {
+ if g.handleNodeAction(sess, action) {
+ return
+ }
+ }
+ if choice != "" {
+ if nextNode, ok := td.Cfg.Nodes[choice]; ok {
+ td.Node = choice
+ g.showTalkNode(sess, nextNode)
+ return
+ }
+ }
+ g.cancelAction(p)
+ sess.State = net.StateGame
+ return
+ }
}
func (g *Game) enterShop(sess *net.Session, cfg *behavior.ShopConfig) {