Debuff applied On Block, or if blocking On Weapon Strike or

Post » Thu May 03, 2012 7:39 am

Hello all!

I am looking for a way to apply a debuff to an enemy that comes in physical contact with my shield.

Say for the sake of the example the shield is enchanted with an entropic energy (think Morgul blade from Lord of the Rings crossed with the sort of reaction you get from being in close proximity to radioactive material!) =P

So, I want to apply the debuff when the enemy's hit actually occurs, if I am currently blocking when it lands, but I don't want to apply it if the enemy is using a ranged attack.

I could probably handle the ranged attack thing with a simple distance check from the player. But the big challenge is, getting a reference variable for the mob that actually caused the hit to register.

I don't know if the engine supports this, but it seems intuitive to assume there would be some event registered when an attack hits the player.

Will continue to search the wiki...

*EDIT: Am thinking OnTrigger might do it, testing...
User avatar
Shiarra Curtis
 
Posts: 3393
Joined: Thu Jan 04, 2007 3:22 pm

Post » Thu May 03, 2012 6:35 am

OBSE Event Handler functions could be your solution. I've successfully used these to cause a "retaliation" of sorts against an enemy who hits the Player. I think you'd be wanting to use an OnHit Event Handler. First, you'd set up the event handler in a script (I did it in a quest script) ...

Scn WhoHitMyShieldQuestScriptRef ThisGuyHitMyShieldBegin Gamemode        If GetGameRestarted                SetEventHandler "OnHit" FunctionWhoHitMyShield        EndifEnd

Instead of GetGameRestarted in a quest script, maybe you can put the SetEventHandler within an OnEquip block script attached to the shield. And then maybe it would work to use RemoveEventHandler within an OnUnEquip block in the same shield object script.

Then this script I wrote above refers to an object script that is not actually attached to any object ...

ScriptName FunctionWhoHitMyShield ;the name comes from the SetEventHandler line above, so it can "find" this object script ....Ref TargetRef Attacker        Begin Function { Target, Attacker }     If Target == PlayerRef		Print $Attacker + " hit the Player" ;optional - great for testing it out though		Set WhoHitMyShieldQuest.ThisGuyHitMyShield to Attacker     EndifEnd

This will get you your ref of the unfortunate chucklehead who hit your shield. You might also just put the debuff inside the Function like this ...

ScriptName FunctionWhoHitMyShieldRef TargetRef Attacker        Begin Function { Target, Attacker }     If Target == PlayerRef		Print $Attacker + " hit the Player"		Attacker.AddSpell MyDebuff		;Or something like this ... I do it all the time ...		aaMyCastingActivator.MoveTo Attacker 10 10 10		aaMyCastingActivator.Cast MyDebuffSpell Attacker     EndifEnd

Maybe you can throw in a Player.IsBlocking condition check (also OBSE) within the Function before you apply the debuff.

Edit:
Using the above method, I was able to cause an enemy who hit me to get struck by lightning by summoning an invisible activator to the attacker, and then having that activator cast a shock spell at it. It works against physical attacks, arrow attacks, but does not seem to work on magic attacks.

Edit 2:
By the way, OBSE might work on your ranged attack check too with GetEquippedObject and GetWeaponType. Type "5" is a bow, so maybe you can try ...

Ref WeaponShort WeaponTypeSet Weapon to Attacker.GetEquippedObject 16 ;"16" refers to the reference's weaponSet WeaponType to GetWeaponType WeaponIf WeaponType == 5      ;then the attacker was using a bowEndif

I've never tried either of these though.
User avatar
Dean Ashcroft
 
Posts: 3566
Joined: Wed Jul 25, 2007 1:20 am

Post » Thu May 03, 2012 1:00 am

Sorry for the delay; busy busy...

Thanks for the illustration! I enjoy a tasty logic cookie with the morning brew! ;)

I like the GetGameRestarted idea for the quest script. Since this is only “registering” the function script that will receive specific event data, we shouldn’t need to do it again during the current play session. Plus if the player saves and exits with the shield equipped, we wouldn't want them to have to un-equip/re-equip it on reload before the script effects would work. (I am assuming these registers are not preserved in the save.)

Just to clarify the OBSE aspects:

It gives us SetEventHandler to “register a function script as a callback to be invoked when an event associated with a block type occurs.”

To my way of thinking, SetEventHandler is essentially pinning a reminder on the game engines’s internal to-do list, telling it to also notify our registered function scripts * whenever certain events occur.

* Below documentation on OBSE functions specifies these function scripts should never be attached to anything, as you pointed out. It also points out these function scripts can only have one block per script, so only one event type (in this case the “OnHit” event) can be sent to a single registered function script.

Next, in our function script which SetEventHandler has now registered to be notified of these OnHit events, you define the function block Begin Function { Target, Attacker } which I see is also OBSE. I found good documentation on this here:

http://cs.elderscrolls.com/constwiki/index.php/User_Functions

I am assuming the reason you are able to call function (target, attacker) and get appropriate reference variables for the actual target and attacker from the game engine is because this type of event is coded to return data in this specific format?

Like a round hole receiving a round peg. I guess that would mean the returned string for the “OnHit” event is an array with two variables?

*EDIT: On the front page of the OBSE site is a link to the documentation, all well formatted on a single page for easy searching! This section specifically covers every event type that can be registered in this way, as well as the format for the data it returns.

http://obse.silverlock.org/obse_command_doc.html#Events

Very useful for numerous scenarios where there is no direct or simple way to ask the game engine for specific information!
User avatar
Kelli Wolfe
 
Posts: 3440
Joined: Thu Aug 23, 2007 7:09 am

Post » Thu May 03, 2012 1:44 am

Just as an aside; I remember recently encountering someone here on the forums asking about what binds a community like this together, for an article they were writing for some online magazine. This to me is what it's all about. The true spirit of Wiki: Ask a question, receive an answer, but an answer in the context of a process of understanding.

Links and applications connect our research instantly with the answers we are seeking, to a purpose which directly accomplishes something we have set out to do. The ultimate answer to "where will I ever use that."

A forum for instant creative gratification which is earned by experience (of knowing what to ask)! It is the exponential evolutionary process of the internet in action! :)

Of course for this magic to work we need generous individuals who are willing to share and publish that process publicly, as well as an environment which encourages them to want and choose to do so.

Of course it's hard to participate in that modern evolution when our society can't free up enough essential resources as entitlements at least to assure you aren't homeless, or completely dependent on employment for survival in the process...

So, human evolution is still hammering out it's greedy little kinks. :gun:
User avatar
KU Fint
 
Posts: 3402
Joined: Mon Dec 04, 2006 4:00 pm

Post » Thu May 03, 2012 7:54 am

I guess that would mean the returned string for the “OnHit” event is an array with two variables?

Yeppers, at least that's what I understand, target and attacker.

Oblivion's modding community rocks! I've gotten so much help with so many things.
User avatar
SamanthaLove
 
Posts: 3565
Joined: Mon Dec 11, 2006 3:54 am

Post » Thu May 03, 2012 2:52 am

I want to say thanks again, your pointers were very helpful, and I was able to achieve exactly what I sought out to. Here is how I ended up doing it:

First, I created a quest with start game enabled checked, and added this as the quest script:

scn phiEventsQS

begin GameMode     if GetGameRestarted          SetEventHandler "OnHit" phiFunctionOnHit          player.additem phiBKshield01a 1          ; this is just to add the shield for testing     endifend

I then put the object script below on the shield itself, which basically adds a magic script effect to the player when the shield is equipped as an ability (passive effect always active), to handle the timers for building back charges of the shield's magic knockback stun effect over time after they've been used.

scn phiBKshield01aSbegin OnEquip     player.addspell phiBKshield01aM     ; adds passive spell to player to rebuild charges when equippedendbegin OnUnEquip     player.removespell phiBKshield01aM     ; removes the above passive charge system when unequippedend

Then the fun part. Here is the function script target of the first event handler quest script:

scn phiFunctionOnHitref targetref attackerbegin Function { target, attacker }     if target == PlayerRef     ; check if the hit character is the player          if player.IsBlocking     ; check if the player had a block up when they were hit               if player.GetEquipped phiBKshield01a     ; make sure the knockback shield is equipped, could have multiple loops for multiple item checks                    if phicharge == 3     ; the shield starts out with 3 charges, defined as a global variable                         if attacker.GetDistance player < 195     ; if the attacker is in mele-ish range, apply the knockback                              player.PlaySound3D splrestorationhit                              attacker.PlaySound3D splfrosthit                              attacker.pms effectFrostDamage 10     ; these three lines just play sounds and visual effects                              player.PushActorAway attacker 25     ; apply the knockback                              set phitimer1 to 1     ; starts the charge cooldown timer counting on the global phitimer which is counted in the player magic effect script.                              set phicharge to phicharge - 1     ; subtract one charge from the current total                              message "Shield lost 1 charge, %.0f remaining!", phicharge, 1     ; briefly notify of event and current charge                              set target to 0     ; reset target so checks won't run until another blocked hit, allowing the loss of charge and other effects to process first                         else                              set target to 0                         endif                    elseif phicharge == 2     ; the next two loops are a repeat of the above as charges are lost                         if attacker.GetDistance player < 195                              player.PushActorAway attacker 25                              player.PlaySound3D splrestorationhit                              attacker.PlaySound3D splfrosthit                              attacker.pms effectFrostDamage 10                              set phitimer2 to 1                              set phicharge to phicharge - 1                              message "Shield lost 1 charge, %.0f remaining!", phicharge, 1                              set target to 0                         else                              set target to 0                         endif                    elseif phicharge == 1                         if attacker.GetDistance player < 195                              player.PushActorAway attacker 25                              player.PlaySound3D splrestorationhit                              attacker.PlaySound3D splfrosthit                              attacker.pms effectFrostDamage 10                              set phitimer3 to 1                              set phicharge to phicharge - 1                              message "Shield lost 1 charge, %.0f remaining!", phicharge, 1                              set target to 0                         else                              set target to 0                         endif                    elseif phicharge <= 0                         set target to 0                    endif               endif          endif     endifend

Finally there is the magic effect script which handles the global variable timers set above, and adds back one charge for each timer after a certain interval:

scn phiBKshield01aMSbegin ScriptEffectUpdate     if phitimer1 > 0     ; won't run unless the timer has been set by the charge use function          if phitimer1 < 10               set phitimer1 to phitimer1 + GetSecondsPassed     ; count up to ten, so in this example, nine seconds pass before a charge is returned.          else               set phicharge to phicharge + 1               message "Shield built 1 charge, %.0f remaining!", phicharge, 1     ; notify of charge gain and total               set phitimer1 to 0     ; reset timer so it isn't checked until the next time that charge level is expended          endif     endif     if phitimer2 > 0     ; repeat for each charge state          if phitimer2 < 10               set phitimer2 to phitimer2 + GetSecondsPassed          else               set phicharge to phicharge + 1               message "Shield built 1 charge, %.0f remaining!", phicharge, 1               set phitimer2 to 0          endif     endif     if phitimer3 > 0          if phitimer3 < 10               set phitimer3 to phitimer3 + GetSecondsPassed          else               set phicharge to phicharge + 1               message "Shield built 1 charge, %.0f remaining!", phicharge, 1               set phitimer3 to 0          endif     endifend

So basically, you start out with three charges, and if something in mele range uses a physical attack against you and you are blocking when it hits, the shield uses one charge to invoke a knockback with some spell visuals, stunning the recipient briefly. Each time you block you lose one charge, until you run out, and blocking works as normal.

After each charge is used an internal cooldown begins counting down nine seconds, after which point the charge is returned and timer reset, sort of like Death Knight runes in wow. Each charge you use has a separate cooldown.

Here's a video of the current shield in action. (Contains Deadly Reflex 5 blood and gore.) You can see how it notifies you how many charges you currently have. This could easily be tweaked into some better UI element, even a behind the scenes swap so the graphical appearance of the shield changes (glow maps possibly) based on how many charges were available.

I was mainly testing this function call method and as you can see, it is totally realtime. I gain a 2nd charge right as two characters hit me simultaneously, and the code is such that it registers each and appropriately sets the charge to zero after they both hit. There is some lag with the message display, but you can see the third guy is hitting me after that and I am blocking normally without charges.

The fire related effects are part of another scripted spell set I made.

http://www.youtube.com/watch?v=HwqANChQlkk

Fun to see the sorts of cool features available this generation of the CS and community of extension projects, as we await the release of Skyrim.
User avatar
Damned_Queen
 
Posts: 3425
Joined: Fri Apr 20, 2007 5:18 pm

Post » Thu May 03, 2012 3:17 am

Dope video! I'm about to start writing my full-blown OnHit Function script. I was worried that it would be a bit long for an OnHit function, but it looks like yours is pretty stout and works fine. Good to know!
User avatar
Your Mum
 
Posts: 3434
Joined: Sun Jun 25, 2006 6:23 pm


Return to IV - Oblivion