Sign in to follow this  
Hornete

Help with stategraphs!

Recommended Posts

Hornete    10309

Hello everyone! I have a couple states that are activated by an action, they work perfectly fine if you don't have lag compensation enabled, If you do however.... It does not work! It works for hosts with lag compensation, but If you are a client or on a caves server and have lag compensation enabled, the state won't activate and instead the character just moves to the mouse. It does work without the lag compensation enabled.

 

AddStategraphState("wilson", State {

        name = "dodge",
        tags = {"busy", "evade","no_stun"},

        onenter = function(inst)
            inst.sg:SetTimeout(0.25) -- 0.15
            inst.components.locomotor:Stop()
            inst.AnimState:PlayAnimation("slide_pre")

            inst.AnimState:PushAnimation("slide_loop")
            inst.SoundEmitter:PlaySound("dontstarve_DLC003/characters/wheeler/slide")
            inst.Physics:SetMotorVelOverride(20,0,0)
            inst.components.locomotor:EnableGroundSpeedMultiplier(false)
            inst.components.health:SetInvincible(true)
            inst:PerformBufferedAction()
			if GLOBAL.TheNet:GetServerGameMode() == "lavaarena" then
				COMMON_FNS.DoFrontAOE(inst, 5, 0, 3.25, "strong", nil)
				inst:DoTaskInTime(0.5, function()
					COMMON_FNS.DoFrontAOE(inst, 5, 0, 3.25, "strong", nil)
				end)
			end
			
			inst.last_dodge_time = GLOBAL.GetTime()
        end,

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

        onexit = function(inst)
            inst.components.locomotor:EnableGroundSpeedMultiplier(true)
            inst.Physics:ClearMotorVelOverride()
            inst.components.locomotor:Stop()
        end,        
    } 
)

	
AddStategraphState("wilson", 
	State
    {
        name = "dodge_pst",
        tags = {"evade","no_stun"},

        onenter = function(inst)
            inst.AnimState:PlayAnimation("slide_pst")
            inst.components.locomotor:SetBufferedAction(nil)
            inst.components.health:SetInvincible(false)
			if GLOBAL.TheNet:GetServerGameMode() == "lavaarena" then
				if inst.laserdodge ~= nil then
					inst.laserdodge:Cancel()
					inst.laserdodge = nil
				end
			end
        end,

        events =
        {
            EventHandler("animover", function(inst)
                inst.sg:GoToState("idle")
            end ),
        }
    }
)
AddStategraphEvent("wilson", 
	EventHandler("locomote", function(inst, data)
        if inst.sg:HasStateTag("busy") then
            return
        end
        local is_moving = inst.sg:HasStateTag("moving")
        local should_move = inst.components.locomotor:WantsToMoveForward()
		local bufferedaction = inst:GetBufferedAction()

        if inst.sg:HasStateTag("bedroll") or inst.sg:HasStateTag("tent") or inst.sg:HasStateTag("waking") then -- wakeup on locomote
            if inst.sleepingbag ~= nil and inst.sg:HasStateTag("sleeping") then
                inst.sleepingbag.components.sleepingbag:DoWakeUp()
                inst.sleepingbag = nil
            end
		elseif bufferedaction and bufferedaction.action.id == "DODGE" then
            inst.sg:GoToState("dodge")
        elseif is_moving and not should_move then
            inst.sg:GoToState("run_stop")
        elseif not is_moving and should_move then
            inst.sg:GoToState("run_start")
        elseif data.force_idle_state and not (is_moving or should_move or inst.sg:HasStateTag("idle")) then
            inst.sg:GoToState("idle")
        end
    end)
)

These states are called when you right click doing the action. I'd appreciate any help!, Also I'd like a way to have the action used while the player is running or attacking. However you must pause in order to do the action. How can I make it so that the action can be performed whilst running or attacking? Thanks for your help!

Share this post


Link to post
Share on other sites
Ultroman    742

Sorry, m8. I have very, very limited insight into the finer details of the action system.

I hope some of the veterans or developers can chime in on this one.

Share this post


Link to post
Share on other sites
ksaab    54

There is "wilson_client" stategraph. The game uses it if lag compenstion is enabled on client (when you're playing as host without caves you are actually not a client and don't use this stategraph). It is almost the same but doesn't contain any game logic.

The solution for the problem — you need to add lightweight versions of your states to "wilson_client".

Share this post


Link to post
Share on other sites
Hornete    10309
3 hours ago, ksaab said:

There is "wilson_client" stategraph. The game uses it if lag compenstion is enabled on client (when you're playing as host without caves you are actually not a client and don't use this stategraph). It is almost the same but doesn't contain any game logic.

The solution for the problem — you need to add lightweight versions of your states to "wilson_client".

Thank you so much!, what should I exactly remove? I copied and pasted everything and added it to the client stategraph, Removed the the SetInvincible line, The dodge works perfectly but when I move after that. I teleport to the original position from where I dodged. Again Thank you so much.

Share this post


Link to post
Share on other sites
ksaab    54

Your character moves after dodging because of inst.Physics:SetMotorVelOverride(20,0,0). Client draws fake position for character, but real doesn't change (actually it doesn't work at all). Any client state should contain animation and tag things only.

AddStategraphState("wilson_client", State {
    name = "dodge",
    tags = {"busy", "evade", "no_stun"},

    onenter = function(inst)
        inst.components.locomotor:Stop()
        inst.AnimState:PlayAnimation("slide_pre", false) --only pre animation, the slide animation command must come from server
        inst:PerformPreviewBufferedAction() --this thing says to server "hey man, I'm doing something"
        inst.sg:SetTimeout(2)
    end,
    
    --[[I use this chunk, but I don't have idea what it really does. Sometime state doesn't work without "onupdate". It requires "doing" tag for server and client states
    onupdate = function(inst)
		if inst:HasTag("doing") then
			if inst.entity:FlattenMovementPrediction() then
				inst.sg:GoToState("idle", "noanim")
			end
		elseif inst.bufferedaction == nil then
			inst.sg:GoToState("idle")
		end
	end,
    ]]--

    --timeout happens only if player have a lag
    ontimeout = function(inst)
		inst:ClearBufferedAction()
		inst.sg:GoToState("idle")
	end,
)

I think the second state is not requied for the client stategraph.

Edited by ksaab

Share this post


Link to post
Share on other sites
Hornete    10309
6 hours ago, ksaab said:

Your character moves after dodging because of inst.Physics:SetMotorVelOverride(20,0,0). Client draws fake position for character, but real doesn't change (actually it doesn't work at all). Any client state should contain animation and tag things only.


AddStategraphState("wilson_client", State {
    name = "dodge",
    tags = {"busy", "evade", "no_stun"},

    onenter = function(inst)
        inst.components.locomotor:Stop()
        inst.AnimState:PlayAnimation("slide_pre", false) --only pre animation, the slide animation command must come from server
        inst:PerformPreviewBufferedAction() --this thing says to server "hey man, I'm doing something"
        inst.sg:SetTimeout(2)
    end,
    
    --[[I use this chunk, but I don't have idea what it really does. Sometime state doesn't work without "onupdate". It requires "doing" tag for server and client states
    onupdate = function(inst)
		if inst:HasTag("doing") then
			if inst.entity:FlattenMovementPrediction() then
				inst.sg:GoToState("idle", "noanim")
			end
		elseif inst.bufferedaction == nil then
			inst.sg:GoToState("idle")
		end
	end,
    ]]--

    --timeout happens only if player have a lag
    ontimeout = function(inst)
		inst:ClearBufferedAction()
		inst.sg:GoToState("idle")
	end,
)

I think the second state is not requied for the client stategraph.

AddStategraphState("wilson_client",
	State {
    name = "dodge",
    tags = {"busy", "evade", "no_stun"},

    onenter = function(inst)
        inst.components.locomotor:Stop()
        inst.AnimState:PlayAnimation("slide_pre", false)
        inst:PerformPreviewBufferedAction()
        inst.sg:SetTimeout(2)
    end,
	
	onupdate = function(inst) --I've tried with and without this
		if inst:HasTag("doing") then
			if inst.entity:FlattenMovementPrediction() then
				inst.sg:GoToState("idle", "noanim")
			end
		elseif inst.bufferedaction == nil then
			inst.sg:GoToState("idle")
		end
	end,
	
    ontimeout = function(inst)
		inst:ClearBufferedAction()
		inst.sg:GoToState("idle")
	end,
	}
)

AddStategraphEvent("wilson_client",  --I need this or the Dodge won't happen at all
	EventHandler("locomote", function(inst)
        if inst.sg:HasStateTag("busy") or inst:HasTag("busy") then
            return
        end
        local is_moving = inst.sg:HasStateTag("moving")
        local should_move = inst.components.locomotor:WantsToMoveForward()
		local bufferedaction = inst:GetBufferedAction()

        if inst:HasTag("sleeping") then
            if should_move and not inst.sg:HasStateTag("waking") then
                inst.sg:GoToState("wakeup")
            end
        elseif not inst.entity:CanPredictMovement() then
            if not inst.sg:HasStateTag("idle") then
                inst.sg:GoToState("idle")
            end
		elseif bufferedaction and bufferedaction.action.id == "DODGE" then
            inst.sg:GoToState("dodge")
        elseif is_moving and not should_move then
            inst.sg:GoToState("run_stop")
        elseif not is_moving and should_move then
            inst.sg:GoToState("run_start")
        end
    end)
)

Sorry for bothering you again, but now the player won't even move, and instead does the dodge animation in its place and instantly reverts to the idle state. Thank you so much for your help.

Share this post


Link to post
Share on other sites
ksaab    54
AddStategraphState("wilson", State 
{
	name = "dodge_pre",
	tags = {"doing", "busy", "evade", "no_stun"},

	onenter = function(inst)
		inst.components.locomotor:Stop()
		inst.AnimState:PlayAnimation("parry_pre", false)
	end,

	events =
	{
		EventHandler("animover", function(inst)
			inst.sg:GoToState("dodge")
		end),
	}
})

AddStategraphState("wilson", State
{
	name = "dodge",
	tags = {"doing", "evade", "no_stun", "nopredict", "nomorph",},

	onenter = function(inst)
		inst.AnimState:PlayAnimation("parry_loop", true)
		inst.components.health:SetInvincible(true)
		inst.components.locomotor:EnableGroundSpeedMultiplier(true)
		inst.last_dodge_time = GLOBAL.GetTime()
		inst.Physics:SetMotorVel(-20,0,0)

		--this should be moved to the action code
		-----------------------------------------
		if GLOBAL.TheNet:GetServerGameMode() == "lavaarena" then
			COMMON_FNS.DoFrontAOE(inst, 5, 0, 3.25, "strong", nil)
			inst:DoTaskInTime(0.5, function()
				COMMON_FNS.DoFrontAOE(inst, 5, 0, 3.25, "strong", nil)
			end)

			if inst.laserdodge ~= nil then
				inst.laserdodge:Cancel()
				inst.laserdodge = nil
			end
		end
		-----------------------------------------

		inst.SoundEmitter:PlaySound("dontstarve_DLC003/characters/wheeler/slide")
		inst:PerformBufferedAction()

		--not the best idea
		--because of latency character will dash for shorter distance then you're expecting
		--will be much better to calculate target postion and use onupdate until character reaches it; 
		--after fire your custom event and catch it in EventHandler("yourevent", function() place code from ontimeout end)
		inst.sg:SetTimeout(0.25)
	end,

	events =
	{
		EventHandler("animqueueover", function(inst)
			--this happens only after inst.AnimState:PlayAnimation("parry_pst", false) will be complete
			if inst.AnimState:AnimDone() then
				inst.sg:GoToState("idle")
			end
		end),
	},

	ontimeout = function(inst)
		inst.Physics:SetMotorVel(0, 0, 0)
		inst.components.health:SetInvincible(false)
		inst.components.locomotor:EnableGroundSpeedMultiplier(false)
		inst.AnimState:PlayAnimation("parry_pst", false)
	end,

	onexit = function(inst)
		--need to double this code — case when we will leave the state before timeout
		inst.Physics:SetMotorVel(0, 0, 0)
		inst.components.health:SetInvincible(false)
		inst.components.locomotor:EnableGroundSpeedMultiplier(false)
	end,
})

AddStategraphActionHandler("wilson", ActionHandler(ACTIONS.DODGE, "dodge_pre"))

AddStategraphState("wilson_client",
	State {
    name = "dodge_pre",
    tags = {"doing", "busy", "evade", "no_stun"},

    onenter = function(inst)
        inst.components.locomotor:Stop()
        inst.AnimState:PlayAnimation("parry_pre", false)
		inst.AnimState:PushAnimation("parry_loop", true)
        inst:PerformPreviewBufferedAction()
        inst.sg:SetTimeout(2)
    end,
	
	onupdate = function(inst) 
		if inst:HasTag("doing") then
			if inst.entity:FlattenMovementPrediction() then
				inst.sg:GoToState("idle", "noanim")
			end
		elseif inst.bufferedaction == nil then
			inst.sg:GoToState("idle")
		end
	end,
	
    ontimeout = function(inst)
		inst:ClearBufferedAction()
		inst.sg:GoToState("idle")
	end,
	}
)

AddStategraphActionHandler("wilson_client", ActionHandler(ACTIONS.DODGE, "dodge_pre"))

--actions collectors, dodge will work with any equipped weapon. Not good but it's just an example
--I'm not sure overriding onlocomote event handler is a good idea
--check out the wortox prefab if you want use dodge like wortox's soul hop
AddComponentAction("EQUIPPED", "weapon", function(inst, doer, target, actions, right)
	if right then table.insert(actions, ACTIONS.DODGE) end
end)

AddComponentAction("POINT", "weapon", function(inst, doer, pos, actions, right)
	if right then table.insert(actions, ACTIONS.DODGE) end
end)

don't forget to rename animation

Share this post


Link to post
Share on other sites
Hornete    10309
57 minutes ago, ksaab said:

[snip snop]

Thank you so much, but sadly it didn't work, The component action thing doesn't work and thats why I edit the locomotoe event.

I'll try experimenting around with the states, thank you.

Share this post


Link to post
Share on other sites
ksaab    54
AddAction("DODGE", "Dodge", function(act)
    act.doer:PushEvent("letsdododge", {pos = act.pos or Vector3(act.target.Transform:GetWorldPosition())})
    return true
end)

ACTIONS.DODGE.distance = math.huge
ACTIONS.DODGE.instant = true

AddStategraphEvent("wilson", EventHandler("letsdododge", function(inst, data)
		inst.sg:GoToState("dodge_pre", data)
	end)
)

I think you want something like this — use dodge while player is moving. It's a bit dirty-tricky and looks weird on clients. But the second could be fixed I suppose.

Client-stategraph doesn't need any code at all. Other things from the previous post. Dodge direction should be handled in the "pre" state with data.pos.

One more thing — pos = act.pos will work incorrect after RoT release. Klei has changed this filed. Check out bufferedaction.lua after update.

Two more thing — 0.25 widnow is too short. Clients with high unstable ping will have problems (I did a parry spell with 0.5 seconds window in The Forge Battle Pack and recieved a lot of complaints)

Share this post


Link to post
Share on other sites
Hornete    10309
1 hour ago, ksaab said:

snip

Ok! I'm getting there, just a couple of problems

1. The dodge can be cancelled by moving, I thought the busy tag prevents this which I have? It's weird how I can cancel the dodge by moving, this didn't happen before

2. The dodge happens in the direction the character is facing, not the direction of the mouse

3. The Dodge text prompt does not dissapear after the action is done.

Share this post


Link to post
Share on other sites
ksaab    54

1) Yep. Busy tag should help.

2) Dodge direction should be handled in the "pre" state with data.pos

if data and data.pos then
	inst:ForceFacePoint(data.pos.x, 0, data.pos.z)
end

3) Add conditions to action collectors. Something like this:

 if not inst:HasTag("busy") then

 

Share this post


Link to post
Share on other sites
Hornete    10309
9 hours ago, ksaab said:

snip snop

Busy tag helped! Thank you!
 

2. I put that code in but my character still dodges in the direction there facng
 

3. What do you mean action collectors? Sorry, i'm not familiar with that term

4. It works great with lag compensation turned on, but while im running and decide to dodge, my character teleports back for some reason. If i'm standing still, it works perfectly fine.
 

5.

ontimeout = function(inst)
		inst.Physics:SetMotorVel(0, 0, 0)
		inst.components.health:SetInvincible(false)
		inst.components.locomotor:EnableGroundSpeedMultiplier(false)
		inst.AnimState:PlayAnimation("slide_pst", false)
	end,

I wanna remove the busy tag here, does inst:RemoveStateTag("busy"), work?

Share this post


Link to post
Share on other sites
ksaab    54

2) Did you add data as second argument for onenter function?

3) AddComponentAction functions.

4) I suppose a fix for this problem requires a dirty hook. I don't have a good solution for it. Try to add inst.Physics:Teleport(inst.Transform:GetWorldPosition()) to pre state.

5) It should.

Share this post


Link to post
Share on other sites
Hornete    10309
1 hour ago, ksaab said:

snip snop

2) Thank you! it worked!

3)

AddComponentAction("EQUIPPED", "weapon", function(inst, doer, target, actions, right)
	if not inst:HasTag("busy") then
		if right then table.insert(actions, GLOBAL.ACTIONS.DODGE) end
	end
end)

AddComponentAction("POINT", "weapon", function(inst, doer, pos, actions, right)
	if not inst:HasTag("busy") then
		if right then table.insert(actions, GLOBAL.ACTIONS.DODGE) end
	end
end)

Is that correct? The prompt still appears, the prompt dissapears if your the host.

 

4.

AddStategraphState("wilson", GLOBAL.State 
{
	name = "dodge_pre",
	tags = {"doing", "busy", "evade", "no_stun"},

	onenter = function(inst, data)
		inst.components.locomotor:Stop()
		inst.AnimState:PlayAnimation("slide_pre", false)
		inst.Physics:Teleport(inst.Transform:GetWorldPosition())
		if data and data.pos then
			inst:ForceFacePoint(data.pos.x, 0, data.pos.z)
		end
	end,

	events =
	{
		GLOBAL.EventHandler("animover", function(inst)
			inst.sg:GoToState("dodge")
		end),
	}
})

I put it there, is that correct? Do I also need to put it in the client stategraph?

 

5. It crashed for me, RemoveStateTag is a nil value.

Edited by Hornete

Share this post


Link to post
Share on other sites
ksaab    54

3) Yes... busy tag doesn't sync... hmmm. Net variable? Anyway it's required if you want to add a cooldown for dodge.

4) Yep. But with this implementation you don't use client ctategraph at all.

5) You've lost sg — inst.sg:RemoveStateTag("busy")

Share this post


Link to post
Share on other sites
Hornete    10309
1 hour ago, ksaab said:

 

3)What's a net variable exactly?

4) So, I should remove the client state? It still isn't working, though thats with the client state

5) a new problem has rised, Whenever I have something equipped in my hand slot, There's no cooldown but when there isn't, theres still the regular 1.5 seconds of cooldown,

 

AddComponentAction("EQUIPPED", "weapon", function(inst, doer, target, actions, right)
	if right then table.insert(actions, GLOBAL.ACTIONS.DODGE) end
end)

I suspect it has to do with this?

Share this post


Link to post
Share on other sites
ksaab    54

3) Check out the forum, there is a pinned thread about them;

4) I offer variants to you. How you will implement dependng on your preferences. The last variant doesn't requie a client stategraph;

5) How do you use dodge without weapon? GetPointSpecialActions? Or key binding? How do you realize cooldown on the client side? You can remove my AddComponentAction if you don't need them.

Share this post


Link to post
Share on other sites
Mobbstar    14118
11 hours ago, Hornete said:

What's a net variable exactly?

On 23.12.2014 at 1:28 AM, rezecib said:

Networked variables:

Spoiler

These are special variables that are synced over the network (exclusively from the server to the client, and not the other way around-- that's what RPCs are for), and that when they get changed it triggers an event on the client-- these are usually called "dirty" events, because they signal that any visuals that were relying on the value are now outdated (or "dirty") and need to be updated (or "cleaned").  Most of the game's net variables are a bit overwhelming to look at, but PeterA's The Hunt mod shows how to use them, and he also made a guide on all the network variables available.

When setting up a network variable, there are several key considerations. They attach to a specific entity via the GUID, so choosing the right entity is important. You can either ride onto existing game entities, or you can create your own like the classifieds. Then you need to make sure that whatever events happen on the server are hooked up to setting the network variables. Then, if the client needs to know when a network variable has changed (usually ones that require a visual update as a result, as opposed to ones where the client just looks at the value when it needs to), you can specify an event to be triggered when the value changes.

There's a file called "scripts/netvars.lua" that lists the exact netvars implemented.

Share this post


Link to post
Share on other sites
Hornete    10309
17 hours ago, ksaab said:

snip

4) Ok, so I've put that line in and gotten rid of the client state, and it still teleports me back. With or without the client state,

Thank you so much for all the help, I would have never solved this without you.

Share this post


Link to post
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
Sign in to follow this