• 🏆 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!

Aerial Strike v1.1

  • Like
Reactions: Losam and Squiggy
ekgwv8.jpg


Helluuu another spell because I'm loser noob nerd bor'd -----_-----

Notes: This is MUI of course - but uh.. level 3 is 96 bombs being handled per second. You don't want to use it more than a couple times at once. Of course, how many bombs you drop and all that is configurable.

Changelog:
v1.1 - Changed bj_DEGTORAD stuff, uses mapbounds (thanks Mag), Added a filter, inlined Atan2 and Distance stuff, Took a lot of fluff out, that I feel it didn't need;;; just improved code in generalllll
v1.0 - Initial Release

Thanks to:
- Cohadar for TT
- Nestharus for WorldBounds
- Adikutz for MapBounds
- Mechanical Man & Crusad3r91 for the Fireprism model(bombship)
- zboc for BTNExplosion
- Fingolfin for Marauder Missile model(bombs)
- Hiveworkshop for such a great community :)

I hope you enjoy, I will continuously upgrade this spell.

JASS:
scope AerialStrike 

/*********************************************************************************/
/*                                                                               */
/*                          Aerial Strike v1.1                                   */
/*                              by: msongyyy                                     */
/*                                                                               */
/*      HOW TO IMPORT:                                                           */
/*          1. Import everything in the Import Manager to the correct paths      */
/*          2. Import the Aerial Strike - Missile, Plane units and ability       */
/*          3. Set the rawcode ID's below to the correct ones                    */
/*                                                                               */
/*          ENJOY ^^                                                             */
/*                                                                               */
/*      THANKS TO:                                                               */
/*          1. Cohadar for TT                                                    */
/*          2. Nestharus for WorldBounds                                         */
/*          3. Adikutz for MapBounds                                             */
/*          3. Mechanical Man & Crusad3r91 for the Fireprism model(bombship)     */
/*          4. zboc for BTNExplosion used                                        */
/*          5. Fingolfin for Marauder missile(bombs)                             */
/*                                                                               */
/*********************************************************************************/

    globals
        private constant integer ABIL_ID = 'A000'           // Rawcode ID of Aerial Strike Spell
        private constant integer MISS_ID = 'h003'           // Rawcode ID of Aerial Strike - Missiles unit
        private constant integer PLANE_ID = 'h002'          // Rawcode ID of Aerial Strike - Plane unit
        private constant string FX = "Abilities\\Weapons\\Mortar\\MortarMissile.mdl"        // Effect created when a bomb explodes

        private constant timer SPEED_TIMER  = CreateTimer()
        
        private constant real DEPLOY_PERIOD             = .1                            // How often the bombship drops bombs
        private constant real SPEED_PERIOD              = .0312500                      // How often the bombship, and bombs move
        private constant real PLANE_SPEED               = 700.00 * SPEED_PERIOD         // Speed of the bombship
        private constant real MISSILE_SPEED             = 300.00 * SPEED_PERIOD         // Speed of the bombs
        
        private constant damagetype DAMAGE_TYPE         = DAMAGE_TYPE_NORMAL
        private constant attacktype ATTACK_TYPE         = ATTACK_TYPE_MAGIC
        private constant weapontype WEAPON_TYPE         = null
        
        private group g = CreateGroup()
        private unit fog
    endglobals
    
    // How many bombs dropped per DEPLOY_PERIOD
    private constant function MISSILE_COUNT takes integer i returns integer
        return 5
    endfunction
    
    // Distance travelled by plane
    private constant function DIST takes integer i returns real
        return i * 300. + 500.
    endfunction
    
    
    // Damage dealt by bombs
    private constant function DMG takes integer i returns real 
        return i * 25. + 30.
    endfunction
    
    
    // Damage aoe of bombs
    private constant function DMG_AOE takes integer i returns real 
        return 135.
    endfunction
    
    private function FILTER takes nothing returns boolean
        if IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) then
            return false
        endif
        
        return true
    endfunction
    
/*********************************************************************************/
/*        END OF CONFIGURABLES                                                   */
/*********************************************************************************/

    private struct Bombs extends array
        private unit caster
        private player play
        private unit missile
        private real ex
        private real ey
        private real ang
        private real dmg
        private real aoe
        private real hinc
        
        private static integer array prev 
        private static integer array next
        private static integer array rn
        private static integer ic = 0
        
        static method Drop takes nothing returns nothing
            local thistype this = next[0]
            local real x
            local real y
            
            loop
                exitwhen this == 0
                
                if GetUnitFlyHeight(.missile) <= 15 then
                    call DestroyEffect(AddSpecialEffect(FX, .ex, .ey))
                    call GroupEnumUnitsInRange(g, .ex, .ey, .aoe, null)
                    loop
                        set fog = FirstOfGroup(g)
                        exitwhen fog == null
                        if IsUnitEnemy(fog, .play) then
                            call UnitDamageTarget(.caster, fog, .dmg, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
                        endif
                        call GroupRemoveUnit(g, fog)
                    endloop
                    call RemoveUnit(.missile)
                    set .missile = null
                    set .caster = null
                    set .play = null
                    set prev[next[this]] = prev[this]
                    set next[prev[this]] = next[this]
                    set rn[this] = rn[0]
                    set rn[0] = this
                else
                    set x = GetUnitX(.missile) + MISSILE_SPEED * Cos(.ang)
                    set y = GetUnitY(.missile) + MISSILE_SPEED * Sin(.ang)
                    
                    set x = GetBoundedX(x)
                    set y = GetBoundedY(y)
                    
                    call SetUnitX(.missile, x)
                    call SetUnitY(.missile, y)
                    call SetUnitFlyHeight(.missile, GetUnitFlyHeight(.missile) - .hinc, 0)
                endif
                set this = next[this]
                
                if next[0] == 0 then
                    call PauseTimer(SPEED_TIMER)
                endif
            endloop
        endmethod
        
        static method Start takes unit caster, unit plane, player play returns nothing
            local thistype this = rn[0]
            local real sx = GetUnitX(plane)
            local real sy = GetUnitY(plane)
            local real rang = GetRandomReal(-90,90)
            
            if this == 0 then
                set ic = ic + 1
                set this = ic
            else
                set rn[0] = rn[this]
            endif
            
            set .caster = caster
            set .play = play
            set .ex = sx + 300.00 * Cos((GetUnitFacing(plane) + rang) * bj_DEGTORAD)
            set .ey = sy + 300.00 * Sin((GetUnitFacing(plane) + rang) * bj_DEGTORAD)
            set .ang = Atan2(.ey-sy, .ex- sx)
            set .hinc = 300/(SquareRoot((.ex-sx)*(.ex-sx) + (.ey-sy)*(.ey-sy))/MISSILE_SPEED)
            set .missile = CreateUnit(.play, MISS_ID, sx, sy, .ang * bj_RADTODEG)
            set .dmg = DMG(GetUnitAbilityLevel(.caster, ABIL_ID))
            set .aoe = DMG_AOE(GetUnitAbilityLevel(.caster, ABIL_ID))
            
            set .ex = GetBoundedX(.ex)
            set .ey = GetBoundedY(.ey)
            
            call UnitAddAbility(.missile, 'Amrf')
            call UnitRemoveAbility(.missile, 'Amrf')
            
            set prev[this] = prev[0]
            set next[this] = 0
            set next[prev[0]] = this
            set prev[0] = this
            
            if prev[this] == 0 then
                call TimerStart(SPEED_TIMER, SPEED_PERIOD, true, function thistype.Drop)
            endif
        endmethod
    endstruct
    
    private struct Aerial extends array   
        private unit caster
        private unit plane
        private player play
        private real ang
        private real sx
        private real sy
        private real dist
        private real tdist
        private integer maxCount
        
        private static integer array rn
        private static integer ic = 0
        
        static method Strike takes nothing returns boolean
            local thistype this = TT_GetData()
            local real tempx
            local real tempy
            
            if .dist > .tdist then
                call RemoveUnit(.plane)
                set .play = null
                set .plane = null
                set .caster = null
                set rn[this] = rn[0]
                set rn[0] = this
                return true 
            else
                set tempx = GetUnitX(.plane) + PLANE_SPEED * Cos(.ang)
                set tempy = GetUnitY(.plane) + PLANE_SPEED * Sin(.ang)
                
                set tempx = GetBoundedX(tempx)
                set tempy = GetBoundedY(tempy)
                
                call SetUnitX(.plane, tempx)
                call SetUnitY(.plane, tempy)
                set .dist = .dist + PLANE_SPEED
            endif
            
            return false 
        endmethod
        
        static method Deploy takes nothing returns boolean
            local thistype this = TT_GetData()
            local integer i = 0
            
            if .plane == null then
                return true 
            endif
            
            loop
                exitwhen i == maxCount
                call Bombs.Start(.caster, .plane, .play)
                set i = i + 1
            endloop
            
            return false 
        endmethod
        
        static method Start takes unit caster, real radians returns nothing
            local thistype this = rn[0]
    
            if this == 0 then
                set ic = ic + 1
                set this = ic
            else
                set rn[0] = rn[this]
            endif
            
            set .caster = caster
            set .play = GetTriggerPlayer()
            set .ang = radians
            set .sx = GetUnitX(.caster) - 300 * Cos(.ang)
            set .sy = GetUnitY(.caster) - 300 * Sin(.ang)
            set .plane = CreateUnit(.play, PLANE_ID, .sx, .sy, .ang * bj_RADTODEG)
            set .dist = 0
            set .tdist = DIST(GetUnitAbilityLevel(.caster, ABIL_ID))
            set .maxCount = MISSILE_COUNT(GetUnitAbilityLevel(.caster, ABIL_ID))
            
            call TT_StartEx(function thistype.Strike, this, SPEED_PERIOD)
            call TT_StartEx(function thistype.Deploy, this, DEPLOY_PERIOD)
        endmethod
        
        static method Cond takes nothing returns boolean
            local unit lvCaster
            
            if GetSpellAbilityId() == ABIL_ID then
                set lvCaster = GetTriggerUnit()
                call Aerial.Start(lvCaster, Atan2(GetSpellTargetY() - GetUnitY(lvCaster), GetSpellTargetX() - GetUnitX(lvCaster)))
                set lvCaster = null
            endif
            
            return false 
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t, Condition(function thistype.Cond))
        
            set t = null
        endmethod
    endstruct
endscope

Keywords:
Aerial, strike, bombs, ownage, fire, power, explosion, armageddon, death, red, gunship, ship, guns, gun, plane, ship, bombs, boom, spell
Contents

Aerial Strike v1.1 (Map)

Reviews
Aerial Strike v1.1 | Reviewed by Maker | 12th Oct 2013 APPROVED The spell is ok but it lacks a bit of smoothness and polish In FILTER, return IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) You don't need a unit...

Moderator

M

Moderator


Aerial Strike v1.1 | Reviewed by Maker | 12th Oct 2013
APPROVED


126248-albums6177-picture66521.png


  • The spell is ok but it lacks a bit of smoothness and polish
126248-albums6177-picture66523.png


  • In FILTER, return IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING)
  • You don't need a unit variable in Cond
  • Why do you make functions return a boolean when they do not
    need to return anything?
  • The airship could use a sound effect and it appears/disappears abruptly
  • Add follow through time
  • Units with Locust are already invulnerable, no need for Invulnerability ability
126248-albums6177-picture66524.png
[tr]


16:56, 30th Jun 2013
Magtheridon96:

Comments

  • There's no point in converting the angle from radians to degrees, then back from degrees to radians in some parts of the code. I'd recommend just having GetAngle return the angle in radians and when you need it in degrees, multiple by bj_RADTODEG.
  • You might want to look into MapBounds. It's a tiny extension to WorldBounds and it will make your code shorter, because instead of having to compare the coordinates to the world bounds, you can get x/y coordinates guarenteed to be bounded with a call of a function ;p
  • Could you add the requirements in a list in the documentation with links to the resources to make everything perfectly explicit? (It's in the Submission rules - You need at least an author name, a resource name and a requirements list, along with importing instructions if needed)
  • Could you make the filter you're using inside the Bombs struct configurable easily by the user via a function (UnitFilter) right under all the other configuration functions in your code?
 
Level 8
Joined
Feb 3, 2013
Messages
277
get rid of all of these. bj_DEGTORAD

yeah sorry I gotta get rid of that in all my spells, it's just that some times I use the GetUnitFacing() so I get confused which ones I have to convert and which ones I don't. My custom GetAngle() currently returns a radians number too. But it shouldn't be that bad. Thanks for your response, always nice to see some kind of feedback :).
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
- The lvCaster and angle should be below the GetSpellAbilityId()
- remove the RADTODEG so that no need to DEGTORAD
- You may save the level with the damage/aoe etc...at cast time so that you dont need to itinerate it every loop which is very very slow
- Bombs.Start(.caster, .plane) >>> Bombs.Start(.caster, .plane, .play) and put this 'i = 0' above the loop
- make the attack/damage configurable
 
Level 9
Joined
Dec 3, 2010
Messages
162
The current FILTER function can be improved. Instead of having constant booleans, you can just handle all unit type checking within your FILTER function. If you really want to keep the booleans, you should use a static if instead.

Configuration functions should take an integer instead of a unit to save GetUnitAbilityLevel calls. Also, your configuration functions can be constant.

GetDistance and GetAtan is unnecessary. Use the native functions directly.

FirstOfGroup loops can use bj_lastCreatedGroup instead to save you from creating 1 group.
 
Top