Best way to get arrow damage in ActiveMagicEffect script?

Post » Thu May 15, 2014 7:08 am

Currently working on implementing a mod feature that generates an effect (for example, area lightning damage) when the player character lands a ranged attack on an NPC. The mod feature uses an OnHit perk to apply the effect.

Here's how it works:
-A perk listens for ranged OnHit events
-The events apply an intermediate ActiveMagicEffect.
-The intermediate ActiveMagicEffect sets the magnitude for the area lightning damage and applies it to its recipient.

So the fundamental problem is: Figuring out what bow/crossbow and what arrow/bolt caused the intermediate ActiveMagicEffect to be applied.

Preferences:
-I would like the effect to scale with bow damage and arrow damage, but the biggest problem is getting arrow damage.
-I have already posted an open question about getting a character's currently equipped arrows/bolts.

I see 3 ways to implement arrow damage, none of which are foolproof:

#1: Use OnObjectEquipped event on the player. Whenever the player equips a bow, log its damage. Whenever the player equips arrows/bolts, log their damage. Use SetEffectMagnitude to update the lightning effect's damage in the OnObjectEquipped event. The problem with this is that players could cheat by launching an arrow from a weak bow and then switching to a much heavier crossbow immediately after, or use a weak arrow and then immediate switch to stronger arrows. The immediate switch would update the area lightning effect's damage, causing it to do way more damage than intended.

#2: Use the OnPlayerBowShot event to update damage, or a "get last projectile launched" function. This is a better solution than #1 because gear switching can't fool it. However, the problem persists if the player is using incredibly fast/modded bows or an "automatic crossbows" mod. This problem could also occur at very long range. For example, suppose the player launches a weak arrow from a weak bow. While the arrow is traveling, the player pulls out an incredibly fast modded crossbow and instantly launches a super bolt that does over 9000 base damage. The area lightning effect's damage is now over 9000. The weak arrow hits, doing an unintended 9000+ damage, and every other NPC in the area starts booing at the player character for cheating.

#3: Get character's currently equipped arrows/bolts (if such a function exists) in the actual area lightning effect instead of pre-setting the effect damage using events. 2 problems with this: It breaks if the special attack is applied on the last arrow of its type. For example, suppose the player is using a premium bow with 1 expensive (and damaging) ebony arrow. Player launches arrow, arrow applies lightning damage effect, and the "get currently equipped arrows" function returns null. The player has just been shortchanged of 20 base damage. Furthermore, it has the same problem as using OnObjectEquipped because the player can just switch arrows after launching an arrow. Note that the "last equipped arrow" problem can be mitigated using OnObjectEquipped as a backup, but it doesn't prevent gear switching.

Currently I'm thinking of implementing option #2, because it's the most reliable. With default ranged choices in Skyrim (no mods), the player will rarely have multiple active arrows at any time because the standard bows/crossbows are so slow. Also: between mods and in-game functions/commands, if the player really wants to cheat that badly, the player will figure out a way to.

Alternately, anyone have any ideas on a foolproof way to implement this?
User avatar
Jack Bryan
 
Posts: 3449
Joined: Wed May 16, 2007 2:31 am

Post » Thu May 15, 2014 12:24 am

I had a similar challenge when with my Archery Perks mod, specifically the Tracer Shots perk. The issue was identifying the projectile launched by the player.

Here's the code that ended up working:

Scriptname JaxonzArrowsGlowEffect extends activemagiceffectEffectShader Property efsGlow AutoVisualEffect Property vfxLight AutoEvent OnInit()    RegisterForActorAction(6) ;register for Bow Release EndEventEvent OnActorAction(int actionType, Actor akActor, Form source, int slot)     ;the projectile shot is reliably the last projectile in the cell    If akActor == Game.GetPlayer()       ObjectReference objPlayer = akActor as ObjectReference ;Game.GetPlayer()       Cell kCell = objPlayer.GetParentCell()       int iTries = 10       ;it may take a moment for the projectile shot to come into existence       int iProjectilesInCell = kCell.GetNumRefs(50)       While (iProjectilesInCell == kCell.GetNumRefs(50)) && iTries           iTries -= 1           Utility.Wait(0.01)       EndWhile       ;NB: if there are tons of projectiles being shot, we can't 100% guarantee that this is the player's projectile       ObjectReference objProjectile = kCell.GetNthRef(kCell.GetNumRefs(50) - 1, 50)       ;make it glow and light it up!       efsGlow.Play(objProjectile)       vfxLight.Play(objProjectile)       ;track it while in motion       Float fDist = objPlayer.GetDistance(objProjectile)       While objPlayer.GetDistance(objProjectile) != fDist           fDist = objPlayer.GetDistance(objProjectile)           Utility.Wait(0.05)       EndWhile       ;kill the light when motion stops       vfxLight.Stop(objProjectile)   EndIfEndEvent

Important pieces:

  • I found the native OnPlayerBowShot to be more latent than SKSE's OnActorAction event.
  • While one could map between AMMO types (from inventory) and corresponding Projectile objects, DLC and third party mods make this problematic, so just finding the newest created Projectile (= last) in the cell is a 99% reliable way of identifying the arrow/bolt/whatever in flight.
  • My code tracks the projectile until it stops moving, which is not the same as where a hit or collision occurs. You will have to change that, but it will give you X/Y/Z coordinates for where to place your lightning blast.

(Wouldn't it be great if we could just programmatically attach an Explosion to an existing Projectile?) If your mod has magic projectiles, it would be easiest to use CK to add an explosion to those and you could probably get the effect you want without scripting.

One other way that has been suggested to test for arrow impact locations is to have a spell cast a second invisible projectile that uses Arrow rather than Missile for the Projectile Type setting. In this way it should follow the same path as the original projectile. An explosion and impact set as part of that spell could give you the arrow-launched lightning blast effects you want. In practice, however, cast projectiles originate from a different location on the player than those shot with bows, so they are not 100% guaranteed to follow the same flight path.

Hope some of this helps.

User avatar
Emma louise Wendelk
 
Posts: 3385
Joined: Sat Dec 09, 2006 9:31 pm


Return to V - Skyrim