1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Find your way through the deepest dungeon in the 18th Mini Mapping Contest Poll.
    Dismiss Notice
  3. A brave new world lies beyond the seven seas. Join the 34th Modeling Contest today!
    Dismiss Notice
  4. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
Hive 3 Remoosed BETA - NOW LIVE. Go check it out at BETA Hive Workshop! Post your feedback in this new forum BETA Feedback.
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Starfall v1.0

Submitted by Aniki
This bundle is marked as substandard. It may contain bugs, not perform optimally or otherwise be in violation of the submission rules.
This is an attempt to replicate the Nightelf's Priestes of the Moon ability Starfall (AEsf) as close as possible. Unfortunately I could not find the path to the mdl file for the stars for AEsf (only the Starfall<Effect>) which wasn't useful because it destroies itself automatically.

Requires:
* xefx (which requires xebasic and xedummy)
* TimerUtils
* GroupUtils
* UnitZ

Code (vJASS):

library StarfallSpell initializer init uses /*
*/
 xebasic    /* http://www.wc3c.net/showthread.php?t=101150
*/
, xedummy    /* -//-
*/
, xefx       /* -//-
*/
, TimerUtils /* http://www.wc3c.net/showthread.php?t=101322
*/
, GroupUtils /* http://www.wc3c.net/showthread.php?t=104464
*/
, UnitZ      /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-getterrainz-unitz-236942/
*/


globals
private constant integer SPELL_ID       = 'A000'
private constant real    INITIAL_BEHIND = 200
private constant real    INITIAL_Z      = 1200
private constant real    FALLING_SPEED  = 1000
private constant string  MODEL          = "Abilities\\Spells\\Undead\\DevourMagic\\DevourMagicBirthMissile.mdl"
private constant real    SCALE          = 3.0
// "Abilities\\Spells\\NightElf\\Starfall\\StarfallTarget.mdl"
//"Abilities\\Weapons\\Mortar\\MortarMissile.mdl"
endglobals

private function IsUnitInvulnerable takes unit u returns boolean
    return GetUnitAbilityLevel(u, 'Avul') > 0 or IsUnitLoaded(u)
endfunction

private function targets_allowed takes unit caster, unit target returns boolean
    return not IsUnitType(target, UNIT_TYPE_DEAD)           /*
*/
    and     IsUnitEnemy(target, GetOwningPlayer(caster)) /*
*/
    and not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)   /*
*/
    and not IsUnitInvulnerable(target)
endfunction



private function distance_between takes unit u1, unit u2 returns real
    local real x1 = GetUnitX(u1)
    local real y1 = GetUnitY(u1)
    local real x2 = GetUnitX(u2)
    local real y2 = GetUnitY(u2)
    local real dx = x2 - x1
    local real dy = y2 - y1
    return SquareRoot(dx * dx + dy * dy)
endfunction

private function say takes string s returns nothing
    call BJDebugMsg(s)
    // call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, s)
endfunction

globals
private          hashtable       HT = InitHashtable()
private constant real            DT = 0.03125

private real tx
private real ty
private real tz
private real dx
private real dy
private real dz
private real dist_behind
private real vlen
private real mvlen

private          real      array random_delays
private constant integer         random_delays_count  = 65
endglobals
private function init_random_delays takes nothing returns nothing
    set random_delays[0] = 0
    set random_delays[1] = 16
    set random_delays[2] = 31
    set random_delays[3] = 47
    set random_delays[4] = 63
    set random_delays[5] = 78
    set random_delays[6] = 94
    set random_delays[7] = 109
    set random_delays[8] = 125
    set random_delays[9] = 141
    set random_delays[10] = 156
    set random_delays[11] = 172
    set random_delays[12] = 188
    set random_delays[13] = 203
    set random_delays[14] = 219
    set random_delays[15] = 234
    set random_delays[16] = 250
    set random_delays[17] = 266
    set random_delays[18] = 281
    set random_delays[19] = 297
    set random_delays[20] = 313
    set random_delays[21] = 328
    set random_delays[22] = 344
    set random_delays[23] = 359
    set random_delays[24] = 375
    set random_delays[25] = 391
    set random_delays[26] = 406
    set random_delays[27] = 422
    set random_delays[28] = 438
    set random_delays[29] = 453
    set random_delays[30] = 469
    set random_delays[31] = 485
    set random_delays[32] = 500
    set random_delays[33] = 516
    set random_delays[34] = 531
    set random_delays[35] = 547
    set random_delays[36] = 563
    set random_delays[37] = 578
    set random_delays[38] = 594
    set random_delays[39] = 609
    set random_delays[40] = 625
    set random_delays[41] = 641
    set random_delays[42] = 656
    set random_delays[43] = 672
    set random_delays[44] = 688
    set random_delays[45] = 703
    set random_delays[46] = 719
    set random_delays[47] = 735
    set random_delays[48] = 750
    set random_delays[49] = 766
    set random_delays[50] = 782
    set random_delays[51] = 797
    set random_delays[52] = 813
    set random_delays[53] = 828
    set random_delays[54] = 844
    set random_delays[55] = 859
    set random_delays[56] = 875
    set random_delays[57] = 891
    set random_delays[58] = 906
    set random_delays[59] = 922
    set random_delays[60] = 938
    set random_delays[61] = 953
    set random_delays[62] = 969
    set random_delays[63] = 984
    set random_delays[64] = 1000
endfunction


public  keyword Starfall
private keyword FallingStars


private struct FallingStar extends array

FallingStars fs
xefx         star

static method move_star takes nothing returns nothing
    local timer        t     = GetExpiredTimer()
    local thistype     this  = GetTimerData(t)
    local FallingStars fsx   = this.fs
    local real         speed = fsx.sf.speed
    local real angle_behind  = (GetUnitFacing(fsx.target) - 180) * bj_DEGTORAD

    set tx = GetUnitX(fsx.target)
    set ty = GetUnitY(fsx.target)
    set tz = GetUnitZ(fsx.target)

    set dx = tx - star.x
    set dy = ty - star.y
    set dz = tz - star.z

    set vlen = SquareRoot(dx * dx  + dy * dy + dz * dz)
    set mvlen = 1 / vlen
    set dx = dx * mvlen
    set dy = dy * mvlen
    set dz = dz * mvlen

    set star.x = star.x + dx * speed * DT
    set star.y = star.y + dy * speed * DT
    set star.z = star.z + dz * speed * DT

    set dx = tx - star.x
    set dy = ty - star.y
    set dz = tz - star.z
    set dist_behind = SquareRoot(dx * dx + dy * dy)

    set star.x = tx + Cos(angle_behind) * (dist_behind)
    set star.y = ty + Sin(angle_behind) * (dist_behind)

    set star.xyangle =  Atan2(ty - star.y, tx - star.x)
    set star.zangle  = -Atan((star.z - tz) / dist_behind)

    if vlen <= 32 then
        if not IsUnitInvulnerable(fsx.target) then
            call UnitDamageTarget(fsx.sf.caster, fsx.target, fsx.sf.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
        endif

        call ReleaseTimer(t)
        call star.destroy()
    endif

    set t = null
endmethod

private static thistype that = 0

static method fall takes FallingStars fs returns thistype
    local real     angle_behind
    local thistype this = that - (that / 8190) * 8190 + 1
    set that = this

    set this.fs = fs

    set angle_behind  = GetUnitFacing(fs.target) - 180
    set tx            = GetUnitX(fs.target) + Cos(angle_behind * bj_DEGTORAD) * fs.sf.initial_behind
    set ty            = GetUnitY(fs.target) + Sin(angle_behind * bj_DEGTORAD) * fs.sf.initial_behind

    set star          = xefx.create(tx, ty, (angle_behind + 180) * bj_DEGTORAD)
    set star.fxpath   = fs.sf.model
    set star.scale    = fs.sf.scale
    set star.z        = fs.sf.initial_z + GetUnitZ(fs.target)
    set star.zangle   = -Atan(star.z / fs.sf.initial_behind)

    call TimerStart(NewTimerEx(this), DT, true, function thistype.move_star)

    return this
endmethod

endstruct


private struct FallingStars extends array

Starfall sf
unit     target

private static thistype self
private static trigger  call_starfall


private static method star_fall takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local thistype this = GetTimerData(t)

    if sf.duration > 0 then
        if distance_between(sf.caster, target) <= sf.AOE and targets_allowed(sf.caster, target) then
            call FallingStar.fall(this)
        endif
        call ReleaseTimer(t)
        set self = this
        call TriggerExecute(call_starfall) // self.starfall(), i.e drop another star
    else
        call ReleaseTimer(t)
    endif

    set t = null
endmethod

private static method starfall takes nothing returns nothing
    local thistype this         = self
    local real     random_delay = random_delays[GetRandomInt(0, random_delays_count - 1)]
    local real     delay

    set delay = random_delay / 1000.0
    if GetRandomInt(1, 100) <= 50 then
        set delay = -delay
    endif
    set delay = sf.initial_delay + delay

    call TimerStart(NewTimerEx(this), delay, false, function thistype.star_fall)
endmethod

private static thistype that = 0

static method fall_over takes Starfall sf, unit target returns thistype
    local thistype this = that - (that / 8190) * 8190 + 1
    set that = this

    set this.sf     = sf
    set this.target = target

    set self = this
    call thistype.starfall()

    return this
endmethod

private static method onInit takes nothing returns nothing
    call ExecuteFunc(SCOPE_PRIVATE + "init_random_delays")

    set call_starfall = CreateTrigger()
    call TriggerAddAction(call_starfall, function thistype.starfall)
endmethod

endstruct


struct Starfall extends array

unit    caster
real    AOE
real    duration
real    damage
real    initial_delay
real    initial_behind
real    initial_z
real    speed
string  model
real    scale

private group affected_units
private boolean stopped

private static thistype self
private static trigger  call_starfall

private static method starfall_recur takes nothing returns nothing
    local timer    t    = GetExpiredTimer()
    local Starfall this = GetTimerData(t)

    set duration = duration - initial_delay
    if duration > 0 then
        call ReleaseTimer(t)
        set t = null

        set self = this
        call TriggerExecute(call_starfall) // call self.starfall()
        return
    else
        if not stopped then
            call IssueImmediateOrder(caster, "stop")
        endif
        call stop()
        call ReleaseTimer(t)
        set t = null
    endif
endmethod

private static method starfall takes nothing returns nothing
    local thistype this = self
    local unit  u

    call GroupUnitsInArea(ENUM_GROUP, GetUnitX(caster), GetUnitY(caster), AOE)
    loop
        set u = FirstOfGroup(ENUM_GROUP)
        exitwhen u == null
        call GroupRemoveUnit(ENUM_GROUP, u)

        if targets_allowed(caster, u) and not IsUnitInGroup(u, affected_units) then
            call GroupAddUnit(affected_units, u)
            call FallingStars.fall_over(this, u)
        endif
    endloop

    call TimerStart(NewTimerEx(this), initial_delay, false, function thistype.starfall_recur)
endmethod

private static thistype that = 0

static method cast takes unit caster, real AOE, real duration, real damage, real initial_delay, real initial_behind, real initial_z, real falling_speed, string model, real scale returns thistype
    local thistype this = that - (that / 8190) * 8190 + 1
    set that = this

    set this.caster         = caster
    set this.AOE            = AOE
    set this.duration       = duration
    set this.damage         = damage
    set this.initial_delay  = initial_delay
    set this.initial_behind = initial_behind
    set this.initial_z      = initial_z
    set this.speed          = falling_speed
    set this.model          = model
    set this.scale          = scale

    set affected_units = NewGroup()
    set stopped        = false

    call SetUnitAnimation(caster, "channel")
    set self = this
    call thistype.starfall() // call self.starfall()

    return this
endmethod

method stop takes nothing returns nothing
    if stopped then
        return
    endif
    set stopped = true
    set duration = 0
    call ReleaseGroup(affected_units)
endmethod

private static method onInit takes nothing returns nothing
    set call_starfall = CreateTrigger()
    call TriggerAddAction(call_starfall, function thistype.starfall)
endmethod

endstruct


private function on_spell_effect takes nothing returns boolean
    local Starfall sf
    local unit     caster
    local integer  spell_level
    local real     AOE
    local real     damage
    local real     duration
    local real     initial_delay
    local real     initial_behind
    local real     initial_z
    local real     falling_speed
    local string   model
    local real     scale

    if GetSpellAbilityId() != SPELL_ID then
        return false
    endif

    set caster         = GetTriggerUnit()
    set spell_level    = GetUnitAbilityLevel(caster, SPELL_ID)

    set AOE            = 1000
    set duration       = 30    // seconds
    set damage         = 50
    set initial_delay  = 1.5
    set initial_behind = INITIAL_BEHIND
    set initial_z      = INITIAL_Z
    set falling_speed  = FALLING_SPEED
    set model          = MODEL
    set scale          = SCALE

    set sf                    /*
*/
     = Starfall.cast(      /*
*/
         caster            /*
*/
       , AOE               /*
*/
       , duration          /*
*/
       , damage            /*
*/
       , initial_delay     /*
*/
       , initial_behind    /*
*/
       , initial_z         /*
*/
       , falling_speed     /*
*/
       , model             /*
*/
       , scale             /*
*/
     )
    call SaveInteger(HT, GetHandleId(GetTriggerUnit()), SPELL_ID, sf)

    set caster = null
    return false
endfunction

private function on_end_cast takes nothing returns boolean
    local integer sf
    if GetSpellAbilityId() == SPELL_ID then
        set sf = LoadInteger(HT, GetHandleId(GetTriggerUnit()), SPELL_ID)
        call Starfall(sf).stop()
        call FlushChildHashtable(HT, GetHandleId(GetTriggerUnit()))
    endif
    return false
endfunction


private function init takes nothing returns nothing
    local trigger t

    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function on_spell_effect))

    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
    call TriggerAddCondition(t, Condition(function on_end_cast))
endfunction

endlibrary



library Rockets initializer init uses StarfallSpell

globals
private constant integer SPELL_ID       = 'A001'
private constant real    INITIAL_BEHIND = 500
private constant real    INITIAL_Z      = 800
private constant real    FALLING_SPEED  = 700
private constant string  MODEL          = "Abilities\\Weapons\\Mortar\\MortarMissile.mdl"
private constant real    SCALE          = 1.0
endglobals

globals
private hashtable HT = InitHashtable() // might be a good idea to use a SPELL_HT for the whole map
endglobals


private function on_spell_effect takes nothing returns boolean
    local StarfallSpell_Starfall sf
    local unit     caster
    local integer  spell_level
    local real     AOE
    local real     damage
    local real     duration
    local real     initial_delay
    local real     initial_behind
    local real     initial_z
    local real     falling_speed
    local string   model
    local real     scale

    if GetSpellAbilityId() != SPELL_ID then
        return false
    endif

    set caster         = GetTriggerUnit()
    set spell_level    = GetUnitAbilityLevel(caster, SPELL_ID)

    set AOE            = 1000
    set duration       = spell_level * 5
    set damage         = 50
    set initial_delay  = 0.5
    set initial_behind = INITIAL_BEHIND
    set initial_z      = INITIAL_Z
    set falling_speed  = FALLING_SPEED
    set model          = MODEL
    set scale          = SCALE

    set sf                    /*
*/
     = StarfallSpell_Starfall.cast(      /*
*/
         caster            /*
*/
       , AOE               /*
*/
       , duration          /*
*/
       , damage            /*
*/
       , initial_delay     /*
*/
       , initial_behind    /*
*/
       , initial_z         /*
*/
       , falling_speed     /*
*/
       , model             /*
*/
       , scale             /*
*/
     )
    call SaveInteger(HT, GetHandleId(GetTriggerUnit()), SPELL_ID, sf)

    set caster = null
    return false
endfunction

private function on_end_cast takes nothing returns boolean
    local integer sf
    if GetSpellAbilityId() == SPELL_ID then
        set sf = LoadInteger(HT, GetHandleId(GetTriggerUnit()), SPELL_ID)
        call StarfallSpell_Starfall(sf).stop()
        call FlushChildHashtable(HT, GetHandleId(GetTriggerUnit()))
    endif
    return false
endfunction


private function init takes nothing returns nothing
    local trigger t

    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function on_spell_effect))

    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
    call TriggerAddCondition(t, Condition(function on_end_cast))
endfunction

endlibrary

 


Keywords:
Starfall, Nieghtelf, Spell, Ability
Contents

Just another Warcraft III map (Map)

Reviews
Moderator
12th Dec 2015 IcemanBo: For long time as NeedsFix. Rejected. 15:47, 7th Jan 2015 IcemanBo: http://www.hiveworkshop.com/forums/spells-569/starfall-v1-0-a-259186/#post2637089
  1. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,846
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    please, indend your code so it is more easily noticable what is inside struct and what isnt.
     
  2. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,549
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    Libraries should be systems. Scopes should be used for spells. Some informative comments would be helpful. Looks quite good on first look. :)

    And yes, indentation, please.^^
     
  3. Mythic

    Mythic

    Media Manager

    Joined:
    Apr 24, 2012
    Messages:
    8,278
    Resources:
    144
    Models:
    122
    Icons:
    6
    Skins:
    1
    Maps:
    5
    Spells:
    6
    Reforged HD Icons:
    1
    Tutorials:
    3
    Resources:
    144
    Creative use of the model.

    Spell looks good.
     
  4. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,549
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    • Please follow http://www.hiveworkshop.com/forums/...80/jpag-jass-proper-application-guide-204383/.
    • Add useful documentation.
    • Scopes are used for spells. Library usually for systems, that it can be used by seen by other functions.
    • You actullly provide a library and two example of how to use it. Sperate the Starfall example from your system.
    • Remove not needed stuff from your system like
      private function say
      .
    • The "random" delay is actually not random. It's pre defined.
    • Add more configuration. Like attack- damage type.
    • Take in consideration of using Table instead of hashtable.
    • Take in consideration of using one loop for all instances in a callback function of one global timer.
    • WEAPON_TYPE_WHOKNOWS
      ->
      null
      .
    • Released timers don't need to be nulled. They are recycled.
    • There must be a decent tooltip with spell description.
    Needs Fix