Jump to content

[TUTORIAL] How to Act with Actions


Lucemodok
 Share

Recommended Posts

Hey, I'm Lucemodok (or Luce), and I'm here to teach you about making actions. (Hope this works, since I'm not sure I explained it well.)

First, we'll need to well... create the action. (Remember to replace everything in square brackets (Including the square barckets) and keep the capitalization.)

Spoiler

-- First create the action and give it a name.
local [ACTION NAME HERE] = Action({priority = [number], mount_enabled = [true or false], distance = [number], [etc.]}) -- Check actions.lua for the full list of parameters.
-- Next add the text that will be displayed.
[ACTION NAME HERE].str = "[Action String Here]"
-- Next create the ID
[ACTION NAME HERE].id = "[ACTION ID HERE]"
-- FInally the function that runs the action.
[ACTION NAME HERE].fn = function(act)
  -- Come back to this later when you have made your components.
  local targ = act.invobject or act.target
    if targ.components.[other component here] then
        return act.doer.components.[component name here]:[Component Function Here](targ)
    end
end

 

 

Then we'll need to create components (Or use existing ones) to make our action work.

Spoiler

-- This makes the component and adds our component tag to the owner of the component.
local [Component Name Here] = Class(function(self, inst)
    self.inst = inst
    inst:AddTag("[component tag here]")
end)

-- When the component is removed, we remove the component tag from our prefab.
function [Component Name Here]:OnRemoveFromEntity()
    self.inst:RemoveTag("[component tag here]")
end

-- This is the function that will let us use both components for our action.
function [Component Name Here]:[Component Function Here]([Other Component Name])
    if [Other Component Name].components.[Other Component Name] then
        if [Other Component Name].components.[Other Component Name]:[Other Component Function](self.inst) then
            [[
          Here you can add something that will happen when your action is triggered, it could be something along the lines of:
          if [Other Component Name].components.finiteuses then
                [Other Component Name].components.finiteuses:Use(1) (This will consume 1 use of the item)
            end
          OR
          [Other Component Name]:Remove() (This will consume the entire item)
          ]]
            return true
        end
    end
end

    -- Completes our component.
return [Component Name Here]

-- Adds our component and defines self.[onsomething] which we will use in our action.
local [Other Component Name] = Class(function(self, inst)
    self.inst = inst
    self.[onsomething] = nil
end)

-- Here we assign an effect to what runs after we trigger our function.
function [Other Component Name]:[SetOnSomethingFn](fn)
    self.[onsomething] = fn
end

-- This is the function our first component checks for and the one that runs the effect we assign to [SetOnSomethingFn].
function [Other Component Name]:[Other Component Function]([Component Name Here])
    if self.[onsomething] ~= nil then
        self.[onsomething](self.inst, [Component Name Here])
    end
    return true
end

  -- Completes our component.
return [Other Component Name]

 

 

After that we need to finish our action.

Spoiler

-- Adds our action to the action list.
AddAction([ACTION NAME HERE])

AddStategraphActionHandler("wilson", ActionHandler(ACTIONS.[ACTION NAME HERE], "[animation here]")) -- In [animation here] we define which animation will be used for our action. Recommended: dolongaction or doshortaction.

AddStategraphActionHandler("wilson_client", ActionHandler(ACTIONS.[ACTION NAME HERE], "[animation here]")) -- Not sure what this does, but it might be for caves so be sure to add it.

-- Allows us to use our action, if the user doesn not meet this requirements, they will not be able to use the action.
local function activate[action name here](inst, doer, actions, right)
    if doer:HasTag("[component tag here]") then
        table.insert(actions, ACTIONS.[ACTION NAME HERE])
    end
end

AddComponentAction("[LOCATION HERE]", "[other component name]", activate[action name here]) -- in [LOCATION HERE] we define where our action will take place, for example INVENTORY is for actions that activate from inventory items, SCENE is for the world and POINT is a rmb action (Like Wortox's Soul Hop)

 

 

Finally, we add our component to the prefab we want using an AddPrefabPostInit.

Spoiler

AddPrefabPostInit("[prefab name here]", function(inst)
    inst:AddComponent("[component name here]")
end)

local function OnSomething(inst, [component name here])
  -- The effect of what happens after your action here.
end

AddPrefabPostInit("[other prefab name]", function(inst)
    inst:AddComponent("[other component name]")
    inst.components.[other component name]:[SetOnSomethingFn](OnSomething)
end)

 

 

Oh yeah, I forgot to say, you'll also need to define Action, ActionHandler and ACTIONS

local Action = GLOBAL.Action
local ActionHandler = GLOBAL.ActionHandler
local ACTIONS = GLOBAL.ACTIONS

 

Edited by Lucemodok
  • Like 4
Link to comment
Share on other sites


I think there are multiple correct ways, thank you for the tutorial.
For an example how it also works, see here:
https://forums.kleientertainment.com/forums/topic/121951-modifying-an-action-to-allow-for-picking-up-rabbits/?do=findComment&comment=1374729
there you see for example. that you can directly give the str and fn to the AddAction command, if you want, so there is no need to use GLOBAL.Action(3) from your first code part.
AddAction returns the action, so you also can do
local [ACTION NAME HERE] = AddAction([ACTION NAME HERE])
[ACTION NAME HERE].str = "[Action String Here]"
-- ...

I think to not use "Action(3) is better, since Action is no modutil, unlike AddAction. But not sure if there may be any real disadvantages when using Action().


In addition there are more things you can customize about your action, you will find it within actions.lua from the game, currently it looks like this:

    self.priority = data.priority or 0 -- if multiple actions would be chosen
    self.fn = function() return false end  -- the fn is executed when executing the action
    self.strfn = nil -- a function for a variable string
    self.instant = data.instant or false
    self.rmb = data.rmb or nil -- note! This actually only does something for tools, everything tests 'right' in componentactions
    self.distance = data.distance or nil -- you can even do the action while being away from the target this distance.
    self.mindistance = data.mindistance or nil
    self.ghost_exclusive = data.ghost_exclusive or false
    self.ghost_valid = self.ghost_exclusive or data.ghost_valid or false -- If it's ghost-exclusive, then it must be ghost-valid
    self.mount_valid = data.mount_valid or false
    self.encumbered_valid = data.encumbered_valid or false
    self.canforce = data.canforce or nil
    self.rangecheckfn = self.canforce ~= nil and data.rangecheckfn or nil
    self.mod_name = nil
    self.silent_fail = data.silent_fail or nil

    --new params, only supported by passing via data field
    self.actionmeter = data.actionmeter or nil
    self.customarrivecheck = data.customarrivecheck
    self.is_relative_to_platform = data.is_relative_to_platform
    self.disable_platform_hopping = data.disable_platform_hopping
    self.skip_locomotor_facing = data.skip_locomotor_facing
    self.do_not_locomote = data.do_not_locomote
    self.extra_arrive_dist = data.extra_arrive_dist

@Doxarez do you know if one really ALWAYS need a component to add an action to an entity? See my link above, there I only added a component to be able to do the action, while the component is basically empty and quite useless.

edit:
I see you forgot "AddStategraphActionHandler("wilson_client", ...)" or is this not always needed?
edit2:
Looking at actions.lua, doing Action(3) like you did is setting the priority to 3. Why are you choosing 3 as priority, why not leaving it empty?
edit3:
Also the comments within modutil and within actions.lua suggest not to use Action() directly and also to not use Acition(number) , since it is derpecated.So better only use AddAction()

Edited by Serpens
Link to comment
Share on other sites

4 minutes ago, Doxarez said:

As far as I know,


AddStategraphActionHandler("wilson_client", ...)

is not needed, I have been able to execute myt actions without it, also, thanks for the customize options, I had completely forgotten about them.

according to the name "wilson_client" I would guess it is needed for clients to execute the action, or in a world with caves enabled, did you test it?
(added an edit2/edit3 above :D)

Edited by Serpens
Link to comment
Share on other sites

I made a prefab with a custom action, along with the action file and component file, and even though nothing's crashing, I can't get the function to work.

can you post an example of this code without the stuff in brackets so I can better understand how everything is put together?

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