Jump to content

How do I add a stategraph post init ?


Recommended Posts

Hello forum,

 

So I've been working on adding a new item for my character. It's a kind of bell, just like the Old Bell, so I'm trying to use the same animation as the Old Bell does.

 

So I did it, and it works only in RoG, obviously. Because the Old Bell only exists in RoG, and therefore so does the character bell ringing animation.

 

Then I need to bring that anim state to the base game when my character mod is activated. I looked around the files and I found this in DLC001/stategraphs/SGwilson :

 

actionhandlers = {    -- ....    ActionHandler(ACTIONS.PLAY, function(inst, action)        if action.invobject then            if action.invobject:HasTag("flute") then                return "play_flute"            elseif action.invobject:HasTag("horn") then                return "play_horn"            elseif action.invobject:HasTag("bell") then                return "play_bell"            end        end    end),    -- ....}

and also this:

states = {    -- ....    State{        name = "play_bell",        tags = {"doing", "playing"},                onenter = function(inst)            inst.components.locomotor:Stop()            inst.AnimState:PlayAnimation("bell")            inst.AnimState:OverrideSymbol("bell01", "bell", "bell01")            --inst.AnimState:Hide("ARM_carry")             inst.AnimState:Show("ARM_normal")            if inst.components.inventory.activeitem and inst.components.inventory.activeitem.components.instrument then                inst.components.inventory:ReturnActiveItem()            end        end,                onexit = function(inst)            if inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS) then                inst.AnimState:Show("ARM_carry")                 inst.AnimState:Hide("ARM_normal")            end        end,                timeline=        {            TimeEvent(15*FRAMES, function(inst)                inst.SoundEmitter:PlaySound("dontstarve_DLC001/common/glommer_bell")            end),            TimeEvent(60*FRAMES, function(inst)                inst:PerformBufferedAction()            end),        },                events=        {            EventHandler("animover", function(inst)                inst.sg:GoToState("idle")            end),        },    },    -- ....}

 

Now I assume these are the stategraphs for the character's bell ringing animation. They're not included in the base game.

 

I want to add them to the normal character stategraph, preferably using functions like AddStateGraphPostInit, that I found thanks to Corrosive's topic here, but I don't know how to use them exactly. For now I tried a few things like adding the code you see up here in my modmain.lua and then...?

if not IsDLCEnabled(REIGN_OF_GIANTS) then    AddStategraphActionHandler("SGwilson", function(inst) --[[ actionhandlers ??? ]] end)    AddStategraphState("SGwilson", function(inst) --[[ states ??? ]] end)end

I know I could make an entire SGwilburn.lua just for my character (his name is Wilburn) but that seems a bit too much and I want to make clean code if possible.

 

Thanks a lot for helping.

 

 

By the way if you want to see the current version of the character you can go here. It is kind of complete, I'm just trying to add some content.

Link to comment
Share on other sites

@Sketchiverse Check out the stategraph portions of the API examples mod.

 

Copied here for convenience.

------------------------------------- Actions and Handlers----  AddAction(action)--		Puts a new action into the global actions table and sets up the basic--		string for it.----	AddStategraphActionHandler("stategraphname", newActionHandler)--		Appends a handler for your action (or an existing action!) to an--		existing stategraph.-------------------------------------local Action = GLOBAL.Actionlocal ActionHandler = GLOBAL.ActionHandlerlocal MYACT = Action(3)MYACT.str = "Do My Action"MYACT.id = "MYACT"MYACT.fn = function(act)	print(act.doer.. " is doing my action!")endAddAction(MYACT)AddStategraphActionHandler("wilson", ActionHandler(MYACT, "doshortaction"))------------------------------------- Stategraph mod example----	AddStategraphState("stategraphname", newState)--	AddStategraphEvent("stategraphname", newEvent)--	AddStategraphActionHandler("stategraphname", newActionHandler)--		Use these functions to append new states, events, and actions handlers--		to existing states.----	AddStategraphPostInit("stategraphname", initfn)--		Use this to modify and mangle existing stategraphs, such as digging--		into existing states or appending new functionality to existing--		handlers.-- -----------------------------------local State = GLOBAL.Statelocal TimeEvent = GLOBAL.TimeEventlocal EventHandler = GLOBAL.EventHandlerlocal FRAMES = GLOBAL.FRAMESlocal spinstate = State({	name = "spin_around",	tags = {"idle", "canrotate"},	onenter = function(inst)		inst.Transform:SetRotation(0)		inst.AnimState:PushAnimation("idle_loop", true)	end,	timeline = {		TimeEvent(20*FRAMES, function(inst) inst.Transform:SetRotation(90) end),		TimeEvent(40*FRAMES, function(inst) inst.Transform:SetRotation(180) end),		TimeEvent(60*FRAMES, function(inst) inst.Transform:SetRotation(270) end),		TimeEvent(80*FRAMES, function(inst) inst.Transform:SetRotation(0) end),		TimeEvent(100*FRAMES, function(inst) inst.sg:GoToState("idle") end),	},	events = {	},})local newIdleTimeout = function(inst)	if math.random() < 0.5 then		inst.sg:GoToState("funnyidle")	else		inst.sg:GoToState("spin_around")	endendlocal function SGWilsonPostInit(sg)	-- note! This overwrites the old timeout behavior! If possible you should	-- always try appending your behaviour instead.	sg.states["idle"].ontimeout = newIdleTimeoutendAddStategraphState("wilson", spinstate)AddStategraphPostInit("wilson", SGWilsonPostInit) 

Link to comment
Share on other sites

@Blueberrys It worked !! Thank you Blueberrys ! :)

I'll rely on these API examples more often from now on.

Have a good day !

 

 

For those who want to see how it looks in my case :

In modmain.lua:

local State = GLOBAL.Statelocal Action = GLOBAL.Actionlocal ActionHandler = GLOBAL.ActionHandlerlocal TimeEvent = GLOBAL.TimeEventlocal EventHandler = GLOBAL.EventHandlerlocal ACTIONS = GLOBAL.ACTIONSlocal FRAMES = GLOBAL.FRAMES-- Adding the bell ringing animation from Reign of Giants.local bell_handler = ActionHandler(ACTIONS.PLAY, function(inst, action)        if action.invobject then            if action.invobject:HasTag("flute") then                return "play_flute"            elseif action.invobject:HasTag("horn") then                return "play_horn"            elseif action.invobject:HasTag("bell") then                return "play_bell"            end        end    end)local bell_state = State{        name = "play_bell",        tags = {"doing", "playing"},                onenter = function(inst)            inst.components.locomotor:Stop()            inst.AnimState:PlayAnimation("bell")            inst.AnimState:OverrideSymbol("bell01", "bell", "bell01")            --inst.AnimState:Hide("ARM_carry")             inst.AnimState:Show("ARM_normal")            if inst.components.inventory.activeitem and inst.components.inventory.activeitem.components.instrument then                inst.components.inventory:ReturnActiveItem()            end        end,                onexit = function(inst)            if inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS) then                inst.AnimState:Show("ARM_carry")                 inst.AnimState:Hide("ARM_normal")            end        end,                timeline=        {            TimeEvent(15*FRAMES, function(inst)                inst.SoundEmitter:PlaySound("dontstarve_DLC001/common/glommer_bell")            end),            TimeEvent(60*FRAMES, function(inst)                inst:PerformBufferedAction()            end),        },                events=        {            EventHandler("animover", function(inst)                inst.sg:GoToState("idle")            end),        },    }if not IsDLCEnabled(REIGN_OF_GIANTS) then    AddStategraphActionHandler("wilson", bell_handler)    AddStategraphState("wilson", bell_state)end 

Link to comment
Share on other sites

@Blueberrys Okay, so I went a bit further, added the actual code for the item's effect in-game, but something is really odd : it works only in RoG...! ... again, I know, but the problem is different.

I'm stuck again and I can't find a way, I've been trying for half a day already :(

 

 

Looking back up in my code, I wrote this (modmain.lua) : 

local bell_state = State{--....   timeline = {      TimeEvent(15*FRAMES, function(inst)         inst.SoundEmitter:PlaySound("dontstarve/<mycustomsound>")      end),      TimeEvent(30*FRAMES, function(inst)         inst:PerformBufferedAction()      end),   }--....}

 

and that inst:PerformBufferedAction() is supposed to... call the OnPlayed function described in my prefab's file (or something like that)

 

Well, this works perfectly in RoG, but not in the base game, and I'd rather have a fully compatible mod. I've searched for issues involving this function on the forums but it didn't help. Anyone has an idea...?

 

Sorry again, I'm really doing my best here...

 

 

To be precise, in non-RoG, upon using the bell item, it successfully plays the bell animation and the <mycustomsound> I specified in the code up here... but the effect in my bell's OnPlayed function doesn't happen. I tested it with a print("something") at the start of OnPlayed and it doesn't show up. The item consumption still occurs though, so it must be going to the prefab's file at some point...

 

Also just for clarifying, I removed the IsDLCEnabled condition at the end. It's useless and I wouldn't be able to correctly change my custom bell's sound if I left it.

Link to comment
Share on other sites

@Blueberrys I didn't make a new action, I'm using ACTIONS.PLAY and I just changed wilson's stategraph's action handler and state. Do I need to edit ACTIONS.PLAY.fn ?

 

The api example isn't very clear to me... It says that you can make a new action and edit its action.fn, and it puts a print("doing action") or something in it, but what if I didn't create an action ?

 

In actions.lua for the base game and for RoG, the function is :

ACTIONS.PLAY.fn = function(act)    if act.invobject and act.invobject.components.instrument then        return act.invobject.components.instrument:Play(act.doer)    endend
Link to comment
Share on other sites

@Sketchiverse No, you don't need to edit it. I just needed to know what the action's fn is doing.

 

The instrument component in base DS doesn't call any onplayed function.

(instrument.lua) copied for reference

function Instrument:Play(musician)    local pos = Vector3(musician.Transform:GetWorldPosition())    local ents = TheSim:FindEntities(pos.x,pos.y,pos.z, self.range)    for k,v in pairs(ents) do		if v ~= self.inst and self.onheard then		    self.onheard(v, musician, self.inst)		end    end    return true    end

 

You will need to either add that in yourself, or find another work around.

Use this to add it.

AddComponentPostInit("instrument", function(inst)    local oldPlay = inst.Play    function inst:Play(musician)        if self.onplayed then            self.onplayed(self.inst, musician)        end        return oldPlay(self, musician)    endend)

Edit: Also, use [ member='Blueberrys' ] to give me a notification (without the spaces).

 

Edit 2: Fixed error mentioned below (oldPlay invalid and pass self to oldPlay)

Link to comment
Share on other sites

@Blueberrys

 

Ohh I see now ! I didn't think about it, I didn't really understand how it all worked, the whole thing with actions and components etc. ^^' I guess that's how you learn. Thanks a lot for your help again !

 

I didn't change the instrument component with AddComponentPostInit as you told me, though. That crashed because musician was nil.

 

I did this, just for the records :

local Vector3 = GLOBAL.Vector3local Instrument = require "components/instrument"local Base_Instrument_Play = Instrument.Play    function Instrument:Play(musician)        --Instruction that was only in RoG        if self.onplayed then            self.onplayed(self.inst, musician)        end        ----------------------------------        local pos = Vector3(musician.Transform:GetWorldPosition())        local ents = TheSim:FindEntities(pos.x,pos.y,pos.z, self.range)        for k,v in pairs(ents) do            if v ~= self.inst and self.onheard then                self.onheard(v, musician, self.inst)            end        end        return true    end

It works perfectly now (if I've made enough testing) I can work on the rest of the update :grin:

Bye bye, thanks again !

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

Please be aware that the content of this thread may be outdated and no longer applicable.

×
  • Create New...