Jump to content

Nightmare Light & Fissure numerical bugs


Harryyw
  • Fixed

Shouldn't TUNING.NIGHTMARELIGHT_REGEN_TIME be the value of regen period? And release time be the value of spawn period.

image.png.e5898510376c907224cecf728ffeeed9.png

Same bug is in nightmarelight.lua


Steps to Reproduce

take a closer look at the code snippet

  • Like 2



User Feedback


A developer has marked this issue as fixed. This means that the issue has been addressed in the current development build and will likely be in the next update.

Huh nice catch, kinda crazy this has gone unnoticed for this long, especially given that:

WorldSettings_ChildSpawner_SpawnPeriod(inst, TUNING.NIGHTMARELIGHT_RELEASE_TIME, TUNING.NIGHTMAREFISSURE_ENABLED)
WorldSettings_ChildSpawner_RegenPeriod(inst, TUNING.NIGHTMARELIGHT_REGEN_TIME, TUNING.NIGHTMAREFISSURE_ENABLED)

Is right below the lines you're showing in the report, and they're set correctly here.

 

Despite the childspawner timers being disabled in place of the worldsettingstimer component handling them externally, they do get used for setting the timers automatically, as you can see in worldsettingsutil.lua:

-- childspawner:GetTimeToNextSpawn()

-- childspawner:GetTimeToNextRegen()

 

 

Also, on a similar note, regarding nightmarelight.lua:

inst.components.childspawner:SetMaxChildren(math.random(TUNING.NIGHTMARELIGHT_MINCHILDREN, TUNING.NIGHTMARELIGHT_MAXCHILDREN))

It should be saving its amount of max children, as it otherwise gets rerolled on load every time. And this is something that's been addressed with other spawners recently.

Along with "nightmarelight", here's a list of prefabs that also seem to randomize their max amount of children without saving it:

  • dropperweb
  • houndmound
  • monkeybarrel
  • oceanvine_cocoon
  • pond
  • pond_mos
  • slurtlehole
  • spiderhole

EDIT: (for some of these, that use world settings, it might make some sense to be this way to account changing the settings and launching the server again, but still good to keep in mind).

Edited by hoxi
Added a clarification to the last bit
  • Like 1
  • Big Ups 1

Share this comment


Link to comment
Share on other sites

Changed Status to Fixed

The values are correct. (Regen period of 5, and Spawn Period of 30). But the labels were mismatched, and incorrect for the world setting functions.

  • Like 2

Share this comment


Link to comment
Share on other sites

1 hour ago, omar_klei said:

Changed Status to Fixed

The values are correct. (Regen period of 5, and Spawn Period of 30). But the labels were mismatched, and incorrect for the world setting functions.

Oh huh! Yeah, those values do match the (hardcoded) values in Don't Starve, it was a probably a mistake from when making the values be defined in TUNING instead of just there in the prefab constructor.

I guess the world settings timers were indeed incorrect all this time. The childspawner timer values do still get used (while the ones set on the worldsettingstimer component are used as "maximum time" for saving and loading).

 

 

Looking into this did make notice one potential oversight with worldsettingstimer. When setting maximum time, timer variance from childspawner isn't accounted for, which for all cases that use worldsettingtimers, it defaults to spawn/regen period * 0.1. Small, but should probably be accounted for to prevent saving and loading from slightly modifying saved timers.

 

There's also another issue, of modifying these spawn/regen timers due to external conditions (i.e. spring season for Bee Hives and Bee Boxes), not always modifying the max time in worldsettingstimer (and thus, also, an ongoing timer). This is accounted for with Merm Houses and is manually done, but it should probably be automatically done when setting a spawn or regen period in childspawner..?

 

This.. will be a bit long due to the code citations and explanations..

-- NOTE: all of the things explained below also apply to regen timers
-- some of these issues might also apply to spawner, not just childspawner


-- variance is rarely ever passed here
-- and currently it's not passed at all for things that use worldsettingstimer
function ChildSpawner:SetSpawnPeriod(period, variance)
    self.spawnperiod = period
    self.spawnvariance = variance or period * 0.1
    self.timetonextspawn = self:GetTimeToNextSpawn()
end

-- variance is added on top of the spawn period, and can only be its full value or less, randomly
-- therefore, the "maximum" time for a spawn timer should be spawn period + variance, not just spawn period
function ChildSpawner:GetTimeToNextSpawn()
    return self.spawnperiod + (math.random()*2-1)*self.spawnvariance
end


-- spawnperiod here should probably also include its own variance
-- NOTE: you could simply check for childspawner.spawnvariance here
    -- as it happens that childspawner always sets both spawn and regen periods before the worldsettingstimer
    -- just make a note to clarify it
function WorldSettings_ChildSpawner_SpawnPeriod(inst, spawnperiod, enabled)
    local childspawner = inst.components.childspawner
    local worldsettingstimer = inst.components.worldsettingstimer
    if not worldsettingstimer then
        inst:AddComponent("worldsettingstimer")
        worldsettingstimer = inst.components.worldsettingstimer
    end

    assert(childspawner, "WorldSettings_ChildSpawner_SpawnPeriod was called without a childspawner component")

    childspawner.useexternaltimer = true
    childspawner.spawntimerstart = Start_ChildSpawner_SpawnPeriod_Timer
    childspawner.spawntimerstop = Stop_ChildSpawner_SpawnPeriod_Timer
    childspawner.spawntimerset = Set_ChildSpawner_SpawnPeriod_Timer_Time

    -- HERE, note how spawnperiod isn't actually used for when starting the timer
    worldsettingstimer:AddTimer(CHILDSPAWNER_SPAWNPERIOD_TIMERNAME, spawnperiod, enabled, On_ChildSpawner_SpawnPeriod_TimerFinished)
    worldsettingstimer:StartTimer(CHILDSPAWNER_SPAWNPERIOD_TIMERNAME, childspawner:GetTimeToNextSpawn(), not (childspawner.spawning and not childspawner.queued_spawn and childspawner.childreninside > 0))
end


-- maxtime is spawnperiod from the function above
function WorldSettingsTimer:AddTimer(name, maxtime, enabled, callback, externallongupdate)
	if not (name and maxtime) then
		print(string.format("Invalid WorldSettingsTimer: name=%s time=%s", tostring(name), tostring(maxtime)))
		return
	elseif self:TimerExists(name) then
        return
    end

    self.timers[name] = {
        maxtime = maxtime,
        enabled = enabled,
        callback = callback,
        externallongupdate = externallongupdate
    }

    local saved = self.saved_timers[name]
    if saved then
        self:StartTimer(name, saved.timeleft * maxtime, saved.paused, saved.initial_time, saved.blocklongupdate)
        self.saved_timers[name] = nil
    end
end

-- note how it gets used when saving how much time there's left
function WorldSettingsTimer:OnSave()
    local data = {}
    for k, v in pairs(self.timers) do
        if self:ActiveTimerExists(k) then
            data[k] =
            {
				timeleft = v.maxtime ~= 0 and self:GetTimeLeft(k) / v.maxtime or 0,
                paused = v.paused,
                blocklongupdate = v.blocklongupdate,
                initial_time = v.initial_time,
            }
        end
    end
	return next(data) and { timers = data }
end

-- and then used here to recalculate the time left, in case the world setting for it was changed
function WorldSettingsTimer:OnLoad(data)
    if data.timers ~= nil then
        for name, v in pairs(data.timers) do
            if self:TimerExists(name) then
                self:StopTimer(name)
                self:StartTimer(name, v.timeleft * self:GetMaxTime(name), v.paused, v.initial_time, v.blocklongupdate)
            else
                self.saved_timers[name] = v
            end
        end
    end
end


-- this function would also need to have variance accounted for..
-- either through the function itself or from what's sent as spawnperiod_max and regenperiod_max
function WorldSettings_ChildSpawner_PreLoad(inst, data, spawnperiod_max, regenperiod_max)
    if data and data.childspawner and not data.worldsettingstimer then
        data.worldsettingstimer = { timers = {} }
        data.worldsettingstimer.timers[CHILDSPAWNER_SPAWNPERIOD_TIMERNAME] = {
            timeleft = math.min(data.childspawner.timetonextspawn, spawnperiod_max) / spawnperiod_max,
            paused = data.childspawner.spawning, -- is this intentional..?
            initial_time = 0,
        }
        data.worldsettingstimer.timers[CHILDSPAWNER_REGENPERIOD_TIMERNAME] = {
            timeleft = math.min(data.childspawner.timetonextregen, regenperiod_max) / regenperiod_max,
            paused = data.childspawner.regening, -- is this intentional..?
            initial_time = 0,
        }
    end
end


-- from mermhouse.lua, UpdateSpawningTime
local mult = Remap(topcount, 0, MAX_COUNT, 1, TUNING.WURT_MAX_OFFERING_REGEN_MULT)

timer:SetMaxTime("ChildSpawner_RegenPeriod", TUNING.MERMHOUSE_REGEN_TIME / 2 * mult)
spawner:SetRegenPeriod(TUNING.MERMHOUSE_REGEN_TIME / 2 * mult)


-- rather than manually doing the thing above
-- there should probably be a function set from worldsettingsutil.lua similar to these
childspawner.spawntimerstart = Start_ChildSpawner_SpawnPeriod_Timer
childspawner.spawntimerstop = Stop_ChildSpawner_SpawnPeriod_Timer
childspawner.spawntimerset = Set_ChildSpawner_SpawnPeriod_Timer_Time
childspawner.regentimerstart = Start_ChildSpawner_RegenPeriod_Timer
childspawner.regentimerstop = Stop_ChildSpawner_RegenPeriod_Timer

-- that if set, can be called from SetSpawnPeriod and SetRegenPeriod in childspawner, to also set the max time
	-- (using period + variance of course)
-- would also be cleaner, as the timer names are all consistents through locals in worldsettingsutil.lua

 

  • Sanity 1

Share this comment


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

×
  • Create New...