Spellpack Gravity Spellpack

Discussion in 'Spells' started by Ayanami, Jan 8, 2011.

  1. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    Introduction
    Made a gravity themed spell pack, out of pure boredom :p

    Requirements
    - Damage
    - Timer32
    - GTrigger
    - GroupUtils


    The spells:
    • Inversion Field [Active]
    • Gravity Matrix [Passive]
    • Gravity Flux [Active]
    • Graviton Acceleration [Active]


    Details
    - The spells are vJASS
    - They should be leak-less and lag-less
    - It is MUI, meaning can be cast many times at the same instance


    Implementation
    JASS:
    
    //==============================================================================
    //                          GRAVITY SPELLPACK v1.2                         
    //                              BY Ayanami                             
    //==============================================================================
    
    //==============================================================================
    //                            REQUIREMENTS                                      
    //==============================================================================
    // - JNGP                                                                                                                                   
    // - Damage  
    //    * Event
    //    * AIDS          
    // - Timer32
    // - GTrigger
    // - GroupUtils                                                                
    //==============================================================================
    
    //==============================================================================
    //                           IMPLEMENTATION                                     
    //==============================================================================
    // 1) Copy the whole "Required Systems" Trigger folder 
    // 2) Save the map, it will take a bit longer than usual
    // 3) Close the map and re-open it, then disable or delete the trigger "Objects"
    // 4) Go to the Import Manager and export all resources, then import into map
    // 5) Copy all 4 abilities under "Night Elf"      
    // 6) Copy the whole "Gravity Spellpack" Trigger folder                                         
    //==============================================================================



    Spells

    Inversion Field

    Description:
    Creates an inverse gravity field at the target point. The inverse field causes enemy units to be trapped within the gravity matrix. The gravity field rejects any allied units, causing them to be forced outside of the field. Due to the gravitational differences between the 2 mediums, no units can enter or leave the field at any time and any damage taken from different mediums are reduced. Target area of 500 at all levels. Lasts 4 seconds.

    Level 1 - Field area of 500. Reduces damage taken by 80%.
    Level 2 - Field area of 450. Reduces damage taken by 70%.
    Level 3 - Field area of 400. Reduces damage taken by 60%.
    Level 4 - Field area of 350. Reduces damage taken by 50%.

    Cast Range: 600
    Target Type: Point Area
    Cooldown: 22/20/18/16 seconds

    Screenshot:

    [​IMG]

    Code:
    JASS:
    
    scope InversionField // requires Damage, Timer32, GTrigger, GroupUtils
    
    //===========================================================================
    //                          CONFIGURABLES                        
    //===========================================================================
    
    globals
        private constant integer ABILID = 'ABIF' // raw code of ability "Inversion Field"
        private constant integer ABILDUMID = 'AIF0' // raw code of ability "Inversion Field (Buff)"
        private constant integer BUFFID = 'BIF0' // raw code of buff "Inversion Field"
        private constant integer PRELOADID = 'prel' // raw code of unit "Preloader"
        
        private constant string WALLART = "Abilities\\Spells\\Undead\\DarkSummoning\\DarkSummonMissile.mdl" // model used as field wall
        
        private constant string DAMAGEART = "Abilities\\Weapons\\WingedSerpentMissile\\WingedSerpentMissile.mdl" // model used when damaged
        private constant string DAMAGEATTACH = "chest" // attachment point of model
        
        private constant real THICKNESS = 100.0 // thickness of the field wall
        
        private constant boolean GFBOOLEAN = true // set to true if Gravity Flux will be used
        private constant boolean GABOOLEAN = true // set to true if Graviton Acceleration will be used
    endglobals
    
    private function GetCastArea takes integer level returns real
        return 500.0 // area of cast
    endfunction
    
    private function GetArea takes integer level returns real
        return 550.0 - (50.0 * level) // area of field
    endfunction
    
    private function GetDuration takes integer level returns real
        return 4.0 // duration of spell
    endfunction
    
    private function GetReduction takes integer level returns real
        return 0.9 - (0.1 * level) // reduction in damage taken where, 1.0 == 100%
    endfunction
    
    private function GetWallCount takes integer level returns integer
        return 30 - (3 * level) // number of effect created
    endfunction
    
    //===========================================================================
    //                          END CONFIGURABLES                        
    //===========================================================================
    // Do not touch below unless you know what you're doing
    
    native UnitAlive takes unit id returns boolean
    
    private struct Data
        real duration
        real aoe
        real castaoe
        real x
        real y
        integer level
        group tgroup
        static thistype tempData
        static unit tempCaster
        static hashtable Hash = InitHashtable()
        
        private static method staticCheck takes unit u returns boolean
            local boolean check = true
        
            static if GFBOOLEAN then
                set check = GetUnitTypeId(u) != GravityFlux_DUMMYID
            endif
        
            static if GABOOLEAN then
                if not check then
                    set check = not IsUnitInGroup(u, GravitonAcceleration_Checkgroup)
                endif
            endif
        
            return check
        endmethod
        
        private static method groupFilter takes nothing returns boolean
            local thistype this = tempData
            local unit u = GetFilterUnit()
            local real a
            local real dx
            local real dy
    
            if IsUnitEnemy(u, GetOwningPlayer(tempCaster)) and UnitAlive(u) then
                if GetUnitAbilityLevel(u, ABILDUMID) == 0 then
                    call UnitAddAbility(u, ABILDUMID)
                endif
                call SetUnitAbilityLevel(u, ABILDUMID, this.level)
                set u = null
                return true
            else
                set dx = GetUnitX(u) - this.x
                set dy = GetUnitY(u) - this.y
                if dx * dx + dy * dy < this.aoe * this.aoe then
                    set a = Atan2(dy, dx)
                    set dx = this.x + (this.aoe + THICKNESS) * Cos(a)
                    set dy = this.y + (this.aoe + THICKNESS) * Sin(a)
                    call SetUnitX(u, dx)
                    call SetUnitY(u, dy)
                endif
                set u = null
            endif
            return false
        endmethod
        
        private static method groupFunc takes nothing returns nothing
            local thistype this = tempData
            local unit u = GetEnumUnit()
            local real dx = GetUnitX(u) - this.x
            local real dy = GetUnitY(u) - this.y
            local real a
            
            if GetUnitAbilityLevel(u, ABILDUMID) == 0 then
                call UnitAddAbility(u, ABILDUMID)
                call SetUnitAbilityLevel(u, ABILDUMID, this.level)
            endif
            if SquareRoot(dx * dx + dy * dy) > this.aoe - THICKNESS and thistype.staticCheck(u) then
                set a = Atan2(dy, dx)
                set dx = this.x + (this.aoe - THICKNESS - 50) * Cos(a)
                set dy = this.y + (this.aoe - THICKNESS - 50) * Sin(a)
                call SetUnitX(u, dx)
                call SetUnitY(u, dy)
            endif
            set u = null
        endmethod
        
        private static method groupAction takes nothing returns boolean
            local thistype this = tempData
            local unit u = GetFilterUnit()
            local real dx
            local real dy
            local real a
            
            if not IsUnitInGroup(u, this.tgroup) and thistype.staticCheck(u) then
                set a = Atan2(GetUnitY(u) - this.y, GetUnitX(u) - this.x)
                set dx = this.x + (this.aoe + THICKNESS + 50) * Cos(a)
                set dy = this.y + (this.aoe + THICKNESS + 50) * Sin(a)
                call SetUnitX(u, dx)
                call SetUnitY(u, dy)
            endif
            set u = null
            return false
        endmethod
        
        private static method removeGroup takes nothing returns nothing
            call UnitRemoveAbility(GetEnumUnit(), ABILDUMID)
            call UnitRemoveAbility(GetEnumUnit(), BUFFID)
        endmethod
        
        private method periodic takes nothing returns nothing
            local integer i
            local real dx
            local real dy
            
            if this.duration <= 0 then
                set i = LoadInteger(Hash, this, StringHash("count"))
                loop
                    exitwhen i < 0
                    call DestroyEffect(LoadEffectHandle(Hash, this, i))
                    set i = i - 1
                endloop
            
                call ForGroup(this.tgroup, function thistype.removeGroup)
                call ReleaseGroup(this.tgroup)
                call FlushChildHashtable(Hash, this)
                call this.stopPeriodic()
                call this.deallocate()
            else
                set tempData = this
                call ForGroup(this.tgroup, function thistype.groupFunc)
                call GroupEnumUnitsInArea(ENUM_GROUP, this.x, this.y, this.aoe + THICKNESS, Filter(function thistype.groupAction))
                set this.duration = this.duration - T32_PERIOD
            endif
        endmethod
        implement T32x
    
        static method actions takes nothing returns nothing
            local thistype this = thistype.allocate()
            local unit u = GetTriggerUnit()
            local integer i
            local integer count
            local real a
            local real x
            local real y
            
            set this.level = GetUnitAbilityLevel(u, ABILID)
            set this.duration = GetDuration(this.level)
            set this.aoe = GetArea(this.level)
            set this.castaoe = GetCastArea(this.level)
            set this.x = GetSpellTargetX()
            set this.y = GetSpellTargetY()
            set this.tgroup = NewGroup()
            
            set tempCaster = u
            set tempData = this
            call GroupEnumUnitsInArea(this.tgroup, this.x, this.y, this.castaoe, Filter(function thistype.groupFilter))
            
            set count = GetWallCount(this.level)
            set i = 0
            loop
                exitwhen i == count
                set a = ((bj_PI * 2) / count) * i
                set x = this.x + this.aoe * Cos(a)
                set y = this.y + this.aoe * Sin(a)
                call SaveEffectHandle(Hash, this, i, AddSpecialEffect(WALLART, x, y))
                set i = i + 1
            endloop
            
            call SaveInteger(Hash, this, StringHash("count"), i - 1)
            call this.startPeriodic()
            set u = null
        endmethod
        
        private static method damageReduction takes nothing returns boolean
            local unit u = GetEventDamageSource()
            local unit t = GetTriggerUnit()
            local integer sourcelevel = GetUnitAbilityLevel(u, ABILDUMID)
            local integer targetlevel = GetUnitAbilityLevel(t, ABILDUMID)
        
            if sourcelevel > 0 and targetlevel > 0 then
            elseif sourcelevel > 0 then
                call Damage_Block(GetEventDamage() * GetReduction(sourcelevel))
                call DestroyEffect(AddSpecialEffectTarget(DAMAGEART, t, DAMAGEATTACH))
            elseif targetlevel > 0 then
                call Damage_Block(GetEventDamage() * GetReduction(targetlevel))
                call DestroyEffect(AddSpecialEffectTarget(DAMAGEART, t, DAMAGEATTACH))
            endif
            set u = null
            set t = null
            return false
        endmethod
        
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            local unit u = CreateUnit(Player(13), PRELOADID, 0, 0, 0)
            
            call GT_RegisterStartsEffectEvent(t, ABILID)
            call TriggerAddAction(t, function thistype.actions)
            
            set t = CreateTrigger()
            call Damage_RegisterEvent(t)
            call TriggerAddCondition(t, Condition(function thistype.damageReduction))
            set t = null
            
            //preload
            call UnitAddAbility(u, ABILDUMID)
            call RemoveUnit(u)
            set u = null
        endmethod
    endstruct
    
    endscope


    Gravity Matrix

    Description:
    Constantly generates an unstable gravity field in an area of 1000. In this unstable gravitized zone, all damage loses its power if the damage source is further away from the hero. Further away the damage source is, less damage this unit takes, with a maximum reduction at above 1000 distance. The blocked damage is converted into energy, and absorbed. For every 100 mana absorbed, an automatic gravity force is unleashed, dealing 100 damage to all units within 500 range. Insufficient mana results in a reset in mana counter.

    Level 1 - Maximum reduction of 17%.
    Level 2 - Maximum reduction of 28%.
    Level 3 - Maximum reduction of 39%.
    Level 4 - Maximum reduction of 50%.

    Passive

    Screenshot:
    [​IMG]

    Code:
    JASS:
    
    scope GravityMatrix initializer OnInit // requires Damage, GTrigger, GroupUtils
    
    //===========================================================================
    //                          CONFIGURABLES                        
    //===========================================================================
    
    globals
        private constant integer ABILID = 'ABGM' // raw code of ability "Gravity Matrix"
        
        private constant string DAMAGEART = "Abilities\\Weapons\\WingedSerpentMissile\\WingedSerpentMissile.mdl" // model used when damaged
        private constant string DAMAGEATTACH = "chest" // attachment point of model
        
        private constant string MANAART = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" // model used when damaging enemy units
        private constant string MANAATTACH = "origin" // attachment point of model
        
        private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type of damage
        private constant damagetype DMG = DAMAGE_TYPE_MAGIC // damage type of damage
        private constant weapontype WEP = WEAPON_TYPE_WHOKNOWS // weapon type of damage
    endglobals
    
    private function GetDistance takes integer level returns real
        return 1000.0 // distance where damage reduction becomes maximum
    endfunction
    
    private function GetReduction takes integer level returns real
        return 0.06 + (0.11 * level) // maximum damage reduction where, 1.00 = 100%
    endfunction
    
    private function GetManaInterval takes integer level returns real
        return 100.0 // every interval of mana where damage is unleashed
    endfunction
    
    private function GetArea takes integer level returns real
        return 500.0 // area of damage
    endfunction
    
    //===========================================================================
    //                          END CONFIGURABLES                        
    //===========================================================================
    // Do not touch below unless you know what you're doing
    
    native UnitAlive takes unit id returns boolean
    
    globals
        private hashtable Hash = InitHashtable()
        private trigger Trig
        private unit Tempunit
        private real Tempreal
    endglobals
    
    private function GroupFunc takes nothing returns boolean
        local unit u = GetFilterUnit()
        
        if IsUnitEnemy(u, GetOwningPlayer(Tempunit)) and UnitAlive(u) then
            call DisableTrigger(Trig)
            call UnitDamageTargetEx(Tempunit, u, Tempreal, true, false, ATK, DMG, WEP)
            call DestroyEffect(AddSpecialEffectTarget(MANAART, u, MANAATTACH))
            call EnableTrigger(Trig)
        endif
        set u = null
        return false
    endfunction
    
    private function Learn takes nothing returns boolean
        local unit u = GetTriggerUnit()
        if GetUnitAbilityLevel(u, ABILID) == 1 then
            call SaveReal(Hash, GetHandleId(u), 0, 0.0)
        endif
        set u = null
        return false
    endfunction
    
    private function Actions takes nothing returns boolean
        local unit u
        local integer i
        local integer count
        local integer id
        local real r
        local real x
        local real y
        local real dist
        local real maxdist
        
        set Tempunit = GetTriggerUnit()
        set i = GetUnitAbilityLevel(Tempunit, ABILID)
        set id = GetHandleId(Tempunit)
        set maxdist = GetDistance(i)
        if i > 0 then
            set u = GetEventDamageSource()
            set x = GetUnitX(u) - GetUnitX(Tempunit)
            set y = GetUnitY(u) - GetUnitY(Tempunit)
            set dist = SquareRoot(x * x + y * y)
            if dist > maxdist then
                set dist = maxdist
            endif
            set r = GetEventDamage() * GetReduction(i) * (dist / maxdist)
            call Damage_Block(r)
            call DestroyEffect(AddSpecialEffectTarget(DAMAGEART, Tempunit, DAMAGEATTACH))
            call SetUnitState(Tempunit, UNIT_STATE_MANA, GetUnitState(Tempunit, UNIT_STATE_MANA) + r)
            set r = r + LoadReal(Hash, id, 0)
            if r >= GetManaInterval(i) then
                set count = R2I(r / GetManaInterval(i))
                call SaveReal(Hash, id, 0, r - (GetManaInterval(i) * count))
                if GetUnitState(Tempunit, UNIT_STATE_MANA) >= GetManaInterval(i) then
                    set Tempreal = GetManaInterval(i)
                    loop
                        exitwhen count == 0
                        call GroupEnumUnitsInArea(ENUM_GROUP, GetUnitX(Tempunit), GetUnitY(Tempunit), GetArea(i), Filter(function GroupFunc))
                        call SetUnitState(Tempunit, UNIT_STATE_MANA, GetUnitState(Tempunit, UNIT_STATE_MANA) - GetManaInterval(i))
                        set count = count - 1
                    endloop
                endif
            else
                call SaveReal(Hash, id, 0, r)
            endif
        endif
        set u = null
        return false
    endfunction
    
    private function OnInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        
        call GT_RegisterLearnsAbilityEvent(t, ABILID)
        call TriggerAddCondition(t, Condition(function Learn))
        
        set Trig = CreateTrigger()
        call Damage_RegisterEvent(Trig)
        call TriggerAddCondition(Trig, Condition(function Actions))
        set t = null
    endfunction
    
    endscope


    Gravity Flux

    Description:
    Creates a heavy gravity current at target point. The sudden change in gravity causes a leakage of nearby enemy units' mana. The mana materializes in a ball form, which then travels towards the target point. Upon arrival at the target point, the volatile energy balls violently explodes, unleashing a devastating gravity force, dealing damage equal to the total mana absrobed to all units within the area. Damage is distributed evenly among all enemy units. Area of 400 at all levels.

    Level 1 - 50 mana absorption.
    Level 2 - 75 mana absorption.
    Level 3 - 100 mana absorption.
    Level 4 - 125 mana absorption.

    Cast Range: 700
    Target Type: Point
    Cooldown: 12 seconds

    Screenshot:
    [​IMG]

    Code:
    JASS:
    
    scope GravityFlux // requires Damage, Timer32, GTrigger, GroupUtils
    
    native UnitAlive takes unit id returns boolean
    
    //===========================================================================
    //                          CONFIGURABLES                        
    //===========================================================================
    
    globals
        private constant integer ABILID = 'ABGF' // raw code of ability "Gravity Flux"
        public constant integer DUMMYID = 'grFl' // raw code of unit "Gravity Flux Dummy"
        
        private constant boolean SELF = true // true if includes mana cost
        
        private constant real TIME = 1.0 // time taken for balls to reach target point
        
        private constant string ART = "Objects\\Spawnmodels\\NightElf\\NEDeathMedium\\NEDeath.mdl" // effect upon enemy damage
        private constant string ARTPOINT = "origin" // attachment point of damage effect
        private constant string DEATHART = "Objects\\Spawnmodels\\NightElf\\NEDeathMedium\\NEDeath.mdl" // effect upon projectile death
        
        private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type of damage
        private constant damagetype DMG = DAMAGE_TYPE_MAGIC // damage type of damage
        private constant weapontype WEP = WEAPON_TYPE_WHOKNOWS // weapon type of damage
    endglobals
    
    private function GetMana takes integer level returns real
        return 25.0 + (25.0 * level) // mana absorbed from unit
    endfunction
    
    private function GetTargetAoe takes integer level returns real
        return 400.0 // target area of effect
    endfunction
    
    private function GetAoe takes integer level returns real
        return 400.0 // area of effect of damage
    endfunction
    
    private function FilterTarget takes unit owner, unit target returns boolean
        return UnitAlive(target) or IsUnitEnemy(target, GetOwningPlayer(owner)) // targets allowed for damage
    endfunction
    
    //===========================================================================
    //                          END CONFIGURABLES                        
    //===========================================================================
    
    private struct Data
        real duration
        real mana
        real x
        real y
        integer level
        integer count = 0
        group ballgroup
        group damagegroup
        unit caster
        static Data tempData
        static hashtable Hash = InitHashtable()
        static unit tempUnit
        static real tempX
        static real tempY
        static real tempReal
        static integer tempInt
        
        private static method damageFunc takes nothing returns nothing
            local thistype this = tempData
            local unit u = GetEnumUnit()
            
            call UnitDamageTargetEx(this.caster, u, this.mana, true, false, ATK, DMG, WEP)
            call DestroyEffect(AddSpecialEffectTarget(ART, u, ARTPOINT))
            set u = null
        endmethod
        
        private static method countFunc takes nothing returns boolean
            local thistype this = tempData
            local unit u = GetFilterUnit()
            
            if IsUnitEnemy(u, GetOwningPlayer(this.caster)) and UnitAlive(u) then
                set this.count = this.count + 1
                call GroupAddUnit(this.damagegroup, u)
            endif
            set u = null
            return false
        endmethod
        
        private static method groupFunc takes nothing returns nothing
            local thistype this = tempData
            local unit u = GetEnumUnit()
            local real x = GetUnitX(u)
            local real y = GetUnitY(u)
            local real a = Atan2(this.y - y, this.x - x)
            local real dist = LoadReal(Hash, GetHandleId(u), 0)
            
            set x = x + dist * Cos(a)
            set y = y + dist * Sin(a)
            call SetUnitPosition(u, x, y)
            set u = null
        endmethod
        
        private static method filterFunc takes nothing returns boolean
            local thistype this = tempData
            local unit u = GetFilterUnit()
            local real x
            local real y
            local real mana
            local real unitmana
            
            if FilterTarget(this.caster, u) then
                set x = GetUnitX(u)
                set y = GetUnitY(u)
                set mana = GetMana(this.level)
                set unitmana = GetUnitState(u, UNIT_STATE_MANA)
                if unitmana < mana then
                    set mana = unitmana
                endif
                set this.mana = this.mana + mana
                set tempUnit = CreateUnit(GetOwningPlayer(this.caster), DUMMYID, x, y, Atan2(this.y - y, this.x - x) * bj_RADTODEG)
                call GroupAddUnit(this.ballgroup, tempUnit)
                call SetUnitState(u, UNIT_STATE_MANA, unitmana - mana)
                set x = x - this.x
                set y = y - this.y
                call SaveReal(Hash, GetHandleId(tempUnit), 0, (SquareRoot(x * x + y * y) / TIME) * T32_PERIOD)
            endif
            set u = null
            return false
        endmethod
        
        private static method GroupFlush takes nothing returns nothing
            local unit u = GetEnumUnit()
            local thistype this = tempData
            
            call FlushChildHashtable(Hash, GetHandleId(u))
            call GroupRemoveUnit(this.ballgroup, u)
            call RemoveUnit(u)
            set u = null
        endmethod
        
        private method periodic takes nothing returns nothing
            if this.duration <= 0 then
                set tempData = this
                call GroupEnumUnitsInArea(ENUM_GROUP, this.x, this.y, GetAoe(this.level), Filter(function thistype.countFunc))
                if this.count > 0 then
                    set this.mana = this.mana / this.count
                    call ForGroup(this.damagegroup, function thistype.damageFunc)
                endif
                call DestroyEffect(AddSpecialEffect(DEATHART, this.x, this.y))
                call ForGroup(this.ballgroup, function thistype.GroupFlush)
                call ReleaseGroup(this.ballgroup)
                call this.stopPeriodic()
                call this.deallocate()
            else
                set tempData = this
                call ForGroup(this.ballgroup, function thistype.groupFunc)
                set this.duration = this.duration - T32_PERIOD
            endif
        endmethod
        implement T32x
        
        private static method create takes unit caster, integer level, real x, real y, real mana returns thistype
            local thistype this = thistype.allocate()
    
            set this.duration = TIME
            set this.mana = mana
            set this.x = x
            set this.y = y
            set this.level = level
            set this.count = 0
            set this.ballgroup = NewGroup()
            set this.damagegroup = NewGroup()
            set this.caster = caster
            
            static if SELF then
                set tempX = GetUnitX(this.caster)
                set tempY = GetUnitY(this.caster)
                set tempUnit = CreateUnit(GetOwningPlayer(this.caster), DUMMYID, tempX, tempY, Atan2(y - tempY, x - tempX) * bj_RADTODEG)
                set tempX = tempX - this.x
                set tempY = tempY - this.y
                call GroupAddUnit(this.ballgroup, tempUnit)
                call SaveReal(Hash, GetHandleId(tempUnit), 0, (SquareRoot(tempX * tempX + tempY * tempY) / TIME) * T32_PERIOD)
            endif
            
            set tempData = this
            call GroupEnumUnitsInArea(ENUM_GROUP, this.x, this.y, GetTargetAoe(this.level), Filter(function thistype.filterFunc))
            call this.startPeriodic()
            return this
        endmethod
        
        private static method manaTimeOut takes nothing returns nothing
            local timer t = GetExpiredTimer()
        
            call thistype.create(tempUnit, tempInt, tempX, tempY, tempReal - GetUnitState(tempUnit, UNIT_STATE_MANA))
            call PauseTimer(t)
            call DestroyTimer(t)
            set t = null
        endmethod
        
        private static method actions takes nothing returns nothing
            local timer t
        
            set tempUnit = GetTriggerUnit()
            set tempInt = GetUnitAbilityLevel(tempUnit, ABILID)
            set tempX = GetSpellTargetX()
            set tempY = GetSpellTargetY()
            
            static if SELF then
                set t = CreateTimer()
                set tempReal = GetUnitState(tempUnit, UNIT_STATE_MANA)
                call TimerStart(t, 0.00, false, function thistype.manaTimeOut)
            else
                call thistype.create(tempUnit, tempInt, tempX, tempY, 0)
            endif
            set t = null
        endmethod
        
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            
            call GT_RegisterStartsEffectEvent(t, ABILID)
            call TriggerAddAction(t, function thistype.actions)
            set t = null
            
            //preload
            call RemoveUnit(CreateUnit(Player(13), DUMMYID, 0, 0, 0))
        endmethod
    endstruct
    
    endscope


    Graviton Acceleration

    Description:
    Causes a huge amount of gravitons to accelerate towards the target point, creating a black hole. The black hole causes all enemy units within 600 range to have their mana drained periodically and dragged towards the target point. The less mana enemies have, the stronger the dragging force. At 0 mana, enemies cannot resist the dragging force and is pulled towards the center and receives damage instead of mana drain. Lasts 5 seconds.

    Level 1 - 20 mana per second.
    Level 2 - 30 mana per second.
    Level 3 - 40 mana per second.

    Cast Range: 300
    Target Type: Point
    Cooldown: 75 seconds

    Screenshot:
    [​IMG]

    Code:
    JASS:
    
    scope GravitonAcceleration // requires Damage, Timer32, GTrigger, GroupUtils
    
    //===========================================================================
    //                          CONFIGURABLES                        
    //===========================================================================
    
    globals
        private constant integer ABILID = 'ABGA' // raw code of ability "Graviton Acceleration"
        private constant integer DUMMYID = 'grAc' // raw code of unit "Graviton Acceleration Dummy"
        
        private constant real MAXSPEED = 200.0 // maximum drag speed
        private constant real SPINRATE = 2.0 // number of spins in the center area per second
        
        private constant string LIGHTNING = "DRAM" // lightning effect used, small note that lightning effect
                                                   // can generate lag when dealing with mass units
        private constant boolean ALLOWLIGHT = true // true if lightning effect is used
        
        private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type of damage
        private constant damagetype DMG = DAMAGE_TYPE_MAGIC // damage type of damage
        private constant weapontype WEP = WEAPON_TYPE_WHOKNOWS // weapon type of damage
    endglobals
    
    private function GetCenterAoe takes integer level returns real
        return 100.0 // radius of center area
    endfunction
    
    private function GetAoe takes integer level returns real
        return 600.0 // area of effect of spell
    endfunction
    
    private function GetAbsorbAmount takes integer level returns real
        return 10.0 + (10.0 * level) // amount of mana absorbed per second
    endfunction
    
    private function GetDuration takes integer level returns real
        return 5.0 // duration of spell
    endfunction
    
    //===========================================================================
    //                          END CONFIGURABLES                        
    //===========================================================================
    
    native UnitAlive takes unit id returns boolean
    
    globals
        public group Checkgroup = CreateGroup()
    endglobals
    
    private struct Data
        integer level
        real x
        real y
        real duration
        real amount
        real angle
        real center
        real aoe
        group track
        unit caster
        unit dummy
        static Data tempData
        static hashtable Hash = InitHashtable()
        
        private static method groupFunc takes nothing returns nothing
            local thistype this = tempData
            local unit u = GetEnumUnit()
            local real x = GetUnitX(u)
            local real y = GetUnitY(u)
            local real dx = x - this.x
            local real dy = y - this.y
            local real a
            local real mana
            local real dist = SquareRoot(dx * dx + dy * dy)
            local integer id = GetHandleId(u)
            local boolean check
            
            if dist > this.aoe or not UnitAlive(u) then
                call DestroyLightning(LoadLightningHandle(Hash, this, id))
                call GroupRemoveUnit(this.track, u)
                call GroupRemoveUnit(Checkgroup, u)
            else
                set mana = GetUnitState(u, UNIT_STATE_MANA)
                if mana >= this.amount then
                    call SetUnitState(u, UNIT_STATE_MANA, mana - this.amount)
                else
                    call UnitDamageTargetEx(this.caster, u, this.amount, true, false, ATK, DMG, WEP)
                    call SetUnitState(u, UNIT_STATE_MANA, 0)
                endif
                set check = mana >= 1
                set a = LoadReal(Hash, id, 0) + (this.angle)
                set dx = this.x + this.center * Cos(a)
                set dy = this.y + this.center * Sin(a)
                call MoveLightningEx(LoadLightningHandle(Hash, this, id), true, dx, dy, 0, x, y, GetUnitFlyHeight(u) + 50.0)
                call SaveReal(Hash, id, 0, a)
                if dist > this.center then
                    set a = Atan2(this.y - y, this.x - x)
                    if check then
                        set dist = MAXSPEED * T32_PERIOD
                    else
                        set dist = MAXSPEED * (1.0 - (mana / GetUnitState(u, UNIT_STATE_MAX_MANA))) * T32_PERIOD
                    endif
                    set dx = x + dist * Cos(a)
                    set dy = y + dist * Sin(a)
                endif
                if check then
                    call SetUnitX(u, dx)
                    call SetUnitY(u, dy)
                else
                    call SetUnitPosition(u, dx, dy)
                endif
            endif
            set u = null
        endmethod
        
        private static method filterFunc takes nothing returns boolean
            local thistype this = tempData
            local unit u = GetFilterUnit()
            local real a
            local real x
            local real y
            
            if IsUnitEnemy(u, GetOwningPlayer(this.caster)) and UnitAlive(u) and not IsUnitInGroup(u, this.track) then
                set x = GetUnitX(u)
                set y = GetUnitY(u)
                set a = Atan2(y - this.y, x - this.x)
                static if ALLOWLIGHT then
                    call SaveLightningHandle(Hash, this, GetHandleId(u), AddLightning(LIGHTNING, true, this.x + this.center + Cos(a), this.y + this.center + Sin(a), x, y))
                endif
                call SaveReal(Hash, GetHandleId(u), 0, a)
                call GroupAddUnit(this.track, u)
                call GroupAddUnit(Checkgroup, u)
            endif
            set u = null
            return false
        endmethod
        
        private static method groupFlush takes nothing returns nothing
            local thistype this = tempData
            local unit u = GetEnumUnit()
            local integer id = GetHandleId(u)
            
            static if ALLOWLIGHT then
                call DestroyLightning(LoadLightningHandle(Hash, this, id))
            endif
            call GroupRemoveUnit(Checkgroup, u)
            set u = null
        endmethod
        
        private method periodic takes nothing returns nothing
            set tempData = this
            if this.duration <= 0 then
                call KillUnit(this.dummy)
                call ForGroup(this.track, function thistype.groupFlush)
                call FlushChildHashtable(Hash, this)
                call ReleaseGroup(this.track)
                call this.stopPeriodic()
                call this.deallocate()
            else
                call GroupEnumUnitsInArea(ENUM_GROUP, this.x, this.y, this.aoe, Filter(function thistype.filterFunc))
                call ForGroup(this.track, function thistype.groupFunc)
                set this.duration = this.duration - T32_PERIOD
            endif
        endmethod
        implement T32x
        
        static method actions takes nothing returns nothing
            local thistype this = thistype.allocate()
            
            set this.caster = GetTriggerUnit()
            set this.level = GetUnitAbilityLevel(this.caster, ABILID)
            set this.x = GetSpellTargetX()
            set this.y = GetSpellTargetY()
            set this.duration = GetDuration(this.level)
            set this.amount = GetAbsorbAmount(this.level) * T32_PERIOD
            set this.angle = bj_PI * 2 * (T32_PERIOD / SPINRATE)
            set this.center = GetCenterAoe(this.level)
            set this.aoe = GetAoe(this.level)
            set this.track = NewGroup()
            set this.dummy = CreateUnit(GetOwningPlayer(this.caster), DUMMYID, this.x, this.y, 0)
            
            call this.startPeriodic()
        endmethod
        
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            
            call GT_RegisterStartsEffectEvent(t, ABILID)
            call TriggerAddAction(t, function thistype.actions)
            set t = null
            
            //preload
            call RemoveUnit(CreateUnit(Player(13), DUMMYID, 0, 0, 0))
        endmethod
    endstruct
    
    endscope


    Credits
    Code:
    [B]Jesus4Lyf[/B]
    - [URL="http://www.thehelper.net/forums/showthread.php/131287-Damage"]Damage[/URL]
      * [URL="http://www.thehelper.net/forums/showthread.php/130752-Advanced-Indexing-Data-Storage"]AIDS[/URL]
      * [URL="http://www.thehelper.net/forums/showthread.php/126846-Event"]Event[/URL]
    - [URL="http://www.thehelper.net/forums/showthread.php/132538-Timer32"]Timer32[/URL]
    - [URL="http://www.thehelper.net/forums/showthread.php/123288-GTrigger-Event-System"]GTrigger[/URL]
    [B]Rising_Dusk[/B] - [URL="http://www.wc3c.net/showthread.php?t=104464"]GroupUtils[/URL]
    [B]WILL THE ALMIGHTY[/B] - [URL="http://www.hiveworkshop.com/forums/models-530/black-hole-48468/?prev=search%3DBlackhole%26d%3Dicon%26r%3D200"]Graviton Acceleration Model[/URL]
    

    Changelogs
    Code:
    [B]Version 1.0[/B]
    - Initial release
    
    [B]Version 1.1[/B]
    [COLOR="red"]Inversion Field[/COLOR]
    - Made number of special effects to be configurable
    - Optimized trigger
    [COLOR="red"]Gravity Matrix[/COLOR]
    - Optimized trigger
    [COLOR="red"]Gravity Flux[/COLOR]
    - Made targets allowed to be configurable
    - Optimized trigger
    [COLOR="red"]Graviton Acceleration[/COLOR]
    - Added a line to preload dummy unit
    - Fixed a bug where lightning effects were not destroyed when 2 or more instances of the skill were running
    - Optimized trigger
    
    [B]Version 1.2[/B]
    - Reworked code using Timer32, GTrigger and GroupUtils, instead of KeyTimers2 and Recycle
    
    Feedback will be appreciated.
     

    Attached Files:

  2. skyblader

    skyblader You're living only because it's illegal killing.

    Ratings:
    +159 / 0 / -0
    Ah.. So this is what you've been busy with. There's a spelling error in the tooltip for gravity flux, and also if the ultimate is casted together the lightning effects would leak, but I guess that shouldn't be a problem since the cooldown is supposed to be longer than the spell anyway. Other than that, the spells are amazing. :)
     
  3. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    Ah yes, that's a problem. Will fix it in the next version.

    Thanks for the feedback :)
     
  4. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    Bump. Anymore feedback?
     
  5. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    Can you put a header on all of them? It's annoying to go look back and forth for the heading, and if someone imports a spell they will want documentation with each copy.

    Why are you using Event?
     
  6. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    What do you mean?

    Event is required for Damage.
     
  7. BlackRose

    BlackRose Forum User

    Ratings:
    +239 / 0 / -0
    Damage is the required system, not Event. Unless you use Event, it is better organisation to list Event as a requirement of Damage, not a requirement of the spellpack. I suggest making Event branch off Damage in the bullet point list like so:

    • Damage
      • Event

    In addition, it is helpful to users to let them know which systems which spells require specifically. I suggest you perhaps add a commented out requires line that libraries use: scope ScopeName //requires Damage, T32
     
    • Like Like x 1
  8. Sevion

    Sevion The DIY Ninja

    Ratings:
    +423 / 0 / -0
    Pretty nice from a glance. No time right now, but I'll see about taking a look at that code ;)

    Good job on all the hard work put into learning vJASS :thup:
     
  9. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    Updated to version 1.1

    Changelog
    Code:
    [B]Version 1.1[/B]
    [COLOR="red"]Inversion Field[/COLOR]
    - Made number of special effects to be configurable
    - Optimized trigger
    [COLOR="red"]Gravity Matrix[/COLOR]
    - Optimized trigger
    [COLOR="red"]Gravity Flux[/COLOR]
    - Made targets allowed to be configurable
    - Optimized trigger
    [COLOR="red"]Graviton Acceleration[/COLOR]
    - Added a line to preload dummy unit
    - Fixed a bug where lightning effects were not destroyed when 2 or more instances of the skill were running
    - Optimized trigger
    
     
  10. kingkingyyk3

    kingkingyyk3 Visitor (Welcome to the Jungle, Baby!)

    Ratings:
    +216 / 0 / -0
    Since you are using Damage, then please, use UnitDamageTargetEx instead of UnitDamageTarget.
     
  11. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    Oh, alright. Forgot about that :p
     
  12. Sim

    Sim Forum Administrator Staff Member

    Ratings:
    +531 / 0 / -0
    Has this seen any update recently?
     
  13. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    Yep, made the map. Just need to upload it. Will do soon.
     
  14. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    Updated to version 1.2

    Changelog
    Code:
    [B]Version 1.2[/B]
    - Reworked code using Timer32, GTrigger and GroupUtils, instead of KeyTimers2 and Recycle
    
     
  15. Sevion

    Sevion The DIY Ninja

    Ratings:
    +423 / 0 / -0
    TBH I think Recycle is a better choice than GroupUtils. Less overhead.
     
  16. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    I think the results will be about the same, so what Glenphir wanted to use more was fine.

    Code is fine as far as I can see.

    Approved.
     

Share This Page