Effect

Executor

I see you
Reaction score
57
Just updated my old Effect-library for my requirements.

Maybe someone else is interested in this too

JASS:
library Effect requires optional AutoFly

    // ========== Effect ==========
        
    // Introduces the type "Effect"
    
    // This type allows you to color,scale,move,time and hide ground effects
    // And to time widget-attached effects.
    
    // IMPORTANT:
    
    // If you want to destroy a non-timed effect use : RemoveEffect() / Instance.remove()
    // If you want to destroy a     timed effect use : RemoveTimedEffect() / Instance.removeTimed()
    // Do not color,scale,move or hide widget-attached effects, but you may time em
    
    // Author: Executor alias Lord_Executor
    
    // ====== Implementation ======
    
    // Make a new trigger called "Effect"
    // Convert it to custom text
    // Paste the whole code in the empty page
    
    // IMPORTANT: 
    // Import Vexorian's dummy.mdx
    
    // adjust the "war3mapImported\dummy.mdl" (beneath) to your custom import path
    // finally save the map once, close it, reopen it and AFTER that remove the '!' of the '//!' beneath this line
    
    // runtextmacro Effect_MergeDummy("Visible Dummy","vdum","war3mapImported\dummy.mdl")
    
    // ========== Credits =========
    
    // Vexorian: vJass & Dummy.mdx & recycle idea
    // Azlier: Autofly
    
    globals
        private constant integer  DUMMY_ID            = 'vdum'
        private constant real     RECYCLE_DELAY       = 2.
        private          player   DUMMY_OWNER         = Player(14) // neutral
        private constant real     RECYCLE_X           = 5000.
        private constant real     RECYCLE_Y           = 5000.
    endglobals
    
    struct Effect   
        private unit        carrier
        private effect      sfx
        
        private thistype    next
        private real        schedule
        
        private static group DUMMIES
        private static timer NOW
        
        private static timer recycleTimer
        private static timer removeTimer
        
        private static thistype recycleStart
        private static thistype recycleEnd
        private static thistype removeStart
        private static thistype removeEnd
        
        private static method onInit takes nothing returns nothing
            set .DUMMIES        = CreateGroup()
            set .NOW            = CreateTimer()
            set .recycleTimer   = CreateTimer()
            set .removeTimer    = CreateTimer()
            set .recycleStart   = 0
            set .recycleEnd     = 0
            set .removeStart    = 0
            set .removeEnd      = 0
            call TimerStart(.NOW,999999999.,false,null)
        endmethod
        
        method scale takes real size returns nothing
            call SetUnitScale(.carrier,size,size,size)
        endmethod
        method scalePercent takes real size returns nothing
            set size = size * 0.01
            call SetUnitScale(.carrier,size,size,size)
        endmethod
        method color takes integer r, integer g, integer b, integer alpha returns nothing
            call SetUnitVertexColor(.carrier,r,g,b,alpha)
        endmethod
        method move takes real x, real y , real z returns nothing
            call SetUnitX(.carrier,x)
            call SetUnitY(.carrier,y)
            call SetUnitFlyHeight(.carrier,z,0.)
        endmethod
        method operator dummy takes nothing returns unit
            return .carrier
        endmethod
        method operator facing takes nothing returns real
            return GetUnitFacing(.carrier)
        endmethod
        method operator x takes nothing returns real
            return GetUnitX(.carrier)
        endmethod
        method operator y takes nothing returns real
            return GetUnitY(.carrier)
        endmethod
        method operator z takes nothing returns real
            return GetUnitFlyHeight(.carrier)
        endmethod
        method operator visibility takes nothing returns boolean
            return not IsUnitHidden(.carrier)
        endmethod
        method operator facing= takes real f returns nothing
            call SetUnitFacing(.carrier,f)
        endmethod
        method operator x= takes real x returns nothing
            call SetUnitX(.carrier,x)
        endmethod
        method operator y= takes real y returns nothing
            call SetUnitY(.carrier,y)
        endmethod
        method operator z= takes real z returns nothing
            call SetUnitFlyHeight(.carrier,z,0.)
        endmethod
        method operator visibility= takes boolean vis returns nothing
            call ShowUnit(.carrier,vis)
        endmethod
        method operator zAngleRad= takes real value returns nothing
            local integer i=R2I(value*bj_RADTODEG+90.5)
            if(i>=180) then
                set i=179
            elseif(i<0) then
                set i=0
            endif
            call SetUnitAnimationByIndex(.carrier, i  )
        endmethod
        method operator zAngleDeg= takes integer value returns nothing
            set value = value + 90
            if value >= 180 then
                set value = 179
            elseif value < 0 then
                set value = 0
            endif
            call SetUnitAnimationByIndex(.carrier,value)
        endmethod
        method remove takes nothing returns nothing
            call .recycle()
        endmethod
        method removeTimed takes nothing returns nothing
            local thistype curr = .removeStart
            local thistype next = .removeStart.next
            loop
                exitwhen next == 0
                if next == this then
                    set curr.next = .next
                    exitwhen true
                endif
                set curr = next
                set next = next.next
            endloop
            if .removeStart == this then
                set .removeStart = .next
            endif
            if .removeEnd == this then
                if .removeStart == 0 then
                    set .removeEnd = 0
                else
                    set .removeEnd = curr
                endif
            endif
            call .recycle()
        endmethod
        private method onDestroy takes nothing returns nothing
            if .carrier != null then
                call SetUnitScale(.carrier,1.,1.,1.)
                call SetUnitVertexColor(.carrier,255,255,255,255)
                call SetUnitX(.carrier,RECYCLE_X)
                call SetUnitY(.carrier,RECYCLE_Y)
                call GroupAddUnit(DUMMIES,.carrier)
                set .carrier = null
            endif
        endmethod
        private static method onRecycleTimer takes nothing returns nothing
            local thistype this = .recycleStart
            set .recycleStart = .recycleStart.next
            call .destroy()
            if .recycleStart != 0 then
                call TimerStart(.recycleTimer,TimerGetRemaining(.NOW)-.recycleStart.schedule,false,function thistype.onRecycleTimer)
            else
                set .recycleEnd = 0
            endif
        endmethod
        private method recycle takes nothing returns nothing
            call DestroyEffect(.sfx)
            set .sfx = null
            set .schedule   = TimerGetRemaining(.NOW)-RECYCLE_DELAY
            if .recycleStart == 0 then
                set .recycleStart  = this
                set .recycleEnd    = this
                call TimerStart(.recycleTimer,RECYCLE_DELAY,false,function thistype.onRecycleTimer)
            else
                set .recycleEnd.next   = this
                set .recycleEnd        = this
            endif
        endmethod
        private static method onRemoveTimer takes nothing returns nothing
            local thistype this = .removeStart
            set .removeStart = .removeStart.next
            call DestroyEffect(.sfx)
            call .recycle()
            if .removeStart != 0 then
                call TimerStart(.removeTimer,TimerGetRemaining(.NOW)-.removeStart.schedule,false,function thistype.onRemoveTimer)
            else
                set .removeEnd = 0
            endif
        endmethod
        static method newGroundEffect takes string modelName, real x, real y, real z returns thistype
            local thistype this = thistype.allocate()
            set .next    = 0
            set .carrier = FirstOfGroup(DUMMIES)
            if .carrier == null then 
                set .carrier = CreateUnit(DUMMY_OWNER,DUMMY_ID,x,y,0.)
                call SetUnitPathing(.carrier,false)
                static if not AutoFly then
                call UnitAddAbility(.carrier,'Amrf')
                call UnitRemoveAbility(.carrier,'Amrf')
                endif
            else
                call GroupRemoveUnit(DUMMIES,.carrier)
            endif
            call SetUnitX(.carrier,x)
            call SetUnitY(.carrier,y)
            call SetUnitFlyHeight(.carrier, z, 0.)
            set .sfx = AddSpecialEffectTarget(modelName,.carrier, "origin")
            return this
        endmethod  
        static method newTargetEffect takes string modelName, widget target, string attachPoint returns thistype
            local thistype this = thistype.allocate()
            set .next = 0
            set .carrier = null
            set .sfx = AddSpecialEffectTarget(modelName,target,attachPoint)
            return this
        endmethod
        static method newTimedGroundEffect takes string modelName, real x, real y, real z, real duration returns thistype
            local thistype this = .newGroundEffect(modelName,x,y,z)
            call .limitEffectDuration(duration)
            return this
        endmethod
        static method newTimedTargetEffect takes string modelName, widget target, string attachPoint, real duration returns thistype
            local thistype this = .newTargetEffect(modelName,target,attachPoint)
            call .limitEffectDuration(duration)
            return this
        endmethod
        private method limitEffectDuration takes real duration returns thistype
            local thistype curr = 0
            local thistype next = 0
            set .schedule = TimerGetRemaining(.NOW)-duration
            if .removeStart == 0 then
                set .removeStart  = this
                set .removeEnd    = this
                call TimerStart(.removeTimer,duration,false,function thistype.onRemoveTimer)
                return this
            elseif .removeStart.schedule<.schedule then
                call PauseTimer(.removeTimer)
                call TimerStart(.removeTimer,duration,false,function thistype.onRemoveTimer)
                set .next           = .removeStart
                set .removeStart    = this
                return this
            else
                set curr = .removeStart
                set next = .removeStart.next
                loop
                    exitwhen next == 0
                    if next.schedule < .schedule then
                        set .next       = next
                        set curr.next   = this
                        return this
                    endif
                    set curr = next 
                    set next = next.next
                endloop
                set .removeEnd.next   = this
                set .removeEnd        = this
            endif
            return this
        endmethod
        //! textmacro Effect_MergeDummy takes NAME, ID, MODELPATH
            //! external ObjectMerger w3u ushd $ID$ unam "$NAME$" uabi "Aeth,Avul,Aloc" umvs "520" umvr 1 umvt foot umvh -500 uhpm 99999 uhom 1 ucol -10 umdl "$MODELPATH$" ucbs 0 ucpt 0 ushu "" umvh 0 ufoo 0
        //! endtextmacro
    endstruct
        
    function AddEffect takes string modelName, real x, real y, real z returns Effect
        return Effect.newGroundEffect(modelName,x,y,z)
    endfunction
    
    function AddTimedEffect takes string modelName, real x, real y, real z, real duration returns Effect
        return Effect.newTimedGroundEffect(modelName,x,y,z,duration)
    endfunction
    
    function AddEffectTarget takes string modelName, widget target, string attachPoint returns Effect
        return Effect.newTargetEffect(modelName,target,attachPoint)
    endfunction
    
    function AddTimedEffectTarget takes string modelName, widget target, string attachPoint, real duration returns Effect
        return Effect.newTimedTargetEffect(modelName,target,attachPoint,duration)
    endfunction
    
    function ScaleEffect takes Effect e, real scale returns nothing
        call e.scale(scale)
    endfunction       
    
    function ScaleEffectByPercent takes Effect e, real scale returns nothing
        call e.scalePercent(scale)
    endfunction
    
    function ReColorEffect takes Effect e, integer r, integer g, integer b, integer alpha returns nothing
        call e.color(r,g,b,alpha)
    endfunction
    
    function MoveEffect takes Effect e, real x, real y, real z returns nothing
        call e.move(x,y,z)
    endfunction
    
    function HideEffect takes Effect e returns nothing
        set e.visibility=false
    endfunction
    
    function ShowEffect takes Effect e returns nothing
        set e.visibility=true
    endfunction
    
    function RemoveEffect takes Effect e returns nothing
        call e.remove()
    endfunction
    
    function RemoveTimedEffect takes Effect e returns nothing
        call e.removeTimed()
    endfunction
endlibrary
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
It can be looked more "pro", if you change those method to method operator. scale, scale percent, color, visibility, x, y, z, facing.
For scale, it is not so good.
When someone want to magnify/minimize the dummy without interrupting it's height, he is forced to "hack" into your system to make a function to do so.
And also, I want zangle.
zangle.jpg
 

Executor

I see you
Reaction score
57
Updated.

Added operators.
Added access to the carrier unit.
Z-Axis now independent to ground height.
 

Jesus4Lyf

Good Idea™
Reaction score
397
This stuff gets irritating because at what point does it become worth using a resource over scripting it yourself? If it's periodic usage, you should code your stuff right in for efficiency probably. If it's not, then you're probably not moving effects around, and you just need a time-out and destroy, or something else...

I dunno, requirements analysis is the difficulty around this.

Edit: Especially because this doesn't cover the main thing in my opinion, which is moving an effect by an x/y/z offset irrelevant to terrain. :p
 

Executor

I see you
Reaction score
57
Well, the benefit of mine is that I don need many struct vars and that you may time my effects.

xefx provides some additional disputable features, like flash, ability etc.

Well what about a little poll:

What should an Effect library using dummy units comprise?
 

Jesus4Lyf

Good Idea™
Reaction score
397
Edit: Especially because this doesn't cover the main thing in my opinion, which is moving an effect by an x/y/z offset irrelevant to terrain. :p
That. With max efficiency. Because, let's face it, that's what we want to do periodically. :)

What you have to remember is this... It's rare somebody wants to set an effect's x/y coordinates. They want to add to them. You need a struct with two main methods: addXY(real,real) and addXYZ(real,real,real). "create" should take x/y/z with a z relative to terrain height (ie. not your fancy stuff) and the addXYZ should factor in terrain stuff (ie. fancy stuff). The current x/y/z/terrainheight should be stored in struct members. :)

I dunno, then you could add things like show/hide, set size, etc. That stuff is superfluous. You need to analyse your requirements. :)
 

Executor

I see you
Reaction score
57
Well, the main point of this library is the efficient creation and destruction of effects. I'll add an integer userData for adding a struct containing xyz. Then you can still move the effect periodically and effectivly, but my struct only contains the minimum of vars needed.

I will revert the system to use only SetUnitFlyHeight without taking into account the terrain height. Because this is usually only needed when using projectiles and so you could add them in the above mentioned struct containing xyz data.
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
Actually function call is costy.
Benchmark result :
Hashtable -> 2.xxx
Table -> 19.xxx
You need to add some exciting features to attract people and overhelm the efficiency problem.
 

Jesus4Lyf

Good Idea™
Reaction score
397
That. With max efficiency. Because, let's face it, that's what we want to do periodically. :)

What you have to remember is this... It's rare somebody wants to set an effect's x/y coordinates. They want to add to them. You need a struct with two main methods: addXY(real,real) and addXYZ(real,real,real). "create" should take x/y/z with a z relative to terrain height (ie. not your fancy stuff) and the addXYZ should factor in terrain stuff (ie. fancy stuff). The current x/y/z/terrainheight should be stored in struct members. :)

I dunno, then you could add things like show/hide, set size, etc. That stuff is superfluous. You need to analyse your requirements. :)
Functions should either inline, or blatantly not, for something like this. Stuff like show/hide can inline, and stuff like add offset should blatantly not. :)
 

Executor

I see you
Reaction score
57
Well, for high frequency usage you can access the dummy unit directly without a function call.

//Ah, now I get what you meant with offset and addingXYZ, but as I said, this library mainly exists for static Effects and only workaroundwise for projectiles. I mean you can still use them as projectiles, but then you have to access the dummy unit directly as mentioned above.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top