Devil Black Hole v1.0

Spell Information
untit110.jpg


JASS:
//*******************************************************************
//******************D*E*V*I*L**B*L*A*C*K**H*O*L*E********************
//*****************Requires:
//                          - SimpleJumpSystem
//                          - UnitScale
//                          - xebasic - http://www.wc3c.net/showthread.php?t=101150
//                          - TimerUtils - http://www.wc3c.net/showthread.php?t=101322
//                          - DelDX - https://www.hiveworkshop.com/forums/spells-569/delfx-delayed-effects-1-4-2-a-116064/?prev=search%3DDel%2520Effect%26d%3Dlist%26r%3D20%26c%3D112
//*****************Optional:                   
//                          - xebasic - http://www.wc3c.net/showthread.php?t=101150
//                          - SpellEffectEvent - https://www.hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193/                               
//**********Spell Create By:
//                          - Elphis
//********Spell Information:
//                          - Pull all enemies into devil black hole, enemies can go inside black hole but can't not run out of black hole
//                            and it call some black/fire missle slash to that enemies.
//                          - Level 1: Deals 4 damage/slash and hole will destroy when size = 4
//                          - Level 2: Deals 6 damage/slash and hole will destroy when size = 5
//                          - Level 3: Deals 8 damage/slash and hole will destroy when size = 6
//                          - Level 4: Deals 10 damage/slash and hole will destroy when size = 7
//*************Installation:
//                          - Import/copy the required libraries and Black Hole code to your map
//                          - Import/copy the custom ability and unit to your map and change the SPELL_ID if needed
//                          - You may view the raw ID of the objects by pressing CTRL+D in the object editor
//                          - You may play with the configurables below
//*******************************************************************
//*******************************************************************
//*******************************************************************
library DevilBlackHole uses SJS,UnitScale,AaD,TimerUtils,DelFX optional xebasic,SpellEffectEvent

    globals
        //============================CONFIGURABLES============================================================//
        //Missle slash count
        private             constant             integer            COUNT_BLACK_MISSLE           = 6
        //Rawcode of spell
        private             constant             integer            SPELL_ID                     = 'A000'
        //Time of every timer run
        private             constant             real               LOOP_TIMER                   = .05
        //Base limit size of Black hole
        private             constant             real               SIZE_EXPLORE                 = 3.
        //Area of Effect
        private             constant             real               AOE                          = 2000.
        //Range of Block unit pass through
        private             constant             real               BLOCK_UNIT_AOE               = 300.
        //Size of black hole increase every timer runing
        private             constant             real               SIZE_INCREASE                = .05
        //Pull speed
        private             constant             real               PULL_SPEED                   = 10.
        //Damage radius every missle slashed
        private             constant             real               DAMAGE_RADIUS                = 100.
        //Speed of missle slash
        private             constant             real               SPEED_AROUND                 = 15.
        //Base damage deals to enemy every missle slashed
        private             constant             real               BASE_DAMAGE                  = 2.
        //Explore all unit in AOE speed
        private             constant             real               SPEED_EXPLORE_AOE            = 50.
        //This value is optional of missle slash
        private             constant             real               SAFE_HOLE                    = 1.
        //Distance explore when black hole destroyed
        private             constant             real               EXPLORE_UNIT_AOE             = 600.
        //Hole destroying speed
        private             constant             real               HOLE_DESTROY                 = 15.
        //Black Hole model
        private             constant             string             HOLE                         = "BlackHOLE.mdl"
        //Effect of black hole combine
        private             constant             string             HOLE_EFFECT                  = "Abilities\\Spells\\Human\\FlameStrike\\FlameStrikeTarget.mdl"
        //Missle slash Model
        private             constant             string             BLACK_MISSLE                 = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
        //Explore Effect
        private             constant             string             EXPLORE                      = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
        //Fire effect when missle slash every time
        private             constant             string             EXPLORE_FIRE                 = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
        //Attachment effect of dummy
        private             constant             string             ATTACHMENT                   = "chest"
        //Non - Configurables
        private                                  group              GROUP                        = CreateGroup()
        //============================CONFIGURABLES============================================================//
    endglobals
    //
    //==================================DO NOT EDIT ANYTHING BELOW=======================================================//
    private struct DevilBlackHole
        //
        unit caster
        unit dummy
        unit dumm
        real size
        real xx
        real yy
        real maxsize
        //
        static method onDamage takes unit u returns real
            return BASE_DAMAGE*GetUnitAbilityLevel(u,SPELL_ID) //Damage when slashed
        endmethod
        
        static method sizeLimit takes unit u returns real
            return SIZE_EXPLORE+I2R(GetUnitAbilityLevel(u,SPELL_ID)) // Size of Hole Limited
        endmethod
        
        static method filterUnit takes unit caster, unit u returns boolean
        return /*
        
        */ IsUnitEnemy(u, GetTriggerPlayer()) and /* Target is an enemy of caster
        */ not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0 and /* Target is alive
        */ not IsUnitType(u, UNIT_TYPE_STRUCTURE) /* Target is not a structure
        */
        endmethod
        
        static method onPeriodic takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local unit f
            local real ang
            local real x
            local real y
            //
            call GroupEnumUnitsInRange(GROUP,xx,yy,AOE,null)
            loop
                set f = FirstOfGroup(GROUP)
                exitwhen f == null
                //
                if filterUnit(caster,f) and IsUnitInRange(caster,f,BLOCK_UNIT_AOE) != true then
                    set ang = Angle(GetUnitX(f),GetUnitY(f),xx,yy)
                    set x = GetUnitX(f) + PULL_SPEED * Cos(ang*0.01747)
                    set y = GetUnitY(f) + PULL_SPEED * Sin(ang*0.01747)
                    call SetUnitX(f,x)
                    call SetUnitY(f,y)
                endif
                //
                if filterUnit(caster,f) then
                    if size >= maxsize then
                        set ang = Angle(xx,yy,GetUnitX(f),GetUnitY(f))
                        set x = GetUnitX(f) + EXPLORE_UNIT_AOE * Cos(ang*0.01747)
                        set y = GetUnitY(f) + EXPLORE_UNIT_AOE * Sin(ang*0.01747)
                        call J.JumpTarget(f,x,y,SPEED_EXPLORE_AOE,EXPLORE,EXPLORE,false,false,0.)
                    endif
                endif
                //
                call GroupRemoveUnit(GROUP,f)
                //
            endloop
            //
            set size = size + SIZE_INCREASE
            call SetUnitScale(dummy,size,0,0)
            //
            if size >= maxsize then
                call ReleaseTimer(GetExpiredTimer())
                call USC.USCS(0.,dummy,maxsize*100,0.,HOLE_DESTROY,true,false,true,0.)
                call RemoveUnit(dumm)
                set dumm = null
                set dummy = null
                set caster = null
                call destroy()
            endif
            //
        endmethod
        
        static method onCast takes nothing returns nothing
            local thistype this = allocate()
            local integer i = COUNT_BLACK_MISSLE
            local unit d
            local real temp = -100
            local real x = GetSpellTargetX()
            local real y = GetSpellTargetY()
            local player p = GetTriggerPlayer()
            set caster = GetTriggerUnit()
            set dumm = CreateUnit(p,XE_DUMMY_UNITID,x,y,0.)
            set maxsize = sizeLimit(caster)
            loop
                exitwhen i == 0
                set temp = temp + 100.
                set d = CreateUnit(p,XE_DUMMY_UNITID,x,y,0.)
                call CreateDelayedEffectTarget(BLACK_MISSLE,d,ATTACHMENT,0.,maxsize)
                call Effect.Around(0.,dumm,d,temp,DAMAGE_RADIUS,-SPEED_AROUND,maxsize,true,true,onDamage(caster),EXPLORE_FIRE,0.1,"")
                set i = i - 1
            endloop
            //
            set dummy = CreateUnit(Player(15),XE_DUMMY_UNITID,x,y,0.)
            set xx = GetUnitX(dummy)
            set yy = GetUnitY(dummy)
            call CreateDelayedEffectTarget(HOLE,dummy,ATTACHMENT,0.,maxsize)
            call CreateDelayedEffectTarget(HOLE_EFFECT,dummy,ATTACHMENT,0.,maxsize)
            call SetUnitScale(dummy,0,0,0)
            set size = 0.
            set d = null
            //
            call TimerStart(NewTimerEx(this),LOOP_TIMER,true,function thistype.onPeriodic)
            //
        endmethod
        
        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(SPELL_ID,function thistype.onCast)
        endmethod
    endstruct
endlibrary
[/HIDDEN]

JASS:
//I Created xe dummy by Vexorian in your Object, Just create trigger and copy this in your trigger: //! runtextmacro RunXeUnit()
//! textmacro RunXeUnit
//! external ObjectMerger w3u ewsp xeca unam "xe unit" uhpm 1 uabi Aloc,Aeth, umdl "war3mapImported\dummy.mdx" uico "ReplaceableTextures\CommandButtons\BTNtemp.blp" usid 1 usin 1 unsf "(Caster System?)" uble 0.00 ucbs 0.00 umxp 0.00 umxr 0.00 uimz 0.00 ulpz 0.00 uprw 1.00 uspa "" udty Divine umvs 522 ucol 0.00 ufoo 0 uhom 1 umpi 1000 umpm 1000 umpr 1.00 urac Commoner utyp "" ubui "" upgr "" utub "http://wc3campaigns.net/vexorian-External By Elphis" utip ""
//! endtextmacro
//---------------------------------------------
//
//
//! zinc
//
//
//---------------------------------------------
//
library SJS requires TimerUtils,AaD
{
        constant                real                            time            =           .032;
        //
        public struct J
        {
            real Reach;
            real Fly;
            real Dis;
            real Angle;
            real sp;
            real ins;
            boolean kill;
            unit U;
            real High;
            real d;
            real Highsettings;
            real Timer;
            boolean B;
            boolean Ag;
            real ag;
            boolean Jumpag;
            boolean rem;
            string EfEnd;
//
    static method onLoop()
    {
        real x;
        real y;
        thistype this = GetTimerData(GetExpiredTimer());
        if(!IsUnitType(U, UNIT_TYPE_DEAD) && GetUnitTypeId(U) != 0)
        {
            if (Reach < Dis)
            {
                     x = GetUnitX(U) + sp * Cos(Angle * bj_DEGTORAD);
                     y = GetUnitY(U) + sp * Sin(Angle * bj_DEGTORAD);
                     SetUnitPosition(U,x,y);
                     Reach = Reach + sp;
                     Timer = Timer + 180/(Dis / sp);
                     Fly   = Sin(Timer*bj_DEGTORAD)*Highsettings*1.3;
                     SetUnitFlyHeight(U,Fly,0.);
            }
                else
                {
                     if(kill && !rem)
                        KillUnit(U);
                     if(!kill && rem && ins <= 0)
                        RemoveUnit(U);
                     if(!kill && rem && ins > 0)
                        Dum.Remove(U,ins);
                     DestroyEffect(AddSpecialEffect(EfEnd,GetUnitX(U),GetUnitY(U)));
                     SetUnitPathing(U,true);
                     UnitAddAbility(U,'Amrf');
                     UnitRemoveAbility(U,'Amrf');
                     SetUnitFlyHeight(U,0.,GetUnitDefaultFlyHeight(U));
                     if(rem)
                        RemoveUnit(U);
                     EfEnd = null;
                     ReleaseTimer(GetExpiredTimer());
                     U = null;
                     destroy();
                }
        }
                else
                {
                     if(kill && !rem)
                        KillUnit(U);
                     if(!kill && rem && ins <= 0)
                        RemoveUnit(U);
                     if(!kill && rem && ins > 0)
                        Dum.Remove(U,ins);
                     SetUnitPathing(U,true);
                     UnitAddAbility(U,'Amrf');
                     UnitRemoveAbility(U,'Amrf');
                     SetUnitFlyHeight(U,0.,GetUnitDefaultFlyHeight(U));
                     if(rem)
                        RemoveUnit(U);
                     EfEnd = null;
                     ReleaseTimer(GetExpiredTimer());
                     U = null;
                     destroy();
                }
    }
        static method JumpTarget(unit whatunit,real xj,real yj,real speed,string XJ,string EJ,boolean rems,boolean kills,real inss) 
        {
             thistype this;
             if(!IsUnitType(U, UNIT_TYPE_DEAD) && GetUnitTypeId(U) != 0)
             {
                 this = allocate();
                 U = whatunit;
                 Reach = 0.;
                 Timer = 0.;
                 ins = inss;
                 rem = rems;
                 kill = kills;
                 rem = rems;
                 sp = speed;
                 Fly = 0.;
                 EfEnd = EJ;
                 Dis = Distance(GetUnitX(whatunit),GetUnitY(whatunit),xj,yj);
                 Angle = Angle(GetUnitX(whatunit),GetUnitY(whatunit),xj,yj);
                 High = .35;
                 Highsettings = Dis*High;
                 DestroyEffect(AddSpecialEffect(XJ,GetUnitX(U),GetUnitY(U)));
                 SetUnitPathing(U,false);
                 UnitAddAbility(U,'Amrf');
                 UnitRemoveAbility(U,'Amrf');
                 TimerStart(NewTimerEx(this),time,true,function thistype.onLoop);
            }
        }
    }
}
//
library AaD
{
    public function Distance(real xa,real ya,real xb,real yb) -> real
    {
        real dx = xb - xa;
        real dy = yb - ya;
        return SquareRoot(dx * dx + dy * dy);
    }
    //
    public function Angle(real xa,real ya,real xb,real yb) -> real
    {
        return bj_RADTODEG * Atan2(yb - ya, xb - xa);
    }
}
//
library DummRemove requires TimerUtils
{
    public struct Dum
    {
        unit u;
        //
        static method Loop()
        {
            thistype this = GetTimerData(GetExpiredTimer());
            RemoveUnit(u);
            u = null;
            ReleaseTimer(GetExpiredTimer());
            destroy();
        }
        //
        static method Remove(unit whatunit,real dura) -> unit
        {
            thistype this = allocate();
            u = whatunit;
            TimerStart(NewTimerEx(this),dura,false,function thistype.Loop);
            return whatunit;
        }
    }
}
//
library UnitScale requires TimerUtils
{
    public struct USC
    {
        unit u;
        real s;
        real sta;
        real end;
        real time;
        boolean fade;
        boolean rem;
        boolean kill;
        real ins;
        //
        static method Loop()
        {
            thistype this = GetTimerData(GetExpiredTimer());    
            if(time > 0)
                time = time - .04;
            else
            {
                SetUnitScale(u,sta*0.01,sta*0.01,sta*0.01);
                if(fade)
                {
                    sta = sta - s;
                        if(sta <= end)
                        {
                            if(kill && !rem)
                                KillUnit(u);
                            if(!kill && rem && ins <= 0)
                                RemoveUnit(u);
                            if(!kill && rem && ins > 0)
                                Dum.Remove(u,ins);
                            ReleaseTimer(GetExpiredTimer());
                            u = null;
                            destroy();
                        }
                }
                else
                {
                    sta = sta + s;
                    if(sta > end)
                        {
                            if(kill && !rem)
                                KillUnit(u);
                            if(!kill && rem && ins <= 0)
                                RemoveUnit(u);
                            if(!kill && rem && ins > 0)
                                Dum.Remove(u,ins);
                            ReleaseTimer(GetExpiredTimer());
                            u = null;
                            destroy();
                        }
                }
            }
        }
        //
        static method USCS(real waits,unit us,real stas,real ends,real ss,boolean fades,boolean kills,boolean rems,real inss)
        {
            thistype this;
            //fade <=> true = >
            //fade <=> false = <     
            if(fades)
            {
                if(stas > ends)
                {
                    this = allocate();
                    u = us;
                    time = waits;
                    sta = stas;
                    kill = kills;
                    ins = inss;
                    end = ends;
                    fade = fades;
                    s = ss;
                    rem = rems;
                    TimerStart(NewTimerEx(this),.04,true,function thistype.Loop);
                }
                else
                    BJDebugMsg("Start value must > end value");
            }
            else
                if(stas < ends)
                {
                    this = allocate();
                    u = us;
                    time = waits;
                    sta = stas;
                    kill = kills;
                    ins = inss;
                    end = ends;
                    fade = fades;
                    s = ss;
                    rem = rems;
                    TimerStart(NewTimerEx(this),.04,true,function thistype.Loop);
                }
                else
                    BJDebugMsg("Start value must < end value");
        }
    }
}
//
library SpecialEffect requires TimerUtils,DummRemove,xedamage
{
    public struct Effect
    {
        unit u;
        unit tg;
        string ef;
        real dura;
        effect E;
        real a;
        real Area;
        real Dl;
        real run;
        string w;
        real wai;
        real dma;
        boolean RM;
        boolean dmg;
        real within;
        real sp;
        string whentaken;
        xedamage dam;
        //
        static method ArLoop()
        {
            thistype this = GetTimerData(GetExpiredTimer());
            real x;
            real y;
            group g;
            unit f;
            if(!IsUnitType(u, UNIT_TYPE_DEAD) && !IsUnitType(tg, UNIT_TYPE_DEAD) && GetUnitTypeId(u) != 0 && GetUnitTypeId(tg) != 0)
            {
                if(wai > 0)
                    wai = wai - .03250000;
                else
                {
                    a = a + sp;
                    x = GetUnitX(u) + Area * Cos(a*bj_DEGTORAD);
                    y = GetUnitY(u) + Area * Sin(a*bj_DEGTORAD);
                    SetUnitX(tg,x);
                    SetUnitY(tg,y);
                    if(run < Dl)
                        run = run + .032500000;
                    else
                    {
                        run = 0.;
                        DestroyEffect(AddSpecialEffect(ef,GetUnitX(tg),GetUnitY(tg)));
                    }
                    if(dmg)
                        {
                            dam.useSpecialEffect(whentaken,"chest");
                            dam.damageAOE(u,x,y,within,dma);
                        }
                    if(dura > 0)
                        dura = dura - .03250000;
                    if(dura <= 0 || GetWidgetLife(tg) < 0.425 || GetWidgetLife(u) < 0.425)
                    {
                        if(RM)
                            RemoveUnit(tg);
                        ReleaseTimer(GetExpiredTimer());
                        u = null;
                        tg = null;
                        destroy();
                    }
                }
            }
            else
            {
                if(RM)
                    RemoveUnit(tg);
                ReleaseTimer(GetExpiredTimer());
                u = null;
                tg = null;
                destroy();
            }
        }
        //
        static method Around(real wait,unit aro,unit tgs,real AOE,real withinn,real speed,real dur,boolean rms,boolean DMGs,real dmgg,string effe,real Delay,string effwhentaken) 
        {
            thistype this;
            if(!IsUnitType(tgs, UNIT_TYPE_DEAD) && !IsUnitType(aro, UNIT_TYPE_DEAD) && GetUnitTypeId(tgs) != 0 && GetUnitTypeId(aro) != 0)
            {
                this = allocate();
                u = aro;
                whentaken = effwhentaken;
                tg = tgs;
                wai = wait;
                dura = dur;
                ef = effe;
                dma = dmgg;
                within = withinn;
                dmg = DMGs;
                a = 0.;
                run = 0.;
                Dl = Delay;
                RM = rms;
                sp = speed;
                Area = AOE;
                dam = xedamage.create();   
                dam.dtype = DAMAGE_TYPE_FIRE;
                dam.atype = ATTACK_TYPE_NORMAL;
                dam.damageEnemies = true;
                dam.damageTrees   = false;
                TimerStart(NewTimerEx(this),.0325000,true,function thistype.ArLoop);
            }
            
        }
    }
}
//! endzinc

[/HIDDEN]

Keywords:
hole,black,devil
Contents

Just another Warcraft III map (Map)

Reviews
23:35, 7th Aug 2013 PurgeandFire: Approved and recommended! Very pretty, eye-catching spell.
23:35, 7th Aug 2013
PurgeandFire: Approved and recommended! Very pretty, eye-catching spell.
 
I think GroupUtils is useless.

In the On Cast function, cache first GetSpellAbilityUnit() then use it in the parameters of the functions that use it, because currently, you are calling it 3 times :(

Make the attachment for the effect configurable.

Use Dummy by Nes perhaps?

When naming constants, make sure to put "_" in between words. Do SIZEINCREASE and COUNTBLACKMISSILES and more look readable?

call SetUnitScale(dummy,size,size,size) - > call SetUnitScale(dummy, size, 0, 0)

You can remove the "BlackHole" in the struct methods
 
Here is another review from yours truly but with something entirely different.... now it has a template :D

REVIEW
rating_none.gif


Resource Status: Needs Fix
GENERAL
  • Links to required libraries are missing.
  • Credit is missing in the description.
PROS
  • The effect is splendid. Good job.
  • Global block is lined up beautifully.
  • Configuration is decent.
  • You are using a struct <3
  • The code seems to be well-executed with the use of helper libraries.
CONS
  • There should be a filter function for users to configure. It is something alone this line
    JASS:
        // Filter targets
        private function filterUnit takes unit caster, unit u returns boolean
            return /*
            
            */ IsUnitEnemy(u, GetOwningPlayer(caster)) and /* Target is an enemy of caster
            */ not IsUnitType(u, UNIT_TYPE_DEAD) and /* Target is alive
            */ not IsUnitType(u, UNIT_TYPE_STRUCTURE) /* Target is not a structure
            */
        endfunction
  • The library requires GroupUtils, TimerUtils and SpellEffectEvent. You can make GroupUtils optional though.
  • You are missing importing instructions.
  • USER EDIT should be CONFIGURABLES. Sorry for being OCD though ;__;
  • Global block is not indented correctly. Please reread my review for your other resource on how to indent properly.
  • All methods should be private. And they are namedLikeThis, not NamedLikeThis. Check out the JPAG page.
  • Cast and Loop should be onCast and onPeriodic. It is more or less a convention here to name methods like that.
  • dumm is a bit weird for a variable name. Use dummy2 or something.
  • There should be 2 or more heroes for MUI testing.
[/TD][/tr]
That is all I can see for now. Feel free to speak up if you feel like I misread your code somewhere :)
 
Last edited:
Back
Top