Hello developers,
I’ve seen a lot of posts about people who are struggling with creating swords. I myself have in the past struggled with this as well, I could never find a post or video that could give me a good straight-forward answer. Even after I figured out how to make an effective sword, it’d take me about 30mins-2hrs to create one. Using Sword Smith, it only take 5-10 minutes to create a good sword, every sword only has to be around 20-30 lines of code, depending on your purposes. Sword Smith handles hit detection, animation playing, adding to kills leaderstats, trails, damaging, and more. I would like to note that this is some pretty valuable information in the Basic Usage category if you’re planning on using this.
Documentation
Functions
- :SetTrail(property, value)…Sets a property of the sword’s trail to the given value
- :GetOwner()…Returns the player instance which has the tool
- :Swing(dmg, ignoreList)…Plays swinging animations and damages targets
- :On[event](callback)…Calls function when event is fired
- :BindAction(key, debounce, callback)…Creates a special attack
Events
Some of these are pretty self-explanatory
- Activated…Fired when :Swing function is called
- Equipped…Fired when sword is equipped
- Unequipped…Fired when sword is unequipped
- HumanoidHit…Fired when an “live” target is hit when sword is swinging
- ObjectHit…Fired when handle is touched
- SwiningEnded…Fired when swing is officially over
- TargetKilled…Fired when a target is killed
- CastPointHit…Fired when a node on the sword is hit during a swing
- CriticalHit…Fired every swing in sync with the critical interval
Basic Usage
Creating a Basic Sword
I’d like to note that since this post really isn’t a tutorial, I won’t be going super in-depth. First thing’s first, you need to insert the Sword Smith module in ServerStorage
.
Create a sword model from scratch or find one from the tool box, personally I’ll be using The Sword of Azurewrath from the toolbox. Just make sure that when you take a sword from the tool box, you remove all objects beside the handle, and of course anything suspicious that may be inside the handle. Create a new script inside the tool.
Inside this script we must first get the Sword Smith module from ServerStorage
.
local ServerStorage = game:GetService("ServerStorage")
local SwordSmith = require(ServerStorage:WaitForChild("SwordSmith"))
Next we can create the sword instance using the module like so:
local ServerStorage = game:GetService("ServerStorage")
local SwordSmith = require(ServerStorage:WaitForChild("SwordSmith"))
local tool = script.Parent
local debounce = false
local data = {swingAnims={}} --This is a table of information the module uses to make the sword work
--In the swing anims table insert the IDs of swing animation(s) you would like to use in the sword
local sword = SwordSmith.new(tool, data)
Next we can create the swinging
local ServerStorage = game:GetService("ServerStorage")
local SwordSmith = require(ServerStorage:WaitForChild("SwordSmith"))
local tool = script.Parent
local debounce = false
local data = {swingAnims={}}
local sword = SwordSmith.new(tool, data)
tool.Activated:Connect(function()
if debounce then
return
end
debounce = true
sword:Swing()
end);
sword:OnSwingEnded(function()
--You can of course add extra delay if you have a short swing animation
debounce = false
end)
This should be your final result if you followed these steps correctly:
Kills Leaderstat
Leaderstats are a pretty simple concept, however I’ll give a quick refresher regardless. !The script given here will not save!
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local kills = Instance.new("IntValue")
kills.Name = "Kills"
kills.Value = 0
kills.Parent = leaderstats
end)
Sword Smith will not automatically know that you want to make it add to the kills leaderstats, so there needs to be some adjustments made to the data table. It should now look more along the lines of this:
local data = {
swingAnims = {6947086383},
killsStat = {
StatName = "Kills"
}
}
Sword Smith is also compatable with DataStore2 by @Kampfkarren when the data table is told so:
local data = {
swingAnims = {6947086383},
killsStat = {
isDataStore2 = true,
key = 'whatever your kills stat key is'
}
}
Basic Properties
As seen before, the Sword Smith module uses properties such as swingAnims
and killsStat
, which we used before. Since most of them are quite self-explanatory, I’ll just leave a code-block with some comments below.
local data = {
swingAnims = { --For swords compatable with both rig-types
R6 = {00000},
R15 = {00000}
},
holdAnims = { --List of animations played in sequence when sword is equipped
0000,
0000,
}
killsStat = {
StatName = "Kills"
},
Trail = false, --The automatically generated trails are quite-buggy, so creating your own may be a good choice
ignoreList = {game.Players.Arc_Cosine.Character}, --Characters of people who are uneffected by the sword
killPlayers = false --If you only want the sword to kill NPCs, set this to false
}
Dual Wielding
Dual wield swords are possible using Sword Smith, the duplicates will have the same features as the original blade, is equipped and unequipped the same time as the original blade, and damages players like the original blade.
data = {
dualWield = true,
};
However sometimes the sword won’t be facing the correct direction, giving for some funny results. To fix this you can change the offset for the new blade’s CFrame.
local data = {
C1 = CFrame.new(0, 0, -2.3) * CFrame.fromEulerAnglesXYZ(-math.rad(90), 0, 0), --This won't work for every sword, you may have to mess around with the settings a bit
dualWield = true,
};
Special Attacks
A special attack can be added to your sword through the function: :BindAction(key, callback)
, which sets up a special attack that activates when a specific key is pressed. To begin setup of an action it’s important to know that Sword Smith now creates a remote event called Action
in ReplicatedStorage
to setup actions, if the name of this remote event interferes with things you’ve already created in your game you can edit the Sword Smith module and find the ActionEvent
property:
Change the value to the name you want the event to have.
Now in our sword script, we can now set up the special attack. I personally will be making a fireball attack similar to that from @Alvin_Blox 's fireball magic tutorial.
local sword = SwordSmith.new(tool, data)
local Debounce = 5--Number of seconds between when the attack can be played
sword:BindAction("E", Debounce, function(player) --The player must press E to do the special attack
local root = player.Character.HumanoidRootPart
local fireball = Instance.new("Part")
fireball.CanCollide = false
fireball.Material = Enum.Material.Neon
fireball.BrickColor = BrickColor.Red()
fireball.Shape = Enum.PartType.Ball
fireball.Size = Vector3.new(3, 3, 3)
fireball.CFrame = root.CFrame * CFrame.new(0, 0, -2)
local fire = Instance.new("Fire")
fire.Size = 5
fire.Parent = fireball;
local velocity = Instance.new("BodyVelocity")
velocity.MaxForce = Vector3.new(1,1,1)*5000
velocity.Velocity = root.CFrame.lookVector * 100
velocity.Parent = fireball
fireball.Parent = workspace
local touch do
touch = fireball.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid and not hit:IsDescendantOf(root.Parent) then
humanoid:TakeDamage(25)
if touch ~= nil then touch:Disconnect() end
end
end)
end
end)
And now the special attack is finished. Some tweaking may need to take place in your data table however. If you have two different swords, a fire sword and a water sword for example. There are multiple copies of each of those swords, but they both have different attacks. You can assign a type to your swords.
data = {
type = "Fire",
};
or
data = {
type = "Water"
}
Knockback
Knockback is a pretty straight-forward concept so I won’t spend much time going over it. When a victim is hit they’re flung in the direction the attacker is facing. Firstly to add a knockback system using Sword Smith you can begin by enabling it.
local data = {
knockBackEnabled = true,
}
For more customization you can change the strength, which by default is 50.
local data = {
knockBackEnabled = true,
knockBackStrength = 100,
}
Critical Hits
A critical hit is when you swing your sword multiple times and then one of those swings causes more damage. Which is why it’s called a critical hit, it’s a hit that packs more of a punch. Through use of the data table, it’s possible to create critical hits.
local data = {
criticalHits = 45, --How much damage is dealt to the target on a critical hit
criticalInterval = 3, --How many swings it takes until a critical hit happens
}
Source
--Module written by: Arc_Cosine
local sword = {
_VERSION = "1.11", --Don't change
Settings = {
['DebugMode'] = false,
["ActionEvent"] = "Action",
}
};
sword.__index = sword;
local Players = game:GetService("Players");
local Debris = game:GetService("Debris");
local RunService = game:GetService("RunService");
local ServerStorage = game:GetService("ServerStorage");
local ReplicatedStorage = game:GetService("ReplicatedStorage");
local InsertService = game:GetService("InsertService");
local function loadDependency(name)
return require(script:WaitForChild("Dependencies"):WaitForChild(name));
end;
local function If(con, Then, Else)
if con then
return Then else return Else;
end;
end;
local Dependencies = {
['DataStore2'] = require(1936396537),
};
local Settings = sword.Settings;
local Events = {
'__onActivatedCallbacks' ;
'__onEquippedCallbacks' ;
'__onUnequippedCallbacks' ;
'__onHumanoidHitCallbacks' ;
'__onObjectHitCallbacks' ;
'__onSwingEndedCallbacks' ;
'__onTargetKilledCallbacks' ;
'__onCastPointHitCallbacks' ;
};
local baseURL = "rbxassetid://%d";
do
if script.Parent:GetFullName() ~= "Model" then
print([[
SwordSmith is now loading!
]]);
local currentVersion =
{
high = sword._VERSION:match'%d+',
low =({ sword._VERSION:gsub('%d+%.','') })[1],
};
local newAssetid = 6946292010;
local alternateModel = require(InsertService:LoadAsset(newAssetid):WaitForChild("SwordSmith"));
local alternateVersion =
{
high = alternateModel._VERSION:match'%d+',
low = ({ alternateModel._VERSION:gsub('%d+%.','') })[1],
};
local migrateTo =
{
tonumber(currentVersion.high) < tonumber(alternateVersion.high),
tonumber(currentVersion.low) < tonumber(alternateVersion.low),
}; for _, bool in pairs(migrateTo) do
if bool then
warn("Sword Smith is updating!", "No action is required.");
return alternateModel;
end;
end;
end;
for event = 1, #Events do
local index = Events[event];
local funcName = index:gsub("__on", "On"):gsub("Callbacks", "");
sword[funcName] = function(self, callback)
table.insert(self[index], callback);
end;
end;
end
function sword.new(tool, data)
assert(tool:IsA("Tool") and tool:FindFirstChild("Handle") ~= nil,
"Invalid tool.");
local data = data or {};
local self = {
handle = tool.Handle,
hitBox = nil,
dualWield = data.dualWield,
C1 = data.C1,
knockBackEnabled = data.knockBackEnabled,
knockBackStrength = data.knockBackStrength or 5*10,
swingAnims = data.swingAnims,
holdAnims = data.holdAnims,
lastAnim = nil,
hasTrail = If(data.Trail ~= nil, data.Trail),
trail = nil,
hitBoxOffset = If(data.hitBoxOffset ~= nil, data.hitBoxOffset, 1),
killPlayers = If(data.killPlayers ~= nil, data.killPlayers, true),
ignoreList = If(data.ignoreList ~= nil, data.ignoreList, {}),
killsStat = If(data.killsStat ~= nil, data.killsStat, nil),
keyBinds = {},
type = data.type or "None",
equipped = false,
swinging = false,
currentDamage = 0,
};
local newSwordInstance do
sword[tool] = setmetatable(self, sword);
newSwordInstance = sword[tool];
for event = 1, #Events do
newSwordInstance[Events[event]] = {};
end;
end;
newSwordInstance:__GeneratePoints();
local hitBox = nil do
hitBox = {
Active = false,
OnHit = function(callback)
newSwordInstance:OnCastPointHit(function(...)
if not hitBox.Active then
return;
end;
callback(...);
end);
end,
};
end;
newSwordInstance.hitBox = hitBox;
newSwordInstance.hitBox.OnHit(function(hit, humanoid)
local character = humanoid.Parent;
for _, ignoreAncestor in pairs(newSwordInstance.ignoreList) do
if not ignoreAncestor then
continue;
end;
if hit:IsDescendantOf(ignoreAncestor) then
return;
end;
end;
if not newSwordInstance.killPlayers then
local isPlayer = Players:GetPlayerFromCharacter(character);
if isPlayer then
return;
end;
end;
humanoid:TakeDamage(newSwordInstance.currentDamage);
newSwordInstance:__Tag(character);
if humanoid.Health <= 0 and newSwordInstance.killsStat then
if humanoid:FindFirstChild("Dead") then
return;
else
local marker = Instance.new("BoolValue", humanoid);
marker.Name = "Dead";
marker.Value = true;
end;
local killSettings = newSwordInstance.killsStat;
local _, player = newSwordInstance:__GetLastTag(character);
if not player then
return;
end;
if killSettings.isDataStore2 then
local kills = Dependencies.DataStore2(killSettings.key, player);
kills:Increment(1, 0);
else
player.leaderstats[killSettings.StatName].Value += 1;
end;
if player == newSwordInstance:GetOwner() then
newSwordInstance:__FireEvent("TargetKilled", character);
end;
else
if newSwordInstance.knockBackEnabled then
local strength = newSwordInstance.knockBackStrength;
if Settings.DebugMode then
character.HumanoidRootPart.Anchored = false;
end;
local UnitDirection = (character.HumanoidRootPart.Position
- self:GetOwner().Character.HumanoidRootPart.Position).Unit;
local BP = Instance.new("BodyPosition");
BP.Parent = character.HumanoidRootPart;
BP.Position = character.HumanoidRootPart.Position + UnitDirection * (strength/10);
BP.D = 500;
BP.MaxForce = Vector3.new(math.huge, math.huge, math.huge);
Debris:AddItem(BP, 0.5);
end;
end;
newSwordInstance:__FireEvent("HumanoidHit", humanoid);
end);
newSwordInstance.handle.Touched:Connect(function(touch)
newSwordInstance:__FireEvent("ObjectHit", touch);
end);
tool.Equipped:Connect(function()
newSwordInstance.equipped = true;
newSwordInstance:__FireEvent("Equipped");
local humanoid = self:GetOwner().Character:WaitForChild("Humanoid");
for _, id in pairs(newSwordInstance.holdAnims or {}) do
local anim = Instance.new("Animation");
anim.AnimationId = baseURL:format(id);
local track = humanoid:LoadAnimation(anim);
track:Play();
newSwordInstance.__hold = track
end;
end);
tool.Unequipped:Connect(function()
newSwordInstance.equipped = false;
newSwordInstance:__FireEvent("Unequipped");
if newSwordInstance.__hold then
newSwordInstance.__hold:Stop()
newSwordInstance.__hold = nil;
end;
end);
local event = ReplicatedStorage:FindFirstChild(Settings.ActionEvent);
if not event then
event = Instance.new("RemoteEvent")
event.Name = Settings.ActionEvent;
event.Parent = ReplicatedStorage;
end;
event.OnServerEvent:Connect(function(player, key)
pcall(function() --Didn't wanna waste time checking if stuff existed or not
local _tool = player.Character:FindFirstChildWhichIsA("Tool");
local type = sword[_tool].type;
if type ~= sword[tool].type then
return;
end;
for _, bind in pairs(newSwordInstance.keyBinds) do
if bind.Key ~= key then continue end;
if (os.time() - bind.Debounce[1]) <= bind.Debounce[2] then continue end;
bind.Callback(player);
bind.Debounce[1] = os.time();
break;
end;
end);
end);
return newSwordInstance;
end;
function sword:__FireEvent(event, ...)
local event = "__on" .. event .. "Callbacks";
for _, callback in pairs(self[event]) do
pcall(callback, ...);
end;
end;
function sword:__PlaySwingAnimations(humanoid, onEndCallback)
local anims = self.swingAnims do
local backUp = anims;
if typeof(anims) ~= "table" then
if typeof(anims) == "number" then
anims = {anims};
elseif typeof(anims) == "string" then
anims = {tonumber(anims)}; if not anims then
anims = {tonumber(backUp:match("%d+"))}
end;
end;
if (typeof(anims) == "table" and #anims < 1) or not anims then
delay(2, onEndCallback)
return;
end;
end;
end;
local rigTypeStr = humanoid.RigType.Name;
local animId;
if anims[rigTypeStr] then
anims = anims[rigTypeStr];
end;
animId = anims[math.random(1, #anims)]
if animId == self.lastAnim and #anims > 1 then
repeat
animId = tonumber(anims[math.random(1, # anims)]);
RunService.Heartbeat:Wait();
until animId ~= self.lastAnim;
end;
self.lastAnim = animId;
local anim = Instance.new("Animation");
anim.AnimationId = baseURL:format(animId);
local track = humanoid:LoadAnimation(anim);
track:Play();
track.Stopped:Connect(onEndCallback);
end;
function sword:__GeneratePoints()
local handle = self.handle;
do
local parts = {};
for _, object in pairs(handle.Parent:GetChildren()) do
if object:IsA("BasePart") then table.insert(parts, object) end;
end;
if #parts > #({ handle }) then
local fakeHandle = Instance.new("Part");
fakeHandle.Name = "FakeHandle";
fakeHandle.Transparency = 1;
fakeHandle.CanCollide = false;
fakeHandle.Material = Enum.Material.Neon;
local isolatedModel = Instance.new("Model")
for _, part in pairs(parts) do
part:Clone().Parent = isolatedModel;
end;
local size = isolatedModel:GetExtentsSize() do
isolatedModel:Destroy();
end;
fakeHandle.CFrame = handle.CFrame;
fakeHandle.Size = size;
local weld = Instance.new("WeldConstraint");
weld.Part0 = fakeHandle;
weld.Part1 = handle;
weld.Parent = fakeHandle;
fakeHandle.Parent = handle.Parent;
self.handle = fakeHandle;
handle = self.handle;
end;
end;
local resolution = math.ceil((handle.Size.Y + self.hitBoxOffset) / 2);
local increment = 1 / (resolution - 1);
local topSurface = CFrame.new(0, -math.huge, 0);
local bottomSurface = CFrame.new(0, math.huge, 0);
local function getSurface(handle, normId)
local surfaces = {
[Enum.NormalId.Top] = handle.CFrame
* CFrame.new(0, handle.Size.Y / 2, 0) * CFrame.Angles(math.pi / 2, 0, 0),
[Enum.NormalId.Bottom] = handle.CFrame
* CFrame.new(0, handle.Size.Y / -2, 0) * CFrame.Angles(math.pi / -2, 0, 0),
[Enum.NormalId.Front] = handle.CFrame
* CFrame.new(0, 0, handle.Size.Z / -2),
[Enum.NormalId.Back] = handle.CFrame
* CFrame.new(0, 0, handle.Size.Z / 2) * CFrame.Angles(0, math.pi ,0),
[Enum.NormalId.Left] = handle.CFrame
* CFrame.new(handle.Size.X / -2, 0, 0) * CFrame.Angles(0, math.pi / 2, 0),
[Enum.NormalId.Right] = handle.CFrame
* CFrame.new(handle.Size.X / 2, 0, 0) * CFrame.Angles(0, math.pi / -2, 0),
};
return surfaces[normId];
end;
local callbacks = {
function()
local surfaces = {} do
local ids = {Enum.NormalId.Front, Enum.NormalId.Back, Enum.NormalId.Top, Enum.NormalId.Bottom, Enum.NormalId.Right, Enum.NormalId.Left};
for index, id in pairs(ids) do
surfaces[index] = getSurface(handle, id);
end;
end;
for _, cframe in pairs(surfaces) do
local y = cframe.Y;
if y < bottomSurface.Y then
bottomSurface = cframe;
elseif y > topSurface.Y then
topSurface = cframe;
end;
end;
for i = 0, 1, increment do
local Attachment = Instance.new("Attachment", handle);
Attachment.Name = "CastPoint";
Attachment.CFrame = handle.CFrame:ToObjectSpace(bottomSurface:Lerp(topSurface, i));
Attachment.Visible = Settings.DebugMode;
end;
if self.hasTrail then
local attachments = {
['highest'] = {-math.huge, nil},
['lowest'] = {math.huge, nil},
};
for _, attachment in pairs(handle:GetChildren()) do
local isAttachment = attachment:IsA("Attachment") and attachment.Name == "CastPoint";
if not isAttachment then
continue;
end;
local y = attachment.CFrame.Y;
if y < attachments.lowest[1] then
attachments.lowest[1] = y;
attachments.lowest[2] = attachment;
elseif y > attachments.highest[1] then
attachments.highest[1] = y;
attachments.highest[2] = attachment;
end;
end;
assert(attachments.lowest[2] ~= nil and attachments.highest[2] ~= nil,
"Error while loading trail.");
local Trail = Instance.new("Trail", handle);
Trail.Name = "SwordTrail";
Trail.Enabled = false;
Trail.MaxLength = .2;
Trail.Attachment0 = attachments.lowest[2];
Trail.Attachment1 = attachments.highest[2];
self.trail = Trail;
end;
end,
function()
if self.dualWield then
local dual = handle:Clone();
local hand do
local _player = self:GetOwner();
local character = _player.Character or _player.CharacterAdded:Wait();
local humanoid = character:WaitForChild("Humanoid");
if humanoid.RigType == Enum.HumanoidRigType.R6 then
hand = humanoid.Parent["Left Arm"];
else
hand = humanoid.Parent.LeftHand;
end;
end;
dual.CFrame = getSurface(handle, Enum.NormalId.Bottom);
dual.CanCollide = false;
dual.Transparency = self.equipped and 0 or 1;
dual.Parent = self:GetOwner().Character;
local motor6d = Instance.new("Motor6D");
motor6d.C1 = self.C1;
motor6d.Part0 = dual;
motor6d.Part1 = hand;
motor6d.Parent = dual;
local function transparency()
dual.Transparency = self.equipped and 0 or 1;
local effects = {"Fire", "Sparkles", "Smoke", "PointLight", "ParticleEmitter"}
local function isBlackListed(object)
for _, effect in pairs(effects) do
if object:IsA(effect) then return true; end;
end;
end;
for _, object in pairs(dual:GetDescendants()) do
if isBlackListed(object) then object.Enabled = self.equipped; end;
end;
end;
do
self:OnEquipped(transparency);
self:OnUnequipped(transparency);
end;
transparency();
self.dual = dual;
end;
end,
};
for _, callback in pairs(callbacks) do
coroutine.wrap(callback)();
end;
end;
function sword:__GetLastTag(character)
local greatest = -math.huge;
local player = nil;
for _, obj in pairs(character:GetChildren()) do
local n = tonumber(obj.Name);
if n and obj:IsA("ObjectValue") and obj.Value ~= nil then
if n > greatest then
greatest = n;
player = obj.Value;
end;
end;
end;
return If(greatest < 0, 0, greatest), player;
end;
function sword:__Tag(character)
local player = self:GetOwner();
if not player or character.Humanoid.Health <= 0 then
return;
end;
local last = self:__GetLastTag(character) + 1;
local newTag = Instance.new("ObjectValue", character);
newTag.Name = tostring(last);
newTag.Value = player;
end
function sword:SetTrail(property, value)
if self.trail then
self.trail[property] = value;
end;
end;
function sword:GetOwner()
local tool = self.handle.Parent;
local classes = {
["Model"] = function(parent)
if parent:FindFirstChild("Humanoid") then
local player = Players:GetPlayerFromCharacter(parent)
if player then
return player;
else
local fakePlayer = {
Name = parent.Name,
Character = parent,
CharacterAdded = {
Wait = function()
--When an npc dies it doesn't actually respawn.. so...
end,
},
};
return fakePlayer;
end;
end;
end,
["Backpack"] = function(parent)
if parent.Parent:IsA("Player") then
return parent.Parent;
end;
end,
}
local parent = tool.Parent;
for class, check in pairs(classes) do
if parent:IsA(class) then
local owner = check(parent);
if owner then
return owner;
end;
end;
end;
end;
function sword:BindAction(key, debounceTime, callback)
local key = typeof(key) == "string" and Enum.KeyCode[key] or key;
local data = {
Key = key,
Debounce = {-os.time(), debounceTime or 1},
Callback = callback,
};
table.insert(self.keyBinds, data);
end;
function sword:Swing(dmg, ignoreList)
if not self.equipped or self.swinging then
return;
end;
local character = self:GetOwner().Character;
if not character then
return;
end;
local ignoreList = ignoreList or {character};
local dmg = dmg or 20;
self:SetTrail("Enabled", true);
self.ignoreList = ignoreList;
self.currentDamage = dmg;
self.swinging = true;
self.hitBox.Active = true;
self:__FireEvent("Activated");
local function detect(handle)
local function draw(origin, dir)
if not Settings.DebugMode then
return;
end;
local rayFolder = workspace:FindFirstChild("RayFolder");
if rayFolder then
rayFolder = Instance.new("Folder");
rayFolder.Name = "RayFolder";
rayFolder.Parent = workspace;
end;
local midpoint = origin + dir / 2;
local radius = 0.1;
local part = Instance.new("Part");
part.Name = "Ray";
part.CanCollide = false;
part.Anchored = true;
part.Material = Enum.Material.Neon;
part.BrickColor = BrickColor.Red();
part.CFrame = CFrame.new(midpoint, origin);
part.Size = Vector3.new(radius, radius, dir.Magnitude);
end;
local blacklist = {character};
local params = RaycastParams.new();
params.FilterType = Enum.RaycastFilterType.Blacklist;
if workspace:FindFirstChild("RayFolder") then
table.insert(blacklist, workspace.RayFolder);
end;
params.FilterDescendantsInstances = blacklist;
while self.swinging do
local hitDetected = false;
local hit, humanoid = nil, nil;
for _, obj in pairs(handle:GetChildren()) do
if not obj:IsA("Attachment") and obj.Name ~= "CastPoint" then
continue;
end;
local origin = obj.WorldPosition;
local dir = obj.CFrame.lookVector.Unit*2;
coroutine.wrap(draw)(origin, dir);
local result = workspace:Raycast(origin, dir, params);
if result then
local _hit = result.Instance;
local _humanoid = _hit.Parent:FindFirstChildWhichIsA("Humanoid");
if _humanoid then
hitDetected = true;
hit, humanoid = _hit, _humanoid;
break;
end;
end;
end;
if hitDetected then
self:__FireEvent("CastPointHit", hit, humanoid, hit.Position);
break;
end;
RunService.Heartbeat:Wait();
end;
end;
coroutine.wrap(detect)(self.handle);
if self.dualWield then
coroutine.wrap(detect)(self.dual);
end;
local humanoid = character.Humanoid;
self:__PlaySwingAnimations(humanoid, function()
self.swinging = false;
self.hitBox.Active = false;
self:SetTrail("Enabled", false);
self:__FireEvent("SwingEnded");
end);
end;
return sword;
Where to get it
Click this link and get it, it’s free!
Video Tutorial
Changle Log
- Dual Wield…6/14/2021
- Holding/Unsheathing Animations…6/14/21
- Special Attacks…6/15/2021
- Knockback…6/21/2021
- Swing Animation Bug Fix…6/21/2021
- Dual Wielding Bug Fix…6/21/2021
- Auto-Migration…6/22/2021
- NPC Support…6/22/2021
- Fixing Messy/Inefficient Code…6/22/2021
- More Efficient Initialization…6/23/2021
- Better Hitboxes…6/23/2021
- Hold Animation Bug Fix…6/23/2021
- Critical Hits…6/24/2021
- Critical Hits Event…2/24/2021
Future Updates
- ProfileService compatibility
- Blocking
- Viewport Models
- Bug Fixing
This took a long time to make, so feel free to heart, ask questions, or give feedback.