Created
January 30, 2024 07:40
-
-
Save littletsu/eec03ffb5b214eb7e5f98688a1584809 to your computer and use it in GitHub Desktop.
Proof of Concept State machine for Roblox simulator egg hatching
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local StateMachine = {} | |
export type StateMachine = typeof(StateMachine.new()) | |
export type StateMachineEnterCallback = (previousState: string|nil) -> () | |
export type StateMachineState = { | |
Enter: StateMachineEnterCallback?, | |
Exit: ((nextState: string|nil) -> ())?, | |
Name: string? | |
} | |
---@diagnostic disable-next-line: unused-vararg | |
local function nop(...) end | |
function StateMachine.new(): StateMachine | |
local self: StateMachine = {} | |
self.States = {} :: {[string]: StateMachineState} | |
self.CurrentState = nil :: StateMachineState? | |
local onStart = nop | |
local onStop = nop | |
-- Start is a special state that is called when transitioning to a new state and there is no previous state | |
function self:OnStart(callback: () -> ()) | |
onStart = callback | |
end | |
-- Stop is a special state that is only called by the Stop function | |
function self:OnStop(callback: () -> ()) | |
onStop = callback | |
end | |
-- Name property on state will be replaced by the name in the arguments | |
function self:DeclareState(name: string, state: StateMachineState|StateMachineEnterCallback) | |
if type(state) == "function" then | |
state = { | |
Enter = state | |
} | |
end | |
state.Name = name | |
state.Exit = state.Exit or nop | |
state.Enter = state.Enter or nop | |
self.States[name] = state | |
return self | |
end | |
-- Yields until the previous state has Exited and the next state has Entered. Varargs will be passed to the enter function of the transitioning state | |
function self:To(nextState: string, ...) | |
local newState = self.States[nextState] | |
if newState == nil then | |
error(`StateMachine: no state {nextState}`) | |
end | |
local prevState | |
if self.CurrentState ~= nil then | |
prevState = self.CurrentState | |
self.CurrentState.Exit(nextState) | |
else | |
onStart() | |
end | |
self.CurrentState = newState | |
self.CurrentState.Enter(prevState and prevState.Name, ...) | |
return self | |
end | |
-- Calls exits the current state if there is one and calls OnStop after | |
function self:Stop() | |
if self.CurrentState ~= nil then | |
self.CurrentState.Exit(nil) | |
self.CurrentState = nil | |
end | |
onStop() | |
end | |
return self | |
end | |
local sm = StateMachine.new() | |
local results = {} | |
local currentResult = 1 | |
local resultSm = StateMachine.new() | |
resultSm:DeclareState("Start", function() end) | |
local connectedGoNext = function() | |
if currentResult > #results then | |
resultSm:Stop() | |
return | |
end | |
local result = results[currentResult] | |
print("You hatched " .. result.Name .. " (x" .. result.Multiplier .. ")") | |
currentResult += 1 | |
if currentResult > #results then | |
resultSm:Stop() | |
return | |
end | |
end | |
local disconnectedGoNext = function() | |
print("Calling disconnected go next!!!") | |
end | |
local goNextResult = disconnectedGoNext | |
resultSm:OnStop(function() | |
goNextResult = disconnectedGoNext | |
end) | |
resultSm:OnStart(function() | |
goNextResult = connectedGoNext | |
end) | |
local function onUserInteraction() | |
resultSm:Stop() | |
end | |
local wins = 100 | |
local cost = 50 | |
local function Hatch(amount): boolean | |
print("Hatching ", amount) | |
if wins < cost then | |
print("Hatch: not enough wins!") | |
return false | |
end | |
results = { | |
{Name = "Gupi", Multiplier = 7*11*13 * math.random()}, | |
{Name = "Cat", Multiplier = 9}, | |
{Name = "Dog", Multiplier = 7.534}, | |
} | |
currentResult = 1 | |
sm:To("EggAnim") | |
return true | |
end | |
local eggAnimTime = 1 | |
sm:DeclareState("EggAnim", function() | |
print("HATCHING!!!") | |
print("HATCHING!!!!!") | |
print("HATCHING!!!!!!") | |
print("HATCHING!!!!") | |
sm:To("Results") | |
end) | |
sm:DeclareState("Results", { | |
Enter = function() | |
resultSm:To("Start") | |
sm:To("Connect") | |
print("You hatched", #results, "pets!") | |
end | |
}) | |
local function connectedHatchOneInput() | |
onUserInteraction() | |
sm:To("Disconnect") | |
Hatch(3) | |
end | |
local function disconnectedHatchOneInput() | |
print("Calling disconnected hatch one input!") | |
end | |
local hatchOneInput = disconnectedHatchOneInput | |
local isInZone = true | |
sm:DeclareState("Connect", function() | |
if not isInZone then return print("Connect: not in zone!") end | |
print("Connect: Connecting inputs") | |
hatchOneInput = connectedHatchOneInput | |
end) | |
sm:DeclareState("Disconnect", function() | |
print("Disconnect: Disconnecting inputs") | |
hatchOneInput = disconnectedHatchOneInput | |
end) | |
sm:To("Connect") | |
hatchOneInput() | |
goNextResult() | |
goNextResult() | |
goNextResult() | |
hatchOneInput() | |
goNextResult() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment