Jump to content

Recommended Posts

I'm trying to make an item that you don't equip and just sits on your inventory, and by having it on your inventory you gain sanity.

I was told to check InventoryItem components specifically setondroppedfn and setonpickupfn but I couldn't make it work because I don't understand what I'm doing at all, items aren't my specialty.

I've attached my poor attempt, but also worth noting that I plan for this item to do more than just sanity, I just need the structure to then apply effects to my heart's content.

medal_red.lua

Edited by ArcAngela
Solved!

Well you could use the "itemaffinity" component:

AddPlayerPostInit(function(inst)
	if inst.components.itemaffinity == nil then
		inst:AddComponent("itemaffinity")
	end
	inst.components.itemaffinity:AddAffinity("medal_red", nil, TUNING.DAPPERNESS_MED, 1)
end)

This adds the component to every player. Not the item itself. This should go into modmain.lua.

"itemaffinity" component is not added to every player prefab, so we have to add it first. The only prefab that uses this component in vanilla is the Wurt prefab.

Here is an example of how the Wurt prefab uses it to make having fish in inventory give sanity bonus (or debuff for fish meat):

inst:AddComponent("itemaffinity")
inst.components.itemaffinity:AddAffinity("hutch_fishbowl", nil, TUNING.DAPPERNESS_MED, 1)
inst.components.itemaffinity:AddAffinity(nil, "fish", TUNING.DAPPERNESS_MED, 1)
inst.components.itemaffinity:AddAffinity(nil, "fishmeat", -TUNING.DAPPERNESS_MED_LARGE, 2)
inst.components.itemaffinity:AddAffinity(nil, "spoiled_fish", -TUNING.DAPPERNESS_MED_LARGE, 2)

First argument is the prefab that will give sanity bonus if it's in the inventory. "medal_red" goes here. Can be nil to use a tag instead.

Second argument allows to supply a tag instead of a specific prefab, so you don't have to list each one. There are many types of fish, but they all have the same "fish" tag, so all of them will give a bonus. Can be nil to use a prefab instead.

Third argument is the sanity bonus.

Fourth argument is the priority. This component works so that the highest priority item is the only one giving a bonus. So, in this example having fish meat has a higher priority than having live fish, only the fish meat sanity bonus (debuff in this case) will be applied; you cannot cancel out the bonus by having equal amounts of dead and alive fish.

 

If you look into "itemaffinity.lua" and "sanity.lua" components you can see how it works under the hood. But basically, the player's sanity component has a special variable for having one, and only one additional multiplier. You can set it with:

inst.components.sanity.externalmodifiers:SetModifier(inst, 1)

(assuming inst is a player prefab)

The game doesn't mind overriding it, it is used in few places. You could design something with this if you HAVE to have the sanity boost be in item's code, and not player's.

That doesn't seem to be quite what I'm looking for, I've used sanity mostly as a structure to add more stuff in the future.

I'm planning to do 6 medals based on each gem, and they're supposed to give multiple buffs each, to any character that has them, and obviously I need to remove said buffs from them if they decide to drop the item.

I got it working with this code but this is finnicky and very limited, having 2 medals on your inventory does nothing but dropping one removes all buffs, but the biggest limitation is that I can't exactly do things like picker.components.combat.damagemultiplier = +.15 because this would break several characters and their unique damage multipliers, so I need a method to be able to always just "add" to the picker/owner so I can subtract later.

You can see I added sanity and winter insulation to demonstrate how I plan to give multiple buffs with 1 medal item, but setting winter insulation to 0 sounds very problematic if a character happens to have insulation of their own.

local function OnDropped(inst)
	--this needs a check to see if the owner still has a red_medal in their inventory in order to remove sanity
	inst.owner.components.sanity.dapperness = 0
	inst.owner.components.temperature.inherentinsulation = 0
	inst.owner = nil
end

local function OnPickedUp(inst, picker)
	inst.owner = picker
	if picker and picker.components.sanity then
	picker.components.temperature.inherentinsulation = TUNING.INSULATION_MED
	picker.components.sanity.dapperness = TUNING.DAPPERNESS_MED
	end
end

 

Edited by ArcAngela

Oh, I see. Now that I look more closely at the code I realize your initial way of doing it is better and simpler.

One thing, you probably shouldn't override the dapperness variable like that.

Currently there are 2 places where this variable is used: Maxwell's inherent sanity boost and WX-78's music module.

This means when Maxwell picks up the red amulet, his inherent sanity boost of TUNING.DAPPERNESS_LARGE gets overwritten by TUNING.DAPPERNESS_MED, and when he drops it, overwritten to 0.

You can fix this by adding or subtracting on what is already there, rather than just overwriting it.

local function OnDropped(inst)
	inst.owner.components.sanity.dapperness = inst.owner.components.sanity.dapperness - TUNING.DAPPERNESS_MED
end

local function OnPickedUp(inst, picker)
	picker.components.sanity.dapperness = picker.components.sanity.dapperness + TUNING.DAPPERNESS_MED
end

Same applies for temperature.inherentinsulation as Wes has negative TUNING.INSULATION_TINY, meaning it too will get overwritten.

5 minutes ago, Hamurlik said:

Oh, I see. Now that I look more closely at the code I realize your initial way of doing it is better and simpler.

One thing, you probably shouldn't override the dapperness variable like that.

Currently there are 2 places where this variable is used: Maxwell's inherent sanity boost and WX-78's music module.

This means when Maxwell picks up the red amulet, his inherent sanity boost of TUNING.DAPPERNESS_LARGE gets overwritten by TUNING.DAPPERNESS_MED, and when he drops it, overwritten to 0.

You can fix this by adding or subtracting on what is already there, rather than just overwriting it.

local function OnDropped(inst)
	inst.owner.components.sanity.dapperness = inst.owner.components.sanity.dapperness - TUNING.DAPPERNESS_MED
end

local function OnPickedUp(inst, picker)
	picker.components.sanity.dapperness = picker.components.sanity.dapperness + TUNING.DAPPERNESS_MED
end

Same applies for temperature.inherentinsulation as Wes has negative TUNING.INSULATION_TINY, meaning it too will get overwritten.

Would this work for basically everything? summer insulation, extra move speed, extra damage multipliers, etc?

I can only see this not working when it involves components and tags... cause I'd have to add a component (example: efficientworker) and then I presume I need to also remove the component when the person drops the item, correct?

Example: fastbuilder from Winona, how can I prevent a character who already has fastbuilder to not have their tag removed when they drop the medal if I want the medal to give fastbuilder tag?

Edited by ArcAngela
Quote

Example: fastbuilder from Winona, how can I prevent a character who already has fastbuilder to not have their tag removed when they drop the medal if I want the medal to give fastbuilder tag?

You could store whether the player had the tag before they picked the amulet up.

local hadFastBuilder = false

local function OnDropped(inst)
	if not hadFastBuilder then
		inst.owner:RemoveTag("fastbuilder")
	end
end

local function OnPickedUp(inst, picker)
	hadFastBuilder = inst.owner:HasTag("fastbuilder")

	if not hadFastBuilder then
		inst.owner:AddTag("fastbuilder")
	end
end

A component equivalence for this would be:

local hadEfficientUser = false

local function OnDropped(inst)
	if not hadEfficientUser then
		inst.owner:RemoveComponent("efficientuser")
	end
end

local function OnPickedUp(inst, picker)
	hadEfficientUser = inst.owner.components.efficientuser and true or false

	if not hadEfficientUser then
		inst.owner:AddComponent("efficientuser")
	end
end

 

Quote

Would this work for basically everything? summer insulation, extra move speed, extra damage multipliers, etc?

For stuff that uses a variable, yes. inherentinsulation in temperature or dapperness in sanity use a simple variable. They would work like that.

A quirk about this, while dapperness in code is always added together to not overwrite, inherentinsulation is overwritten. See, the only 2 places where this variable is used on character prefabs is on Wes and Wolfgang's mightiness component. Wes inherently has negative insulation, so he gets cold easier (and he also has negative summer insulation, so it doesn't mean it's better in summer). And mightiness component gives some winter insulation when Wolfgang is in mighty form. But since Wes will never have the mightiness component, the devs don't mind overwriting it. Then again the WX-78 music module technically won't ever overwrite dapperness because only Maxwell has that variable ever used.

...But anyway, ignoring this inconsistency, you should use the safer option of adding together most of the time, as updates might change this in the future.

 

Speed uses a different system. You can see how it works in detail at "scripts\components\locomotor.lua"

But basically, you use functions and supply them with a key instead.

inst.components.locomotor:SetExternalSpeedMultiplier(inst, "speedmultiplier_key", 5)

First argument is the player. Second argument is a key; speed is calculated from multiple sources, this allows you to add another. Third argument is the value.

When you don't need it anymore, you can remove it so:

inst.components.locomotor:RemoveExternalSpeedMultiplier(inst, "speedmultiplier_key")

Using this will make sure you won't overwrite anything.

 

Damage modifiers also use a different system. To add a modifier:

inst.components.combat.externaldamagemultipliers:SetModifier(inst, 1.5, "key")

First argument is the player. Second argument is the value. Third argument is the key.

One quirk about this, Wolfgang's mightiness component overwrites this whenever he changes form. Idk why. There is definitely a way to fix this but I won't look into it right now.

  • Like 1

Thank you so much, this is very helpful!

for clarity, "speedmultiplier_key" and "key" in this case can be anything, just to make sure it doesn't collide with existing code in the game, right?

10 minutes ago, ArcAngela said:

for clarity, "speedmultiplier_key" and "key" in this case can be anything, just to make sure it doesn't collide with existing code in the game, right?

Yep, any string.

While putting this code to practice I found a very silly behavior, you can keep your tags if you pick up more than 1 medal, meaning you always have the "hadTagBefore" 

image.png.5db34313e967530aacbbe7e47432f34c.png

But more importantly, any way I can make Medals not stack to prevent both the "hadTagBefore" issue and the exponential amount of buffs as shown by Wolfgang?

2am update, with the help of my friend he managed to make the medals not stack buffs, but this broke tags completely, in the file below I add the tag expertchef to test this, but when dropping the item it never removes the tag despite removing the extra damage.

medal_red.lua

My friend has figured it out and fixed the code, it now works perfectly! a character who had Tags will keep their tags, a character who didn't will have them removed.

The solution was kind of very much in our faces, first we check if they didn't have expertchef, which makes us add expertchef and a completely new tag, this is the tag that differentiates the player, so when they drop the item, we check for that made-up tag before removing both.

This makes sure they always lose expertchef, and makes sure characters who already had expertchef don't.

local function OnDropped(inst)
	local owner = inst.owner
	inst.owner = nil
	if owner.components.inventory:HasItemWithTag("medal_red", 1) then --Still have one, do nothing
		return
	end
	if owner:HasTag("medal_expertchef") then
		owner:RemoveTag("medal_expertchef")
		owner:RemoveTag("expertchef")
	end

	owner:RemoveTag("navyrecruit")
	--owner.components.sanity.dapperness = picker.components.sanity.dapperness - TUNING.DAPPERNESS_MED
	owner.components.temperature.inherentinsulation = owner.components.temperature.inherentinsulation - TUNING.INSULATION_MED
	owner.components.combat.externaldamagemultipliers:SetModifier("medal_red", 1)
	owner.components.health.absorb = owner.components.health.absorb - 0.5
end

local function OnPickedUp(inst, picker)
	if picker == nil then --is this even possible?
		return
	end

	inst.owner = picker
	if inst.owner.components.inventory:HasItemWithTag("medal_red", 1) then --Already have one, do nothing
		return
	end
	if not inst.owner:HasTag("expertchef") then
		inst.owner:AddTag("medal_expertchef")
		inst.owner:AddTag("expertchef")
	end
	if picker.components.sanity then
		inst.owner:AddTag("navyrecruit") --so Whiskey doesn't lose sanity near them
		--picker.components.sanity.dapperness = picker.components.sanity.dapperness + TUNING.DAPPERNESS_MED
		picker.components.temperature.inherentinsulation = picker.components.temperature.inherentinsulation + TUNING.INSULATION_MED
		picker.components.combat.externaldamagemultipliers:SetModifier("medal_red", 1.25)
		picker.components.health.absorb = picker.components.health.absorb + 0.5
		
	end
end

This isn't exactly a "fix-all" because I think mods have a cap of only being allowed to add 30 tags, so obviously 1 tag for every component or existing tag is gonna heavily limit someone's options, not mine tho!

That was a field trip, now we know how to make inventory items that do special things without the need of being equipped/unequipped.

Glad to hear that!

I have a concern however, does it work when using it with backpacks? Try putting a medal into a backpack, and then dropping that backpack. Will OnDropped trigger, or will player still have the tag?

You could just forbid putting the item in backpacks in the first place.

local function fn()
	-- . . .

	inst.components.inventoryitem.canonlygoinpocket = true

	-- . . .
end

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