Jump to content

[SOLVED] How to make stategraphpostinit?


Recommended Posts

Hello, can someone tell me how to make stategraphpostinit because I heard it's not good to edit SGwilson for your character because it causes incompatibilities with other mods that do that..

So, for example can someone make this into statehraphpostinit then I can see & learn then I convert all my custom state to stategrapghpostinit? Thanks so much :D!!

State{
        name = "adam_whistle",
        tags = { "whistling", "pausepredict", "nomorph", "busy" },

        onenter = function(inst)
		      inst.Physics:Stop()
             inst.components.locomotor:Stop()
            inst.AnimState:Hide("ARM_carry") 
            inst.AnimState:Show("ARM_normal")
		   inst.AnimState:PlayAnimation("horn")
			 inst.AnimState:PushAnimation("horn")
            inst.components.inventory:ReturnActiveActionItem(inst.bufferedaction ~= nil and inst.bufferedaction.invobject or nil)
			
		if inst.components.playercontroller ~= nil then
        inst.components.playercontroller:RemotePausePrediction()
        end
		
        if inst.whistle_cooldown == nil then
		if TheWorld:HasTag("cave") then
		return
		end
        inst.whistle_cooldown = true
        local birdspawner = TheWorld.components.birdspawner
        birdspawner:SetMaxBirds(TUNING.BIRD_SPAWN_MAX_FEATHERHAT)
        birdspawner:SetSpawnTimes(TUNING.BIRD_SPAWN_DELAY_FEATHERHAT)
        inst:DoTaskInTime(90, function() inst.whistle_cooldown = nil end) -- Cooldown = 1.30.0.
        inst:DoTaskInTime(30, function(inst)
        local birdspawner = TheWorld.components.birdspawner
        birdspawner:SetSpawnTimes(TUNING.BIRD_SPAWN_DELAY)
        birdspawner:SetMaxBirds(TUNING.BIRD_SPAWN_MAX)
        end)
        end
        end,

        timeline =
        {
            TimeEvent(21*FRAMES, function(inst)
				inst.SoundEmitter:PlaySound("dontstarve/creatures/smallbird/chirp", "whistle")
                inst:PerformBufferedAction()
            end),
            TimeEvent(41*FRAMES, function(inst)
				inst.SoundEmitter:KillSound("whistle")
            end),
			TimeEvent(61*FRAMES, function(inst)
				inst.sg:GoToState("idle")
            end),
        },

        events =
        {
            EventHandler("animqueueover", function(inst)
                if inst.AnimState:AnimDone() then
                    inst.sg:GoToState("idle")
                end
            end),
        },

        onexit = function(inst)
			inst.SoundEmitter:KillSound("whistle")
            if inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS) then
                inst.AnimState:Show("ARM_carry") 
                inst.AnimState:Hide("ARM_normal")
            end
        end,
    },

 

Edited by SuperDavid
Link to comment
Share on other sites

Sorry but i'm gonna bump this again since I really would love to know how to make stategraphpostinit, because the way my mod is now it crashes with lots of other character mods because I use a edited SGwilson & that's a big problem :(.

I would appreciate any help so much :D! Have a lovely day/night :D!!!

Link to comment
Share on other sites

There are many functions.

AddStategraphActionHandler(name, actionhandler)

AddStategraphState(name, state)

AddStategraphEvent(name, eventhandler)

local postinit = function(stategraph)
end

AddStategraphPostInit(name, postinit)

You could say that the stategraph post init allows you to do what the others do.

 

For example:

local new_state = State {
	name = "hello",
	tags = {},
}

AddStategraphState(name, new_state)

is equivalent to

local new_state = State {
	name = "hello",
	tags = {},
}

local postinit = function(stategraph)
	stategraph.states["hello"] = new_state
end

AddStategraphPostInit(name, postinit)

 

You can see in stategraph.lua, in the class definition of StateGraph, that all these functions are pretty much called consecutively.

StateGraph = Class( function(self, name, states, events, defaultstate, actionhandlers)
    assert(name and type(name) == "string", "You must specify a name for this stategraph")
    local info = debug.getinfo(3, "Sl")
    self.defline = string.format("%s:%d", info.short_src, info.currentline)
    self.name = name
    self.defaultstate = defaultstate
    
    --reindex the tables
    self.actionhandlers = {}
    if actionhandlers then
        for k,v in pairs(actionhandlers) do
            assert( v:is_a(ActionHandler),"Non-action handler added in actionhandler table!")
            self.actionhandlers[v.action] = v
        end
    end
	for k,modhandlers in pairs(ModManager:GetPostInitData("StategraphActionHandler", self.name)) do
		for i,v in ipairs(modhandlers) do
			assert( v:is_a(ActionHandler),"Non-action handler added in mod actionhandler table!")
			self.actionhandlers[v.action] = v
		end
	end

    self.events = {}
    for k,v in pairs(events) do
        assert( v:is_a(EventHandler),"Non-event added in events table!")
        self.events[v.name] = v
    end
	for k,modhandlers in pairs(ModManager:GetPostInitData("StategraphEvent", self.name)) do
		for i,v in ipairs(modhandlers) do
			assert( v:is_a(EventHandler),"Non-event added in mod events table!")
			self.events[v.name] = v
		end
    end

    self.states = {}
    for k,v in pairs(states) do
        assert( v:is_a(State),"Non-state added in state table!")
        self.states[v.name] = v
    end
	for k,modhandlers in pairs(ModManager:GetPostInitData("StategraphState", self.name)) do
		for i,v in ipairs(modhandlers) do
			assert( v:is_a(State),"Non-state added in mod state table!")
			self.states[v.name] = v
		end
    end

	-- apply mods
	local modfns = ModManager:GetPostInitFns("StategraphPostInit", self.name)
	for i,modfn in ipairs(modfns) do
		modfn(self)
	end
end)

Another thing is that "name" corresponds to the stategraph name (which you can see at the end of the stategraph file), and NOT to the stategraph filename.

 

So, using the function that exists specially to add new states, it would look like this:

local State = GLOBAL.State
local EQUIPSLOTS = GLOBAL.EQUIPSLOTS
local EventHandler = GLOBAL.EventHandler
local TimeEvent = GLOBAL.TimeEvent
local FRAMES = GLOBAL.FRAMES
local TUNING = GLOBAL.TUNING

local adam_whistle_state = State {
	name = "adam_whistle",
	tags = { "whistling", "pausepredict", "nomorph", "busy" },

	onenter = function(inst)
		inst.Physics:Stop()
		inst.components.locomotor:Stop()
		inst.AnimState:Hide("ARM_carry") 
		inst.AnimState:Show("ARM_normal")
		inst.AnimState:PlayAnimation("horn")
		inst.AnimState:PushAnimation("horn")
		inst.components.inventory:ReturnActiveActionItem(inst.bufferedaction ~= nil and inst.bufferedaction.invobject or nil)

		if inst.components.playercontroller ~= nil then
			inst.components.playercontroller:RemotePausePrediction()
		end

		local world = GLOBAL.TheWorld

		if inst.whistle_cooldown == nil then
			if world:HasTag("cave") then
				return
			end
			inst.whistle_cooldown = true
			local birdspawner = world.components.birdspawner
			birdspawner:SetMaxBirds(TUNING.BIRD_SPAWN_MAX_FEATHERHAT)
			birdspawner:SetSpawnTimes(TUNING.BIRD_SPAWN_DELAY_FEATHERHAT)
			inst:DoTaskInTime(90, function() inst.whistle_cooldown = nil end) -- Cooldown = 1.30.0.
			inst:DoTaskInTime(30, function(inst)
				local birdspawner = world.components.birdspawner
				birdspawner:SetSpawnTimes(TUNING.BIRD_SPAWN_DELAY)
				birdspawner:SetMaxBirds(TUNING.BIRD_SPAWN_MAX)
			end)
		end
	end,

	timeline = {
		TimeEvent(21 * FRAMES, function(inst)
			inst.SoundEmitter:PlaySound("dontstarve/creatures/smallbird/chirp", "whistle")
			inst:PerformBufferedAction()
		end),
		TimeEvent(41 * FRAMES, function(inst)
			inst.SoundEmitter:KillSound("whistle")
		end),
		TimeEvent(61 * FRAMES, function(inst)
			inst.sg:GoToState("idle")
		end),
	},

	events = {
		EventHandler("animqueueover", function(inst)
			if inst.AnimState:AnimDone() then
				inst.sg:GoToState("idle")
			end
		end),
	},

	onexit = function(inst)
		inst.SoundEmitter:KillSound("whistle")
		if inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS) then
			inst.AnimState:Show("ARM_carry") 
			inst.AnimState:Hide("ARM_normal")
		end
	end,
}

AddStategraphState("wilson", adam_whistle_state)

 

Edited by DarkXero
Link to comment
Share on other sites

2 hours ago, DarkXero said:

local adam_whistle_state = State {

@DarkXero I'm sorry to bother but game crashes saying "attempt to index global "State" a nil value" at this line of code, I added a GLOBAL. & it works but would there be a way to do like "local State = ???" then I don't need to add GLOBAL.? (I just did "local State = GLOBAL.State" & that worked, haha)...

But I have a new problem @DarkXero if you can help me that would be great, so i'm trying to convert my custom states 1 by 1 & I tried to covert this state which works perfectly fine in SGwilson

	local adam_dodge_state = State{
        name = "dodge",
        tags = { "busy", "pausepredict", "nomorph", "dodging" },

        onenter = function(inst)
        inst.Physics:Stop()
        inst.AnimState:PlayAnimation("leap")

        if inst.components.playercontroller ~= nil then
        inst.components.playercontroller:RemotePausePrediction()
        end
        end,

        events =
        {
        EventHandler("animover", function(inst)
        if inst.AnimState:AnimDone() then
        inst.sg:GoToState("idle")
        end
        end),
        },
    },

AddStategraphState("wilson", adam_dodge_state)

but when I tried adding it into my modmain.lua the game crashes & says ":195: unexpected symbol near 'local'" line 195 being adam_dodge_state & that confuses me greatly because that didn't happen with the whistle state?!

Edited by SuperDavid
Link to comment
Share on other sites

36 minutes ago, SuperDavid said:

but when I tried adding it into my modmain.lua the game crashes & says ":195: unexpected symbol near 'local'" line 195 being adam_dodge_state & that confuses me greatly?!

Remove the comma at the end of -State { etc },-

Link to comment
Share on other sites

@DarkXero This's so embarrassing but can you please show me exactly what's wrong? I keep staring hard & don't see any comma anywhere weird... I put all the stuff I did but the code that causes the crash is all the way at the bottom "local adam_dodge_state = State {" and I don't see any comma or I must be blind :wilson_facepalm:... Thanks so much for helping me DarkXero :D!!!!

local EQUIPSLOTS = GLOBAL.EQUIPSLOTS
local EventHandler = GLOBAL.EventHandler
local TimeEvent = GLOBAL.TimeEvent
local FRAMES = GLOBAL.FRAMES
local TUNING = GLOBAL.TUNING
local State = GLOBAL.State
local ShakeAllCameras = GLOBAL.ShakeAllCameras
local CAMERASHAKE = GLOBAL.CAMERASHAKE

local adam_whistle_state = State {
	name = "adam_whistle",
	tags = { "whistling", "pausepredict", "nomorph", "busy" },

	onenter = function(inst)
		inst.Physics:Stop()
		inst.components.locomotor:Stop()
		inst.AnimState:Hide("ARM_carry") 
		inst.AnimState:Show("ARM_normal")
		inst.AnimState:PlayAnimation("horn")
		inst.AnimState:PushAnimation("horn")
		inst.components.inventory:ReturnActiveActionItem(inst.bufferedaction ~= nil and inst.bufferedaction.invobject or nil)

		if inst.components.playercontroller ~= nil then
			inst.components.playercontroller:RemotePausePrediction()
		end

		local world = GLOBAL.TheWorld

		if inst.whistle_cooldown == nil then
			if world:HasTag("cave") then
				return
			end
			inst.whistle_cooldown = true
			local birdspawner = world.components.birdspawner
			birdspawner:SetMaxBirds(TUNING.BIRD_SPAWN_MAX_FEATHERHAT)
			birdspawner:SetSpawnTimes(TUNING.BIRD_SPAWN_DELAY_FEATHERHAT)
			inst:DoTaskInTime(90, function() inst.whistle_cooldown = nil end) -- Cooldown = 1.30.0.
			inst:DoTaskInTime(30, function(inst)
				local birdspawner = world.components.birdspawner
				birdspawner:SetSpawnTimes(TUNING.BIRD_SPAWN_DELAY)
				birdspawner:SetMaxBirds(TUNING.BIRD_SPAWN_MAX)
			end)
		end
	end,

	timeline = {
		TimeEvent(21 * FRAMES, function(inst)
			inst.SoundEmitter:PlaySound("dontstarve/creatures/smallbird/chirp", "whistle")
			inst:PerformBufferedAction()
		end),
		TimeEvent(41 * FRAMES, function(inst)
			inst.SoundEmitter:KillSound("whistle")
		end),
		TimeEvent(61 * FRAMES, function(inst)
			inst.sg:GoToState("idle")
		end),
	},

	events = {
		EventHandler("animqueueover", function(inst)
			if inst.AnimState:AnimDone() then
				inst.sg:GoToState("idle")
			end
		end),
	},

	onexit = function(inst)
		inst.SoundEmitter:KillSound("whistle")
		if inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS) then
			inst.AnimState:Show("ARM_carry") 
			inst.AnimState:Hide("ARM_normal")
		end
	end,
}

local adam_roar_state = State {
	name = "adam_roar",
        tags = { "busy", "roaring", "pausepredict" },

        onenter = function(inst, data)
            inst:ClearBufferedAction()
			inst.components.locomotor:Stop()
			inst.AnimState:PlayAnimation("yawn")
			
            if inst.components.playercontroller ~= nil then
            inst.components.playercontroller:RemotePausePrediction()
            end

        end,

        timeline =
        {
		TimeEvent(18*FRAMES, function(inst)
		    ShakeAllCameras(CAMERASHAKE.FULL, 1.38, 0.02, 10, inst, 60)
		    inst.SoundEmitter:PlaySound("dontstarve/creatures/worm/death", "death")
            inst.SoundEmitter:PlaySound("dontstarve/creatures/werepig/howl", "howl")
            inst.SoundEmitter:PlaySound("dontstarve/creatures/werepig/grunt", "grunt")
			inst.SoundEmitter:PlaySound("dontstarve/creatures/worm/distant", "distant")
            inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/vargr/howl", "vargr")
            inst.SoundEmitter:PlaySound("dontstarve_DLC001/creatures/bearger/grrrr", "grrrr")
            end),
			TimeEvent(68*FRAMES, function(inst)
			inst.sg:RemoveStateTag("roaring")
			inst.SoundEmitter:KillSound("howl")
			inst.SoundEmitter:KillSound("death")
			inst.SoundEmitter:KillSound("grunt")
			inst.SoundEmitter:KillSound("vargr")
            end),
			TimeEvent(128*FRAMES, function(inst)
			inst.SoundEmitter:KillSound("grrrr")
			inst.SoundEmitter:KillSound("distant")
            end),
        },

        events =
        {
            EventHandler("animover", function(inst)
                if inst.AnimState:AnimDone() then
                    inst.sg:RemoveStateTag("roaring")
			        inst.SoundEmitter:KillSound("howl")
					inst.SoundEmitter:KillSound("death")
			        inst.SoundEmitter:KillSound("grunt")
			        inst.SoundEmitter:KillSound("vargr")
			        inst.SoundEmitter:KillSound("grrrr")
					inst.SoundEmitter:KillSound("distant")
                    inst.sg:GoToState("idle")
                end
            end),
        },

        onexit = function(inst)
		    inst.sg:RemoveStateTag("roaring")
            inst.SoundEmitter:KillSound("howl")
			inst.SoundEmitter:KillSound("death")
			inst.SoundEmitter:KillSound("grunt")
			inst.SoundEmitter:KillSound("vargr")
			inst.SoundEmitter:KillSound("grrrr")
			inst.SoundEmitter:KillSound("distant")
        end,
    },
	
	local adam_dodge_state = State {
        name = "dodge",
        tags = { "busy", "pausepredict", "nomorph", "dodging" },

        onenter = function(inst)
        inst.Physics:Stop()
        inst.AnimState:PlayAnimation("leap")

        if inst.components.playercontroller ~= nil then
        inst.components.playercontroller:RemotePausePrediction()
        end
        end,

        events =
        {
        EventHandler("animover", function(inst)
        if inst.AnimState:AnimDone() then
        inst.sg:GoToState("idle")
        end
        end),
        },
    },
AddStategraphState("wilson", adam_roar_state)
AddStategraphState("wilson", adam_dodge_state)
AddStategraphState("wilson", adam_whistle_state)

@DarkXero I have another stupid question, i'm sorry...

I noticed that my character has multiple for example "local State = GLOBAL.State"

since he has some actions that require that & I have multiple actions which do that but do those actions all need to have a "local ??? = GLOBAL.???" right next to them or can I just put all the locals

local require         = GLOBAL.require
local STRINGS         = GLOBAL.STRINGS
local Ingredient 	  = GLOBAL.Ingredient
local RECIPETABS 	  = GLOBAL.RECIPETABS
local TECH 			  = GLOBAL.TECH
local resolvefilepath = GLOBAL.resolvefilepath

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

local DEGREES = GLOBAL.DEGREES
local COLLISION = GLOBAL.COLLISION
local TheSim = GLOBAL.TheSim
local Vector3 = GLOBAL.Vector3
local PlayFootstep = GLOBAL.PlayFootstep
local SpawnPrefab = GLOBAL.SpawnPrefab

local EQUIPSLOTS = GLOBAL.EQUIPSLOTS
local EventHandler = GLOBAL.EventHandler
local TimeEvent = GLOBAL.TimeEvent
local FRAMES = GLOBAL.FRAMES
local TUNING = GLOBAL.TUNING
local State = GLOBAL.State
local ShakeAllCameras = GLOBAL.ShakeAllCameras
local CAMERASHAKE = GLOBAL.CAMERASHAKE

all the way at the top of my 2,000 line code modmain.lua & those actions would still work, right? The actions for say require "local TUNING = GLOBAL.TUNING" & "local TUNING = GLOBAL.TUNING" is all the way at the top of modmain.lua & the action is 2,000 lines of code down from the "local TUNING = GLOBAL.TUNING" would that make the action preform slower? Would it be better to have multiple "local TUNING = GLOBAL.TUNING" near everything that needs it or would it be fine to just put 1 all the way at the top? Sorry for this stupid question :?.

Edited by SuperDavid
Link to comment
Share on other sites

41 minutes ago, SuperDavid said:

and I don't see any comma or I must be blind

Roar state and dodge state have commas at the end of the -State { }-

42 minutes ago, SuperDavid said:

all the way at the top of my 2,000 line code modmain.lua & those actions would still work, right? The actions for say require "local TUNING = GLOBAL.TUNING" & "local TUNING = GLOBAL.TUNING" is all the way at the top of modmain.lua & the action is 2,000 lines of code down from the "local TUNING = GLOBAL.TUNING" would that make the action preform slower? Would it be better to have multiple "local TUNING = GLOBAL.TUNING" near everything that needs it or would it be fine to just put 1 all the way at the top?

Having one or multiple doesn't practically make a difference.

Just put the locals once at the beginning and then use them as you see fit.

The point of having them as locals is so you don't write GLOBAL everywhere.

Link to comment
Share on other sites

@DarkXero Thank you so, so, so much :D:D:D:D:D:D!!!!!!!!!!!!!

 

 

Sorry for bringing this old post up but I need to ask a question!

@DarkXero If I can ask you a question is that can stategraph post init be used to edit states in SGwilson that aren't custom?

Say for example I want to edit "run" state & make it that characters with "inst.stealth == true" don't play footstep sounds like this

would that be fine or would that be bad?

State{
        name = "run",
        tags = { "moving", "running", "canrotate", "autopredict" },

        onenter = function(inst) 
            inst.components.locomotor:RunForward()
            local anim = inst:HasTag("groggy") and "idle_walk" or "run_loop"
            if not inst.AnimState:IsCurrentAnimation(anim) then
                inst.AnimState:PlayAnimation(anim, true)
            end
            inst.sg:SetTimeout(inst.AnimState:GetCurrentAnimationLength())
            inst.sg.statemem.riding = inst.components.rider ~= nil and inst.components.rider:IsRiding()
        end,

        onupdate = function(inst)
            inst.components.locomotor:RunForward()
        end,

        timeline =
        {
             if inst.stealth == true then
             return
             end
            --unmounted
            TimeEvent(7 * FRAMES, function(inst)
                if not inst.sg.statemem.riding then
                    if inst.sg.mem.footsteps > 3 then
                        PlayFootstep(inst, .6, true)
                    else
                        inst.sg.mem.footsteps = inst.sg.mem.footsteps + 1
                        PlayFootstep(inst, 1, true)
                    end
                    DoFoleySounds(inst)
                end
            end),
            TimeEvent(15 * FRAMES, function(inst)
                if not inst.sg.statemem.riding then
                    if inst.sg.mem.footsteps > 3 then
                        PlayFootstep(inst, .6, true)
                    else
                        inst.sg.mem.footsteps = inst.sg.mem.footsteps + 1
                        PlayFootstep(inst, 1, true)
                    end
                    DoFoleySounds(inst)
                end
            end),

            --mounted
            TimeEvent(0 * FRAMES, function(inst)
                if inst.sg.statemem.riding then
                    DoMountedFoleySounds(inst)
                end
            end),
            TimeEvent(5 * FRAMES, function(inst)
                if inst.sg.statemem.riding then
                    if inst.sg.mem.footsteps > 3 then
                        PlayFootstep(inst, .6, true)
                    else
                        inst.sg.mem.footsteps = inst.sg.mem.footsteps + 1
                        PlayFootstep(inst, 1, true)
                    end
                end
            end),
        },

        ontimeout = function(inst)
            inst.sg:GoToState("run")
        end,
    },

 

Edited by SuperDavid
Link to comment
Share on other sites

On 8/10/2016 at 5:57 PM, SuperDavid said:

would that be fine or would that be bad?

This is bad for many reasons:

1) Making your own state and passing it to the stategraph means that if somebody else does it, you lose your state.

2) You slapped code right in the middle of a table. timeline is a table with TimeEvent values. This is a crash.

3) "inst.stealth == true" is redundant, and will also be only available on the host/server. People with movement prediction enabled will still hear the sounds because you didn't override it on the client stategraph. So a correct solution overriding the wilson stategraph would be 50% useful.

 

So, what is to be done here?

To override the function responsible for footstep noises, which luckily for us is a global function defined in worldtiledefs.lua.

-- modmain.lua

local _PlayFootstep = GLOBAL.PlayFootstep
GLOBAL.PlayFootstep = function(inst, ...)
	-- networked variable that cancels footstep noise
	if inst.stealthsteps and inst.stealthsteps:value() then
		return
	end
	return _PlayFootstep(inst, ...)
end




-- character.lua
-- in common_postinit function

inst.stealthsteps = net_bool(inst.GUID, "player.stealthsteps")
inst.stealthsteps:set(false)

First, put this code in the respective places.

 

Second, whenever you do

inst.stealth = true

you also do

inst.stealthsteps:set(true)

and when you do

inst.stealth = false

you also do

inst.stealthsteps:set(false)

to activate and deactivate the variable responsible for regulating the noises.

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