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

Binding Shot v1.3

Description
Unleashes binding arrows at the target and nearby enemies, pulling them towards the caster.

Level 1 - 120 damage.
Level 2 - 200 damage.
Level 3 - 280 damage.
Level 4 - 360 damage.

Cast Range: 700
Target Type: Unit
Cooldown: 9 seconds



Implementation
JASS:
//==============================================================================
//                          BINDING SHOT v1.3                      
//                              BY Ayanami                             
//==============================================================================

//==============================================================================
//                            REQUIREMENTS                                      
//==============================================================================
// - JNGP
// - Alloc     
// - SpellEffectEvent
//   * RegisterPlayerUnitEvent
//   * Table                                                   
// - Timer32                                                                
//==============================================================================

//==============================================================================
//                           IMPLEMENTATION                                     
//==============================================================================
// 1) Copy the whole "Required Systems" Trigger folder 
// 2) Copy the trigger "Binding Shot"
// 3) Copy the ability "Binding Shot" 
// 4) Copy the unit "Binding Shot Dummy"
// 5) Go through the configuration section in "Binding Shot" trigger                            
//==============================================================================


Spell Code

JASS:
library BindingShot uses Alloc, SpellEffectEvent, T32

//===========================================================================
//                           CONFIGURABLES                        
//===========================================================================

globals
    private constant integer ABIL_ID = 'ABBS' // raw code of base ability
    private constant integer DUMMY_ID = 'dBIN' // raw code of unit "Binding Shot Dummy"
    
    private constant real ANIM_IMPACT = 0.5 // time taken for arrow to reach target
    private constant real ANIM_BIND = 0.5 // time taken for enemies to be pulled back
    private constant real ANIM_OFFSET = 200. // offset distance of pull location (relative to caster's location)
    private constant real ANIM_HEIGHT = 50. // height of arrow
    private constant real ANIM_LIGHT_HEIGHT = 50. // height of lightning attachment point
    private constant string ANIM_LIGHTNING = "SPLK" // lightning type
    
    private constant real ENUM_RADIUS = 176. // max collision size of a unit in your map.
    
    private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type
    private constant damagetype DMG = DAMAGE_TYPE_MAGIC // damage type
endglobals

// bind area relative to target
private constant function GetArea takes integer level returns real
    return 300.
endfunction

// damage dealt
private constant function GetDamage takes integer level returns real
    return 80. * level + 40.
endfunction

// filter for allowed targets
private constant function GetFilter takes unit u, unit caster returns boolean
    return /*
    */ not IsUnitType(u, UNIT_TYPE_DEAD) /* // target is alive
    */ and IsUnitEnemy(u, GetOwningPlayer(caster)) /* // target is an enemy of caster
    */ and not IsUnitType(u, UNIT_TYPE_STRUCTURE) // target is not a structure
endfunction

//===========================================================================
//                          END CONFIGURABLES                        
//===========================================================================

globals
    private constant player NEUTRAL_PASSIVE = Player(PLAYER_NEUTRAL_PASSIVE)
    
    private group G = bj_lastCreatedGroup
endglobals

private struct Data extends array
    implement Alloc

    private unit u
    private unit target
    private unit dummy
    private lightning l
    private boolean b
    private real dmg
    private real dur
    private real x
    private real y
    
    private method destroy takes nothing returns nothing
        call RemoveUnit(this.dummy)
        call DestroyLightning(this.l)
        call this.stopPeriodic()
        call this.deallocate()
    endmethod
    
    private method periodic takes nothing returns nothing
        local real x
        local real y
        local real dx
        local real dy
        local real a
        local real dist
        local real offset
    
        if not IsUnitType(this.target, UNIT_TYPE_DEAD) and GetUnitTypeId(this.target) != 0 then
            set x = GetUnitX(this.dummy)
            set y = GetUnitY(this.dummy)
            if this.b then
                set dx = this.x - x
                set dy = this.y - y
                set a = Atan2(dy, dx)
                set dist = SquareRoot(dx * dx + dy * dy)
                set offset = dist / this.dur * T32_PERIOD
                
                call SetUnitX(this.target, GetUnitX(this.target) + offset * Cos(a))
                call SetUnitY(this.target, GetUnitY(this.target) + offset * Sin(a))
            else
                set dx = GetUnitX(this.target) - x
                set dy = GetUnitY(this.target) - y
                set a = Atan2(dy, dx)
                set dist = SquareRoot(dx * dx + dy * dy)
                set offset = dist / this.dur * T32_PERIOD
                
                call SetUnitFacing(this.dummy, a * bj_RADTODEG)
            endif
            
            call SetUnitX(this.dummy, x + offset * Cos(a))
            call SetUnitY(this.dummy, y + offset * Sin(a))
            call MoveLightningEx(this.l, true, GetUnitX(this.u), GetUnitY(this.u), GetUnitFlyHeight(this.u) + ANIM_LIGHT_HEIGHT, x, y, ANIM_HEIGHT + ANIM_LIGHT_HEIGHT)
            
            set this.dur = this.dur - T32_PERIOD
            if this.dur <= 0 then
                if this.b then
                    call this.destroy()
                else
                    set this.b = true
                    set this.dur = ANIM_BIND
                    
                    call UnitDamageTarget(this.u, this.target, this.dmg, true, false, ATK, DMG, null)
                endif
            endif
        else
            call this.destroy()
        endif
    endmethod
    implement T32x

    private static method onCast takes nothing returns boolean
        local thistype this
        local unit u = GetTriggerUnit()
        local unit target = GetSpellTargetUnit()
        local integer level = GetUnitAbilityLevel(u, ABIL_ID)
        local real a = GetUnitFacing(u) * bj_DEGTORAD
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local real dx
        local real dy
        
        call GroupEnumUnitsInRange(G, GetUnitX(target), GetUnitY(target), GetArea(level) + ENUM_RADIUS, null)
        
        loop
            set target = FirstOfGroup(G)
            exitwhen target == null
            call GroupRemoveUnit(G, target)
            
            if GetFilter(target, u) then
                set this = thistype.allocate()
            
                set this.u = u
                set this.target = target
                set dx = GetUnitX(target)
                set dy = GetUnitY(target)
                set this.dummy = CreateUnit(NEUTRAL_PASSIVE, DUMMY_ID, x, y, bj_RADTODEG * Atan2(dy - y, dx - x))
                call SetUnitFlyHeight(this.dummy, ANIM_HEIGHT, 0)
                set this.l = AddLightningEx(ANIM_LIGHTNING, true, x, y, GetUnitFlyHeight(this.u) + ANIM_LIGHT_HEIGHT, GetUnitX(this.dummy), GetUnitY(this.dummy), ANIM_HEIGHT + ANIM_LIGHT_HEIGHT)
                set this.b = false
                set this.dmg = GetDamage(level)
                set this.dur = ANIM_IMPACT
                set this.x = x + ANIM_OFFSET * Cos(a)
                set this.y = y + ANIM_OFFSET * Sin(a)
                
                call this.startPeriodic()
            endif
        endloop
        
        set u = null
        set target = null
        return false
    endmethod

    private static method onInit takes nothing returns nothing
        call RegisterSpellEffectEvent(ABIL_ID, function thistype.onCast)
    endmethod
endstruct

endlibrary


Credits
Sevion - Alloc
Jesus4Lyf - Timer32
Bribe - SpellEffectEvent
Mr.Goblin - Binding Shot Icon


Changelogs

- Initial relase


- Used UNIT_TYPE_DEAD instead of UnitAlive


- Removed Damage, GroupUtils and GTrigger
- Now needs SpellEffectEvent


- Fixed a condition bug in .periodic method
- Optimized variable usage in .onCast static method


Feedback will be appreciated.

Keywords:
binding, shot, arrow, bind
Contents

Binding Shot v1.3 (Map)

Reviews
12th Nov 2011 Bribe: Thanks for making the changes, nice and unique spell here. Approved.

Moderator

M

Moderator

12th Nov 2011
Bribe: Thanks for making the changes, nice and unique spell here.

Approved.
 
Top