TheMrDomino Posted March 26, 2012 Share Posted March 26, 2012 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 More sharing options...
LoginToDownload Posted March 26, 2012 Share Posted March 26, 2012 I'm pretty sure you can't use OnHit like that. I think it can only be applied to ObjectReference scripts, and only runs when the ObjectReference in question is hit. Sorry I can't be of more help. Link to comment Share on other sites More sharing options...
TheMrDomino Posted March 26, 2012 Author Share Posted March 26, 2012 That certainly makes sense. Thanks for the info! I suppose that makes my last question about the OnAnimationEvent() event even more important. Link to comment Share on other sites More sharing options...
SinderionsBones Posted March 26, 2012 Share Posted March 26, 2012 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 More sharing options...
nephlm Posted March 28, 2012 Share Posted March 28, 2012 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 More sharing options...
TheMrDomino Posted March 29, 2012 Author Share Posted March 29, 2012 (edited) 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 March 29, 2012 by TheMrDomino Link to comment Share on other sites More sharing options...
nephlm Posted March 29, 2012 Share Posted March 29, 2012 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 More sharing options...
TheMrDomino Posted March 29, 2012 Author Share Posted March 29, 2012 (edited) 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 March 29, 2012 by TheMrDomino Link to comment Share on other sites More sharing options...
nephlm Posted March 30, 2012 Share Posted March 30, 2012 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 More sharing options...
TheMrDomino Posted March 31, 2012 Author Share Posted March 31, 2012 (edited) 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 March 31, 2012 by TheMrDomino Link to comment Share on other sites More sharing options...
Recommended Posts