Spellpack Gravity Spellpack

Ayanami

칼리
Reaction score
288
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:

inversionfield.jpg

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:
gravitymatrix.jpg

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:
gravityflux.jpg

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:
gravitonacceleration.jpg

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.
 

Attachments

  • Gravity Spellpack v1.1.w3x
    111.1 KB · Views: 459
  • Gravity Spellpack v1.2.w3x
    140.3 KB · Views: 628

skyblader

You're living only because it's illegal killing.
Reaction score
159
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. :)
 

Ayanami

칼리
Reaction score
288
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. :)

Ah yes, that's a problem. Will fix it in the next version.

Thanks for the feedback :)
 

tooltiperror

Super Moderator
Reaction score
231
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?
 

Ayanami

칼리
Reaction score
288
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?

What do you mean?

Event is required for Damage.
 

BlackRose

Forum User
Reaction score
239
What do you mean?

Event is required for Damage.

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: [LJASS]scope ScopeName //requires Damage, T32[/LJASS]
 

Sevion

The DIY Ninja
Reaction score
413
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:
 

Ayanami

칼리
Reaction score
288
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
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
Since you are using Damage, then please, use UnitDamageTargetEx instead of UnitDamageTarget.
 

Ayanami

칼리
Reaction score
288
Updated to version 1.2

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

tooltiperror

Super Moderator
Reaction score
231
TBH I think Recycle is a better choice than GroupUtils. Less overhead.
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.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/
  • The Helper The Helper:
    Decided to put up a healthier type recipe to mix it up - Honey Garlic Shrimp Stir-Fry https://www.thehelper.net/threads/recipe-honey-garlic-shrimp-stir-fry.193595/

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top