1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  4. The poll for Hive's 12th Concept Art Contest is up! Go cast your vote for your favourite genie!
    Dismiss Notice
  5. Travel to distant realms and encounter scenes unknown to the common folk. The Greatest of Adventures is upon us with the 8th Cinematic Contest. Join in on a fun ride.
    Dismiss Notice
  6. The 18th Icon Contest is ON! Choose any ingame unit and give him/her Hero abilities. Good luck to all.
    Dismiss Notice
  7. Contestants are to create a scene set in the Stone Age. Come and see what you can come up with. We wish you the best of luck!
    Dismiss Notice
  8. Colour outside the lines! Techtree Contest #13 is a go. The contest is optionally paired.
    Dismiss Notice
  9. Greetings cerebrates, our Swarm needs new spawners that will have numerous children. Join the HIVE's 31st Modeling Contest - Spawners and Spawned! The contest is optionally paired.
    Dismiss Notice
  10. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Trigger Viewer

Spell Max Bash 2.1c.w3x
Variables
Initialization
Init
refresh
TST
revive
Spell
DD Library
Max Bash
Max Bash old
Enter map-specific custom script code below. This text will be included in the map script after variables are declared and before any trigger code.

		
Name Type Is Array Initial Value
Init
  Events
    Map initialization
  Conditions
  Actions
    Visibility - Create an initially Enabled visibility modifier for Player 1 (Red) emitting Visibility across (Playable map area)
    Game - Set the time of day to 12
    Game - Turn the day/night cycle Off
    Game - Display to (All players) for 10.00 seconds the text: |cff2030eeWelcome to my spell Max Bash!Press 'Esc' key to refresh life|r
    Unit Group - Pick every unit in (Units in (Playable map area) owned by Neutral Hostile) and do (Actions)
      Loop - Actions
        Custom script: local unit u = GetEnumUnit()
        Custom script: local p_real pr = pVector(GetUnitX(u), GetUnitY(u), GetUnitFacing(u))
        Custom script: call DDSet(u, -1, pr)
    Custom script: call E_EnableUnitReviveForPlayer(DD_p[0], -1., 3.)
    Custom script: call E_EnableUnitReviveForPlayer(DD_p[PLAYER_NEUTRAL_AGGRESSIVE], 60., -1.)
refresh
  Events
    Player - Player 1 (Red) skips a cinematic sequence
  Conditions
  Actions
    Trigger - Turn off (This trigger)
    Special Effect - Create a special effect attached to the origin of Paladin 0000 <gen> using Abilities\Spells\Items\AIre\AIreTarget.mdl
    Special Effect - Destroy (Last created special effect)
    Unit - Set life of Paladin 0000 <gen> to 100%
    Wait 2 seconds
    Trigger - Turn on (This trigger)
TST
  Events
    Time - Every 2 seconds of game time
  Conditions
  Actions
    Special Effect - Create a special effect attached to the weapon of Paladin 0000 <gen> using DDSpells\MaxBashHit.mdx
revive
  Events
    Unit - A unit owned by Neutral Hostile Dies
  Conditions
    ((Triggering unit) is Summoned) Equal to False
  Actions
    Custom script: local unit u = GetTriggerUnit()
    Custom script: local integer id = GetUnitTypeId(u)
    Custom script: local p_real vec = DDGet(u, -1)
    Custom script: local real x = vec[0]
    Custom script: local real y = vec[1]
    Custom script: local real fac = vec[2]
    Wait 45.00 seconds
    Custom script: set u = CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE), id, x, y, fac)
    Custom script: call SetUnitAcquireRange(u, 200.)
    Custom script: call DDSet(u, -1, vec)
//==========================================================================
//                  Dark Dragon Library Code v1.3
//
//                  * Made on Warcraft III v1.30.4
//
//  Installation:
//                  
//                  1) Export instantdummy.mdx from this map and import it to your map, leave path at "war3mapImported/instantdummy.mdx"
//                  2) Copy this trigger to your map, save your map and then change below line "// external ... " or copy "DD Dummy" and paste it in your map
//                  3) Copy and paste "Unit Chill" ability from this map to your map
//                  4) Match the rawcodes below to your map or use same ones as below
//
// Credits:
//          ('Vexorian' - dummy.mdx)
//============================================================================

// *** Change "// external" to "//! external", save your map, close map, change back from "//!" to "//" and save map again.
// *** This will create dummy in your map
//
// ==================================
// external ObjectMerger w3u ushd dumy uabi "Aloc,Amrf" uble 0 ucbs 0 ucpt 0 umxp 0 umxr 0 umdl "war3mapImported\instantdummy.mdx" ushu "None" umvh 0 umvs 1 umas 1 umis 1 ucol 0 ufoo 0 uhom 1 umpi 10000 umpm 10000 usid 1 usin 1 unam "DD Dummy"
// ==================================


//! zinc

library DDLib requires optional TimerUtils, optional GroupUtils
{
   
    // -----------------------------------------------------------------------
    // -----------------------------------------------------------------------
    // *** Lib constants ***
    public {
       
        // ----------------------------------------------------------------------
        // * Start modify/match rawcodes to your map
        constant        integer     DD_DUMMYCODE                = 'dumy';
        constant        integer     DD_ABILITY_CROWN_FORM       = 'Amrf';
        constant        integer     DD_CHILL                    = 'Achl';
        constant        integer     DD_CHILL_BUFF               = 'Bfro';
        // * End modify
        // ----------------------------------------------------------------------
       
       
        constant        integer     p_null                      = (0x0);
        constant        real        DD_INTERVAL                 = .017;
       
                        // map min and max coords
                        real        DDMinX                      = 0.;
                        real        DDMinY                      = 0.;
                        real        DDMaxX                      = 0.;
                        real        DDMaxY                      = 0.;
    }
   
    private {
        constant        integer     HARVEST_ID                  = 'Ahrl';
        constant        real        TRIGGER_REFRESH_RATE        = (60.)*3.; /// damage detection trigger
                       
                        unit        TreeChecker                 = null;
                        trigger     TempTrig                    = null;
                        integer     NTrig                       = 0;
                        trigger     DmgTrig[];
                        p_real      EnumVec                     = p_null;
                        boolexpr    EnumFilter                  = null;
                        sound       ErrorSound                  = null;
                        timer       GameElapsedTimer            = null;
        constant        integer     RND_INT_MAX_ARRAY_N         = 100;
                        integer     RndInt[], RndIntWriteN = 00, RndIntReadN = 00;
                        trigger     TrigMouseEvent              = null;
                        force       RndGenForce                 = null;
                        real        RndElapsedTime              = 0.;
    }
    // -----------------------------------------------------------------------
    // -----------------------------------------------------------------------
    // -----------------------------------------------------------------------
   
    // * types
    public {
       
        // *** pointer to list of data ***
        type    p_integer           extends     integer[8];
        type    p_real              extends     real[8];
        type    p_unit              extends     unit[8];
       
        function H2ID(handle h) -> integer {
            return GetHandleId(h) - 0x100000;
        }
       
        function New_pInteger(integer i) -> p_integer
        { p_integer p = p_integer.create(); p[0] = i; return p; }
        function New_pReal(real r) -> p_real
        { p_real p = p_real.create(); p[0] = r; return p; }
        function New_pUnit(unit u) -> p_unit
        { p_unit p = p_unit.create(); p[0] = u; return p; }
       
        function pVector(real x, real y, real z) -> p_real {
            p_real v = p_real.create();
            v[0] = x; v[1] = y; v[2] = z;
            return v;
        }
       
        // --------------------------------------------------------------------------------------
   
        function DDMsg(string str) {
            DisplayTimedTextFromPlayer(GetLocalPlayer(), 0., 0., 30., str);
        }
       
        // --------------------------------------------------------------------------------------
       
        function DisplayErrorMsgPlayer(player p, real dur, string msg) {
            if (GetLocalPlayer() == p) {
                StartSound(ErrorSound);
                DisplayTimedTextFromPlayer(p, 0., 0., dur, "|cffffcc00"+ msg +"|r");
            }
        }

    }
   
    // -----------------------------------------------------------------------
    // ->           ***** private globals *****
    // -----------------------------------------------------------------------
    private {
        location    TempLoc         = Location(0., 0.);
        timer       TimerStack[];
        integer     TimN            = 0;
        group       GroupStack[];
        integer     GrpN            = 0;
        unit        DummyStack[];
        integer     DumN            = 0;
        integer     TimTicks[];
        integer     TimData[];
        timer       TimTim1[];
        timer       TimTim2[];
       
       
        integer     UnitStackData   = 0;
        unit        UnitStack[];
        integer     US_N            = 0;
       
        public hashtable   DDHT            = InitHashtable();
    }
    // -----------------------------------------------------------------------
   
   public {
   
        // *** Global funcs
       
        function Pw_2(real x) -> real {
            return x*x;
        }
       
        function DDHypot(real x, real y) -> real {
            return (x*x) + (y*y);
        }
       
        function DDTerrZ(real x, real y) -> real {
            MoveLocation(TempLoc, x, y);
            return GetLocationZ(TempLoc);
        }
       
        function DDWidgetTerrZ(widget w) -> real {
            MoveLocation(TempLoc, GetWidgetX(w), GetWidgetY(w));
            return GetLocationZ(TempLoc);
        }
       
        function DDEffectTerrZ(effect e) -> real {
            MoveLocation(TempLoc, BlzGetLocalSpecialEffectX(e), BlzGetLocalSpecialEffectY(e));
            return GetLocationZ(TempLoc);
        }
       
        function DDGetUnitZ(unit u) -> real {
            return BlzGetUnitZ(u) + GetUnitFlyHeight(u);
        }
       
        // =================================================================
        //              *** Save Handle data ****
        // =================================================================
       
        function DDSet(handle h, integer id, integer val) {
            SaveInteger(DDHT, id+1, GetHandleId(h), val);
        }
       
        function DDGet(handle h, integer id) -> integer {
            return LoadInteger(DDHT, id+1, GetHandleId(h));
        }
       
        function DDHas(handle h, integer id) -> boolean {
            return HaveSavedInteger(DDHT, id+1, GetHandleId(h));
        }
       
        function DDFlush(integer id) {
            FlushChildHashtable(DDHT, id+1);
        }
       
        // =================================================================
        //              *** Timer Handling ****
        // =================================================================
       
        // -> check if timer is recycled
        function DDIsTimRecycled(timer t) -> boolean {
            integer i;
            for(i=TimN-01; i >= 00; i-=01)
                if (TimerStack[i] == t)
                    return true;
            return false;
        }
       
        // -> Load timer for recycling
        function DDLoadTim() -> timer {
            static if (LIBRARY_TimerUtils) { return NewTimer(); }
            else {
                if (TimN > 0) {
                    TimN -= 1;
                    return TimerStack[TimN];
                }
                return CreateTimer();
            }
        }
       
        // -> recycle loaded timer
        function DDRecycleTim(timer t) {
            static if (LIBRARY_TimerUtils) { ReleaseTimer(t); }
            else {
                static if (DEBUG_MODE)
                    if (DDIsTimRecycled(t)) {
                        DDMsg("Multiple recycle of timer!");
                        return;
                    }
                TimerStack[TimN] = t;
                TimN += 1;
            }
        }
       
        // ** Get data stored on expired timer
        function DDTimData() -> integer {
            return TimData[H2ID(GetExpiredTimer())];
        }
       
        // *** Custom timer tick
        function DDCTimTick(timer t) -> integer {
            return TimTicks[H2ID(t)];
        }
       
        // *** Gets current tick and adds next one ***
        function DDTimTick() -> integer {
            integer id = H2ID(GetExpiredTimer());
            TimTicks[id] += 01;
            return TimTicks[id];
        }
       
        // ** start timer with data storage
        function DDStartTim(real secs, boolean looping, integer pdata, code func) -> timer {
            timer t = DDLoadTim();
           
            TimData[H2ID(t)] = pdata;
            TimerStart(t, secs, looping, func);
            return t;
        }
       
        // ** start timer with data storage, and launches it instantly
        function DDStartTimInst(real secs, boolean looping, integer pdata, code func) -> timer {
            timer t1 = DDLoadTim(), t2 = DDLoadTim(), t3 = DDLoadTim();
           
            TimData[H2ID(t2)] = pdata;
            TimerStart(t2, 0., false, func);
           
            TimTim1[H2ID(t3)] = t1;
            TimTim2[H2ID(t3)] = t2;
            TimerStart(t3, .005, false, function() {
                timer t = GetExpiredTimer();
                integer id = H2ID(t);
               
                PauseTimer(t);
                static if (LIBRARY_TimerUtils)
                    ReleaseTimer(t);
                else {
                    TimerStack[TimN] = t;
                    TimN += 1;
                }
               
                t = TimTim2[id];
                if (DDIsTimRecycled(t))
                    t = TimTim1[id];
                TimTicks[H2ID(t)] = 00;
                PauseTimer(t);
                static if (LIBRARY_TimerUtils)
                    ReleaseTimer(t);
                else {
                    TimerStack[TimN] = t;
                    TimN += 1;
                }
            });
           
            TimData[H2ID(t1)] = pdata;
            TimerStart(t1, secs, looping, func);
           
            return t1;
        }
       
        // *** Quit expired timer ***
        function DDQuitTim() {
            timer t = GetExpiredTimer();
            TimTicks[H2ID(t)] = 00;
            PauseTimer(t);
            static if (LIBRARY_TimerUtils)
                ReleaseTimer(t);
            else {
                TimerStack[TimN] = t;
                TimN += 1;
            }
        }
       
        function DDQuitTimEx(timer t) {
            TimTicks[H2ID(t)] = 00;
            PauseTimer(t);
            static if (LIBRARY_TimerUtils)
                ReleaseTimer(t);
            else {
                TimerStack[TimN] = t;
                TimN += 1;
            }
        }
       
        // =================================================================
        //              *** Group Handling ****
        // =================================================================
       
        // -> Load timer for recycling
        function DDLoadGroup() -> group {
            static if (LIBRARY_GroupUtils) { return NewGroup(); }
            else {
                if (GrpN > 0) {
                    GrpN -= 1;
                    return GroupStack[GrpN];
                }
                return CreateGroup();
            }
        }
       
        // -> Recycle group
        function DDRecycleGroup(group g) {
            static if (LIBRARY_GroupUtils) { ReleaseGroup(g); }
            else {
                GroupClear(g);
                GroupStack[GrpN] = g;
                GrpN += 1;
            }
        }
       
        // --------------------------------------------------------
        // -- Quick filter area
        private integer GroupFilterData = 00;
       
        function DDGroupFilterArea(real x, real y, real radius, integer data, code func) {
            group g = DDLoadGroup();
            GroupFilterData = data;
            GroupEnumUnitsInRange(g, x, y, radius, Filter(func));
            DDRecycleGroup(g);
        }
       
        // --------------------------------------------------------
        // -- Quick filter rect
        function DDGroupFilterRect(rect r, integer data, code func) {
            group g = DDLoadGroup();
            GroupFilterData = data;
            GroupEnumUnitsInRect(g, r, Filter(func));
            DDRecycleGroup(g);
        }
       
        function DDGFilterData() -> integer {
            return GroupFilterData;
        }
       
        function DDGFilterDataSet(integer data) {
            GroupFilterData = data;
        }
       
        // --------------------------------------------------------
        // *** Filtrates and fills units in to memory
        function DDGroupFillMemArea(real x, real y, real radius, integer data, code filter) {
            group g = DDLoadGroup();
            boolexpr exp = And(Filter(filter), Filter(function() -> boolean {
                UnitStack[US_N] = GetFilterUnit();
                US_N += 1;
                return false;
            }));
            US_N = 0;
            UnitStack[0] = null;
            UnitStackData = data;
            GroupEnumUnitsInRange(g, x, y, radius, exp);
            DDRecycleGroup(g);
            DestroyBoolExpr(exp);
            exp = null;
        }
       
        function DDGroupFillMemRect(rect r, integer data, code filter) {
            group g = DDLoadGroup();
            boolexpr exp = And(Filter(filter), Filter(function() -> boolean {
                UnitStack[US_N] = GetFilterUnit();
                US_N += 1;
                return false;
            }));
            US_N = 0;
            UnitStack[0] = null;
            UnitStackData = data;
            GroupEnumUnitsInRect(g, r, exp);
            DDRecycleGroup(g);
            DestroyBoolExpr(exp);
            exp = null;
        }
       
        function DDMemUnitN() -> integer { return US_N; }
        function DDMemUnitData() -> integer { return UnitStackData; }
       
        function DDMemUnit(integer index) -> unit {
            if (US_N == 0) return null;
            debug {
                if (index < 0) {
                    BJDebugMsg("DDMemUnit: index less than 0");
                    index = 0;
                } else if (index >= US_N) {
                    BJDebugMsg("DDMemUnit: index greater than units alloc size");
                    index = 0;
                }
            }
            return UnitStack[index];
        }
        // --------------------------------------------------------
       
        // --------------------------------------------------------
        // ***
       
        // =================================================================
        //              *** Dummy Handling ****
        // =================================================================
       
        // -> Load dummy for recycling
        function DDLoadDummy() -> unit {
            if (DumN > 0) {
                DumN -= 1;
                PauseUnit(DummyStack[DumN], false);
                return DummyStack[DumN];
            }
            return CreateUnit(Player(0xF), DD_DUMMYCODE, DDMaxX, DDMaxY, 0.);
        }
       
        // *** prepares/setups dummy for spell casting
        function DDLoadSpellDummy(player owner, real x, real y, integer abil, integer abilLevel) -> unit {
            unit dummy = DDLoadDummy();
            SetUnitOwner(dummy, owner, false);
            SetUnitX(dummy, x);
            SetUnitY(dummy, y);
            if (abil != p_null) {
                UnitAddAbility(dummy, abil);
                SetUnitAbilityLevel(dummy, abil, abilLevel);
            }
            return dummy;
        }
       
        // -> Recycle dummy
        function DDRecycleDummy(unit u) {
            PauseUnit(u, true);
            DummyStack[DumN] = u;
            DumN += 1;
        }
       
        // -> Recycle dummy timed
        function DDRecycleDummyTimed(unit u, real secs) {
            DDStartTim(secs, false, New_pUnit(u), function() {
                DDRecycleDummy(p_unit(DDTimData())[0]);
                p_unit(DDTimData()).destroy();
                DDQuitTim();
            });
        }
       
        // *** shares vision for timed amount, usually for dummy casting
        function DDUnitShareVisionTimed(unit u, player toP, real secs) {
            p_integer pi = p_integer.create();
           
            pi[0] = New_pUnit(u);
            pi[1] = GetPlayerId(toP);
            UnitShareVision(u, toP, true);
            DDStartTim(secs, false, pi, function() {
                p_integer pi = DDTimData();
               
                UnitShareVision(p_unit(pi[0])[0], Player(pi[1]), false);
                p_unit(pi[0])[0] = null;
                p_unit(pi[0]).destroy();
                pi.destroy();
                DDQuitTim();
            });
           
        }
       
        // *** Remove ability timed ***
        private struct uratimed {
            private {
                unit u;
                integer abil;
            }
           
            static method create(unit whichUnit, integer id, real time) -> uratimed {
                thistype this = allocate();
               
                u = whichUnit;
                abil = id;
                DDStartTim(time, false, this, function() {
                    thistype this = DDTimData();
                    UnitRemoveAbility(u, abil);
                    DDQuitTim();
                    deallocate();
                });
               
                return 0;
            }
        }
        function DDRemoveAbilityTimed(unit u, integer abil, real secs) { uratimed.create(u, abil, secs); }
       
        function DDSpellDamage(unit u, unit v, real dmg) {
            real life = GetWidgetLife(v);
            real dmgfactor = 1.;
           
            if (IsUnitType(v, UNIT_TYPE_HERO)) {
                if (UnitHasItemOfTypeBJ(v, 'brac'))
                    dmgfactor = .5;
                else
                    dmgfactor = .75;
            }
           
            if (life > dmg*dmgfactor) {
                SetWidgetLife(v, life-(dmg*dmgfactor));
            } else
                UnitDamageTarget(u, v, 99999., false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
        }
       
        // -------------------------------------------------------------------------------------
        // *** Chills target unit
        private struct chill {
            unit u, dmy;
            real dur;
           
            static chill Data[];
            //static key CHILL_KEY;
        }
       
        function DDUnitChill(player p, unit u, real dur) -> boolean {
            //chill c = DDGet(u, chill.CHILL_KEY);
            chill c = chill.Data[H2ID(u)];
            unit d;
            real rad;
           
           
            if (c == p_null) {
                c = chill.create();
                c.u = u; c.dur = dur;
                chill.Data[H2ID(u)] = c;
                //DDSet(u, chill.CHILL_KEY, c);
               
                d = DDLoadDummy();
                c.dmy = d;
                rad = GetUnitFacing(d) * bj_DEGTORAD;
                SetUnitOwner(d, p, false);
                UnitAddAbility(d, DD_CHILL);
                SetUnitX(d, GetUnitX(u) - 20.*Cos(rad));
                SetUnitY(d, GetUnitY(u) - 20.*Sin(rad));
               
                if (IssueTargetOrder(d, "frostnova", u)) {
                    DDStartTim(.1, true, c, function() {
                        chill c = DDTimData();
                       
                        c.dur -= .1;
                        if (c.dur <= 0. || GetUnitAbilityLevel(c.u, DD_CHILL_BUFF) == 00) {
                            UnitRemoveAbility(c.u, DD_CHILL_BUFF);
                            UnitRemoveAbility(c.dmy, DD_CHILL);
                            DDRecycleDummy(c.dmy);
                            chill.Data[H2ID(c.u)] = p_null;
                            //DDSet(c.u, chill.CHILL_KEY, p_null);
                            c.u = null;
                            c.destroy();
                            DDQuitTim();
                        }
                    });
                    return true;
                }
               
                return false;
            }
           
            c.dur = dur;
           
            return true;
        }
       
        // ------------------------------------------------------------------------------------------------
       
        struct fade {
            unit u;
            real trans;
            real rate, e_trans, dur;
           
            static constant real INTERVAL = .1;
           
            static method create(unit u, real dur, real s_trans, real e_trans) -> fade {
                fade this = allocate();
               
                this.u = u;
                this.trans = s_trans;
                this.rate = ((e_trans-s_trans)/dur)*fade.INTERVAL;
                this.e_trans = e_trans;
                this.dur = dur;
               
                return this;
            }
        }
       
        // *** Fades unit over time ***
        public function DDFadeUnit(unit u, integer from_alpha, integer to_alpha, real duration) {
            fade f = fade.create(u,
                                 duration,
                                 from_alpha/2.55,
                                 to_alpha/2.55);
           
            SetUnitVertexColor(u, 255, 255, 255, from_alpha);
            // --- Start thread ---
            DDStartTim(fade.INTERVAL, true, f, function() {
                fade f = DDTimData();
               
                f.trans += f.rate;
                f.dur -= fade.INTERVAL;
                SetUnitVertexColor(f.u, 255, 255, 255, R2I(f.trans*2.55));
               
                if (f.dur < 0.) {
                    SetUnitVertexColor(f.u, 255, 255, 255, R2I(f.e_trans*2.55));
                    f.u = null;
                    f.destroy();
                    DDQuitTim();
                }
            });
                       
        }
       
        // ------------------------------------------------------------------------------------------------
       
        // Check if unit is invulnerable
        function DDIsUnitInvulnerable(unit u) -> boolean {
            unit d = DDLoadDummy();
            real hp = GetWidgetLife(u);
            boolean flag;
           
            UnitDamageTarget(d, u, 1., true, false, null, null, null);
            flag = GetWidgetLife(u) == hp;
            SetWidgetLife(u, hp);
            DDRecycleDummy(d);
           
            return flag;
        }
       
        // *** check if unit is ward
        function DDIsUnitWard(unit whichUnit) -> boolean {
            return GetUnitDefaultMoveSpeed(whichUnit) == 0.;
        }
       
        // =================================================================
        //              *** Effect Handling ****
        // =================================================================
       
        // -----------------------------------------------
        // *** Define movable effect
        struct ddeffect {
            private {
                effect e;
                real fac; // facing angle in radians
                real effZ; // pitch in radians
                real decay;
                real stepTrans, cTrans, eTrans;
               
                static constant real        EFFECT_DECAY        = 5.;
            }
           
            // =========================================================================================
            // =========================================================================================
            static method create(string mdl, real x, real y, real facRad, real size) -> ddeffect {
                ddeffect this = allocate();
               
                this.e      = AddSpecialEffect(mdl, x, y);
                this.fac    = facRad;
                this.effZ   = 0.;
                BlzSetSpecialEffectRoll(this.e, facRad);
                BlzSetSpecialEffectScale(this.e, size);
               
                return this;
            }
           
            static method createZ(string mdl, real x, real y, real z, real facRad, real size) -> ddeffect {
                ddeffect this = allocate();
               
                this.e      = AddSpecialEffect(mdl, x, y);
                this.fac    = facRad;
                this.effZ   = z-DDTerrZ(x, y);
                BlzSetSpecialEffectRoll(this.e, facRad);
                BlzSetSpecialEffectScale(this.e, size);
                BlzSetSpecialEffectZ(this.e, z);
               
                return this;
            }
           
            // -----------------
            method destroy() {
                DestroyEffect(this.e);
                this.e = null;
                deallocate();
            }
           
            // *** destroys effect timed
            method destroyx(real decayTime) {
                DDStartTim(decayTime, false, this, function() {
                    ddeffect se = DDTimData();
                    BlzSetSpecialEffectPosition(se.e, DDMaxX, DDMaxY, 0.);
                    DestroyEffect(se.e);
                    se.e = null;
                    se.deallocate();
                    DDQuitTim();
                });
            }
            // =========================================================================================
            // =========================================================================================
           
           
           
            method operator Z=(real z)                  { BlzSetSpecialEffectZ(this.e, z); }
           
            method operator X() -> real                 { return BlzGetLocalSpecialEffectX(this.e); }
            method operator Y() -> real                 { return BlzGetLocalSpecialEffectY(this.e); }
            method operator Z() -> real                 { return BlzGetLocalSpecialEffectZ(this.e); }
            method operator WZ() -> real                { return DDEffectTerrZ(this.e); }
            method operator Height() -> real            { return this.Z-this.WZ; }
           
            method operator Facing=(real facRad)        { BlzSetSpecialEffectRoll(this.e, facRad); this.fac = facRad; }
            method operator Facing() -> real            { return this.fac; }
           
            method Position(real x, real y)             { BlzSetSpecialEffectPosition(this.e, x, y, this.effZ+this.WZ); }
            method PositionZ(real x, real y, real z)    { BlzSetSpecialEffectPosition(this.e, x, y, z); }
            method Animation(animtype at)               { BlzPlaySpecialEffect(this.e, at); }
            method AnimationSpeed(real animSpeed)       { BlzSetSpecialEffectTimeScale(this.e, animSpeed/100.); }
           
            //method operator Pitch=(integer pitch)     { SetUnitAnimationByIndex(u, pitch); }
           
            //method Face(widget w) { Facing = Atan2(GetWidgetY(w)-Y, GetWidgetX(w)-X)*bj_RADTODEG; }
           
            method Fade(real startTransparency, real endTransparency, real duration) {
                this.cTrans = startTransparency;
                this.eTrans = endTransparency;
                this.stepTrans = .1*(endTransparency-startTransparency) / duration;
               
                BlzSetSpecialEffectAlpha(this.e, R2I(startTransparency*2.55));
               
                DDStartTim(.1, true, this, function() {
                    ddeffect dde = DDTimData();
                   
                    dde.cTrans += dde.stepTrans;
                    if (dde.stepTrans > 0.)
                        if (dde.cTrans >= dde.eTrans) {
                            BlzSetSpecialEffectAlpha(dde.e, R2I(dde.eTrans*2.55));
                            DDQuitTim();
                            return;
                        }
                    else
                        if (dde.cTrans <= dde.eTrans) {
                            BlzSetSpecialEffectAlpha(dde.e, R2I(dde.eTrans*2.55));
                            DDQuitTim();
                            return;
                        }
                    BlzSetSpecialEffectAlpha(dde.e, R2I(dde.cTrans*2.55));
                });
            }
           
        }
       
        private type timedeffect extends effect[01];
       
        function DDDestroyEffectTimed(effect e, real secs) {
            timedeffect te = timedeffect.create();
            te[00] = e;
            DDStartTim(secs, true, te, function() {
                timedeffect te = DDTimData();
                DestroyEffect(te[00]);
                te.destroy();
                DDQuitTim();
            });
        }
    }
   
    // ----------------------------------------------------------------------------
    // *** Main damage detection function, registers any damage dealt to units ***
    public function DDTriggerRegisterAnyUnitDamaged(trigger t) {
        DmgTrig[NTrig] = t;
        NTrig += 1;
    }
   
    function InitDamageDetection() {
        code c = function() {
            if (TempTrig != null)
                DestroyTrigger(TempTrig);
           
            TempTrig = CreateTrigger();
            TriggerRegisterEnterRectSimple(TempTrig, bj_mapInitialPlayableArea);
            TriggerAddCondition(TempTrig, function() -> boolean {
                integer i;
               
                // *** Check if we need to exec triggers or register an unit ***
                if (GetTriggerEventId() == EVENT_UNIT_DAMAGED) {
                    for(i=0; i < NTrig; i+=1)
                        if (IsTriggerEnabled(DmgTrig[i]))
                            if (TriggerEvaluate(DmgTrig[i]))
                                TriggerExecute(DmgTrig[i]);
                }
                else
                    // *** Register new unit ***
                    TriggerRegisterUnitEvent(GetTriggeringTrigger(),
                                             GetTriggerUnit(),
                                             EVENT_UNIT_DAMAGED);
               
                return false;
            });
           
            DDGroupFilterRect(bj_mapInitialPlayableArea, 00, function() -> boolean {
                TriggerRegisterUnitEvent(TempTrig, GetFilterUnit(), EVENT_UNIT_DAMAGED);
                return false;
            });
        };
        trigger t = CreateTrigger();
       
        TriggerAddAction(t, c);
        TriggerExecute(t);
        DestroyTrigger(t);
        TimerStart(CreateTimer(), TRIGGER_REFRESH_RATE, true, c);
        t = null;
    }
   
    // ---------------------------------------------------------------------------------
   
    // *** Main enum dests in range function ***
    public function DDEnumDestsInRange(p_real vec, real radius, boolexpr filter, code pfunc) {
        rect r = Rect(vec[0]-radius, vec[1]-radius, vec[0]+radius, vec[1]+radius);
       
         EnumVec[0] = vec[0];
         EnumVec[1] = vec[1];
         EnumVec[2] = radius;
           
        if (filter != null) filter = And(EnumFilter, filter);
        else filter = EnumFilter;
        EnumDestructablesInRect(r, filter, pfunc);
       
        if (filter != EnumFilter) { DestroyBoolExpr(filter); filter = null; }
        RemoveRect(r);
        r = null;
    }
       
    function InitEnumDests() {
        EnumVec = p_real.create();
        EnumFilter = Filter(function() -> boolean {
            return Pw_2(EnumVec[0]-GetDestructableX(GetFilterDestructable())) + Pw_2(EnumVec[1]-GetDestructableY(GetFilterDestructable())) < Pw_2(EnumVec[2]);
        });
    }
   
    // --------------------------------------------------------------------------------------
   
    // *** checks is destruct tree ***
    public function DDIsDestructableTree(destructable d) -> boolean {
        if (d != null) {
            PauseUnit(TreeChecker, false);
            if (IssueTargetOrder(TreeChecker, "harvest", d)) {
                PauseUnit(TreeChecker, true);
                return true;
            }
            PauseUnit(TreeChecker, true);
        }
        return false;
    }

    function InitDestTreeCheck() {
        TreeChecker = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), DD_DUMMYCODE, DDMaxX, DDMaxY, 0.);
        UnitAddAbility(TreeChecker, HARVEST_ID);
        PauseUnit(TreeChecker, true);
    }
   
    // --------------------------------------------------------------------------------------
   
    public function DDNewTextTagUnit(unit whichUnit, string text, real dur, real red, real green, real blue, real transparency) {
        CreateTextTagUnitBJ( text, whichUnit, 0., 11.00, red, green, blue, transparency );
        SetTextTagPermanentBJ( bj_lastCreatedTextTag, false );
        SetTextTagVelocityBJ( bj_lastCreatedTextTag, 48.00, 90 );
        SetTextTagFadepointBJ( bj_lastCreatedTextTag, dur-1.25 );
        SetTextTagLifespanBJ( bj_lastCreatedTextTag, dur );
    }
   
    // --------------------------------------------------------------------------------------
   
    struct cameranoisedata {
        player p[12];
        integer n=00;
    }
   
    public function DDCameraSetSourceNoiseForPlayers(real x, real y, real mag, real vel, real maxDist, real duration) {
        integer i;
        real d;
        cameranoisedata cnd = cameranoisedata.create();
       
        for (i=00; i < bj_MAX_PLAYERS; i+=01) {
            if (GetLocalPlayer() == Player(i)) {
                d = SquareRoot( Pw_2(GetCameraTargetPositionX()-x) + Pw_2(GetCameraTargetPositionY()-y) );
                if (d < maxDist) {
                    cnd.p[cnd.n] = Player(i);
                    CameraSetSourceNoise(mag-(d*(mag/maxDist)), vel-(d*(vel/maxDist)));
                    CameraSetTargetNoise(mag-(d*(mag/maxDist)), vel-(d*(vel/maxDist)));
                    cnd.n += 01;
                }
            }
        }
       
        DDStartTim(duration, false, cnd, function() {
            cameranoisedata cnd = DDTimData();
           
            while(cnd.n != 00) {
                cnd.n -= 01;
                if (GetLocalPlayer() == cnd.p[cnd.n])
                    CameraSetSourceNoise(0., 0.);
                    CameraSetTargetNoise(0., 0.);
            }
           
            cnd.destroy();
            DDQuitTim();
        });
    }
   
    // --------------------------------------------------------------------------------------
   
    hashtable GenSndTable = null;
   
    public function DDGenericSound(string file, real vol, real x, real y, real mxDist, real pitch) {
        sound s;
        real d;
        integer i;
        integer snd_n, sh;
       
        // Play sound and shake camera for players within spell cast range
        for (i=00; i < bj_MAX_PLAYERS; i+=01) {
            if (GetLocalPlayer() == Player(i)) {
                d = SquareRoot( Pw_2(GetCameraTargetPositionX()-x) + Pw_2(GetCameraTargetPositionY()-y) );
                if (d < mxDist) {
                    sh = StringHash(file);
                    snd_n = LoadInteger(GenSndTable, sh, 04);
                    s = LoadSoundHandle(GenSndTable, sh, snd_n);
                    if (s == null) {
                        s = CreateSound(file, false, false, false, 10, 10, "");
                        SaveSoundHandle(GenSndTable, sh, snd_n, s);
                    } else if (GetSoundIsPlaying(s)) {
                        StopSound(s, false, false);
                    }
                    SetSoundPitch(s, pitch);
                    SetSoundVolume(s, R2I((vol-d*(vol/mxDist))*1.27));
                    StartSound(s);
                    snd_n += 01;
                    if (snd_n == 04)
                        snd_n = 00;
                    SaveInteger(GenSndTable, sh, 04, snd_n);
                }
            }
        }
    }
   
    public function DDGetGameElapsedTime() -> real {
        return TimerGetElapsed(GameElapsedTimer);
    }
   
    public function DDGetRndReal(real min, real max) -> real {
        real rnd = ((max-min)/1000000.)*I2R(RndInt[RndIntReadN]);
        debug if (max > 1000000.)
            DDMsg("ERROR: \"DDGetRndNumber\" - 'max' variable is greater than 1000000!");
        RndIntReadN += 01; if (RndIntReadN == RND_INT_MAX_ARRAY_N) RndIntReadN = 00;
        return min + rnd;
    }
   
    public function DDGetRndInt(integer min, integer max) -> integer {
        return R2I( DDGetRndReal(I2R(min), I2R(max)) );
    }
   
    // ====================================================================
    function onInit() {
        InitDamageDetection();
        InitDestTreeCheck();
        InitEnumDests();
       
        DDMinX = GetRectMinX(bj_mapInitialPlayableArea);
        DDMinY = GetRectMinY(bj_mapInitialPlayableArea);
        DDMaxX = GetRectMaxX(bj_mapInitialPlayableArea);
        DDMaxY = GetRectMaxY(bj_mapInitialPlayableArea);
       
        GenSndTable = InitHashtable();
       
        ErrorSound = CreateSound( "Sound\\Interface\\Error.wav", false, false, false, 10, 10, "" );
        SetSoundParamsFromLabel( ErrorSound, "InterfaceError" );
        SetSoundDuration( ErrorSound, 614 );
        SetSoundVolume(ErrorSound, 127);
       
        GameElapsedTimer = CreateTimer();
        TimerStart(GameElapsedTimer, 10800., false, null);
       
        for(RndIntWriteN=00; RndIntWriteN < RND_INT_MAX_ARRAY_N; RndIntWriteN+=01)
            RndInt[RndIntWriteN] = GetRandomInt(00, 1000000);
       
        RndIntWriteN = 00;
        RndGenForce = CreateForce();
        TrigMouseEvent = CreateTrigger();
        ForForce(bj_FORCE_ALL_PLAYERS, function() {
            if (GetPlayerController(GetEnumPlayer()) == MAP_CONTROL_USER)
                TriggerRegisterPlayerEvent(TrigMouseEvent, GetEnumPlayer(), EVENT_PLAYER_MOUSE_MOVE);
        });
        TriggerAddCondition(TrigMouseEvent, Condition(function() -> boolean {
            real mouseN;
            boolean xFirst = GetRandomInt(00, 01) == 01;
           
            if (!IsPlayerInForce(GetTriggerPlayer(), RndGenForce)) {
                // example: input x = 578.4571496
                //          output rnd n = 4571498
                if (xFirst)
                    mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
                else
                    mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
                if (mouseN == 0.)
                    return false;
                //mouseN *= 100.;
                RndInt[RndIntWriteN] = R2I((mouseN - I2R(R2I(mouseN))) * 1000.);
                //DDMsg(I2S(RndInt[RndIntWriteN]));
                //RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
               
                if (xFirst)
                    mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
                else
                    mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
                RndInt[RndIntWriteN] += R2I((mouseN - I2R(R2I(mouseN))) * 1000.)*1000;
                //DDMsg(I2S(RndInt[RndIntWriteN]));
                RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
                ForceAddPlayer(RndGenForce, GetTriggerPlayer());
            }
           
            if (DDGetGameElapsedTime()-RndElapsedTime > .125) {
                ForceClear(RndGenForce);
                RndElapsedTime = DDGetGameElapsedTime();
            }
           
            return false;
        }));
    }
   
}

//! endzinc











 
//TESH.scrollpos=0
//TESH.alwaysfold=0
// *******************************************************************************
//                          Spell: Max Bash by Dark Dragon
//
//                 Credits: IceFrog    -> Used in Dota! Idea is by someone...
//                          Venom      -> Icon
//                          nerovesper -> Request
//
//
//                              Installation:
//
//  1. Copy 'DD Library' and this trigger to your map
//  2. Copy abilitie and buff 'Max Bash' to your map
//  3. Export and import "DDSpells\MaxBashHit.mdx" and icons with same path to your map if you are going to use them
//  4. Modifie data below to mach your map and other constants
//
// * Enjoy!
// *******************************************************************************






//! zinc
// *** This is the max bash spell library ***
library MaxBash requires DDLib
{
    // ======================================================================
    //                  *** Editable constants ***
    // ======================================================================
   
    // -------------------------------------------------------
    // This is the raw code of max bash ability
    // Default: 'MBsh'
    constant integer        MAX_BASH_ID                     = 'MBsh';
   
    // -------------------------------------------------------
    // This is the raw code of max bash buff
    // Default: 'MBed'
    constant integer        MAX_BASH_BUFF_ID                = 'MBed';
   
    // ----------------------------------------------------------
    // Push model file effect
    // Default: Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl
    constant string         PUSH_MODEL_FILE                 = "Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl";
   
    // ----------------------------------------------------------
    // Push model file effect of sliding unit, attach point
    // Default: foot
    constant string         PUSH_EFFECT_ATT_POINT           = "foot";
   
    // ----------------------------------------------------------
    // If custom water effect is wanted set this to true
    // Default: true
    constant boolean        USE_WATER_FILE                  = true;
   
    // ----------------------------------------------------------
    // Push model file effect on water
    // Default: Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl
    constant string         PUSH_MODEL_WATER_FILE           = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl";
   
    // -------------------------------------------------------
    // On push file (when ability is triggerd) applay this effect
    // Default: DDSpells\\MaxBashHit.mdx
    constant string         ON_PUSH_FILE                    = "DDSpells\\MaxBashHit.mdx";
   
    // -------------------------------------------------------
    // How often to create "PUSH_MODEL_FILE" effect
    // Default: .1
    constant real           PUSH_EFFECT_INTERVAL            = .15;
   
    // -------------------------------------------------------    
    // On which point of attacker "ON_PUSH_FILE" is added?
    // Default: weapon
    constant string         ATTACH_POINT                    = "weapon";
   
    // -------------------------------------------------------    
    // Effect generated on target unit when its hit
    // Default: none
    constant string         BASH_HIT_EFFECT_FILE            = "";
    constant string         BASH_HIT_EFFECT_FILE_ATT_POINT  = "origin";
   
    // -------------------------------------------------------    
    // Effect generated when unit hits the ground
    // Default: Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl
    constant string         BASH_HIT_GROUND_EFFECT_FILE     = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl";
   
    // -------------------------------------------------------    
    // Effect generated when unit hits/impacts the water, requires USE_WATER_FILE to be true
    // Default: Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl
    constant string         BASH_IMPACT_WATER_EFFECT_FILE   = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl";
   
    // -------------------------------------------------------    
    // While unit is sliding, what will be its animation speed in percentage
    // Default: 65.
    constant real           UNIT_ANIM_SPEED                 = 65.;
   
    // -------------------------------------------------------    
    // While unit is sliding, what will be its animation
    // Default: attack
    constant string         UNIT_ANIM_NAME                  = "attack";
   
    // -------------------------------------------------------    
    // Extra damage that bash deals seted up by level "function BashDamage" will
    // be treated as extra/bonus damage or as percentage of dealt damage,
    // if true percent damage will be dealt
    // Default: true
    constant boolean        BASH_DAMAGE_AS_PERCENTAGE       = true;
   
    // -------------------------------------------------------    
    // This is speed in game coordinates, of how fast unit is pushed away
    // defined start and end speeds of pushed unit
    // start speed = initial speed, end speed = speed before stopping
    // Default: 350 / 50
    constant real           PUSH_SPEED_START                = 350.;
    constant real           PUSH_SPEED_END                  = 50.;
   
    // -------------------------------------------------------
    // Enemy units that collide with sliding unit will be slightly pushed too?
    // Default: true
    constant boolean        ENABLE_SPLASH_BASH              = true;
   
    // -------------------------------------------------------
    // Damage splashed on extra colliding units in percentage
    // Default: 30%
    constant real           SPLASH_DAMAGE_PERCENTAGE        = 30.;
   
    // -------------------------------------------------------
    // The min speed at which sliding unit has to be to cause pushing other unit
    // Default: 175.
    constant real           SPLASH_BASH_NEED_SPEED          = 175.;
   
    // -------------------------------------------------------
    // The area/radius around sliding unit to check for splash damage
    // Default: 120.
    constant real           SPLASH_BASH_SCAN_R              = 120.;
   
    // -------------------------------------------------------
    // How often to check for splash bash damage
    // Default: .2
    constant real           SPLASH_BASH_INTERVAL            = .2;
   
    // -------------------------------------------------------
    // Some units have default ground offset height, what is that max height
    // so that bash does treat this units as grounded
    // Default: 50.
    constant real           SPLASH_BASH_MAX_HEIGHT          = 50.;
   
    // -------------------------------------------------------
    // Should trees be destroyed?
    // Default: true
    constant boolean        DESTROY_TREES                   = true;
   
    // -------------------------------------------------------
    // How often to clear trees
    // Default: .2
    constant real           CLEAR_TREES_INTERVAL            = .2;
   
    // -------------------------------------------------------
    // Range in which to clear trees
    // Default: 160.
    constant real           CLEAR_TREES_RADIUS              = 160.;
   
    // -------------------------------------------------------
    // Range at which to check if pathing is unpathable, for bounce code to be triggerd
    // Default: 60.
    constant real           BOUNCE_DETECT_RANGE             = 60.;
   
    // -------------------------------------------------------    
    // Should floating text be created?
    // Default: true
    constant boolean        ALLOW_FLOATING_TEXT             = true;
   
    // -------------------------------------------------------    
    // When units are killed by bash display KO or damage dealt
    // Default: true
    constant boolean        USE_KO_TEXT                     = true;
   
    // -------------------------------------------------------    
    // The duration of floating text in seconds
    // Default: 4.25
    constant real           FLOATING_TEXT_DURATION          = 3.;
   
    // -------------------------------------------------------    
    // The color of text rgb transparency in percentage
    // Default: 100. / 100. / 100. / 20.
    constant real           FLOATING_TEXT_RED               = 100.;
    constant real           FLOATING_TEXT_GREEN             = 100.;
    constant real           FLOATING_TEXT_BLUE              = 100.;
    constant real           FLOATING_TEXT_TRANS             = 20.;
   
    // -------------------------------------------------------------
    constant key            UNIT_BASH_KEY;
    // -------------------------------------------------------------
    //          *** Level data setup here ***
   
   
    // -------------------------------------------
    // *** The bash damage per level ***
    // Default: 2.6 / 3.4 / 4.5
    function BashDamage(integer level) -> real {
        real bash_damage[];
       
        bash_damage[1] = 2.6;
        bash_damage[2] = 3.4;
        bash_damage[3] = 4.5;
       
        return bash_damage[level];
    }
   
    // -------------------------------------------
    // *** The bash push distance per level ***
    // Default: 200. / 230. / 270.
    function BashPushDistance(integer level) -> real {
        real bash_range[];
       
        bash_range[1] = 200.;
        bash_range[2] = 230.;
        bash_range[3] = 270.;
       
        return bash_range[level];
    }
   
    // -------------------------------------------
    // *** The bash push height per level ***
    // Default: 90. / 110. / 130.
    function BashPushHeight(integer level) -> real {
        real bash_height[];
       
        bash_height[1] = 90.;
        bash_height[2] = 110.;
        bash_height[3] = 130.;
       
        return bash_height[level];
    }
   
    // -------------------------------------------
    // *** The bash push height travel percentage per level ***
    // This setting says at which percentage of path will unit land
    // Default: 30. / 34. / 39.
    function BashPushHeightTravelPerc(integer level) -> real {
        real bash_height_travel_perc[];
       
        bash_height_travel_perc[1] = 30.;
        bash_height_travel_perc[2] = 34.;
        bash_height_travel_perc[3] = 39.;
       
        return bash_height_travel_perc[level];
    }
   
    // -------------------------------------------
    // *** The damaged dealt by impact
    // Default: 30. / 40. / 55.
    function BashPushGroundImpactDamage(integer level) -> real {
        real bash_ground_impact_damage[];
       
        bash_ground_impact_damage[1] = 30.;
        bash_ground_impact_damage[2] = 40.;
        bash_ground_impact_damage[3] = 55.;
       
        return bash_ground_impact_damage[level];
    }
   
   
    // -------------------------------------------
    // *** If splash bash is activated, this will filter which are proper units to be pushed.
    function SplashBashFilter(unit filter_unit, player bashPlayer) -> boolean {
        return  !IsUnitType(filter_unit, UNIT_TYPE_DEAD)                        &&
                IsUnitEnemy(filter_unit, bashPlayer)                            &&
                IsUnitType(filter_unit, UNIT_TYPE_GROUND)                       &&
                !BlzIsUnitInvulnerable(filter_unit)                             &&
                !IsUnitType(filter_unit, UNIT_TYPE_STRUCTURE)                   &&
                !DDIsUnitWard(filter_unit);
    }
   
    // ==================================================================================
    //                      *** End spell modification ***
    // ==================================================================================
   
    // ---------------------------------------------------------
    // *** Bash data structure
    struct bash {
        private {
            unit        b, s;
            real        dist, mxdist, impdist;
            real        dx, dy, initZ;
            p_real      z_parab;
            real        dmg;
            p_real      vec;
            integer     p, lvl;
            boolean     hit_ground;
        }
       
        /// ----------------------------------------------------------------
       
        // ***
        static method create(integer castplayer, unit bashingUnit, unit whichUnit, integer lvl, real distance, real radians, real damage) -> bash {
            bash this = allocate();
           
            s           = bashingUnit;
            b           = whichUnit;
            dist        = distance;
            mxdist      = SquareRoot(distance);
            dx          = Cos(radians);
            dy          = Sin(radians);
            vec         = pVector(GetUnitX(b), GetUnitY(b), GetUnitFlyHeight(b));
            p           = castplayer;
            dmg         = damage;
            hit_ground  = BashPushGroundImpactDamage(lvl) < 1.;
           
            // -------------------------
            // a(x-x0)^2 + y0 = a(x^2 - 2x*x0 + x0^2) + y0 = a*(x)^2 - 2*a*x0*(x) + a*x0^2+y0
            // -------------------------
            // x0 = mxDist(100-perc/2)/100
            // y0 = vec[2] + h
            // a = -h / (mtd/2)^2
            // b = -2*a*x0
            // c = y0+a*x0^2
            z_parab     = p_real.create();
            z_parab[0]  = -BashPushHeight(lvl) / Pw_2(BashPushHeightTravelPerc(lvl)*distance/200.);         // a = -h / (mtd/2)^2
            z_parab[1]  = -2. * z_parab[0] * ((100.-BashPushHeightTravelPerc(lvl)/2.)*distance/100.);                                               // b = -2*a*x0
            z_parab[2]  = (BashPushHeight(lvl) + vec[2]) + z_parab[0]*Pw_2((100.-BashPushHeightTravelPerc(lvl)/2.)*distance/100.);              // c = a*x0^2+y0
            this.lvl    = lvl;
            impdist     = (100.-BashPushHeightTravelPerc(lvl))*distance/100.;
            initZ       = vec[2];
           
            DDSet(b, UNIT_BASH_KEY, this);
            UnitAddAbility(b, 'Amrf');
            UnitRemoveAbility(b, 'Amrf');
            SetUnitTimeScale(b, UNIT_ANIM_SPEED*.01);
            if (!IsUnitType(b, UNIT_TYPE_DEAD)) {
                SetUnitAnimation(b, UNIT_ANIM_NAME);
                QueueUnitAnimation(b, "stand");
            }
           
            return this;
        }
       
        // ***
        method destroy() {
            DDSet(b, UNIT_BASH_KEY, p_null);
            SetUnitTimeScale(b, 1.);
            vec.destroy();
            z_parab.destroy();
            s = null;
            b = null;
            deallocate();
        }
       
        method operator IsAirborn() -> boolean { return !hit_ground; }
       
        /// ----------------------------------------------------------------
        //* splash code
        private method TryDoSplashBash() {
            integer i;
            unit u;
           
            // *** Skip splash if unit is in air ***
            if (vec[2] > SPLASH_BASH_MAX_HEIGHT)
                return;
           
            // --------------------------------------
            // *** pick units to be splash bashed
            DDGroupFillMemArea(vec[0], vec[1], SPLASH_BASH_SCAN_R, p, function() -> boolean {
                return SplashBashFilter(GetFilterUnit(), Player(DDMemUnitData())) && DDGet(GetFilterUnit(), UNIT_BASH_KEY) == p_null;
            });
           
            // --------------------------------------------------------------
            // *** Now launch them in there respective directions and speeds
            for(i=0; i < DDMemUnitN(); i+=1) {
                u = DDMemUnit(i);
               
                // *** Do damage and bash the target ***
                UnitDamageTarget(s, u, dmg*SPLASH_DAMAGE_PERCENTAGE*.01, true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
                BashExecute.evaluate(p, s, u, lvl, dist, Atan2(GetUnitY(u)-vec[1], GetUnitX(u)-vec[0]), dmg);
               
                // *** Floating text add? ***
                static if (ALLOW_FLOATING_TEXT)
                    static if (USE_KO_TEXT)
                        if (IsUnitType(u, UNIT_TYPE_DEAD))
                            DDNewTextTagUnit(u, "|cffff0000KO!!!|r", FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
                        else
                            DDNewTextTagUnit(u, "+"+I2S(R2I(dmg*SPLASH_DAMAGE_PERCENTAGE*.01)), FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
                    else
                        DDNewTextTagUnit(u, "+"+I2S(R2I(dmg*SPLASH_DAMAGE_PERCENTAGE*.01)), FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
            }
           
        }
       
        // -------------------------------------------------------------------------
        // * checks and performs ground impact as well as dealing damage and displaying floating text
        method CheckGroundHit() {
            if (impdist > dist) {
                vec[2] = initZ;
                if (!hit_ground) {
                    hit_ground = true;
                    static if (USE_WATER_FILE)
                        if (IsTerrainPathable(vec[0], vec[1], PATHING_TYPE_FLOATABILITY))
                            DestroyEffect(AddSpecialEffect(BASH_HIT_GROUND_EFFECT_FILE, vec[0], vec[1]));
                        else
                            DestroyEffect(AddSpecialEffect(BASH_IMPACT_WATER_EFFECT_FILE, vec[0], vec[1]));
                    else
                        DestroyEffect(AddSpecialEffect(BASH_HIT_GROUND_EFFECT_FILE, vec[0], vec[1]));
                   
                    if (!IsUnitType(b, UNIT_TYPE_DEAD)) {
                        UnitDamageTarget(s, b, BashPushGroundImpactDamage(lvl), true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
                        // *** Floating text add? ***
                        static if (ALLOW_FLOATING_TEXT)
                            static if (USE_KO_TEXT)
                                if (IsUnitType(b, UNIT_TYPE_DEAD))
                                    DDNewTextTagUnit(b, "|cffff0000KO!!!|r", FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
                                else
                                    DDNewTextTagUnit(b, "+"+I2S(R2I(BashPushGroundImpactDamage(lvl))), FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
                            else
                                DDNewTextTagUnit(b, "+"+I2S(R2I(BashPushGroundImpactDamage(lvl))), FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
                    }
                }
            }
        }
       
        /// ----------------------------------------------------------------
        //* primary push code
        method Launch() {
            DDStartTim(DD_INTERVAL, true, this, static method() {
                bash        this        = bash( DDTimData() );
                integer     tick        = DDTimTick();
                real        speed       = DD_INTERVAL * (PUSH_SPEED_END + ( (PUSH_SPEED_START-PUSH_SPEED_END)/mxdist )*SquareRoot(dist));
                // basic formula for speed: y = a*sqrt(x) + b
               
                // ----------------------------------
                // *** Position calcs
                dist    -= speed;
                vec[0]  += speed * dx;
                vec[1]  += speed * dy;
                vec[2]  = (z_parab[0]*Pw_2(dist)) + (z_parab[1]*dist) + (z_parab[2]);
               
                // ------------------------------------
                // *** Check if hit ground ***
                CheckGroundHit();
               
                // ----------------------------------
                // *** Motion
                SetUnitX(b, vec[0]);
                SetUnitY(b, vec[1]);
                SetUnitFlyHeight(b, vec[2], 0.);
               
                // ----------------------------------
                // *** Push Effects
                if (ModuloReal(tick*DD_INTERVAL, PUSH_EFFECT_INTERVAL) < DD_INTERVAL)
                    static if (USE_WATER_FILE)
                        if (IsTerrainPathable(vec[0], vec[1], PATHING_TYPE_FLOATABILITY) || vec[2] > SPLASH_BASH_MAX_HEIGHT)
                            DestroyEffect(AddSpecialEffectTarget(PUSH_MODEL_FILE, b, PUSH_EFFECT_ATT_POINT));
                        else
                            DestroyEffect(AddSpecialEffectTarget(PUSH_MODEL_WATER_FILE, b, PUSH_EFFECT_ATT_POINT));
                    else
                        DestroyEffect(AddSpecialEffectTarget(PUSH_MODEL_FILE, b, PUSH_EFFECT_ATT_POINT));
               
                // ----------------------------------------
                // *** Check if trees are in the way
                static if (DESTROY_TREES)
                    if (ModuloReal(tick*DD_INTERVAL, CLEAR_TREES_INTERVAL) < DD_INTERVAL) {
                       
                        DDEnumDestsInRange(vec, CLEAR_TREES_RADIUS, null, function() {
                            if (DDIsDestructableTree(GetEnumDestructable()))
                                KillDestructable(GetEnumDestructable());
                        });
                    }
               
                // ---------------------------------------------------------
                // *** Check for bounce/collision with unpathable points
                if (ModuloReal(tick*DD_INTERVAL, CLEAR_TREES_INTERVAL) < DD_INTERVAL) {
                    if (IsTerrainPathable(vec[0]+BOUNCE_DETECT_RANGE*dx, vec[1], PATHING_TYPE_WALKABILITY))
                        dx = -dx;
                    else if (IsTerrainPathable(vec[0], vec[1]+BOUNCE_DETECT_RANGE*dy, PATHING_TYPE_WALKABILITY))
                        dy = -dy;
                }
               
                // ----------------------------------------
                // *** Check if there are any units to be splash bashed
                static if (ENABLE_SPLASH_BASH)
                    if (speed > SPLASH_BASH_NEED_SPEED*DD_INTERVAL && ModuloReal(tick*DD_INTERVAL, SPLASH_BASH_INTERVAL) < DD_INTERVAL)
                        TryDoSplashBash();
                   
                // ----------------------------------
                // *** Check to quit push
                if (dist <= 0.) {
                    PauseUnit(b, false);
                    destroy();
                    DDQuitTim();
                }
            });
        }
    }
   
    // *** Bash function that will be called from above and below
    function BashExecute(integer p, unit basher, unit u, integer lvl, real dist, real radians, real dmg) {
        // *** Now pause the victim ***
        PauseUnit(u, true);
        DestroyEffect(AddSpecialEffectTarget(BASH_HIT_EFFECT_FILE, u, BASH_HIT_EFFECT_FILE_ATT_POINT));
        bash.create(p, basher, u, lvl, dist, radians, dmg).Launch(); /// *** Will be auto destroyed
    }
   
    // ==================================================================================
    //                          *** Initialization ***
    function onInit() {
        trigger t = CreateTrigger();
       
        TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ATTACKED);
        DDTriggerRegisterAnyUnitDamaged(t);
        TriggerAddCondition(t, function() -> boolean {
            unit        u;
            unit        v;
            integer     lvl;
            real        dmg = 0.;
            bash        b;
           
            // *** Attac
            if (GetTriggerEventId() == EVENT_PLAYER_UNIT_ATTACKED) {
                u = GetAttacker();
                v = GetTriggerUnit();
                b = DDGet(v, UNIT_BASH_KEY);
               
                if (b != p_null)
                    if (IsUnitType(u, UNIT_TYPE_MELEE_ATTACKER) && b.IsAirborn)
                        IssueImmediateOrder(u, "stop");
               
                u = null;
                v = null;
                return false;
            }
           
            u       = GetEventDamageSource();                   // Damages source unit
            v       = GetTriggerUnit();                         // Victim unit
            lvl     = GetUnitAbilityLevel(u, MAX_BASH_ID);      // Spell level
           
           
            // --------------------------------------
            // *** Condition
            if (GetUnitAbilityLevel(v, MAX_BASH_BUFF_ID) == 0 || DDGet(v, UNIT_BASH_KEY) != p_null) {
                UnitRemoveAbility(v, MAX_BASH_BUFF_ID);
                u = null;
                v = null;
                return false;
            }
           
            // *** No future exec, prevent exec this trigger when we call "UnitDamageTarget" below ***
            DisableTrigger(GetTriggeringTrigger());
           
            // *** Clear the buff ***
            UnitRemoveAbility(v, MAX_BASH_BUFF_ID);
           
            // *** Primary damage ***
            static if (BASH_DAMAGE_AS_PERCENTAGE)
                dmg = GetEventDamage()*BashDamage(lvl);
            else
                dmg = BashDamage(lvl);
           
            BlzSetEventDamage(GetEventDamage() + dmg);
            //UnitDamageTarget(u, v, dmg, true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);

            // *** Apply custom effect ***
            DestroyEffect(AddSpecialEffectTarget(ON_PUSH_FILE, u, ATTACH_POINT));

            // *** Floating text add? ***
            static if (ALLOW_FLOATING_TEXT)
                static if (USE_KO_TEXT)
                    if (IsUnitType(v, UNIT_TYPE_DEAD))
                        DDNewTextTagUnit(v, "|cffff0000KO!!!|r", FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
                    else
                        DDNewTextTagUnit(v, "+"+I2S(R2I(dmg)), FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
                else
                    DDNewTextTagUnit(v, "+"+I2S(R2I(dmg)), FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
                   
           
            // *** Load main bash struct ***
            BashExecute(GetPlayerId(GetOwningPlayer(u)), u, v, lvl, BashPushDistance(lvl), Atan2(GetUnitY(v)-GetUnitY(u), GetUnitX(v)-GetUnitX(u)), dmg);

            // *** Allow next exec ***
            EnableTrigger(GetTriggeringTrigger());
       
            // *** Null locals ***
            u = null;
            v = null;
            return false;
        });
    }
}
//! endzinc


// ============================================================================================
// Change me from /* to // and then back to /* after saving and reopening the map
    // |
    // ˇ
      /*
     
// Credits: PurgeandFire for lua tutorial
//! externalblock extension=lua ObjectMerger $FILENAME$
    //! i -- =================================================================
   
    //! i MAX_BASH_ABIL_ID                  = "MBsh"
    //! i MAX_BASH_BUFF_ID                  = "MBed"
   
    //! i -- =================================================================

     //! i setobjecttype("buffs")
     //! i createobject("BSTN", MAX_BASH_BUFF_ID)
     //! i makechange(current, "fart", "ReplaceableTextures\\CommandButtons\\PASBash.blp")
     //! i makechange(current, "ftat", "")
     //! i makechange(current, "fta0", "")
     //! i makechange(current, "fnam", "Max Bash")
     //! i makechange(current, "ftip", "Max Bash")
   
     //! i setobjecttype("abilities")
     //! i createobject("AHbh", MAX_BASH_ABIL_ID)
     //! i makechange(current,"anam", "Max Bash")
     //! i makechange(current,"aart", "ReplaceableTextures\\CommandButtons\\PASBash.blp")
     //! i makechange(current,"arar", "ReplaceableTextures\\CommandButtons\\PASBash.blp")
     //! i makechange(current,"abpx", "3")
     //! i makechange(current,"arpx", "3")
     //! i -- makechange(current,"alev", "3")
     //! i -- makechange(current,"arlv", "1")
     //! i -- makechange(current,"ahky", "H")
     //! i makechange(current,"arhk", "X")
     //! i makechange(current,"aret", "Learn Ma|cff109da9x|r Bash - [|cffffcc00Level ".. string.char(37) .."d|r]")
     //! i makechange(current,"arut", "|cffc0c0c0Gives a percent chance to deal extra damage and push target. Sliding unit deals splash bash that slightly pushes units and deals 30% damage from original bash. |r|n|n|cffffcc00Level 1|r - 15% chance, 2.6x damage, 200 distance push. |n|cffffcc00Level 2|r - 17% chance, 3.4x damage, 230 distance push. |n|cffffcc00Level 3|r - 20% chance, 4.5x damage, 270 distance push.")
     //! i makechange(current,"aub1","1","|cffc0c0c0Gives a 15% chance to deal extra 2.6x damage, push target 200 offset distance and deal 30% splash bash.|r")
     //! i makechange(current,"aub1","2","|cffc0c0c0Gives a 17% chance to deal extra 3.4x damage, push target 230 offset distance and deal 30% splash bash.|r")
     //! i makechange(current,"aub1","3","|cffc0c0c0Gives a 20% chance to deal extra 4.5x damage, push target 270 offset distance and deal 30% splash bash.|r")
     
     //! i local i = 0
     //! i for i=1, 3 do
        //! i local si = tostring(i)
        //! i makechange(current,"atar",si,"Enemy,Ground,Organic")
        //! i -- makechange(current,"aran",si,"99999.")
        //! i makechange(current,"Hbh1",si,tostring(13+(2*i)))
        //! i makechange(current,"abuf",si,"MBed")
        //! i makechange(current,"Hbh5",si,"1")
        //! i makechange(current,"Hbh3",si,"0.")
        //! i makechange(current,"atp1",si,"Max Bash - [|cffffcc00Level ".. si .."|r]")
     //! i end
     //! i makechange(current,"Hbh1","3","20.")
//! endexternalblock

       */

    // ^
    // |
// Change me from */ to // and then back to */ after saving and reopening the map
// ============================================================================================


 
//TESH.scrollpos=20
//TESH.alwaysfold=0
// *******************************************************************************
//                          Spell: Max Bash by Dark Dragon
//
//                 Credits: IceFrog    -> Used in Dota! Idea is by someone...
//                          Venom      -> Icon
//                          nerovesper -> Request
//
//
//                              Installation:
//
//  1. Make sure you have opend JNGPS!
//  2. Copy and paste following:
//                              - icons from import manager if u want to use them
//                              - ability max bash and while pasting set its raw code to MBsh
//                              - this trigger
// 3. Now as you can see this constants below are the values you need to edit to your own will
//    then even more below are level data setup values. change them to your will as well
//
// * You dont need to give me credits but it would be nice that other people get credits
// * If you need extra help or found a bug in this spell feel free to comment
//
// * Enjoy!
// *******************************************************************************

// *** Include ddup.j ***
//! import "ddup.j"


// *** This is the max bash spell library ***
library MaxBash initializer Init requires DDUniversalPack

    globals
        // This is the raw code of max bash ability
        // Default: 'MBsh'
        private constant integer        MAX_BASH                = 'MBsh'
       
        // Push model file
        // Default: Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl
        private constant string         PUSH_FILE               = "Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl"
       
        // On push file (when ability is triggerd) applay this effect
        // Default: Abilities\\Spells\\NightElf\\ThornsAura\\ThornsAuraDamage.mdl
        private constant string         ON_PUSH_FILE            = "Abilities\\Spells\\NightElf\\ThornsAura\\ThornsAuraDamage.mdl"
       
        // On which point of attacker "ON_PUSH_FILE" is added?
        // Default: weapon
        private constant string         ATTACH_POINT            = "weapon"
       
        // This is speed in game coordinates, of how fast unit is pushed away
        // defined star and end speeds of pushed unit
        // Default: 300 / 150
        private constant real           PUSH_SPEED_START       = 300.
        private constant real           PUSH_SPEED_END         = 150.
       
        // This is deacceleration of unit. This value must never be negative
        // Default: 250
        private constant real           PUSH_DEACC             = 250.
       
        // Should trees be destroyed?
        // Default: true
        private constant boolean        DESTROY_TREES          = true
       
        // Should floating text be created?
        // Default: true
        private constant boolean        ALLOW_FLOATING_TEXT    = true
       
        // The duration of floating text in seconds
        // Default: 4.25
        private constant real           FLOATING_TEXT_DURATION = 4.25
       
        // *** Do not edit this vars here, edit them below ***
        // {
            private integer             Levels = 0
            private dd_color            Clr = Null
            private integer array       BashChance[8191]
            private real    array       BashDamage[8191]
            private boolean array       BashAsPerc[8191]
            private real    array       BashPRange[8191]
        // }
    endglobals
   
    // -------------------------------------------------------------
    //          *** Level data setup here ***
    private function SetupMaxBash takes nothing returns nothing
        // *** Max level of the spell ***
        // Default: 3
        set Levels = 3
       
        // *** The bash chance per level ***
        // Default: 15 / 17 / 20
        set BashChance[1] = 15
        set BashChance[2] = 17
        set BashChance[3] = 20
       
        // *** Defines should dealed damage be a percentage of
        // dealed damage or should it be extra damage ***
        // Default: true / true / true
        set BashAsPerc[1] = true
        set BashAsPerc[2] = true
        set BashAsPerc[3] = true
       
        // *** The bash damage per level ***
        // Default: 3, 4.5, 6
        set BashDamage[1] = 3.0
        set BashDamage[2] = 4.5
        set BashDamage[3] = 6.0
       
        // *** This is the push range of unit ***
        // Default: 200 / 250 / 310
        set BashPRange[1] = 200.
        set BashPRange[2] = 250.
        set BashPRange[3] = 310.
       
        // *** Color of floating text ***
        // *** This only works if floating text is allowed ***
        // *** Arguments: Red, Green, Blue, Alpha
        // Default: 255, 255, 255, 225
        set Clr = CreateColor(255, 255, 255, 225)
    endfunction
   
    // ======================================================================
    //             DO NOT EDIT BELOW IF YOU DONT KNOW JASS
    // ======================================================================
   
    // *** Keywords ***
    private keyword MaxBashPeriod
   
    // *** Main global vars ***
    globals
        // *** Constants ***
        private constant real       TREES_PICK_RANGE    = 192.
        private constant real       DELAY               = THREAD_DELAY*.001
    endglobals
   
    // ----------------------
    // --- Custom types ---
    // ----------------------
   
    // *** Main Bash Type ***
    private struct bash
        // *** Members ***
        private unit u          // victim
        private float dist      // push distance
        private float vel       // current velocity
        private real acc        // acceleration
        private dd_vector v     // direction vector
        private integer exec    // current execution
       
        // *** Pointer ***
        implement optional dd_pointer
       
        // *** Constructor ***
        //! runtextmacro constr("create", "unit u, real rad, integer lvl")
            // *** Assign members ***
            set this.u = u
            set this.dist = Float(lvl:BashPRange)
            set this.vel = Float(PUSH_SPEED_START)
            set this.acc = PUSH_DEACC*rsign(PUSH_SPEED_END-PUSH_SPEED_START)*DELAY
            set this.v = CreateVectorDD(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u), rad, 0.)
           
            // *** Pointer apply ***
            call this.SetPtrObj(u)
           
            // *** Apply member actions ***
            call FloatLimits(this.vel, rmin(PUSH_SPEED_START, PUSH_SPEED_END), rmax(PUSH_SPEED_START, PUSH_SPEED_END), true)
            call FloatLimits(this.dist, 0., GetFloat(this.dist), true)
        //! runtextmacro endconstr()
       
       
        // *** Destructor ***
        //! runtextmacro destr()
            call DestroyFloat(this.vel)
            call DestroyFloat(this.dist)
            call DestroyVectorDD(this.v)
            call bash.ClrPtrObj(this.u)
            set this.u = null
            set this.exec = 0
        //! runtextmacro enddestr()
       
        // ---------------------------------------------------------------------------
       
        // *** If spell is triggered at same time then (reset) ***
        public method Reset takes real rad, integer lvl returns nothing
            // *** Reset bash data ***
            call SetFloat(this.dist, lvl:BashPRange)
            call SetFloat(this.vel, PUSH_SPEED_START)
            call SetVDirection(this.v, rad, 0.)
        endmethod
       
        // *** Knockback main method ***
        // *** Returns true if its done movement ***
        public method Push takes nothing returns boolean
            // *** Acceleration apply & vector offset move & distance decrease ***
            call this.vel.Add(this.acc)
            call this.dist.Sub(GetFloat(this.vel)*DELAY)
            call this.v.OffsetMove(GetFloat(this.vel)*DELAY)
           
            // *** Next exec ***
            set this.exec = this.exec + 1
           
            // *** Move main unit ***
            call SetUnitVPos(this.u, this.v)
           
            // *** All distance passed? ***
            return (GetFloat(this.dist) == 0.)
        endmethod
       
        // ========================================================
        //                  *** Operators ***
        public method operator victim takes nothing returns unit
            return (this.u)
        endmethod
       
        public method operator vector takes nothing returns dd_vector
            return (this.v)
        endmethod
       
        public method operator execution_count takes nothing returns integer
            return (this.exec)
        endmethod
        //                *** End Operators ***
        // ========================================================
       
    endstruct
   
   
    // *** Clears nerby trees ***
    private function EnumDests takes nothing returns nothing
        if (GetWidgetLife(GetEnumDestructable()) > .405) then
            if (IsDestructableTree(GetEnumDestructable())) then
                call KillDestructable(GetEnumDestructable())
            endif
        endif
    endfunction
   
    private function TryClearTrees takes dd_vector v, integer count returns nothing
        // *** If trees can be destroyed ***
        if (DESTROY_TREES and QMod(count, 4) == 0) then
            // *** Clear all trees ***
            // === NOTE: My functions do not leak (null) boolexper's ===
            call EnumDestructablesInRange(v.x, v.y, TREES_PICK_RANGE, null, function EnumDests)
        endif
    endfunction
   
    // *** Loads or resets bash ***
    private function BashExecute takes unit u, real ang, integer lvl returns nothing
        // *** Get ptr ***
        local bash pb = bash.GetPtrObj(u)
       
        // *** Is valid? ***
        if (pb == Null) then
            // *** Load new bash ***
            call NewThread(THREAD_DELAY, bash.create(u, ang, lvl), callback.MaxBashPeriod, true)
        else
            // *** Reset if in second cast ***
            call pb.Reset(ang, lvl)
        endif
    endfunction
   
    // -------------------------------------------------------------------
    //              *** Main loop function ***
    private function MaxBashPeriod takes bash b returns boolean
        // *** Push ***
        if (b.Push()) then
            // *** Done? ***
            call PauseUnit(b.victim, false)
            call b.delete()
            return (true)
        endif
       
        // *** Clear trees if wanted ***
        call TryClearTrees(b.vector, b.execution_count)
       
        // *** Push effect ***
        call DestroyEffect(AddSpecialEffect(PUSH_FILE, b.vector.x, b.vector.y))
       
        // *** Spell not done jet ***
        return (false)
    endfunction
   
    // ===================================================================
    // ***          Main spell function (condition)          ***
    private function Conditions takes nothing returns boolean
        return ( GetRandomChance(GetUnitAbilityLevel(GetEventDamageSource(), MAX_BASH):BashChance) and IsDamageTypeMelee() and not IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) )
    endfunction

    // ===================================================================
    // ***          Main spell function (action)          ***
    private function MaxBash takes nothing returns nothing
        local unit      u       = GetEventDamageSource()            // Damages source unit
        local unit      v       = GetTriggerUnit()                  // Victim unit
        local integer   lvl     = GetUnitAbilityLevel(u, MAX_BASH)  // Spell level
        local real      dmg     = 0.                                // Spells extra damage
       
        // *** No future exec ***
        call DisableTrigger(GetTriggeringTrigger())
       
        // *** Deal extra damage ***
        if (lvl:BashAsPerc) then
            set dmg = GetEventDamage()*lvl:BashDamage
        else
            set dmg = lvl:BashDamage
        endif
        call UnitDamageTarget(u, v, dmg, true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
       

        // *** Apply custom effect ***
        call DestroyEffect(AddSpecialEffectTarget(ON_PUSH_FILE, u, ATTACH_POINT))

        // *** Floating text add? ***
        if (ALLOW_FLOATING_TEXT) then
            call CreateTextTagUnit(v, "+"+I2S(round(dmg)), 10., FLOATING_TEXT_DURATION, Clr)
        endif

        // *** Now pause the victim ***
        call PauseUnit(v, true)

        // *** Load main bash struct ***
        call BashExecute(v, E_AngWidget(u, v)*DEGTORAD, lvl)

        // *** Allow next exec ***
        call EnableTrigger(GetTriggeringTrigger())
       
        // *** Null locals ***
        set u = null
        set v = null
    endfunction
   

    // *** Main init function ***
    private function Init takes nothing returns nothing
        // ---> Register event
        // ---> Add cond/act
        call TriggerRegisterAnyUnitDamaged(onTriggerEvent(null, function Conditions, function MaxBash))
       
        // *** Now call user setup ***
        call SetupMaxBash()
       
        // Preload!
        call DestroyEffect(AddSpecialEffect(PUSH_FILE, 0., 0.))
        call DestroyEffect(AddSpecialEffect(ON_PUSH_FILE, 0., 0.))
    endfunction

endlibrary