Sign in to follow this  
rosalovesyou

[SOLVED] how to increase the sanity upon picking flowers?

Recommended Posts

it'd be awesome if i could let my character gain more sanity than average upon picking flowers! flowers give a +5 sanity boost. if i can double this to +10, that'd be perfect!

i've looked around in a "making basic character perks" guide and have tried both "Erase/Increase/Decrease an existing sanity regen/damage on an item" on the prefab "flower", and also "Add a sanity aura heal/damage to an item/creature" to the prefab "flower", as well as "petals". none of these approaches have worked, so i'm wondering if anyone else out there has any idea how i can achieve this? ;3; tysm in advance!

Share this post


Link to post
Share on other sites
Ultroman    742

Put this at the bottom of your modmain.lua

-- Make a list of the prefabs we want to change (found the names in flower.lua).
local prefabsToChange = {"flower", "flower_rose", "planted_flower"}

-- Loop over our list with a for-loop where i is the index in the list, and v is one of the flower names.
for i, v in ipairs(prefabsToChange) do
	-- Change the prefab at initialization.
	AddPrefabPostInit(v, function(inst)
		-- We extend the onpickedfn function of the pickable component of all flowers.
		local origFunction = inst.components.pickable.onpickedfn
		function inst.components.pickable.onpickedfn(inst, picker, ...)
			-- We call the original function first. There's no return value, so we just call it.
			origFunction(inst, picker, ...)
			-- Then, if the picker is our character, add an additional 5 sanity.
			if picker.prefab == "MY_CHARACTER_PREFAB_NAME" and picker.components.sanity ~= nil then
				picker.components.sanity:DoDelta(5)
			end
		end
	end)
end

 

Edited by Ultroman

Share this post


Link to post
Share on other sites

@Ultroman

omg thank you so much!! <3

when i tried picking up a flower, i got an error. something to do with "inst". here's what client_log says:

Quote

[00:58:20]: [string "scripts/prefabs/flower.lua"]:44: attempt to index local 'inst' (a nil value)
LUA ERROR stack traceback:
    scripts/prefabs/flower.lua:44 in (upvalue) origFunction (Lua) <43-66>
    ../mods/character-flora/modmain.lua:95 in (field) onpickedfn (Lua) <93-100>
    scripts/components/pickable.lua:444 in (method) Pick (Lua) <403-463>
    scripts/actions.lua:635 in (field) fn (Lua) <633-638>
    scripts/bufferedaction.lua:24 in (method) Do (Lua) <20-34>
    scripts/entityscript.lua:1313 in (method) PerformBufferedAction (Lua) <1305-1323>
    scripts/stategraphs/SGwilson.lua:4084 in (field) fn (Lua) <4078-4086>
    scripts/stategraph.lua:568 in (method) UpdateState (Lua) <536-580>
    scripts/stategraph.lua:607 in (method) Update (Lua) <599-627>
    scripts/stategraph.lua:125 in (method) Update (Lua) <109-148>
    scripts/update.lua:218 in () ? (Lua) <149-228>
    
[00:58:20]: Warning: Widget:SetFocusFromChild is happening on a widget outside of the screen/widget hierachy. This will cause focus moves to fail. Is     ScriptErrorWidget    not a screen?    
[00:58:20]: stack traceback:
    scripts/widgets/widget.lua:602 in (method) SetFocusFromChild (Lua) <599-624>
    scripts/widgets/widget.lua:649 in (method) SetFocus (Lua) <626-658>
    scripts/widgets/scripterrorwidget.lua:107 in (method) OnUpdate (Lua) <102-119>
    scripts/update.lua:90 in () ? (Lua) <33-129>    
[00:58:23]: Registering master server in US lobby
[00:58:40]: Warning: Widget:SetFocusFromChild is happening on a widget outside of the screen/widget hierachy. This will cause focus moves to fail. Is     ScriptErrorWidget    not a screen?    
[00:58:40]: stack traceback:
    scripts/widgets/widget.lua:602 in (method) SetFocusFromChild (Lua) <599-624>
    scripts/widgets/widget.lua:621 in (method) SetFocusFromChild (Lua) <599-624>
    scripts/widgets/widget.lua:621 in (method) SetFocusFromChild (Lua) <599-624>
    scripts/widgets/widget.lua:621 in (method) SetFocusFromChild (Lua) <599-624>
    scripts/widgets/widget.lua:649 in (method) SetFocus (Lua) <626-658>
    scripts/widgets/scripterrorwidget.lua:107 in (method) OnUpdate (Lua) <102-119>
    scripts/update.lua:90 in () ? (Lua) <33-129>    
[00:58:50]: Force aborting...
[00:58:50]: Removing server from US lobby

i couldn't find a server_log in my cluster folder for some reason o.o

Edited by rosalovesyou

Share this post


Link to post
Share on other sites
CarlZalph    5132

Slight alternative means to do this, using the event and seeing from what it was generated:

AddPrefabPostInit(
    "wilson",
    function(inst)
        inst:ListenForEvent(
            "picksomething",
            function(inst, data)
                if data.object and (data.object.prefab:find("flower") ~= nil) and data.loot and data.loot.prefab == "petals"
                then
                    if inst.components.sanity
                    then
                        inst.components.sanity:DoDelta(20)
                    end
                end
            end
        )
    end
)

In this case finding if the source prefab has flower, then checking the loot to see if it isn't an evil flower.

Though you could probably get away with just the petals as loot, but this should help against mods that also have petals for loot but aren't flowers.  (Magic mods?)

Share this post


Link to post
Share on other sites

thank you both @Ultroman and @CarlZalph SOOO much!! they both work flawlessly!!!

i have two follow-up questions while we're working on flowers!!

1. specifically to you @CarlZalph - let's say i want that sanity boost applying to other items that actually do have petals as loot, and not just flowers. for example, there's a very nice cherry blossom forest mod i love that has items such as these. how would i go about doing that? should i modify a part of your code to this?

Quote

 


function(inst, data)
                if data.loot and data.loot.prefab == "petals"
                then

 

2. is there a way i can manipulate either of these codes to remove the damage done by roses? this doesn't have to apply to the thorn tag overall. i would actually prefer it not to, as i'd still like the damage done by cacti, but i understand if that's not possible. at best, just roses in particular. i'm thinking it could be something like:

Quote

 


AddPrefabPostInit(
    "wilson",
    function(inst)
        inst:ListenForEvent(
            "picksomething",
            function(inst, data)
                if data.object and (data.object.prefab:find("flower_rose") ~= nil) and data.loot and data.loot.prefab == "petals"
                then
                    if inst.components.health
                    then
                        inst.components.health:DoDelta(1)
                    end
                end
            end
        )
    end
)

 

but, i feel like this is too straight-forward to actually work? i feel roses are probably coded differently than generic flowers but idk for sure.

THANK YOU GUYS SO MUCH IN ADVANCE YOU'RE A HUGE HELP AND DEFINITELY GETTING A SPECIAL THANKS <3 <3

Edited by rosalovesyou

Share this post


Link to post
Share on other sites
Ultroman    742

If you look at the game code for flowers in flower.lua, you'll see that it creates these prefabs using the same base-code for "flower", "flower_rose" and "planted_flower".

Also, the following line is interesting, and is part of this base for the three flower prefabs:

MakeHauntableChangePrefab(inst, "flower_evil")

So, all these prefabs, when haunted, are changed to the "flower_evil". However, "flower_evil" is in his own prefab file.

Anyway, in order to make your character not be damaged by roses, we can take a look at the rose-specific code in flower.lua. This is the code that is called when a flower is picked.

local function onpickedfn(inst, picker)
    local pos = inst:GetPosition()

    if picker ~= nil then
        if picker.components.sanity ~= nil and not picker:HasTag("plantkin") then
            picker.components.sanity:DoDelta(TUNING.SANITY_TINY)
        end

        if inst.animname == ROSE_NAME and
            picker.components.combat ~= nil and
            not (picker.components.inventory ~= nil and picker.components.inventory:EquipHasTag("bramble_resistant")) then
            picker.components.combat:GetAttacked(inst, TUNING.ROSE_DAMAGE)
            picker:PushEvent("thorns")
        end
    end

    if not inst.planted then
        TheWorld:PushEvent("beginregrowth", inst)
    end

    inst:Remove()

    TheWorld:PushEvent("plantkilled", { doer = picker, pos = pos }) --this event is pushed in other places too
end

This is the rose-specific part:

if inst.animname == ROSE_NAME and
	picker.components.combat ~= nil and
	not (picker.components.inventory ~= nil and picker.components.inventory:EquipHasTag("bramble_resistant")) then
	picker.components.combat:GetAttacked(inst, TUNING.ROSE_DAMAGE)
	picker:PushEvent("thorns")
end

And this line does the damage:

picker.components.combat:GetAttacked(inst, TUNING.ROSE_DAMAGE)

picker is the player picking the flower and inst is the flower instance. We can use this to our advantage, to properly block the damage from happening, instead of giving back the health after the fact.

Add this code at the bottom of your character's master_postinit function:

local oldGetAttacked = inst.components.combat.GetAttacked
inst.components.combat.GetAttacked = function(self, attacker, damage, ...)
	if attacker:HasTag("flower") then
		return
	end
	oldGetAttacked(self, attacker, damage, ...)
end

This will make your character not take damage if the attacker has the tag "flower". But, if someone attacks you with a weapon that has the tag "flower", it will still do damage. The only game-prefabs with the tag "flower" are the flowers in flower.lua and the evil_flower prefab, so this should do what you want.

Edited by Ultroman

Share this post


Link to post
Share on other sites

@Ultroman OH MY GOODNESS THANK YOU SO MUCH!!! ;A; it works perfectly!! i greatly appreciate how you teach me these things too!!!

i was just about to say how it wasn't working unfortunately, but then i saw your reply and gave it a whirl before posting!!

oh my gosh, with everything you've helped me with, i think i just about have all the perks finished that i really want for my character!!! there's just one more...

is there a way to increase the chance of roses being planted upon planting butterflies? i've looked around in flower.lua and tried messing with ROSE_CHANCE but what i cooked up didn't work. D: any ideas here on this one?

Edited by rosalovesyou

Share this post


Link to post
Share on other sites
Ultroman    742

EDIT: Sorry that this became sort of a "how to fail until you succeed" kind of mega-post. I left it all here to show you and others how I figure out how to do these things, and kind of explain which questions you need to be asking yourself, and some of the pitfalls you can fall into. I still fall into them sometimes, because I'm not ultra-experienced with neither Lua nor the DS/DST code. I'm really an intermediate at best, with a very poor memory :) The final solution is at the very bottom, but it would be good to understand why I didn't jump to that solution first, and you'll understand that better if you read the whole thing (or at least the notes above the last code snippet).

Original post starts here:

Sadly, the ROSE_CHANCE variable and the setflowertype function are both local, and the latter is not made available through hooks in any components (unlike the also local function onpickedfn which we just altered). So we can't utilize the existing code for this, which is sad. You can study the code for it here:

local names = {"f1","f2","f3","f4","f5","f6","f7","f8","f9","f10"}
local ROSE_NAME = "rose"
local ROSE_CHANCE = 0.01

local function setflowertype(inst, name)
    if inst.animname == nil or (name ~= nil and inst.animname ~= name) then
        if inst.animname == ROSE_NAME then
            inst:RemoveTag("thorny")
        end
        inst.animname = name or (math.random() < ROSE_CHANCE and ROSE_NAME or names[math.random(#names)])
        inst.AnimState:PlayAnimation(inst.animname)
        if inst.animname == ROSE_NAME then
            inst:AddTag("thorny")
        end
    end
end

But what we CAN do, is change the planted flower AFTER it has been planted, and by "after" I mean immediately after, so you won't see it switch. I noticed that the third prefab in flower.lua was called "planted_flower", so there's a separate prefab for flowers you've planted yourself. This is because planted flowers do not regrow, but when you pick a world-populated flower, it starts some regrowth code. Anyway, I searched for the name of the prefab "planted_flower" in the game files, because it has to get spawned somewhere in an ACTION or a picking function somewhere, and found it in the butterfly prefab:

local function OnDeploy(inst, pt, deployer) 
    local flower = SpawnPrefab("planted_flower")
    if flower then
        flower:PushEvent("growfrombutterfly")
        flower.Transform:SetPosition(pt:Get())
        inst.components.stackable:Get():Remove()
        AwardPlayerAchievement("growfrombutterfly", deployer)
    end
end

This function is sadly also local, BUT it IS hooked up to a non-local variable on the "deployable" component in the butterfly's fn() function (its sort of "initializer" function) with the following line.

inst.components.deployable.ondeploy = OnDeploy

So, we can do sort of the same thing as we did before with the onpickedfn, and extend this function, and add some code for ourselves. Or so I thought...another lesson here that I keep forgetting. Notice that in OnDeploy (see above) the spawned flower is set to a local variable named "flower" within the scope of the function. When we extend this function, we do not have access to local variables created within the function we are extending :( So to show that in code:

AddPrefabPostInit("butterfly", function(inst)
	local oldondeploy = inst.components.deployable.ondeploy
	inst.components.deployable.ondeploy = function(inst, pt, deployer, ...)
		oldondeploy(inst, pt, deployer, ...)
		-- the "flower" variable will be nil here
	end
end)

So, what can we do? Studying flower.lua further, I saw this in the fn() function:

if not POPULATING then
    setflowertype(inst)
end

So, there's a global variable, POPULATING, which is set to true while the world is populating (generating, possibly also loading(?)) flowers on the map, so the code inside this if-statement in the fn() is only called when the world is NOT populating, so this code is specifically for when a flower is planted during gameplay. The setflowertype function (which I posted in full above) is called without the "name" parameter, so it will randomly find a flower animation to use, and if it has chosen a rose, it will do special things for the thorns, etc. We can use this POPULATING variable to our advantage, and do an AddPrefabPostInit on the "planted_flower" prefab which only makes a change if the world is not currently populating. We can steal the code for setflowertype and replace the chances with our own numbers. I've done so in the code below, and only slightly altered the code to remove some checks we don't need.

I should mention, this code affects all players on the server, as long as your mod is enabled on the server. I'll see if I can figure out how to only do this for your character, but since only the OnDeploy function knows who the deployer is, it might be hard.

This all goes into the modmain:

local names = {"f1","f2","f3","f4","f5","f6","f7","f8","f9","f10"}
local ROSE_NAME = "rose"
local ROSE_CHANCE = 0.20 -- vanilla is 0.01 which is 1% chance; this is 20% chance

local function setflowertype(inst)
	-- If the flower has an animname already (it should) and it is a rose,
	-- then we first strip away its "thorny" tag.
	if inst.animname ~= nil and inst.animname == ROSE_NAME then
		inst:RemoveTag("thorny")
	end
	-- Use our ROSE_CHANCE to see if it is going to be a rose,
	-- and otherwise choose between the other 10 flower types.
	inst.animname = math.random() < ROSE_CHANCE and ROSE_NAME or names[math.random(#names)]
	inst.AnimState:PlayAnimation(inst.animname)
	-- If it became a rose, we add the "thorny" tag.
	if inst.animname == ROSE_NAME then
		inst:AddTag("thorny")
	end
end

AddPrefabPostInit("planted_flower", function(inst)
	if not GLOBAL.POPULATING then
		setflowertype(inst)
	end
end)

Notice that we don't actually mess with any of the original code, and there are no hooks or anything. We just use the same method of changing the type of a flower as the game does, and we do it whenever a "planted_flower" is spawned, but only if it is not during world generation. The only downside to this, aside from it not being character-specific, is if Klei adds more flower types to the game, represented here in the "names" list, then your code will not spawn them, unless you also add those flowers to your "names" list. It is IMO a negligible thing, since those flowers are all generic, unlike the rose.

UPDATE:

Well, I think I can get it to work for your character only, but it does require you to keep a look-out for changes in the original code from time to time. It requires that we make OnDeploy work differently specifically for your character, by duplicating the original code, and altering it a little bit. This means that if Klei changes the original code, then you will also have to make that change in your character-specific code.

Here's the code. Only the AddPrefabPostInit has changed. NOTICE that you have to put in your character's prefab name at one line in the AddPrefabPostInit:

local names = {"f1","f2","f3","f4","f5","f6","f7","f8","f9","f10"}
local ROSE_NAME = "rose"
local ROSE_CHANCE = 0.20 -- vanilla is 0.01 which is 1% chance; this is 20% chance

local function setflowertype(inst)
	-- If the flower has an animname already (it should) and it is a rose,
	-- then we first strip away its "thorny" tag.
	if inst.animname ~= nil and inst.animname == ROSE_NAME then
		inst:RemoveTag("thorny")
	end
	-- Use our ROSE_CHANCE to see if it's going to be a rose,
	-- and otherwise choose between the other 10 flower types.
	inst.animname = math.random() < ROSE_CHANCE and ROSE_NAME or names[math.random(#names)]
	inst.AnimState:PlayAnimation(inst.animname)
	-- If it became a rose, we add the "thorny" tag.
	if inst.animname == ROSE_NAME then
		inst:AddTag("thorny")
	end
end

AddPrefabPostInit("butterfly", function(inst)
	local oldondeploy = inst.components.deployable.ondeploy
	inst.components.deployable.ondeploy = function(inst, pt, deployer, ...)
		if deployer.prefab == "MY_CHARACTER_PREFAB_NAME" then
			local flower = GLOBAL.SpawnPrefab("planted_flower")
			if flower then
				setflowertype(flower)
				flower:PushEvent("growfrombutterfly")
				flower.Transform:SetPosition(pt:Get())
				inst.components.stackable:Get():Remove()
				GLOBAL.AwardPlayerAchievement("growfrombutterfly", deployer)
			end
		else
			oldondeploy(inst, pt, deployer, ...)
		end
	end
end)

 

Edited by Ultroman

Share this post


Link to post
Share on other sites

@Ultroman i wish i could give you the biggest bearhug ever. oh my god, i think you made me the happiest i've been in a LONG time, IT WORKS FLAWLESSLY!! and i totally don't mind it applying to the server overall, nor the other downfall!!

thank you so so so so SO much for all of your help with my mod!! i greatly appreciate how in-depth you go with all of this and telling me everything you've done and why, and hopefully i'll be able to wrap my head around all of it someday!! i will definitely be saving all of these for reference so i don't lose anything you've provided. i greatly very much appreciate every single ounce of effort you've put into this with me, and i surely hope you've learned a thing or two along with me!! :D you're getting a HUGE special thanks in my mod's description when it's uploaded, you can surely count on that!! <3

UPDATE: AAAA YOU GOT IT WORKING JUST FOR THE CHARACTER TOO?? YOU ARE AN ANGEL <3

Edited by rosalovesyou

Share this post


Link to post
Share on other sites
Ultroman    742

@rosalovesyou I can't tell you how happy it makes ME feel, to see (read?) someone being so appreciative of my help :D Really? The happiest you've been in a long time? I'd wish I could help with more than your mods, then :( You seem to have a drive, though, so you'll probably be able to find your own way to happiness. I hope so, anyway.

While I didn't learn much new, I re-learned some things and reaffirmed other things, so I guess I did learn something ;)

9 minutes ago, rosalovesyou said:

UPDATE: AAAA YOU GOT IT WORKING JUST FOR THE CHARACTER TOO?? YOU ARE AN ANGEL <3

Well, I hope so. Let's see what happens when you test it xD

Share this post


Link to post
Share on other sites

@Ultroman awww that's so sweet of you!! <3<3 i'm sure i'll find my way, i just know it!!

seems like you called it with the testing lol! got an error on planting, idk how much i'd have to copy-paste so i'm sending over the client_log. i'm sending over modmain as well, just to be sure it's nothing that's conflicting with the other codes! i wouldn't think so since the former non-character-specific one worked just fine, but just being safe!!

client_log.txt

modmain.lua

Share this post


Link to post
Share on other sites
Ultroman    742

Another update to the final code above. I moved the call to setflowertype to the top of the if-statement, so when the "growfrombutterfly" event is called, the flower will already have had its type changed. This way, if some mod listens for that event and does something depending on which type of flower it is, then we have already made our change, so they'll be able to read the type that the flower ended up being.

UPDATE: I have fixed the problem in the code above, but before you copy it, let's see if you can't remember how we fixed a similar problem a few days ago. This is your error from the client_log.txt

[00:04:00]: [string "../mods/character-flora/modmain.lua"]:133: attempt to call global 'SpawnPrefab' (a nil value)

This is a common thing to forget when copying game code (for me, at least).

 

3 minutes ago, rosalovesyou said:

@Ultroman awww that's so sweet of you!! <3<3 i'm sure i'll find my way, i just know it!!

With that attitude, you surely will! Happiness has many faces. You just gotta find yours.

Edited by Ultroman

Share this post


Link to post
Share on other sites

@Ultroman i had a feeling i just had to add GLOBAL! :D added it to SpawnPrefab - got another crash - and then added it to AwardPlayerAchievement. it's working perfectly!! <3

thank you for being such a huge support!! you're so very kind! ;w;

Share this post


Link to post
Share on other sites
Ultroman    742
2 minutes ago, rosalovesyou said:

@Ultroman i had a feeling i just had to add GLOBAL! :D added it to SpawnPrefab - got another crash - and then added it to AwardPlayerAchievement. it's working perfectly!! <3

That's awesome! :D  Great job! I've now added GLOBAL to AwardPlayerAchievement in the final code above, as well.

4 minutes ago, rosalovesyou said:

thank you for being such a huge support!! you're so very kind! ;w;

You are most welcome! :) I hope the experience with modding hasn't scared you away, hehe.

Share this post


Link to post
Share on other sites

@Ultroman thank you!! <3 it hasn't scared me away at all actually! if anything i'm just super excited to keep modding and learning new things~ ^^

after this character of myself, i'm hoping to do an entirely different, more simple character in the far future. you may not know the character as he's at a weird middle-ground of the internet - underrated, but also pretty big at the same time. let's just say he's a bald teacher in green and i definitely plan on giving him a ruler weapon with the damage of a ham bat, if not stronger.... //winkwink

you'll probably see me in the forums again for that one! :D thankfully i already know a tutorial for the weapon!

Share this post


Link to post
Share on other sites
Ultroman    742
22 minutes ago, rosalovesyou said:

@Ultroman thank you!! <3 it hasn't scared me away at all actually! if anything i'm just super excited to keep modding and learning new things~ ^^

That's great to hear :)

21 minutes ago, rosalovesyou said:

after this character of myself, i'm hoping to do an entirely different, more simple character in the far future. you may not know the character as he's at a weird middle-ground of the internet - underrated, but also pretty big at the same time. let's just say he's a bald teacher in green and i definitely plan on giving him a ruler weapon with the damage of a ham bat, if not stronger.... //winkwink

The guy from Baldi's Basics? Or Mr. Garrison?

23 minutes ago, rosalovesyou said:

you'll probably see me in the forums again for that one! :D thankfully i already know a tutorial for the weapon!

Don't hold back :) Just remember that you learn the most from banging your head against the wall until it breaks, and that we're here if it won't xD

Share this post


Link to post
Share on other sites

@Ultroman "welcome to baldi's basics in survival and hunger, that's me!!" XD you nailed it! i have a very interesting perk planned for his sanity, too~

and i will definitely keep that in mind lol! thank you again for all of your help!! you're truly wonderful~! <3

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