aboutsummaryrefslogtreecommitdiff
path: root/internal/game
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game')
-rw-r--r--internal/game/act_steal.go14
-rw-r--r--internal/game/act_talk.go119
-rw-r--r--internal/game/cmd_shop.go42
-rw-r--r--internal/game/look_entities.go11
-rw-r--r--internal/game/look_target.go49
5 files changed, 155 insertions, 80 deletions
diff --git a/internal/game/act_steal.go b/internal/game/act_steal.go
index 6f67944..7371782 100644
--- a/internal/game/act_steal.go
+++ b/internal/game/act_steal.go
@@ -28,7 +28,7 @@ func (g *Game) executeSteal(sess *net.Session, args []string, rawInput string) {
var stallGuardTalk = &behavior.TalkConfig{
Nodes: map[string]behavior.TalkNode{
"start": {
- Message: "Caught you red-handed! You have three options, thief.",
+ Message: behavior.MessageList{"Caught you red-handed! You have three options, thief."},
Options: []behavior.TalkOption{
{Text: "\"I'll pay a fine. (500 credits)\"", Goto: "bribe", Condition: &behavior.Condition{MinCredits: 500}},
{Text: "\"Take me to jail.\"", Goto: "jail"},
@@ -36,19 +36,19 @@ var stallGuardTalk = &behavior.TalkConfig{
},
},
"bribe": {
- Message: "Smart choice. Hand over 500 credits and we'll forget this happened.",
+ Message: behavior.MessageList{"Smart choice. Hand over 500 credits and we'll forget this happened."},
Action: &behavior.NodeAction{Cost: 500},
- Options: []behavior.TalkOption{{Text: "\"Fine, take it.\"", End: true}},
+ Options: []behavior.TalkOption{{Text: "\"Fine, take it.\""}},
},
"jail": {
- Message: "Off to the detention cell with you!",
+ Message: behavior.MessageList{"Off to the detention cell with you!"},
Action: &behavior.NodeAction{Teleport: 162},
- Options: []behavior.TalkOption{{Text: "(You are dragged away)", End: true}},
+ Options: []behavior.TalkOption{{Text: "(You are dragged away)"}},
},
"fight": {
- Message: "Then defend yourself!",
+ Message: behavior.MessageList{"Then defend yourself!"},
Action: &behavior.NodeAction{SetFlags: map[string]any{"guard_hostile": true}},
- Options: []behavior.TalkOption{{Text: "(The guard attacks!)", End: true}},
+ Options: []behavior.TalkOption{{Text: "(The guard attacks!)"}},
},
},
}
diff --git a/internal/game/act_talk.go b/internal/game/act_talk.go
index e9047b2..f54290a 100644
--- a/internal/game/act_talk.go
+++ b/internal/game/act_talk.go
@@ -6,6 +6,7 @@ import (
"strings"
"thehouseoficarus/internal/behavior"
+ "thehouseoficarus/internal/color"
"thehouseoficarus/internal/net"
"thehouseoficarus/internal/object"
"thehouseoficarus/internal/player"
@@ -52,51 +53,99 @@ func (g *Game) startTalk(sess *net.Session, p *player.Player, obj *object.Object
}
}
+func (g *Game) handleNodeAction(sess *net.Session, na *behavior.NodeAction) (intercepted bool) {
+ if na.Shop != nil {
+ g.applyNodeAction(sess, na)
+ g.enterShop(sess, na.Shop)
+ return true
+ }
+ g.applyNodeAction(sess, na)
+
+ 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 true
+ }
+ }
+ }
+ return false
+}
+
func (g *Game) showTalkNode(sess *net.Session, node behavior.TalkNode) {
- sess.WriteLine(fmt.Sprintf("%s", g.colorize(sess, "dialog", node.Message)))
+ p := sess.Player
+ td, _ := p.Action.Data.(*behavior.TalkData)
+
+ 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 node.Action.Shop != nil {
- g.applyNodeAction(sess, node.Action)
- g.enterShop(sess, node.Action.Shop)
+ 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))
+ }
+ sess.State = net.StateTalk
+ sess.Write("\nChoice: ")
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)
- }
+
+ if node.Next != "" && cfg != nil {
+ next, ok := cfg.Nodes[node.Next]
+ if !ok {
+ break
+ }
+ if td != nil {
+ td.Node = node.Next
+ }
+ 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
}
}
- if len(node.Options) == 0 {
- sess.State = net.StateGame
- g.writePrompt(sess)
- return
- }
+ sess.State = net.StateGame
+ g.writePrompt(sess)
+}
- visible := 0
- for _, opt := range node.Options {
+func (g *Game) visibleOptions(sess *net.Session, options []behavior.TalkOption) []behavior.TalkOption {
+ var visible []behavior.TalkOption
+ for _, opt := range options {
if opt.Condition != nil && !g.checkCondition(sess, opt.Condition) {
continue
}
- visible++
- sess.WriteLine(fmt.Sprintf(" %d. %s", visible, opt.Text))
+ visible = append(visible, opt)
}
- sess.State = net.StateTalk
- sess.Write("\nChoice: ")
+ return visible
}
func (g *Game) handleTalkInput(sess *net.Session, input string) {
@@ -108,6 +157,9 @@ func (g *Game) handleTalkInput(sess *net.Session, input string) {
}
input = strings.TrimSpace(input)
+ if input == "" {
+ input = "1"
+ }
lower := strings.ToLower(input)
if lower == "bye" || lower == "exit" || lower == "end" || lower == "quit" {
g.cancelAction(p)
@@ -168,11 +220,10 @@ func (g *Game) handleTalkInput(sess *net.Session, input string) {
return
}
- if chosen.End {
- g.cancelAction(p)
- sess.State = net.StateGame
- g.writePrompt(sess)
- return
+ if chosen.Action != nil {
+ if g.handleNodeAction(sess, chosen.Action) {
+ return
+ }
}
if chosen.Goto != "" {
diff --git a/internal/game/cmd_shop.go b/internal/game/cmd_shop.go
index 51d0ab1..d1b0e9c 100644
--- a/internal/game/cmd_shop.go
+++ b/internal/game/cmd_shop.go
@@ -401,10 +401,8 @@ func (g *Game) leaveShop(sess *net.Session) {
g.writePrompt(sess)
return
}
- nodeKey := td.Node
- cfg := td.Cfg
- node, ok := cfg.Nodes[nodeKey]
+ node, ok := td.Cfg.Nodes[td.Node]
if !ok {
sess.State = net.StateGame
g.cancelAction(p)
@@ -412,20 +410,30 @@ func (g *Game) leaveShop(sess *net.Session) {
return
}
- sess.State = net.StateTalk
- visible := 0
- for _, opt := range node.Options {
- if opt.Condition != nil && !g.checkCondition(sess, opt.Condition) {
- continue
+ 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))
+ }
+ sess.State = net.StateTalk
+ sess.Write("\nChoice: ")
+ return
+ }
+
+ if node.Next != "" {
+ next, ok := td.Cfg.Nodes[node.Next]
+ if !ok {
+ break
+ }
+ td.Node = node.Next
+ node = next
+ } else {
+ break
}
- visible++
- sess.WriteLine(fmt.Sprintf(" %d. %s", visible, opt.Text))
- }
- if visible == 0 {
- sess.State = net.StateGame
- g.cancelAction(p)
- g.writePrompt(sess)
- return
}
- sess.Write("\nChoice: ")
+
+ sess.State = net.StateGame
+ g.cancelAction(p)
+ g.writePrompt(sess)
}
diff --git a/internal/game/look_entities.go b/internal/game/look_entities.go
index f9412d4..915317f 100644
--- a/internal/game/look_entities.go
+++ b/internal/game/look_entities.go
@@ -59,9 +59,14 @@ func (g *Game) showRoomMobs(sess *net.Session, p *player.Player, room *world.Roo
if m.Protected {
mobColor = "protected_mob"
}
- mobLevel := mobCombatLevel(m)
- levelStr := g.levelColorize(sess, playerLevel, mobLevel, fmt.Sprintf("(level %d)", mobLevel))
- lines = append(lines, fmt.Sprintf("%s %s%s%s", g.colorize(sess, mobColor, displayName), levelStr, hp, desc))
+ var levelStr string
+ if m.Protected {
+ levelStr = ""
+ } else {
+ mobLevel := mobCombatLevel(m)
+ levelStr = " " + g.levelColorize(sess, playerLevel, mobLevel, fmt.Sprintf("(level %d)", mobLevel))
+ }
+ lines = append(lines, fmt.Sprintf("%s%s%s%s", g.colorize(sess, mobColor, displayName), levelStr, hp, desc))
}
return lines
}
diff --git a/internal/game/look_target.go b/internal/game/look_target.go
index 4819411..96dc174 100644
--- a/internal/game/look_target.go
+++ b/internal/game/look_target.go
@@ -57,33 +57,40 @@ func (g *Game) doLookTarget(sess *net.Session, input string) {
if best.Protected {
mobColor = "protected_mob"
}
- mobLevel := mobCombatLevel(best)
- levelStr := g.levelColorize(sess, p.CombatLevel(), mobLevel, fmt.Sprintf("(level %d)", mobLevel))
+ var levelStr string
+ if best.Protected {
+ levelStr = color.Render(g.colorMode(sess), color.Parse("223"), "(protected)")
+ } else {
+ mobLevel := mobCombatLevel(best)
+ levelStr = g.levelColorize(sess, p.CombatLevel(), mobLevel, fmt.Sprintf("(level %d)", mobLevel))
+ }
sess.WriteLines(
"",
fmt.Sprintf("%s %s", g.colorize(sess, mobColor, best.Name), levelStr),
)
- if best.IdleDescription != "" {
- sess.WriteLine(best.IdleDescription)
+ if best.Description != "" {
+ sess.WriteLine(color.ExpandTags(g.colorMode(sess), best.Description))
}
- sess.WriteLines(
- "",
- fmt.Sprintf("Attack: %d", best.Attack),
- fmt.Sprintf("Strength: %d", best.Strength),
- fmt.Sprintf("Defense: %d", best.Defense),
- fmt.Sprintf("HP: %d/%d", best.HP, best.MaxHP),
- )
- if best.StabDefense != 0 || best.SlashDefense != 0 || best.CrushDefense != 0 ||
- best.ScienceDefense != 0 || best.RangedDefense != 0 {
+ if !best.Protected {
sess.WriteLines(
"",
- "Defense bonuses:",
- fmt.Sprintf(" Stab: %+d Slash: %+d Crush: %+d", best.StabDefense, best.SlashDefense, best.CrushDefense),
- fmt.Sprintf(" Science: %+d Ranged: %+d", best.ScienceDefense, best.RangedDefense),
+ fmt.Sprintf("Attack: %d", best.Attack),
+ fmt.Sprintf("Strength: %d", best.Strength),
+ fmt.Sprintf("Defense: %d", best.Defense),
+ fmt.Sprintf("HP: %d/%d", best.HP, best.MaxHP),
)
- }
- if best.Weakness != "" {
- sess.WriteLine(fmt.Sprintf("Weakness: %s", best.Weakness))
+ if best.StabDefense != 0 || best.SlashDefense != 0 || best.CrushDefense != 0 ||
+ best.ScienceDefense != 0 || best.RangedDefense != 0 {
+ sess.WriteLines(
+ "",
+ "Defense bonuses:",
+ fmt.Sprintf(" Stab: %+d Slash: %+d Crush: %+d", best.StabDefense, best.SlashDefense, best.CrushDefense),
+ fmt.Sprintf(" Science: %+d Ranged: %+d", best.ScienceDefense, best.RangedDefense),
+ )
+ }
+ if best.Weakness != "" {
+ sess.WriteLine(fmt.Sprintf("Weakness: %s", best.Weakness))
+ }
}
return
}
@@ -142,6 +149,10 @@ func (g *Game) doLookTarget(sess *net.Session, input string) {
sess.WriteLine(color.ExpandTags(g.colorMode(sess), objDescText))
}
+ if def.OnLook != nil {
+ g.applyNodeAction(sess, def.OnLook)
+ }
+
if def.Safespot != nil {
realSt := g.World.GetObjState(p.RoomID, st.DefID, st.Index)
if realSt != nil {