local a=game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local L=require(a:WaitForChild("\078\101\118\101\114\109\111\114\101",5))local I=L("\083\116\100")local R=I.rquery local X=I.uuid local P={__type="\115\116\097\116\101";StateId=nil;Name=nil,InternalData={},NextStatesNames={},NextStates={};Wait=.2;IsRunning=false}local A={__index=P}function P.new(a)local L=setmetatable({},A)L.Name=a or"\085\078\078\065\077\069\068\095\083\084\065\084\069"L.StateId=X()return L end function P.PrintInternalDataTable(a)print("------------------------------------")print("  \073\110\116\101\114\110\097\108 \100\097\116\097 \102\111\114 "..a.Name)for a,L in pairs(a.InternalData)do print("    - "..(tostring(a)..("\058 "..tostring(L))))end print("------------------------------------")end function P.CanTransitionFrom(I,a,L)error("\078\111 \116\114\097\110\115\105\116\105\111\110 \102\117\110\099 \115\112\101\099\105\102\105\101\100")end function P.StateStoppedCallback(a)return end function P.CanPerformAction(L,a)error(L.Name.." \109\105\115\115\105\110\103 \067\097\110\080\101\114\102\111\114\109\065\099\116\105\111\110\040\041 \105\109\112\108\101\109\101\110\116\097\116\105\111\110")end function P.Action(a)error(a.Name.." \109\105\115\115\105\110\103 \065\099\116\105\111\110\040\041 \105\109\112\108\101\109\101\110\116\097\116\105\111\110")return false end function P.Init(a)print("\073\110\105\116\105\097\108\105\122\105\110\103 \083\116\097\116\101\058 "..a.Name)end local i={MachineId="\110\105\108",CurrentState={},States={},Context={Items={}}}local q={__index=i}function i.new()local a=setmetatable({},q)a.MachineId=X()return a end function i.NewState(L,a)local I=R.DeepCopyTable(P.new(a))return I end function i.Stop(L,a)a.IsRunning=false a:StateStoppedCallback(L.Context)end function i.AddState(L,a)L.States[a.Name]=a for I,R in pairs(a.NextStatesNames)do if L.States[R]~=nil then a.NextStates[R]=L.States[R]end end for L,I in pairs(L.States)do for L,R in pairs(I.NextStatesNames)do if R==a.Name then I.NextStates[R]=a end end end end function i.ClearContextItems(a)for a,L in pairs(a.Context.Items)do L=nil end a.Context.Items=nil a.Context.Items={}end function i.TryTransition(L,a)for I,R in pairs(L.CurrentState.NextStates)do if R:CanTransitionFrom(L.CurrentState,L.Context,a)then L:Stop(a)L:Start(R,a)return true end end return false end function i.Next(L,a)if L.CurrentState:CanPerformAction(a)then L.CurrentState:Action()else L:Update()end end function i.Start(I,a,L)if a==nil then a=I.CurrentState if a==nil then error("\078\111 \115\116\097\116\101 \112\097\115\115\101\100 \097\110\100 \110\111 \115\116\097\116\101 \115\101\116 \097\115 \067\117\114\114\101\110\116\083\116\097\116\101")return nil end end I.CurrentState=a I.CurrentState:Init(I.Context,L)local R=function()I:Next()end local X=coroutine.create(R)local P,A=coroutine.resume(X)if P then I.CurrentState.IsRunning=true else error(A)return nil end end function i.Update(L,a)if L:TryTransition(a)then return true else L:Next(a)end end function i.PrintStateGraph(a)local function L(a)print(a.Name)for a,L in pairs(a.NextStates)do print("    - \078\101\120\116 \083\116\097\116\101 \078\097\109\101\058 "..L.Name)end end for a,I in pairs(a.States)do L(I)print("    -------------    ")end end local k={_machineProto=i;_stateProto=P}function k.NewStateMachine()local a=R.DeepCopyTable(k._machineProto.new())a.MachineId=X()return a end function k.NewState(a)local L=R.DeepCopyTable(k._stateProto.new())L.Name=a return L end return k
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local require = require(ReplicatedStorage:WaitForChild("Nevermore", 5))

local std = require("Std")
local rq = std.rquery
local uuid = std.uuid

local stateProto = {
	__type = "state",
	StateId = nil,
	Name = nil,
	InternalData = {},
	NextStatesNames = {},
	NextStates = {},
	Wait = 0.2,
	IsRunning = false
}

local stateMeta = { __index = stateProto }

function stateProto.new(name)
	local self = setmetatable({}, stateMeta)
	self.Name = name or "UNNAMED_STATE"
	self.StateId = uuid()
	return self
end

function stateProto:PrintInternalDataTable()
    print("------------------------------------")
    print("  Internal data for " .. self.Name)
    for name, value in pairs(self.InternalData) do
        print("    - " .. tostring(name) .. ": ".. tostring(value))
    end
    print("------------------------------------")
end

function stateProto:CanTransitionFrom(previousState, data)
	error("No transition func specified")
end

function stateProto:StateStoppedCallback()
	return
end

function stateProto:CanPerformAction(data)
	error(self.Name .. " missing CanPerformAction() implementation")
end

function stateProto:Action()
	error(self.Name .. " missing Action() implementation")
	return false
end

function stateProto:Init()
	print("Initializing State: " .. self.Name)
end

local machineProto = {
	MachineId = "nil",
	CurrentState = {},
	States = {},
	Context = {
		Items = {}
	}
}
local machineMeta = { __index = machineProto }

function machineProto.new()
	local self = setmetatable({}, machineMeta)
	self.MachineId = uuid()
	return self
end

function machineProto:NewState(stateName)
	local stateInstance = rq.DeepCopyTable(stateProto.new(stateName))
	return stateInstance
end

function machineProto:Stop(stateToStop)
	--print("Stopping " .. state.Name)
	stateToStop.IsRunning = false
	stateToStop:StateStoppedCallback(self.Context)
end

function machineProto:AddState(state)
	self.States[state.Name] = state
	for _, nextStateName in pairs(state.NextStatesNames) do
		if self.States[nextStateName] ~= nil then
			state.NextStates[nextStateName] = self.States[nextStateName]
		end
	end

	for _, existingState in pairs(self.States) do
		for _, nextStateName in pairs(existingState.NextStatesNames) do
			if nextStateName == state.Name then
				existingState.NextStates[nextStateName] = state
			end
		end
	end
end

function machineProto:ClearContextItems()
	for _, item in pairs(self.Context.Items) do
		item = nil
	end
	self.Context.Items = nil
	self.Context.Items = {}
end

function machineProto:TryTransition(data)

	for _, nextState in pairs(self.CurrentState.NextStates) do
		if nextState:CanTransitionFrom(self.CurrentState, self.Context, data) then
			self:Stop(data)
			self:Start(nextState, data)
			return true
		end
	end
	return false
end

function machineProto:Next(data)
	if self.CurrentState:CanPerformAction(data) then
		self.CurrentState:Action()
	else
		self:Update()
	end
	--wait(state.WaitTime)
end

function machineProto:Start(startingState, data)
	if startingState == nil then
		startingState = self.CurrentState
		if startingState == nil then
			error("No state passed and no state set as CurrentState")
			return nil
		end
	end
	self.CurrentState = startingState

	self.CurrentState:Init(self.Context, data)
	local runDelegate = function()
		self:Next()
	end
	local thread = coroutine.create(runDelegate)
	local success, errorMsg = coroutine.resume(thread)
	if success then
		self.CurrentState.IsRunning = true
	else
		error(errorMsg)
		return nil
	end
end

function machineProto:Update( data )
	if self:TryTransition(data) then
		return true
	else
		self:Next(data)
	end
	--wait(self.CurrentState.WaitTime)
end

function machineProto:PrintStateGraph()
	local function printNextStates(stateToPrint)
		print(stateToPrint.Name)
		for _, stuffs in pairs(stateToPrint.NextStates) do
			print("    - Next State Name: " .. stuffs.Name)
		end
	end
	
	for _, st in pairs(self.States) do
		printNextStates(st)
		print("    -------------    ")
	end
end

local machineMachine = {
	_machineProto = machineProto,
	_stateProto = stateProto
}

function machineMachine.NewStateMachine()
	local machineInstance = rq.DeepCopyTable(machineMachine._machineProto.new())
	machineInstance.MachineId = uuid()
	return machineInstance
end

function machineMachine.NewState( newStateName )
	local stateInstance = rq.DeepCopyTable(machineMachine._stateProto.new())
	stateInstance.Name = newStateName
	return stateInstance
end

return machineMachine