diff options
| author | historia <[not public]> | 2026-06-25 15:40:48 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-25 15:40:48 -0400 |
| commit | abd612c15799f604e671e83dc7c410ed2b44185f (patch) | |
| tree | 0597ca92350de73aac2a7cf26b2f2d6595dac0cc /internal/game/act_talk.go | |
| parent | 2725e2927a1595c7b100d942d1f14146252adeb7 (diff) | |
| download | thehouseoficarus-abd612c15799f604e671e83dc7c410ed2b44185f.tar.gz | |
slop refactor
Diffstat (limited to 'internal/game/act_talk.go')
| -rw-r--r-- | internal/game/act_talk.go | 277 |
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) + } +} |
