Jump to content

[SOLVED] How to Call Function When Chopping/Mining/Digging/Hammering/Tilling?


Recommended Posts

Hello,

I would like to know how to call a function after a specific tool action. For example, I might call function 1 when the prefab is used to chop a tree, call function 2 when mining, function 3 when digging, etc. By the way, this is for a prefab that can chop, mine, dig, hammer, and till.
I know there is already a "ListenForEvent" thing but I can't seem to find a list of the events it can hear. So, where might I find this list? (if it exists and contains what I need)

Any help would be appreciated!

Edited by BombardmentPigs
Link to comment
Share on other sites

7 minutes ago, LeafyFly said:
-- scripts/components/workable.lua
worker:PushEvent("working", { target = self.inst })

This doesn't really help. It doesn't tell you what action did the work and also it wouldn't run on Till, since it's not a work action.

2 hours ago, BombardmentPigs said:

I know there is already a "ListenForEvent" thing but I can't seem to find a list of the events it can hear. So, where might I find this list? (if it exists and contains what I need)

I don't think there's a convenient list of events, you sort just look for an event being pushed in the relevant components or other places. Unfortunately, I can't quite find an event that also sends the action inside the data. But I have an other idea!

If you're willing to make a component (it's very easy, I'll guide you through it if needed), just have a function in this component called OnUsedAsItem(action, doer, target). The game runs this function on every component of an inventory item that is performing an action, as long as the component has this function, obviously. Right now it's only used by finiteuses to consume durability, but nothing stops you from using it yourself.

Link to comment
Share on other sites

6 minutes ago, ClumsyPenny said:

This doesn't really help. It doesn't tell you what action did the work and also it wouldn't run on Till, since it's not a work action.

local on_working = function(worker, data)
  local tool = worker and worker.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS)
  local target = data and data.target
  local workable = target and target.components.workable
  if tool and tool.prefab == "your_tools_prefab_name" and workable then
    if workable.action == ACTIONS.CHOP then
      function1()
    elseif workable.action == ACTIONS.MINE then
      function2()
    elseif workable.action == ACTIONS.HAMMER then
      function3()
    end
  end
end

local onequip = function(inst, owner)
	--......
	if owner:HasTag("player") then
		inst:ListenForEvent("working", on_working, owner)
	end
end

local onunequip = function(inst, owner)
  --......
  if owner:HasTag("player") then
    inst:RemoveEventCallback("working", on_working, owner)
  end
end

 

-- scripts/components/farmtiller.lua
function FarmTiller:Till(pt, doer)
    if TheWorld.Map:CanTillSoilAtPoint(pt.x, 0, pt.z, false) then
		TheWorld.Map:CollapseSoilAtPoint(pt.x, 0, pt.z)
        SpawnPrefab("farm_soil").Transform:SetPosition(pt:Get())

		if doer ~= nil then
			doer:PushEvent("tilling")
		end
        return true
    end
    return false
end

 

Link to comment
Share on other sites

Alright, I tried to make a component with the OnUsedAsItem function, and added the custom component into my prefab, but the server crashed upon equipping the item.
The code for the component is this: (Component is called multitoolsorter by the way)

local MultiToolSorter = Class(function(self, inst)
    self.inst = inst
end,
nil,
{
--empty
})

function ChopDeduct(self)
	self.inst.components.fueled:DoDelta(-120)
end

function HammerDeduct(self)
	self.inst.components.fueled:DoDelta(-120)
end

function DigDeduct(self)
	self.inst.components.fueled:DoDelta(-120)
end

function MineDeduct(self)
	self.inst.components.fueled:DoDelta(-120)
end

function MultiToolSorter:OnUsedAsItem(action, doer, target)
	if target.components.workable.action == ACTIONS.CHOP then
		ChopDeduct()
	elseif target.components.workable.action == ACTIONS.HAMMER then
		HammerDeduct()
	elseif target.components.workable.action == ACTIONS.DIG then
		DigDeduct()
	elseif target.components.workable.action == ACTIONS.MINE then
		MineDeduct()
	end
end

return MultiToolSorter

The error in the server log is this:

Quote

[00:01:46]: [string "../mods/Mymod/scripts/component..."]:27: attempt to index local 'target' (a nil value)

I looked into the finiteuses component but I couldn't see anything defining 'target', nor was there anything doing that inside of an item that has the finiteuses component, like the spear. This confused me as I now don't know how to define target to fix this.

Assistance would be appreciated!

Link to comment
Share on other sites

1 hour ago, BombardmentPigs said:

I looked into the finiteuses component but I couldn't see anything defining 'target', nor was there anything doing that inside of an item that has the finiteuses component, like the spear. This confused me as I now don't know how to define target to fix this.

OnUsedAsItem is run in entityscript.lua with all the relevant parameters, that's where the "target" parameter comes from. The reason it's nil is likely because some actions don't necessarily have a target, you have to keep in mind this is being run for every action possible you perform with that item, even something as simple as "Examine". All you have to do is simply add a check for target not being nil. Having checks like that is generally good practice anyway!

Edited by ClumsyPenny
Link to comment
Share on other sites

11 hours ago, ClumsyPenny said:

All you have to do is simply add a check for target not being nil. Having checks like that is generally good practice anyway!

Got it, thanks.
I added the check and it no longer crashes on equipping, but it crashes whenever it tries to chop, hammer, etc.
The error is this:

Quote

[00:02:22]: [string "../mods/Mymod/scripts/component..."]:6: attempt to index local 'self' (a nil value)

The code in the component now is this:

local MultiToolSorter = Class(function(self, inst)
    self.inst = inst
end)

function ChopDeduct(self, inst)
	self.inst.components.fueled:DoDelta(-120)
end

function HammerDeduct(self, inst)
	self.inst.components.fueled:DoDelta(-120)
end

function DigDeduct(self, inst)
	self.inst.components.fueled:DoDelta(-120)
end

function MineDeduct(self, inst)
	self.inst.components.fueled:DoDelta(-120)
end

function MultiToolSorter:OnUsedAsItem(action, doer, target)
	if target ~= nil then
		if target.components.workable.action == ACTIONS.CHOP then
			ChopDeduct()
		elseif target.components.workable.action == ACTIONS.HAMMER then
			HammerDeduct()
		elseif target.components.workable.action == ACTIONS.DIG then
			DigDeduct()
		elseif target.components.workable.action == ACTIONS.MINE then
			MineDeduct()
		end
	end
end

return MultiToolSorter

A similar error happens if I remove the 'self' part and it instead says that 'inst' is nil.
This happens on all 4 functions (ChopDeduct, HammerDeduct, etc.) which are triggered when I chop, hammer, etc.

Link to comment
Share on other sites

2 minutes ago, BombardmentPigs said:

A similar error happens if I remove the 'self' part and it instead says that 'inst' is nil.

Well, you're not providing any arguments when calling the functions. Why are you functions global and not part of the component, anyway?

Without changing anything about your code, fixing would be a simple as changing this in OnUsedAsItem (do the same for the other 3, obviously):

ChopDeduct(self, self.inst)

 

But I'd recommend making the functions part of the component:

function MultiToolSorter:ChopDeduct()
	self.inst.components.fueled:DoDelta(-120)
end

And the other one:

self:ChopDeduct()

The reason you don't need any arguments here is because self is already being given as an argument by virtue of using the colon, and inst is referenced inside self as self.inst.

  • Thanks 1
Link to comment
Share on other sites

1 hour ago, ClumsyPenny said:

Well, you're not providing any arguments when calling the functions. Why are you functions global and not part of the component, anyway?

Without changing anything about your code, fixing would be a simple as changing this in OnUsedAsItem (do the same for the other 3, obviously):

ChopDeduct(self, self.inst)

 

But I'd recommend making the functions part of the component:

function MultiToolSorter:ChopDeduct()
	self.inst.components.fueled:DoDelta(-120)
end

And the other one:

self:ChopDeduct()

The reason you don't need any arguments here is because self is already being given as an argument by virtue of using the colon, and inst is referenced inside self as self.inst.

It worked perfectly for all the actions except tilling!
Thanks!

The problem now is how do I get the tilling to work?
I have this in my prefab but my fuel doesn't go down when I till.

local function OnTill(inst, data)
	inst.components.fueled:DoDelta(-120/35)
end

--In MainFunction:
inst:ListenForEvent("tilling", OnTill)
Link to comment
Share on other sites

3 hours ago, BombardmentPigs said:

It worked perfectly for all the actions except tilling!
Thanks!

The problem now is how do I get the tilling to work?
I have this in my prefab but my fuel doesn't go down when I till.

local function OnTill(inst, data)
	inst.components.fueled:DoDelta(-120/35)
end

--In MainFunction:
inst:ListenForEvent("tilling", OnTill)

The `tilling` event is pushed to the player using the till tool, rather than the tool itself, which is why this does not work properly.

You should be able to have your implementation performed in OnUsedAsItem, same with the rest of the actions, though there is no target entity in the action of tilling of course. But perform a conditional for whether the action is the till action and deduct fuel afterwards.

Thinking on it, it'd likely be a small (but good) optimization to cut out the middleman checks for target and indexes into the workable component and simply use the `action` parameter provided. But if your current implementation is what makes sense to you, then that is perfectly fine!

  • Thanks 2
Link to comment
Share on other sites

8 minutes ago, Hornete said:

The `tilling` event is pushed to the player using the till tool, rather than the tool itself, which is why this does not work properly.

You should be able to have your implementation performed in OnUsedAsItem, same with the rest of the actions, though there is no target entity in the action of tilling of course. But perform a conditional for whether the action is the till action and deduct fuel afterwards.

Thinking on it, it'd likely be a small (but good) optimization to cut out the middleman checks for target and indexes into the workable component and simply use the `action` parameter provided. But if your current implementation is what makes sense to you, then that is perfectly fine!

Thanks very much, now everything works perfectly!
Thanks to everyone who helped me.
Here's the component code in case anyone needs it:

local MultiToolSorter = Class(function(self, inst)
    self.inst = inst
end)

function MultiToolSorter:ChopDeduct()
	self.inst.components.fueled:DoDelta(-120/130)
end

function MultiToolSorter:HammerDeduct()
	self.inst.components.fueled:DoDelta(-120/95)
end

function MultiToolSorter:DigTillDeduct() --btw I deduct the same amount of fuel for both dig and till so they all call the same function
	self.inst.components.fueled:DoDelta(-120/35)
end

function MultiToolSorter:MineDeduct()
	self.inst.components.fueled:DoDelta(-120/50)
end

function MultiToolSorter:OnUsedAsItem(action, doer, target)
	if action == ACTIONS.CHOP then
		self:ChopDeduct()
	elseif action == ACTIONS.HAMMER then
		self:HammerDeduct()
	elseif action == ACTIONS.DIG then
		self:DigTillDeduct()
	elseif action == ACTIONS.MINE then
		self:MineDeduct()
	elseif action == ACTIONS.TILL then
		self:DigTillDeduct()
	end
end

return MultiToolSorter
  • GL Happy 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
  • Create New...