Open-Source "Storyboard" Module! Useful for story games or any event-based needs!

Well, I’m not sure if “Storyboard” is a good name for it, but we all have seen those linear story games such as Camping and Breakout. The players get to a location, then an event happens and so on. To make things a bit simpler, I’ve created this module: Storyboard Module

If you don’t want to download the module, then here is the source code:

-- Storyboard module by auseless_Scout
-- Credit isn't needed, would be appreaciated though

local storyBoard = {}
storyBoard.__index = storyBoard

function storyBoard.new(debugoption)
	local self = setmetatable({}, storyBoard)
	self.Board = {}
	self.OnEvent = Instance.new("BindableEvent")
	self.paused = false
	self.stopped = false
	if debugoption == true then
		self.debug = true
	else
		self.debug = false
	end
	return self
end

function storyBoard:addEvent(requirementFunc, outcomeFunc, waitTime, eventName)
	table.insert(self.Board, {["Requirement"] = requirementFunc, ["Outcome"] = outcomeFunc, ["Time"] = waitTime, ["Name"] = eventName})
end


function storyBoard:addWait(waitTime, eventName)
	table.insert(self.Board, {["Time"] = waitTime, ["Name"] = eventName})
end

function storyBoard:pause()
	self.paused = true
end

function storyBoard:unPause()
	self.paused = false
end

function storyBoard:stop()
	self.stopped = true
end

function storyBoard:delete()
	self = nil
end

function storyBoard:run()
	for i, event in ipairs(self.Board) do
		if self.stopped then return end
		
		if self.paused then
			repeat task.wait() until self.paused == false
		end
		
		self.OnEvent:Fire(i, event["Name"])
		
		if event["Name"] and self.debug then
			print(event["Name"].. " has been reached")
		end
		
		local check
		local resultType
		
		if event["Requirement"] then
			
			if event["Name"] and self.debug then
				print(event["Name"].. " Requirement has been reached")
			end
			
			check, resultType = event["Requirement"](i)
			if check ~= true then
				repeat
					check = event["Requirement"](i)
					task.wait()
				until check == true
			end
			
		end
		
		if event["Time"] then
			if event["Name"] and self.debug then
				print(event["Name"].. " Wait Time")
			end
			
			task.wait(event["Time"])
		end
		
		if event["Outcome"] then
			if event["Name"] and self.debug then
				print(event["Name"].. " Outcome has began")
			end
			
			event["Outcome"](resultType, i)
			
			if event["Name"] and self.debug then
				print(event["Name"].. " Outcome has ended")
			end
		end
		
	end
end

function storyBoard:deferRun()
	task.defer(function()
		self:run()
	end)
end

function storyBoard:spawnRun()
	task.spawn(function()
		self:run()
	end)
end

return storyBoard

The module itself isn’t that complex, but it might be useful in making simple story games.

How to install:

  1. Get Module,
  2. Place in ReplicatedStorage (or ServerStorage, doesn’t matter unless you wan’t the client to access it as well, in which you’d use ReplicatedStorage),
  3. Require the module and you’re set :slight_smile: .

How to create a storyboard object:

  1. Create a storyboard.

    As seen in this image, we’re creating a storyboard object. This function has a parameter for debugging, as setting it to true will print out information useful for debugging.

How to use:
(All parameters are optional to allow for full creativity, hence leaving some as nil will not result in an error).

  1. There are three functions that the storyboard object can perform. The first one is addEvent()

    This function allows for the creation of an event. The requirement function is a function that you can create to include a requirement that must be met in order for the outcome function to occur. As a result, the requirement function MUST return true or false. It can also return a second variable called resultType which allows for multiple pathways (If A is met, we get “good ending”, if B is met, “we get bad ending” etc.) The only parameter for this function is the index of the event, which is useful for debug purposes. The second parameter, outcome function, is the outcome that will occur if a requirement is met. This could involve spawning in an enemy that you have to fight off. There are two parameters, the first being the resultType as mentioned, and the second being the index of the event for debug purposes. The waitTime is a wait time that is option for those who want a delay before the outcome will occur. Essentially it’s a task.wait() that occurs after the requirement is met but before the outcome occurs. The last parameter is the eventName which is useful for debugging as it allows you to know which event is currently running.
  2. The second function is addWait()
    Screenshot 2023-12-03 175136
    This allows for a wait between events. The first parameter is the length of the wait, while the second one, eventName, is for debugging.
  3. The third function is run()
    Screenshot 2023-12-03 175254
    This will run all the events.

The order that the events occur is from the order they are created. Lets look at this example:

local storyboardModule = require(script.Parent.StoryboardModule)

local storyboard = storyboardModule.new()

storyboard:addEvent(function()
	if game.Lighting.ClockTime >= 15 then
		return true
	else
		return false
	end
end, function()
	print("ClockTime is larger than 15")
end, 1.5, "Event1")

storyboard:addWait(3, "Event2")

storyboard:addEvent(function()
	if game.Lighting.ClockTime >= 20 then
		return true
	else
		return false
	end
end, function()
	print("ClockTime is larger than 20")
end, 1.5, "Event3")

storyboard:run()

In this example, the storyboard will begin with Event1, then it will go through the 3 second wait that Event2 offers, then it will finish with Event3. This example is quite simple and I tried to create a quick demo. Here is the place: Storyboard-Module-House demo. In this demo, the first event will require you to enter the house, followed by a short delay, and then you will be faced with two options. You can wait for 30 seconds and accept the bad ending (death), or you can jump 5 times and you’ll receive the good ending (“good ending” gets printed out in the console). You can check the code in the place. (Keep in mind that the demo is simple and to fully utilize this module, you’ll need to have a good chunk of knowledge in scripting.

Oh yea, and to have GUIs pop up due to events, you can just use a remote event that fires to the clients.

I might add more functions. I’m thinking of making a simple Dialogue System that will go well together for this.

19 Likes

This is really well made! If I were you, I would make an improved one and charge it.

3 Likes

Thank you very much! :smiley: I’ll try adding some improvements later on. I’m thinking of a way to pause and cancel the events. I’ll try to avoid charging for necessary features and keep it open-sourced. I might make an improved version with specific features such as dialogue and sound events (and have that one be charged at a moderate while the one with broad needed features remain open source)

1 Like

Alright so I’m currently working on an updated version. This update will include:

  1. Reformatting of This devforum post (Aka I’ll be editing the original post and I will add a documentation link once I figure out how to use github)
  2. Some sort of onEventBegan and onEventEnded functions. They’ll include 2 parameters, one being the index of the event while the other would be the name (the name allows for a more organized use of this function, but the way I coded the module allows for the name to be optional so I found that the index would be useful for those not using the naming feature for the events)
  3. Some sort of stop/pause functions and properties. the functions allow for the next event to stop/pause (would need be called before the next event occurs), while the property can be used within the requirement and output functions that you create to provide you the choice of being able to stop the event while the requirement/output functions are still occurring. I will be adding more detail to this in the future documentation)
  4. I’m also thinking of creating a youtube tutorial as a way to demonstrate this module’s capabilities.
  5. deferRun and spawnRun functions. An issue I found with the run function is that it will require to be ran on a different thread for the code below it to occur. While some people might find it useful that the run function actually prevents the code below it from occurring, others may not. While this can simply be solved with a coroutine or a task.defer, I decided to make those actual functions (task.defer/spawn) to prevent repetition :smiley: .

If you have any suggestions on any possible features you want in the module, feel free to reply to this and I’ll see what I can do.

1 Like

In the open source place, what do you do, except go in the house and die?

Well if you jump 5 times, you don’t really die. It prints out “Good Ending” instead.

What the house is testing is events. The first event is that if player goes into the house, it locks it. The second event has options. It either waits for the player to jump 5 times or 30 seconds. If 30 seconds pass, it sends off the bad ending (aka the player dies.) If the player jumps 5 times, it sends off the good ending (I didn’t know what to do for a good ending, so it just prints it out)

ah ok, thanks! I will try it out in the house and see what happens

How about a UI /GUI that shows what event name #1 your are on, and can have a description of what to do, plus a image, that you can put an image ID in, to help give a hint visually of what to do?

1 Like

That’s an interesting idea. I’ll try to attempt GUI mechanics for this plugin. Thank you for the suggestion :pray:

Update 1.0.1 is out! It includes a Pause/Stop system. What this does is that it allows you to pause/stop the storyboard!.

To use this, you simply do storyboard:pause()/:unPause()/:stop().
There are also 2 values: storyboard.stopped, storyboard.paused. These allow you to check the storyboard’s state and can even be used to stop/pause the storyboard alongside the functions. Now be aware that since this is a loop-based module, the stop/pause action would take place after a loop is done, hence it would take action after an event is completed. A solution to that is to add

iff storyboard.paused then repeat task.wait() until storyboard.paused == false
if storyboard.stopped then return "stopped" end 

or something like that inside the event functions you’ve made.

This update also includes a deferrun and a spawnrun function. Since this a loop based module, anything under the storyboard:run() function will not run until the storyboard itself has ended. These new functions allow for the storyboard to run on a different thread (or something like that), letting the code below it run after the storyboard is ran.

Looking at this module, it’s kind of messy and lacks proper documentation. It is the first Open-sourced module I’ve made, it was a learning opportunity. I thank you all for the feedback :smile: as I may remake this module in a more organized manner (and hopefully with proper documentation), or I may make a different open-sourced module.

Nice, are going to update the code in the OS game link you have at the top?

Nah since it already has the main functionalities. There is no use changing it. I did update the module from the marketplace and the source-code listed in the original post though.