I’m basically trying to bring my work a step further, and revamp my previous combat because I didn’t like the feeling of it. I know that’s more up to the vfx part of the game, but I also the back end part of the work could also need a revamp as I go along with that VFX path of the game
local DamageInterpretation = {}
DamageInterpretation.__index = DamageInterpretation
function DamageInterpretation:New(Damage: number, Knockback: boolean, Blockable: boolean, AttackType: string, QiType: string, BlockBreak: boolean, KBTime: number)
local DMGInter = setmetatable({}, self)
DMGInter.__index = DMGInter
DMGInter.Damage = Damage
DMGInter.KnockBack = Knockback
DMGInter.Blockable = Blockable
DMGInter.AttackType = AttackType
DMGInter.QiType = QiType
DMGInter.BlockBreak = BlockBreak
DMGInter.KBTime = KBTime
return DMGInter
end
function DamageInterpretation:Damage(Character, HitHum, HitChar)
local DMG: number = self.Damage
local OGDMG = DMG
if self.KnockBack == true then
local BV = Instance.new("BodyVelocity")
BV.Parent = HitChar:FindFirstChild("HumanoidRootPart")
BV.MaxForce = Vector3.new(100000, 22500, 100000)
BV.P = 33300
BV.Velocity = Character.HumanoidRootPart.CFrame.LookVector * 40
Debris:AddItem(BV, self.KBTime)
end
local Player = Players:GetPlayerFromCharacter(Character)
local ePlayer = Players:GetPlayerFromCharacter(HitChar)
if self.Blockable == true and self.AttackType == "Blunt" then
DMG = DMG/2
end
damageHum(HitHum, DMG)
end
return DamageInterpretation
The New method instead of having multiple parameters, why not have a single parameter which is a table. Also it’s not necessary to use “:” on New instead use “.”.
local AvailableProperties = {
BlockBreak = "boolean",
KnockBack = "boolean",
Blockable = "boolean",
AttackType = "string",
Damage = "number",
KBTime = "number",
QiType = "string",
}
function DamageInterpretation.new(properties: {})
return setmetatable({}, DamageInterpretation):_Initialize(properties)
end
function DamageInterpretation:_Initialize(properties: {})
for key, value in pairs(properties) do
local dataType = AvailableProperties[key]
--[[
This step is unnecessary all you need is `self[key] = value`,
this just checks if the type of data is correct/the same and
the key should be added.
]]
if dataType then
if typeof(value) == dataType then
self[key] = value
else
error(("%s must be a type of %s"):format(key, dataType))
end
else
warn("%s can't be added")
end
end
return self
end
Is this similar to an assert what you are doing in initialize so the scripter could know if the wrong value was input?
Otherwise, this is an interesting way to do it, glad I learned it before I got deep into OOP. Thank you.
Read through it and I think I understand it better. When you input the values it’s just checking if they are all equal to those specific values in the avaliable properties, and if so, then procceed to set the self/avaliable property values to that, or it’ll warn/error that the value isn’t equal to the avaliable properties.
(I just do this to understand better, like of active recalling for me.)
Yes, and not many people would recommend using assert, especially on big projects where a lot of errors might occur and cause some performance issues.
I tend to make my own function which is Assert with the capital A.
local function Assert(value, errorMessage: string?)
if not value then
error(errorMessage)
end
end
You can even make this as a global variable simply removing the local though this would only work with frameworks where, initializing the important parts get loaded first.
I think why assert causes performance issues on big projects is probably because it formats/concatenates the string with the method traceback on the Debug library.
If you aren’t going to directly mutate self in the constructor, then you can just directly use the properties table for it.
--!strict
local Damager = {}
Damager.__index = Damager
type Properties = {
Damage: number,
KBTime: number,
UseKnockBack: boolean,
IsBlockable: boolean,
OnBlockBreak: boolean,
AttackType: "Blunt" | "etc.", -- We can use literals,
QIType: "Ditto"
}
-- Hackish way of getting proper Type-Checking
export type Damager = typeof(setmetatable({} :: Properties, Damager))
function Damager.new(Properties: Properties): Damager
return setmetatable(Properties, Damager)
end
function Damager:Damage(Character: Model): ()
local self: Damager = self -- Allows Type-Checker to know what 'self' is
end
return Damager
One issue with using a table for the constructor is that you don’t get a very good Auto-Complete recommendation when using it, but it’s much cleaner than listing out all the arguments.
I see, so that’s what the export is used for. I never really tried to mess with this because I thought this only works with plugins. I always have wanted to have an auto correct when doing something like this.
Is this using Inhertiance to make a new class? I’m a little confused on a few of these functions such as type, export. I just skimmed over it somewhat of course so let me read through it more.
It’s my rendition of this idiom that allows proper type-checking inside the constructor and method scopes + outside logic but most of it is syntax sugar. It doesn’t support inheritance but for standalone classes like this it’s more preferable.
so export type automatically makes the certain properties an automatically type check what it was : to, or if not what does type do and what does export do?
Ah, so it just makes that specific variable get type-checked. Now, with damager new, would I make a different oop part of damageinterpretation and input that table into the values?
Took me some time to figure out but it’d probably just make a seperate damage function that takes those values and does what damage does. All in all, these responses were pretty helpful.
I happen to have a question about the implemenation of the actual table within the damage script after using _Initialize, because for some reason self is not acknowledged and all the values are considered nil. Do I need to turn _Initialize into a variable?
So if you have called the method correctly self becomes the first parameter of the function. If you called the funciton like this module._Initialize() without passing in the object then self becomes null.