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

Suicide Squad v1.0b

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
This spell was designed for the Spells & Systems Mini-Contest #19



Calls a Zeppelin flying in the target direction dropping several goblin sapper on its way. Goblin sapper will kindle their explosion once falling on the ground and search for enemy units. Once a sapper found a valid target, it will run to the target unit to suicide. The sapper will explode once reaching the target or after the explosive blow up. The explosion deals magical damage to nearby enemies. If the target a sapper is following dies, it will search for a new one until its explosive blows up. Goblin sappers have 100 health and are magic immune.

Level 1: Drops 4 sappers, 15 explosion damage, explodes after 7 seconds.
Level 2: Drops 5 sappers, 30 explosion damage, explodes after 8 seconds.
Level 3: Drops 6 sappers, 45 explosion damage, explodes after 9 seconds.



  • Table [Vexorian]
  • ListModule [grim001]
  • xebasic, xedamage & xefx [Vexorian]
  • ARGB [Vexorian]
  • SoundUtils [Rising_Dusk]
  • GroupUtils [Rising_Dusk]
  • Z-Utils [Kricz]




JASS:
 /*=====================================================================*\
//======                      SUICIDE SQUAD                        =====*\\
\\======                       Version 1.0b                        ======//
 \\======                        by Kricz                         ======//
 ||                                                                     ||
 ||                                                                     ||
 ||                              Requires:                              ||
 \\                          Table [Vexorian]                          //  
  \\                       ListModule [grim001]                       //
   \\              xebasic, xedamage & xefx [Vexorian]               //
    \\                       ARGB [Vexorian]                        //
    ||                  SoundUtils [Rising_Dusk]                   ||
    ||                  GroupUtils [Rising_Dusk]                   ||
    ||                       Z-Utils [Kricz]                       ||
   //                                                              \\
  //                           Optional:                            \\
  ||              AutoFly [Kricz] (requires AutoIndex)              ||
  ||                    AbilityPreload [grim001]                    ||
  ||                                                                ||
  ||    Please give credits if you use this spell in your map.      ||
  ||                                                                ||
  \*==============================================================?=*/
                    
scope SuicideSquad initializer onInit

    /*Main Setup*/
    globals
        
        //The Spell-Id of the SuicideSquad Ability
        private constant integer SPELL_ID                       = 'A000'
        
        //The Timer-Interval of timers for movements and checks
        //Default Value: 0.05
        private constant real TIMER_INTERVAL                    = 0.05
        
        //If true, the system will use sounds using Rising_Dusk's SoundUtils Library.
        //You can manually disable specific sounds, by setting their path to "".
        private constant boolean USE_SOUNDS                     = true         

    endglobals
    
    /*Sapper Setup*/
    globals
    
        //The Unit-Id of the Sapper
        private constant integer SAPPER_UNIT_ID                     = 'sapp'
        
        //The speed of the down-falling sappers
        //Default Value: 375.00
        private constant real SAPPER_DROP_SPEED                     = 375.00
        
        //The radius, in where sappers search valid targets
        //Default Value: 700.00
        private constant real SAPPER_TARGET_SEARCH_AREA             = 700.00
        
        //If this is true, the sapper will walk to the position of the target unit once it dies
        //and search for new targets while walking. If false, the sapper will stop walking and
        //then search for new targets
        //Default Value: false
        private constant boolean SAPPER_STOP_WHEN_TARGET_DIES       = false
        
        //This is the distance a sapper has to be with its target to make suicide.
        //Default Value: 150.00
        private constant real SAPPER_SUICIDE_RANGE                  = 150.00
        
        //If true, the sappers are invulnerable while falling down and become normal once reaching the gorund
        //Default Value: true
        private constant boolean SAPPER_INVULNERABLE_WHILE_FALLING  = true
        
        //This is the animation-id which is played indstead of the normal death-animation.
        //Information: 3 is the "spell death" animation, which shows, that the sapper does suicide
        //4 is the normal death animation. Ofcourse you could use any other number, if you are funny :D
        //Default Value: 3
        private constant integer SAPPER_SUICIDE_ANIMATION_ID        = 3
        
        //This is the effect that is created when a sapper is dropped by a zeppeling and falling down
        //Use "" for no effect.
        //Default Value: "Abilities\\Spells\\Other\\Tornado\\Tornado_Target.mdl"
        private constant string SAPPER_DROP_EFFECT                  = "Abilities\\Spells\\Other\\Tornado\\Tornado_Target.mdl"
        
        //This is the attachment point of the drop effect above.
        //Default Value: "chest"
        private constant string SAPPER_DROP_EFFECT_ATTACHPOINT          = "chest"
        
        //This is the effect that is created once a sapper reaches the ground and kindles himself
        //Default Value: "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"
        private constant string SAPPER_BURN_EFFECT                  = "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"
        
        //This is the attachment point of the burn effect above
        //Default Value: "chest"
        private constant string SAPPER_BURN_EFFECT_ATTACHPOINT          = "chest"
        
        //This is the effect that is displayed, when a sapper found a target
        //Set it to "" if you want no effect.
        //Default Value: "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl"
        private constant string SAPPER_TARGET_FOUND_EFFECT          = "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl"
        
        //This is the attachment point of the target-found effect above
        //Default Value: "overhead"
        private constant string SAPPER_TARGET_FOUND_EFFECT_ATTACHPOINT  = "overhead"
        
        //The effect created by the explosion. Set it to "" for no effect
        //Default Value: "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
        private constant string SAPPER_EXPLOSION_EFFECT             = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
        
        
        //This boolean decides, whether the movespeed of a sapper should be changed using
        //the native SetUnitMoveSpeed or not. In some maps, changing the movespeed via trigger
        //may cause some bugs with other systems/spells.
        //If you dont want, that the movespeed is changed using SetUnitMoveSpeed, change it in the OE.
        //Default Value: true
        private constant boolean SAPPER_ENABLE_MOVESPEED_CHANGE     = true
        
        //This is the movespeed of sappers. It's only used, if the value above is set to true
        //Default Value: 250.00
        private constant real SAPPER_MOVEMENT_SPEED                     = 250.00
        
        //If this boolean is set a true, a texttag is created above each sapper, showing when it will
        //explode. Please note, that you can only have max. 100 Texttags active at the same time, so
        //disable it, when you think you could reach more than 100.
        //Default Value: true
        private constant boolean SAPPER_SHOW_EXPLOSION_INDICATOR    = true
        
        //This is the color of the texttag. Read the ARGB Readme for mroe information about how to use it.
        //Default Value: 0xFFFF0000 (red)
        private ARGB SAPPER_EXPLOSION_TIME_INDICATOR_COLOR              = 0xFFFF0000
            
        //This is the size of the texttag.
        //Default Value: 10.00
        private constant real SAPPER_EXPLOSION_TIME_INDICATOR_SIZE      = 10.00
            
        //This is the height of the texttag.
        //Default Value: 75.00
        private constant real SAPPER_EXPLOSION_TIME_INDICATOR_HEIGHT    = 75.00
        
        //If this boolean is set to true, the damage will be done by the caster, not by the sapper
        //that exploded. Result of that is, the each unit, that received damage through the explosion
        //will attack the caster. If false, they will just do like nothing, cause they cannnot attack
        //dead units (the sapper).
        //Default Value: true
        private constant boolean SAPPER_EXPLOSION_USE_CASTER_AS_SOURCE  = true
        
        //This is the attack type which xedamage uses once a sapper does suicide or explodes
        //Default Value: ATTACK_TYPE_MAGIC
        private constant attacktype SAPPER_EXPLOSION_ATTACK_TYPE    = ATTACK_TYPE_MAGIC
        
        //This is the damage type which xedamage uses once a sapper does suicide or explodes
        //Default Value: DAMAGE_TYPE_FIRE
        private constant damagetype SAPPER_EXPLOSION_DAMAGE_TYPE    = DAMAGE_TYPE_FIRE
        
        //This is the radius of the explosion.
        //Default Value: 250.00
        private constant real SAPPER_EXPLOSION_RADIUS               = 250.00
        
        //If true, the explosion will also deal damage to destructables in the explosion area
        //Default Value: true
        private constant boolean SAPPER_EXPLOSION_DAMAGE_DESTRUCTS  = true
        
        //If this boolean is set to true, the explosion will also damage allied unit.
        //Default Value: false
        private constant boolean SAPPER_EXPLOSION_DAMAGE_ALLIED     = false
        
        //If this boolean is true, sappers will play a random sound, 
        //which you can add/remove/change in the SAPPER_INIT_SOUNDS textmacro below
        //Default Value: true
        private constant boolean SAPPER_ENABLE_FUNNY_SOUNDS         = true
        
        //This value is only important, if funny sounds of sappers are enabled.
        //Because it may sounds stupid, when 5 sappers speak at the same time, 
        //there is this possibility to give each sapper only a chance to tell something
        //which you can setup with this value.
        //Default Value: 0.33 (33%)
        private constant real SAPPER_FUNNY_SOUND_CHANCE             = 0.30
        
        
    endglobals
    
    
    //in this macro, you can add funny sounds for sappers. 
    //the function you have to call is:
    //call addSapperSound(string soundHandle, real duration)
    //soundHandle is the path of the sound, you can get them i.e from the SoundEditor (F5)
    //duration is the duration of the sound, you can also get this information in the SoundEditor
    //Example: call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYes1.wav", 1.463)
    //! textmacro SAPPER_INIT_SOUNDS
        
        call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYes1.wav", 1.463)
        call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYes5.wav", 1.434)
        call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperWhat2.wav", 1.694)
        call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYesAttack3.wav", 2.026)
        call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYesAttack4.wav", 1.091)
        call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed1.wav", 1.515)
        call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed2.wav", 2.776)
        call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed4.wav", 2.554)
        call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed5.wav", 0.592)
        call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed6.wav", 2.201)
        call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed7.wav", 2.374)
    
    //! endtextmacro
    
    //This is the amount of sappers dropped each level.
    //Default Value: 3 + lvl
    private constant function SAPPER_AMOUNT takes integer lvl returns integer
        return 3 + lvl
    endfunction
    
    //This is the time, a sapper has until it will explode
    //Default Value: 6.00 + lvl
    private constant function SAPPER_EXPLOSION_TIMER takes integer lvl returns real
        return 6.00 + lvl
    endfunction
    
    //This is the damage a explosion deals.
    //Default Value: 25.00 + 25.00 * lvl
    private constant function SAPPER_EXPLOSION_DAMAGE takes integer lvl returns real
        return 15.00 * lvl
    endfunction
    
    //This is the filter used to get valid targets for sappers.
    //Default Value: not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitType(u, UNIT_TYPE_GROUND) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitVisible(u, GetOwningPlayer(sapper)) and IsUnitEnemy(u, GetOwningPlayer(sapper)) 
    private constant function SAPPER_TARGET_SEARCH_FILTER takes unit u, unit sapper returns boolean
        return not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitType(u, UNIT_TYPE_GROUND) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitVisible(u, GetOwningPlayer(sapper)) and IsUnitEnemy(u, GetOwningPlayer(sapper)) 
    endfunction
    
    
    /*Zeppelin Setup*/
    globals
    
        //The model of the zeppelin that drops the sappers
        //Default Value: "units\\creeps\\GoblinZeppelin\\GoblinZeppelin.mdl"
        private constant string ZEPPELIN_MODEL                  = "units\\creeps\\GoblinZeppelin\\GoblinZeppelin.mdl"
        
        //The height of the zeppelin. Please note, that the higher this value is, the longer a sapper requires
        //to reach the ground. If you change this value, you may should change the drop speed of the sappers too.
        //Default Value: 500.00
        private constant real ZEPPELIN_HEIGHT                   = 500.00
        
        //The model-size of the zeppelin.
        //Default Value: 0.80
        private constant real ZEPPELIN_SIZE                     = 0.80
        
        //The speed the zeppelin is moving with. This does not effect the distance, when a sapper is dropped
        //The formula for the distance for a sapper drop is:
        //ZEPPELIN_MOVE_DISTANCE / (SAPPER_DROP_AMOUNT(lvl) + 1)
        //Default Value: 400.00
        private constant real ZEPPELIN_SPEED                    = 400.00
        
        //This is the distance the zeppelin moves until is expires.
        //Default Value: 1000.00
        private constant real ZEPPELIN_MOVE_DISTANCE            = 1000.00
        
        //If this boolean is set to true, the zeppelin will display its death animation
        //If false, it will be just removed without any animation
        //Default Value: false
        private constant boolean ZEPPELIN_DESTROY_ON_END        = false
        
        //Zeppelin Sounds
        //This is the sound which is played when the spell is cast. Set this to "" if you want no sound.
        //Default Value: "Units\\Creeps\\GoblinZeppelin\\GoblinZeppelinWhat3.wav"
        private constant string ZEPPELIN_SUMMON_SOUND           = "Units\\Creeps\\GoblinZeppelin\\GoblinZeppelinWhat3.wav"
    
        //This is the duration of the sound above
        //Default Value: 1.838
        private constant real ZEPPELIN_SUMMON_SOUND_DUR         = 1.838
        
        //This sound is played whenever a zeppelin drops a sapper. Set it to "" if you want no sound.
        //Default Value: "Abilities\\Spells\\Other\\LoadUnload\\Loading.wav"
        private constant string ZEPPELIN_SAPPER_DROP_SOUND      = "Abilities\\Spells\\Other\\LoadUnload\\Loading.wav"
    
        //This is the duration of the sound above
        //Default Value: 0.74
        private constant real ZEPPELIN_SAPPER_DROP_SOUND_DUR    = 0.74
    
    endglobals
    
  //=================================================================//
 // Do not touch anything below unless you know what you are doing! //
//=================================================================//
    
    private keyword Main
    private keyword Zeppelin
    private keyword Sapper
    
    private struct Sapper
        unit caster = null
        unit target = null
        unit sapper = null
        
        real time = 0.00
        real x = 0.00
        real y = 0.00
        real tx = 0.00
        real ty = 0.00
        real z = 0.00
        real explosion = 0.00
        
        texttag explosionIndicator = null
        
        integer lvl = 0
        
        effect dropEffect = null
        effect burnEffect = null
        
        player owner = null
        
        boolean onGround = false
        boolean oldTargetDied = false
        
        static trigger onDeath = CreateTrigger()
        static trigger onOrder = CreateTrigger()
        static timer ticker = CreateTimer()
        static thistype temp = 0
        static HandleTable sapperTable = 0
        static HandleTable targetTable = 0
        static delegate xedamage dmg = 0
        static boolean ignoreOrders = false
        static real dropSpeed = SAPPER_DROP_SPEED * TIMER_INTERVAL
        
        static integer array sapperSound
        static integer sapperSoundAmount = 0
        
        implement List
        
        static method addSapperSound takes string soundHandle, real duration returns nothing
            set sapperSound[sapperSoundAmount] = DefineSound(soundHandle, R2I(duration * 1000), false, true)
            set sapperSoundAmount = sapperSoundAmount + 1
        endmethod
        
        static method unitFilter takes nothing returns boolean
            return SAPPER_TARGET_SEARCH_FILTER(GetFilterUnit(), temp.sapper)
        endmethod
        
        method onDestroy takes nothing returns nothing
            call sapperTable.flush(sapper)
        
            call listRemove()
            if count == 0 then
                call PauseTimer(ticker)
            endif
            
            static if SAPPER_SHOW_EXPLOSION_INDICATOR then
                call DestroyTextTag(explosionIndicator)
            endif
        endmethod
        
        method searchForTarget takes nothing returns boolean
            set temp = this
            call GroupEnumUnitsInArea(ENUM_GROUP, x, y, SAPPER_TARGET_SEARCH_AREA, Condition(function thistype.unitFilter))
            //These are valid BJ's which SHOULD be used...
            if CountUnitsInGroup(ENUM_GROUP) > 0 then
                set target = GroupPickRandomUnit(ENUM_GROUP)
                //Using the table to save whether a unit is already registred to the trigger or not
                if targetTable[target] == 0 then
                    call TriggerRegisterUnitEvent(onDeath, target, EVENT_UNIT_DEATH)
                    set targetTable[target] = 1
                endif
                set tx = GetUnitX(target)
                set ty = GetUnitY(target)
                call IssueTargetOrder(sapper, "attack", target)
                            
                if SAPPER_TARGET_FOUND_EFFECT != "" then
                    call DestroyEffect(AddSpecialEffectTarget(SAPPER_TARGET_FOUND_EFFECT, sapper, SAPPER_TARGET_FOUND_EFFECT_ATTACHPOINT))
                endif
                            
                static if SAPPER_ENABLE_FUNNY_SOUNDS then
                    if GetRandomReal(0.00, 1.00) <= SAPPER_FUNNY_SOUND_CHANCE then
                        call RunSoundOnUnit(sapperSound[GetRandomInt(0, sapperSoundAmount - 1)], sapper)
                    endif
                endif
            else
                set target = null
            endif
            return target != null
        endmethod
        
        
        method moveSapper takes nothing returns nothing
            if target == null then
                static if not SAPPER_STOP_WHEN_TARGET_DIES then
                    if oldTargetDied then
                        call IssuePointOrder(sapper, "smart", tx, ty)
                    else
                        call PauseUnit(sapper, true)
                        call IssueImmediateOrder(sapper, "stop")
                        call PauseUnit(sapper, false)
                    endif
                else
                    call PauseUnit(sapper, true)
                    call IssueImmediateOrder(sapper, "stop")
                    call PauseUnit(sapper, false)
                endif
            else
                if not IsUnitVisible(target, owner) then
                        
                    if GetUnitCurrentOrder(sapper) == 851983 then
                        //if target becomes invisible or gets out of sight, the sapper will
                        //run to the position, where the target was last seen.
                        call IssuePointOrder(sapper, "smart", tx, ty)
                    endif
                            
                else                            
                    if IsUnitType(target, UNIT_TYPE_DEAD) then
                        if not searchForTarget() then
                            set oldTargetDied = true
                            static if not SAPPER_STOP_WHEN_TARGET_DIES then
                                call IssuePointOrder(sapper, "smart", tx, ty)
                            else
                                call PauseUnit(sapper, true)
                                call IssueImmediateOrder(sapper, "stop")
                                call PauseUnit(sapper, false)
                            endif
                        else
                            set oldTargetDied = false
                        endif
                    elseif GetUnitCurrentOrder(sapper) != 851983 then
                        call IssueTargetOrder(sapper, "attack", target)
                    endif
                endif
            endif
        endmethod
        
        static method onLoop takes nothing returns nothing
            local thistype this = first
            local real dx = 0.00
            local real dy = 0.00
            
            set ignoreOrders = true
            loop
                exitwhen this == 0
                if not onGround then
                    call SetUnitZ(sapper, GetUnitZ(sapper) - dropSpeed)
                    if GetUnitZ(sapper) <= 0.01 then
                    
                        static if SAPPER_INVULNERABLE_WHILE_FALLING then
                            call SetUnitInvulnerable(sapper, false)
                        endif
                        
                        if dropEffect != null then
                            call DestroyEffect(dropEffect)
                        endif
                        
                        if SAPPER_BURN_EFFECT != "" then
                            set burnEffect = AddSpecialEffectTarget(SAPPER_BURN_EFFECT, sapper, SAPPER_BURN_EFFECT_ATTACHPOINT)
                        endif
                        
                        static if SAPPER_SHOW_EXPLOSION_INDICATOR then
                            set explosionIndicator = CreateTextTag()
                            call SetTextTagVisibility(explosionIndicator, true)
                            call SetTextTagPermanent(explosionIndicator, true)
                            call SetTextTagText(explosionIndicator, I2S(R2I(explosion + 0.5)), SAPPER_EXPLOSION_TIME_INDICATOR_SIZE * 0.0023)
                            call SetTextTagColor(explosionIndicator, SAPPER_EXPLOSION_TIME_INDICATOR_COLOR.red, SAPPER_EXPLOSION_TIME_INDICATOR_COLOR.green, SAPPER_EXPLOSION_TIME_INDICATOR_COLOR.blue, SAPPER_EXPLOSION_TIME_INDICATOR_COLOR.alpha)
                            call SetTextTagPos(explosionIndicator, x, y, GetUnitZ(sapper) + SAPPER_EXPLOSION_TIME_INDICATOR_HEIGHT)
                        endif
            
                        set onGround = true
                    endif
                else
                    set x = GetUnitX(sapper)
                    set y = GetUnitY(sapper)
                    
                    set explosion = explosion - TIMER_INTERVAL
                    
                    static if SAPPER_SHOW_EXPLOSION_INDICATOR then
                        call SetTextTagPos(explosionIndicator, x, y, GetUnitZ(sapper) + SAPPER_EXPLOSION_TIME_INDICATOR_HEIGHT)
                        call SetTextTagText(explosionIndicator, I2S(R2I(explosion + 0.5)), SAPPER_EXPLOSION_TIME_INDICATOR_SIZE * 0.0023)
                    endif
                    
                    if explosion <= 0 then
                        call SetUnitExploded(sapper, true)
                        call KillUnit(sapper)
                        call SetUnitAnimationByIndex(sapper, SAPPER_SUICIDE_ANIMATION_ID)
                        call destroy()
                    endif
                    
                    if target == null then
                        call searchForTarget()
                    else
                        call moveSapper()
                        
                        if IsUnitVisible(target, owner) then
                            set tx = GetUnitX(target)
                            set ty = GetUnitY(target)
                            
                            set dx = x - tx
                            set dy = y - ty
                            if (dx * dx + dy * dy) <= SAPPER_SUICIDE_RANGE * SAPPER_SUICIDE_RANGE then
                                call SetUnitExploded(sapper, true)
                                call KillUnit(sapper)
                                call SetUnitAnimationByIndex(sapper, SAPPER_SUICIDE_ANIMATION_ID)
                                call destroy()
                            endif
                        endif
                    endif
                        
                endif
                    
                set this = next
            endloop
            set ignoreOrders = false
        endmethod
        
        static method create takes Zeppelin data returns thistype
            local thistype this = allocate()
            set caster = data.caster
            set y = data.y
            set x = data.x
            set z = data.z
            set lvl = data.lvl
            set explosion = SAPPER_EXPLOSION_TIMER(lvl)
            
            set sapper = CreateUnit(GetOwningPlayer(caster), SAPPER_UNIT_ID, x, y, data.xyangle * bj_RADTODEG)
            set owner = GetOwningPlayer(sapper)
            
            static if not LIBRARY_AutoFly then
                call UnitAddAbility(sapper, XE_HEIGHT_ENABLER)
                call UnitRemoveAbility(sapper, XE_HEIGHT_ENABLER)
            endif
            
            static if SAPPER_INVULNERABLE_WHILE_FALLING then
                call SetUnitInvulnerable(sapper, true)
            endif
            
            if SAPPER_DROP_EFFECT != "" then
                set dropEffect = AddSpecialEffectTarget(SAPPER_DROP_EFFECT, sapper, SAPPER_DROP_EFFECT_ATTACHPOINT)
            endif
            
            call SetUnitZ(sapper, z)
            call TriggerRegisterUnitEvent(onDeath, sapper, EVENT_UNIT_DEATH)
            
            set sapperTable[sapper] = integer(this)
            
            call listAdd()
            if count == 1 then
                call TimerStart(ticker, TIMER_INTERVAL, true, function thistype.onLoop)
            endif
            return this
        endmethod
        
        static method onUnitDeath takes nothing returns boolean
            local unit killed = GetDyingUnit()
            local thistype this = thistype(sapperTable[killed])
            if this != 0 then
            
                static if SAPPER_EXPLOSION_USE_CASTER_AS_SOURCE then
                    call damageAOE(caster, x, y, SAPPER_EXPLOSION_RADIUS, SAPPER_EXPLOSION_DAMAGE(lvl))
                else
                    call damageAOE(sapper, x, y, SAPPER_EXPLOSION_RADIUS, SAPPER_EXPLOSION_DAMAGE(lvl))
                endif
                    
                static if SAPPER_EXPLOSION_DAMAGE_DESTRUCTS then
                    call damageDestructablesAOE(caster, x, y, SAPPER_EXPLOSION_RADIUS, SAPPER_EXPLOSION_DAMAGE(lvl))
                endif
                if SAPPER_EXPLOSION_EFFECT != "" then
                    call DestroyEffect(AddSpecialEffect(SAPPER_EXPLOSION_EFFECT, x, y))
                endif
            else
                //loop through everything and check targets. if the dying unit is a target of any sapper, find a new one
                //or order it to move to the target's location if enabled
                set this = first
                loop
                    exitwhen this == 0
                    if killed == target then
                        set oldTargetDied = true
                        if not searchForTarget() then
                            static if not SAPPER_STOP_WHEN_TARGET_DIES then
                                set tx = GetUnitX(killed)
                                set ty = GetUnitY(killed)
                                call IssuePointOrder(sapper, "smart", tx, ty)
                            else
                                call PauseUnit(sapper, true)
                                call IssueImmediateOrder(sapper, "stop")
                                call PauseUnit(sapper, false)
                            endif
                        endif
                    endif
                    set this = next
                endloop
            endif
            set killed = null
            return false
        endmethod
        
        static method onUnitOrder takes nothing returns boolean
            local thistype this = thistype(sapperTable[GetOrderedUnit()])
            if this != 0 and not ignoreOrders then
                set ignoreOrders = true
                call moveSapper()
                set ignoreOrders = false
            endif
            return false
        endmethod
        
        static method onInit takes nothing returns nothing
            set sapperTable = HandleTable.create()
            set targetTable = HandleTable.create()
            
            set dmg = xedamage.create()
            set dtype = SAPPER_EXPLOSION_DAMAGE_TYPE
            set atype = SAPPER_EXPLOSION_ATTACK_TYPE
            set damageAllies = SAPPER_EXPLOSION_DAMAGE_ALLIED
            
            //! runtextmacro SAPPER_INIT_SOUNDS()
            
            call TriggerAddCondition(onDeath, Condition(function thistype.onUnitDeath))
            
            call TriggerRegisterAnyUnitEventBJ(onOrder, EVENT_PLAYER_UNIT_ISSUED_ORDER)
            call TriggerRegisterAnyUnitEventBJ(onOrder, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
            call TriggerRegisterAnyUnitEventBJ(onOrder, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
            call TriggerRegisterAnyUnitEventBJ(onOrder, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER)
            call TriggerAddCondition(onOrder, Condition(function thistype.onUnitOrder))
        endmethod
        
    endstruct
            
    private struct Zeppelin
        
        unit caster = null
        
        real cos = 0.00
        real sin = 0.00
        real moved = 0.00
        
        delegate xefx model = 0
        
        sound summonSound = null
        
        integer lvl = 0
        integer sapperDropped = 0
        integer sapperToDrop = 0
        
        real sapperDropCounter = 0.00
        
        static integer zeppelinSummonSound = 0
        static integer zeppelinDropSound = 0
        
        static timer ticker = CreateTimer()
        static real speed = TIMER_INTERVAL * ZEPPELIN_SPEED
        
        implement List
        
        
        method onDestroy takes nothing returns nothing
        
            static if not ZEPPELIN_DESTROY_ON_END then
                call model.hiddenDestroy()
            else
                call model.destroy()
            endif
        
            call listRemove()
            if count == 0 then
                call PauseTimer(ticker)
            endif
        endmethod
        
        static method onLoop takes nothing returns nothing
            local thistype this = first
            
            loop
                exitwhen this == 0
                set x = x + cos
                set y = y + sin
                
                static if USE_SOUNDS then
                    if summonSound != null then
                        call SetSoundPosition(summonSound, x, y, z)
                    endif
                endif
                
                set moved = moved + speed
                set sapperDropCounter = sapperDropCounter + speed
                
                if sapperDropCounter >= ZEPPELIN_MOVE_DISTANCE / (sapperToDrop + 1) then
                    set sapperDropCounter = 0.00
                    call Sapper.create(this)
                    
                    static if USE_SOUNDS then
                        if zeppelinDropSound != 0 then
                            call RunSoundAtPoint(zeppelinDropSound, x, y, z)
                        endif
                    endif
                endif

                if moved >= ZEPPELIN_MOVE_DISTANCE then
                    call destroy()
                endif
                
                set this = next
            endloop
            
        endmethod
        
        static method create takes nothing returns thistype
            local thistype this = allocate()
            
            set sin = ZEPPELIN_SPEED * TIMER_INTERVAL * Sin(Main.angle)
            set cos = ZEPPELIN_SPEED * TIMER_INTERVAL * Cos(Main.angle)
            set caster = Main.caster
            set lvl = GetUnitAbilityLevel(caster, SPELL_ID)
            set sapperToDrop = SAPPER_AMOUNT(lvl)
            
            set model = xefx.create(Main.cx, Main.cy, Main.angle)
            set fxpath = ZEPPELIN_MODEL
            set z = ZEPPELIN_HEIGHT
            set scale = ZEPPELIN_SIZE 
            set teamcolor = GetPlayerColor(GetOwningPlayer(caster))
            set owner = GetOwningPlayer(caster)
            
            static if USE_SOUNDS then
                if ZEPPELIN_SUMMON_SOUND != "" then
                    call RunSoundAtPoint(zeppelinSummonSound, x, y, z)
                endif
            endif
            
            call listAdd()
            if count == 1 then
                call TimerStart(ticker, TIMER_INTERVAL, true, function thistype.onLoop)
            endif
            return this
        endmethod
        
        static method onInit takes nothing returns nothing
            static if USE_SOUNDS then
                if ZEPPELIN_SUMMON_SOUND != "" then
                    set zeppelinSummonSound = DefineSound(ZEPPELIN_SUMMON_SOUND, R2I(ZEPPELIN_SUMMON_SOUND_DUR * 1000), false, true)
                endif
                
                if ZEPPELIN_SAPPER_DROP_SOUND != "" then
                    set zeppelinDropSound = DefineSound(ZEPPELIN_SAPPER_DROP_SOUND, R2I(ZEPPELIN_SAPPER_DROP_SOUND_DUR * 1000), false, true)
                endif
            endif
        endmethod
        
    endstruct
    
    private struct Main extends array
    
        static unit caster = null
        static integer lvl = 0
        static real cx = 0.00
        static real cy = 0.00
        static real angle = 0.00
        
        static method onCast takes nothing returns boolean
            if GetSpellAbilityId() != SPELL_ID then
                return false
            endif
            set caster = GetSpellAbilityUnit()
            set lvl = GetUnitAbilityLevel(caster, SPELL_ID)
            set cx = GetUnitX(caster)
            set cy = GetUnitY(caster)
            
            set angle = Atan2(GetSpellTargetY() - cy, GetSpellTargetX() - cx)
            
            call Zeppelin.create()
            return false
        endmethod
        
        private static trigger cast = CreateTrigger()
        
        static method onInit takes nothing returns nothing
            call TriggerRegisterAnyUnitEventBJ(cast, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(cast, Condition(function thistype.onCast))
            
            static if LIBRARY_AbilityPreload then
                call AbilityPreload(SPELL_ID)
            endif
        endmethod
        
    endstruct
    
    
endscope


Keywords:
boom, goblin, sapper, funny, zeppelin, explosion, fire, fire, suicide, tinker,
Contents

Just another Warcraft III map (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 19th Oct 2011 Bribe: You don't need to register EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER because that's the same thing as a target order. A lot of these libraries are superfluous and are worse...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.

19th Oct 2011
Bribe: You don't need to register EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER because that's the same thing as a target order.

A lot of these libraries are superfluous and are worse to make the user implement them than to not require them. This is a very simple spell and to see so many requirements for it is a bit of a shock.

"onDestroy" is a function interface making the compiled code slow and bulky. Use "destroy" and "deallocate" instead.

After getting rid of some of your libraries implementing OrderEvent and/or SpellEffectEvent would be good substitutes. At the very least RegisterPlayerUnitEvent is a good choice.

Required libraries should be linked to.
 

FSB

FSB

Level 6
Joined
Jun 8, 2009
Messages
40
Requires: ||
\\ Table [Vexorian] //
\\ ListModule [grim001] //
\\ xebasic, xedamage & xefx [Vexorian] //
\\ ARGB [Vexorian] //
|| SoundUtils [Rising_Dusk] ||
|| GroupUtils [Rising_Dusk] ||
|| Z-Utils [Kricz]

OMFG WHY¿?
U CANT DO THIS JUST ALONE?
 
Level 8
Joined
Aug 2, 2008
Messages
193
Ofc i can...

But...
Why shall i write a own list for each of those 2 Structs, when i can easily use ListModule?
Why shall i use a own Hashtable to save important values when i can use HandleTables?
Why shall i use more variables for color-options, when i can use ARGB?
Why shall i create a new group for enumerations, when i can use ENUM_GROUP of GroupUtils?
Why shall i use a custom damage and alot less options for damage ect, when xe can do this with a even simpler and better syntax?
And why should i do my own sound play and recycling system, when SoundUtils does that for me?
And with Z-Utils I dont need to add the Fly-Ability by myself?

If I wouldn't use all this libraries, the code would be about 2 times longer and uglier...
And those are Systems (which, imho) are libraries, which are in the most maps, aside TimerUtils, AutoIndex ect..
 
Top