Attempt to index method with nil, despite the method existing?

Straight to the point, I wrote an OOP inventory system and made a class for it called Melee, The tools for the inventory system have a FastSignal event which fires whenever they get equipped and unequipped, But for some reason when I unequip it tells me that :StopCharge() method does not exists.

The functions are connected to the events on the class constructor function:

function Melee.new(Replicated, ServerID)
	local self = setmetatable(Melee.ParentClass.new(Replicated,ServerID),Melee)

	self.ClassName = Melee.ClassName
	-- Properties
	self.Icon = 9759885209
	self.ClassName = Melee.ClassName
	self.Name = Melee.ClassName
	-- Visuals
	self.WeaponModel = nil
	self.HeavyKillEffect = "Normal"
	self.LightKillEffect = "Normal"
	self.ClientTrail = nil
	-- Stats
	self.WoundType = "Swift"
	self.WoundPercentage = 0
	self.ChargeLength = 1.5
	self.LightRecoveryDuration = 0.30
	self.LightMissRecoveryDuration = 0.7
	self.HeavyRecoveryDuration = 0.30
	self.HeavyMissRecoveryDuration = 0.8
	self.MinDamage = 6
	self.MaxDamage = 15
	self.HitDelayDuration = 0.1
	self.Range = 4.5
	self.BlockBreaking = 0
	self.BlockStrength = 1
	-- GUI
	self.GUI = nil
	-- SFX
	self.SFX = {
		LightHit = Assets.SFX.Weapons.Fists.LightHitSFX,
		HeavyHit =Assets.SFX.Weapons.Fists.HeavyHitSFX,
		LightSwing = Assets.SFX.Weapons.Fists.LightSwingSFX,
		HeavySwing = Assets.SFX.Weapons.Fists.HeavySwingSFX,
		Equip = Assets.SFX.Weapons.Fists.EquipSFX
	}
	-- ViewModel Animations/Local
	self.ViewModelAnimations = {
		Equip = Assets.Animations.Fists.Equip,
		Idle = Assets.Animations.Fists.Idle,
		Blocking = Assets.Animations.Fists.Blocking,
		Charge = Assets.Animations.Fists.Charge,
		Charging = Assets.Animations.Fists.Charging,
		LightHit = Assets.Animations.Fists.LightHit,
		HeavyHit = Assets.Animations.Fists.HeavyHit,
		Shove = Assets.Animations.Fists.Shove,
		Inspect = Assets.Animations.Fists.Inspect,
	}

	self.Equipped:Connect(function(State)
		if State == true then
			
			local playerRecord = ClientPlayerRecord:GetPlayerRecord(self.Owner)
			local PlayerViewmodel = playerRecord.Assets.Player.Viewmodel
			local CombatStates = playerRecord.States.Combat
			-- Load all assets
			PlayerViewmodel:Attach()
			PlayerViewmodel:Show()
			-- Loads Animations into the viewmodel
			for AnimationName,Animation in pairs(self.ViewModelAnimations) do
				local ClonedAnimation = Animation:Clone()
				ClonedAnimation.Parent = PlayerViewmodel.Animator
				PlayerViewmodel.AnimationTracks[ClonedAnimation.Name] = PlayerViewmodel.Animator:LoadAnimation(ClonedAnimation)
			end

			-- Reset Chargebar size
			playerRecord.Assets.Player.GUI.HUD.ChargeBarOutline.Charge.Size = UDim2.fromScale(0,1)
			-- Set priorities
			PlayerViewmodel.AnimationTracks.Equip.Priority = Enum.AnimationPriority.Action2
			PlayerViewmodel.AnimationTracks.Idle.Priority = Enum.AnimationPriority.Action
			-- Transition handled by anim priorities
			PlayerViewmodel.AnimationTracks.Equip:Play(0)
			PlayerViewmodel.AnimationTracks.Idle:Play(0)

			local function ActionHandler(Action, InputState)
				local Actions = {
					MouseButton1 = function()
						if InputState == Enum.UserInputState.Begin then
							ClientCommands:SetCommand("MAI",Enums.MeleeData.Charge)
						else
							ClientCommands:SetCommand("MAI",Enums.MeleeData.Hit)
						end
					end,
					MouseButton2 = function()
						if InputState == Enum.UserInputState.Begin then
							ClientCommands:SetCommand("MAI",Enums.MeleeData.Block)
						else
							ClientCommands:SetCommand("MAI",Enums.MeleeData.Unblock)
						end
					end,
					F = function()
						if InputState == Enum.UserInputState.Begin then
							ClientCommands:SetCommand("MAI",Enums.MeleeData.Inspect)
						end
					end,
					Q = function()
						if InputState == Enum.UserInputState.Begin then
							ClientCommands:SetCommand("MAI",Enums.MeleeData.Shove)
						end
					end,
				}
				Actions[Action]()
			end

			-- Bind all actions
			ContextActionService:BindAction("MouseButton1",ActionHandler, false, Enum.UserInputType.MouseButton1)
			ContextActionService:BindAction("MouseButton2",ActionHandler, false, Enum.UserInputType.MouseButton2)
			ContextActionService:BindAction("F",ActionHandler, false, Enum.KeyCode.F)
			ContextActionService:BindAction("Q",ActionHandler, false, Enum.KeyCode.Q)

			return
		else
			local playerRecord = ClientPlayerRecord:GetPlayerRecord(self.Owner)
			local PlayerViewmodel = playerRecord.Assets.Player.Viewmodel
			local CombatStates = playerRecord.States.Combat
			-- Unbind all actions
			ContextActionService:UnbindAction("MouseButton1")
			ContextActionService:UnbindAction("MouseButton2")
			ContextActionService:UnbindAction("F")
			ContextActionService:UnbindAction("Q")

			PlayerViewmodel:Hide()
			-- Stop charge
			self:StopCharge()
			-- Set states
			CombatStates.IsChargeWaiting = false
			self:Unblock()
			-- Disconnect this event since we are no longer waiting for charge
			playerRecord.FastSignals.AbleToCharge:DisconnectAll()
			playerRecord.FastSignals.AbleToBlock:DisconnectAll()  
			if playerRecord.Connections.Rbxl.HideTrailOnHitAnimationStopped then
				playerRecord.Connections.Rbxl.HideTrailOnHitAnimationStopped:Disconnect()
			end
			-- Kill all transition coroutines

			-- Stop all anims
			for i,Animation in pairs(PlayerViewmodel.Animator:GetChildren()) do
				Animation:Destroy()
			end

			for i,AnimationTrack in pairs(PlayerViewmodel.AnimationTracks) do
				AnimationTrack:Stop()
				PlayerViewmodel.AnimationTracks[i] = nil
			end
		end
	end)

	return self
end

Full script:

-- Services
local Players = game:GetService("Players")
local StarterGui = game:GetService("StarterGui")
local ReplicatedFirst = game:GetService("ReplicatedFirst")
local ContentProvider = game:GetService("ContentProvider")
local ContextActionService = game:GetService("ContextActionService")

local LocalPlayer = Players.LocalPlayer
local FastSignal = require(ReplicatedFirst.Packages.Chickynoid.Vendor.FastSignal)
local Enums = require(ReplicatedFirst.Packages.Chickynoid.Enums)
local BitBuffer = require(ReplicatedFirst.Packages.Chickynoid.Vendor.BitBuffer)
local Client = require(ReplicatedFirst.Packages.Chickynoid.Client)
local ClientMods = require(ReplicatedFirst.Packages.Chickynoid.Client.ClientMods)
local ClientCommands = ClientMods:GetMod("clientmods","ClientCommands")
local ClientPlayerRecord = nil

local Assets = script.Assets

local Melee = {}
Melee.ParentClass = require(script.Parent.Parent)
Melee.__index = Melee.ParentClass
Melee.ClassName = script.Name

function Melee:Charge()
	
end

function Melee:StopCharge()
	
end

function Melee:Block()
	
end

function Melee:Unblock()
	
end

function Melee:Hit()
	
end

function Melee.new(Replicated, ServerID)
	local self = setmetatable(Melee.ParentClass.new(Replicated,ServerID),Melee)

	self.ClassName = Melee.ClassName
	-- Properties
	self.Icon = 9759885209
	self.ClassName = Melee.ClassName
	self.Name = Melee.ClassName
	-- Visuals
	self.WeaponModel = nil
	self.HeavyKillEffect = "Normal"
	self.LightKillEffect = "Normal"
	self.ClientTrail = nil
	-- Stats
	self.WoundType = "Swift"
	self.WoundPercentage = 0
	self.ChargeLength = 1.5
	self.LightRecoveryDuration = 0.30
	self.LightMissRecoveryDuration = 0.7
	self.HeavyRecoveryDuration = 0.30
	self.HeavyMissRecoveryDuration = 0.8
	self.MinDamage = 6
	self.MaxDamage = 15
	self.HitDelayDuration = 0.1
	self.Range = 4.5
	self.BlockBreaking = 0
	self.BlockStrength = 1
	-- GUI
	self.GUI = nil
	-- SFX
	self.SFX = {
		LightHit = Assets.SFX.Weapons.Fists.LightHitSFX,
		HeavyHit =Assets.SFX.Weapons.Fists.HeavyHitSFX,
		LightSwing = Assets.SFX.Weapons.Fists.LightSwingSFX,
		HeavySwing = Assets.SFX.Weapons.Fists.HeavySwingSFX,
		Equip = Assets.SFX.Weapons.Fists.EquipSFX
	}
	-- ViewModel Animations/Local
	self.ViewModelAnimations = {
		Equip = Assets.Animations.Fists.Equip,
		Idle = Assets.Animations.Fists.Idle,
		Blocking = Assets.Animations.Fists.Blocking,
		Charge = Assets.Animations.Fists.Charge,
		Charging = Assets.Animations.Fists.Charging,
		LightHit = Assets.Animations.Fists.LightHit,
		HeavyHit = Assets.Animations.Fists.HeavyHit,
		Shove = Assets.Animations.Fists.Shove,
		Inspect = Assets.Animations.Fists.Inspect,
	}

	self.Equipped:Connect(function(State)
		if State == true then
			
			local playerRecord = ClientPlayerRecord:GetPlayerRecord(self.Owner)
			local PlayerViewmodel = playerRecord.Assets.Player.Viewmodel
			local CombatStates = playerRecord.States.Combat
			-- Load all assets
			PlayerViewmodel:Attach()
			PlayerViewmodel:Show()
			-- Loads Animations into the viewmodel
			for AnimationName,Animation in pairs(self.ViewModelAnimations) do
				local ClonedAnimation = Animation:Clone()
				ClonedAnimation.Parent = PlayerViewmodel.Animator
				PlayerViewmodel.AnimationTracks[ClonedAnimation.Name] = PlayerViewmodel.Animator:LoadAnimation(ClonedAnimation)
			end

			-- Reset Chargebar size
			playerRecord.Assets.Player.GUI.HUD.ChargeBarOutline.Charge.Size = UDim2.fromScale(0,1)
			-- Set priorities
			PlayerViewmodel.AnimationTracks.Equip.Priority = Enum.AnimationPriority.Action2
			PlayerViewmodel.AnimationTracks.Idle.Priority = Enum.AnimationPriority.Action
			-- Transition handled by anim priorities
			PlayerViewmodel.AnimationTracks.Equip:Play(0)
			PlayerViewmodel.AnimationTracks.Idle:Play(0)

			local function ActionHandler(Action, InputState)
				local Actions = {
					MouseButton1 = function()
						if InputState == Enum.UserInputState.Begin then
							ClientCommands:SetCommand("MAI",Enums.MeleeData.Charge)
						else
							ClientCommands:SetCommand("MAI",Enums.MeleeData.Hit)
						end
					end,
					MouseButton2 = function()
						if InputState == Enum.UserInputState.Begin then
							ClientCommands:SetCommand("MAI",Enums.MeleeData.Block)
						else
							ClientCommands:SetCommand("MAI",Enums.MeleeData.Unblock)
						end
					end,
					F = function()
						if InputState == Enum.UserInputState.Begin then
							ClientCommands:SetCommand("MAI",Enums.MeleeData.Inspect)
						end
					end,
					Q = function()
						if InputState == Enum.UserInputState.Begin then
							ClientCommands:SetCommand("MAI",Enums.MeleeData.Shove)
						end
					end,
				}
				Actions[Action]()
			end

			-- Bind all actions
			ContextActionService:BindAction("MouseButton1",ActionHandler, false, Enum.UserInputType.MouseButton1)
			ContextActionService:BindAction("MouseButton2",ActionHandler, false, Enum.UserInputType.MouseButton2)
			ContextActionService:BindAction("F",ActionHandler, false, Enum.KeyCode.F)
			ContextActionService:BindAction("Q",ActionHandler, false, Enum.KeyCode.Q)

			return
		else
			local playerRecord = ClientPlayerRecord:GetPlayerRecord(self.Owner)
			local PlayerViewmodel = playerRecord.Assets.Player.Viewmodel
			local CombatStates = playerRecord.States.Combat
			-- Unbind all actions
			ContextActionService:UnbindAction("MouseButton1")
			ContextActionService:UnbindAction("MouseButton2")
			ContextActionService:UnbindAction("F")
			ContextActionService:UnbindAction("Q")

			PlayerViewmodel:Hide()
			-- Stop charge
			self:StopCharge()
			-- Set states
			CombatStates.IsChargeWaiting = false
			self:Unblock()
			-- Disconnect this event since we are no longer waiting for charge
			playerRecord.FastSignals.AbleToCharge:DisconnectAll()
			playerRecord.FastSignals.AbleToBlock:DisconnectAll()  
			if playerRecord.Connections.Rbxl.HideTrailOnHitAnimationStopped then
				playerRecord.Connections.Rbxl.HideTrailOnHitAnimationStopped:Disconnect()
			end
			-- Kill all transition coroutines

			-- Stop all anims
			for i,Animation in pairs(PlayerViewmodel.Animator:GetChildren()) do
				Animation:Destroy()
			end

			for i,AnimationTrack in pairs(PlayerViewmodel.AnimationTracks) do
				AnimationTrack:Stop()
				PlayerViewmodel.AnimationTracks[i] = nil
			end
		end
	end)

	return self
end

function Melee:Setup()
	-- Load mods
	ClientPlayerRecord = ClientMods:GetMod("clientmods","ClientPlayerRecord")
	-- Load all of our networking Enums
	Enums.MeleeData = {
		Equip = 0,
		Idle = 1,
		Block = 2,
		Shove = 3,
		Charge = 4,
		Hit = 5,
		Inspect = 6
	}
	-- Load all the Enums for our animations!
	Enums.Anims.FistsIdle = 7
	Enums.Anims.FistsBlock = 8
	Enums.Anims.FistsInspect = 9
	Enums.Anims.FistsShove = 10
	Enums.Anims.FistsCharge = 11
	Enums.Anims.FistsLightHit = 12
	Enums.Anims.FistsHeavyHit = 13
	Enums.Anims.FistsEquip = 14
end

function Melee:HandleEvent()
	
end

function Melee:ProcessCommand()
	
end

return Melee

Please help you can see how the method :StocChargeExists()

1 Like

I may be wrong but this:

Melee.__index = Melee.ParentClass

causes the metatable to only look at the “ParentClass” and not ever look inside Melee itself.

An alternative is to use the following:

local Melee = setmetatable({}, ParentClass)
Melee.__index = Melee

The following is correct however, no change is needed:

local self = setmetatable(Melee.ParentClass.new(Replicated,ServerID),Melee)
3 Likes

I did this and man it works!, thank you so much!
image

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.