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

Black Parade v1.51

  • Like
Reactions: Byaku and mjllonir
Arthas calls his undead minions out to come to the battlefield and fight alongside him. If a minion crash enemy, he will deal amount of damage, and stay in the battlefield to fight. If an enemy unit killed by those stayed minions, it will be reborn as an undead for 45 seconds to aid Arthas, and its soul will be consumed to recover Arthas' hp and mana by 25.

Requires:
- TimerUtils by Vexorian
- PUI by Cohadar
- GroupUtils by Rising_Dusk



JASS:
//============================================================================================
//Black Parade v1.51 by blanc_dummy aka scorpion182
//2009
//
//Requires :
//- vJASS compiler
//- TimerUtils, by Vexorian
//- PUI by Cohadar
//- GroupUtils by Rising_Dusk 
//
//
//============================================================================================
scope BLACKPARADE initializer INIT
//====Config Option===========================================================================
globals
    private constant integer DUMMY_ID='e000'
    private constant integer SPELL_ID='A000' //rawcode of Black Parade Ability
    private constant real EXPIRATION_TIMER=45.0 //skeleton minion life time
    private constant real ANIMATE_D_TIME=45.0 //animate dead unit life time
    private constant integer BUFF_TIME_LIFE='Brai' //minion's buff time life
    private constant integer ANIMATE_DEAD_BUFF='BUan' //buff for animate dead unit
    private constant integer LEVEL=3 //how many level?
    private constant string FX_IMPACT="Abilities\\Weapons\\MeatwagonMissile\\MeatwagonMissile.mdl" //effect on target when hit
    private constant string FX_ANIMATE_D="Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl" //animate dead effect 
    private constant string FX_MISSILE="Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl" //healer missile effect 
    private constant string FX_HEAL="Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl" //effect on heal
    private constant real AOE=800.0 //spell AOE
    private constant real RADIUS=75. //wave radius
    private constant real INTERVAL=0.03125 //soul interval
    private constant integer MINION=2 //how many minions create per second
    private constant attacktype AT=ATTACK_TYPE_MAGIC //attack type
    private constant damagetype DT=DAMAGE_TYPE_UNIVERSAL //damage type
    private constant weapontype WT=WEAPON_TYPE_WHOKNOWS //weapon type
    private constant string ORDER_ID="stampede" //spell order id
    
    private integer array SKELL_ID[LEVEL]
    
    //for damage filter
    private boolexpr b
    private integer array ABIL_LVL
endglobals

private function SKELETON takes nothing returns nothing
    set SKELL_ID[0]='h001' //skeleton lvl 1
    set SKELL_ID[1]='h002' //skeleton lvl 2
    set SKELL_ID[2]='h003' //skeleton lvl 3
endfunction

private constant function damage takes integer lvl returns real
    return 50.0*lvl //deal 50/100/150 damage per hit
endfunction

private constant function heal takes integer lvl returns real
    return 25.0+lvl*0.0 //recover hp and mp by 25 per soul
endfunction
//======end of config=========================================================================
//=====Don't touch anything below unless you konw what are you doing!=========================
globals
     private integer array UnitData
endglobals

private function AttachIntegerToUnit takes unit u, integer i returns nothing
     set UnitData[GetUnitIndex(u)] = i
endfunction

private function GetIntegerFromUnit takes unit u returns integer
     return UnitData[GetUnitIndex(u)]
endfunction
//struct #1 for spell wave
private struct data
    unit caster
    timer t
    real angle=0.
    group fading
    real dur=0.
    
    static method create takes unit c, timer t returns data
        local data d=data.allocate()
        
        set d.caster=c
        set d.t=t
        set d.fading=NewGroup()
        
        call AttachIntegerToUnit(d.caster,GetUnitIndex(d.caster))
        set ABIL_LVL[GetIntegerFromUnit(d.caster)]=GetUnitAbilityLevel(d.caster,SPELL_ID)
        
        return d
    endmethod
    
    private method onDestroy takes nothing returns nothing
        local unit f
        //kill minions
        loop
        set f=FirstOfGroup(.fading)
        exitwhen f==null
            call KillUnit(f)
            call GroupRemoveUnit(.fading,f)
        endloop
        
        call ReleaseTimer(.t)
        call ReleaseGroup(.fading)
        
        set f=null
    endmethod
endstruct
//spell filter damage
private function filter takes nothing returns boolean
	return (IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)==false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE)==false) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction
//spell casting condition
private function Conditions takes nothing returns boolean
    return(GetSpellAbilityId() == SPELL_ID)
endfunction

//Function CopyGroup made by Blade.dk;BJ variables are NOT evil as many people think, they are actually faster. Only some BJ functions are evil :D
private function CopyGroup takes group g returns group
        set bj_groupAddGroupDest = NewGroup()
        call ForGroup(g, function GroupAddGroupEnum)
        return bj_groupAddGroupDest
endfunction
//create & move skeleton minions per interval
private function Create_Skel takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local data d=data(GetTimerData(t))
    local unit f
    local unit targ
    local unit fnew
    local real x = GetUnitX(d.caster) + GetRandomReal(-1.0*AOE, AOE) * Cos((d.angle+90.) * bj_DEGTORAD)
    local real y = GetUnitY(d.caster) + GetRandomReal(-1.0*AOE, AOE) * Sin((d.angle+90.) * bj_DEGTORAD)
    local group g=NewGroup()
    local group victim
    local group temp
    local integer id=GetIntegerFromUnit(d.caster)
    
    if (GetUnitCurrentOrder(d.caster)==OrderId(ORDER_ID)) then
        
        //create skeleton
        if(d.dur>=1.0/MINION) then
            set d.dur=0.
            
            set f=CreateUnit(GetOwningPlayer(d.caster),SKELL_ID[0],x,y,d.angle)
            call UnitAddAbility(f,'Aloc')
            call UnitAddType(f, UNIT_TYPE_PEON)
            call UnitApplyTimedLife(f,BUFF_TIME_LIFE,6.0)
            call SetUnitColor(f,PLAYER_COLOR_PURPLE)
            call SetUnitAnimationByIndex(f,6)
            call GroupAddUnit(d.fading,f)
        endif
        
        set g=CopyGroup(d.fading)
        //move skeleton
        loop
            set f=FirstOfGroup(g)
        exitwhen f==null
            if (GetUnitState(f,UNIT_STATE_LIFE)>0) then
                set x = GetUnitX(f) + 12.0 * Cos(d.angle * bj_DEGTORAD)
                set y = GetUnitY(f) + 12.0 * Sin(d.angle * bj_DEGTORAD)
                call SetUnitPosition(f,x,y)
                call SetUnitFacingTimed(f,d.angle,0)
                
                //check & damage
                set victim=NewGroup()
                set temp=NewGroup()
                call GroupEnumUnitsInRange(temp,GetUnitX(f),GetUnitY(f),RADIUS,b)
                
                loop
                    set targ=FirstOfGroup(temp)
                exitwhen targ==null
                    if (IsUnitEnemy(targ,GetOwningPlayer(d.caster))) then
                        call GroupAddUnit(victim,targ)
                    endif
                    call GroupRemoveUnit(temp,targ)
                endloop
                
                set targ=FirstOfGroup(victim)
                //minion crashed enemy
                if (targ!=null) then
                    call DestroyEffect(AddSpecialEffectTarget(FX_IMPACT,targ,"origin"))
                    call UnitDamageTarget(d.caster,targ,damage(ABIL_LVL[id]),true, false, AT, DT, WT) //damage the target
                    call GroupRemoveUnit(d.fading,f)
                    
                    set fnew=CreateUnit(GetOwningPlayer(d.caster),SKELL_ID[ABIL_LVL[id]-1],GetUnitX(f),GetUnitY(f),GetUnitFacing(f))
                    call AttachIntegerToUnit(fnew,id)
                    call RemoveUnit(f)
                    call UnitApplyTimedLife(fnew,BUFF_TIME_LIFE,EXPIRATION_TIMER)
                endif
                
                call ReleaseGroup(temp)
                call ReleaseGroup(victim)
            
            else
                call GroupRemoveUnit(d.fading,f)
                call RemoveUnit(f)
            endif
            
            call GroupRemoveUnit(g,f)
        endloop
        
        set d.dur=d.dur+0.03
        call TimerStart(t,0.03,false,function Create_Skel)
       
    else
        call d.destroy()
    endif
    
    call ReleaseGroup(g)
    
    set fnew=null
    set temp=null
    set targ=null
    set victim=null
    set g=null
    set f=null
    set t=null
endfunction
//spell casting actions
private function Actions takes nothing returns nothing
    local timer t=NewTimer()
    local data d=data.create(GetSpellAbilityUnit(),t)
    local location l=GetSpellTargetLoc()
    local real angle
   
    set angle=bj_RADTODEG * Atan2(GetLocationY(l) - GetUnitY(d.caster), GetLocationX(l) - GetUnitX(d.caster))
    set d.angle=angle
    call SetTimerData(t,integer(d))
    call TimerStart(t,0.03,false,function Create_Skel)
    call RemoveLocation(l)
    
    set l=null
    set t=null
endfunction
//skeleton kill enemy cond
private function SkellD_Conditions takes nothing returns boolean
    local integer lvl=GetIntegerFromUnit(GetKillingUnit())
    return( GetUnitTypeId(GetKillingUnit()) == SKELL_ID[ABIL_LVL[lvl]-1] )
endfunction
//check player's hero 
private function Hero_Cond takes nothing returns boolean
    return IsUnitType(GetFilterUnit(), UNIT_TYPE_HERO) == true and GetWidgetLife(GetFilterUnit()) > 0.405 and GetUnitAbilityLevel(GetFilterUnit(),SPELL_ID)>0
endfunction
//struct #2 for recover HP & mana
private struct heals
    unit hero
    unit healer
    timer t
    effect fx
    
    static method create takes unit h, unit healer, timer t returns heals
        local heals d=heals.allocate()
        
        set d.hero=h
        set d.healer=healer
        set d.t=t
        set d.fx=AddSpecialEffectTarget(FX_MISSILE,d.healer,"origin")
        
        call UnitAddAbility(d.healer,'Aloc')
        call UnitAddAbility(d.healer,'Avul')
        
        return d
    endmethod
    
    private method onDestroy takes nothing returns nothing
        call KillUnit(.healer)
        call DestroyEffect(.fx)
    endmethod
endstruct
//move the souls
private function Healing takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local heals h=heals(GetTimerData(t))
    local real x1 = GetUnitX(h.healer)
    local real y1 = GetUnitY(h.healer)
    local real x2 = GetUnitX(h.hero)
    local real y2 = GetUnitY(h.hero)
    local real ang = bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
    local real newx = x1 + 15 * Cos(ang * bj_DEGTORAD)
    local real newy = y1 + 15 * Sin(ang * bj_DEGTORAD)
    local real dx=GetUnitX(h.hero) - GetUnitX(h.healer)
    local real dy=GetUnitY(h.hero) - GetUnitY(h.healer)
    local real dist=SquareRoot(dx * dx + dy * dy)
    
    if GetUnitState(h.hero,UNIT_STATE_LIFE)>0 then
    
        if (dist>80) then
            //move souls
            call SetUnitX(h.healer, newx)
            call SetUnitY(h.healer, newy)
            call SetUnitFacing(h.healer,ang)
            call TimerStart(t,INTERVAL,false,function Healing)
        else
            //recover hp & mana
            call SetUnitState(h.hero,UNIT_STATE_LIFE,GetUnitState(h.hero,UNIT_STATE_LIFE)+heal(GetUnitAbilityLevel(h.hero,SPELL_ID)))
            call SetUnitState(h.hero,UNIT_STATE_MANA,GetUnitState(h.hero,UNIT_STATE_MANA)+heal(GetUnitAbilityLevel(h.hero,SPELL_ID)))
            call DestroyEffect(AddSpecialEffectTarget(FX_HEAL,h.hero,"origin"))
            call h.destroy()
        endif
    else 
        call h.destroy()
    endif
    
    set t=null
endfunction
//skeleton kill enemy actions
private function SkellD_Actions takes nothing returns nothing
    local unit k=GetKillingUnit()
    local unit f=GetDyingUnit()
    local unit d
    local unit healer
    local group g
    local group temp
    local boolexpr bol
    local timer t
    local heals h
    local integer id=GetIntegerFromUnit(k)
    
    if (IsUnitEnemy(f,GetOwningPlayer(k)) and IsUnitType(f,UNIT_TYPE_HERO)==false and IsUnitType(f,UNIT_TYPE_STRUCTURE)==false) then
        //animate death
        set d=CreateUnit(GetOwningPlayer(k),GetUnitTypeId(f),GetUnitX(f),GetUnitY(f),GetUnitFacing(f))
        call DestroyEffect(AddSpecialEffectTarget(FX_ANIMATE_D,d,"origin"))
        call UnitApplyTimedLife(d,ANIMATE_DEAD_BUFF,ANIMATE_D_TIME)
        
        set healer=CreateUnit(GetOwningPlayer(k),DUMMY_ID,GetUnitX(f),GetUnitY(f),bj_UNIT_FACING)
        
        //filter group
        set temp=NewGroup()
        set g=NewGroup()
        set bol=Condition(function Hero_Cond)
        call GroupEnumUnitsOfPlayer(temp, GetOwningPlayer(d), bol)
        
        loop
        set d=FirstOfGroup(temp)
            exitwhen d==null
            //who the owner of the skeleton
            if (GetIntegerFromUnit(d)==id) then
                call GroupAddUnit(g,d) 
            endif
            call GroupRemoveUnit(temp,d)
        endloop
        
        set d=FirstOfGroup(g)
        
        if (d!=null) then
            set t=NewTimer()
            set h=heals.create(d,healer,t)
            call SetTimerData(t,integer(h))
            //move the souls
            call TimerStart(t,INTERVAL,false,function Healing)
        else
            call RemoveUnit(healer)
        endif
        
        call DestroyBoolExpr(bol)
        call ReleaseGroup(g)
        call ReleaseGroup(temp)
    endif
    
    set temp=null
    set k=null
    set healer=null
    set t=null
    set bol=null
    set g=null
    set d=null
    set f=null
endfunction
//init function scope, hoorayy!!
private function INIT takes nothing returns nothing
    local trigger t=CreateTrigger()
    
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition(t, Condition( function Conditions ) )
    call TriggerAddAction(t, function Actions )
    
    set t=CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddCondition(t, Condition( function SkellD_Conditions ) )
    call TriggerAddAction(t, function SkellD_Actions )
    
    //init globals
    set b=Condition(function filter)
    call SKELETON()
    
    set t=null
endfunction
endscope



v1.51 - 1.24b compatibility.



Keywords:
black, parade, undead, skeletons, stampede, army, arthas, soul, mana, hp, epic, complex, mui, summon, ulti
Contents

Black Parade v1.51 (Map)

Reviews
15:12, 28th Nov 2009 TriggerHappy: Very cool spell, at first I thought it was just going to be a rampage ripoff until one of the skeletons hit the bandits. You don't need to null f inside onDestroy. You don't need to be using GroupUtils, a...

Moderator

M

Moderator

15:12, 28th Nov 2009
TriggerHappy:

Very cool spell, at first I thought it was just going to be a rampage ripoff until one of the skeletons hit the bandits.

  • You don't need to null f inside onDestroy.
  • You don't need to be using GroupUtils, a single global group would work fine, it seems.
  • GetUnitState -> GetWidgetLife
  • Storing the Sin/Cos would be more efficient, since it never changes.

Though, the coding seems good enough to be approved.
 
Level 8
Joined
May 31, 2009
Messages
439
I have to say I'm not a big reviewer but here is what I have to say :

Review

Creativity : 2/5
Usefulness : 3/5
Effects used : 3/5
Complexity : 2/5

Total : 12/20 ==> 3/5
Comments :

I have to say, it's not your best spell. It seems too simplistic if you ask me. You use the Rampage spell, followed by a few summons and healing spells. Anyone could make this really...which is why I don't think it's particularly creative.

Don't get me wrong, I can certainly see this spell being used in various games, except the fact that you practically become unbeatable by the time your spell gets to level 3 doesn't quite thrill me. Of course this is just an example to show it off, but it's what I think.

Now, I feel like an ass because it seems my review was all about bad things that you did. Except here is the truth : I probably can't do any better, I know nothing about Jass for that matter, so perhaps this is super duper complicated to make, and I just don't know it.

PM me if you have any comments or concerns.

Mjllonir
 
Level 16
Joined
Jun 9, 2008
Messages
734
I have to say I'm not a big reviewer but here is what I have to say :

Review

Creativity : 2/5
Usefulness : 3/5
Effects used : 3/5
Complexity : 2/5

Total : 12/20 ==> 3/5
Comments :

You use the Rampage spell, followed by a few summons and healing spells. Anyone could make this really...which is why I don't think it's particularly creative.
this is not 'rampage' spell?? --" and only use 1 ability (based on channel ability)...follow by a few summons, and healing spells?? you get wrong :ugly:, please read the whole code first before commenting
 
Level 8
Joined
May 31, 2009
Messages
439
Either I didn't clearly express myself or you got me wrong...

Of course you only have one spell, and I just looked at the whole code, and it's just a fancy way to recreate the rampage spell. After that, its not a summon spell, but more of a trigger to make the skeleton to stay there. And if they kill a certain unit, summon another of the unit you just killed. After, if he dies, you just give some hp back to the caster?

I didn't get you wrong at all, I understand perfectly what your showing us.

And on another note, I have to say that it's really good, cause I can't do any better myself...(at least not that I currently know of, unless my spell making skills increased dramatically without me knowing...)
 
Level 7
Joined
Sep 2, 2011
Messages
350
But the point is, people, is that the customer knows what he wants even though he doesn't know how to create such a thing.

Great spell btw.
 
Top