Jump to content

Recommended Posts

This is no small feat, since the player doesn't control which time of day they can sleep. The bedrolls, tent and siestahut and the character's stategraph controls those things. I made a mod called Sleepy Time which allows you to set at which times of day bedrolls, tents and/or siestahuts can be used, and I ended up having to edit the "bedroll" and "tent" states to do it. This seems like a lot of work to have to go through, to do something you'd think many people want to mess with; their character's sleep cycle. But there you go. Go subscribe to Sleepy Time on the Steam Workshop and check out the code. It should be well commented, and if not, I apologize :)

Edited by Ultroman
On 10/25/2019 at 8:39 PM, Ultroman said:

This is no small feat, since the player doesn't control which time of day they can sleep. The bedrolls, tent and siestahut and the character's stategraph controls those things. I made a mod called Sleepy Time which allows you to set at which times of day bedrolls, tents and/or siestahuts can be used, and I ended up having to edit the "bedroll" and "tent" states to do it. This seems like a lot of work to have to go through, to do something you'd think many people want to mess with; their character's sleep cycle. But there you go. Go subscribe to Sleepy Time on the Steam Workshop and check out the code. It should be well commented, and if not, I apologize :)

Thanks a bunch for this!

I took a look, but like I said I'm extremely new to this. Any specific code I should be looking at?

Edited by Arestless

This would be the simplified version, which shows how to change just one state. All of this in modmain. Understand, though, that this overwrites the state, so if any other mod also does this, one or the other mod won't work.

local function addState(SGname, state)
	print("addState")
	for k,v in pairs(_G.SGManager.instances)
	do
		if(k.sg.name == SGname) then
			k.sg.states[state.name] = state
			break
		end
	end
end

local function ChangeBedrollAndTentStateDST(inst)
	-- Override host bedroll-state
	addState("wilson", _G.State{
		name = "bedroll",
        tags = { "bedroll", "busy", "nomorph" },

        onenter = function(inst)
            inst.components.locomotor:Stop()
			-- If the player has the "furry" tag, then he is currently using a furry bedroll, and not a
			-- straw bedroll. The tag is added in an overridden onuse.
			local furry = false
			if inst:HasTag("furry") then
				furry = inst:HasTag("furry")
				inst:RemoveTag("furry")
			end
			local canSleepAtDay = false
			if bedrollCanSleepAtDay and not furry then
				canSleepAtDay = true
			elseif furryBedrollCanSleepAtDay and furry then
				canSleepAtDay = true
			end
			inst.canSleepAtDay = canSleepAtDay
			
            local failreason =
                (_G.TheWorld.state.isday and
                    (_G.TheWorld:HasTag("cave") and "ANNOUNCE_NODAYSLEEP_CAVE" or "ANNOUNCE_NODAYSLEEP")
                )
                or (IsNearDanger(inst) and "ANNOUNCE_NODANGERSLEEP")
                or nil
			
			-- Custom code to override the ability to sleep/siesta at day/night
			if siestahutCanSiestaAtNight and (failreason == "ANNOUNCE_NONIGHTSIESTA" or
			   failreason == "ANNOUNCE_NONIGHTSIESTA_CAVE") then 
			   failreason = nil
			elseif canSleepAtDay and (failreason == "ANNOUNCE_NODAYSLEEP_CAVE"
				   or failreason == "ANNOUNCE_NODAYSLEEP") then
				failreason = nil
			end

            -- you can't sleep if you will die from it
			if inst.components.health and ((not furry and bedrollHealthChange < 0 and inst.components.health.currenthealth <= -bedrollHealthChange) or (furry and furryBedrollHealthChange < 0 and inst.components.health.currenthealth <= -furryBedrollHealthChange)) then
				failreason = "health"
			end

			-- you can still sleep if you're hungry, but only if you have the required hunger
			if not canSleepThroughStarvation and inst.components.hunger and ((not furry and bedrollHungerChange < 0 and inst.components.hunger.current < -bedrollHungerChange) or (furry and furryBedrollHungerChange < 0 and inst.components.hunger.current < -furryBedrollHungerChange)) then
				failreason = "ANNOUNCE_NOHUNGERSLEEP"
			end
			
            if failreason ~= nil then
                inst:PushEvent("performaction", { action = inst.bufferedaction })
                inst:ClearBufferedAction()
                inst.sg:GoToState("idle")
                if inst.components.talker ~= nil then
					if failreason == "health" then
						inst.components.talker:Say("No way! I'll die in my sleep!")
					else
						inst.components.talker:Say(_G.GetString(inst, failreason))
					end
                end
                return
            end

            inst.AnimState:PlayAnimation("action_uniqueitem_pre")
            inst.AnimState:PushAnimation("bedroll", false)

            SetSleeperSleepState(inst)
        end,

        timeline =
        {
            _G.TimeEvent(20 * _G.FRAMES, function(inst) 
                inst.SoundEmitter:PlaySound("dontstarve/wilson/use_bedroll")
            end),
        },

        events =
        {
            _G.EventHandler("firedamage", function(inst)
                if inst.sg:HasStateTag("sleeping") then
                    inst.sg.statemem.iswaking = true
                    inst.sg:GoToState("wakeup")
                end
            end),
            _G.EventHandler("animqueueover", function(inst)
                if inst.AnimState:AnimDone() then
                    if _G.TheWorld.state.isday and not inst.canSleepAtDay or
                        (inst.components.health ~= nil and inst.components.health.takingfiredamage) or
                        (inst.components.burnable ~= nil and inst.components.burnable:IsBurning()) then
                        inst:PushEvent("performaction", { action = inst.bufferedaction })
                        inst:ClearBufferedAction()
                        inst.sg.statemem.iswaking = true
                        inst.sg:GoToState("wakeup")
                    elseif inst:GetBufferedAction() then
                        inst:PerformBufferedAction() 
                        if inst.components.playercontroller ~= nil then
                            inst.components.playercontroller:Enable(true)
                        end
                        inst.sg:AddStateTag("sleeping")
                        inst.sg:AddStateTag("silentmorph")
                        inst.sg:RemoveStateTag("nomorph")
                        inst.sg:RemoveStateTag("busy")
                        inst.AnimState:PlayAnimation("bedroll_sleep_loop", true)
                    else
                        inst.sg.statemem.iswaking = true
                        inst.sg:GoToState("wakeup")
                    end
                end
            end),
        },

        onexit = function(inst)
			inst.canSleepAtDay = false
            if inst.sleepingbag ~= nil then
                --Interrupted while we are "sleeping"
                inst.sleepingbag.components.sleepingbag:DoWakeUp(true)
                inst.sleepingbag = nil
				SetSleeperAwakeState(inst)
            elseif not inst.sg.statemem.iswaking then
                --Interrupted before we are "sleeping"
				SetSleeperAwakeState(inst)
            end
        end,
	})
	return inst
end

if _G.TheNet and _G.TheNet:GetIsServer() then
	AddPlayerPostInit(ChangeBedrollAndTentStateDST)
end

 

1 hour ago, Ultroman said:

This would be the simplified version, which shows how to change just one state. All of this in modmain. Understand, though, that this overwrites the state, so if any other mod also does this, one or the other mod won't work.


local function addState(SGname, state)
	print("addState")
	for k,v in pairs(_G.SGManager.instances)
	do
		if(k.sg.name == SGname) then
			k.sg.states[state.name] = state
			break
		end
	end
end

local function ChangeBedrollAndTentStateDST(inst)
	-- Override host bedroll-state
	addState("wilson", _G.State{
		name = "bedroll",
        tags = { "bedroll", "busy", "nomorph" },

        onenter = function(inst)
            inst.components.locomotor:Stop()
			-- If the player has the "furry" tag, then he is currently using a furry bedroll, and not a
			-- straw bedroll. The tag is added in an overridden onuse.
			local furry = false
			if inst:HasTag("furry") then
				furry = inst:HasTag("furry")
				inst:RemoveTag("furry")
			end
			local canSleepAtDay = false
			if bedrollCanSleepAtDay and not furry then
				canSleepAtDay = true
			elseif furryBedrollCanSleepAtDay and furry then
				canSleepAtDay = true
			end
			inst.canSleepAtDay = canSleepAtDay
			
            local failreason =
                (_G.TheWorld.state.isday and
                    (_G.TheWorld:HasTag("cave") and "ANNOUNCE_NODAYSLEEP_CAVE" or "ANNOUNCE_NODAYSLEEP")
                )
                or (IsNearDanger(inst) and "ANNOUNCE_NODANGERSLEEP")
                or nil
			
			-- Custom code to override the ability to sleep/siesta at day/night
			if siestahutCanSiestaAtNight and (failreason == "ANNOUNCE_NONIGHTSIESTA" or
			   failreason == "ANNOUNCE_NONIGHTSIESTA_CAVE") then 
			   failreason = nil
			elseif canSleepAtDay and (failreason == "ANNOUNCE_NODAYSLEEP_CAVE"
				   or failreason == "ANNOUNCE_NODAYSLEEP") then
				failreason = nil
			end

            -- you can't sleep if you will die from it
			if inst.components.health and ((not furry and bedrollHealthChange < 0 and inst.components.health.currenthealth <= -bedrollHealthChange) or (furry and furryBedrollHealthChange < 0 and inst.components.health.currenthealth <= -furryBedrollHealthChange)) then
				failreason = "health"
			end

			-- you can still sleep if you're hungry, but only if you have the required hunger
			if not canSleepThroughStarvation and inst.components.hunger and ((not furry and bedrollHungerChange < 0 and inst.components.hunger.current < -bedrollHungerChange) or (furry and furryBedrollHungerChange < 0 and inst.components.hunger.current < -furryBedrollHungerChange)) then
				failreason = "ANNOUNCE_NOHUNGERSLEEP"
			end
			
            if failreason ~= nil then
                inst:PushEvent("performaction", { action = inst.bufferedaction })
                inst:ClearBufferedAction()
                inst.sg:GoToState("idle")
                if inst.components.talker ~= nil then
					if failreason == "health" then
						inst.components.talker:Say("No way! I'll die in my sleep!")
					else
						inst.components.talker:Say(_G.GetString(inst, failreason))
					end
                end
                return
            end

            inst.AnimState:PlayAnimation("action_uniqueitem_pre")
            inst.AnimState:PushAnimation("bedroll", false)

            SetSleeperSleepState(inst)
        end,

        timeline =
        {
            _G.TimeEvent(20 * _G.FRAMES, function(inst) 
                inst.SoundEmitter:PlaySound("dontstarve/wilson/use_bedroll")
            end),
        },

        events =
        {
            _G.EventHandler("firedamage", function(inst)
                if inst.sg:HasStateTag("sleeping") then
                    inst.sg.statemem.iswaking = true
                    inst.sg:GoToState("wakeup")
                end
            end),
            _G.EventHandler("animqueueover", function(inst)
                if inst.AnimState:AnimDone() then
                    if _G.TheWorld.state.isday and not inst.canSleepAtDay or
                        (inst.components.health ~= nil and inst.components.health.takingfiredamage) or
                        (inst.components.burnable ~= nil and inst.components.burnable:IsBurning()) then
                        inst:PushEvent("performaction", { action = inst.bufferedaction })
                        inst:ClearBufferedAction()
                        inst.sg.statemem.iswaking = true
                        inst.sg:GoToState("wakeup")
                    elseif inst:GetBufferedAction() then
                        inst:PerformBufferedAction() 
                        if inst.components.playercontroller ~= nil then
                            inst.components.playercontroller:Enable(true)
                        end
                        inst.sg:AddStateTag("sleeping")
                        inst.sg:AddStateTag("silentmorph")
                        inst.sg:RemoveStateTag("nomorph")
                        inst.sg:RemoveStateTag("busy")
                        inst.AnimState:PlayAnimation("bedroll_sleep_loop", true)
                    else
                        inst.sg.statemem.iswaking = true
                        inst.sg:GoToState("wakeup")
                    end
                end
            end),
        },

        onexit = function(inst)
			inst.canSleepAtDay = false
            if inst.sleepingbag ~= nil then
                --Interrupted while we are "sleeping"
                inst.sleepingbag.components.sleepingbag:DoWakeUp(true)
                inst.sleepingbag = nil
				SetSleeperAwakeState(inst)
            elseif not inst.sg.statemem.iswaking then
                --Interrupted before we are "sleeping"
				SetSleeperAwakeState(inst)
            end
        end,
	})
	return inst
end

if _G.TheNet and _G.TheNet:GetIsServer() then
	AddPlayerPostInit(ChangeBedrollAndTentStateDST)
end

I wouldn't still put this in modmain though to make only one specific character nocturnal, right? I'm sorry if that's a dumb question, very new.

Yes, you would, because you just need to copy the original state, and add some check to make specifically your character prefab be able to sleep. I also believe that there are other wakeup functions for your character which will need to have their checks altered, but you'll figure out which and when they are fired once you've made the state work. Note, though, that my example code is for changing an entirely different state than you are trying to change. You'll have to study the state you are trying to change.

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