Hi,
I am making a sword module to make it easy to make my type of swords… The module consists of type checking and public methods.
I am wondering if I can fix my messy .new constructor. It doesn’t look good in my opinion because of my findfirstchild(“animations”).
Is it possible to remove findfirstchild with ‘typechecking’?
I am very new to making classes
Sword Class::
--!strict
local Sword = {}
Sword.__index = Sword
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--local RunService = game:GetService("RunService")
local Source = ReplicatedStorage.Source
local RaycastHitbox = require(Source.RaycastHitboxV4)
local Trove = require(Source.Packages.Trove)
local ContextActionUtility = require(Source.ContextActionUtility)
--local LocalPlayer = Players.LocalPlayer
local ATTACK_IMG = "rbxassetid://5743593320"
-- constants
local Debounce = false
local hitSounds = 0
type tab = {
Damage: number,
Yield: number,
AttackTime: number,
StartDelay: number,
EndDelay: number,
Combos: number,
ComboReset: number,
Holster: boolean, -- if holster or no...
Special: boolean, -- if special attacks or no...
}
type self = {
_sword: Tool,
_animations: Folder,
_settings: tab,
_connections: Trove.ClassType,]
_hitbox: RaycastHitbox.ClassType,
_loadedAnimations: any,
_loadedSounds: any,
_combo: number,
_lastClicked: any,
}
export type Sword = typeof(
setmetatable({} :: self, {__index = Sword})
)
function Sword.new(sword: Tool, setting: tab, Player: Player): Sword
local self = setmetatable({}, Sword)
local Animator = Player:FindFirstChild("Character"):FindFirstChild("Humanoid"):FindFirstChild("Animator"):: Animator
self._sword = sword
self._animations = sword:FindFirstChild("Animations"):: Folder
self._settings = setting
self._connections = Trove.new()
self._hitbox = RaycastHitbox.new(self._sword:FindFirstChild("Handle"))
-- constants
self._loadedAnimations = {}
self._loadedSounds = {}
self._combo = 0
self._lastClicked = tick()
local Animations = self._sword:FindFirstChild("Animations"):: Folder
local Parameters = RaycastParams.new()
Parameters.FilterDescendantsInstances = {Player.Character}
self._loadedAnimations.Equip = Animator:LoadAnimation(Animations:FindFirstChild("Equip"):: Animation)
self._loadedAnimations.Idle = Animator:LoadAnimation(Animations:FindFirstChild("Idle"):: Animation)
self._loadedAnimations.Swing1 = Animator:LoadAnimation(Animations:FindFirstChild("Swing1"):: Animation)
self._loadedAnimations.Swing2 = Animator:LoadAnimation(Animations:FindFirstChild("Swing2"):: Animation)
self._loadedAnimations.Swing3 = Animator:LoadAnimation(Animations:FindFirstChild("Swing3"):: Animation)
for _,v in ipairs(self._sword:FindFirstChild("Handle"):GetChildren()) do
local hitString = "Hit"..tostring(_)
if self._sword:FindFirstChild("Handle"):FindFirstChild(hitString) then
self._loadedSounds[hitString] = self._sword:FindFirstChild("Handle"):FindFirstChild(hitString):: Sound
hitSounds += 1
end
end
self._loadedSounds.Equip = self._sword:FindFirstChild("Handle"):FindFirstChild("Equip"):: Sound
self._loadedSounds.Swing1 = self._sword:FindFirstChild("Handle"):FindFirstChild("Swing1"):: Sound
self._loadedSounds.Swing2 = self._sword:FindFirstChild("Handle"):FindFirstChild("Swing2"):: Sound
self._loadedSounds.Swing3 = self._sword:FindFirstChild("Handle"):FindFirstChild("Swing3"):: Sound
self._hitbox.RaycastParams = Parameters
self._connections:Connect(self._sword.Equipped, function(mouse: Mouse)
self:Equip()
self:Bind(self._sword.Name, Enum.UserInputType.MouseButton1)
end)
self._connections:Connect(self._sword.Unequipped, function(mouse: Mouse)
self:Unequip()
self:Unbind(self._sword.Name)
end)
self._hitbox.OnHit:Connect(function(hit, humanoid, results)
if humanoid.Health ~= 0 then
local RandomHit = math.random(0,hitSounds)
humanoid:TakeDamage(self._settings.Damage)
if RandomHit == 0 and hitSounds ~= 0 then
self._loadedSounds.Hit1:Play()
else
self._loadedSounds["Hit"..RandomHit]:Play()
end
end
end)
self._hitbox.Visualizer = true
return self
end
-- methods
function Sword:Equip()
self._loadedSounds.Equip:Play()
self._loadedAnimations.Equip:Play()
self._loadedAnimations.Idle:Play()
end
function Sword:Unequip()
if self._loadedAnimations.Idle.IsPlaying then
self._loadedAnimations.Idle:Stop()
end
if self._loadedAnimations.Equip.IsPlaying then
self._loadedAnimations.Equip:Stop()
end
end
function Sword:Attack(InputState)
if InputState ~= Enum.UserInputState.Begin or Debounce then
return
end
if tick() - self._lastClicked >= self._settings.ComboReset then -- combo reset
self._combo = 0
end
self._lastClicked = tick()
Debounce = true
if self._combo < self._settings.Combos then
self._combo += 1
else
self._combo = 1
end
local swingString = "Swing"..tostring(self._combo)
print(self._combo, swingString)
self._loadedAnimations[swingString]:Play()
task.wait(self._settings.StartDelay:: number)
self._loadedSounds[swingString]:Play()
self._hitbox:HitStart()
task.wait(self._settings.Yield:: number)
self._hitbox:HitStop()
task.wait(self._settings.EndDelay:: number)
Debounce = false
end
function Sword:Holster()
end
function Sword:Special()
end
function Sword:Bind(ActionName: string, Input)
ContextActionUtility:BindAction(ActionName,function(actionName,inputState)
self:Attack(inputState)
end, true,Input)
ContextActionUtility:SetImage(ActionName,ATTACK_IMG)
end
function Sword:Unbind(ActionName: string)
ContextActionUtility:UnbindAction(ActionName)
end
function Sword:Destroy()
self._connections:Destroy()
end
return Sword
Also… if you see any bad practices with the public methods, it won’t hurt to help!!!