This code serves as a middle ground between the player’s input and the action they want to perform. It validates actions and handles player commands based on state changes. I’m unsure about the cancel logic, so I’d appreciate feedback on that and any other suggestions for improvement.
--[[
Event driven action service.
Flow:
- User presses key, server receives it.
- Server will run the perform function with the parameters
- The :Perform() function will first validate the action
- If the action isn't valid at the moment, queue the action if the player is still holding the key. (InputState boolean)
- If it is valid, run the action like normal.
Note: ProcessQueuedAction and CheckForCancel functions are ran every time a state is changed. Hence why I check if the states are relevant before doing anything.
This is my first time doing something like this, and I'm simply exploring other ways to handle actions in games. This is for a Battlegrounds game if you haven't guessed.
]]
local ServerStorage = game:GetService("ServerStorage")
local Modules = ServerStorage:WaitForChild("Modules")
local CooldownManager = require(Modules.CooldownManager)
local Invalidators = require(Modules.Invalidators)
local StateManager = require(Modules.StateManager)
local Actions = {}
function Actions:ValidateAction(playerObject, action)
local InvalidatingStates = Invalidators[action] or {}
local isAllowed = true --// assume we are allowed to do the action until proven otherwise
for state, bool in InvalidatingStates do
if StateManager:CheckState(playerObject, state) == true then
isAllowed = false
break
end
end
local InCooldown = CooldownManager:CheckCooldown(playerObject, action)
if InCooldown then
isAllowed = false
end
return isAllowed
end
function Actions:CheckForCancel(playerObject, stateString: string, state: boolean)
if stateString == "Hit" or stateString == "Stunned" then
if not playerObject.CurrentAction then
return
end
local invalidator = Invalidators[playerObject.CurrentAction.Name]
if not invalidator then return end
if invalidator[stateString] and state == true and playerObject.CurrentAction.Name ~= "Attacking" then
playerObject.CurrentAction.Cancel()
playerObject.CurrentAction = nil
end
end
end
function Actions:QueueActionToPlayer(playerObject, action, ...)
local args = ...
playerObject.QueuedAction = {
Name = action,
Callback = function()
Actions:Perform(playerObject, action, args)
end,
}
print("QUEUED")
end
function Actions:ClearQueue(playerObject)
playerObject.QueuedAction = nil
end
function Actions:CheckQueue(playerObject)
return playerObject.QueuedAction ~= nil
end
function Actions:ProcessQueuedAction(playerObject, stateString)
local queuedAction = playerObject.QueuedAction
if not queuedAction then return end
local InvalidatingStates = Invalidators[queuedAction.Name]
if not InvalidatingStates then
return
end
local IsRelevant = InvalidatingStates[stateString] ~= nil
if not IsRelevant then
return
end
local isAllowed = Actions:ValidateAction(playerObject, queuedAction.Name)
if isAllowed then
queuedAction.Callback()
Actions:ClearQueue(playerObject)
end
end
--// the vararg itself is a table
function Actions:Perform(playerObject, action, ...)
local ActionToPerform = require(script:FindFirstChild(action))
local player = playerObject.player
local args = ...
local isAllowed = Actions:ValidateAction(playerObject, action)
--// sorry for making this part so messy
if not isAllowed then
if args == nil then return end
if args.InputState == true then
Actions:QueueActionToPlayer(playerObject, action, args)
elseif args.InputState == false then
Actions:ClearQueue(playerObject)
playerObject.CurrentAction = nil
end
return
end
playerObject.CurrentAction = {
Name = action,
Args = args,
StartedAt = os.clock(),
Cancel = function()
if not args then
args = {}
end
args.Cancel = true
ActionToPerform(player, playerObject, args)
playerObject.CurrentAction = nil
end,
}
ActionToPerform(player, playerObject, args)
end
function Actions:UpdateOnStateChange(playerObject, stateString, state: boolean) --// update on state change
Actions:CheckForCancel(playerObject, stateString, state)
Actions:ProcessQueuedAction(playerObject, stateString)
end
--// perform an action without needing validation
function Actions:ForcePerform(playerObject, action, ...)
local ActionToPerform = require(script:FindFirstChild(action))
local player = playerObject.player
local args = ...
ActionToPerform(player, playerObject, args)
end
return Actions