Jump to content

Poison Birchnut Tree (deciduousmonster) worldgen settings do not have the intended effect due to old/inconsistent variable names in worldsettings_overrides.lua


oblivioncth
  • Pending

I was curious as to how exactly the worldgen settings for Poison Birchnut trees affected their spawn rate so I inspected the game's scripts and was surprised to find that the options clearly don't work as intended. I apologize for the long post, but sadly it doesn't seem that you can make code blocks collapsible like spoilers.

Short Version:

The values "never" (None) and "default" (Default) are the only two that function as one would expect, changing the first day upon which Poison Birches can spawn, as well as their spawn chance, while "rare" (Little), "often" (More), and "always" (Tons) only affect the minimum day and not the spawn rate, due to what is most likely a mistake with left over variable names.

Fix:

worldsettings_overrides.lua

DECID_MONSTER_SPAWN_CHANCE_BASE -> DECID_MONSTER_SPAWN_CHANCE_AUTUMN

DECID_MONSTER_SPAWN_CHANCE_LOW -> DECID_MONSTER_SPAWN_CHANCE_SPRING

DECID_MONSTER_SPAWN_CHANCE_MED -> DECID_MONSTER_SPAWN_CHANCE_SUMMER

Long Version:
In tuning.lua we can see the default values that the game uses for handling Posion Birchnut occurrences:

-- [ tuning.lua - 2496:2503 ]
-- Birchnut monster chances have been reduced and tied to seasons instead of the number of days to balance things out for dedicated servers (which may be running for extremely long times)
DECID_MONSTER_MIN_DAY = 3, -- No monsters during the first few days
DECID_MONSTER_SPAWN_CHANCE_AUTUMN = .15,    -- high chance of monsters in autumn to cancel out double birchnut and general easyness of autumn
DECID_MONSTER_SPAWN_CHANCE_SPRING = .08, -- next highest in spring because everything attacks in spring
DECID_MONSTER_SPAWN_CHANCE_SUMMER = .033, -- low chance in summer since summer is hard anyway
DECID_MONSTER_SPAWN_CHANCE_WINTER = 0, -- can't make monsters in winter, they have to have leaves
DECID_MONSTER_DAY_THRESHOLDS = { 20, 35, 70 }, -- Ramp monster spawns a bit over time
DECID_MONSTER_SPAWN_CHANCE_MOD = { .2, .5, 1, 1.12 },

Then, in \prefabs\deciduoustrees.lua we can see how these values are used when the player chops a Birchnut tree:

-- [ deciduoustrees.lua - 509:536 ]
local function chop_down_tree(inst, chopper)
    local days_survived = chopper.components.age ~= nil and chopper.components.age:GetAgeInDays() or TheWorld.state.cycles
    if not inst.monster and inst.leaf_state ~= "barren" and inst.components.growable ~= nil and inst.components.growable.stage == 3 and days_survived >= TUNING.DECID_MONSTER_MIN_DAY then
        --print("Chance of making a monster")
        --winter should always be 0 (because barren trees can't become monsters), but is included in tuning values for consistency
        local chance =
            (TheWorld.state.isautumn and TUNING.DECID_MONSTER_SPAWN_CHANCE_AUTUMN) or
            (TheWorld.state.isspring and TUNING.DECID_MONSTER_SPAWN_CHANCE_SPRING) or
            (TheWorld.state.issummer and TUNING.DECID_MONSTER_SPAWN_CHANCE_SUMMER) or
            (TheWorld.state.iswinter and TUNING.DECID_MONSTER_SPAWN_CHANCE_WINTER) or
            0

        local chance_mod = TUNING.DECID_MONSTER_SPAWN_CHANCE_MOD[1]
        for i, v in ipairs(TUNING.DECID_MONSTER_DAY_THRESHOLDS) do
            if days_survived < v then
                break
            end
            chance_mod = TUNING.DECID_MONSTER_SPAWN_CHANCE_MOD[i + 1]
        end
        if chopper:HasTag("beaver") then
            chance_mod = chance_mod * TUNING.BEAVER_DECID_MONSTER_CHANCE_MOD
        elseif chopper:HasTag("woodcutter") then
            chance_mod = chance_mod * TUNING.WOODCUTTER_DECID_MONSTER_CHANCE_MOD
        end

        --print("Chance is ", chance * chance_mod, TheWorld.state.season)
        if math.random() < chance * chance_mod then
            --print("Trying to spawn monster")
-- ... (remainder of function)

In English, the game determines the days survived by the player (falling back on the world age) and ensures that the minimum days determined by DECID_MONSTER_MIN_DAY have passed. Then, it gets the spawn rate based on the current season using the conjunction and disjunction LUA operators, determines the chance modifier by getting a multiplier from within the DECID_MONSTER_SPAWN_CHANCE_MOD array based on days_survived (itself selected using the day thresholds in DECID_MONSTER_DAY_THRESHOLDS), and lastly alters that modifier further if the player is Woodie. Finally, the base chance is multiplied by the modifier to get the final true chance and RNG is used to "roll a die" and if the result is within the odds then a Posion Birchnut is spawned.

Note that these tuning values (as well as the two exceptions for Woodie) are the only ones that have bearing on their spawn rate.

Looking at the options for the Poison Birchnut spawn rate parameter, known as "deciduousmonster", we see it can have the following values in worldgenoverride.lua/leveldataoverride.lua:

  1. never (In-game: None)
  2. rare (In-game: Little)
  3. default (In-game: Default)
  4. often (In-game: More)
  5. always (In-game: Tons)

The game loads the static values from tuning.lua into a table (TUNING), which are then modified by worldsettings_overrides.lua according to the player's settings before actually being used. We can look there to see how these spawn rate parameters affect the above function. Note, I have omitted the function OverrideTuningVariables() at the top of the file, but just know that it's what actually handles swapping the custom values into the TUNING table:

-- [ worldsettings_overrides.lua - 166:203 ]
deciduousmonster = function(difficulty)
  local tuning_vars =
  {
    never = {
      DECID_MONSTER_MIN_DAY = NEVER_TIME,
      DECID_MONSTER_SPAWN_CHANCE_AUTUMN = 0,
      DECID_MONSTER_SPAWN_CHANCE_SPRING = 0,
      DECID_MONSTER_SPAWN_CHANCE_SUMMER = 0,
    },
    rare = {
      DECID_MONSTER_MIN_DAY = 5,
      DECID_MONSTER_SPAWN_CHANCE_BASE = 0.075,
      DECID_MONSTER_SPAWN_CHANCE_LOW = 0.04,
      DECID_MONSTER_SPAWN_CHANCE_MED = 0.0165,
    },
    --[[
    default = {
    DECID_MONSTER_MIN_DAY = 3,
    DECID_MONSTER_SPAWN_CHANCE_AUTUMN = 0.15,
    DECID_MONSTER_SPAWN_CHANCE_SPRING = 0.08,
    DECID_MONSTER_SPAWN_CHANCE_SUMMER = 0.033,
  },
    --]]
    often = {
      DECID_MONSTER_MIN_DAY = 2,
      DECID_MONSTER_SPAWN_CHANCE_BASE = 0.3,
      DECID_MONSTER_SPAWN_CHANCE_LOW = 0.16,
      DECID_MONSTER_SPAWN_CHANCE_MED = 0.066,
    },
    always = {
      DECID_MONSTER_MIN_DAY = 1,
      DECID_MONSTER_SPAWN_CHANCE_BASE = 0.6,
      DECID_MONSTER_SPAWN_CHANCE_LOW = 0.32,
      DECID_MONSTER_SPAWN_CHANCE_MED = 0.132,
    },
  }
  OverrideTuningVariables(tuning_vars[difficulty])
end,

At first, looking at the value "never", things make sense. The minimum day is set to a macro that equates to day 9999999999 and the spawn chance for each season (except winter since its always zero) is set to zero. The same is true for the commented out default (which is just there as a reminder of the defaults and commented so that the values in tuning.lua are always used and Klei doesn't have to remember to update the defaults in two different places), but the moment we check any of the other values it immediately becomes apparent that something is wrong.

The use of the variables DECID_MONSTER_SPAWN_CHANCE_BASEDECID_MONSTER_SPAWN_CHANCE_LOW, and DECID_MONSTER_SPAWN_CHANCE_MED is completely new here and these are not values present in the TUNING table (tuning.lua), and as we know from earlier these variables are not used at all within the Poison Birch spawning function (deciduoustrees.lua). In fact, with help of something like Notepad++'s handy "Find in Files" function one can easily see that these names are not referenced even once anywhere else in the entirety of the games scripts. 

Essentially what this means is that only the minimum day portion of those values have any effect and the function is effectively:

deciduousmonster = function(difficulty)
  local tuning_vars =
  {
    never = {
      DECID_MONSTER_MIN_DAY = NEVER_TIME,
      DECID_MONSTER_SPAWN_CHANCE_AUTUMN = 0,
      DECID_MONSTER_SPAWN_CHANCE_SPRING = 0,
      DECID_MONSTER_SPAWN_CHANCE_SUMMER = 0,
    },
    rare = {
      DECID_MONSTER_MIN_DAY = 5,
    },
    --[[
    default = {
    DECID_MONSTER_MIN_DAY = 3,
    DECID_MONSTER_SPAWN_CHANCE_AUTUMN = 0.15,
    DECID_MONSTER_SPAWN_CHANCE_SPRING = 0.08,
    DECID_MONSTER_SPAWN_CHANCE_SUMMER = 0.033,
  },
    --]]
    often = {
      DECID_MONSTER_MIN_DAY = 2,
    },
    always = {
      DECID_MONSTER_MIN_DAY = 1,
    },
  }
  OverrideTuningVariables(tuning_vars[difficulty])
end,

where the default spawn rate values are used when "rare", "often", or "always" is selected.

I think it's reasonable to say that this wasn't intended and that the smoking gun is this comment in tuning.lua:

Quote

-- Birchnut monster chances have been reduced and tied to seasons instead of the number of days to balance things out for dedicated servers (which may be running for extremely long times)

I think that for whatever reason when changing over to the new system/tuning values for spawning Poison Birchnut Trees, someone at Klei in their haste forgot to change the variables names for "rare", "often", and "always" from the old "BASE", "LOW" and "MED" implementation to the new season based one, resulting in those variables being dynamically added to the TUNING table (and left unused), instead of the intended "..._AUTMUM", "..._SPRING", and "..._SUMMER" variables being modified.

It's technically possible that these other values are referred to directly by the engine, but given the fact that the handling for a Birch tree being chopped is fully fleshed out in deciduoustrees.lua, this would be a bit spaghetti and is highly unlikely.

Additionally, if you look at the actual numbers used with the old variable names, it's clear that they are reasonable decreases/increases to the season based defaults. Therefore, with all the above being considered, I think the correct/originally intended function is:

deciduousmonster = function(difficulty)
  local tuning_vars =
  {
    never = {
      DECID_MONSTER_MIN_DAY = NEVER_TIME,
      DECID_MONSTER_SPAWN_CHANCE_AUTUMN = 0,
      DECID_MONSTER_SPAWN_CHANCE_SPRING = 0,
      DECID_MONSTER_SPAWN_CHANCE_SUMMER = 0,
    },
    rare = {
      DECID_MONSTER_MIN_DAY = 5,
      DECID_MONSTER_SPAWN_CHANCE_AUTUMN = 0.075, -- Updated
      DECID_MONSTER_SPAWN_CHANCE_SPRING = 0.04, -- Updated
      DECID_MONSTER_SPAWN_CHANCE_SUMMER = 0.0165, -- Updated
    },
    --[[
    default = {
    DECID_MONSTER_MIN_DAY = 3,
    DECID_MONSTER_SPAWN_CHANCE_AUTUMN = 0.15,
    DECID_MONSTER_SPAWN_CHANCE_SPRING = 0.08,
    DECID_MONSTER_SPAWN_CHANCE_SUMMER = 0.033,
  },
    --]]
    often = {
      DECID_MONSTER_MIN_DAY = 2,
      DECID_MONSTER_SPAWN_CHANCE_AUTUMN = 0.3, -- Updated
      DECID_MONSTER_SPAWN_CHANCE_SPRING = 0.16, -- Updated
      DECID_MONSTER_SPAWN_CHANCE_SUMMER = 0.066, -- Updated
    },
    always = {
      DECID_MONSTER_MIN_DAY = 1,
      DECID_MONSTER_SPAWN_CHANCE_AUTUMN = 0.6, -- Updated
      DECID_MONSTER_SPAWN_CHANCE_SPRING = 0.32, -- Updated
      DECID_MONSTER_SPAWN_CHANCE_SUMMER = 0.132, -- Updated
    },
  }
  OverrideTuningVariables(tuning_vars[difficulty])
end,

I know that this is a relatively minor issue compared to others, but I'd appreciate it if this could be corrected.


Steps to Reproduce

Read the above code review, or create a world with deciduousmonster set to "rare", "often", or "always" and notice that the Poison Birches don't seem to occur any more or less.

  • Like 1



User Feedback


There are no comments to display.



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