jucoking Posted March 4, 2019 Share Posted March 4, 2019 I'm attempting to make a ricochet spell that, when it hits a hostile, the script attached to the hostile searches for any other targets nearby and fires again, this time from the hostile. It works in part but there are some mysterious things going on that should not be happening. First of all, whenever the "OnHit" script fires from the hostile actor they regain all of their health. Second, the spell fires fine but it doesn't fire towards the targets outlined in the formlist. Instead it just shoots out in front of the hostile actor towards whichever way they are facing (I'm using a "beam" projectile which I suspect is what is causing that...I don't think beam spell can follow x, y, z offsets). I have attached a screenshot of the magic effect screen for the spell And here is the script that is attached to enemies in order to "ricochet": Scriptname CV2_DiamondMGEFRicochet extends ObjectReference import utility Float Property xOffset Auto Float Property yOffset Auto Float Property zOffset Auto Float Property RandomOffsetx Auto Float Property RandomOffsety Auto Float Property RandomOffsetz Auto Float Property PulseRate = 3.0 Auto Float Property RandomRate Auto Activator Property TargetType Auto ObjectReference Property CurrentTarget Auto Bool Property SpawnNode Auto Spell Property CV2_Diamond Auto FormList Property TargetTypeList Auto Float Property SeekRange = 20.0 Auto Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, \ bool abBashAttack, bool abHitBlocked) if aksource == CV2_Diamond if CurrentTarget elseif SpawnNode float newxoffset = xoffset + RandomFloat(-RandomOffsetX, RandomOffsetX) float newyoffset = yoffset + RandomFloat(-RandomOffsety, RandomOffsety) float newzoffset = zoffset + RandomFloat(-RandomOffsetz, RandomOffsetz) CurrentTarget = PlaceAtMe(Game.FindClosestReferenceOfAnyTypeInListfromRef(TargetTypeList, self, SeekRange)) CurrentTarget.MoveTo(self, newxoffset, newyoffset, newzoffset) endif RegisterForSingleUpdate(PulseRate) endif EndEvent Event OnUpdate() if !SpawnNode && !TargetTypeList && GetDistance(Game.FindClosestReferenceOfAnyTypeInListfromRef(TargetTypeList, self, SeekRange)) < SeekRange CurrentTarget = Game.FindClosestReferenceOfAnyTypeInListfromRef(TargetTypeList, self, SeekRange) elseif TargetTypeList CurrentTarget = Game.FindClosestReferenceOfAnyTypeInListfromRef(TargetTypeList, self, SeekRange) endif if CurrentTarget CV2_Diamond.Cast(self, Game.FindClosestReferenceOfAnyTypeInListfromRef(TargetTypeList, self, SeekRange)) endif RegisterForSingleUpdate(PulseRate + RandomFloat(0.0, RandomRate)) EndEventAny advice, comments, help is much appreciated, as always. Link to comment Share on other sites More sharing options...
Evangela Posted March 5, 2019 Share Posted March 5, 2019 (edited) Your SeekRage is too small. Bump it up a little more. 128 might suffice. I think 2 npcs standing as close as possible would still not be within a 20 unit radius, as their center of mass(which the search goes by) may still fall outside of the radius. Also, it may just be the find function finding the calling actor. Find functions will return the caller, if the caller is the same type as the reference being searched for. It is a nice idea btw. Chain lightening does the same thing, but without a script. Edited March 5, 2019 by Rasikko Link to comment Share on other sites More sharing options...
ReDragon2013 Posted March 6, 2019 Share Posted March 6, 2019 Your script is a mess, maybe next code will help to reach your aim. CV2_DiamondMGEFRicochetScript Scriptname CV2_DiamondMGEFRicochetScript extends ObjectReference ; https://forums.nexusmods.com/index.php?/topic/7450976-ricocheting-spell-help/ ; jucoking wrote: "I am attempting to make a ricochet spell that, when it hits a hostile, ; the script attached to the hostile searches for any other targets nearby and fires again, this time from the hostile." ; Problem: "some mysterious things going on that should not be happening" Spell PROPERTY CV2_Diamond auto ; the spell for ricochet ;Activator PROPERTY TargetType auto ; UnUSED by default FormList PROPERTY TargetTypeList auto ObjectReference PROPERTY CurrentTarget auto Hidden Float PROPERTY xOffset auto ; !? Float PROPERTY yOffset auto Float PROPERTY zOffset auto Float PROPERTY ROx auto ; randomize the offsets from above Float PROPERTY ROy auto Float PROPERTY ROz auto Float PROPERTY fRandomRate = 1.0 auto ; [default=1.0] Float PROPERTY fPulseRate = 3.0 auto ; [default=3.0] Float PROPERTY fSeekRange = 256.0 auto ; [default=128.0] ; recommended by Rasikko Bool PROPERTY bSpawnNode auto ; [default=False] ; -- EVENTs -- 2 + "Busy" EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProj, Bool b1, Bool b2, Bool b3, Bool b4) IF (akSource == CV2_Diamond as Form) ELSE RETURN ; - STOP - ENDIF ;--------------------- IF ( CurrentTarget ) ; target already exists ELSEIF ( bSpawnNode ) gotoState("Busy") ; ### STATE ### make sure we avoid hit events currently myF_Target() gotoState("") ; ### STATE ### ENDIF UnRegisterForUpdate() ; do not overlap OnUpdate() events RegisterForSingleUpdate(fPulseRate) ENDEVENT EVENT OnUpdate() float f = myF_Update() IF (f > 0.0) RegisterForSingleUpdate(f) ; try it again, update chain ENDIF ENDEVENT ;======================== state Busy ;========= EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProj, Bool b1, Bool b2, Bool b3, Bool b4) ENDEVENT ;======= endState ; -- FUNCTIONs -- 3 ;-------------------------- Float FUNCTION myF_Update() ;-------------------------- ;;; IF ( TargetTypeList ) ;;; CurrentTarget = Game.FindClosestReferenceOfAnyTypeInListfromRef(TargetTypeList, self, fSeekRange) CurrentTarget = myF_FindRef() ;;; ELSEif (!bSpawnNode) && (self.GetDistance(Game.FindClosestReferenceOfAnyTypeInListfromRef(TargetTypeList, self, fSeekRange)) < fSeekRange) ;;; CurrentTarget = Game.FindClosestReferenceOfAnyTypeInListFromRef(TargetTypeList, self, fSeekRange) ;;; ENDIF IF ( CurrentTarget ) CV2_Diamond.Cast(self, CurrentTarget) RETURN Utility.RandomFloat(0.01, fRandomRate) + fPulseRate ENDIF RETURN -1 ENDFUNCTION ;------------------------------------- ObjectReference FUNCTION myF_FindRef() ;------------------------------------- ; https://www.creationkit.com/index.php?title=FindClosestReferenceOfAnyTypeInListFromRef_-_Game objectReference oRef int iMax = TargetTypeList.GetSize() - 1 float fMax = 6000.0 float f = fSeekRange WHILE (f < fMax) && (iMax) ;;; ; returns "A random reference of any of the typelist within the radius or the center reference" ;;; oRef = Game.FindClosestReferenceOfAnyTypeInListfromRef(TargetTypeList, self, f) ; returns "A random reference of that type within the radius, none if none was found." form fm = TargetTypeList.GetAt( Utility.RandomInt(0, iMax) ) ; get a random type oRef = Game.FindClosestReferenceOfTypeFromRef(fm, self, f) IF (oRef) || !self.GetParentCell() f = fMax ; stop loop here ELSE f = f + f ; 200, 400, 800, 1600, .. ENDIF ENDWHILE RETURN oRef ENDFUNCTION ;-------------------- FUNCTION myF_Target() ;-------------------- float fx = Utility.RandomFloat(-ROx, ROx) + xoffset float fy = Utility.RandomFloat(-ROy, ROy) + yoffset float fz = Utility.RandomFloat(-ROz, ROz) + zoffset objectReference oRef = myF_FindRef() IF (oRef) && self.GetBaseObject() ELSE CurrentTarget = None RETURN ; - STOP - nothing found /or/ detached /or/ mod has been removed ENDIF ;--------------------- oRef = self.PlaceAtMe(oRef) IF ( oRef ) oRef.MoveTo(self, fx, fy, fz) ; move the closest target near to this object ENDIF CurrentTarget = oRef ; set script property here ENDFUNCTION Link to comment Share on other sites More sharing options...
jucoking Posted March 6, 2019 Author Share Posted March 6, 2019 Your SeekRage is too small. Bump it up a little more. 128 might suffice. I think 2 npcs standing as close as possible would still not be within a 20 unit radius, as their center of mass(which the search goes by) may still fall outside of the radius. Also, it may just be the find function finding the calling actor. Find functions will return the caller, if the caller is the same type as the reference being searched for. It is a nice idea btw. Chain lightening does the same thing, but without a script.Should I make separate form lists for every actor, which does not include the actor holding the script? I can definitely give that a shot! Your script is a mess, maybe next code will help to reach your aim. CV2_DiamondMGEFRicochetScript Scriptname CV2_DiamondMGEFRicochetScript extends ObjectReference ; https://forums.nexusmods.com/index.php?/topic/7450976-ricocheting-spell-help/ ; jucoking wrote: "I am attempting to make a ricochet spell that, when it hits a hostile, ; the script attached to the hostile searches for any other targets nearby and fires again, this time from the hostile." ; Problem: "some mysterious things going on that should not be happening" Spell PROPERTY CV2_Diamond auto ; the spell for ricochet ;Activator PROPERTY TargetType auto ; UnUSED by default FormList PROPERTY TargetTypeList auto ObjectReference PROPERTY CurrentTarget auto Hidden Float PROPERTY xOffset auto ; !? Float PROPERTY yOffset auto Float PROPERTY zOffset auto Float PROPERTY ROx auto ; randomize the offsets from above Float PROPERTY ROy auto Float PROPERTY ROz auto Float PROPERTY fRandomRate = 1.0 auto ; [default=1.0] Float PROPERTY fPulseRate = 3.0 auto ; [default=3.0] Float PROPERTY fSeekRange = 256.0 auto ; [default=128.0] ; recommended by Rasikko Bool PROPERTY bSpawnNode auto ; [default=False] ; -- EVENTs -- 2 + "Busy" EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProj, Bool b1, Bool b2, Bool b3, Bool b4) IF (akSource == CV2_Diamond as Form) ELSE RETURN ; - STOP - ENDIF ;--------------------- IF ( CurrentTarget ) ; target already exists ELSEIF ( bSpawnNode ) gotoState("Busy") ; ### STATE ### make sure we avoid hit events currently myF_Target() gotoState("") ; ### STATE ### ENDIF UnRegisterForUpdate() ; do not overlap OnUpdate() events RegisterForSingleUpdate(fPulseRate) ENDEVENT EVENT OnUpdate() float f = myF_Update() IF (f > 0.0) RegisterForSingleUpdate(f) ; try it again, update chain ENDIF ENDEVENT ;======================== state Busy ;========= EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProj, Bool b1, Bool b2, Bool b3, Bool b4) ENDEVENT ;======= endState ; -- FUNCTIONs -- 3 ;-------------------------- Float FUNCTION myF_Update() ;-------------------------- ;;; IF ( TargetTypeList ) ;;; CurrentTarget = Game.FindClosestReferenceOfAnyTypeInListfromRef(TargetTypeList, self, fSeekRange) CurrentTarget = myF_FindRef() ;;; ELSEif (!bSpawnNode) && (self.GetDistance(Game.FindClosestReferenceOfAnyTypeInListfromRef(TargetTypeList, self, fSeekRange)) < fSeekRange) ;;; CurrentTarget = Game.FindClosestReferenceOfAnyTypeInListFromRef(TargetTypeList, self, fSeekRange) ;;; ENDIF IF ( CurrentTarget ) CV2_Diamond.Cast(self, CurrentTarget) RETURN Utility.RandomFloat(0.01, fRandomRate) + fPulseRate ENDIF RETURN -1 ENDFUNCTION ;------------------------------------- ObjectReference FUNCTION myF_FindRef() ;------------------------------------- ; https://www.creationkit.com/index.php?title=FindClosestReferenceOfAnyTypeInListFromRef_-_Game objectReference oRef int iMax = TargetTypeList.GetSize() - 1 float fMax = 6000.0 float f = fSeekRange WHILE (f < fMax) && (iMax) ;;; ; returns "A random reference of any of the typelist within the radius or the center reference" ;;; oRef = Game.FindClosestReferenceOfAnyTypeInListfromRef(TargetTypeList, self, f) ; returns "A random reference of that type within the radius, none if none was found." form fm = TargetTypeList.GetAt( Utility.RandomInt(0, iMax) ) ; get a random type oRef = Game.FindClosestReferenceOfTypeFromRef(fm, self, f) IF (oRef) || !self.GetParentCell() f = fMax ; stop loop here ELSE f = f + f ; 200, 400, 800, 1600, .. ENDIF ENDWHILE RETURN oRef ENDFUNCTION ;-------------------- FUNCTION myF_Target() ;-------------------- float fx = Utility.RandomFloat(-ROx, ROx) + xoffset float fy = Utility.RandomFloat(-ROy, ROy) + yoffset float fz = Utility.RandomFloat(-ROz, ROz) + zoffset objectReference oRef = myF_FindRef() IF (oRef) && self.GetBaseObject() ELSE CurrentTarget = None RETURN ; - STOP - nothing found /or/ detached /or/ mod has been removed ENDIF ;--------------------- oRef = self.PlaceAtMe(oRef) IF ( oRef ) oRef.MoveTo(self, fx, fy, fz) ; move the closest target near to this object ENDIF CurrentTarget = oRef ; set script property here ENDFUNCTION Thank you so much for this. I'm going to test it out today along with some recommendations from Rasikko and I will let you know what happens. Link to comment Share on other sites More sharing options...
SeraphimKensai Posted March 6, 2019 Share Posted March 6, 2019 I would personally get rid of the target list and use a function named close to findnearestactor and use conditions such as hostility and range to determine if it fires. That way it will be able to work with mod added actors that aren't in your target list. Link to comment Share on other sites More sharing options...
jucoking Posted March 6, 2019 Author Share Posted March 6, 2019 (edited) I would personally get rid of the target list and use a function named close to findnearestactor and use conditions such as hostility and range to determine if it fires. That way it will be able to work with mod added actors that aren't in your target list.This actually worked, even attaching it to my previous script (I was too afraid to experiment with ReDragon's script lol). However, my actors are still regaining their full health in lock-step with the pulse rate. It's so strange. The spell has no regenerative properties whatsoever. I will try attaching another MGEF to the spell which rids targets of all health regen. I also need to work on conditions so that it doesn't attack the player, only enemies. EDIT: Sorry, that was premature. It still isn't targeting other enemies. Even after tweaking the mgef's to reduce the actor healrate by -6000, they are still healing themselves every time the spell fires from them. Edited March 6, 2019 by jucoking Link to comment Share on other sites More sharing options...
jucoking Posted March 8, 2019 Author Share Posted March 8, 2019 I finally got it working. I had to take out the pulse rate completely, which really wasn't needed to begin with. It makes more sense when only one projectile ricochets anyway. I also had to give it an "area" in the spell window. For anyone interested, here is the final script I used (this is placed on all actors that the effect should work on): Scriptname CV2_DiamondMGEFRicochet extends ObjectReference import utility Float Property xOffset Auto Float Property yOffset Auto Float Property zOffset Auto Float Property RandomOffsetx Auto Float Property RandomOffsety Auto Float Property RandomOffsetz Auto Float Property RandomRate Auto Activator Property TargetType Auto ObjectReference Property CurrentTarget Auto Bool Property SpawnNode Auto Spell Property CV2_Diamond Auto FormList Property TargetTypeList Auto Float Property SeekRange = 600.0 Auto Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, \ bool abBashAttack, bool abHitBlocked) if aksource == CV2_Diamond if CurrentTarget elseif SpawnNode float newxoffset = xoffset + RandomFloat(-RandomOffsetX, RandomOffsetX) float newyoffset = yoffset + RandomFloat(-RandomOffsety, RandomOffsety) float newzoffset = zoffset + RandomFloat(-RandomOffsetz, RandomOffsetz) CurrentTarget = PlaceAtMe(Game.FindClosestActorFromRef(self, 600.0)) CurrentTarget.MoveTo(self, newxoffset, newyoffset, newzoffset) endif if !TargetTypeList && GetDistance(Game.FindClosestActorFromRef(Self, 600.0)) < SeekRange CurrentTarget = Game.FindClosestActorFromRef(Self, 600.0) elseif TargetTypeList CurrentTarget = Game.FindClosestActorFromRef(Self, 600.0) endif if CurrentTarget CV2_Diamond.Cast(self, Game.FindClosestActorFromRef(Self, 600.0)) endif endif EndEvent Sorry, ReDragon, I did not use your clean version of the script because I wasn't sure how to manipulate it, but I really appreciate your help. If you want to take a look at my final version of the script and clean it up, I would definitely use it! Thank you all for your time. Link to comment Share on other sites More sharing options...
Evangela Posted March 9, 2019 Share Posted March 9, 2019 Glad you got it working. In case you want to work with explosions in the future, they also need the area set as well. Area of 1 will suffice, if it's going to be a "fake" explosion for the sole purpose of dropping a marker. Link to comment Share on other sites More sharing options...
Recommended Posts