Unfortunately, and please don't take this as an insult, there is a gross misunderstanding of Factorio's modding/Lua Environment.
In simple terms, there are no persistent variables in the global scope, they're lost when the game is saved/loaded (and are defined only when the scenario/control file is ran - which it is, every game load)
A quick patch to make this work, without setting it up the "right" way:
Instead of mainSurface =, it should be written, e.g.:
local function spawnFish()
local mainSurface = game.surfaces.nauvis
...
local function spawnGarden()
local mainSurface = game.surfaces.nauvis
```
local function spawnTree()
local mainSurface = game.surfaces.nauvis
The original code only defines the variable mainSurface when spawnFish executes. If spawnFish was not the first function executed after save/load, one will get mainSurface is nil errors.
The longer, better way is during on_init, assign that surface you want to global (the special table that's saved and loaded scenario/mod control), as something like global.mainSurface. Then it will always be present across save/load.
I can offer some help in Factorio's #mod-making discord, because when you borrow Freeplay, you also borrowed the vanilla event handler, which is more complicated than it needs to be (I can give you a very simple event handler file - otherwise Factorio will overwrite other script.on_<init/reconfig>, or script.on_event(identity, function-to-call) lines, and not do what is desired)