Jump to content

Recommended Posts

I'm new to modding dst and without code documentation i've been struggling quite a bit. I wanted to make a character that loses health if it's currently day or dusk and gain health if it's night. I looked around the forums for a bit but couldn't fully find what i was looking for. I've been trying to get this piece of code working for a bit but it doesn't seem to want to work:

local function drainbydaylight(inst)
  local phase = TheWorld.state.phase

  if phase == "day" then 
    inst.components.health:DoDelta(-1)
  end
end
	
inst:DoPeriodicTask(15, drainbydaylight(inst))

I put this in the character's master_postinit like a different post recommended. I was under the impression that DoPeriodicTask executed the function every 15 seconds? The only way i got this to work if instead of putting it in the master_postinit i put the function above it and instead of DoPeriodic i used:

inst:WatchWorldState("phase", drainbydaylight)

Which runs the function but it only executes once every time the game changes to day which is not what i wanted to do. If anyone could explain to me what's not working would be appreciated.

Try this:

inst.components.Health:AddRegenSource(source, amount, period, key)

where:

  • Source: I never really understood what this variable was for, but it is usually used for the object that you want to regenerate life, although I think it does not fulfill a function, since in the end the object that will end up regenerating life is the one that has the component
  • amount: the regeneration amount
  • period: the time between each health increase
  • key: A string ID (important)

To remove it use:

inst.components.health:RemoveRegenSource(source, key)

with the same source and key.

If the solution given by reply above doesn't suit your needs, you could try:

local function drainbydaylight(inst)
    if TheWorld.state.isnight then
        inst.components.health:DoDelta(1)
    else
        inst.components.health:DoDelta(-1)
    end
end

-- On your master_postinit
inst:DoPeriodicTask(15, drainbydaylight)

Note that your character will constantly regenerate health while in the caves since the game considers to be always night in there. If you don't want that happening you'll need to add some checks for that. You can find more information about it in: scripts/components/worldstate.lua

Thanks for the help @Kyno @FerniFrenito!

I was able to make it work with the suggested functions.

local function drainbydaylight(inst)
	local phase = TheWorld.state.phase
	if not TheWorld.ismastersim then
		return inst
	end
	
	if phase == "day" or phase == "dusk" then
		inst.components.health:RemoveRegenSource(inst, "nighttime")
		inst.components.health:AddRegenSource(inst, -1, 3, "daytime")
	elseif phase == "night" then
		inst.components.health:RemoveRegenSource(inst, "daytime")
		inst.components.health:AddRegenSource(inst, 2.5, 3, "nighttime")
	end
end

--master_postinit
inst:WatchWorldState("phase", drainbydaylight)

I know it's not pretty but it works somewhat. i also ran into an issue i remembered that when the player switches characters or spawns for the first time WatchWorldState doesn't trigger because the time of day didn't actually change. I attempted to fix this problem by calling the drainbydaylight function inside of the onload function as such:

-- When loading or spawning the character
local function onload(inst)
    inst:ListenForEvent("ms_respawnedfromghost", onbecamehuman)
    inst:ListenForEvent("ms_becameghost", onbecameghost)

    if inst:HasTag("playerghost") then
        onbecameghost(inst)
    else
        onbecamehuman(inst)
    end
  
	drainbydaylight(inst)
end

But this resulted in when switching to dusk it would attempt to remove the nighttime regenSource but crashes because it doesn't exit is there any way for me to check if the character has that component and only then remove it?

3 hours ago, gamehun20 said:

when switching to dusk it would attempt to remove the nighttime regenSource but crashes because it doesn't exit

Is it crashing? The game shouldn't crash with that. Could you show me the crash message?

25 minutes ago, FerniFrenito said:

Is it crashing? The game shouldn't crash with that. Could you show me the crash message?

Maybe crash was a strong word to use but it still makes the server give a lost connection popup so it's still a problem. This is the error i'm getting:

[00:05:57]: [string "scripts/components/health.lua"]:414: attempt to index field '?' (a nil value)
LUA ERROR stack traceback:
    scripts/components/health.lua:414 in (method) RemoveRegenSource (Lua) <407-438>
    ../mods/wilda the mandrake queen/scripts/prefabs/wilda.lua:31 in (field) ? (Lua) <24-37>
    scripts/components/worldstate.lua:32 in (upvalue) SetVariable (Lua) <24-48>
    scripts/components/worldstate.lua:73 in (local) fn (Lua) <72-80>
    scripts/entityscript.lua:1238 in (method) PushEvent_Internal (Lua) <1225-1254>
    scripts/entityscript.lua:1257 in (method) PushEvent (Lua) <1256-1258>
    scripts/components/clock.lua:396 in (method) OnUpdate (Lua) <322-440>
    scripts/update.lua:262 in () ? (Lua) <224-298>

row 31 is the below code in my previous post

inst.components.health:RemoveRegenSource(inst, "nighttime")

 

Edited by gamehun20

I don't know why some codes in the game hasn't nil validation.

You have two options:

  1. Add regen and remove it instantanly
    if phase == "day" or phase == "dusk" then
      inst.components.health:AddRegenSource(inst, 2.5, 3, "nighttime")
      inst.components.health:RemoveRegenSource(inst, "nighttime")
      
      inst.components.health:AddRegenSource(inst, -1, 3, "daytime")
    elseif phase == "night" then
      inst.components.health:AddRegenSource(inst, -1, 3, "daytime")
      inst.components.health:RemoveRegenSource(inst, "daytime")
      
      inst.components.health:AddRegenSource(inst, 2.5, 3, "nighttime")
    end

    This shouldn't cause any problems because it's not supposed to accumulate.

  2. validate it for yourself
    local src_params = inst.components.health.regensources ~= nil and inst.components.health.regensources[source] or nil
    
    if phase == "day" or phase == "dusk" then
      if src_params ~= nil and src_params.tasks["nighttime"] ~= nil then
        inst.components.health:RemoveRegenSource(inst, "nighttime")
      end
    
      inst.components.health:AddRegenSource(inst, -1, 3, "daytime")
    elseif phase == "night" then
      if src_params ~= nil and src_params.tasks["nighttime"] ~= nil then
        inst.components.health:RemoveRegenSource(inst, "daytime")
      end
    
      inst.components.health:AddRegenSource(inst, 2.5, 3, "nighttime")
    end

    Internally, this is the validation that the code should do, but instead it assumes that src_params.tasks[key] will not be null and so, when it does not exist, it throws a crash.

I would recommend the first option. While it doesn't look very elegant, it keeps your mod stable over time as long as the add and remove functions exist. If they change their internal functionality, your mod would still work perfectly. The downside is that it executes more instructions than necessary.
The second option is good, but there's no guarantee that it will be stable over time, since if you change the name of regensources or something like that, you'd have to go to the new source code and see which variable you need to validate now (in case they haven't fixed the code to do the validation). The upside is that you only execute the instructions you need.

I think I should start reporting these things as bugs since they are things that, by correcting them in the game's source code, do not modify or break anything that is already implemented.

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