Jump to content

Recommended Posts

Hello @DarkXero,

Thanks for your generous help this summer. I have learnt a lot from you. You're just like my mentor of modding. I can't thank you enough!

Since I have asked you so many questions, would you mind if I ask you one more question?

I have been thinking about the world regrowth for DST these days. How to make the world renewable. There is a mod "renewable world" do a good job. But it has some shortages. For example, the beefalo can reproduce themselves. With this mod, if a beefalo is killed, the world will spawn a new beefalo, which makes the world flood with beefalos.

I think the mod base on the current amount of a specific prefab. Once it is removed, another will spawn in the world.

I want to make a mod base on the mod, which you make for me before, Respawnable Rocks.

I want set a limited amount of a specific prefab once the game launchs. The limited amount is the amount of the prefab when game launchs. The component will check the current amount of the prefab periodically and if the current amonut < the limited amount, the world will spawn the prefab.

Is that possible? Can you make a sample for me, such as beefalo and berrybush regrowth.

Link to comment
https://forums.kleientertainment.com/forums/topic/57673-world-regrowth-modding/
Share on other sites

@Jupiters:

local c_countprefabs = GLOBAL.c_countprefabslocal GROUND = GLOBAL.GROUNDlocal SpawnPrefab = GLOBAL.SpawnPrefablocal Vector3 = GLOBAL.Vector3local function TrySpawnBeefalo()	local pt = Vector3(math.random(-1000, 1000), 0, math.random(-1000, 1000))	local tile = GLOBAL.TheWorld.Map:GetTileAtPoint(pt.x, pt.y, pt.z)	local canspawn = tile ~= GROUND.IMPASSABLE and tile ~= GROUND.INVALID and tile ~= 255	canspawn = canspawn and (tile == GROUND.SAVANNA)	if canspawn then		local b = SpawnPrefab("beefalo")		b.Transform:SetPosition(pt:Get())	else		TrySpawnBeefalo()	endendlocal function TrySpawnBerry()	local pt = Vector3(math.random(-1000, 1000), 0, math.random(-1000, 1000))	local tile = GLOBAL.TheWorld.Map:GetTileAtPoint(pt.x, pt.y, pt.z)	local canspawn = tile ~= GROUND.IMPASSABLE and tile ~= GROUND.INVALID and tile ~= 255	canspawn = canspawn and (tile == GROUND.GRASS or tile == GROUND.FOREST or tile == GROUND.DECIDUOUS)	if canspawn then		local b = SpawnPrefab("berrybush")		b.Transform:SetPosition(pt:Get())	else		TrySpawnBerry()	endendlocal min_prefabs = {	beefalo = { 10, TrySpawnBeefalo },	berrybush = { 40, TrySpawnBerry },}local function updateWorld()	for k, v in pairs(min_prefabs) do		local numtospawn = v[1] - c_countprefabs(k, true)		if numtospawn > 0 then			for i = 1, numtospawn, 1 do				v[2]()			end		end	endendAddPrefabPostInit("world", function(inst)	if inst.ismastersim then		inst:ListenForEvent("ms_cyclecomplete", updateWorld)	endend)
 

local min_prefabs = {     beefalo = { 10, TrySpawnBeefalo },     berrybush = { 40, TrySpawnBerry }, }

 

 

Thank you! This is exactly what I want except that I want the amount of a specific prefab to be based on the amount of the prefab the first time the mod is enabled, not a number that I set it to be.

 

 

inst:ListenForEvent("ms_cyclecomplete", updateWorld)

 

"ms_cyclecomplete" is a cycle of a day. What the event name of season change and a year cycle?

 

If the mod is done, is it a server only mod or a all clients required mod?

 

By the way, there are so many "stack" related mod to make stuff stack to 999 or more but they have a common problem. When stuffs stack more that 256, the amount display goes wrong. Why is that? Is this a technical problem that can't be solved by mods? In DS, stuff can be stacked to 999 with mod enabled.

what I want except that I want the amount of a specific prefab to be based on the amount of the prefab the first time the mod is enabled, not a number that I set it to be.

 

You want the world to load. Then the mod to load. Then to count all beefalo in world (e.g 40), then have this number be the minimum?

 

What the event name of season change

 

The event is "seasontick". But that one contains more than just the season.

local function onSeasonChange(inst, season)	print("Season is now", season)endinst:WatchWorldState("season", onSeasonChange)

Is what you want.

 

and a year cycle?

 

No such thing as a year exists.

I suppose you can view it as

local year_length = TUNING.AUTUMN_LENGTH + TUNING.WINTER_LENGTH + TUNING.SPRING_LENGTH + TUNING.SUMMER_LENGTHlocal year = TheWorld.state.cycles % year_length
If the mod is done, is it a server only mod or a all clients required mod?

 

Server only.

 

By the way, there are so many "stack" related mod to make stuff stack to 999 or more but they have a common problem. When stuffs stack more that 256, the amount display goes wrong. Why is that? Is this a technical problem that can't be solved by mods? In DS, stuff can be stacked to 999 with mod enabled.

 

This is thanks to stackable_replica screwing with the netvars because the size isn't enough.

We have to override its constructor to replace the net variables.

GLOBAL.TUNING.STACK_SIZE_LARGEITEM = 999local r_s = GLOBAL.require("components/stackable_replica")r_s._ctor = function(self, inst)	self.inst = inst	self._stacksize = GLOBAL.net_shortint(inst.GUID, "stackable._stacksize", "stacksizedirty")	self._maxsize = GLOBAL.net_tinybyte(inst.GUID, "stackable._maxsize")end

Now you can stack 999 boards.

 

If you put this in the mod, it will be client required though.

@Jupiters, use

AddPrefabPostInit("world", function(inst)	if inst.ismastersim then		inst:AddComponent("minspawner")	endend)

with this

local function TrySpawnBeefalo()	local pt = Vector3(math.random(-1000, 1000), 0, math.random(-1000, 1000))	local tile = GLOBAL.TheWorld.Map:GetTileAtPoint(pt.x, pt.y, pt.z)	local canspawn = tile ~= GROUND.IMPASSABLE and tile ~= GROUND.INVALID and tile ~= 255	canspawn = canspawn and (tile == GROUND.SAVANNA)	if canspawn then		local b = SpawnPrefab("beefalo")		b.Transform:SetPosition(pt:Get())	else		TrySpawnBeefalo()	endend local function TrySpawnBerry()	local pt = Vector3(math.random(-1000, 1000), 0, math.random(-1000, 1000))	local tile = GLOBAL.TheWorld.Map:GetTileAtPoint(pt.x, pt.y, pt.z)	local canspawn = tile ~= GROUND.IMPASSABLE and tile ~= GROUND.INVALID and tile ~= 255	canspawn = canspawn and (tile == GROUND.GRASS or tile == GROUND.FOREST or tile == GROUND.DECIDUOUS)	if canspawn then		local b = SpawnPrefab("berrybush")		b.Transform:SetPosition(pt:Get())	else		TrySpawnBerry()	endendlocal MinSpawner = Class(function(self, inst)	self.inst = inst	self.mins = {		beefalo = nil,		berrybush = nil,	}	self.func = {		beefalo = TrySpawnBeefalo,		berrybush = TrySpawnBerry,	}	self.needmins = true	inst:ListenForEvent("ms_cyclecomplete", function() self:updateWorld() end)end)function MinSpawner:updateWorld()	if self.needmins == true then		self.mins.beefalo = c_countprefabs("beefalo", true)		self.mins.berrybush = c_countprefabs("berrybush", true)		self.needmins = false	end    for k, v in pairs(self.mins) do        local numtospawn = self.mins[k] - c_countprefabs(k, true)        if numtospawn > 0 then            for i = 1, numtospawn, 1 do                self.func[k]()            end        end    endendfunction MinSpawner:OnSave()	local data = {}	for k, v in pairs(self.mins) do		data[k] = v	end	data.needmins = self.needmins	return dataendfunction MinSpawner:OnLoad(data)	if data then		for k, v in pairs(data) do			self.mins[k] = v		end		self.needmins = data.needmins	endendreturn MinSpawner

Minimums are needed at the start, then when they are set up, the needmins gets set to false and will load false.

@DarkXero

 

Thanks for fast reply. :-)

Something goes wrong. :(

 

[00:02:12]: [string "../mods/World Regrowth/scripts/components/m..."]:51: attempt to perform arithmetic on field '?' (a boolean value)
LUA ERROR stack traceback:
    ../mods/World Regrowth/scripts/components/minspawner.lua:51 in (method) updateWorld (Lua) <44-58>
    ../mods/World Regrowth/scripts/components/minspawner.lua:41 in (local) fn (Lua) <41-41>
    scripts/entityscript.lua:941 in (method) PushEvent (Lua) <935-958>
    scripts/components/clock.lua:225 in (method) OnUpdate (Lua) <193-281>
    scripts/update.lua:183 in () ? (Lua) <146-219>

World Regrowth.zip

@DarkXero

 

I try to make it without component like this. And it works. But the code is too awkward. Would you please help me make it more flexible or do it your way with component? I don't which is the better way make the mod. :(

World Regrowth - 1.zip

@DarkXero

 

WOW, how amazing! You are just like a magician. Thank you!!!

 

I know c_countprefabs() is used for count the number of the prefab existing the world, no matter whether it's in the container or in the players' inventory. How to count the prefab that is not in the container or inventory? For instance, the flint. I want to count the flints that is on the ground. Is is possible?

@Jupiters, use a function like:

function c_countprefabsonground(prefab, noprint)	local count = 0	for k, v in pairs(Ents) do		local owner = v.components.inventoryitem and v.components.inventoryitem.owner		if owner == nil then			if v.prefab == prefab then				count = count + 1			end		end	end	if not noprint then		print("There are ", count, prefab.."s in the world's ground.")	end	return countend
check that the player is on ground, cave or ruins.
GetWorld().IsCave()GetWorld().IsRuins()
a console command that can print the tile type under cursor?
-- Print the (visual) tile under the cursorfunction c_tile()	local s = ""	local ground = GetWorld()	local mx, my, mz = TheInput:GetWorldPosition():Get()	local tx, ty = ground.Map:GetTileCoordsAtPoint(mx,my,mz)	s = s..string.format("world[%f,%f,%f] tile[%d,%d] ", mx,my,mz, tx,ty)	local tile = ground.Map:GetTileAtPoint(TheInput:GetWorldPosition():Get())	for k,v in pairs(GROUND) do		if v == tile then			s = s..string.format("ground[%s] ", k)			break		end	end	print(s)end

@DarkXero

 

Thank you very much~ :)

So I should write this in modmain.lua:

 

        AddPrefabPostInit("forest", function(inst)
            inst:AddComponent("minspawner_forest")
            end)
 
        AddPrefabPostInit("cave", function(inst)
            inst:AddComponent("minspawner_cave")
            end)        
 
And in the component mindpawner_cave, I use GetWorld().IsCave() and GetWorld().IsRuins() to check whether the player is on cave or ruins. Am I right?

@Jupiters, no, it's "daycomplete".

 

Thanks, get it. :-)

 

I have an annoying on my dedicated server: beefalo biome always flood with beefalos. How to stop their reproduction where the number of beefalo is up to 30 or 40 or other numbers?

Edited by Jupiters

@Jupiters, hmmm, countprefabs would be expensive for this particular task.

 

This should be less expensive:

local worldbeefalonum = 0local function prefabEditsNum(inst)	if not GLOBAL.TheWorld.ismastersim then return end	worldbeefalonum = worldbeefalonum + 1	inst:ListenForEvent("onremove", function() worldbeefalonum = worldbeefalonum - 1 end)endAddPrefabPostInit("beefalo", prefabEditsNum)AddPrefabPostInit("babybeefalo", prefabEditsNum)AddPrefabPostInit("beefaloherd", function(inst)	if not GLOBAL.TheWorld.ismastersim then return end	local _spawntest = inst.components.periodicspawner.spawntest	local function mytest(inst)		if worldbeefalonum > GLOBAL.TheWorld.components.minspawner.minprefs.beefalo then			return false		end		return _spawntest(inst)	end	inst.components.periodicspawner:SetSpawnTestFn(mytest)end)

Used with the minspawner component to take advantage of the minimum.

@Jupiters, hmmm, countprefabs would be expensive for this particular task.

This should be less expensive:

local worldbeefalonum = 0local function prefabEditsNum(inst)	if not GLOBAL.TheWorld.ismastersim then return end	worldbeefalonum = worldbeefalonum + 1	inst:ListenForEvent("onremove", function() worldbeefalonum = worldbeefalonum - 1 end)endAddPrefabPostInit("beefalo", prefabEditsNum)AddPrefabPostInit("babybeefalo", prefabEditsNum)AddPrefabPostInit("beefaloherd", function(inst)	if not GLOBAL.TheWorld.ismastersim then return end	local _spawntest = inst.components.periodicspawner.spawntest	local function mytest(inst)		if worldbeefalonum > GLOBAL.TheWorld.components.minspawner.minprefs.beefalo then			return false		end		return _spawntest(inst)	end	inst.components.periodicspawner:SetSpawnTestFn(mytest)end)
Used with the minspawner component to take advantage of the minimum.

Thank you!

So if the initial number of beefalo is 10 and there are 10 beefalos or more in the world now, the beefalo will not reproduce. And if there are 9 beefalos or less, the beefalo will reproduce themselves?

So if the initial number of beefalo is 10 and there are 10 beefalos or more in the world now, the beefalo will not reproduce. And if there are 9 beefalos or less, the beefalo will reproduce themselves?

 

Correct, any beefalo herd prefab will have the spawn test return false if the beefalo world number is higher than the minimum from the minspawner component.

 

prefabEditsNum makes beefalo and babybeefalo edit the counter up when they initialize, and down when they get removed.

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