How to layout story based gameplay

I’m trying to create some sort of storyline for my rpg game, with dialogue, etc. and I’ve got a dialogue system down, however trying to figure out what’s the best way to go about story line gameplay (in a way like Loomian Legacy) where there’s a whole story/quest you complete and progress through, and character dialogue changes, etc. based on how far into the story you are.

Example of my dialogue atm

["Ol' Mate"] = {
	Messages = {
		[1] = {Msg = "Hello", Type = 'Normal'},
		[2] = {Msg = "Do you wanna go on a quest?", Type = 'Question', Responses = {["Sure"] = 3, ["Goodbye"] = 4}},
		[3] = function(dialogueFrame)
			
		end,
		[4] = {Msg = "Goodbye then", Type = 'End'},
	}
},

Has simple normal dialogue, npcs can ask questions players can then answer, etc. My question is how could I easily keep track of how far into a story a player is, and thus have different dialogue appear, based on how far into the story they are?

1 Like

I do not think that there is any easy way to keep track of progress the way you want it to work, the only feasible way I can see you doing this is to have conditional checks for every single major milestone that a player completes in their journey throughout the story.

You can’t expect to have a simple function for situations like this in where there are multiple outcomes possible unless you specifically write your own algorithm to do so and have the changes very simple.

1 Like

Would you have any advice on how to go about it then? All the story based games, like Camping, etc. do a simplified story telling system, that’s all I need. Or even an understanding how Loomian legacy story works

1 Like

I made a story game, from experience I’d separate each scene into a separate modules and a global “util” module for things like getting alive players, sending messages/countdowns, functions you’d use more than once.

If you know how to use metatables you could setup a simple story framework, if not simply require each module and run through them asynchronously

How would having multiple modules work with my dialogue tho? :confused:

Most story games store the player’s current level as a variable in the server. The client performs actions based on that sequence number. Once the player finishes a task, the task is verified by the server and the sequence number is increased. It’s all about conditional checks. Nothing more.

1 Like

Well I was thinking of doing something like that

["Ol' Mate"] = {
	Messages = {
[1] = {
		[1] = {Msg = "Hello", Type = 'Normal'},
		[2] = {Msg = "Do you wanna go on a quest?", Type = 'Question', Responses = {["Sure"] = 3, ["Goodbye"] = 4}},
		[3] = function(dialogueFrame)
			
		end,
		[4] = {Msg = "Goodbye then", Type = 'End'},
	}
},
}
[2] = {
		[1] = {Msg = "Well done you have completed the quest!", Type = 'Normal'},
}

But I feel that could get choppy and messy real quick

you could try what they did in The Legend of Zelda, Oracle of Ages.

Essentially, you have a global “story progress” value. each time the player does something that progresses the story, the value goes up.
NPCs have different dialog depending of this value.

consider the following MS paint masterpiece

so you could have a system like

Dialog = {}
Dialog['10'] = "Lala, Peaceful Life!"
Dialog['90'] = "Mr Goodguy! You've got to stop Dr Badguy!"
Dialog['999'] = "You saved us! Thank you!"

then you pick which dialog box based on the Story Progress value.

for i,v in pairs(Dialog) do
    if tonumber(i) >= StoryProgressValue then
        DialogChoice = v
        break
    end
end

if you have a lot of new story, and don’t want to reorganize everything, you can even do decimals.
the number you set as a key will be the Story Progress Value at which that dialog is no longer used.

this is of course presuming you’re doing a completely linear story, and no side quests.

2 Likes

Yea that’s kinda the idea I was thinking of going for, I just wasn’t sure if it would get messy after a while, as there will probs end up being a lot of dialogue.

My example of how I thought of setting this up to fit in with my current dialogue system is like so:

["Ol' Mate"] = {
		Messages = {
			[1] = {
				[1] = {Msg = "Hello", Type = 'Normal'},
				[2] = {Msg = "Do you wanna go on a quest?", Type = 'Question', 
					Responses = {
						[1] = {
							Answer = "Sure",
							Direct = 3
						},
					},
				},
				[3] = {Msg = "Ok, go do stuff then!", Type = 'End'},
			},
			[10] = {
				[3] = {Msg = "Thank you for saving us", Type = 'End'},
			}
		}
	},

Something like that, and just doing a linear story. I wanna have side quests, etc. But I think I could get away with having a main story line, and the side quests won’t do much to the main story. If I had a side quest say where they need to ‘20’ into the story, have their dialogue from 1-19 be just random blabber, then when the players reaches 20 in the main story, it’d unlock this other npcs dialogue, but completing the side quest wouldn’t add to the story value

2 Likes

PLEASE READ BOTTOM FOR EDIT AND A HUGE PROBLEM THAT COULD BE EXPLOITED!!

Just posting this here for anyone else whose interested in how I’m gonna go about it (or if anyone has any quarrel with what I’ve done they can tell what I’ve done wrong :grimacing:)

So dialogue is laid out in this table like so. With each final dialogue that an NPC has (‘End’) it has an UpdateStory variable, and that determines whether this section of dialogue would update the story. As you can see, [0] updates the story once dialogue has finished, while the others don’t. So when I talk to the NPC again, my story has been updated, and thus [1] shows instead of [0]

return {
	-- Main storyline dialogue
	["Ol' Mate"] = {
		Messages = {
			[0] = {
				[1] = {Msg = "Hello", Type = 'Normal'},
				[2] = {Msg = "Do you wanna go on a quest?", Type = 'Question', 
					Responses = {
						[1] = {
							Answer = "Sure",
							Direct = 3
						},
					},
				},
				[3] = {Msg = "Ok, go do stuff then!", Type = 'End', UpdateStory = true},
			},
			[1] = {
				[1] = {Msg = "Go on! Go do the quest", Type = 'End', UpdateStory = false},
			},
			[2] = {
				[1] = {Msg = "Thank you for doing this! I am happy now", Type = 'Normal'},
				[2] = {Msg = "You can go do some other stuff now", Type = 'End', UpdateStory = false},
			},
		}
	},
}

And then simply on the server, I can return and update the story as needed

local function Update(player)
	local User = PlayerData[player.UserId]
	if not User then return end
	
	User.StoryProgress = User.StoryProgress + 1
end

local function ReturnStoryProgress(player)
	local User = PlayerData[player.UserId]
	if not User then return end	
	
	return User.StoryProgress
end

UpdateStoryProgress.OnServerEvent:Connect(Update)

StoryProgress.OnServerInvoke = ReturnStoryProgress

EDIT having written this, just realised a problem that could occur with this. Exploiters can fire RemoteEvents, correct? And thus, they could just constantly fire the UpdateStoryProgress event, and get to the end story instantly. How can I prevent this? There is no real way to do sanity checks on the server :confused:

1 Like

Someone may have mentioned this, but you can look at fallout 3 and elderscrolls oblivion for a functional system that tracks progression. Each quest/milestone/etc within both games provides the player with an associated ID, that the system then references to determine when/what dialog/etc to launch at the player.

Ie Event 345 = 86770
event 345 is the player completeing a specific room of a dungeon; next time they talk to specific NPC it scans that part of a table and goes “Here is my response to event ID86770”

PS: GregTame Mentions this in his message as well.

1 Like

you could probably tie each UpdateStory event to a rough location

back to my Zelda example, let’s say completing a dungeon set your StoryProgress to 20, well it’s pretty safe to say that you’d be at that dungeon when your story progress hits 20.

in your system, it seems an NPC dialog is needed to update the story progress. so clearly you’d be within talking range of the NPC.
you can add a Linked NPC value to each update story, and if the player is too far away it goes “hang on, do that again” and just doesn’t update the story.

you wouldn’t have to do this for all of them, maybe just major ones.

1 Like

Kinda got a more advanced I guess sanity check here:

I don’t think it’s 100% perfect, but I think it’ll do, well, hopefully enough to deter most people from exploiting.

1 Like