Sign in to follow this  
zazabar

Mod Write Up: Multiharvest Farms

Recommended Posts

zazabar    8

EDIT: Replaced code with updated code. If conversations after this don't make sense, that is why.

Hey dere fellow modders! 

I wrote a new mod recently and I decided to do a write up on it to help others who are still new to modding (like myself) try to navigate through the system. The mod in question isn't too complicated.  It adds two pieces of functionality. The first is that it allows 1-5 harvests of a crop from a single seed. What I mean by this is after you harvest the crop, instead of disappearing, it returns to growth level 0 and starts anew until X harvests have been reached. The second bit of functionality is the ability to use the shovel to remove said crop if you don't like what crop is growing for you.  So let's get into it.

The start: modinfo.lua --- This is going to be where we add the configuration options for the mod to access. There are already guides on how to write the general documentation, so I'm just going to talk about the configuration options themselves.

configuration_options =
{
	{
		name = "YourVariableName",
		description = "Your Variable Description",
		default = 4,	--A default option out of the list to use. This is referring to the "data" variable and not the "description" variable.
		options = {
			{
				description = "1",
				data = 1,
			},
			{
				description = "2",
				data = 2,
			},
						{
				description = "3",
				data = 3,
			},
						{
				description = "4",
				data = 4,
			},
						{
				description = "5",
				data = 5,
			},
		},
	},
}

Adding additional options is as simple as adding an additional {}, after the one before the last.

In the case of my mod, the entire file looks like this:

name = "Multiple Harvest Farms"
description = "Allows you to get multiple harvests from a single seed. After picking your crop, the plant will return to 0 growth and start anew until it hits the set number of rotations."
author = "zazabar"
version = "1.0"

forumthread = ""

api_version = 10
priority = 1

--This lets the clients know that they need to download the mod before they can join a server that is using it.
all_clients_require_mod = true

--This let's the game know that this mod doesn't need to be listed in the server's mod listing
client_only_mod = false

--Let the mod system know that this mod is functional with Don't Starve Together
dont_starve_compatible = false
reign_of_giants_compatible = false
dst_compatible = true

--These tags allow the server running this mod to be found with filters from the server listing screen
server_filter_tags = {"farm","crop","plant"}

icon_atlas = "preview.xml"
icon = "preview.tex"

configuration_options =
{
	{
		name = "NumCropRotations",
		description = "Number of Crop Rotations",
		default = 4,	
		options = {
			{
				description = "1",
				data = 1,
			},
			{
				description = "2",
				data = 2,
			},
						{
				description = "3",
				data = 3,
			},
						{
				description = "4",
				data = 4,
			},
						{
				description = "5",
				data = 5,
			},
		},
	},
}

In order for your component to get the configuration data from the mod, you'll need to use the GetModConfigData" function, which will pull it based on the name you gave it in the modinfo.lua file.

Now for the meat: modmain.lua ---

local require = GLOBAL.require
GLOBAL.TUNING.NUMCROPROTATIONS = GetModConfigData("NumCropRotations") 

local function shoveled(inst, worker)
	inst.components.crop.matured = false
	inst.components.crop.growthpercent = 0
	inst.components.crop.product_prefab = nil
	if inst.components.crop.grower ~= nil and inst.components.crop.grower:IsValid() and inst.components.crop.grower.components.grower ~= nil then
		inst.components.crop.grower.components.grower:RemoveCrop(inst)
	end
	inst.components.crop.grower = nil
end

local function AddWorkable(grower, plant)
	if grower.components.inspectable.nameoverride == "FARMPLOT" then
		plant:AddComponent("workable")
		plant.components.workable:SetWorkAction(GLOBAL.ACTIONS.DIG)
		plant.components.workable:SetWorkLeft(1)
		plant.components.workable:SetOnFinishCallback(shoveled)
	end
end

local function RemoveWorkable(grower, plant)
	if grower.components.inspectable.nameoverride == "FARMPLOT" then
		plant.inst:RemoveComponent("workable")
	end
end

AddComponentPostInit("grower", function(self)
	local oldPlantItem = self.PlantItem
	function self:PlantItem(item)
		local functionBool = false
		if self.inst.components.inspectable.nameoverride == "FARMPLOT" and item.components.plantable ~= nil then
			functionBool = oldPlantItem(self, item)
			self.curRotations = 0
			return functionBool
		else
			return oldPlantItem(self, item)
		end
	end
	
	local oldOnSave = self.OnSave
	function self:OnSave()
		local mainData = oldOnSave(self)
		if self.inst.components.inspectable.nameoverride == "FARMPLOT" and self.curRotations ~= nil then
			mainData.cropTimerCurRotations = self.curRotations
		else
			mainData.cropTimerCurRotations = 0
		end
		return mainData
	end
	
	local oldOnLoad = self.OnLoad
	function self:OnLoad(data, newents)
		oldOnLoad(self, data, newents)
		if self.inst.components.inspectable.nameoverride == "FARMPLOT" then
			self.curRotations = data.cropTimerCurRotations
		end
	end
end)

AddComponentPostInit("crop", function(self)
	local oldStartGrowing = self.StartGrowing
	function self:StartGrowing(prod, grow_time, grower, percent)
		oldStartGrowing(self, prod, grow_time, grower, percent)
		AddWorkable(grower, self.inst)
	end
	
	local oldHarvest = self.Harvest
	function self:Harvest(harvester)
		if self.grower.components.inspectable.nameoverride ~= "FARMPLOT" then
			return oldHarvest(self, harvester)
		end
		
		local tempItemProduct = self.product_prefab
		local tempCurRotations = self.grower.components.grower.curRotations + 1
		local tempGrower = self.grower.components.grower
		local tempBoolean, tempPostProduct = oldHarvest(self, harvester)
		
		if tempCurRotations < GLOBAL.TUNING.NUMCROPROTATIONS then
			local tempItemPrefab = GLOBAL.SpawnPrefab("seeds")
			tempItemPrefab.components.plantable.product = tempItemProduct
			tempGrower.PlantItem(tempGrower, tempItemPrefab)
			tempGrower.curRotations = tempCurRotations
		end

		return tempBoolean, tempPostProduct
	end
	
	local oldResume = self.Resume
	function self:Resume()
		oldResume(self)
		AddWorkable(self.inst.components.crop.grower, self.inst)
	end
end)

There's a lot here so let's step through it.

First, we need global data so we do a local require which specifies the GLOBAL data. Next, we assign our variable to the GLOBAL.TUNING area so that it can be modified by outside processes if necessary and so we don't have to make multiple calls to find it.

Function shoveled - This function is what decides what happens to our crop when we use the shovel on it. It's required to have a callback function whenever you have something workable.

Functions AddWorkable and RemoveWorkable - These two functions are what we use to add and remove the workable status from our crop. We want to make sure that we don't apply this to non-farm crops, so we do a quick check to make sure they are a part of that.

Grower PostInit Function PlantItem - Once again, we want to make sure this only applies to farms. If it doesn't, just return the old function. Whenever possible we want to use the old functions for maximum compatibility, so we run the old function then add our extra variable before returning.

Grower PostInit Function OnSave and OnLoad - We want to keep our data for the farms when we close the game or reopen it, so we need to have these two functions. Once again, call old functions whenever possible and then add our data before returning. Make sure the main format of the data doesn't change or the game might crash. We have the addition of the workable part in the crop portion later.

Crop PostInit Function StartGrowing - Very simple, call the old function then add our workable. 

Crop PostInit Function Harvest - This function was slightly trickier. We want to run the old function, but when we do so, we inadvertently clear out all of the information about the crop from the grower. So to get around this, we need to get a couple of variables first then run the original function. After, we generate a new seed with the same product to throw back into the farm. Since the PlantItem function cleans up the seed prefab we generated, we don't have to worry about doing anything with it.

Crop PostInit Function Resume - This function just adds our workable function to the crop. The reason it is here and not in the grower function is because it's actually troublesome to pull crops directly from the farmplot. 

-----------

And that's everything. I hope it helps someone!

Edited by zazabar
Posting current code.

Share this post


Link to post
Share on other sites
Aquaterion    821

a few things if you don't mind me to point out:

You don't need a new AddComponentPostInit for each function you're changing, you can do them all in 1:

AddComponentPostInit("grower", function(self)
	function self:Function1()
	end
	
	function self:Function2()
	end
end)

You declared variables with the old functions, but you didn't actually use them I'll take OnLoad as an example here:

Your code:

local old = self.OnLoad
function self:OnLoad(data, newents)
	if data.crops ~= nil then
		for k, v in pairs(data.crops) do
			self.isempty = false
			self.inst:AddTag("NOCLICK")
			local child = GLOBAL.SpawnSaveRecord(v, newents)
			if child ~= nil then
				child.components.crop.grower = self.inst
				child.Transform:SetPosition(v.x or 0, v.y or 0, v.z or 0)
				child.persists = false
				self.crops[child] = true
				AddWorkable(self, child)
				child.components.crop:Resume()
			end
		end
	end

	self.cycles_left = data.cycles_left or self.cycles_left

	if self.setfertility ~= nil then
		self.setfertility(self.inst, self:GetFertilePercent())
	end
	
	if data.cropTimerNumRotations ~= nil then
		self.inst:AddComponent("croptimer")
		self.inst.components.croptimer.numRotations = data.cropTimerNumRotations
		self.inst.components.croptimer.curRotations = data.cropTimerCurRotations
	end
end

And what you would ideally do:

local old = self.OnLoad
function self:OnLoad(data, newents)
	old(data, newents)--RUNS THE OLD OnLoad that we named 'old', so we don't have to rewrite it
	--and then runs our new code;
	if data.cropTimerNumRotations ~= nil then
		self.inst:AddComponent("croptimer")
		self.inst.components.croptimer.numRotations = data.cropTimerNumRotations
		self.inst.components.croptimer.curRotations = data.cropTimerCurRotations
	end
end

Of course this is situational, as it depends when you want to run your new code, if its before or after the existing code. If it was in the middle, then yea you'd probably have to overwrite the whole function.

Share this post


Link to post
Share on other sites
SrJardel    146

Thank you guys.
Content like this + reading through the game code + "Programming Lua" book+ patience is what help a clueless Lua user like me to achieve most of my ideas, even with the lack of decent internet connection on my end to execute real time research while trying to code.

Thanks again.

Share this post


Link to post
Share on other sites
Serpens    551

I like the idea :) I use a mod which gives you 1-3 harvests at once when harvesting.
But making every single harvest need to grow again, is a nice alternative :)

Share this post


Link to post
Share on other sites
zazabar    8
50 minutes ago, Aquaterion said:

a few things if you don't mind me to point out:

You don't need a new AddComponentPostInit for each function you're changing, you can do them all in 1:


AddComponentPostInit("grower", function(self)
	function self:Function1()
	end
	
	function self:Function2()
	end
end)

You declared variables with the old functions, but you didn't actually use them I'll take OnLoad as an example here:

Your code:


local old = self.OnLoad
function self:OnLoad(data, newents)
	if data.crops ~= nil then
		for k, v in pairs(data.crops) do
			self.isempty = false
			self.inst:AddTag("NOCLICK")
			local child = GLOBAL.SpawnSaveRecord(v, newents)
			if child ~= nil then
				child.components.crop.grower = self.inst
				child.Transform:SetPosition(v.x or 0, v.y or 0, v.z or 0)
				child.persists = false
				self.crops[child] = true
				AddWorkable(self, child)
				child.components.crop:Resume()
			end
		end
	end

	self.cycles_left = data.cycles_left or self.cycles_left

	if self.setfertility ~= nil then
		self.setfertility(self.inst, self:GetFertilePercent())
	end
	
	if data.cropTimerNumRotations ~= nil then
		self.inst:AddComponent("croptimer")
		self.inst.components.croptimer.numRotations = data.cropTimerNumRotations
		self.inst.components.croptimer.curRotations = data.cropTimerCurRotations
	end
end

And what you would ideally do:


local old = self.OnLoad
function self:OnLoad(data, newents)
	old(data, newents)--RUNS THE OLD OnLoad that we named 'old', so we don't have to rewrite it
	--and then runs our new code;
	if data.cropTimerNumRotations ~= nil then
		self.inst:AddComponent("croptimer")
		self.inst.components.croptimer.numRotations = data.cropTimerNumRotations
		self.inst.components.croptimer.curRotations = data.cropTimerCurRotations
	end
end

Of course this is situational, as it depends when you want to run your new code, if its before or after the existing code. If it was in the middle, then yea you'd probably have to overwrite the whole function.

Thanks for the information! Wasn't aware you could do multiple declarations in one PostInit.  That is definitely good to know. For the functions, yeah, in this case I couldn't just run them before or after. The code had to be inserted in specific points so I had to copy over the old code. I added the local old declarations at the start and forgot to remove them later.

Share this post


Link to post
Share on other sites
Kzisor    1058

A few pointers from a veteran modder.

4 hours ago, zazabar said:

And modifying the base classes is not a preferred method.

This is inaccurate; more accurately "it's frowned upon to override the original files with modified files in your mod". By overriding the files you are creating a scenario where if the original files are updated by Klei then your files will be out of date which means they will most likely not work. It's preferred to use the API to modify any and all entities, components, etc. before trying to make your own component. What you've done could have easily been done in the grower component without the need to add a secondary component.

Additionally, it's better to add your configuration settings to the GLOBAL.TUNING table so you don't need to use the "KnownModIndex:GetModActualName" function. This way you have a centralized location in order to get the data, rather than calling a process intensive function such as the KnownModIndex functions. Inside your own modmain.lua file you do not need to call those expensive functions because you have direct access to the configuration settings.

A few things to consider:

  • Adding new components for basic functionality adds a lot of additional overhead which isn't needed. If you are modifying something already in game, do it through one of the already available components.
  • When working with configurations, always put them in the GLOBAL.TUNING table. This prevents additional processing cycles wasted to call a function which isn't required during game-play.
  • As Aquaterion pointed out, ALWAYS call previously used functions when override functions. This way if another mod alters the component your mod won't break theirs if it loads last. This is non-negotiable, you MUST do this if you're using the API. If you don't know how to make your mod work by doing it, rework your code.

Hopefully this didn't come across offensively; it's simply we don't need more broken mods cluttering the workshop.

 

Share this post


Link to post
Share on other sites
Aquaterion    821
29 minutes ago, Kzisor said:
  • As Aquaterion pointed out, ALWAYS call previously used functions when override functions. This way if another mod alters the component your mod won't break theirs if it loads last. This is non-negotiable, you MUST do this if you're using the API. If you don't know how to make your mod work by doing it, rework your code.

 

How would you go on about following the api when you want to change some code in a function which is in the middle of the function?

I'll use the fishable's HookFish function as an example again;

function Fishable:HookFish()
    local fishprefab = GetRandomKey(self.fish)
    local fish = SpawnPrefab(fishprefab)
    if fish ~= nil then
        self.hookedfish[fish] = fish
        self.inst:AddChild(fish)
        fish.entity:Hide()
        fish.persists = false
        if fish.DynamicShadow ~= nil then
            fish.DynamicShadow:Enable(false)
        end
        if fish.Physics ~= nil then
            fish.Physics:SetActive(false)
        end
        if self.fishleft > 0 then
            self.fishleft = self.fishleft - 1
        end
    end
    return fish
end

Say I want to change the fishprefab variable, how would i go about it without actually redoing the whole function?

Share this post


Link to post
Share on other sites
Kzisor    1058
2 minutes ago, Aquaterion said:

How would you go on about following the api when you want to change some code in a function which is in the middle of the function?

I'll use the fishable's HookFish function as an example again;


function Fishable:HookFish()
    local fishprefab = GetRandomKey(self.fish)
    local fish = SpawnPrefab(fishprefab)
    if fish ~= nil then
        self.hookedfish[fish] = fish
        self.inst:AddChild(fish)
        fish.entity:Hide()
        fish.persists = false
        if fish.DynamicShadow ~= nil then
            fish.DynamicShadow:Enable(false)
        end
        if fish.Physics ~= nil then
            fish.Physics:SetActive(false)
        end
        if self.fishleft > 0 then
            self.fishleft = self.fishleft - 1
        end
    end
    return fish
end

Say I want to change the fishprefab variable, how would i go about it without actually redoing the whole function?

Modify the self.fish variable.

AddComponentPostInit('fishable', function(inst)
	local _HookFish = inst.HookFish
	function inst:HookFish()
		self.fish = {}
		return _HookFish(self)
	end
end)

 

Edited by Kzisor
Added code...

Share this post


Link to post
Share on other sites
Serpens    551
38 minutes ago, Kzisor said:

When working with configurations, always put them in the GLOBAL.TUNING table. This prevents additional processing cycles wasted to call a function which isn't required during game-play

Should it be always GLOBAL.TUNING? Or would it also be okay to make GLOBAL.MyModVariableXY

Share this post


Link to post
Share on other sites
Kzisor    1058
Just now, Serpens said:

Should it be always GLOBAL.TUNING? Or would it also be okay to make GLOBAL.MyModVariableXY

If it's a 'tuning' variable; it should be put in the GLOBAL.TUNING table; period. That is the central location where all 'settings' variables are placed. Inside the GLOBAL.TUNING table; you can easily add a new table for your specific mod if you wanted to do that.

1 minute ago, Aquaterion said:

 

huh.. could you give an example?

I edited the original reply and added code. Alternatively you could simply modify the prefab which has the 'fishable' component and modify the fishprefab that way.

Share this post


Link to post
Share on other sites
Aquaterion    821
7 minutes ago, Kzisor said:

 edited the original reply and added code. Alternatively you could simply modify the prefab which has the 'fishable' component and modify the fishprefab that way.

I don't think you understood what I exactly wanted, but I did manage to sort of implement your example in a way. Atm I can't test it out but i believe that it should work, so thank you for that knowledge.

 

P.S where dat autocompiler fix tho xD

Share this post


Link to post
Share on other sites
Kzisor    1058
8 minutes ago, Aquaterion said:

I don't think you understood what I exactly wanted, but I did manage to sort of implement your example in a way. Atm I can't test it out but i believe that it should work, so thank you for that knowledge.

I imagine you're wanting to change the fish prefab which people get from things like ponds, etc. This can be achieved in many manners, in fact the one I posted isn't the correct way; it follows suit of what your question asked pertaining to my original comment.

The correct way is the following:

AddPrefabPostInit('pond', function(inst)
	if not GLOBAL.TheWorld.ismastersim then
		return inst
	end

	inst.components.fishable.fish = {}
	inst.components.fishable:SetFish('tropical_fish')

	return inst
end)

AddPrefabPostInit('pond_cave', function(inst)
	if not GLOBAL.TheWorld.ismastersim then
		return inst
	end

	inst.components.fishable.fish = {}
	inst.components.fishable:SetFish('tropical_fish')

	return inst
end)

AddPrefabPostInit('pond_marsh', function(inst)
	if not GLOBAL.TheWorld.ismastersim then
		return inst
	end

	inst.components.fishable.fish = {}
	inst.components.fishable:SetFish('tropical_fish')

	return inst
end)

 

Edited by Kzisor
Fixed code to remove the original fish added to ponds.

Share this post


Link to post
Share on other sites
SrJardel    146
1 hour ago, Kzisor said:
Spoiler

 

A few pointers from a veteran modder...

  • we don't need more broken mods cluttering the workshop.

 

 

 

Yes, please... :D

Share this post


Link to post
Share on other sites
zazabar    8
39 minutes ago, Kzisor said:

I imagine you're wanting to change the fish prefab which people get from things like ponds, etc. This can be achieved in many manners, in fact the one I posted isn't the correct way; it follows suit of what your question asked pertaining to my original comment.

The correct way is the following:


AddPrefabPostInit('pond', function(inst)
	if not GLOBAL.TheWorld.ismastersim then
		return inst
	end

	inst.components.fishable.fish = {}
	inst.components.fishable:SetFish('tropical_fish')

	return inst
end)

AddPrefabPostInit('pond_cave', function(inst)
	if not GLOBAL.TheWorld.ismastersim then
		return inst
	end

	inst.components.fishable.fish = {}
	inst.components.fishable:SetFish('tropical_fish')

	return inst
end)

AddPrefabPostInit('pond_marsh', function(inst)
	if not GLOBAL.TheWorld.ismastersim then
		return inst
	end

	inst.components.fishable.fish = {}
	inst.components.fishable:SetFish('tropical_fish')

	return inst
end)

 

Quick question.  Trying to redo some of the code but I'm running into a weird crash that I'm having trouble solving. 

AddComponentPostInit("grower", function(self)
	local _old = self.PlantItem
	function self:PlantItem(item)
		return _old(item)
	end
end)

This is causing the program to crash, as it states that item is undefined.  This happens when I try to plant a seed. I removed everything else and left just this, and it still crashes.

"Attempt to index local 'item' (a nil value)"

Am I missing an include or something?

Share this post


Link to post
Share on other sites
Kzisor    1058
1 minute ago, zazabar said:

Quick question.  Trying to redo some of the code but I'm running into a weird crash that I'm having trouble solving. 


AddComponentPostInit("grower", function(self)
	local _old = self.PlantItem
	function self:PlantItem(item)
		return _old(item)
	end
end)

This is causing the program to crash, as it states that item is undefined.  This happens when I try to plant a seed. I removed everything else and left just this, and it still crashes.

"Attempt to index local 'item' (a nil value)"

Am I missing an include or something?

You have to push the 'self' variable as the first parameter when calling old functions. Don't worry, you don't need to set it, it is automatically set.

Edited by Kzisor

Share this post


Link to post
Share on other sites
Aquaterion    821
45 minutes ago, Kzisor said:

I imagine you're wanting to change the fish prefab which people get from things like ponds, etc. This can be achieved in many manners, in fact the one I posted isn't the correct way; it follows suit of what your question asked pertaining to my original comment.

The correct way is the following:

 

no no that I know how to do, I basically wanted to choose a fish from self.fish depending on a different way rather than the "GetRandomKey" function. But I tested it and it worked! ^^ again, thank you for this knowledge. I'm sure it will be helpful.

Share this post


Link to post
Share on other sites
zazabar    8
1 hour ago, Kzisor said:

You have to push the 'self' variable as the first parameter when calling old functions. Don't worry, you don't need to set it, it is automatically set.

Thanks!  I'm a developer in the real world, so hearing stuff from other developers is always nice as well when it comes to stuff in other applications I don't know.

Share this post


Link to post
Share on other sites
zazabar    8
4 hours ago, Kzisor said:

 

So I revamped the entire thing based on your feedback.  I got rid of the extra component and included all of the main primary functions without editing them. If you could look over this and see if you can find any other major flaws, it would be much appreciated!

local require = GLOBAL.require
GLOBAL.TUNING.NUMCROPROTATIONS = GetModConfigData("NumCropRotations") 

local function shoveled(inst, worker)
	inst.components.crop.matured = false
	inst.components.crop.growthpercent = 0
	inst.components.crop.product_prefab = nil
	if inst.components.crop.grower ~= nil and inst.components.crop.grower:IsValid() and inst.components.crop.grower.components.grower ~= nil then
		inst.components.crop.grower.components.grower:RemoveCrop(inst)
	end
	inst.components.crop.grower = nil
end

local function AddWorkable(grower, plant)
	if grower.components.inspectable.nameoverride == "FARMPLOT" then
		plant:AddComponent("workable")
		plant.components.workable:SetWorkAction(GLOBAL.ACTIONS.DIG)
		plant.components.workable:SetWorkLeft(1)
		plant.components.workable:SetOnFinishCallback(shoveled)
	end
end

local function RemoveWorkable(grower, plant)
	if grower.components.inspectable.nameoverride == "FARMPLOT" then
		plant.inst:RemoveComponent("workable")
	end
end

AddComponentPostInit("grower", function(self)
	local oldPlantItem = self.PlantItem
	function self:PlantItem(item)
		local functionBool = false
		if self.inst.components.inspectable.nameoverride == "FARMPLOT" and item.components.plantable ~= nil then
			functionBool = oldPlantItem(self, item)
			self.curRotations = 0
			return functionBool
		else
			return oldPlantItem(self, item)
		end
	end
	
	local oldOnSave = self.OnSave
	function self:OnSave()
		local mainData = oldOnSave(self)
		if self.inst.components.inspectable.nameoverride == "FARMPLOT" and self.curRotations ~= nil then
			mainData.cropTimerCurRotations = self.curRotations
		else
			mainData.cropTimerCurRotations = 0
		end
		return mainData
	end
	
	local oldOnLoad = self.OnLoad
	function self:OnLoad(data, newents)
		oldOnLoad(self, data, newents)
		if self.inst.components.inspectable.nameoverride == "FARMPLOT" then
			self.curRotations = data.cropTimerCurRotations
		end
	end
end)

AddComponentPostInit("crop", function(self)
	local oldStartGrowing = self.StartGrowing
	function self:StartGrowing(prod, grow_time, grower, percent)
		oldStartGrowing(self, prod, grow_time, grower, percent)
		if grower.components.inspectable.nameoverride == "FARMPLOT" then
			AddWorkable(grower, self.inst)
		end
	end
	
	local oldHarvest = self.Harvest
	function self:Harvest(harvester)
		if self.grower.components.inspectable.nameoverride ~= "FARMPLOT" then
			return oldHarvest(self, harvester)
		end
		
		local tempItemProduct = self.product_prefab
		local tempCurRotations = self.grower.components.grower.curRotations + 1
		local tempGrower = self.grower.components.grower
		local tempBoolean, tempPostProduct = oldHarvest(self, harvester)
		
		if tempCurRotations < GLOBAL.TUNING.NUMCROPROTATIONS then
			local tempItemPrefab = GLOBAL.SpawnPrefab("seeds")
			tempItemPrefab.components.plantable.product = tempItemProduct
			tempGrower.PlantItem(tempGrower, tempItemPrefab)
			tempGrower.curRotations = tempCurRotations
		end

		return tempBoolean, tempPostProduct
	end
	
	local oldResume = self.Resume
	function self:Resume()
		oldResume(self)
		AddWorkable(self.inst.components.crop.grower, self.inst)
	end
end)

 

Share this post


Link to post
Share on other sites
Kzisor    1058
42 minutes ago, zazabar said:

So I revamped the entire thing based on your feedback.  I got rid of the extra component and included all of the main primary functions without editing them. If you could look over this and see if you can find any other major flaws, it would be much appreciated!

This is a very elegant solution, I would say that this solution is the most flexible that could be programmed.

A few notes about your code so others can learn from it.

  • The code uses the proper API, this means it won't break if Klei changes things. It should only break if they remove the function names.
  • The new functions are calling the old functions so separate mods which modify the same entity/prefabs/components will still work.
  • You're using the GLOBAL.TUNING which means separate mods may change your settings from their mod if they wish to have it set a specific way. This ultimately means we shouldn't see duplicates of your mod on the workshop.

The biggest reason I advocate to use GLOBAL.TUNING for settings is so people who want to have "modifications" for certain mods can do so by creating a separate mod and uploading it. Because GLOBAL.TUNING only runs on the server, their mod can be Hidden or Friends Only which prevents it from being displayed on the workshop; which ultimately reduces clutter on the workshop.

Share this post


Link to post
Share on other sites
SrJardel    146

Since you guys are into it, I assume it would mean no harm to ask a simple question.

How could I get **.AnimState:function() to take effect on an equipped item, or any item in general?

I tried ThePlayer.AnimState and the command took effect on my character, so I assume there is a way to do the same on an item.

I looked at some scripts and it most likely says "inst.AnimState", but I can't figure out what "inst" means, since there is no "inst = something" like other stuff.

Sorry for the messy aproach, I am just curious while I try to learn a bit here and there.

Edited by SrJardel
Typo

Share this post


Link to post
Share on other sites
Kzisor    1058
19 minutes ago, SrJardel said:

Since you guys are into it, I assume it would mean no harm to ask a simple question.

How could I get **.AnimState:function() to take effect on an equipped item, or any item in general?

I tried ThePlayer.AnimState and the command took effect on my character, so I assume there is a way to do the same on an item.

I looked at some scripts and it most likely says "inst.AnimState", but I can't figure out what "inst" means, since there is no "inst = something" like other stuff.

Sorry for the messy aproach, I am just curious while I try to learn a bit here and there.

inst is the instance of the item/entity. It's usually passed through, but is only created in the main function in the prefab file of the item/entity. You should be able to Ctrl + F, inst = CreateEntity() and it will show you where it is located in the file.

Share this post


Link to post
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
Sign in to follow this