Jump to content
Heavy Traffic ×

[LE] Ricocheting Spell Help


Recommended Posts

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))

EndEvent

Any advice, comments, help is much appreciated, as always.

 

Link to comment
Share on other sites

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 by Rasikko
Link to comment
Share on other sites

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

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

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 by jucoking
Link to comment
Share on other sites

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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...