Jump to content

Scripting: several questions for a constant-running script


TheMrDomino

Recommended Posts

I'm working on a script that I'd like to run constantly in the background, monitoring for OnHit() events and performing certain actions when the player attacks certain types of enemies, and then after the event has concluded, to reset those values to their initial value. I've practically no experience with papyrus, and would greatly appreciate some help from those more talented and experienced than I. Here's what I've managed to cobble together so far:

 


ScriptName myScript extends Quest

import Actor
import Game
import Utility

GlobalVariable Property myProperty auto

int initialValue = myProperty.GetValue()
int enabled = 0

Event OnHit(ObjectReference akAggressor)
	if akAggressor == game.getPlayer()
		if game.getPlayer().actor.getCombatTarget().HasKeyword(ActorTypeCreature) == 1
		race targetRace = game.GetPlayer.Actor.GetCombatTarget().GetRace()
		if targetRace == SkeletonRace
			myProperty.SetValue(0)
			Utility.Wait(0.2)
			myProperty.SetValue(initialValue)
	endif
	endif
EndEvent

 

I know that's ugly and probably all very, very wrong. For instance, in the third line of the OnHit() event code chunk, I want to get the type of actor the character is attacking. How can you set which actor the getCombatTarget() function runs on? Moreso, what kind of value does the function return? I'd like to store the value in a variable, but don't know what kind to declare. Int, string, float? The same goes for the GetRace() function, because I need to use the return value in a conditional statement, but don't know how to format the second half. As you can see, for now I just have the string 'SkeletonRace'.

 

The event GetCombatTarget(), will it not trigger if the player isn't currently in combat? Say, a stealth character is initiating combat, will that first attack still trigger the function?

 

Right now I've got the idea to attach the script to a dummy quest in order to auto-launch it when the player loads the save. The script, though, needs to be running in the background, constantly looking for OnHit() events. How could I set that up?

 

Finally, would it be possible to run this script off of the OnAnimationEvent() event? Specifically, the ActorRightAttack and ActorRightPowerAttack animation nodes? This script would likely be much more efficient if it instead ran only when the player attacked rather than constantly scanning for when actors are hit.

Link to comment
Share on other sites

I'm not gonna test it myself, but this sounds like something you *might* be able to implement with a hidden perk. There are some options I believe allow you to specify actions based on conditions and events from the players end. that'll take some digging to figure out tho
Link to comment
Share on other sites

I haven't written a single line of papyrus, but I am paid to code out in the real world so take everything I say with that grain of salt.

 

My understanding is you want to insert your code in the actor or objectreference onHit event.

 

if you do that every time anyone gets hit you'll get the execution pointer. From the actor script you can ask am I an skeletonRace and was the attacker the player. All you do in the sample script is set a variable for .2 seconds. I suspect you actually want to set a global. I was looking at bookshelf code last night and they set a global variable the the first time you touch a playerbookshelf. I suspect you want to do something like this, however instead of resetting it after .2 seconds I suspect you have something else that you want to fire. Whatever it is should probably be responsible to resetting the variable

 

However Papyrus seems to an event driven object oriented language, so setting a variable doesn't seem like the correct action. The OnHit event you care about gives you player, I'd think about whether your can accomplish your real goal by following the player reference.

 

A better solution then modifying actor would be to create a new script that extend objectreference that you attach to the skeletons to minimize conflicts with other mods. If you can avoid modifying vanilla scripts that is probably a good idea.

 

Those are my thoughts without a real sense of what your ultimate goal is.

Link to comment
Share on other sites

Thanks for the tip SinderionsBones, that sounds promising.

 

@nephlm:

 

I should better explain the situation. I have a global variable that controls whether killmoves are enabled or not. What I want to happen is, when attacking certain monsters, for that variable to be changed to 0, and then after a short delay, to be changed back to its initial value. This is to overcome (what I think is) a bug that manifests when you change the killmove frequency and remove last enemy requirements. To that end, after looking through the various events, onhit seemed like the best trigger to encapsulate this code with. While I do think onAnimation would be a better solution, I don't know that it's possible. Ultimately I'm not dedicated to any particular method, and am wide open to suggestions.

 

If I'm understanding it correctly, I like your idea of attaching the onhit event script to the creatures, as it seems that would simplify things greatly. Much simpler, much more straightforward.

Edited by TheMrDomino
Link to comment
Share on other sites

Yes, the idea is to attach the script to creature being hit and hook onHIt. I'm not sure what order of the various events are called, but you may be able to use onDeath given your description.

 

One thing you may want to watch out for is as I'm hacking away with my dagger their will be a steady stream on onHIt events that cause the global variable to be set. Make sure the delay before unsetting is shorter than the next possible blow (even when dual wielding) or you may unset immediately after setting. Unless you do something a little more complicated you have a race condition, though I can't say whether it is one that matters.

Link to comment
Share on other sites

The strange thing is, the bug only occurs on two-handed weapons (though I have some reports of H2H experiencing it as well), so the delay should be sufficient. Though the delay is longer because initially the script would have to cycle through a long list of races, so I wanted to give it sufficient time to complete. Now it shouldn't be an issue, I can set it fast enough to cover multiple attacks from any weapon.

 

Edit:

 

I have a question: if I'm adding this script to an actor, would the script have to extend Actor? Or could I have it extend ObjectReference? I guess the real question is can the actor be considered an object for the purpose of extending the script.

Edited by TheMrDomino
Link to comment
Share on other sites

It is my understanding that actors are ObjectReferences so any actor can be cast as an object reference. Given that, extending either should be synonymous to your needs. I don't think actor has any code it didn't inherit from ObjectReference so I'm not sure it matters which you extend. I would extend actor because that fits how I think about such things, but I'm not sure it makes much of a difference from a practical level.
Link to comment
Share on other sites

I've got a basic script set up, but it refuses to compile, giving me odd errors.

 

The code:

Scriptname myscript extends ObjectReference

GlobalVariable Property killmoveEnabled  Auto  

import Utility
import Debug

int initialValue = killmoveEnabled.GetValue()

Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
if initialValue == 1
	debug.Notification("Onhit has triggered")
	killmoveEnabled.SetValue(0)
	debug.Notification("Killmove Disabled")
	Utility.Wait(0.1)
	debug.Notification("Game waited for .1 seconds")
	killmoveEnabled.SetValue(initialValue)
	debug.Notification("Killmove reset to initial value")
endif
EndEvent

 

The errors:

(8,19): no viable alternative at input 'KillMove'
(8,27): required (...)+ loop did not match anything at input '.'
(8,4): Unknown user flag KillMove
No output generated, compilation failed.

Batch compile of 1 files finished. 0 succeeded, 1 failed.

 

If I replace all the instances of KillMove.GetValue()/KillMove.SetValue() and just enter in constants, the script compiles just fine. I've connected the property with a global in the CK, double-checked that. I've used this exact same syntax for other scripts editing other global variables in the same manner and they've worked splendidly. I'm sure it's something obvious, but I can't figure out what I'm doing wrong.

Edited by TheMrDomino
Link to comment
Share on other sites

  • Recently Browsing   0 members

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