Jump to content

Trait which adds temporary effect to player whenever they kill an animal


Auberginuspl

Recommended Posts

So, I created the .esp using the above scripts. Spawned myself the debug pistol on a new character and went around the world killing animals that should have been affected by the perk. Surprisingly enough, it sort of worked. Sort of, because it affected only roughly a quarter of animals that I found.

 

So.. despite the fact that I added pretty much every Creature from the AnimalFriendFaction to my Form List, only some of them are affected by the script, for whatever reason.

 

For example, while domesticated Bighorners are affected (and do give the player the debuff upon killing them), wild Bighorners are not. Not a single coyote or gecko (except for a few Golden Geckos) I found was affected, but every Night Stalker was, including the Legendary variant. Every Brahmin I found was affected, including the hostile variant. Wild Molerats weren't affected, but Snuffles was. Giant rats weren't affected, except for two "Unnaturally Large Sized Rodents" found in the sewers. Viscious dogs weren't affected, but NCR and Legion dogs were.

As you can see, it's very inconsistent and I'm trying to figure out why. I studied the case of coyotes that spawn around Goodsprings. Killing them doesn't give me the debuff. In G.E.C.K it seems that coyote spawners were objects VSpawnTier1CoyoteMed75, VSpawnTier1CoyoteMed50, and others that were NOT in the Animal Friend Faction, so I hadn't added them to my form list at first. Therefore, I went ahead and added those, as well as pretty much every relevant creature from the Actors>Creatures tab, so that every Actor entry that, for example, contains "Coyote" or "Bighorner" has been added into my form list.

 

This, however, did nothing, and I am at a loss. How come some creatures are affected but others aren't? What might be causing this? I'd appreciate any suggestions because I don't know what to do... Thanks in advance.

Link to comment
Share on other sites

Well, I suppose I found a workaround, but it involves manually adding scripts to a huge bulk of Actors that were unaffected by my original script, like GamerRick originally sugested. The original script still covers a lot of animals, including Legion Mongrels which already had a script attached to them so I wouldn't be able to add mine manually, so I guess it sort of works.

 

I attached this script to every Coyote object I found:

scn 111AnimalCrueltySCRIPT

Begin OnDeath
	if player.hasperk AnimalFriend
	player.castimmediateonself AnimalFriendDebuff
	showmessage AnimalCrueltyMessage
	endif
end

And bam, it works as intended.

 

I'm gonna try this with the rest of unaffected animals and see how it works. Thank you so much for your suggestions, GamerRick, you were extremely helpful! I might just accomplish what I wanted with my mod.

 

But if you, or anybody else has any answers as to why my original script with the SetJohnnyOnDyingEventHandler function with the Form List with every animal in it didn't work for all animals, but only some of them, I'd like to hear em. Thanks all!

Link to comment
Share on other sites

Sorry for being spammy. While I was manually attaching scripts to animals, I realized I want to also penalize the player for eating the meat of affected animals. I could do that by manually editing the Ingestibles themselves but since I'm using a mod that makes changes to these items (JSawyer), I figured to try and see if there's a way to implement this via scripting. I did a little test on Bighorner Meat:

scn AnimalFoodDebuffQuest
begin gamemode
if Player.hasperk AnimalFriend
if Player.IsSpellTargetAlt BighornerMeat
player.castimmediateonself AnimalFriendDebuff
showmessage AnimalCrueltyMessage
endif
endif
end

And it works, however, the message, as well as the debuff is being applied CONTINUOUSLY for as long as the base effect of the ingested meat is active. Is there a way to make it only fire ONCE upon consumption? I tried the OnMagicEffectHit but I don't think it works with consumables, only base effects. Any ideas? Thanks in advance...

Link to comment
Share on other sites

You need a "flag" variable to indicate you have already applied the debuff, and a condition for when the debuff is over. For ex:

scn AnimalFoodDebuffQuest
int iDoOnce
begin gamemode
if Player.hasperk AnimalFriend
   if Player.IsSpellTargetAlt BighornerMeat
      If iDoOnce == 0
         player.castimmediateonself AnimalFriendDebuff
         Let iDoOnce := 1
         showmessage AnimalCrueltyMessage
      EndIf
   Elseif iDoOnce <> 0
      Let iDoOnce := 0
   Else
      print "Player is not target of BigHornerMeat"
   endif
endif
end

-Dubious-

Link to comment
Share on other sites

Hey, that's fantastic Dubious, thank you very much for your help, it works perfectly. Well, almost, rarely it just won't apply the effect for no reason but the next time I eat the meat, it does, probably engine limitations or something. Either way, it's what I was looking for, fantastic!

 

BTW: initially the script only fired once and that's it, I had to change Elseif iDoOnce <> 0 to Elseif iDoOnce == 1 and now it works.

 

I started adding other meat items to your script, like so:

if Player.IsSpellTargetAlt BighornerMeat > 0 || Player.IsSpellTargetAlt BigHornerSteak > 0 || Player.IsSpellTargetAlt CoyoteMeat > 0 || Player.IsSpellTargetAlt BrahminMeat > 0 || Player.IsSpellTargetAlt BrahminSteak > 0

Etcetera, but upon adding about a dozen of items, I get an error:

 

 

Max Script line length (512 characters) exceeded.

 

How I can add more items to the same script? I tried fiddling with elseif and else functions but I only end up breaking the script...

Link to comment
Share on other sites

Glad you figured out how to fix something I threw together off the top of my head without any testing. As for why the code isn't firing off in some instances, afraid that is something you have to determine by testing. Most likely some value you are expecting when the code in question starts is not "initialized" as you expect. Use debug print statements before conditional tests to see exactly what values are being passed at the moment.

 

As you have found, there is a limit (i.e. 512 characters to include spaces) to how long a single line of code can extend before the engine chokes on it. In part this is because the engine evaluates the entire line of code before it then breaks down it down to testing the result of individual "test conditions". In the specific case of "compound conditionals" (all your " || (OR)" tests, these can also become a horror story when trying to determine which is causing a logic bomb.

 

Note that when you are putting together a series of "compound conditions", it becomes essential to help the engine parse the line correctly, by grouping each "conditional" in it's own set of the appropriate "encapsulation" marks as described in 'TIP Best Practice Encapsulation Parens Brackets and Braces".

Please see "TIP Basic conditional test syntax" and the other linked "Tips" in that one for a more extensive break down of the subject. However, for your situation consider this.

 

You are only going to have one "SpellTarget" in effect at the moment in that script. So you just need to determine which target 'meat' is "in play". So rather than all those "OR" compound conditions you merely need to convert them into a series of "ElseIf" statements (the logic equivalent to the OR), so only one result for the specific "target meat" fires off. Ex:

If Player.IsSpellTargetAlt BighornerMeat > 0
  ; do something 1
ElseIf Player.IsSpellTargetAlt BigHornerSteak > 0
  ; do something 2
ElseIf Player.IsSpellTargetAlt CoyoteMeat > 0
  ; do something 3
ElseIf ; repeat condition logic as often as required
  ; do something as needed
Else
  ; failsafe action to take when the target is not valid, which can include a "do nothing" comment
EndIf

While this seems like many lines of code, it is extremely efficient for the engine to process and usually is quicker than parsing out the equivalent compound conditional line as the engine skips everything remaining as soon as it finds the first "true" condition line.

 

If you should hit upon a limit to the number of "elseif" statements you can have, that should not be a problem. Just start another "If ... ElseIf" series picking up from the next "target". You have already logically eliminated all the previously tested targets from consideration, and you are only going to hit upon a single "match" either way.

 

You can improve efficiency even further by prioritizing your "target" conditions so the most likely are encountered first in the test chain.

 

There are other ways to accomplish a similar process (walking through an array of "targets" using a "While" loop for instance), but they are just applying the same principle.

 

-Dubious-

Edited by dubiousintent
Link to comment
Share on other sites

For the meat eating, you could add your new effect to their effect list, and it is contingent on the player having the perk and not already affected by that hidden spell you created..

 

Or you can add something to the creatures you want to cover to make them unique to the others, like adding a hammer or an unplayable weapon you create to their inventory. Then you have a single if condition to check in your script. In your case, checking for the faction should suffice though.

 

I would like to know why the JohnnyGuitar function doesn't work. Maybe the author would like to know as well, so they can fix it if it's broken.

Edited by GamerRick
Link to comment
Share on other sites

Hey Dubious, once again thanks for helping me with my mod! I reworked my script like you showed me and it works as intended, though with a slight delay, which I'm assuming because the game processes scripts in ticks? Example:

scn AnimalFoodDebuffQuestUncooked
int iDoOnce
begin gamemode
if Player.hasperk AnimalFriend
	if Player.IsSpellTargetAlt NVGeckoMeat > 0
		If iDoOnce == 0
			player.castimmediateonself AnimalFriendDebuff
			Let iDoOnce := 1
			showmessage AnimalCrueltyMessage
		Endif
	Elseif Player.IsSpellTargetAlt CoyoteMeat > 0
		If iDoOnce == 0
			player.castimmediateonself AnimalFriendDebuff
			Let iDoOnce := 1
			showmessage AnimalCrueltyMessage
		Endif
	Elseif Player.IsSpellTargetAlt BighornerMeat > 0
		If iDoOnce == 0
			player.castimmediateonself AnimalFriendDebuff
			Let iDoOnce := 1
			showmessage AnimalCrueltyMessage
		Endif

[etc...]

	Elseif iDoOnce == 1 
		Let iDoOnce := 0
	Else
		print "Player is not under effects of uncooked meat"
	endif
endif
end

I don't know if it matters, but I decided to split it into three scripts, 1st handling uncooked meat, 2nd cooked meat, and 3rd more uncommon items containing meat and attached them to three separate quests. I think I noticed it kicking a little faster that way, but I might be wrong. Either way I'm really happy with this and very thankful for your assistance, I learned a lot thanks to you.

 

The only minor problem I noticed is that the script takes about 2-5 seconds to recognize that the meat effects have worn out, so if the player consumes another meat item right after the previous' effect ended, the negative debuff won't be applied. But it's not an issue in my case because I extended the debuff's duration to be longer than any food item's. Great stuff.

 

To GamerRick: yeah I wanted to add an effect directly to food items, but I decided against it because it would interfere with JSawyer mod's changes to these food items.

 

As for the JohnnyGuitar thing, maybe it works well for single actors but has trouble processing large form lists? Mine had almost 300 entries in it, so maybe that was the problem, I'm not sure.

Link to comment
Share on other sites

You want to make sure the player doesn't already have your spell, and I don't see the purpose of the DoOnce variable.

 

How about this:

scn AnimalFoodDebuffQuestUncooked
begin gamemode

     if Player.hasperk AnimalFriend && player.IsSpellTargetAlt AnimalFriendDebuff == 0
          if Player.IsSpellTargetAlt NVGeckoMeat > 0
               player.castimmediateonself AnimalFriendDebuff
               showmessage AnimalCrueltyMessage
          Elseif Player.IsSpellTargetAlt CoyoteMeat > 0
               player.castimmediateonself AnimalFriendDebuff
               showmessage AnimalCrueltyMessage
          
[etc...]
          Else
            print "Player is not under effects of uncooked meat"
          endif
     endif
end
Edited by GamerRick
Link to comment
Share on other sites

Even better:

scn AnimalFoodDebuffQuestUncooked
ref effect
int num

begin gamemode
if Player.hasperk AnimalFriend && player.IsSpellTargetAlt AnimalFriendDebuff == 0
     set num to player.GetNumActorEffects
     if num < 1
       return
     endif
     while num > 0
       let num -= 1
       set effect to player.GetNthActorEffect num
       if effect IsInList MyFormListOfMeatEffects
        player.castimmediateonself AnimalFriendDebuff
        showmessage AnimalCrueltyMessage
        Set num to 9999
        break
       endif
     loop
     if num != 9999
       print "Player is not under effects of uncooked meat"
     endif
endif
end
Edited by GamerRick
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...