Jump to content

Mood component doesn't work properly with endless seasons (plus some other issues)


hoxi
  • Pending

Either, the component needs to be refactored to simply not use season duration when season mood is active, and simply listen to the season changing to change this, or, keep what I think might've been intended here (similar to other features), which is to have things during endless seasons use a length of 20 days as reference, as a way to not be too repetitive and allow some cycles, but address some of its behavior.

 

For starters, the component doesn't save its self.seasonmood variable. This means that even when season mood does end in an endless mood season, it'll simply reenable itself the moment you restart the server.

function Mood:OnSave()
    local multiplier = self.isinmood and self.worldsettingsmultiplier_inmood or self.worldsettingsmultiplier_outmood
    return {inmood = self.isinmood, daysleft = self.daystomoodchange ~= 0 and self.daystomoodchange / multiplier or 0, moodseasons = self.moodseasons, version = 2}
end

function Mood:OnLoad(data)
	self.moodseasons = data.moodseasons or self.moodseasons
    self.isinmood = not data.inmood
    local active = false
    local season = TheWorld.state.season
    if self.moodseasons then
	    for i, s in pairs(self.moodseasons) do
	        if season and s == season then
	            active = true
	            break
	        end
	    end
	end
    self:SetIsInMood(data.inmood, active)
    if not data.version then
        local max = self.isinmood and self.worldsettingsmultiplier_inmood or self.worldsettingsmultiplier_outmood
        self.daystomoodchange = math.min(data.daysleft, max)
    elseif data.version == 2 then
        local multiplier = self.isinmood and self.worldsettingsmultiplier_inmood or self.worldsettingsmultiplier_outmood
        self.daystomoodchange = RoundBiasedUp(data.daysleft * multiplier)
    end
end

Honestly, I'm not even sure why self.moodseasons is saved, given that it's set on the prefab constructor, and changing it on the fly could only lead to issues as it's not dynamically checked until a restart, season change, or manual function call is made, so it doesn't seem like something you'd need or want to save. But you would want to save self.seasonmood and use it there for the SetIsInMood call.

Another very important bit is that the daysleft save data value needs to outright save the amount of days without the multiplier division if self.seasonmood is true, otherwise you could end up shortening seasonal mood if settings (for Beefalo Heat frequency) are changed between server restarts, both in endless seasons and in normal seasons.

 

At the bottom of the report, there is an edited version of the save and load functions with the things pointed out here and some stuff pointed out below, as well as more.

 

There's also these bits:

function Mood:CheckForMoodChange()
    if self.daystomoodchange <= 0 then
        --self:SetIsInMood(not self:IsInMood() or self.forcemood)
        self:SetIsInMood(not self:IsInMood() or self.forcemood, self.seasonmood)
    end
end

function Mood:ResetMood()
    if self.seasonmood then
        local wasinmood = self.isinmood
        self.seasonmood = false
        self.isinmood = false
        self.daystomoodchange = self.moodtimeindays.wait
        --if self.onleavemood then
        if wasinmood and self.onleavemood then -- don't call onleavemood if mood wasn't active
            self.onleavemood(self.inst)
        end
    end
end

CheckForMoodChange currently overrides if season mood is active, which is relevant for endless mood seasons. This gets in the way of allowing an endless mood season to allow cycling back and forth between being in heat or not (which again, I think that was the original intention).

The ResetMood bit is just a small change to be cleaner.

 

Lastly for the component, this function does need some changes:

function Mood:SetIsInMood(inmood, entireseason)
    if inmood and not (self.enabled and self.worldsettingsenabled) then
        return
    end

    if self.isinmood ~= inmood or entireseason then

        self.isinmood = inmood
        if self.isinmood then
            if entireseason then
                self.seasonmood = true
                self.daystomoodchange = GetSeasonLength() or self.moodtimeindays.length
            else
                self.seasonmood = false
                self.daystomoodchange = self.moodtimeindays.length
            end
            if self.onentermood then
                self.onentermood(self.inst)
            end
        else
            if not entireseason then
                self.seasonmood = false
                self.daystomoodchange = self.moodtimeindays.wait
            end
            if self.onleavemood then
                self.onleavemood(self.inst)
            end
        end
    end
end

To something like this maybe:

function Mood:SetIsInMood(inmood, entireseason)
    if inmood and not (self.enabled and self.worldsettingsenabled) then
        return
    end

    -- you can do this more properly, just doing it like this for reference
    -- you should set self.seasonmood = false in the component constructor as well
    entireseason = entireseason or false
    inmood = inmood or false

    if self.isinmood ~= inmood or self.seasonmood ~= entireseason then
        local mood_updated = self.isinmood ~= inmood
        self.isinmood = inmood
        self.seasonmood = entireseason

        if self.isinmood then
            self.daystomoodchange = self.seasonmood and GetSeasonLength() or self.moodtimeindays.length

            if mood_updated and self.onentermood then
                self.onentermood(self.inst)
            end
        else
            self.daystomoodchange = self.seasonmood and GetSeasonLength() or self.moodtimeindays.wait

            if mood_updated and self.onleavemood then
                self.onleavemood(self.inst)
            end
        end
    end
end

The general idea is to prevent the entireseason parameter from always going through and producing changes, including calling the onentermood and onleavemood functions, as well as allowing a seasonal wait time to be set as well, for endless mood seasons.

 

Lastly for all this, there's one last thing to tackle outside of the mood component, in prefabs/beefaloherd.lua:

local function OnInit(inst)
    inst.components.mood:ValidateMood()
end

-- in "beefaloherd" prefab constructor
inst:DoTaskInTime(0, OnInit)

This is a problem because it can override loaded save data, both currently, and it still would with the changes I suggested here. This has no actual effects outside of an endless season, but it can still end up overriding things, and during an endless mood season, it straight up resets the whole process, where mood gets forced enabled and duration is set to 20 days.

 

 

You might want to make this task get scheduled through the component instead (it doesn't make much sense for it to be done externally when it seems like something that would be relevant for anything using the mood component, instead of specifically the beefalo herd), and cancel it when the component loads save data.

 

After all, if it's there specifically for this reason:

-- Use this to set the mood correctly (used for making sure the beefalo are mating when the start season is spring)

It shouldn't be going through when loading in.

 

You could do something as simple as this to get the same result:

-- in component constructor
local Mood = Class(function(self, inst)
    -- rest of the function

    self.startingseasontask = self.inst:DoTaskInTime(0, function(inst)
        self.startingseasontask = nil

        local mood = inst.components.mood

        if mood ~= nil then
            mood:ValidateMood()
        end
    end)
end

And cancel it through the OnLoad function.

Below is how the modified save and load functions look like with all the corrections and changes mentioned through the report:

function Mood:OnSave()
    local multiplier = self.isinmood and self.worldsettingsmultiplier_inmood or self.worldsettingsmultiplier_outmood
    local data = {
        inmood = self.isinmood,
        --moodseasons = self.moodseasons, -- don't want to save this
        seasonmood = self.seasonmood,
        daysleft = self.seasonmood and self.daystomoodchange or self.daystomoodchange ~= 0 and self.daystomoodchange / multiplier or 0,
        version = 2,
    }
    return data
end

function Mood:OnLoad(data)
    -- we don't want to run the init task after loading save data or we'll override most of it
    if self.startingseasontask ~= nil then
        self.startingseasontask:Cancel()
        self.startingseasontask = nil
    end
    --self.moodseasons = data.moodseasons or self.moodseasons
    self.isinmood = not data.inmood
    self.seasonmood = not data.seasonmood
    self:SetIsInMood(not not data.inmood, not not data.seasonmood)
    if not data.version then
        local max = self.isinmood and self.worldsettingsmultiplier_inmood or self.worldsettingsmultiplier_outmood
        self.daystomoodchange = math.min(data.daysleft, max)
    elseif data.version == 2 then
        -- don't apply a multiplier for season mood
        if self.seasonmood then
            self.daystomoodchange = data.daysleft
        else
            local multiplier = self.isinmood and self.worldsettingsmultiplier_inmood or self.worldsettingsmultiplier_outmood
            self.daystomoodchange = RoundBiasedUp(data.daysleft * multiplier)
        end
    end
end

 

 

I'll update the report if I find more issues, but this should be enough to make this work more consistently in endless seasons.

I have verified that all of this works with all of the changes. Cycles are saved properly during endless mood seasons, and otherwise the usual gameplay.


Steps to Reproduce
  • Start a world with endless spring.
  • Find a Beefalo herd.
  • Skip/wait until spring starts.
  • Then skip/wait a few days.
  • Print how many days are left for the Beefalo herd to change mood.
  • Now restart the server, and print that again (can always use the GetDebugString function).
  • Notice how the day counter has reset.
  • Now skip enough days for mood to change, then print the same value again, and keep in mind that seasonal mood should now be disabled.
  • Restart the server.
  • Notice how seasonal mood is enabled again and the duration has reset.
  • Like 1
  • Big Ups 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...