Jump to content

Modding help; Biome-sensitive sanity toll/gain possible?


Recommended Posts

Hey! I'm just getting into modding, and rn I'm making a custom character with, ya guessed it, custom perks. But!! I think I know how to tackle all but one; I can't for the life of me figure out if making Biome-sensitive sanity toll/gain is possible? My idea is gain when near the "coast" as in within a certain range of the waterfront, and a toll within the forest biome. Does anybdy knnow if that's possible? To be honest I'm unsure if the game even consistently logs which biome you're in once the map has been generated.

Link to comment
Share on other sites

It depends.

What do you classify as a biome? Just being on a specific turf? Being in a specific area? What if that area has been dug out and replaced with other turf? Is it still the original biome?

Edited by penguin0616
  • Like 2
Link to comment
Share on other sites

Hey! Thanks for the answer! And good point - I think I'm rather flexible with the definition. Ideally it would be the "original" turf if it'd been replaced, but I reckon that the chances the game keeps record of that would be slim to none - can't think of a single pre-existing mechanic it'd be useful for. So, for the sake of pragmaticism let's say the turf itself, player-placed or not. Except if you have a specific definition in mind already? 

Link to comment
Share on other sites

On 3/18/2021 at 7:48 PM, 0ct0duck said:

My idea is gain when near the "coast" as in within a certain range of the waterfront,

and a toll within the forest biome.

The second one sounds a bit like the inverse of Walter's perk (sanity gain near a bunch of trees)

The first one... I'm not too sure. Check scripts/worldtiledefs.lua and see if

is_shoreline = true

is something you're looking for.

  • Thanks 1
Link to comment
Share on other sites

@0ct0duck
I use this code to detect when I'm near shore.  It scans in an 11x11 square around the player (as long as you're in the water), calculating the shortest distance to shore, so long as that distance is bellow 25 units, meaning that the square grid pattern is converted into a circle.

			--Checking water depth.
			print("Testing Water Depth/Distance")
			local x, y, z = inst.Transform:GetWorldPosition()
		--	print("Player's Position: ", x, y, z)

			local map = TheWorld.Map
		--	print("Map Test")
			local tcx, _, tcz = map:GetTileCenterPoint(x , 0, z)  --Tile coordinated that the player is standing on.
		--	print("TCX: ", tcx)
		--	print("TCZ: ", tcz)
			
			local tx, tz = map:GetTileCoordsAtPoint(tcx, 0, tcz)  --Used to get information about tiles, NOT measurements in relation to the player.
		--	print("TX: ", tx)
		--	print("TZ: ", tz)

			local tileid = map:GetTile(tx, tz)
		--	print("Tile ID: ", tileid)

			local tileinfo = GetTileInfo(tileid)
			
		--	print("Current Platform: ", inst:GetCurrentPlatform() )  --Used to check if you're on a boat.

		--	print("Water Type: ", tileinfo.ocean_depth)

		--	if tileinfo.ocean_depth == nil then
		--		print("Player is on land.")
		--	end


			--[ local variable definition --]
			local a = 0
			local b = 0
			local byland = 0
			local shortestdistance = nil
			local waterdebuff = nil

			if tileinfo.ocean_depth ~= nil and inst:GetCurrentPlatform() == nil then  --If you're in the water and there's no boat under you.
				--[ repeat loop execution --]
				repeat
					b = 0
					repeat
					--	print("Value of a:", a)
					--	print("Value of b:", b)

						local tileid = map:GetTile(tx - 5 + b, tz + 5 - a)  --Scans left to right, top to bottom.
					--	print("Location Tested: ", tx - 5 + b, tz + 5 - a)
					--	print("Tile ID: ", tileid)

						local tileinfo = GetTileInfo(tileid)
					--	print("Tile Info: ", tileinfo)

					--	print("Testing Tile Info.")
					--	print("Water Type: ", tileinfo.ocean_depth)
		--[[
						if tileinfo.ocean_depth == "SHALLOW" then
							print("SHALLOW WATER")
						elseif tileinfo.ocean_depth == "NORMAL" then
							print("NORMAL WATER")
						elseif tileinfo.ocean_depth == "DEEP" then
							print("DEEP WATER")
						elseif tileinfo.ocean_depth == "VERY_DEEP" then
							print("VERY_DEEP WATER")
						else
							print("Water's Depth Could Not Be Tested.")
					--		byland = 1
						end
		--]]

						local dist2d = math.sqrt( ((tcx - 20 + (4 * b)) - x)^2 + ((tcz + 20 - (4 * a)) - z)^2 )  --Scans left to right, top to bottom.
					--	print("Tile's distance to player: ", dist2d)

						if tileinfo.is_shoreline == true then
						--	print("Tile is shoreline.")
							byland = 1

							if shortestdistance == nil then
								shortestdistance = dist2d
							elseif dist2d < shortestdistance then
								shortestdistance = dist2d
							end
						elseif tileinfo.is_shoreline == nil then
						--	print("Tile is not shoreline.")
						end

						b =  b + 1
					until( b > 10 )  --or byland == 1)
					a = a + 1
				until( a > 10 )  --or byland == 1)
				print("Shortest distance to a shoreline tile as of this test: ", shortestdistance)

				if byland == 1 and shortestdistance ~= nil and shortestdistance < 25 then
					print("By land.")
					inst.components.locomotor.walkspeed = (inst.components.locomotor.walkspeed / 2.5) + 1 - (shortestdistance / 14)^2  --Player becomes slower the further from shore they go.  --Note: Pack speed bonus is already present, so do not add it again.
					inst.components.locomotor.runspeed = (inst.components.locomotor.runspeed / 2.5) + 1 - (shortestdistance / 14)^2
					if inst.components.locomotor.runspeed < 0.7 then  --Test code.  (Used to prevent player's speed going too low.)
						print("Too Slow.")
						inst.components.locomotor.walkspeed = 0.25
						inst.components.locomotor.runspeed = 0.7
					end
				else
					print("Not by land.")
					inst.components.locomotor.walkspeed = (inst.components.locomotor.walkspeed / 6.25) + 1
					inst.components.locomotor.runspeed = (inst.components.locomotor.runspeed / 6.25) + 1
					if inst.components.locomotor.runspeed > 0.7 then  --Test code.  (Used to hard-cap player's speed in deep water.)
						print("Too Fast.")
						inst.components.locomotor.walkspeed = 0.25
						inst.components.locomotor.runspeed = .7
					end
				end
				if inst.components.moisture:GetMoisturePercent() ~= nil then  --Test code.
					if inst.components.moisture:GetMoisturePercent() < 0.95 then
						inst.components.moisture:SetPercent(inst.components.moisture:GetMoisturePercent() + 0.01)
					else
						inst.components.moisture:SetPercent(1)
					end
				end
			end

I use this to control the speed of my player while swimming.

I'm betting you can figure out how to adapt this code to your own needs.  If you just want to know if there's a shoreline nearby and not the distance to the shoreline, pay attention to "until( a > 10 )  --or byland == 1)".  I'm telling it to scan all of the surrounding tiles, but you can tell it to simply scan until a shoreline is detected.

Edited by FurryEskimo
  • Thanks 1
Link to comment
Share on other sites

Ah dang! That's a ton of help, thanks @Bigfootmechand @FurryEskimo ! I'm new to both lua and modding, and even knowing the basics of lua I'm still confused about how to go about a majority of the things I want, what with how anything but the prefabs work. So! I'm gonna check out walters prefab and take it from there, thanks a bunch @Bigfootmech ! I've never played walter before so that suggestion was a ton of help. And @FurryEskimo, thank you, that's fantastic! I will try to adapt it, but to be honest I've never tried doing anything with position before - am I right in assuming that I should only need to modify the bit you've highlighted, and the function world position bit is the characters position? If so, do I need to require a file that isn't usually required in a character prefab? And could I ask you whether there's a (vaIue?/variabIe?/instance?) like it that keeps track of the selected characters max health (for another prefab I'm trying to make)? I've tried to figure out what that would be called - checked all files but looked the closest at the ones in tuning - and I can't for the life of me figure out which one it would be, so I assumed that I'd just have to work with wilsons max/3 instead until I found the one for current health.

I'm trying to teach myself more lua and looking at the bigger guides myself instead of relying on more experienced modders to have the answers for me, but I'm beginning to suspect that I need to know more about programming in general, outside of the specific languages.

So thank you again! It really is unimaginably helpful to me.

 

  • Health 1
Link to comment
Share on other sites

Oh and also! @Bigfootmech, you wouldn't happen to have any idea how t0 modify walters gain to a toll? The bit claims the gain = 0 as in;

inst._tree_sanity_gain = 0

inst._update_tree_sanity_task = inst:DoPeriodicTask(TUNING.WALTER_TREE_SANITY_UPDATE_TIME, UpdateTreeSanityGain)

and furthermore how to restrict the toll to nighttime?

Link to comment
Share on other sites

> and furthermore how to restrict the toll to nighttime?

Wendy has this.

    inst.components.sanity.night_drain_mult = TUNING.WENDY_SANITY_MULT

You can set it to whatever you like.

1 = "normal"
0.75 = "25% less"
0 = none
-1 = "gain what you would normally lose"

> Oh and also! @Bigfootmech, you wouldn't happen to have any idea how t0 modify walters gain to a toll? The bit claims the gain = 0 as in;

Hmm. This would make me think it's modified somewhere else.

 

prefabs/Walter.lua:230 (part of master_postinit in prefabs/Walter.lua:223)

    inst.components.sanity.custom_rate_fn = CustomSanityFn

This looks about right/suspicious. (note: this is "functional programming" in case you wanna google more)

prefabs/Walter.lua:90

local function CustomSanityFn(inst, dt)
    local health_drain = (1 - inst.components.health:GetPercentWithPenalty()) * TUNING.WALTER_SANITY_HEALTH_DRAIN * inst._sanity_damage_protection:Get()

    return inst._tree_sanity_gain - health_drain
end

so tree sanity gain - calculated health drain. Where is tree sanity gain modified.done?

prefabs/Walter.lua:84

local function UpdateTreeSanityGain(inst)
    local x, y, z = inst.Transform:GetWorldPosition()
    local num_trees = #TheSim:FindEntities(x, y, z, TUNING.WALTER_TREE_SANITY_RADIUS, REQUIRED_TREE_TAGS, EXCLUDE_TREE_TAGS)
    inst._tree_sanity_gain = num_trees >= TUNING.WALTER_TREE_SANITY_THRESHOLD and TUNING.WALTER_TREE_SANITY_BONUS or 0
end

Specifically:

prefabs/Walter.lua:87

inst._tree_sanity_gain = num_trees >= TUNING.WALTER_TREE_SANITY_THRESHOLD and TUNING.WALTER_TREE_SANITY_BONUS or 0

 

Which says:

Set "tree stanity gain" to:

if there are enough trees (TUNING.WALTER_TREE_SANITY_THRESHOLD), then to "Walter's Forest Sanity Gain" (TUNING.WALTER_TREE_SANITY_BONUS)

otherwise, 0

 

This is because of Lua's looser assigning / syntactic sugar.

 

Hope this helps :)

  • Thanks 1
Link to comment
Share on other sites

@Bigfootmech
Prefabs are are important, but ‘component’ are important too.  They’re like, pre-written qualities we can play around with and which influence how items function.

The code I provided is basically ready to go, little extra is needed, and I could probably finish writing it all for you.  I added code to the character’s prefab (master prefab section?) which calls a function every half second.  That function contains this code, so it scans around the player and changed their speed.

To adapt the code I’d give you the code to call the function, give the code that has the function name, and I’d replace the code that changes the player’s speed with code to change the player’s sanity (I think that’s what you said.)

Feel free to DM me, otherwise I’ll post the code here when I can.

  • Thanks 1
Link to comment
Share on other sites

local master_postinit = function(inst)  --Set player's abilities here.
  
	inst.task = inst:DoPeriodicTask(0.5, control_sanity_function)
local function control_sanity_function(inst)
	--Checking water depth.
		print("Testing Water Depth/Distance")
		local x, y, z = inst.Transform:GetWorldPosition()
	--	print("Player's Position: ", x, y, z)

		local map = TheWorld.Map
	--	print("Map Test")
		local tcx, _, tcz = map:GetTileCenterPoint(x , 0, z)  --Tile coordinated that the player is standing on.
	--	print("TCX: ", tcx)
	--	print("TCZ: ", tcz)
			
		local tx, tz = map:GetTileCoordsAtPoint(tcx, 0, tcz)  --Used to get information about tiles, NOT measurements in relation to the player.
	--	print("TX: ", tx)
	--	print("TZ: ", tz)

		local tileid = map:GetTile(tx, tz)
	--	print("Tile ID: ", tileid)

		local tileinfo = GetTileInfo(tileid)
			
	--	print("Current Platform: ", inst:GetCurrentPlatform() )  --Used to check if you're on a boat.

	--	print("Water Type: ", tileinfo.ocean_depth)

	--	if tileinfo.ocean_depth == nil then
	--		print("Player is on land.")
	--	end


		--[ local variable definition --]
		local a = 0
		local b = 0
		local byland = 0
		local shortestdistance = nil
		local waterdebuff = nil
		local shoreline_tiles = nil

		if tileinfo.ocean_depth ~= nil and inst:GetCurrentPlatform() == nil then  --If you're in the water and there's no boat under you.
			--[ repeat loop execution --]
			repeat
				b = 0
				repeat
				--	print("Value of a:", a)
				--	print("Value of b:", b)

					local tileid = map:GetTile(tx - 5 + b, tz + 5 - a)  --Scans left to right, top to bottom.
				--	print("Location Tested: ", tx - 5 + b, tz + 5 - a)
				--	print("Tile ID: ", tileid)

					local tileinfo = GetTileInfo(tileid)
				--	print("Tile Info: ", tileinfo)

				--	print("Testing Tile Info.")
				--	print("Water Type: ", tileinfo.ocean_depth)
	--[[
					if tileinfo.ocean_depth == "SHALLOW" then
						print("SHALLOW WATER")
					elseif tileinfo.ocean_depth == "NORMAL" then
						print("NORMAL WATER")
					elseif tileinfo.ocean_depth == "DEEP" then
						print("DEEP WATER")
					elseif tileinfo.ocean_depth == "VERY_DEEP" then
						print("VERY_DEEP WATER")
					else
						print("Water's Depth Could Not Be Tested.")
				--		byland = 1
					end
	--]]

					local dist2d = math.sqrt( ((tcx - 20 + (4 * b)) - x)^2 + ((tcz + 20 - (4 * a)) - z)^2 )  --Scans left to right, top to bottom.
				--	print("Tile's distance to player: ", dist2d)

					if tileinfo.is_shoreline == true then
					--	print("Tile is shoreline.")
						byland = 1
          				shoreline_tiles = shoreline_tiles + 1

						if shortestdistance == nil then
							shortestdistance = dist2d
						elseif dist2d < shortestdistance then
							shortestdistance = dist2d
						end
					elseif tileinfo.is_shoreline == nil then
					--	print("Tile is not shoreline.")
					end

					b =  b + 1
				until( b > 10 )  --or byland == 1)
				a = a + 1
			until( a > 10 )  --or byland == 1)
			print("Shortest distance to a shoreline tile as of this test: ", shortestdistance)

			if shoreline_tiles ~= nil then
				print("By land.")
				inst.components.sanity:SetPercent(inst.components.sanity:GetPercentWithPenalty() + (shoreline_tiles / 100), 2)
			else
				print("Not by land.")
			end
		end
  end

@0ct0duck
This is just my first quick attempt to update the code for what you want.  I haven't edited sanity directly before, and there's unused code in here, but I think this will cause the player to gain 1% sanity over two seconds per shore tile near the player.  I also included the code that can call this function, with the section of your character's prefab file where it's added.

The main issue is that I don't know how to use this function:

function Sanity:SetPercent(per, overtime)
    local target = per * self.max
    local delta = target - self.current
    self:DoDelta(delta, overtime)
end

This is pre-written into the game and you can use it to control the player's sanity.  I've attempted to use it, but haven't tested it yet.  Best of luck!  Just tag me again and I'll check in on this again.

  • Thanks 1
Link to comment
Share on other sites

On 3/29/2021 at 6:11 PM, Bigfootmech said:

> and furthermore how to restrict the toll to nighttime?

Wendy has this.


    inst.components.sanity.night_drain_mult = TUNING.WENDY_SANITY_MULT

You can set it to whatever you like.

1 = "normal"
0.75 = "25% less"
0 = none
-1 = "gain what you would normally lose"

> Oh and also! @Bigfootmech, you wouldn't happen to have any idea how t0 modify walters gain to a toll? The bit claims the gain = 0 as in;

Hmm. This would make me think it's modified somewhere else.

 

prefabs/Walter.lua:230 (part of master_postinit in prefabs/Walter.lua:223)


    inst.components.sanity.custom_rate_fn = CustomSanityFn

This looks about right/suspicious. (note: this is "functional programming" in case you wanna google more)

prefabs/Walter.lua:90


local function CustomSanityFn(inst, dt)
    local health_drain = (1 - inst.components.health:GetPercentWithPenalty()) * TUNING.WALTER_SANITY_HEALTH_DRAIN * inst._sanity_damage_protection:Get()

    return inst._tree_sanity_gain - health_drain
end

so tree sanity gain - calculated health drain. Where is tree sanity gain modified.done?

prefabs/Walter.lua:84


local function UpdateTreeSanityGain(inst)
    local x, y, z = inst.Transform:GetWorldPosition()
    local num_trees = #TheSim:FindEntities(x, y, z, TUNING.WALTER_TREE_SANITY_RADIUS, REQUIRED_TREE_TAGS, EXCLUDE_TREE_TAGS)
    inst._tree_sanity_gain = num_trees >= TUNING.WALTER_TREE_SANITY_THRESHOLD and TUNING.WALTER_TREE_SANITY_BONUS or 0
end

Specifically:

prefabs/Walter.lua:87


inst._tree_sanity_gain = num_trees >= TUNING.WALTER_TREE_SANITY_THRESHOLD and TUNING.WALTER_TREE_SANITY_BONUS or 0

 

Which says:

Set "tree stanity gain" to:

if there are enough trees (TUNING.WALTER_TREE_SANITY_THRESHOLD), then to "Walter's Forest Sanity Gain" (TUNING.WALTER_TREE_SANITY_BONUS)

otherwise, 0

 

This is because of Lua's looser assigning / syntactic sugar.

 

Hope this helps :)

She does! I have no idea why that didn't occur to me - I'll make sure to go through ALL of the character prefabs to see if there might be a relevant bit somewhere next time. And as for Walter, that's awesome, thank you!

And thank you for explaining how you found the relevant bit that determines Walters sanity gain as well as explaining the different terms and why the syntax is as it is! I'll be honest, it really confused me but I think I understand it a lot better now. And I'll be sure to look into functional programming!

Thanks again!

15 hours ago, FurryEskimo said:

@Bigfootmech
Prefabs are are important, but ‘component’ are important too.  They’re like, pre-written qualities we can play around with and which influence how items function.

The code I provided is basically ready to go, little extra is needed, and I could probably finish writing it all for you.  I added code to the character’s prefab (master prefab section?) which calls a function every half second.  That function contains this code, so it scans around the player and changed their speed.

To adapt the code I’d give you the code to call the function, give the code that has the function name, and I’d replace the code that changes the player’s speed with code to change the player’s sanity (I think that’s what you said.)

Feel free to DM me, otherwise I’ll post the code here when I can.

Thank you! I think I understand components (or at least the simplere ones), though not entirely: I think I know how to define them, but I don't know og that's enough. I think I've gathered that the masterpostit (if that's right?) is quite important if you don't want it to bug out? But honestly I'm not sure what master means in this context.  

And thank you! That's amazing of you, and once again, a gigantic help!!

And, lastly but not leastly, thank you both for your patience! Now I'm really convinced I've ought to go fill out the holes in my knowledge about lua and programming syntax and programming terminology across languages, but you've both been a splendid help, so thanks!

Link to comment
Share on other sites

@0ct0duck
Don’t even sweat it, people offered me a lot of help too and I’m trying to pass some of that good will along.

To learn more about the components go and read the component file you want to use.  When you reference it you’re basically telling it to run one of the prewritten functions inside that folder.  You might do “inst.components.hunger:GetHunger()” (just a guess) and it would run the “GetHunger” function and give you the value.  There aren’t prewritten functions for everything, but for a lot of stuff!

Link to comment
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
 Share

×
  • Create New...