Trying to make a spell reflect script

Post » Mon Nov 19, 2012 9:55 pm

Basically, and I know this isn't really balanced but it's mostly for educational purposes, I'm trying to make a perk that reflects any spell the player is hit with back at the target that cast it. The idea here is similar to spell reflect in WoW (If anybody here has played that) except constant and no limit to the number reflected.

These are my basic goals with it:
  • The spell is reflected directly back at the person who cast it at me. By this I mostly mean I get hit by the spell (I take damage or not, doesn't matter), that same spell is cast back at the person who hit me initially.
  • The reflected spell should cost me no mana, and give me no skill bonus for using it.
  • Channeled spells should only cast back at the target with the channel time that was used on me. Basically, when they stop channeling it stops reflecting.
  • Doesn't interfere with my normal movement/gameplay.
I know somebody else made a mod like this, but they didn't release the source code to the script so I can't look at it for tips, and that's the only mod like this I've been able to find. Like I said, mostly trying to learn here, not really aiming for balance or anything like that.

This is the code I'm using right now:
Scriptname corePassiveReflectSpells extends ActiveMagicEffect{Passive non-ward script to reflect any spell that hits the player.}Import StringUtilEvent OnHit(ObjectReference akAgressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked);Debug.MessageBox("Hit by " + akAgressor + " using " + akSource + " and " + akProjectile)ObjectReference pcRef = Game.GetPlayer() as ObjectReferenceif akAgressor != pcRef  Int sourceType = akSource.GetType();  if sourceType == 22 || sourceType == 119   Spell recast = akSource as Spell   recast.Cast(pcRef, akAgressor)    EndIfEndIfEndEvent

The problems I'm facing are these:
The major ones:
  • The spell is not actually reflected back at the target doing this. Instead, it launches the spell from my face (aimed at the first person reticle). In fact it seems to ignore the target argument alltogether.
  • The spell cast consumes my mana.
  • Channeled spells, such as flames, simply keep channeling after the attacker casts it until I either run out of mana or switch weapons (activate my block, attack, etc).
  • Every time I'm hit by a spell, OnHit seems to be called for every effect on the spell. For instance, i was having IceSpikeLeftHand (form id 40000) cast at me by a hagraven. For each of the 3 effects on it OnHit was called. This causes me to recast the spell not just once, but once for every effect (I launched 3 spikes in that example). While that was cool looking, it is not what I'm after.
The minor problems:
  • Channeled spells continue casting which prevents me from jumping, because I'm casting.
  • Because it's me casting the spell, I'm actually leveling off of the reflects when I hit people with it.
Is there perhaps another route I need to be going to accomplish this? I have been trying to do it for the better part of today and I just can't seem to find any methods in the Papyrus or SKSE documentation that would help me out here.

I've tried changing it to RemoteCast(pcRef, akAgressor as Actor, akAgressor) and while it did seem to recast the spell at the enemy that hit me with it, it still consumed my mana and also launched the spell effect from my face where the first person reticle was aiming (Even if the person was behind me).

Thanks in advance for any help anybody can provide :smile:

Edit: Also I should specify, just in case it matters, this is attached to a constant Magic Effect that that is applied to a constant effect spell I gave to myself.
Edit 2: Edited for typo.
User avatar
Kahli St Dennis
 
Posts: 3517
Joined: Tue Jun 13, 2006 1:57 am

Post » Tue Nov 20, 2012 2:21 am

I will actually be writing a script soon with the same function but for a different purpose. I'll point out somethings I noticed.

Scriptname corePassiveReflectSpells extends ActiveMagicEffectActor Property player Auto ;;; Define this as the player in "any" cell, this is the most efficient way to do it.Event OnHit(ObjectReference akAgressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)  If akSource.GetType() == 22 || akSource.GetType() == 119	If akAgressor != player	  Spell reCast = akSource as Spell ;; not sure what this will do to shouts	  reCast.Cast(player, akAgressor)	EndIf  EndIfEndEvent

Now that doesn't help much with your issues. But I wanted to do that first to break it down my self.
The spell is not actually reflected back at the target doing this. Instead, it launches the spell from my face (aimed at the first person reticle). In fact it seems to ignore the target argument alltogether.
This is from the wiki:
"Actor races can be set to cast magic only in the direction that the actor is facing; if the source is an actor with this racial setting, this parameter will be ignored."
I'm not 100% what there saying here but it sounds like your problem. Perhaps you can make an invisible object that appears at the player that will do a remote cast and blame it on you. You can also try out DoCombatSpellApply, I've never tried that before.
The spell cast consumes my mana.
Looks like they changed how the cast function works. It used to not consume mana. You can do one of two things. You can get the mana pool before and after the cast then replace the mana, but this will require some temporary mana. Or you can use the function GetMagickaCost() on the spell and add the needed mana beforehand. I don't believe this gives the exact mana cost and I'm not sure how it works on channeled spells.
Channeled spells, such as flames, simply keep channeling after the attacker casts it until I either run out of mana or switch weapons (activate my block, attack, etc).
You could just use DispelSpell() after a set amount of time. Another method that may work is to find all the magic effects associated with the spell. Then compare those effect either against a formlist you build or a keyword. If it is a concentration spell, you cast it until HasMagicEffect no longer passes. The you dispel it. Not sure if that will work.
Every time I'm hit by a spell, OnHit seems to be called for every effect on the spell.
There is a way of fixing this I think may work. Basically you put OnHit in two different states. One fills an effect array by checking the effects associated with the spell and cast it, then it switches to the other state. The other state is an empty onhit command. You then use OnMagicEffectApply to check for each effect in the array then remove that effect as it passes. Once the array is empty again you switch back to the original state. Not sure how the timing of this would work. I can think of a few other crude ways to over come this.

I'm not sure what to do about the leveling issue, but where there's a will there's a way. Let me know if you make any progress on this. Like I said earlier, I will be attempting to do the same thing soon but for another purpose. If your still stuck I'll be writing the code for all this probably next weekend. Might be able to help each other. I know the explanations above can be hard to understand or put to use with out any real code.
User avatar
Jessica Colville
 
Posts: 3349
Joined: Wed Oct 18, 2006 6:53 pm

Post » Tue Nov 20, 2012 5:14 am

I will actually be writing a script soon with the same function but for a different purpose. I'll point out somethings I noticed.

Scriptname corePassiveReflectSpells extends ActiveMagicEffectActor Property player Auto ;;; Define this as the player in "any" cell, this is the most efficient way to do it.Event OnHit(ObjectReference akAgressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)  If akSource.GetType() == 22 || akSource.GetType() == 119	If akAgressor != player	  Spell reCast = akSource as Spell ;; not sure what this will do to shouts	  reCast.Cast(player, akAgressor)	EndIf  EndIfEndEvent

Now that doesn't help much with your issues. But I wanted to do that first to break it down my self.

This is from the wiki:
"Actor races can be set to cast magic only in the direction that the actor is facing; if the source is an actor with this racial setting, this parameter will be ignored."
I'm not 100% what there saying here but it sounds like your problem. Perhaps you can make an invisible object that appears at the player that will do a remote cast and blame it on you. You can also try out DoCombatSpellApply, I've never tried that before.

Looks like they changed how the cast function works. It used to not consume mana. You can do one of two things. You can get the mana pool before and after the cast then replace the mana, but this will require some temporary mana. Or you can use the function GetMagickaCost() on the spell and add the needed mana beforehand. I don't believe this gives the exact mana cost and I'm not sure how it works on channeled spells.

You could just use DispelSpell() after a set amount of time. Another method that may work is to find all the magic effects associated with the spell. Then compare those effect either against a formlist you build or a keyword. If it is a concentration spell, you cast it until HasMagicEffect no longer passes. The you dispel it. Not sure if that will work.

There is a way of fixing this I think may work. Basically you put OnHit in two different states. One fills an effect array by checking the effects associated with the spell and cast it, then it switches to the other state. The other state is an empty onhit command. You then use OnMagicEffectApply to check for each effect in the array then remove that effect as it passes. Once the array is empty again you switch back to the original state. Not sure how the timing of this would work. I can think of a few other crude ways to over come this.

I'm not sure what to do about the leveling issue, but where there's a will there's a way. Let me know if you make any progress on this. Like I said earlier, I will be attempting to do the same thing soon but for another purpose. If your still stuck I'll be writing the code for all this probably next weekend. Might be able to help each other. I know the explanations above can be hard to understand or put to use with out any real code.


Hey! Thanks for the detailed reply !

I'm not actually sure how I missed the part in that wiki about the spell going where the actor is facing. I read that block several times lol. Thanks for pointing that out.

So, I was reading your reply this morning on my tablet and noted the first suggestion of spawning an invisible. I realized that this actually might solve a lot of problems. For starters, it has the advantages of:
  • It's an object we can control the location of, and delete when ready. Being able to be deleted, this actually has another added advantage of causing it to not sit there and channel forever.
  • The object would be casting the spell and blaming me, it probably won't use my mana (And doesn't after testing).
  • It's an object an not an actor and, like you said, doesn't suffer from the restriction of having to fire where I'm facing.
So here's what I changed so far, taking into account your advice on the player reference (Thanks for that, btw), also any input on how this could be done better is welcome. (Also got the code block to properly tab this \o/)
Scriptname corePassiveReflectSpells extends ActiveMagicEffect{Passive non-ward script to reflect any spell that hits the player.}Actor Property player AutoImport StringUtilEvent OnHit(ObjectReference akAgressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)	;Debug.MessageBox("Hit by " + akAgressor + " using " + akSource + " and " + akProjectile)		if akAgressor != player			Int sourceType = akSource.GetType();		if sourceType == 22 || sourceType == 119			Spell recast = akSource as Spell			if recast.IsHostile()				; Let's spawn an invisible object at the player from which we can cast the reflected spells.				Form invisibleObjectForm = Game.GetForm(0x00109AC3) ; I'm going to make this a proper proptery in a bit. Testing for now first.										    ; Also need a better "invisible" object. This technically shows up for a min.										   ; Also noting that this is the form named MAGINVInvisibility static object.				ObjectReference invisibleObject = player.PlaceAtMe(invisibleObjectForm)				invisibleObject.MoveTo(player, 30.0, 30.0, 150.0) ; So, the object was too close to me and somewhat in the floor.										   ; When I got hit by something like a fireball, the reflected fireball										   ; hit me or the floor. This seems to be fixing it pretty good so far.				recast.RemoteCast(invisibleObject, player, akAgressor)				Utility.Wait(1) ; Keep the object spawned for a second so it actually has time to channel something meaningful.				invisibleObject.Delete() ; Delete the object, we don't want to litter or cause turrets or endless channeling.			EndIf		EndIf	EndIfEndEvent


So, this seemed to fix most of my problems. The leveling still exists from what I can tell, but honestly it doesn't bother me that much. I could probably fix the leveling by changing it to a Cast instead of RemoteCast (I don't really care about getting credit, unless there's some benefit I'm not realising).

OnHit still seems to get called, but I would expect this (We're just spawning an object, and not doing anything to throttle that to once per actual spell instead of once per magic effect). I'm not exactly sure how to implement the suggestions you had, however from my understanding a form list and a keyword check, these may make this not take into account new spells from mods since the creator may not add keywords that I use or I would have to add their spell to the list and create a compatability patch of sorts containing the new form list. If possible I'd really prefer it to reflect pretty much any damaging spell that hits me (Which reminds me, that is another thing I actually need to check for, IsHostile, but I'll add that in a bit. It looks easy enough).

Another bug that seems to have come about is the fact that sometimes when stuff gets channeled at me a lot and this is triggered a lot from it, the sound of the spell gets stuck where the object was even after it's gone.

The next thing is, I don't really know how to create a "true" invisible object. Right now this shows up as a cool purple diamond shaped thing for a second. I'm also not sure if moving the object is the best way to overcome the apparent collisions it has. For instance, if I'm in a tight spot and it moves up into the ceiling, it's going to be pretty worthless and may even splash damage myself. Now, I'm fine with it casting the spell at where the NPC was and it hits an object on the way (for instance it was hitting poles and stuff between me and the person attacking me because the NPC was moving - but this is fine and makes sense). It's mostly when it hits me with it because it's technically inside of me, or hits the floor because it's too low, etc.

Right now this object spawns, gets placed just a little bit above and to the side of me, casts the spell and is deleted after a second.

Thanks again for your reply.

Also this has the hilarious side effect of causing NPCs to almost instantly get hit by a powerful spell, making them flee yelling. But fairly sure that's just due to combat mechanics and my char being pretty powerful. I think the spell might even be affected by my skills, which means they would be hitting me with it, it gets amplified by about 5x (Other things I'm testing / messing with) and sent back to them.

Edit: Updated with the IsHostile() check. I should probably check to make sure the NPC is actually in combat with me too, I'm not sure if there are quests out there that cast spells at me, but I don't want to reflect something back at a quest NPC and cause a problem.


Edit2: So I got the chance to try this on a dragon... Yeah, it spawned a ton of the objects, created this wall of fire breath that pretty much made seeing anything else impossible and left this really loud sound effect hovering in the world. It also didn't appear to do all that much damage to the dragon (Where the mages that were hitting me were almost getting insta-killed). I also had this effect applied to a ward spell about two days ago using OnWardHit and it did quite a bit of damage to the dragon doing it that way. This seemed like it was missing and aiming in the wrong direction sometimes.

It would appear the next big thing to really fix is the OnHit spam.


Edit3: The sound bug doesn't persist through saves, thankfully.

Edit4: Fixed some alignment issues with the post and the comments in my code.
User avatar
Alex [AK]
 
Posts: 3436
Joined: Fri Jun 15, 2007 10:01 pm

Post » Tue Nov 20, 2012 5:06 am

I've got this book marked and I'll look at it again before too long. Sounds like your making good progress. For now you probably want to put a boolean guard on the event so it doesn't run multiple time at once. This may not completely fix the issue but i will help alot. With the guard and the wait variable you already have you can get a rough handle on the spamming.

The guard works like this if your unfamiliar:
bool property Reflecting = False autoEvent OnHit(etc etc)  if akAgressor != player	Int sourceType = akSource.GetType();	if sourceType == 22 || sourceType == 119	  If !Reflecting		Reflecting = True		...		Reflecting = False	  endif	endif  endifEndEvent
User avatar
Franko AlVarado
 
Posts: 3473
Joined: Sun Nov 18, 2007 7:49 pm

Post » Tue Nov 20, 2012 7:27 am

I've got this book marked and I'll look at it again before too long. Sounds like your making good progress. For now you probably want to put a boolean guard on the event so it doesn't run multiple time at once. This may not completely fix the issue but i will help alot. With the guard and the wait variable you already have you can get a rough handle on the spamming.

The guard works like this if your unfamiliar:
bool property Reflecting = False autoEvent OnHit(etc etc)  if akAgressor != player	Int sourceType = akSource.GetType();	if sourceType == 22 || sourceType == 119	  If !Reflecting		Reflecting = True		...		Reflecting = False	  endif	endif  endifEndEvent

Thanks for the input, I will definitely try that. That should help with the spam for sure.

I will continue working on it, I'm going to have to find some way to make an invisible object to launch these from (Never done that before) but I'll probably have a look around for that later tonight or tomorrow morning.
User avatar
Steven Nicholson
 
Posts: 3468
Joined: Mon Jun 18, 2007 1:24 pm

Post » Mon Nov 19, 2012 10:43 pm

Alright, so I've been testing various different things.

Scriptname corePassiveReflectSpells extends ActiveMagicEffect{Passive non-ward script to reflect any spell that hits the player.}Import StringUtilActor Property player AutoBool Property isReflecting = false AutoForm Property invisibleObjectForm AutoEvent OnInit()    invisibleObjectForm = Game.GetForm(0x00109AC3)EndEventEvent OnHit(ObjectReference akAgressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)    if akAgressor != player            Int sourceType = akSource.GetType()        if sourceType == 22 || sourceType == 119            Spell recast = akSource as Spell            if recast.IsHostile() && !isReflecting                isReflecting = true                ; Let's spawn an invisible object at the player from which we can cast the reflected spells.                ObjectReference invisibleObject = player.PlaceAtMe(invisibleObjectForm)                invisibleObject.MoveTo(player, 30.0, 30.0, 150.0) ; Move the object so it's not stuck inside of the player                recast.RemoteCast(invisibleObject, player, akAgressor)                Utility.Wait(5) ; Keep the object spawned for a few seconds so it actually has time to channel something meaningful.                invisibleObject.InterruptCast() ; Interrupt the objects casting, because channeled spells                invisibleObject.Delete() ; Delete the object, we don't want to litter                ;Utility.Wait(5) ; Doing this to see if we can make the sounds not bug into a location                isReflecting = false            EndIf        EndIf    EndIfEndEvent

This seems to do a good job of throttling the OnHit spam, however, the sound bug is still there. It's only for channeled spells. Basically, the object spawns, starts channeling something then stops and gets deleted. The sound effect from the channel still lingers there and I can't figure out how to make it stop. For instance, a dragon was hitting me with flame breath. From what I could tell only one of these spawned, channeled for a sec, and then when it was deleted the sound of the dragon flame breath stayed there. It's not persistent through saves.

So far the only other real problem is the fact that when the dragon breath (Not sure what else it does it with yet) hits me, this thing channels it at the floor in front of it some and doesn't see to actually hit the dragon.
User avatar
Schel[Anne]FTL
 
Posts: 3384
Joined: Thu Nov 16, 2006 6:53 pm

Post » Tue Nov 20, 2012 5:15 am

Well, I think I actually might have a fix for the sound. I was thinking about it and wondered if maybe the object was getting deleted too quickly causing the sound to mess up. So, I changed it so the object stays casting for 2.5s, the spell is interrupted and then there is a 1s wait before it's Delete() method is called:
Scriptname corePassiveReflectSpells extends ActiveMagicEffect{Passive non-ward script to reflect any spell that hits the player.}Import StringUtilActor Property player AutoBool Property isReflecting = false AutoForm Property invisibleObjectForm AutoEvent OnInit()    invisibleObjectForm = Game.GetForm(0x00109AC3)EndEventEvent OnHit(ObjectReference akAgressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)    if akAgressor != player            Int sourceType = akSource.GetType()        if sourceType == 22 || sourceType == 119            Spell recast = akSource as Spell            if recast.IsHostile() && !isReflecting                isReflecting = true                ; Let's spawn an invisible object at the player from which we can cast the reflected spells.                ObjectReference invisibleObject = player.PlaceAtMe(invisibleObjectForm)                invisibleObject.MoveTo(player, 30.0, 30.0, 150.0) ; Move the object so it's not stuck inside of the player                recast.RemoteCast(invisibleObject, player, akAgressor)                Utility.Wait(2.5) ; Keep the object spawned for a few seconds so it actually has time to channel something meaningful.                invisibleObject.InterruptCast() ; Interrupt the objects casting, because channeled spells                Utility.Wait(1)                invisibleObject.Delete() ; Delete the object, we don't want to litter                ;Utility.Wait(5) ; Doing this to see if we can make the sounds not bug into a location                isReflecting = false            EndIf        EndIf    EndIfEndEvent


I fought a dragon just now and it seemed to handle fine. Being able to actually see it now, it looks like it only occasionally sprays the flame at the ground.
User avatar
Strawberry
 
Posts: 3446
Joined: Thu Jul 05, 2007 11:08 am

Post » Tue Nov 20, 2012 10:00 am

Really looking/sounding good =]
The spray at the ground sounds like it may be unavoidable. Is it possibly a line of sight issue?
Do you think spamming is still a problem or is that rudimentary fix working good enough? Was thinking, that Bool guard may be best to put as the first check in the event. No point in going through all the other code just to cancel it.

I'm going to load this up soon to see what it looks like and get a handle on any bugs. Sounds like fun =D
User avatar
Alyce Argabright
 
Posts: 3403
Joined: Mon Aug 20, 2007 8:11 pm

Post » Tue Nov 20, 2012 4:03 am

Really looking/sounding good =]
The spray at the ground sounds like it may be unavoidable. Is it possibly a line of sight issue?
Do you think spamming is still a problem or is that rudimentary fix working good enough? Was thinking, that Bool guard may be best to put as the first check in the event. No point in going through all the other code just to cancel it.

I'm going to load this up soon to see what it looks like and get a handle on any bugs. Sounds like fun =D


Actually I've changed it more since then a little. The OnHit spam was actually a minor issue except for the fact that it seemed to leave behind areas of really loud sound. However, I think the object was getting deleted so fast it didn't have time to cancel the sound effect. The 1s wait between interrupting the spell and deleting the object seems to fix that problem. I haven't had it since.

I changed the code to this:
Scriptname corePassiveReflectSpells extends ActiveMagicEffect{Passive non-ward script to reflect any spell that hits the player.}Import StringUtilActor Property player AutoBool Property isReflecting = false AutoForm Property invisibleObjectForm AutoEvent OnInit()	;invisibleObjectForm = Game.GetForm(0x00109AC3)	invisibleObjectForm = Game.GetForm(0x00000005)EndEventEvent OnHit(ObjectReference akAgressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)	if akAgressor != player			Int sourceType = akSource.GetType()		if sourceType == 22 || sourceType == 119			Spell recast = akSource as Spell			if recast.IsHostile() ;&& !isReflecting				;isReflecting = true				; Let's spawn an invisible object at the player from which we can cast the reflected spells.				ObjectReference invisibleObject = player.PlaceAtMe(invisibleObjectForm)				invisibleObject.MoveTo(player, 30.0, 30.0, 150.0) ; Move the object so it's not stuck inside of the player				;invisibleObject.MoveTo(player as ObjectReference, 0.0, 0.0, 150.0) ; Move the object so it's not stuck inside of the player				recast.RemoteCast(invisibleObject, player, akAgressor)				Utility.Wait(2.5) ; Keep the object spawned for a few seconds so it actually has time to channel something meaningful.				invisibleObject.InterruptCast() ; Interrupt the objects casting, because channeled spells				Utility.Wait(1)				invisibleObject.Delete() ; Delete the object, we don't want to litter				;Utility.Wait(5) ; Doing this to see if we can make the sounds not bug into a location				;isReflecting = false			EndIf		EndIf	EndIfEndEvent

Still working on it, but I removed the isReflecting check all together to see and the sounds don't get left behind. I also changed to an actual invisible object (I think it's a marker, but it seems to work fine. I might duplicate it later just in case there are any conflicts with it being that particular thing.)


However, what does a pretty good job of reducing the OnHit spam is staying isReflecting long enough for all the effects to go off. The only thing I didn't like about this is the fact that it limits how many people it can fire stuff at simultaneously.

As for the ground thing, I think it might be a combination of sometimes being a range/los issue, and others me standing in the pools of fire left behind by spells like flames / dragon breath. The fire is probably getting launched at the ground based on the fact that those are hazards on the ground.

I'm still workign on it and will continue to post updates / welcome feedback, but been kind of busy lately so it slowed me down some.

Anyway, thanks for your help!

Edit: also, it looks pretty cool.
User avatar
Jynx Anthropic
 
Posts: 3352
Joined: Fri Sep 08, 2006 9:36 pm

Post » Tue Nov 20, 2012 8:07 am

I use FXEmptyActivator for all my spell effects that require a remote casting point, including spell reflection on a couple of my boss monsters. It's very versatile and it can be set with a property, which is faster than GetForm. I also encountered the stuck-sound feature on concentration-type spells, and fixed more-or-less it the same way you did, by making sure InterruptCast gets called before the object is disabled or deleted.
User avatar
Euan
 
Posts: 3376
Joined: Mon May 14, 2007 3:34 pm

Post » Tue Nov 20, 2012 3:40 am

I use FXEmptyActivator for all my spell effects that require a remote casting point, including spell reflection on a couple of my boss monsters. It's very versatile and it can be set with a property, which is faster than GetForm. I also encountered the stuck-sound feature on concentration-type spells, and fixed more-or-less it the same way you did, by making sure InterruptCast gets called before the object is disabled or deleted.

Thanks for the info with that, I'll probably switch to that myself then.

Yeah, I used to have it just doing interrupt then delete, but the 1s delay seems to always ensure the spell stops before the object is deleted.

I'm also going to add a check to make sure akAgressor is an Actor type, that might fix the firing at the floor bug.


Edit: If I may ask, how do I set FXEmptyActivator with a property instead of using GetForm? I can't seem to find a spot to make it reference that form without using GetForm.

Edit: Just got it, had to set it as type Activator for it to show up in the properties dropdown in the CK (Most of this stuff I write by hand in notepad++, not used to dealing with the CK properties interface)
User avatar
No Name
 
Posts: 3456
Joined: Mon Dec 03, 2007 2:30 am

Post » Mon Nov 19, 2012 10:29 pm

Yeah, remembering to set Properties is probably my #1 source of "why isn't this working?"-type failures. You not only have to set it, but you have to Ok your way out of the Properties dialog, the Spell Effect dialog and THEN hit Save for it to work :P
User avatar
BethanyRhain
 
Posts: 3434
Joined: Wed Oct 11, 2006 9:50 am

Post » Tue Nov 20, 2012 12:33 am

Yeah, remembering to set Properties is probably my #1 source of "why isn't this working?"-type failures. You not only have to set it, but you have to Ok your way out of the Properties dialog, the Spell Effect dialog and THEN hit Save for it to work :tongue:


lol yeah. I actually just tried to test some changes I made and forgot to save the esp with the magic effect which set the property to refer to that object. Sadly I realized that after about 10 minutes of adding Debug.MessageBox calls and recompiling / reloadscript console commands, lol.


Well, I changed it and cleaned it up some. Moved the isReflecting check to the top because, as spinner pointed out, it doesn't need to run all of that code first. Also moved the comments to the top of the function, and added a check to ensure the agressor is an NPC / Character.

Scriptname corePassiveReflectSpells extends ActiveMagicEffect{Passive non-ward script to reflect any spell that hits the player.}Actor Property player AutoBool Property isReflecting = false Auto{Allows us to throttle the OnHit spam. The only downside is it limits the number of reflects this can do at any given time}Bool Property weCareAboutThrottlingOnHit = false Auto{Let's me disable OnHit throttling using the isReflecting safeguard}Activator Property invisibleObjectForm Auto; How this works:; * Make sure the agressor isn't the player; * Get the form type of the agressor and the source and check that the agressor is an NPC and the source is a spell/shout; * Ensure the spell is hostile; * Spawn an invisible object (FXEmptyActivator) near the player to recast the spells at the NPC who initially hit us; * Keep the object casting for a few seconds so it can do something meaningful with channeled spells.; * After a few seconds, interrupt the spell cast and then wait a second to ensure the spell cast is fully stopped (This prevents sounds getting stuck in the environment); * Finally, delete the invisible object.; Optional check for isReflecting to try and throttle any OnHit spam that may occur.Event OnHit(ObjectReference akAgressor, Form akSource, Projectile akProjectile, Bool abPowerAttack, Bool abSneakAttack, Bool abBashAttack, Bool abHitBlocked)	if !isReflecting || !weCareAboutThrottlingOnHit		Int agressorType = (akAgressor as Form).GetType()		if akAgressor != player	&& (agressorType == 43 || agressorType == 62)			Int sourceType = akSource.GetType()			if sourceType == 22 || sourceType == 119				Spell recast = akSource as Spell				if recast.IsHostile()					isReflecting = true					ObjectReference invisibleObject = player.PlaceAtMe(invisibleObjectForm as Form)					invisibleObject.MoveTo(player, 30.0, 30.0, 150.0)					recast.RemoteCast(invisibleObject, player, akAgressor)					Utility.Wait(2.5)					invisibleObject.InterruptCast()					Utility.Wait(1)					invisibleObject.Delete()					isReflecting = false				EndIf			EndIf		EndIf	EndIfEndEvent

The optional throttle check lets me disable it on things I don't want it to be on. So far this seems to be working nicely, pools of fire on the ground no longer trigger the reflect which seems to be fixing the fire breath aimed at the floor problem (Though due to the angle of the dragon and the fact that it's moving almost constantly, the reflect is kind of pointless unless the dragon lands and does it when he's stationary - but that's to be expected.)

The sound bug appears to be gone and, thanks to Verteiron, I have a proper invisible object to use for it. I'll probably implement this into a perk at some point. Who knows, I could add some kind of a roll check to make it interesting (25% chance to reflect hostile spells kind of a thing) in the destruction tree. Later on I was planning on adding some kind of cool effect where it detects melee weapons as well and has a chance to do a single-target knockback spell on the actor who hits me with one.

Anyway, thanks for the help! I'll keep checking here of course, and will update the script. Of course, feel free to use it (In case anybody is wondering if they can.)


Edit: I do have a side question though. Would anybody have any idea on how to make a 360degree egg-shaped ward? I'd like to make a ward spell that can actually enclose the whole player in it, rather than just a frontal area.
User avatar
SEXY QUEEN
 
Posts: 3417
Joined: Mon Aug 13, 2007 7:54 pm

Post » Mon Nov 19, 2012 7:09 pm

I've experimented with placing hollow havok objects around the player (the Eye of Magnus barriers work great for this!). The main things you have to watch for are:
  • The camera: If the object you're using is on the ground layer (i.e. the game considers it a blocking barrier like a wall) the camera will cram itself between the player and the barrier in 3rd person mode. This can be mitigated by editing the NIF to put the havok mesh on the Clutter layer. It will still block projectiles and npcs but the camera will ignore it. The downside of this is that it makes your barrier immobile; more on that later.
  • The enemy AI: If you have a barrier that completely surrounds the player, enemy NPCs go nuts. Sometimes they run away, sometimes they go into "lost" mode, sometimes they get stuck running into the barrier. Fixing this involves randomly removing the barrier for very brief intervals and generating almost continuous detection events. It's a hassle and it took me a long time to get it working properly.
  • Movement: Most havok meshes don't play well when it comes to being moved. Even if you use Translate functions to make the barrier follow and stay with you, unless it's on a specific layer (I believe L_BOX_MOVABLE or similar, don't have nifscope in front of me) the collision won't move with the graphic, so you'll run into an invisible wall. Of course, this can be resolved, but then you have to deal with the camera bug mentioned in #1.
Of course, even a static barrier can be pretty effective, and I believe the Apocalypse spell pack has a spell that uses the Eye of Magnus mesh to create a temporary, static shield around you. Foes outside when it's cast stay outside and vice versa.

I have a similar barrier spell that I developed a few months ago called http://www.youtube.com/watch?v=fJWzi7dztmA. This pushes all nearby actors away before it appears, so no one gets trapped inside with you. It also incorporates a knockdown and counter-clockwise Havok push when enemies approach the barrier, but it doesn't move. I actually have a working movable barrier spell (with the aforementioned camera issue) that's very amusing to watch but I don't have video of it. I'll see if I can get one uploaded later.

All this said, there are almost certainly other approaches to this and there may be solutions to all these problems at once. They almost certainly will involve lots of experimenting with nifscope, trying out various layers and combinations of objects.
User avatar
Janine Rose
 
Posts: 3428
Joined: Wed Feb 14, 2007 6:59 pm

Post » Tue Nov 20, 2012 12:45 am

I've experimented with placing hollow havok objects around the player (the Eye of Magnus barriers work great for this!). The main things you have to watch for are:
  • The camera: If the object you're using is on the ground layer (i.e. the game considers it a blocking barrier like a wall) the camera will cram itself between the player and the barrier in 3rd person mode. This can be mitigated by editing the NIF to put the havok mesh on the Clutter layer. It will still block projectiles and npcs but the camera will ignore it. The downside of this is that it makes your barrier immobile; more on that later.
  • The enemy AI: If you have a barrier that completely surrounds the player, enemy NPCs go nuts. Sometimes they run away, sometimes they go into "lost" mode, sometimes they get stuck running into the barrier. Fixing this involves randomly removing the barrier for very brief intervals and generating almost continuous detection events. It's a hassle and it took me a long time to get it working properly.
  • Movement: Most havok meshes don't play well when it comes to being moved. Even if you use Translate functions to make the barrier follow and stay with you, unless it's on a specific layer (I believe L_BOX_MOVABLE or similar, don't have nifscope in front of me) the collision won't move with the graphic, so you'll run into an invisible wall. Of course, this can be resolved, but then you have to deal with the camera bug mentioned in #1.
Of course, even a static barrier can be pretty effective, and I believe the Apocalypse spell pack has a spell that uses the Eye of Magnus mesh to create a temporary, static shield around you. Foes outside when it's cast stay outside and vice versa.

I have a similar barrier spell that I developed a few months ago called http://www.youtube.com/watch?v=fJWzi7dztmA. This pushes all nearby actors away before it appears, so no one gets trapped inside with you. It also incorporates a knockdown and counter-clockwise Havok push when enemies approach the barrier, but it doesn't move. I actually have a working movable barrier spell (with the aforementioned camera issue) that's very amusing to watch but I don't have video of it. I'll see if I can get one uploaded later.

All this said, there are almost certainly other approaches to this and there may be solutions to all these problems at once. They almost certainly will involve lots of experimenting with nifscope, trying out various layers and combinations of objects.

Thanks for your response!

All of that sounds like it could be a little complicated than what I have time to actually do right now. I was figuring doing nif work would come in, but wasn't sure. I was noticing, however, that there is no event that could capture and alter the results of you being hit. For instance, OnBeginHit where resistance / actual damage taken / etc is calculated. Is there a way to detect something like that and negate the damage of stuff?

My main goal with the shield ward was mostly to completely resist spells that hit, get them reflected, and the spells themselves do their shatter effect that normally happens when they hit a ward (Good example being the ice spike). But sadly it doesn't look like there is a good way to handle this kind of thing inside of the scripting engine right now (With or without an actual shield object. I'd be happy with it just hitting my char and doing this).

Also a question about the methods you mentioned: It will block spells coming in, but will it also block my spells going out? A cool way I was thinking that could be implemented would be 4 blocks, not big enough to entirely enclose your char, that rotate counter-clockwise around you at a some what slow pace. This way it doesn't need to be a full shield. If a spell manages to get through the gap between the blocks and hit you, you just get hit. If a spell hits one of the blocks, the blocks act as a mirror and "reflect" it. Again, I'm not really sure how hard all of that is to do (I've never experimented with NIFs at all, or anything with the havok stuff. I don't know much about havok or models at all, really)


Edit: Also that video is really cool looking.
User avatar
Kirsty Wood
 
Posts: 3461
Joined: Tue Aug 15, 2006 10:41 am


Return to V - Skyrim