i mean thats exactly my concern, will it get messy? i cant tell.
i had ai just now write me some dialogue
simpler one:
Summary
local require = shared.require
local option = require('server@dialogueHandler/option')
return option.build()
-- ENTRY
.speak("...Oh. A traveler.")
.with({
speaker = "stranger",
delay = 2
})
.iff(function(player, info)
return not player:GetAttribute("MetStranger")
end)
.speak("I don't get many visitors down here.")
.with({ delay = 1 })
.reply("Who are you?")
.speak("Names don't carry much weight anymore.")
.speak("But once, they called me Caelum.")
.with({ speaker = "Caelum" })
.label("intro_name")
.tie()
.reply("Where am I?")
.speak("Below the old roads.")
.speak("Where decisions come to rest.")
.with({ delay = 2 })
.tie()
.reply("I should go.")
.speak("You could.")
.speak("But something tells me you won't.")
.tie()
-- HUB
.speak("So. What do you want to know?")
.label("hub")
.reply("Tell me about this place.")
.speak("This place exists between moments.")
.speak("When people hesitate...")
.speak("It grows.")
.with({ delay = 1 })
.reply("That doesn't make sense.")
.speak("It doesn't have to.")
.tie()
.reply("Sounds dangerous.")
.speak("Only if you plan to leave unchanged.")
.tie()
.tie()
.reply("Tell me about yourself.")
.speak("I was once like you.")
.speak("Curious. Armed. Certain.")
.reply("What happened?")
.speak("I chose poorly.")
.speak("Then I chose again.")
.speak("And again.")
.tie()
.reply("Do you regret it?")
.speak("Regret is just memory with teeth.")
.tie()
.tie()
.reply("I'm looking for work.")
.speak("Work?")
.speak("Hmm.")
.speak("There *is* something.")
.label("offer_job")
.reply("I'm listening.")
.speak("A fracture has formed nearby.")
.speak("Something is leaking through.")
.reply("What kind of something?")
.speak("The kind that remembers being human.")
.tie()
.reply("I'll handle it.")
.speak("Good.")
.speak("Return if you survive.")
.with({ speaker = "Caelum", delay = 2 })
.tie()
.tie()
.reply("Not interested.")
.speak("Few are.")
.speak("Fewer last.")
.tie()
.tie()
.reply("This is getting weird.")
.speak("You noticed.")
.tie()
.reply("Let's talk about something else.")
.jump("hub")
.tie()
-- SECRET PATH
.speak("Wait.")
.iff(function(player, info)
return player:GetAttribute("KilledManyMobs") == true
end)
.speak("Your hands are loud.")
.with({ speaker = "Caelum" })
.reply("What does that mean?")
.speak("Violence echoes.")
.speak("Some of us can hear it.")
.tie()
.reply("They deserved it.")
.speak("Maybe.")
.speak("So did I, once.")
.tie()
.reply("You're judging me?")
.speak("No.")
.speak("I'm measuring you.")
.label("measurement")
.tie()
-- LOOPING MORAL TEST
.speak("Answer honestly.")
.with({ delay = 1 })
.reply("I do what I must to survive.")
.speak("A common answer.")
.speak("And an incomplete one.")
.jump("measurement")
.tie()
.reply("I enjoy the fight.")
.speak("Honest.")
.speak("Concerning.")
.jump("measurement")
.tie()
.reply("I hate it.")
.speak("Good.")
.speak("That means you're still human.")
.tie()
-- ENDING BRANCHES
.speak("We've talked long enough.")
.reply("So what happens now?")
.speak("Now you leave.")
.speak("Or you stay.")
.tie()
.reply("I'll leave.")
.speak("Most do.")
.speak("Few return.")
.with({ delay = 2 })
.tie()
.reply("I'll stay.")
.speak("Then listen carefully.")
.speak("This place will change you.")
.speak("Whether you want it to or not.")
.tie()
.reply("Can we talk again?")
.speak("We already are.")
.jump("hub")
.tie()
-- FINAL
.speak("Until next time, traveler.")
.with({
speaker = "Caelum",
delay = 3
})
some of the simpler ones lines are cool asf what the hell 
.reply("Tell me about this place.")
.speak("This place exists between moments.")
.speak("When people hesitate...")
.speak("It grows.")
.with({ delay = 1 })
.reply("That doesn't make sense.")
.speak("It doesn't have to.")
---
.reply("That doesn't make sense.")
.speak("It doesn't have to.")
---
I was once like you.
Curious. Armed. Certain.
I chose poorly.
Then I chose again.
And again.
Regret is just memory with teeth.
complexer one:
Summary
return option.build()
-- ENTRY POINT
.speak("...Hey. You came back.")
.with({
speaker = "man",
delay = 2
})
.iff(function(player, info)
return workspace:GetAttribute("hey") == true
end)
.speak("Most people don't. They say they will, but they never do.")
.with({ delay = 2 })
.reply("Yeah. I figured I'd check in.")
.speak("That figures.")
.speak("You always struck me as the type who can't quite leave things unfinished.")
.with({ delay = 3 })
.tie()
.reply("I didn't really have a choice.")
.speak("Funny thing about choices.")
.speak("We only notice them once they're gone.")
.tie()
.reply("Let's skip the small talk.")
.speak("Heh.")
.speak("Straight to business, then.")
.tie()
-- HUB
.speak("So. What are we doing this time?")
.label("main_hub")
.reply("Ask about you.")
.speak("About me?")
.speak("That's a deep hole to start digging.")
.reply("Try me.")
.speak("Alright.")
.speak("I used to run jobs for people who didn't want to get their hands dirty.")
.speak("Scouting. Cleanup. Making sure problems stayed quiet.")
.with({ delay = 2 })
.reply("Sounds familiar.")
.speak("Yeah.")
.speak("That's what scares me.")
.tie()
.reply("Why'd you stop?")
.speak("Because eventually you realize you're not solving problems.")
.speak("You're just making sure someone else never has to see them.")
.tie()
.reply("Do you regret it?")
.speak("Regret implies I had better options.")
.speak("I'm not convinced I did.")
.tie()
.tie()
.tie()
.reply("Ask about the area.")
.speak("This place?")
.speak("It's a crossroads.")
.speak("Not on any map, but plenty of people pass through it anyway.")
.reply("Doing what?")
.speak("Running from something.")
.speak("Or toward it.")
.tie()
.reply("Is it dangerous?")
.speak("Only if you stay long enough to get comfortable.")
.tie()
.tie()
.reply("Looking for work.")
.speak("Of course you are.")
.speak("You have that look.")
.iff(function(player, info)
return player:GetAttribute("CombatLevel") >= 5
end)
.reply("What look?")
.speak("Someone who's survived enough fights to stop being scared of the next one.")
.tie()
.reply("What've you got?")
.speak("There's something nesting under the old transit tunnel.")
.speak("It's not supposed to be alive.")
.speak("And it's definitely not supposed to be learning.")
.with({ delay = 2 })
.reply("Learning?")
.speak("Adapting.")
.speak("Remembering.")
.speak("That's usually when things go wrong.")
.tie()
.reply("I'll handle it.")
.speak("I thought you'd say that.")
.speak("Try not to make it angry before you understand it.")
.player(function(player)
player:SetAttribute("TunnelQuest", true)
end)
.tie()
.reply("Not my problem.")
.speak("It will be.")
.speak("Just not today.")
.tie()
.tie()
.tie()
.reply("About the killing.")
.iff(function(player, info)
return player:GetAttribute("KillCount") and player:GetAttribute("KillCount") > 20
end)
.speak("Ah.")
.speak("So you noticed.")
.speak("Most don't.")
.with({ delay = 2 })
.reply("You judging me?")
.speak("No.")
.speak("I'm trying to understand whether you still hear it.")
.tie()
.reply("Hear what?")
.speak("The part of you that hesitates.")
.speak("Once that goes quiet, it never comes back.")
.tie()
.reply("They deserved it.")
.speak("Maybe.")
.speak("That answer gets easier every time, doesn't it?")
.tie()
.tie()
.reply("I should go.")
.speak("Yeah.")
.speak("You probably should.")
.reply("Will I see you again?")
.speak("Depends.")
.speak("On the choices you make when no one's watching.")
.tie()
.reply("You make it sound ominous.")
.speak("I prefer 'honest'.")
.tie()
.tie()
.reply("Let's start over.")
.jump("main_hub")
.tie()
-- SECRET LOOP
.speak("One more thing.")
.iff(function(player, info)
return player:GetAttribute("TunnelQuest") == true
end)
.speak("If it talks to you...")
.speak("Don't answer immediately.")
.with({ delay = 2 })
.reply("Why?")
.speak("Because the first answer is never really yours.")
.tie()
.reply("You're not helping.")
.speak("I know.")
.speak("Good luck.")
.tie()
do you think this is sustainable in the long run? do you have any ideas on improving this, or do you have any ideas for an alternative structure? etc. like just any thoughts. like how would this be a pain to deal with, and do you think something like raw data tables, e.g. (i had ai turn âcomplexer oneâ into a raw datatable below)
data table
return {
entry = "start",
nodes = {
start = {
type = "speak",
text = "...Hey. You came back.",
with = { speaker = "man", delay = 2 },
iff = function(player, info)
return workspace:GetAttribute("hey") == true
end,
next = "came_back_1"
},
came_back_1 = {
type = "speak",
text = "Most people don't. They say they will, but they never do.",
with = { delay = 2 },
replies = {
{
text = "Yeah. I figured I'd check in.",
next = "check_in"
},
{
text = "I didn't really have a choice.",
next = "no_choice"
},
{
text = "Let's skip the small talk.",
next = "skip_smalltalk"
}
}
},
check_in = {
type = "speak",
text = "That figures. You always struck me as the type who can't quite leave things unfinished.",
with = { delay = 3 },
next = "main_hub"
},
no_choice = {
type = "speak",
text = "Funny thing about choices. We only notice them once they're gone.",
next = "main_hub"
},
skip_smalltalk = {
type = "speak",
text = "Heh. Straight to business, then.",
next = "main_hub"
},
-- HUB
main_hub = {
type = "speak",
label = "main_hub",
text = "So. What are we doing this time?",
replies = {
-- ABOUT NPC
{
text = "Ask about you.",
next = "about_you"
},
-- AREA
{
text = "Ask about the area.",
next = "about_area"
},
-- WORK (STATE GATED)
{
text = "Looking for work.",
iff = function(player)
return (player:GetAttribute("CombatLevel") or 0) >= 5
end,
next = "work_intro"
},
-- MORAL CHECK
{
text = "About the killing.",
iff = function(player)
return (player:GetAttribute("KillCount") or 0) > 20
end,
next = "killing_intro"
},
-- EXIT
{
text = "I should go.",
next = "exit"
},
-- LOOP
{
text = "Let's start over.",
jump = "main_hub"
}
}
},
-- ABOUT YOU BRANCH
about_you = {
type = "speak",
text = "About me? That's a deep hole to start digging.",
replies = {
{ text = "Try me.", next = "about_you_deep" }
}
},
about_you_deep = {
type = "speak",
text = "I used to run jobs for people who didn't want to get their hands dirty. Scouting. Cleanup. Making sure problems stayed quiet.",
with = { delay = 2 },
replies = {
{
text = "Sounds familiar.",
next = "about_you_familiar"
},
{
text = "Why'd you stop?",
next = "about_you_stop"
},
{
text = "Do you regret it?",
next = "about_you_regret"
}
}
},
about_you_familiar = {
type = "speak",
text = "Yeah. That's what scares me.",
next = "main_hub"
},
about_you_stop = {
type = "speak",
text = "Because eventually you realize you're not solving problems. You're just making sure someone else never has to see them.",
next = "main_hub"
},
about_you_regret = {
type = "speak",
text = "Regret implies I had better options. I'm not convinced I did.",
next = "main_hub"
},
-- AREA
about_area = {
type = "speak",
text = "This place? It's a crossroads. Not on any map, but plenty of people pass through it anyway.",
replies = {
{ text = "Doing what?", next = "area_doing" },
{ text = "Is it dangerous?", next = "area_danger" }
}
},
area_doing = {
type = "speak",
text = "Running from something. Or toward it.",
next = "main_hub"
},
area_danger = {
type = "speak",
text = "Only if you stay long enough to get comfortable.",
next = "main_hub"
},
-- WORK
work_intro = {
type = "speak",
text = "There's something nesting under the old transit tunnel. It's not supposed to be alive. And it's definitely not supposed to be learning.",
with = { delay = 2 },
replies = {
{ text = "Learning?", next = "work_learning" },
{
text = "I'll handle it.",
next = "work_accept",
onSelect = function(player)
player:SetAttribute("TunnelQuest", true)
end
},
{ text = "Not my problem.", next = "work_decline" }
}
},
work_learning = {
type = "speak",
text = "Adapting. Remembering. That's usually when things go wrong.",
next = "main_hub"
},
work_accept = {
type = "speak",
text = "I thought you'd say that. Try not to make it angry before you understand it.",
next = "main_hub"
},
work_decline = {
type = "speak",
text = "It will be. Just not today.",
next = "main_hub"
},
-- MORAL CHECK
killing_intro = {
type = "speak",
text = "So you noticed. Most don't.",
with = { delay = 2 },
replies = {
{ text = "You judging me?", next = "killing_judge" },
{ text = "Hear what?", next = "killing_hear" },
{ text = "They deserved it.", next = "killing_deserved" }
}
},
killing_judge = {
type = "speak",
text = "No. I'm trying to understand whether you still hear it.",
next = "main_hub"
},
killing_hear = {
type = "speak",
text = "The part of you that hesitates. Once that goes quiet, it never comes back.",
next = "main_hub"
},
killing_deserved = {
type = "speak",
text = "Maybe. That answer gets easier every time, doesn't it?",
next = "main_hub"
},
-- EXIT
exit = {
type = "speak",
text = "Depends. On the choices you make when no one's watching."
}
}
}
would be easier?
and ai says
No chaining â easier serialization
Explicit graph â easy jumps, loops, rewinds
State logic isolated â testable conditions
Works for editor tooling / visualizers
Can be compiled back into fluent syntax if needed
which is true but im not sure i agree with syntactically, because theres a lot of key-pair values that need to be named manually, and the ânextâ index wouldnât scale well most likely
i really need this system to scale well because thereâs going to be a ridiculous amount of dialogue