Jump to content

Recommended Posts

I'm trying to replace the default walking and idle animations with their sandstorm counterparts as long as they have specific tag or something and it is proving to be super complicated.

Edited by JustCrimson

first you need to know where for example the idle animation is defined.
How did I found it? I know that the idle animation is replaced by a hungry animation when hungry. So I first looked in the hunger component. Then I searched for all event listeners that are pushed within this component, but found nothing that is related to animations.
So I searched for "components.hunger" instead and finally found within the stategraphs SGWilson the code:           

elseif inst.components.hunger:GetPercent() < TUNING.HUNGRY_THRESH then
                inst.AnimState:PlayAnimation("hungry")
                inst.SoundEmitter:PlaySound("dontstarve/wilson/hungry")

(if one knew that animations are mostly handled in stategraphs, the search would have been easier).
For the search I used notepad++ and the "search all files" feature, to search within the unpacked scripts folder of the game and only for lua files.

So curently the state we were searching for has the name "funnyidle" in line 2333 of SGWilson.
Now we need to overwrite that state, but without breaking any other mod that also does this, nor breaking it when the devs update the game.
So the best would be if we save the old code, then check for your specific tag. if your tag is there, we do your code, if not, we simply call the old unchanged function.

To change an existiing state we can use in modmain lua this command (this is an example from my shadow maxwell mod):

AddStategraphPostInit("shadowmaxwell",function(sg)
    if sg.states.death~=nil then
        local old_onenter = sg.states.death.onenter
        local function new_onenter(inst,...)
            if inst.components~=nil and inst.components.container~=nil then
                inst.components.container:Close()
                inst.components.container:DropEverything()
                inst.components.container.canbeopened = false
            end
            return old_onenter(inst,...)
        end
        sg.states.death.onenter = new_onenter
    end
end

This example overwrites the onenter function from the state with the name death for my shadowmaxwell.
You instead want to change the state for SGWilson. All normal game characters use this SGWilson, so there is no extra file for every character. So you need to do AddStategraphPostInit("wilson"...) and within the new_onenter function you can check if inst.prefab is your special character and if it has your tag.

So now try to complete the code to replace the idle animation.

Edited by Serpens
On 9/23/2020 at 5:22 AM, Serpens said:
Spoiler

 

first you need to know where for example the idle animation is defined.
How did I found it? I know that the idle animation is replaced by a hungry animation when hungry. So I first looked in the hunger component. Then I searched for all event listeners that are pushed within this component, but found nothing that is related to animations.
So I searched for "components.hunger" instead and finally found within the stategraphs SGWilson the code:           



elseif inst.components.hunger:GetPercent() < TUNING.HUNGRY_THRESH then
                inst.AnimState:PlayAnimation("hungry")
                inst.SoundEmitter:PlaySound("dontstarve/wilson/hungry")

(if one knew that animations are mostly handled in stategraphs, the search would have been easier).
For the search I used notepad++ and the "search all files" feature, to search within the unpacked scripts folder of the game and only for lua files.

So curently the state we were searching for has the name "funnyidle" in line 2333 of SGWilson.
Now we need to overwrite that state, but without breaking any other mod that also does this, nor breaking it when the devs update the game.
So the best would be if we save the old code, then check for your specific tag. if your tag is there, we do your code, if not, we simply call the old unchanged function.

To change an existiing state we can use in modmain lua this command (this is an example from my shadow maxwell mod):



AddStategraphPostInit("shadowmaxwell",function(sg)
    if sg.states.death~=nil then
        local old_onenter = sg.states.death.onenter
        local function new_onenter(inst,...)
            if inst.components~=nil and inst.components.container~=nil then
                inst.components.container:Close()
                inst.components.container:DropEverything()
                inst.components.container.canbeopened = false
            end
            return old_onenter(inst,...)
        end
        sg.states.death.onenter = new_onenter
    end
end

This example overwrites the onenter function from the state with the name death for my shadowmaxwell.
You instead want to change the state for SGWilson. All normal game characters use this SGWilson, so there is no extra file for every character. So you need to do AddStategraphPostInit("wilson"...) and within the new_onenter function you can check if inst.prefab is your special character and if it has your tag.

So now try to complete the code to replace the idle animation.

 

 

How would I check if the player is moving or standing still? just inst:HasStateTag("run_start") and etc? because I need to get it for the walk_pre, loop, and pst. Also the last time I tried something similar to this the animation looked like it was ending too soon

(also what is the difference between walking and running and which should I use)

Right so I see what I'm doing, but I'm not sure how to do the idle animation. The sandstorm idle is the only one in "idle" and not "funnyidle". I don't know the difference or what to do about it (haven't tested my code yet either so I could be doing everything horribly wrong).

(also that code doesnt have an ending parenthesis)

Edited by JustCrimson
4 hours ago, JustCrimson said:

How would I check if the player is moving or standing still? just inst:HasStateTag("run_start") and etc? because I need to get it for the walk_pre, loop, and pst. Also the last time I tried something similar to this the animation looked like it was ending too soon

(also what is the difference between walking and running and which should I use)

Right so I see what I'm doing, but I'm not sure how to do the idle animation. The sandstorm idle is the only one in "idle" and not "funnyidle". I don't know the difference or what to do about it (haven't tested my code yet either so I could be doing everything horribly wrong).

(also that code doesnt have an ending parenthesis)

I'm no expert about animations and I never made a custom character. I just know how to change the stategraphs like above and I also suceeded to make the shadow maxwell do the "hungry" animations as "funnyidle".
And I thought this is similar to your request, isn't it? funnyidle is the animation the character does every ~10 seconds or so. But even if you don't want to change funnyilde, the same code style should apply to all other stategraphs.

If you explain alot more detailed, what exactly you want to achieve, I can see if I can help you more than this.

23 minutes ago, Serpens said:

I'm no expert about animations and I never made a custom character. I just know how to change the stategraphs like above and I also suceeded to make the shadow maxwell do the "hungry" animations as "funnyidle".
And I thought this is similar to your request, isn't it? funnyidle is the animation the character does every ~10 seconds or so. But even if you don't want to change funnyilde, the same code style should apply to all other stategraphs.

If you explain alot more detailed, what exactly you want to achieve, I can see if I can help you more than this.

No, but what you gave me is probably going to work for everything else, the sand idle (the constant sand position) is just a regular idle so it should work if I just do that

On 9/25/2020 at 4:56 AM, Serpens said:

If you explain alot more detailed, what exactly you want to achieve, I can see if I can help you more than this.

I want my character to use the same walking and idle animations, as if they were in a sandstorm, while they have a specific tag

Here is the code I have now

--[[----------------------------
   Sunburning
----------------------------]]--
AddStategraphPostInit("wilson", function(sg)
    local function OverrideRunStateAnim(inst)
        if (inst.sg.statemem.groggy or inst.sg.statemem.moosegroggy or inst.sg.statemem.goosegroggy) then
            return true
        elseif (inst.sg.statemem.careful) then
            return true
        else
            return false
        end
    end

    --[["run_start/pre"]]--
    if sg.states.run_start ~= nil then
        local old_onenter = sg.states.run_start.onenter
        local function new_onenter(inst,...)
            if inst.prefab == "wyrdelyn" and inst:HasTag("sunburning") and not OverrideRunStateAnim(inst) then
                inst.AnimState:PlayAnimation("sand_walk_pre")
            end
            return old_onenter(inst,...)
        end
        sg.states.run_start.onenter = new_onenter
    end
    --[["run/loop"]]--
    if sg.states.run ~= nil then 
        local old_onenter = sg.states.run.onenter
        local function new_onenter(inst,...)
            if inst.prefab == "wyrdelyn" and inst:HasTag("sunburning") and OverrideRunStateAnim(inst) then
                inst.AnimState:PlayAnimation("sand_walk")
            end
            return old_onenter(inst,...)
        end
        sg.states.run.onenter = new_onenter
    end
    --[["run_stop/pst"]]--
    if sg.states.run_stop ~= nil then 
        local old_onenter = sg.states.run_stop.onenter
        local function new_onenter(inst,...)
            if inst.prefab == "wyrdelyn" and inst:HasTag("sunburning") and OverrideRunStateAnim(inst) then
                inst.AnimState:PlayAnimation("sand_walk_pst")
            end
            return old_onenter(inst,...)
        end
        sg.states.run_stop.onenter = new_onenter
    end
    --[["idle"]]--
    if sg.states.idle ~= nil then 
        local old_onenter = sg.states.idle.onenter
        local function new_onenter(inst,...)
            if inst.prefab == "wyrdelyn" and inst:HasTag("sunburning") and OverrideRunStateAnim(inst) then
                if not (inst.AnimState:IsCurrentAnimation("sand_walk_pst") or inst.AnimState:IsCurrentAnimation("sand_walk") or inst.AnimState:IsCurrentAnimation("sand_walk_pre")) then
                    inst.AnimState:PlayAnimation("sand_idle_pre")
                end
                inst.AnimState:PushAnimation("sand_idle_loop", true)
            end
            return old_onenter(inst,...)
        end
        sg.states.idle.onenter = new_onenter
    end
end)

It all runs, but it just doesn't play any of the animations. The line is being ran like I can put print statements next to it, I just don't play the animation and I get no errors

Edited by JustCrimson

I think this will do it:, using the upvaluehacker to change a local function, will already solve all run states.

local UpvalueHacker = GLOBAL.require("upvaluehacker")

AddStategraphPostInit("wilson", function(sg)

    if sg.states.run ~= nil then 
        local old_GetRunStateAnim = UpvalueHacker.GetUpvalue(sg.states.run.onenter, "GetRunStateAnim")
        local function new_GetRunStateAnim(inst,...)
            if not inst.sg.statemem.heavy and inst.prefab=="wilson" then
                return "sand_walk"
            else
                return old_GetRunStateAnim(inst,...)
            end
        end
        UpvalueHacker.SetUpvalue(sg.states.run.onenter, new_GetRunStateAnim, "GetRunStateAnim") -- this will overwrite that local function, working for run_start,run and run_stop at once
    end

    if sg.states.idle ~= nil then 
        local old_onenter = sg.states.idle.onenter
        local function new_onenter(inst,pushanim,...)
            old_onenter(inst,pushanim,...)
            if inst.prefab == "wilson" then
                if inst.sg.currentstate.name=="idle" then -- if we not left the idle state
                    if not inst.components.inventory:IsHeavyLifting() then
                        local anims = {}
                        if not (inst.AnimState:IsCurrentAnimation("sand_walk_pst") or
                                inst.AnimState:IsCurrentAnimation("sand_walk") or
                                inst.AnimState:IsCurrentAnimation("sand_walk_pre")) then
                            table.insert(anims, "sand_idle_pre")
                        end
                        table.insert(anims, "sand_idle_loop")
                        if pushanim then
                            for k, v in pairs(anims) do
                                inst.AnimState:PushAnimation(v, k == #anims)
                            end
                        else
                            inst.AnimState:PlayAnimation(anims[1], #anims == 1)
                            for k, v in pairs(anims) do
                                if k > 1 then
                                    inst.AnimState:PushAnimation(v, k == #anims)
                                end
                            end
                        end
                    end
                end
            end
        end
        sg.states.idle.onenter = new_onenter
    end
end)

AddStategraphPostInit("wilson_client", function(sg)

    if sg.states.run ~= nil then 
        local old_GetRunStateAnim = UpvalueHacker.GetUpvalue(sg.states.run.onenter, "GetRunStateAnim")
        local function new_GetRunStateAnim(inst,...)
            if not inst.sg.statemem.heavy and inst.prefab=="wilson" then
                return "sand_walk"
            else
                return old_GetRunStateAnim(inst,...)
            end
        end
        UpvalueHacker.SetUpvalue(sg.states.run.onenter, new_GetRunStateAnim, "GetRunStateAnim")
    end

    if sg.states.idle ~= nil then 
        local old_onenter = sg.states.idle.onenter
        local function new_onenter(inst,pushanim,...)
            old_onenter(inst,pushanim,...)
            if inst.prefab == "wilson" then
                if inst.sg.currentstate.name=="idle" then -- if we not left the idle state
                    if (inst.replica.inventory ~= nil and inst.replica.inventory:IsHeavyLifting()) then
                        return
                    elseif pushanim == "cancel" then
                        return
                    elseif inst:HasTag("nopredict") or inst:HasTag("pausepredict") then
                        return
                    elseif pushanim == "noanim" then
                        return
                    end
                    if inst.replica.rider ~= nil and inst.replica.rider:IsRiding() then
                        return
                    end
                    if (   inst.AnimState:IsCurrentAnimation("sand_walk_pst") or
                        inst.AnimState:IsCurrentAnimation("sand_walk") or
                        inst.AnimState:IsCurrentAnimation("sand_walk_pre")
                        ) then
                        if pushanim then
                            inst.AnimState:PushAnimation("sand_idle_loop", true)
                        else
                            inst.AnimState:PlayAnimation("sand_idle_loop", true)
                        end
                    end
                end
            end
        end
        sg.states.idle.onenter = new_onenter
    end
end)

If you don't want the "funny_idle" animation to play, you can overwrite ontimeout within the idle state, or change the funnyidle itself.

I tried to overwrite as less as possible. The idle state is quite important, it is not only for simple animation change. So if we break it somehow (or eg a game update changes it a bit, while we overwrote it) alot can break. So it is quite dangerous to change it.
Therefore I first call the old_onenter within the new_onenter, to eg. trigger a state change and other stuffs. After that I only change the animation to sand, if we not already switched state and if the first few conditions from the old_onenter are not true. This last part is problematic, because if the devs change the onenter function and add some additional animations that have priority over the sand animation, then this will break our code. But this code is still better than overwriting (coyp/pasting) the whole onenter function.

This is the best way I found, although it is by far not perfect, since a game update could break it.

Edited by Serpens

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
×
  • Create New...