Jump to content

[CODING] Help with custom tool


Recommended Posts

Hey, everyone!
I am creating a mod with electrical tools, including chainsaw and mining drill. So far, I started to program only the chainsaw.
I began creating a component called "electicalTool" inspired in the "tool" component. I had an idea of doing the tools to work and deal damage over time, instead of doing multiple successive actions like the axe and pickaxe. I don't know if I will keep it this way, but this is the way it works now (I need to include a range check, I know it):

local ACTION_DELAY = 0.3

local ElectricalTool = Class(function(self, inst)
    self.inst = inst
	self.target = nil
	
	self.onWork = nil
	self.onFinishedWork = nil
end)

--------------------------------------------------------------------------

function ElectricalTool:SetOnWorkFn(fn)
    self.onWork = fn
end

function ElectricalTool:SetOnFinishedWorkFn(fn)
    self.onFinishedWork = fn
end

--------------------------------------------------------------------------

local periodicTask = nil

function WorkTask (self, doer, invobject, target)
	if target.components.workable ~= nil and target.components.workable:CanBeWorked() and ((target.components.workable.action == ACTIONS.CHOP and invobject.components.tool:CanDoAction (ACTIONS.CUTDOWN))) then 
																							--or (target.components.workable.action == ACTIONS.MINE and invobject.components.tool:CanDoAction (ACTIONS.DRILL))) then
		target.components.workable:WorkedBy(
			doer,
        	--10
    		(   invobject ~= nil and
	            invobject.components.tool ~= nil and
        		invobject.components.tool:GetEffectiveness(ACTIONS.CUTDOWN)
    		) or
    		(   doer ~= nil and
	            doer.components.worker ~= nil and
        		doer.components.worker:GetEffectiveness(ACTIONS.CUTDOWN)
    		) or
    		1
		)
		if self.onWork ~= nil then
        	self.onWork(self.inst)
    	end
	else
		self.FinishedWork (self.inst)
		if self.onFinishedWork ~= nil then
        	self.onFinishedWork(self.inst)
    	end
    end
end

function ElectricalTool:FinishedWork (inst)
	periodicTask:Cancel()
	periodicTask = nil
end

function ElectricalTool:Work(doer, invobject, target)
	if (periodicTask == nil) then
		periodicTask = self.inst:DoPeriodicTask (ACTION_DELAY, function () WorkTask (self, doer, invobject, target) end)
		WorkTask (self, doer, invobject, target)
	end
end

return ElectricalTool

This is the chainsaw prefab code:

local CHAINSAW_EFFECTIVENESS = 1

local assets = 
{
	Asset ("ANIM", "anim/chainsaw.zip"),
	Asset ("ANIM", "anim/swap_chainsaw.zip"),
	
	Asset ("ATLAS", "images/inventory_images/chainsaw.xml"),
	Asset ("IMAGE", "images/inventory_images/chainsaw.tex"),

	Asset("SOUNDPACKAGE", "sound/chainsaw.fev"),
    Asset("SOUND", "sound/chainsaw.fsb"),
}

local prefabs = 
{
}

local equipped = false
local startedRevSound = false

local function OnWork (inst)
	print ("Working")
	inst.SoundEmitter:KillAllSounds()
	inst.SoundEmitter:PlaySound ("chainsaw/chainsaw/rev", "rev")
end

local function OnFinishedWork (inst)
	print ("Finished Work")
	--inst.SoundEmitter:KillAllSounds()
	inst.SoundEmitter:KillSound ("rev")
	inst.SoundEmitter:PlaySound ("chainsaw/chainsaw/idle", "idle")
end

local function ChainsawFunction (colour)

	local function OnEquip (inst, owner)
		owner.AnimState:OverrideSymbol ("swap_object", "swap_chainsaw", "swap_chainsaw")
		owner.AnimState:Show ("ARM_carry")
		owner.AnimState:Hide ("ARM_normal")
		inst.SoundEmitter:KillAllSounds()
		inst.SoundEmitter:PlaySound ("chainsaw/chainsaw/equip", "equip")
		owner:AddTag ("playerghost")
		equipped = true
	end
	
	local function OnUnequip (inst, owner)
		owner.AnimState:Hide ("ARM_carry")
		owner.AnimState:Show ("ARM_normal")
		inst.SoundEmitter:KillSound ("rev")
		inst.SoundEmitter:KillSound ("idle")
		inst.SoundEmitter:KillSound ("equip")
		inst.SoundEmitter:PlaySound ("chainsaw/chainsaw/unequip")
		owner:RemoveTag ("playerghost")
		equipped = false
	end
	
	local inst = CreateEntity()
	inst.entity:AddTransform()
	inst.entity:AddAnimState()
	inst.entity:AddSoundEmitter()
	inst.entity:AddNetwork()

	MakeInventoryPhysics (inst)

	inst.AnimState:SetBank ("chainsaw")
	inst.AnimState:SetBuild ("chainsaw")
	inst.AnimState:PlayAnimation ("idle")
	
	inst:AddTag("sharp")

	inst.entity:SetPristine()

	if not TheWorld.ismastersim then   
      return inst  
    end
    
	inst:AddComponent("inspectable")

	inst:AddComponent ("inventoryitem")
	inst.components.inventoryitem.imagename = "chainsaw"
	inst.components.inventoryitem.atlasname = "images/inventory_images/chainsaw.xml"

	inst:AddComponent("tool")
    inst.components.tool:SetAction(ACTIONS.CUTDOWN, CHAINSAW_EFFECTIVENESS)

    inst:AddComponent("electricalTool")
    inst.components.electricalTool:SetOnWorkFn (OnWork)
    inst.components.electricalTool:SetOnFinishedWorkFn (OnFinishedWork)
	
	inst:AddComponent ("equippable")
	inst.components.equippable:SetOnEquip (OnEquip)
	inst.components.equippable:SetOnUnequip (OnUnequip)

	return inst
end

return Prefab ("common/inventory/chainsaw", ChainsawFunction, assets, prefabs)

And finally this is the modmain.lua where I create the custom action CUTDOWN:

GLOBAL.STRINGS.NAMES.CHAINSAW = "Chainsaw"
GLOBAL.STRINGS.CHARACTERS.GENERIC.DESCRIBE.CHAINSAW = "Tree-eater machine"

PrefabFiles = {
    "chainsaw",
}

-- Creating new state to Cut Down using Chainsaw
-- It is a placeholder because I cannot create animations
local State = GLOBAL.State
local TimeEvent = GLOBAL.TimeEvent
local EventHandler = GLOBAL.EventHandler
local FRAMES = GLOBAL.FRAMES


local cutdownState = State({
	name = "cutdown",
	tags = {"cutting", "working"},
	onenter = function(inst)
        inst.AnimState:PlayAnimation("give")
	end,
	timeline = {
		TimeEvent(1 * FRAMES, function(inst)
        	inst:PerformBufferedAction()
	    end),
    },

	events =
        {
            EventHandler("unequip", function(inst) inst.sg:GoToState("idle") end),
            EventHandler("finishedwork", function(inst)
                if inst.AnimState:AnimDone() then
                    inst.sg:GoToState("idle")
                end
            end),
        },
})
        
-- Add the new state to the Stategraph
AddStategraphState("wilson", cutdownState)
AddStategraphState("wilson_client", cutdownState)

---------------------------------------------------------------------------------------------------------------------------

-- Create the Action to cutdown the trees
local CUTDOWN = AddAction("CUTDOWN", "Cut Down", function(act)

	if act.doer.components.inventory then	
		if act.target.components.burnable ~= nil then
			if act.target.components.burnable:IsBurning() or act.target.components.burnable:IsSmoldering() then
				return false
			end
		end
	
		if act.invobject.components.electricalTool == nil then
            return false
        end

        act.invobject.components.electricalTool:Work (act.doer, act.invobject, act.target)
    	return true
    end
end)
CUTDOWN.priority = 2

-- Make my custom action valid as a "tool" action
GLOBAL.TOOLACTIONS.CUTDOWN = true

-- Add my custom actions to the component itself
AddComponentAction("EQUIPPED", "tool", function(inst, doer, target, actions, right)
	if target:HasTag("tree") and inst:HasTag("CUTDOWN_tool") then
		table.insert(actions, GLOBAL.ACTIONS.CUTDOWN)
	end
end)


AddStategraphActionHandler("wilson", GLOBAL.ActionHandler(CUTDOWN, "cutdown"))
AddStategraphActionHandler("wilson_client", GLOBAL.ActionHandler(CUTDOWN, "cutdown"))

Regarding all these codes, I have two doubts:

  • How can I make my custom action doable by holding space bar like all the other actions?
  • How can I detect that the player moved after the action begins
  • What would be the best way to remove the "chop" sound when working a tree using a tool? It is player in the following code (evergreens.lua):
local function chop_tree(inst, chopper, chops)
    if not (chopper ~= nil and chopper:HasTag("playerghost")) then
        inst.SoundEmitter:PlaySound(
            chopper ~= nil and chopper:HasTag("beaver") and
            "dontstarve/characters/woodie/beaver_chop_tree" or
            "dontstarve/wilson/use_axe_tree"
        )
    end
  	
  ...

If you have any suggestions/feedback on my code, feel free to say it. It is the first mod that I am creating for DST.

Link to comment
Share on other sites

1) You need to override the function in playercontroller which select the action to perform when holding the spacebar (PlayerController:GetActionButton) to include your action in the list of possible actions. There was a thread last week about that very same question and I answered it (about a jumping action).

2) What do you want to do if the player moved?

3) Yes. You need to change the do a AddPrefabPostInit to the trees and apply a new function to Workable:SetOnWorkCallback.

 

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...