Cancel System for Combat System

[ explanation/begin ]

Now I would like to talk about the Cancel System. Everybody knows for what it is. This system must cancel or clean the attacks of players. But mostly it prevents one player from damaging other one which depends on time. As people say first wins always. Soo, as you’re already into it… I have many things which can get better but Ive got no idea how to do that…

[ What I had done ]

[ — 1 — ]

I have tried many of things which should work… For example I did repeat-until system which waits for variable “stunned” getting added to cancel the move from going on. Also I did system which sets the fire-event and if its getting called then it makes thread to stop; this system looked like:

--server-side (using skill, just example):
local animation = plr.character.humanoid:LoadAnimation(game.ReplicatedStorage.Animations.Anim1); -- setups animation track
game.ReplicatedStorage.CancelEvent.OnServerEvent:Connect(function(target) -- setups global fire server event to be called. It compares if its called for the right player who must be cancelled by this calling
        if target == plr.Character then -- comparing if its right player which we want to cancel
                animation:Stop() -- doing something... Also we can close the coroutine, using coroutine.close(thread)
        end
end)
animation:Play() -- main thread which keeps working

[ — 2 — ]

Also I learned many things from previous one… But I wanted to do good-looking system which is like wardrobe. I made the structure:

  • Script must have entry point and also it must have clean function which cleans all this mess if it suddenly stops. Its look became like this:
skills = {
       ["skill"] = {
              ["Clean"] = function()
              end,
              ["Main"] = function()
              end,
       },
}

local cancels = {}
function UseSkill(skill_name)
      local Table = skills[skill_name] or nil
      if table == nil then 
              warn("[client-side] This skill doesn't exist! Argument is bad!")
      return end
      local skill = coroutine.create(skills[skill_name]["Main"])
      local lp = game.Players.LocalPlayer
      local remotes = lp.Character.Remotes -- its folder where remotes are, its just example
      
      cancels[skill_name] = { --creates cancel handler, it creates like that to prevent bunch of skills' mixing up
            OriginTable = Table,
            CurrentThread = skill,
            Clean = function(this)
                      if coroutine.status(this.CurrentThread) ~= "dead" then
                              coroutine.close(this.CurrentThread)
                              this.OriginTable["Clean"]()
                      end
            end
      }
      remotes.CancelClientSkill.OnClientEvent(function()
              for _, v in pairs(cancels) do 
                        v:Clean() -- the same as v.Clean(v)
              end
              for i, v in pairs(cancels) do 
                        cancels[i] = nil -- clean it not to do double clean
              end
      end)
end

[what do i want]

I wanna make system which has the most accurate cancelling, which cancels to prevent one player from damaging other if other one is attacking. I just need improvement of my code, so that’s why i’m here. I want to make cancel system which cancels attacks depending on time: for example, two players used the same attack to hit each other but first uses it faster than other one, so first player must cancel the attack of other one blocking from damage…

All you really need to see is if a value has changed while a move is going on to cancel it.

stunCount = 0 
function Stunned()
     stunCount += 1
end

function Attack()
     local lastStun = stunCount
     
     task.wait(0.4)
     if lastStun ~= stunCount then return end
     dohitstuff()
     
     --let's say this attack hits twice or something.
     task.wait(0.2)
     if lastStun ~= stunCount then return end
     dohitstuff()
end

Any time the script is waiting, that is when you’d want to check if a new stun has occured.