• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Rain of Shards v1.2

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: UndeadImmortal
Rain of Shards
Made by KhaosMachine

Tooltip

(Q)Rain of Shards - [Level 1/2/3/4]

The hero goes into a frenzy and starts firing many pieces of ice at one point. Damaging nearby units by 50/80/110/140% of his agility, and slows for 5 seconds.

Releases a fragment every: 0.18 seconds
Area of effect: 300
Cooldown: 40/35/30/25 seconds
Manacost: 150

Screenshots

A0uDO.png

Ovp7R.png

9WjoQ.png


vJASS code

JASS:
/*

Rain of Shards v1.1 MADE BY 'KhaosMachine'

IF YOU WANT TO IMPORT THIS SPELL INTO YOUR MAP YOU HAVE TO COPY THE NEXT THINGS:

*********************************************************************************

TRIGGER EDITOR:
    FOLDERS: NONE
    TRIGGERS: Rain of Shards

OBJECT EDITOR:
    UNITS: Dummy ('u000') (Set the model of this unit to the dummy imported model)
    ABILITIES:  Rain Of Shards ('A000')
                Rain Of Shards ('A001')
    
IMPORT MANAGER:
    dummy.mdx               |   Model   |   34  |   war3mapImported\dummy.mdx

THEN YOU HAVE TO CONFIGURE THE SPELL:

    OBVIOUSLY THINGS:
    
        ABILITY_ID = RAWCODE OF THE SPELL
        DUMMY_ID = RAWCODE OF THE DUMMY
        
********************************************************************************

UPDATES:
            ©­Version 1.2:
                -Some fixes
                -Now the spell cast changing the angle
                -New functions added for setting the angles
            ©­Version 1.1:
                -Added a semi-parabolas system for the arrow
                -Changed the models of the arrow and the explosion
                -Now the arrow damages all touched units in his way
            ©­Version 1.0:
                -First release

********************************************************************************
*/

scope rainOfShards initializer init

/*************************************************************
|***                                                      ***|
|**                                                        **|
|*                                                          *|
|====>        ¡¡¡START OF THE CONFIGURATION!!!          <====|
|*                                                          *|
|**                                                        **|
|***                                                      ***|
|************************************************************/

globals
    private constant integer ABILITY_ID = 'A000'//ABILITY RAWCODE
    private constant integer SLOW_ABILITY_ID = 'A001'//SLOW ABILITY RAWCODE
    private constant string SLOW_ABILITY_ORDER = "slow"//SLOW ABILITY ORDER STRING
    private constant integer DUMMY_ID = 'u000'//DUMMY RAWCODE
    private constant string MISSILE_MODEL = "Abilities\\Weapons\\LichMissile\\LichMissile.mdl"//MISSILE MODEL STRING
    private constant string DEAL_MODEL = "Abilities\\Spells\\Undead\\FreezingBreath\\FreezingBreathMissile.mdl"//DAMAGE POINT MODEL STRING
    private constant real MISSILE_SCALE = 1.3//MISSILE SCALE
    private constant real MISSILE_HEIGHT = 120//MISSILE HEIGHT
    private constant real DM_SCALE = 1.4//DAMAGE POINT SPECIAL EFFECT SCALE
    private constant integer ATTRIBUTE = 2//1 = STRENGHT 2 = AGILITY 3 = INTELLIGENCE
    private constant boolean ADD_BONUSES = true//ADD BONUSES IF TRUE, OR NOT IF FALSE
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL//ATTACK TYPE
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL//DAMAGE TYPE
    private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS//WEAPON TYPE
    private constant string ANIMATION_NAME = "attack"//ANIMATION OF THE UNIT WHEN MAKE THE ARROW
    private constant real ANIMATION_SPEED = 4//ANIMATION SPEED OF THE CASTER
    private constant boolean DAMAGE_WHEN_IMPACT = true//TRUE = DAMAGE WHEN THE MISSILE IMPACTS (DAMAGE ON MOVEMENT + THIS)
    private real SPEED = 2200//SPEED OF THE MISSILE
    private real FPS = 60//FRAMES PER SECOND
endglobals

private function MISSILES takes integer level returns integer
    return 20//COUNT OF ARROWS
endfunction

private function INTERVAL_PER_MISSILE takes integer level returns real
    return .08//TIME WAIT PER MISSILE
endfunction

private function ATTRIBUTE_MULTIPLIER takes integer level returns real
    return .05 + .04 * level//ATTRIBUTE MULTIPLIER
endfunction

private function AREA_OF_EFFECT takes integer level returns real
    return 75. + 25. * level//AREA OF EFFECT OF THE ABILITY
endfunction

private function EXPLOSION_AREA_OF_EFFECT takes integer level returns real
    return 150. + 30. * level//AREA OF EFFECT WHEN THE MISSILE EXPLODES
endfunction

private function MIN_DISTANCE takes integer level returns real
    return 200.
endfunction

private function MAX_ANGLE takes nothing returns real
    return 15.
endfunction

private function ANGLE_FACTOR takes nothing returns real
    return 10.
endfunction

/*************************************************************
|***                                                      ***|
|**                                                        **|
|*                                                          *|
|====>          ¡¡¡END OF THE CONFIGURATION!!!          <====|
|*                                                          *|
|**                                                        **|
|***                                                      ***|
|************************************************************/

private struct rOSRun1
    unit caster
    integer level
    integer missiles
    real interval
    real x
    real y
    real angle
    boolean changeAngle
endstruct

private struct rOSRun2
    unit caster
    integer level
    unit missile
    real angle
    real maxDis
    real currentDis
    effect fx
    group g
endstruct

globals
    private constant timer TIMER = CreateTimer()
    private constant timer TIMER2 = CreateTimer()
    private rOSRun1 array ARRAY
    private integer TOTAL = 0
    private rOSRun2 array ARRAY2
    private integer TOTAL2 = 0
    private constant player PLAYER = Player(PLAYER_NEUTRAL_AGGRESSIVE)
    private constant group UNIT_GROUP = CreateGroup()
endglobals

private function missileAction takes nothing returns nothing
    local rOSRun2 D
    local boolean DESTROY_DATA
    local unit j
    local unit d
    local integer i = 0
    local real DAMAGE
    local real x
    local real y
    loop
    exitwhen i >= TOTAL2
        set D = ARRAY2[i]
        set x = GetUnitX(D.missile)
        set y = GetUnitY(D.missile)
        call GroupEnumUnitsInRange(UNIT_GROUP, x, y, EXPLOSION_AREA_OF_EFFECT(D.level), null)
        loop
            set j = FirstOfGroup(UNIT_GROUP)
        exitwhen j == null
            call GroupRemoveUnit(UNIT_GROUP, j)
            if not IsUnitType(j, UNIT_TYPE_DEAD) and not IsUnitType(j, UNIT_TYPE_STRUCTURE) and not IsUnitType(j, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(j, GetOwningPlayer(D.caster)) and not IsUnitInGroup(j, D.g) then
                if ATTRIBUTE == 1 then
                    set DAMAGE = GetHeroStr(D.caster, ADD_BONUSES)
                elseif ATTRIBUTE == 2 then
                    set DAMAGE = GetHeroAgi(D.caster, ADD_BONUSES)
                else
                    set DAMAGE = GetHeroInt(D.caster, ADD_BONUSES)
                endif
                call UnitDamageTarget(D.caster, j, DAMAGE, true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
                call GroupAddUnit(D.g, j)
                set d = CreateUnit(GetOwningPlayer(D.caster), DUMMY_ID, GetUnitX(j), GetUnitY(j), 0)
                call UnitAddAbility(d, SLOW_ABILITY_ID)
                call SetUnitAbilityLevel(d, SLOW_ABILITY_ID, D.level)
                call IssueTargetOrder(d, SLOW_ABILITY_ORDER, j)
                call UnitApplyTimedLife(d, 'BTLF', .5)
            endif
        endloop
        call SetUnitPosition(D.missile, x + SPEED * Cos(D.angle * bj_DEGTORAD), y + SPEED * Sin(D.angle * bj_DEGTORAD))
        call SetUnitFlyHeight(D.missile, (((D.currentDis + D.maxDis) * (D.currentDis - D.maxDis)) * ((-1.00 * MISSILE_HEIGHT) / (D.maxDis * D.maxDis))), 0)
        set DESTROY_DATA = D.currentDis >= D.maxDis
        set D.currentDis = D.currentDis + SPEED
        if DESTROY_DATA then
            set j = CreateUnit(PLAYER, DUMMY_ID, x, y, 0)
            call SetUnitScale(j, DM_SCALE, DM_SCALE, DM_SCALE)
            call DestroyEffect(AddSpecialEffectTarget(DEAL_MODEL, j, "origin"))
            call UnitApplyTimedLife(j, 'BTLF', 1)
            call GroupEnumUnitsInRange(UNIT_GROUP, x, y, AREA_OF_EFFECT(D.level), null)
            loop
                set j = FirstOfGroup(UNIT_GROUP)
            exitwhen j == null
                call GroupRemoveUnit(UNIT_GROUP, j)
                if DAMAGE_WHEN_IMPACT and not IsUnitType(j, UNIT_TYPE_DEAD) and not IsUnitType(j, UNIT_TYPE_STRUCTURE) and not IsUnitType(j, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(j, GetOwningPlayer(D.caster)) then
                    if ATTRIBUTE == 1 then
                        set DAMAGE = GetHeroStr(D.caster, ADD_BONUSES)
                    elseif ATTRIBUTE == 2 then
                        set DAMAGE = GetHeroAgi(D.caster, ADD_BONUSES)
                    else
                        set DAMAGE = GetHeroInt(D.caster, ADD_BONUSES)
                    endif
                    call UnitDamageTarget(D.caster, j, DAMAGE, true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
                    set d = CreateUnit(GetOwningPlayer(D.caster), DUMMY_ID, GetUnitX(j), GetUnitY(j), 0)
                    call UnitAddAbility(d, SLOW_ABILITY_ID)
                    call SetUnitAbilityLevel(d, SLOW_ABILITY_ID, D.level)
                    call IssueTargetOrder(d, SLOW_ABILITY_ORDER, j)
                    call UnitApplyTimedLife(d, 'BTLF', .5)
                endif
            endloop
            call DestroyEffect(D.fx)
            call UnitApplyTimedLife(D.missile, 'BTLF', 1)
            set ARRAY2[i] = ARRAY2[TOTAL2 - 1]
            set TOTAL2 = TOTAL2 - 1
            call D.destroy()
        endif
        set i = i + 1
    endloop
    if TOTAL2 == 0 then
        call PauseTimer(TIMER2)
    endif
endfunction

private function action takes nothing returns nothing
    local rOSRun1 D
    local rOSRun2 A
    local boolean DESTROY_DATA
    local integer i = 0
    local real x
    local real y
    local real dx
    local real dy
    local real angle
    loop
    exitwhen i >= TOTAL
        set D = ARRAY[i]
        if D.interval <= 0 then
            set A = rOSRun2.create()
            set x = GetUnitX(D.caster)
            set y = GetUnitY(D.caster)
            set dx = D.x - x
            set dy = D.y - y
            call SetUnitAnimation(D.caster, "stand")
            call SetUnitAnimation(D.caster, ANIMATION_NAME)
            set A.caster = D.caster
            set A.level = D.level
            set angle = Atan2(D.y - y, D.x - x) + bj_DEGTORAD * D.angle
            set A.angle = angle * bj_RADTODEG
            set A.missile = CreateUnit(PLAYER, DUMMY_ID, x, y, A.angle)
            call SetUnitFlyHeight(A.missile, MISSILE_HEIGHT, 0)
            set A.currentDis = 0
            set A.maxDis = SquareRoot(dx * dx + dy * dy)
            set A.g = CreateGroup()
            call SetUnitScale(A.missile, MISSILE_SCALE, MISSILE_SCALE, MISSILE_SCALE)
            set A.fx = AddSpecialEffectTarget(MISSILE_MODEL, A.missile, "origin")
            call SetUnitFacing(D.caster, D.angle)
            if D.changeAngle then
                if D.angle < MAX_ANGLE() then
                    set D.angle = D.angle + ANGLE_FACTOR()
                else
                    set D.changeAngle = FALSE
                endif
            else
                if D.angle > - MAX_ANGLE() then
                    set D.angle = D.angle - ANGLE_FACTOR()
                else
                    set D.changeAngle = TRUE
                endif
            endif
            
            if TOTAL2 == 0 then
                call TimerStart(TIMER2, FPS, true, function missileAction)
            endif
            set TOTAL2 = TOTAL2 + 1
            set ARRAY2[TOTAL2 - 1] = A
            set D.missiles = D.missiles - 1
            set D.interval = INTERVAL_PER_MISSILE(D.level)
        else
            set D.interval = D.interval - FPS
        endif
        set DESTROY_DATA = IsUnitType(D.caster, UNIT_TYPE_DEAD) or D.missiles <= 0
        if DESTROY_DATA then
            call PauseUnit(D.caster, false)
            call SetUnitTimeScale(D.caster, 1)
            call IssueImmediateOrder(D.caster, "stop")
            call SetUnitAnimation(D.caster, "stand")
            set ARRAY[i] = ARRAY[TOTAL - 1]
            set TOTAL = TOTAL - 1
            call D.destroy()
        endif
        set i = i + 1
    endloop
    if TOTAL == 0 then
        call PauseTimer(TIMER)
    endif
endfunction

private function run takes nothing returns boolean
    local rOSRun1 D
    local unit caster
    local real x
    local real y
    local real x1
    local real y1
    if GetSpellAbilityId() == ABILITY_ID then
        set D = rOSRun1.create()
        set caster = GetTriggerUnit()
        set D.caster = caster
        set D.level = GetUnitAbilityLevel(caster, ABILITY_ID)
        set D.missiles = MISSILES(D.level)
        set D.interval = 0
        set x = GetUnitX(D.caster)
        set y = GetUnitY(D.caster)
        set D.x = GetSpellTargetX()
        set D.y = GetSpellTargetY()
        set D.changeAngle = FALSE
        set D.angle = 0
        if x == D.x and y == D.y then
            set D.x = x + MIN_DISTANCE(D.level) * Cos(GetUnitFacing(D.caster) * bj_DEGTORAD)
            set D.y = y + MIN_DISTANCE(D.level) * Sin(GetUnitFacing(D.caster) * bj_DEGTORAD)
        endif
        call PauseUnit(D.caster, true)
        call SetUnitTimeScale(caster, ANIMATION_SPEED)
        if TOTAL == 0 then
            call TimerStart(TIMER, FPS, true, function action)
        endif
        set TOTAL = TOTAL + 1
        set ARRAY[TOTAL - 1] = D
        set caster = null
    endif
    return false
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer i = 0
    local unit d = CreateUnit(Player(0), DUMMY_ID, 0, 0, 0)
    loop
        call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set i = i + 1
        exitwhen i == bj_MAX_PLAYER_SLOTS
    endloop
    call TriggerAddCondition(t, Condition(function run))
    call UnitAddAbility(d, ABILITY_ID)
    call UnitAddAbility(d, SLOW_ABILITY_ID)
    call Preload(MISSILE_MODEL)
    call Preload(DEAL_MODEL)
    call RemoveUnit(d)
    set FPS = 1 / FPS
    set SPEED = SPEED * FPS
    set t = null
    set d = null
endfunction

endscope

Credits and some info

Credits:
  • KhaosMachine for the SPELL

I made this spell because I love make spells, haha :p. This is my third vJASS spell, sorry for the errors or bugs if it have any!


zH8Wj.png


Keywords:
vjass, rain of, nice spell, blizzlike, with att, with attributes, hiveworksop, useful, leakless, system, lagless, bugless, shards, slow
Contents

Rain of Shards (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 18th Jul 2012 Bribe: In the function "missileAction" you need to null the local unit "d". Please don't inline TriggerRegisterAnyUnitEventBJ, it is useless to do it. Otherwise, looks...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.

18th Jul 2012
Bribe: In the function "missileAction" you need to null the local unit "d".

Please don't inline TriggerRegisterAnyUnitEventBJ, it is useless to do it.

Otherwise, looks OK. I will approve this after the changes are made.
 
Level 14
Joined
Aug 8, 2010
Messages
1,022
I don't understand vJass but i can see that the spell is MUI for sure! The idea is good, but it would be better if the shards jiggle around. I mean, like the Tinker's cluster rockets, they go to left, right, up, down... Perhaps you can include this in later versions. :p Otherwise, it's good!

4/5 from me! :]
 
Level 8
Joined
Dec 30, 2011
Messages
134
I don't understand vJass but i can see that the spell is MUI for sure! The idea is good, but it would be better if the shards jiggle around. I mean, like the Tinker's cluster rockets, they go to left, right, up, down... Perhaps you can include this in later versions. :p Otherwise, it's good!

4/5 from me! :]

Thanks, yes is 100% MUI, mmmm The tinker's spell use that missile model (the missiles moves because the missile model have that movement (up left, right, down, etc)) But if I can learn how to do it I'll do
 
Top