• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Idea] [system] (vJass) CustomMissle

Status
Not open for further replies.
zArc = Height increases towards middle and goes down.
_.-*-._
xyArc = Same for XY-Coordinates, it goes far away to a side and then comes back again.

I used the parabola equation for it, but i think you will probably use something different, no?
Of course I will.

Well maybe make z angle as in a missile comes toward you then you activate a block spell the missile flies straight upward into the sky
Can already be implemented optionally. I will check for it.
 
Level 12
Joined
May 21, 2009
Messages
994
I would also like an option to make it like: A missile hits something (a shield or something) then it stops and falls down to the ground. I can imagine 20 missiles from each side shoots at a target with a shield around him. They all hit the shield and they just pause and falls down the ground.
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
I would be grateful if you would send me the method as soon as you finish it, i want to test it out on some missiles(not my missile system, just want to have fun with arc movement since it is cool).

EDIT:

I would also like an option to make it like: A missile hits something (a shield or something) then it stops and falls down to the ground. I can imagine 20 missiles from each side shoots at a target with a shield around him. They all hit the shield and they just pause and falls down the ground.

Missiles don't just fall to the ground when they colide, they bounce off the object.
 
Finished xyArc.
xyARC.jpg

Anyway, here is a little demo so you can get what is waiting for you.
View attachment CM_UP_v.0.0.4.w3x

(I know its really basic, but more comes soon)
 
Mine will also have acceleration, you can simply add it or wait for the new version.
However, I will need to do the zArc and the next version will include some new functions such as collision with units, missle collision comes later.

#Update. These are my stub methods for now.
JASS:
        private stub method onTargetReach takes nothing returns nothing
        endmethod
        
        private stub method onUnitTouch takes unit theUnit returns nothing
        endmethod
        
        private stub method onWallHit takes unit theUnit returns nothing
        endmethod
        
        private stub method onCollide takes thistype theCollider returns nothing
            call .destroy()
        endmethod

        private stub method onCreate takes nothing returns nothing
        endmethod

        private stub method onStart takes nothing returns nothing
        endmethod
        
        private stub method onLoop takes nothing returns nothing
        endmethod
        
        private stub method onEnd takes nothing returns nothing
        endmethod
        
        private stub method getMissleSpeed takes nothing returns real
            return .moveSpd
        endmethod
(getMissleSpeed = You can place missle speed enhancers / decreasers here)
 
Last edited:
Yes,.. Exactly.

Update!
Optionally detects
  • Units Touching
  • Destructable Touching
  • Wall Touching

Eventhandlers.
JASS:
private interface eventHandler
        //: If we have a homing missle, we can just use this to react on the 
        //: target reached event.
        method onTargetReach takes nothing returns nothing defaults nothing
        
        //: Whenever the missle touches an unit it will trigger this event.
        //: Note: You will need to enable unit hitting first!
        //: (set .hitunits = true)
        method onUnitTouch takes unit theUnit returns nothing defaults nothing
        
        //: Whenever the missle touches a destructable it will trigger this event.
        //: Note: You will need to enable destructable hitting first!
        //: (set .hitdests = true)
        method onDestTouch takes destructable theDestructable returns nothing defaults nothing
        
        //: Whenever the missle touches a terrain wall it will trigger this event.
        //: Note: You will need to enable terrain hitting first!
        //: (set .hitwalls = true)
        method onWallHit takes nothing returns nothing defaults nothing
        
        //: Whenever the missle touches a missle wall it will trigger this event.
        //: Note: You will need to enable missle hitting first!
        //: (set .hitmissles = true)
        method onCollide takes CustomMissle theCollider returns nothing defaults nothing
        
        //: This will be runned the first time the unit moves (and only once).
        //: Note: Paused units do not move.
        method onStart takes nothing returns nothing defaults nothing

        //: This will be runned everytime the missle is in proceeding, 
        //: whether its active or not.
        method onLoop takes nothing returns nothing defaults nothing
        
        //: This will be runned at the end, in the destroy method.
        method onEnd takes nothing returns nothing defaults nothing
        
        //: This will be runned right after the creation. 
        //: Use this to apply your missle settings here.
        method onCreate takes nothing returns nothing defaults nothing
        
        //: This will return the missle speed. 
        //: Use this method to slow / freeze missles.
        method getMissleSpeed takes nothing returns real defaults 0.
    endinterface

Fixed a few stuff aswell! HAVE FUN!

#Another update.
Removed CustomFilter requirement.
Created the first spell using this engine. ENJOY!
 
Last edited:
I've been through most of it. Here is commented code -- comments with //! are mine.

JASS:
library CustomMissle requires CustomEffect, Loc

    globals
            // Just looked good
                constant real       CM_PERIOD               = 0.03175
        
            // 22.5 * bj_PI / 180
        private constant real       CM_ANGLE_TOLERANCE      = 0.392699082
    endglobals
    
    //! you forgot to give credit to moyack...
    function ParabolaZ takes real h, real d, real x returns real
        return (4 * h / d) * (d - x) * (x / d)
    endfunction
    
    function ParabolaZ2 takes real y0, real y1, real h, real d, real x returns real
        local real A = (2*(y0+y1)-4*h)/(d*d)
        local real B = (y1-y0-A*d*d)/d
        return A*x*x + B*x + y0
    endfunction
    
    // == --   -   -   -- ==
    //  Default settings
    // == --   -   -   -- ==
    globals
        // Declares the default height of a missle. 
        //  Please note, this is irrelevant
        private constant    real        CM_DEFAULT_HEIGHT       = 0.
        
        // Declares the default xyArc of the missle. 
        //  xyArc is a curve, but horizontal.
        private constant    real        CM_DEFAULT_XYARC        = 0.
        // Declares the default zArc of the missle.
        private constant    real        CM_DEFAULT_ZARC         = 0.
        
        // Declares whether missles have decay at default or not
        private constant    boolean     CM_DEFAULT_HASDECAY     = true
        // Declares the time of the default decay. 
        //  Doesn not do anything when hasdecay is on false 
        private constant    real        CM_DEFAULT_DECAY        = 5.
        
        //  Declares the default movespeed of the missle
        private constant    real        CM_DEFAULT_MOVESPEED    = 512.
        //  Declares the default turn of the missle 
        //  Turnspeed: How fast the missel can change its degrees
        private constant    real        CM_DEFAULT_TURNSPEED    = 0.33
        
        // Declares the default range of the missle.
        //  Warning: An unit with 0 range will !never! reach the target. 
        private constant    real        CM_DEFAULT_HITRANGE     = 64.
        
        // Some booleans whether actions 
        // should be done on those hits or not
        private constant    boolean     CM_DEFAULT_HITSWALLS    = false
        private constant    boolean     CM_DEFAULT_HITSUNITS    = false
        private constant    boolean     CM_DEFAULT_HITSDESTS    = false
        private constant    boolean     CM_DEFAULT_HITSMISSLES  = true
    endglobals
    
    private interface eventHandler
        //: If we have a homing missle, we can just use this to react on the 
        //: target reached event.
        method onTargetReach takes nothing returns nothing defaults nothing
        
        //: Whenever the missle touches an unit it will trigger this event.
        //: Note: You will need to enable unit hitting first!
        //: (set .hitunits = true)
        method onUnitTouch takes unit theUnit returns nothing defaults nothing
        
        //: Whenever the missle touches a destructable it will trigger this event.
        //: Note: You will need to enable destructable hitting first!
        //: (set .hitdests = true)
        method onDestTouch takes destructable theDestructable returns nothing defaults nothing
        
        //: Whenever the missle touches a terrain wall it will trigger this event.
        //: Note: You will need to enable terrain hitting first!
        //: (set .hitwalls = true)
        method onWallHit takes nothing returns nothing defaults nothing
        
        //: Whenever the missle touches a missle wall it will trigger this event.
        //: Note: You will need to enable missle hitting first!
        //: (set .hitmissles = true)
        method onCollide takes CustomMissle theCollider returns nothing defaults nothing
        
        //: This will be runned the first time the unit moves (and only once).
        //: Note: Paused units do not move.
        method onStart takes nothing returns nothing defaults nothing

        //: This will be runned everytime the missle is in proceeding, 
        //: whether its active or not.
        method onLoop takes nothing returns nothing defaults nothing
        
        //: This will be runned at the end, in the destroy method.
        method onEnd takes nothing returns nothing defaults nothing
        
        //: This will be runned right after the creation. 
        //: Use this to apply your missle settings here.
        method onCreate takes nothing returns nothing defaults nothing
        
        //: This will return the missle speed. 
        //: Use this method to slow / freeze missles.
        method getMissleSpeed takes nothing returns real defaults 0.
    endinterface

    struct CustomMissle extends eventHandler
        //: == ---------------------------- ---------------------------- 
        //: #  Primary Values
        //: == ---------------------------- ---------------------------- 
        private delegate    CustomEffect    eff             = 0
        private             boolean         isAlive         = false
        private             boolean         isActive        = false
        private             boolean         isStarted       = false
        private             boolean         isCreated       = false
        private             Loc             start           = 0
        private             Loc             end             = 0
        private             real            h               = CM_DEFAULT_HEIGHT
        
        //: == ---------------------------- ---------------------------- 
        //: #  Arc movement (Column flight)
        //: == ---------------------------- ---------------------------- 
        private             real            xyArc           = CM_DEFAULT_XYARC
        private             real            zArc            = CM_DEFAULT_ZARC
        
        //: == ---------------------------- ---------------------------- 
        //: #  Decay (Death timer)
        //: == ---------------------------- ---------------------------- 
        private             boolean         hasDecay        = CM_DEFAULT_HASDECAY
        private             real            decy            = CM_DEFAULT_DECAY
        
        //: == ---------------------------- ---------------------------- 
        //: #  Haze (Circles)
        //: == ---------------------------- ---------------------------- 
        private             boolean         hasHaze         = false
        private             boolean         hazeNeg         = false
        private             real            hazeInc         = 0.
        private             real            hazeMin         = 0.
        private             real            hazeMax         = 0.
        private             real            hazeCur         = 0.
        
        //: == ---------------------------- ---------------------------- 
        //: #  Turn / Movespeed & Management
        //: == ---------------------------- ---------------------------- 
        private             real            moveSpd         = CM_DEFAULT_MOVESPEED
        private             real            turnSpd         = CM_DEFAULT_TURNSPEED
        private             boolean         isTurning       = false
        private             boolean         turnForward     = false
        
        //: == ---------------------------- ---------------------------- 
        //: #  Unit/Wall/Dest/Missle Colliding
        //: == ---------------------------- ---------------------------- 
        private             real            hitRange        = CM_DEFAULT_HITRANGE
        private             boolean         hitsUnits       = CM_DEFAULT_HITSUNITS
        private             boolean         hitsWalls       = CM_DEFAULT_HITSWALLS
        private             boolean         hitsDests       = CM_DEFAULT_HITSDESTS
        private             boolean         hitsMissles     = CM_DEFAULT_HITSMISSLES
        
        //: == ---------------------------- ---------------------------- 
        //: #  Targets & Management
        //: == ---------------------------- ---------------------------- 
        private             boolean         hasTarget       = false
        private             boolean         targetZ         = false
        private             unit            target          = null
        
        //: == ---------------------------- ---------------------------- 
        //: #  Enumeration & Filters
        //: == ---------------------------- ---------------------------- 
        private             group           enumGroup       = null
        private static      boolexpr        enumFilter      = null
        private             rect            locRect         = null
        
        //: == ---------------------------- ---------------------------- 
        //: #  Indexing & Structstuff
        //: == ---------------------------- ---------------------------- 
        private             integer         index           = 0
        private static      thistype        curInstance     = 0
        private static      timer           t               = CreateTimer()
        private static      integer         a               = 0
        private static      thistype        array           i
        
        //: == ---------------------------- ---------------------------- 
        //: #  Methods - Instance Creation
        //: == ---------------------------- ---------------------------- 
        public static method create takes real x, real y, real z, real f returns thistype
            local thistype this = thistype.allocate()
            
            /* Users are so innocent */
            //! what if users want to specify an absolute height?
            set z               = GetLocZ(x, y) + z
            
            /* Initialize our members */
            set .eff            = CustomEffect.create(x, y, z, f)
            set .start          = Loc.create(x, y, z, 0.)
            set .end            = Loc.create(x, y, z, 0.)
            set .enumGroup      = CreateGroup()
            set .locRect        = Rect(0., 0., 0., 0.)
            
            set .isAlive   = true
            set .isActive  = true
            
            set .index = thistype.a
            set thistype.i[thistype.a]  = this
            set thistype.a              = thistype.a +1
            if thistype.a == 1 then
                call TimerStart(thistype.t, CM_PERIOD, true, function thistype.run)
            endif
            
            return this
        endmethod
        
        public static method createForTarget takes real x, real y, real z, unit target, boolean useZ returns thistype
             local real f = bj_RADTODEG * Atan2(GetUnitY(target) - y, GetUnitX(target) - x)
             local thistype this = thistype.create(x, y, z, f)
             
             call .setTargetUnit(target, useZ)
             
             return this
         endmethod
         
         public static method createFromSourceToTarget takes unit source, unit target returns thistype
             local real x = GetUnitX(source)
             local real y = GetUnitY(source)
             local real z = GetLocZ(x, y) + GetUnitFlyHeight(source)
             
             return thistype.createForTarget(x, y, z, target, true)
         endmethod
        
        //: == ---------------------------- ---------------------------- 
        //: #  Methods - Creating general behaviours
        //: == ---------------------------- ---------------------------- 
        stub method onWallHit takes nothing returns nothing
            set .alive = false
            set .active = false
        endmethod
        
        stub method onCollide takes CustomMissle theCollider returns nothing
            set .alive = false
            set .active = false
        endmethod
        
        stub method getMissleSpeed takes nothing returns real
            return .moveSpd
        endmethod
        
        //: == ---------------------------- ---------------------------- 
        //: #  Methods - Setting / Getting of members
        //: == ---------------------------- ---------------------------- 
        method operator xyarc takes nothing returns real
            return .xyArc
        endmethod
        
        method operator xyarc= takes real val returns nothing
            set .xyArc = val
        endmethod
        
        method operator zarc takes nothing returns real
            return .zArc
        endmethod
        
        method operator zarc= takes real val returns nothing
            set .zArc = val
        endmethod
        
        method operator height takes nothing returns real
            return .h
        endmethod
        
        method operator height= takes real val returns nothing
            set .h = val
        endmethod
        
        method operator decay takes nothing returns real
            return .decy
        endmethod
        
        method operator decay= takes real val returns nothing
            set .decy = val
            if .decy > 0. then
                set .hasDecay = true
            else
                set .hasDecay = false
            endif
        endmethod
        
        method operator haze takes nothing returns real
            return .hazeInc
        endmethod
        
        method operator haze= takes real val returns nothing
            set .hazeInc = val
            if .hazeInc > 0. then
                set .hasHaze = true
            else
                set .hasHaze = false
            endif
        endmethod
        
        public method hazeBounds takes real min, real max returns nothing
            set .hazeMin = min
            set .hazeMax = max
        endmethod
        
        method operator hitrange takes nothing returns real
            return .hitRange
        endmethod
        
        method operator hitrange= takes real val returns nothing
            set .hitRange = val
        endmethod
        
        method operator movespeed takes nothing returns real
            return .moveSpd
        endmethod
        
        method operator movespeed= takes real val returns nothing
            set .moveSpd = val
        endmethod
        
        method operator turnspeed takes nothing returns real
            //! the user inputs radians and you handle degrees? Won't radians confuse the user?
            return .turnSpd / TWO_PI
        endmethod
        
        method operator turnspeed= takes real val returns nothing
            set .turnSpd = TWO_PI * val 
        endmethod
        
        method operator active takes nothing returns boolean
            return .isActive
        endmethod
        
        method operator active= takes boolean bol returns nothing
            set .isActive = bol
        endmethod
        
        method operator alive takes nothing returns boolean
            return .isAlive
        endmethod
        
        method operator alive= takes boolean bol returns nothing
            set .isAlive = bol
        endmethod
        
        method operator hitwalls takes nothing returns boolean
            return .hitsWalls
        endmethod
        
        method operator hitwalls= takes boolean bol returns nothing
            set .hitsWalls = bol
        endmethod
        
        method operator hitdests takes nothing returns boolean
            return .hitsDests
        endmethod
        
        method operator hitdests= takes boolean bol returns nothing
            set .hitsDests = bol
        endmethod
        
        method operator hitunits takes nothing returns boolean
            return .hitsUnits
        endmethod
        
        method operator hitunits= takes boolean bol returns nothing
            set .hitsUnits = bol
        endmethod
        
        method operator hitmissles takes nothing returns boolean
            return .hitsMissles
        endmethod
        
        method operator hitmissles= takes boolean bol returns nothing
            set .hitsMissles = bol
        endmethod
        
        //: == ---------------------------- ---------------------------- 
        //: #  Methods - Targeting & Detargeting
        //: == ---------------------------- ---------------------------- 
        //! what if the target is null? then surely hasTarget should be false?
        public method setTargetUnit takes unit u, boolean z returns nothing
            set .end.x = GetUnitX(u)
            set .end.y = GetUnitY(u)
            
            if z then
                set .end.z = GetLocZ(.end.x, .end.y) + GetUnitFlyHeight(u)
            else
                set .end.z = 0.
            endif
            
            set .hasTarget = true
            set .target = u
            set .targetZ = z
        endmethod
        
        public method setTargetUnitPos takes unit u, boolean z returns nothing
            call .setTargetUnit(u, z)
            set .target = null
        endmethod
        
        public method setTargetPos takes real x, real y returns nothing
            set .end.x = x
            set .end.y = y
            set .end.z = GetLocZ(x, y)
            set .target = null
            set .targetZ = false
        endmethod
        
        public method setTargetPosZ takes real x, real y, real z returns nothing
            set .end.x = x
            set .end.y = y
            set .end.z = z
            set .target = null
            set .targetZ = true
        endmethod
        
        //! "lose" is spelled incorrectly in the method name...
        public method looseTarget takes nothing returns nothing
            set .end.x = .x
            set .end.y = .y
            set .end.z = .z
            set .target = null
            set .targetZ = false
        endmethod
        //: == ---------------------------- ---------------------------- 
        //: #  Methods - Missle Movement
        //: == ---------------------------- ---------------------------- 
        //! All these methods are called every frame? That's bad. Really bad. If you MUST split them up, maybe use textmacros?
        private method updateTarget takes nothing returns nothing
            if .hasTarget then
                //! checking if the target is null in the set target method eliminates the need for this check
                //! to check for death / removal, you could use the AutoIndex OnDeindexed events...
                //! or just make a death event and a RemoveUnit hook yourself.
                if .target != null then
                    set .end.x = GetUnitX(.target)
                    set .end.y = GetUnitY(.target)
                    //! avoid using this GetLocZ function every frame -- just handle the location yourself...
                    set .end.z = GetLocZ(.end.x, .end.y) + GetUnitFlyHeight(.target)
                endif
            else
                call .end.applyLoc(.eff.loc)
            endif
        endmethod
        
        private method updateF takes nothing returns nothing
            local real aimF = .eff.loc.angleTo(.end)
            local real newF = 0.
            
            if .eff.f != aimF and not .isTurning then
                set .isTurning = true
                if .eff.f < aimF then
                    set .turnForward = false
                else
                    set .turnForward = true
                endif
            else
                set .isTurning = false
                return 
            endif
            
            if .turnForward then
                //! you have a TWO_PI constant - use it!
                set newF = .eff.f + .turnSpd / (2. * bj_PI * CM_PERIOD) 
                if newF >= aimF then
                    set newF = aimF
                    set .isTurning = false
                endif
            else
                set newF = .eff.f - .turnSpd / (2. * bj_PI * CM_PERIOD)
                if newF <= aimF then
                    set newF = aimF
                    set .isTurning = false
                endif
            endif
            set .f = newF
        endmethod
        
        private method updateXY takes nothing returns nothing
            //! again, lots of these loc function you use every frame, inlining them would increase efficiency
            call .eff.loc.move(.getMissleSpeed() * CM_PERIOD)
            set .eff.loc = .eff.loc
            
            if .eff.loc.distanceTo(.end) < .hitRange then
                if not .targetZ or (.targetZ and .eff.loc.z >= .end.z - .h /2 and .eff.loc.z <= .end.z + .h /2) then
                    set .alive = false
                    set .active = false
                    call .onTargetReach()
                endif
            endif
        endmethod
        
        private method updateZ takes nothing returns nothing
            //! loc functions...
            if .targetZ then
                set .eff.loc.z = .start.z + (.start.z - .end.z) * .eff.loc.distanceTo(.start) / .start.distanceTo(.end)
            endif
        endmethod
        
        private method updateXYarc takes nothing returns nothing
            //! loc functions...
            //! all these variables could be initialised within the first if, increasing efficiency for missiles without xyarcs
            local Loc   new     = Loc.create(.x, .y, .z, .start.angleTo(.end))
            local real  dist    = .start.distanceTo(.end)
            local real  toStart = .eff.loc.distanceTo(.start)
            local real  face    = 0.
            
            if .xyArc != 0. then
                //! To optimise, bj_DEGTORAD * 90 is PI/2. New constant? HALF_PI?
                call new.moveFaced(ParabolaZ(dist * .xyArc, dist, toStart), new.f + bj_DEGTORAD * 90)
                set face = new.angleTo(.end)
                set new.f = face
                call .eff.applyLocOnce(new)
            endif
            
            //! why are these in the xyarc function??? Make a new collision one?
            if .hitsUnits then
                call GroupEnumUnitsInRange(.enumGroup, new.x, new.y, .hitRange, thistype.enumFilter)
            endif
            
            if .hitsDests then
                call SetRect(.locRect, new.x - .hitRange, new.y - .hitRange, new.x + .hitRange, new.y + .hitRange)
                call EnumDestructablesInRect(.locRect, Condition(function thistype.enumDestsInRange), null)
            endif
            
            call new.destroy()
        endmethod
        
        private method updateZarc takes nothing returns nothing
            //! loc functions...
            //! same as above for variables
            local Loc   new     = Loc.create(.x, .y, .z, .start.angleTo(.end))
            local real  dist    = .start.distanceTo(.end)
            local real  toStart = .eff.loc.distanceTo(.start)
            
            if .zArc != 0. then
                set new.z = ParabolaZ2(.start.z, .end.z, .z * .zArc, dist, toStart)
                call .eff.applyLocOnce(new)
            endif
            
            //! again, why is this in an arc function?
            if .hitsWalls then
                if new.z < GetLocZ(new.x, new.y) then
                    call .onWallHit()
                endif
            endif
            
            call new.destroy()
        endmethod
        
        private method haze takes nothing returns nothing
            local Loc   new     = 0
            
            if .hasHaze then
                if hazeNeg then
                    set .hazeCur = .hazeCur - .hazeInc * CM_PERIOD
                    if .hazeCur <= .hazeMin then
                        set .hazeCur = .hazeMin
                        set .hazeNeg = false
                    endif
                else
                    set .hazeCur = .hazeCur + .hazeInc * CM_PERIOD
                    if .hazeCur >= .hazeMax then
                        set .hazeCur = .hazeMax
                        set .hazeNeg = true
                    endif
                endif
                
                //! loc functions...
                set new = Loc.create(.x, .y, .z, .f)
                //! HALF_PI
                call new.moveFaced(.hazeCur, .f + 90 * bj_DEGTORAD)
                call .eff.applyLocOnce(new)
                call new.destroy()
            endif
        endmethod
        
        //: == ---------------------------- ---------------------------- 
        //: #  Methods - Instancemanagement & Instanceexecution
        //: == ---------------------------- ---------------------------- 
        private method move takes nothing returns nothing
            if not .isCreated then
                set .isCreated = true
                call .onCreate()
            else
                call .onLoop()
                
                if .hasDecay then
                    set .decy = .decy - CM_PERIOD
                endif
                
                if .decy < 0. then
                    set .alive = false
                    set .active = false
                endif
            endif
            
            if not .isActive or not .isAlive then
                return
            endif
            
            if not .isStarted then
                set .isStarted = true
                call .onStart()
            endif
            
            call .updateTarget()
            call .updateXY()
            call .updateF()
            call .updateZ()
            call .updateXYarc()
            call .updateZarc()
            
            call .haze()
            
            if .hitsMissles then
                call .enumMissles()
            endif
        endmethod
        
        public static method run takes nothing returns nothing
            local integer i = 0
            
            if thistype.a == 0 then
                call PauseTimer(thistype.t)
                return
            endif
            
            loop
                exitwhen i >= thistype.a
                
                if not thistype.i[i].alive then
                    call thistype.i[i].destroy()
                    set thistype.a = thistype.a -1
                    set thistype.i[i] = thistype.i[thistype.a]
                    set thistype.i[i].index = i
                    set i = i -1
                else
                    set thistype.curInstance = thistype.i[i]
                    call thistype.i[i].move()
                endif
                
                set i = i +1
            endloop
        endmethod
        
        //: == ---------------------------- ---------------------------- 
        //: #  Methods - Enumeration of Missles, Destructables, Units
        //: == ---------------------------- ---------------------------- 
        private method enumMissles takes nothing returns nothing
            local unit      rallyThis   = null
            local unit      rallyThat   = null
            local real      dist        = 0.
            local thistype  that        = 0
            local integer   i           = this.index +1
            local boolean   collide     = false
            
            loop
                exitwhen i >= thistype.a or collide
                set that = thistype.i[i]
                
                set rallyThis = this.eff.getEffUnit()
                set rallyThat = that.eff.getEffUnit()
                
                if not this.targetZ then
                    set dist = DistBetweenUnits(rallyThis, rallyThat)
                else
                    set dist = DistBetweenUnitsZ(rallyThis, rallyThat)
                endif
                
                if dist <= .hitRange and dist != -1 then
                    set collide = true
                endif
                
                set i = i +1
            endloop
            
            set rallyThis = null
            set rallyThat = null
            
            if collide then
                call this.onCollide(that)
                call that.onCollide(this)
            endif
        endmethod
        
        private static method enumUnitsInRange takes nothing returns boolean
            local thistype this = thistype.curInstance
            local unit u = GetFilterUnit()
            local real z = GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
            
            if IsUnitInGroup(u, .enumGroup) then
                return false
            endif
            
            if not .targetZ or (.targetZ and z >= .z - .h /2  and z <= .z + .h /2 ) then
                call GroupAddUnit(.enumGroup, u)
                call .onUnitTouch(u)
            endif
            return false
        endmethod
        
        private static method enumDestsInRange takes nothing returns boolean
            local thistype this = thistype.curInstance
            call .onDestTouch(GetFilterDestructable())
            return false
        endmethod
        
        //: == ---------------------------- ---------------------------- 
        //: #  Methods - Destroying & Filter init
        //: == ---------------------------- ---------------------------- 
        private method onDestroy takes nothing returns nothing
            set .alive = false
            set .active = false
            call .onEnd()
            call .start.destroy()
            call .end.destroy()
            call DestroyGroup(.enumGroup)
            call RemoveRect(.locRect)
            call DestroyGroup(.enumGroup)
            call .eff.destroy()
        endmethod
        
        private static method onInit takes nothing returns nothing
            set thistype.enumFilter = Condition(function thistype.enumUnitsInRange)
        endmethod
    endstruct

endlibrary

All those functions you call... ouch. You seriously need to inline them, whether through turning them into textmacros or just writing them out...

Other than that, I only saw a few minor inefficiencies. It's good code, mostly.
 
JASS:
/* Users are so innocent */
            //! what if users want to specify an absolute height?
            set z               = GetLocZ(x, y) + z
I guess that would be a small problem. So I will change it again? I wanted to make it noobfriendly to automatically convert it to the absolute height.

the user inputs radians and you handle degrees? Won't radians confuse the user?
What? You mean the other way around.

what if the target is null? then surely hasTarget should be false?
Yes,... I was thinking the user would be clever enough to check for a valid target.

//! All these methods are called every frame? That's bad. Really bad. If you MUST split them up, maybe use textmacros?
Hmm I will think about it.

checking if the target is null in the set target method eliminates the need for this check to check for death / removal, you could use the AutoIndex OnDeindexed events...
Or just make a death event and a RemoveUnit hook yourself.
Hmm. I don't want to use that much external librarys.

Avoid using this GetLocZ function every frame -- just handle the location yourself...
How?

Why are these in the xyarc function??? Make a new collision one?
Because I temporary overwrite the missles coordinates and do not save them.
If I would move this functions outside it would enumerate not at the current missle position but on the one that would be default (the straight way) if the missle has xyArc.

All those functions you call... ouch. You seriously need to inline them, whether through turning them into textmacros or just writing them out...
Hmm. Why?

Other than that, I only saw a few minor inefficiencies. It's good code, mostly.
Thanks, glad to hear that.


New Update!

Missles are now HIDDEN when they would fly BELOW terrain!
I also added 2 example spells. (One chain and one target)
(The chain spell isn't finished and does not deal any damage)

Have fun!
 
Last edited:
I guess that would be a small problem. So I will change it again? I wanted to make it noobfriendly to automatically convert it to the absolute height.
Maybe make a function, Z2Abs?

What? You mean the other way around.
Hmm, I read it wrong. It doesn't do either. The user works in multiples of PI? Is it meant to simulate units' turnspeed?

Yes,... I was thinking the user would be clever enough to check for a valid target.
You need to handle all possible user errors.

Hmm I will think about it.
Do it. Each function call takes a few nanoseconds to process, in addition to the actual contents of the function. Even DoNothing takes some processing power. It all stacks up, especially if you do it a lot. And you do it a hell of a lot.

Hmm. I don't want to use that much external librarys.
People make those libraries for a reason...

Just have a global or static location that you can move and GetLocationZ instead of using GetLocZ.

Because I temporary overwrite the missles coordinates and do not save them.
If I would move this functions outside it would enumerate not at the current missle position but on the one that would be default (the straight way) if the missle has xyArc.
Then save them in different variables, like arcpos maybe?

Hmm. Why?
See above.

EDIT:
Wait, doesnt it spells "Missile"? (sorry for abit randomness)
He's right. I must have missed that :p
 
Maybe make a function, Z2Abs?
No, it's always absolute now.

Hmm, I read it wrong. It doesn't do either. The user works in multiples of PI? Is it meant to simulate units' turnspeed?
Yes.

You need to handle all possible user errors.
Not really. (It's about efficiency)

Do it. Each function call takes a few nanoseconds to process, in addition to the actual contents of the function. Even DoNothing takes some processing power. It all stacks up, especially if you do it a lot. And you do it a hell of a lot.
But that's nothing? I tested it with 20 missiles and nothing changed. Will test with more soon.

Then save them in different variables, like arcpos maybe?
You tell me about efficiency and then want to make a new variable for that stuff just to move the stuff out site? No.


#Update
  • Testmap now has cliffs
  • More creeps
  • Z works like a charm now
  • Bugfixes
 

Attachments

  • CM_UP_v.0.0.B.w3x
    79.8 KB · Views: 105
Last edited:
it doesn't use xe
It's made for replacing xe.

it is not modular for not using xe
See above.

it doesn't allow you to change the model of the missile after creation
It easily does,
JASS:
set .sfx        = "Abilities\\Spells\\Undead\\OrbOfDeath\\AnnihilationMissile.mdl"

I couldn't understand it but ... can you "stop" the missile in the air? That would be something nice.
Of course. You should check the system first please.
Anachron said:
JASS:
        //: This will return the missle speed.
        //: Use this method to slow / freeze missles.
        method getMissleSpeed takes nothing returns real defaults 0.
But nice try for the bashing.
 
No, it's always absolute now.
Fair enough.

Not really. (It's about efficiency)
In a function that is run once in fairly rare events, you don't need to care about efficiency.

But that's nothing? I tested it with 20 missiles and nothing changed. Will test with more soon.
It's not nothing. There are loads of unnecessary function calls! And 20 is barely any. HindyHAT's PHYSICS system (which does a lot more calculations etc than this) can handle 200 entities at once.

You tell me about efficiency and then want to make a new variable for that stuff just to move the stuff out site? No.
You already have a local variable, so making it part of the struct and removing the local isn't going to have much impact on efficiency.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Element of Water said:
And 20 is barely any. HindyHAT's PHYSICS system (which does a lot more calculations etc than this) can handle 200 entities at once.

I think (though I haven't tested on a computer other than my desktop) that my system can handle a little over 250, maybe 300. If your computer is better than a single-core AMD processor running at 2.2GHz you will probably be able to function with a hell of a lot more on the field.

Anachron, if you want any help improving the efficiency or if you are having any troubles give me a PM.
 
Status
Not open for further replies.
Top