Crop Diseases can't spread!

I was wondering why crop diseases spreading from one field to the next didn’t seem all that important in practice, and I think I’ve worked out why. It simply doesn’t happen at all. There are, I believe, two bugs (at least) in the code that’s supposed to make this happen. I can’t hard confirm it without dev tools, but looking into the runtime memory and running some experiments seems to confirm it.

At the end of the farming year (aka, a few days into winter, when any unharvested crops are removed), the following method gets called on the CropDiseaseTrait of each Cropfield.

private void SpreadDiseases()
{
    // ...
    foreach (CropDisease cropDisease in this.diseases)
    {
        float rand = Mathf.Clamp01(UnityEngine.Random.Range(0f, 1f) * this.GetCropDiseaseChanceMult());
        if (rand > 0f && cropDisease.chanceToSpread <= rand)
        {
            foreach (Cropfield cropfield in base.resourceManager.cropFieldsRO)
            {
                if (cropfield != this.field && Vector3.Distance(this.field.transform.position, cropfield.transform.position) <= cropDisease.spreadDistance)
                {
                    // ...
                    ICropfieldScheduledItem currentItem = cropfield.cropScheduler.currentScheduledItem;
                    if (currentItem != null && currentItem is CropfieldPlantingScheduledItem)
                    {
                        if ((currentItem as CropfieldPlantingScheduledItem).recordName == cropDisease.recordName)
                        {
                            cropfield.Infect(cropDisease.recordName);
                            break;
                        }
                        break;
                    }
                }
            }
        }
    }
}

(where I’ve tidied up and removed the unimportant parts of the decompiled code).

There are two things that prevent cropfield.Infect from being called. Firstly, because this method is called in winter cropScheduler.currentScheduledItem is always null (because there’s no farming happening at that time). Secondly, it’s comparing the recordName of a CropfieldPlantingScheduledItem with that of a CropDisease. But those are very different. The former is something like PeaField while the latter are of the form CropDisease_StemRot, so that last equallity check will also fail. I’m guessing it’s meant to check that the crop being grown is susceptible to the disease being spread, but it’s not doing that.

I tried manually calling this method over and over again on an infected field, and the disease never spread, which seems to confirm my suspicions. This is all about diseases spreading however, disease do spontaneously appear on crops when they sown as presumably intended.


I’d also add that, even if the name check is solved and some cropScheduler.nextScheduledItem is implemented, having diseases spreading between fields over winter would be problematic. Firstly, it means that spreading only matters to crops at the start of the year and would discourage having offset crop rotations and actually encourage having the same crops growing at the same time in different fields. Secondly, it would be very exploitable in an incredibly tedious way: manually deleting crop rotations over winter so there’s no target for spreading, before recreating them in spring. IMO, having diseases spread when crops mature (just before rolling for new diseases) would make a lot more sense to me. Or, even simpler, increase the odds of rolling a new disease for every field in range where it is already present.

3 Likes

First off, decompiling (or reverse compiling) the game code, I am sure, is a violation of the terms of agreement for your game license.

Second, in the game, at the bottom of the crop UI you see two results and each has pulldown menus but the one on the bottom right is by default set to current, which remains the same throughout winter until a new crop season begins. So your argument that current is always null in winter is unverifiable from the code you have shown and I believe that current remains unchanged due to this statistics readout being unchanged.

Lastly, without seeing the definition of these classes, it looks to me like the recordName property of a cropDisease is maybe not the name of the disease, but the name of the crop it affects. Or at least, in this code maybe the property that is meant to be used is whatever property lists the kind of crop to affect. I do miss programming and I love C# for its interfaces like ICropfieldScheduledItem because it can mean any object having this interface can be treated very much like an object receiving inheritance in the traditional sense. We cannot see here what class cropDisease is or its interfaces.

I hope your findings do at least help the devs to compare the code using those additional data bits we don’t have here and find if there is a problem. In my current game, my crops are nowhere close to one another so there is no risk of infection. The fertile land is scarce and I’ve had to drop down one field on each of these small green zones.

First off, considering that the devs told us to do this to make mods, literally released a Mono build of the game to make this easier, and I have ongoing PM conversations with the devs regarding other bugs I’ve found in the code, I highly doubt that this is against any terms of agreement. If it is, they know where to contact me.

The rest of your reasoning make sense, and is what I assumed first time I skimmed through that method, but it isn’t true. I literally checked the memory while the game was running and saw that cropfield.cropScheduler.currentScheduledItem was null during winter. Similarly with the incompatible values recordName for diseases and crop items (not that your suggestion would even work - most diseases infect more than one crop). I’m not in the habit of declaring “I am sure” without double checking first. I didn’t post all the code here for brevity, but check it out yourself if you want - instructions are in the modding sections of this forum.

I admit I have very little knowledge of game modding as I choose to play games unmodded for a couple of reasons. Namely, to ensure all bug reports are of vanilla builds and to play the game as was intended by the devs.

However, with that said, I looked into the Guide for starting a Mono mod (0.9.7) and it appears to me you are only creating library files (.dll) that add on to the game using the inheritance from MelonMod and this guide says nothing about allowing you to reverse compile any of the base game files. Most EULAs I read strictly forbid this step but I am having difficulty at this time finding the FF EULA so I will drop this dead horse and let the flies feast… for now. I do not need to keep harping on this. Just once is enough to escalate my concern to the moderators who will in turn take action where action is due.

I guess it’s cute that you’re playing hall monitor and want to make sure that an EULA you can’t find and may or may not ban actions that you don’t understand is obeyed… but it’s entirely misguided. There’s no need to make 3 posts about it (especially when you yourself say “once is enough”).

As I said before, there’s nothing to escalate to the moderators, I’ve talked repeatedly to the devs on this forum, in PMs, and on discord about what I’ve found in the code. They know I’m looking at it. They haven’t censored me for it, on the contrary, they’ve thanked me. Secondly, the very act of adding Assembly-CSharp.dll as a reference to a VS project, 100% necessary in order to compile a .dll mod and upload it to steam using the tools that Crate released, de facto involves decompiling the .dll (not that decompiling is really the right term for a JIT .NET library anyway), at least within the IDE. Now if someone was reverse engineering the executable or similar, that might be crossing the other side of the line. Thirdly modding the game without looking at any of the source code would be obnoxiously difficult; like inviting someone to redecorate your house, but then blindfolding them and tying their hands behind their back. In order to not handicap modders this way, Crate went out of its way to release a Mono version of the game so that we could access, read, and modify the code more easily.

1 Like

You can’t find one because it doesn’t exist. We’re not a megacorp with online services.

Also WE are the ones who released this extractable version of the game to modders, and we knew what that would entail.

3 Likes