Context
Hey, I’m currently scripting a combat system that allows the player to swing in 3 different motions; High, Middle or Low (heavy inspiration from For Honor). I thought it would be easiest to have Middle Swing be Left Click (MouseButton1), Low Swing be Right Click (MouseButton2), and High Swing be Left & Right Click together. Since, obviously, a player will never press two buttons at the exact same time on the exact same frame, I’ve scripting a system that does the following.
- Logs the first button you press (Middle or Low Swing; M1 or M2)
- Sends that info to the Local Script (inside the weapon tool)
- If another button is pressed within a 0.03 second window, send signal to swing High
The calculation to check if another button is pressed in a 0.03 window is as follows:
--this script is inside of a function that runs whenever the LocalScript sends the signal that the player pressed M1 or M2
--lastInputTime: the tick() of the input that was just pressed (M1 or M2)
--inputTime: the time of the FIRST input that was pressed (nil if you never pressed a button yet; get set back to nil when High Swing signal is sent)
--swingEvent: the Remote Event that sends the signal
if lastInput == nil then
lastInput = input
lastInputTime = tick()
else
local timeGap = lastInputTime - inputTime
print(timeGap)
lastInputTime = tick()
if (timeGap * -1) <= 0.03 then
tnputTime = nil
warn("Swing High")
swingEvent:FireClient(player, "HIGH") --Send "HIGH" signal to player; Swing High
else
swingEvent:FireClient(player, input) --Time Gap was too long; player only pressed one button; allow the player to swing Middle or Low without overriding.
end
end
Example: If i press M1 then M2 in quick succession, the script will send the signal for Middle, then High. (lastInput is now “MIDDLE” (M1); inputTime is now the tick() that I pressed M2).
The Problem
There are several problems here:
- The script will not read the very first input when you join the game (because
lastInput
is nil) - The script sends the signal to swing Middle or Low BEFORE sending the signal to swing High
- The script cannot tell if the last input is the same button, so if a player uses an autoclicker or somehow manages to press the same button twice within 0.03 seconds the server will send the signal to swing high.
I thought a simple way to fix most of these issues would be to make the script wait 0.03 seconds before firing the first input, and if the other input is fired in that time, then swing High. The only problem is I have no idea how to do this. I’ve attempted it, but it just made it worse so I reverted the changes.
If anyone would like to review these, here they are:
Also if anything is unclear, please let me know.
TOOL SCRIPT:
local CAS = game:GetService("ContextActionService")
local UIS = game:GetService("UserInputService")
local repStor = game:GetService("ReplicatedStorage")
local attackEvent = repStor.AttackInput
local WEAPON_SWINGMIDDLE = "SwingMiddle"
local WEAPON_SWINGLOW = "SwingLow"
local WEAPON_SWINGHIGH = "SwingHigh"
local deciding = false
local tool = script.Parent
local swingingAtt = tool:GetAttribute("Swinging")
--HIGH SWING CHECK (M1 + M2)
local function highSwingCheck(decision)
print(decision)
if decision == "LOW" then
--PERFOM SWING CODE
wait(1) --example swing time
tool:SetAttribute("Swinging", false)
elseif decision == "MIDDLE" then
--PERFOM SWING CODE
wait(1)
tool:SetAttribute("Swinging", false)
elseif decision == "HIGH" then
--Perform High swing Code
wait(1)
tool:SetAttribute("Swinging", false)
end
end
--[=========================================================================================================]
--SWING MIDDLE CODE
local function weaponSwingMiddle(actionName, inputState, _inputObject)
if actionName == WEAPON_SWINGMIDDLE and inputState == Enum.UserInputState.Begin then
tool:SetAttribute("Swinging", true)
attackEvent:FireServer("MIDDLE", tick())
attackEvent.OnClientEvent:Connect(highSwingCheck)
end
end
tool.Equipped:Connect(function()
CAS:BindAction(WEAPON_SWINGMIDDLE, weaponSwingMiddle, false, Enum.UserInputType.MouseButton1)
end)
tool.Unequipped:Connect(function()
CAS:UnbindAction(WEAPON_SWINGMIDDLE)
end)
--[=========================================================================================================]
--SWING LOW CODE
local function weaponSwingLow(actionName, inputState, _inputObject)
if actionName == WEAPON_SWINGLOW and inputState == Enum.UserInputState.Begin then
tool:SetAttribute("Swinging", true)
attackEvent:FireServer("LOW", tick())
attackEvent.OnClientEvent:Connect(highSwingCheck)
end
end
tool.Equipped:Connect(function()
CAS:BindAction(WEAPON_SWINGLOW, weaponSwingLow, false, Enum.UserInputType.MouseButton2)
end)
tool.Unequipped:Connect(function()
CAS:UnbindAction(WEAPON_SWINGLOW)
end)
SERVER SCRIPT:
local repStor = game:GetService("ReplicatedStorage")
local swingEvent = repStor.AttackInput --RemoteEvent
local lastInput = nil
local lastInputTime = nil
local waitingInput = nil
swingEvent.OnServerEvent:Connect(function(player, input, inputTime)
if lastInput == nil then
lastInput = input
lastInputTime = tick()
else
local timeGap = lastInputTime - inputTime
print(timeGap)
lastInputTime = tick()
if (timeGap * -1) <= 0.03 then
waitingInput = nil
warn("SWING HIGH")
swingEvent:FireClient(player, "HIGH")
else
swingEvent:FireClient(player, input)
end
end
end)