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

Corrupted Arrows v1.05

Description:

Dark Ranger soots 7 corrupted arrows that will pierce all enemy units reducing their armor and dealing damage​

Code:

JASS:
scope CorruptedArrows //version 1.05
/*******************************************************************************************************************
*                               Requires:
*                               -CTL                        (By Nestharus)
*                               -WorldBounds                (By Nestharus)
*                               -Table                      (By Bribe)
*                               -GroupUtils                 (By Rising_Dusk)
*                               -IsDestructableTree         (PitzerMike)
*                               -SpellEffectEvent           (By Bribe)
*                               -RegisterPlayerUnitEvent    (By Magtheridon96)
*
*                               How to Import:
*                               -Copy both abilities from object editor (Abilities)
*                               -Copy Multishot - Dummy from object editor (Units)
*                               -Copy Corrupted arrows from object editor (Buffs/Effects)
*                               -Copy Multishot trigger
*                               -Copy Libraries Folder
*
*                               Give credits if use :)
*
***********************************************************************************************************************/

/**************************************************************************************
*                                                                                     *
*                             CONFIGURATION                                           *
*                                                                                     *                                              
**************************************************************************************/

    globals
        //Main ability rawcode
        private constant integer        SPELL_CODE      =   'A002'
        //Armor debuff ability rawcode
        private constant integer        DEBUF_CODE      =   'A000'
        //Dummy rawcode
        private constant integer        DUMMY_CODE      =   'h001'
       
        //Ammout of arrows shotted
        private constant integer        AMMOUT          =   7
       
        //Angle at which arrows are shooted. You can set it to 360 to make nova spell
        private constant real           ANGLE_ARC       =   50
       
        //Arrows dize
        private constant real           DUMMY_SCALE     =   100
        //Arrows height
        private constant real           DUMMY_HEIGHT    =   70
        //Range to pick units to damage
        private constant real           DAMAGE_RANGE    =   40
        //Move effect cooldown
        private constant integer        MOVE_CD         =   3
       
        //Effect that appear on damaged units
        private constant string         DAMAGE_EFFECT   =   "Abilities\\Spells\\Other\\BlackArrow\\BlackArrowMissile.mdl"
       
        //Damage and attack type
        private constant attacktype     ATTACK_TYPE     =   ATTACK_TYPE_MAGIC
        private constant damagetype     DAMAGE_TYPE     =   DAMAGE_TYPE_DEATH
       
        //Should arrows destroy trees?
        private constant boolean        DESTROY_TREE    =   true
    endglobals
    
    //Damage function
    private function GetDamage takes integer level returns real
        return level * 30.00
    endfunction
    
    //Total distance function
    private function GetDistance takes integer level returns real
        return level * 50.00 + 650.00
    endfunction
    
    //Speed function
    private function GetSpeed takes integer level returns real
        return level * 2.00 + 23.00
    endfunction

/**************************************************************************************
*                                                                                     *
*                          DAMAGE FILTER FUNCTIOn                                     *
*                                                                                     *                                              
**************************************************************************************/
   
    private function TargetFilter takes unit u, player p returns boolean
        return IsUnitEnemy(u, p) and not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(u, UNIT_TYPE_FLYING)
    endfunction

/**************************************************************************************
*                                                                                     *
*                             CONFIGURATION END                                       *
*                                                                                     *                                              
**************************************************************************************/
   
    private function Tree_Kill takes nothing returns boolean
        local destructable dummy = GetFilterDestructable()
        if IsDestructableTree(dummy) then
            call KillDestructable(dummy)
        endif
        set dummy = null
        return false
    endfunction

    private struct CorruptedArrows extends array
   
    private static unit array caster
    private static unit array dummy
    
    private static player array owner
   
    private static real array distance
    private static real array speed
    private static real array damage
    private static rect array tree_rect
    
    private static integer array moveCD
    private static integer array moveCCD
   
    private static group array ignoreGroup
   
    static Table array missiles
   
        implement CTL
   
            local unit u
            local unit t
            local unit e
            local real x
            local real y
            local real a
            local real dx
            local real dy
            local group d
            local integer index
            local integer i
   
        implement CTLExpire
   
            set distance[this] = distance[this] - speed[this]
            set index = 0
            set i = 0
   
            loop
                set u = missiles[this].unit[index]
                set a = missiles[this].real[index]
                
                set dx = GetUnitX(u)
                set dy = GetUnitY(u)
   
                set x = dx + speed[this] * Cos(a)
                set y = dy + speed[this] * Sin(a)

                if (x < WorldBounds.maxX and x > WorldBounds.minX and y < WorldBounds.maxY and y > WorldBounds.minY ) then
               
                    call SetUnitX(u, x)
                    call SetUnitY(u, y)
                    
                    set moveCCD[this] = moveCCD[this] + 1
                
                
                    if moveCCD[this] == moveCD[this] then
                
                        set e = CreateUnit(owner[this], DUMMY_CODE, x, y, a * bj_RADTODEG)
                
                        call SetUnitFlyHeight(e, DUMMY_HEIGHT, 0)
                        call SetUnitScale(e, DUMMY_SCALE * 0.01, 0, 0)
                
                        call UnitApplyTimedLife(e, 'BTLF', 0.50)
                        call SetUnitAnimation(e, "death")
                    
                        set moveCCD[this] = moveCCD[this] - moveCD[this]
                    
                        set e = null
                
                    endif
               
                else
               
                    call KillUnit(u)
               
                endif
                
               
                set d = NewGroup()
       
                call GroupUnitsInArea(d, x, y, DAMAGE_RANGE)
       
                loop
                    set t = FirstOfGroup(d)
                    exitwhen t == null
           
                    if TargetFilter(t, owner[this]) and not IsUnitInGroup(t, ignoreGroup[this]) then
           
                        call UnitDamageTarget(caster[this], t, damage[this], false, true, ATTACK_TYPE, DAMAGE_TYPE, null)
                        call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, t, "chest"))
                        
                        call IssueTargetOrder(dummy[this], "faeriefire", t)
               
                        call GroupAddUnit(ignoreGroup[this], t)
                    endif
           
                    call GroupRemoveUnit(d, t)
           
                endloop
               
                call ReleaseGroup(d)
                
                set d = null
       
                static if DESTROY_TREE then
                    call SetRect(tree_rect[this], x - DAMAGE_RANGE, y - DAMAGE_RANGE, x + DAMAGE_RANGE, y + DAMAGE_RANGE)
                    call EnumDestructablesInRect(tree_rect[this],function Tree_Kill,null)
                endif
                
                set u = null
                
                set index = index + 1

                exitwhen index == AMMOUT
                
            endloop
   
            if distance[this] <= 0 then
   
                loop
       
                    call UnitApplyTimedLife(missiles[this].unit[i], 'BTLF', 0.50)
                    call SetUnitAnimation(missiles[this].unit[i], "death")
   
                    exitwhen i == AMMOUT
       
                    set i = i + 1
                endloop
   
                call missiles[this].destroy()
                
                call KillUnit(dummy[this])
                
                set caster[this] = null
                set dummy[this] = null
                set owner[this] = null
                
                call ReleaseGroup(ignoreGroup[this])
                
                set ignoreGroup[this] = null
                
                call destroy()
   
            endif
   
        implement CTLNull
        implement CTLEnd
   
        private static method onCast takes nothing returns nothing
   
            local thistype this = create()
   
            local unit u
            local real cx
            local real cy
            local real tx
            local real ty
            local real a
            local real ainc
            local real arc
            local integer level
            local integer index = 0
   
            set caster[this] = GetTriggerUnit()
            set owner[this] = GetTriggerPlayer()
            set level = GetUnitAbilityLevel(caster[this], SPELL_CODE)
   
            set cx = GetUnitX(caster[this])
            set cy = GetUnitY(caster[this])
            set tx = GetSpellTargetX()
            set ty = GetSpellTargetY()
   
            set distance[this] = GetDistance(level)
            set speed[this] = GetSpeed(level)
            set damage[this] = GetDamage(level)
            set ignoreGroup[this] = NewGroup()
            
            set moveCD[this] = MOVE_CD
            set moveCCD[this] = 0
   
            set arc = ANGLE_ARC * bj_DEGTORAD
   
            set a = Atan2(ty - cy, tx - cx) - (arc/ 2)
            set ainc = arc / AMMOUT
   
            set missiles[this] = Table.create()
   
            loop
                set u = CreateUnit(owner[this], DUMMY_CODE, cx, cy, a * bj_RADTODEG)
       
                set missiles[this].unit[index] = u
                set missiles[this].real[index] = a
       
                call SetUnitScale(u, DUMMY_SCALE * 0.01, 0, 0)
                call SetUnitFlyHeight(u, DUMMY_HEIGHT, 0)
       
                set a = a + ainc
       
                exitwhen index == AMMOUT
   
                set index = index + 1
                
            endloop
            
            set u = null
   
            static if DESTROY_TREE then
                set tree_rect[this] = Rect(0, 0, 0, 0)
            endif
            
            set dummy[this] = CreateUnit(owner[this], DUMMY_CODE, cx, cy, 0)
            
            call ShowUnit(dummy[this], false)
            
            call UnitAddAbility(dummy[this], DEBUF_CODE)
            call SetUnitAbilityLevel(dummy[this], DEBUF_CODE, level)
   
        endmethod
   
        private static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(SPELL_CODE,function thistype.onCast)
        endmethod
   
    endstruct
endscope



v1.5
-Some leakss fix
-World bounds check for move effect
v1.04
-Changed some globals names
-Now uising one global dummy to apply debuffs
-onCast leak fixed
v1.03
-Now using one dummy to apply debuffs
-Removed some leaks
-Imporved effects
v1.02
-Addded WorldBounds
-Removed unused varaiables
-Now using one table instead of 3
-Now using functions for level data
v1.01
-Indentation corrections
-Changed some globals names
-Removed unused Table data
-Fixed some ifs
-Added group flush
v1.0
-First release


Keywords:
Archer, Ranger, Dark, Arrows, Arrow, Corrupted, Multi, Shot
Contents

Multishot (Map)

Reviews
Corrupted Arrows v1.05 | Reviewed by BPower | 14.06.2015 Concept[/COLOR]] Shoots a fan of arrows towards the target cast point. Damaged units loose x armor over a certain time. The concept is analog to wacraft 3 curroption orb effect...
KILLCIDE
To anyone having issues with the debuff not applying to all targets, please refer to this post.

Moderator

M

Moderator


Corrupted Arrows v1.05 | Reviewed by BPower | 14.06.2015

[COLOR="gray"

[COLOR="gray"

[COLOR="gray"

[COLOR="gray"

[COLOR="gray"

[COLOR="gray"

Concept[/COLOR]]
126248-albums6177-picture66521.png
Shoots a fan of arrows towards the target cast point.
Damaged units loose x armor over a certain time.
126248-albums6177-picture66523.png
The concept is analog to wacraft 3 curroption orb effect.
Overall simple and not too unique.
Code[/COLOR]]
126248-albums6177-picture66521.png
  • The spell is MUI, leakless and working.
126248-albums6177-picture66523.png
  • After several fixes he code now appears clean. Good Job!
  • Many simultanious cast might result into a small fps drop due to
    excessive usage of the CreateUnit() native. On weaker engines
    this might be a noticeable side-effect.
Objects[/COLOR]]
126248-albums6177-picture66523.png
  • Object data looks ok to me.
Effects[/COLOR]]
126248-albums6177-picture66521.png
  • Periodic corruption missile effects offers additional eye-candy,
    in relative cost of extra overhead. It looks good.
Rating[/COLOR]]
CONCEPTCODEOBJECTSEFFECTSRATINGSTATUS
2/5
3.5/5
4/5
3/5
3.5/5
APPROVED
Links[/COLOR]]
126248-albums6177-picture66524.png
  • Missile helps to create highly configurable projectile spells.
    It's more a hint from my side than a recommendation.
    Your internal structure using CTL and Table is also ok.

[COLOR="gray"

[/TD]
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
static if work like "normal" if condition. Howver they are evaluated during the compiling process. Not matching code will not be generated at all.
-->static if DESTROY_TREE then

Please use a better indentation, should look like this.
JASS:
library
    
    struct Hi 
        private unit hiUnit
        
        private static method init takes nothing returns nothing
            call setup()
            if doAnotherSetup then
                call setup()
            endif
        endmethod

    endstruct

endlibrary
For best read-ability do: m_distance --> mDistance, mSpeed, ....

GetOwningPlayer(caster) can be shortened to GetTriggerPlayer()

Table data has no usage in you script.

IsUnitInGroup(t, IgnoreGroup[this]) == false is a redundant comparison and can be shortend to not IsUnitInGroup(t, IgnoreGroup[this])

Flush Table DG[this] during deallocation process.

Please use just one dummy caster for the whole game season, instead of one per fearie fire cast.

( Like for every missile like spell, I want to advertise the usage of Missile from the Jass Submission Section. )
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
I see you updated the code, so here is what should be done better:

1. Instead of 3 Tables your could just use one.
Using the same childKey, but a different type will not overwrite previously stored values.
Also currently you never destroy Table DG, but create a new instance per spellcast.

For proper cleanup you should null caster and IgnoreGroup, when deallocating an instance.
IgnoreGroup --> ignoreGroup

set k = GetHandleId(u) has no meaning.

When using SetUnitX/Y for unit movement, you should ensure that units do not leave map bounds --> game crash.
For this use either library WorldBounds or recommend the usage of library BoundSentinel in your documentation.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
You still leak a unit handle in onCast, as you exit the loop before. Null it below the loop.

Now you use one dummy unit for fearie fire per instance. Before it was one per hit unit.
What I want is one dummy unit for all instances. You have to create this one on init and never kill it.

Btw in english "amount" is written with one "m" and not two. You wrote AMMOUNT.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
You still have one minor mistake in your code.

If the main arrow leaves map bounds, WorldBounds protects the map from a crash.
But you don't deallocate that instance --> Your move effect can still appear out of bounds,
because it is created on dx/dy.

You still leak unit e and u in the CTlExpire. Check your code more carefully!
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
You still leak local unit u in onCast
You create a unit handle and then exit the loop via exitwhen index == AMMOUT
in this case that local handle is not nulled.

Simply move the set u = null below the loop.
You don't have to null with a loops, because u get's overwritten by a new handle
and that way the leak is also cleared.
 
Top