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

[Spell] need help with Broken knockback :(

Status
Not open for further replies.
Level 11
Joined
Jul 17, 2013
Messages
544
Hey! its first time i copied jass trigger to my map. i didnt brother with jass triggers before because they seemed too hard for me.i am editing in gui. this is the knockback i used Spell - Max Bash 2.1c

i coppied all jass triggers to my map + max bash abbility, and buff + dummy unit + chest item. after i openned map i made sure for dummy and max bash ability id to be same like in my map. and thats all


i tested spell in game it worked perfectly. the problem is always after EXCATLY 20 minuts of game triggers from my map starts randomly running while they arent supossed to. and some of ai units also walks very strange just like they would be knocked back.i am sure knockback is reason of this :( this system is cool but sometimes it damages my gui triggers.or maybe i didnt install it correctly? insturction said about importing dummy model but i couldnot see it in that map with max bash spell

btw i noticed that there are lines like
// Change me from */ to // and then back to */ after saving and reopening the map
// i gotta admit i didnt do that because i didnt notice it maybe it caused this problem?


ALSO I REALISED SOMETHING IMPORTANT! it enables many triggers which have event a units enters region.but i checked replay and no unit entered regions and still trigger enabled.

there are jass triggers of this spell from my map!

JASS:
//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                    = "death";
   
    // -------------------------------------------------------   
    // 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                  = 200.;
    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                = false;
   
    // -------------------------------------------------------
    // 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            = 100.;
   
    // -------------------------------------------------------
    // 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                      = false;
   
    // -------------------------------------------------------
    // 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                = false;
   
    // -------------------------------------------------------   
    // 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] = 0.0;
        bash_damage[2] = 0.0;
        bash_damage[3] = 0.0;
       
        return bash_damage[level];
    }
   
    // -------------------------------------------
    // *** The bash push distance per level ***
    // Default: 300. / 300. / 300.
    function BashPushDistance(integer level) -> real {
        real bash_range[];
       
        bash_range[1] = 230.;
        bash_range[2] = 350.;
        bash_range[3] = 200.;
       
        return bash_range[level];
    }
   
    // -------------------------------------------
    // *** The bash push height per level ***
    // Default: 150. / 150. / 150.
    function BashPushHeight(integer level) -> real {
        real bash_height[];
       
        bash_height[1] = 150.;
        bash_height[2] = 150.;
        bash_height[3] = 150.;
       
        return bash_height[level];
    }
   
    // -------------------------------------------
    // *** The bash push height travel percentage per level ***
    // This setting says at which percentage of path will unit land
    // Default: 40. / 40. / 40.
    function BashPushHeightTravelPerc(integer level) -> real {
        real bash_height_travel_perc[];
       
        bash_height_travel_perc[1] = 40.;
        bash_height_travel_perc[2] = 40.;
        bash_height_travel_perc[3] = 40.;
       
        return bash_height_travel_perc[level];
    }
   
    // -------------------------------------------
    // *** The damaged dealt by impact
    // Default: 50. / 50. / 50.
    function BashPushGroundImpactDamage(integer level) -> real {
        real bash_ground_impact_damage[];
       
        bash_ground_impact_damage[1] = 120.;
        bash_ground_impact_damage[2] = 300.;
        bash_ground_impact_damage[3] = 120.;
       
        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
// ============================================================================================





JASS:
//==========================================================================
//                  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
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,553
I really think you should just use Bribe's Damage Detection System and GUI Knockback 2D. That map I uploaded for you in your previous knockback thread has the Knockback stuff in it. It's just one folder that you have to import and nothing else. Bribe's stuff is kept up to date so you don't have to worry about weird issues like you'll encounter in these old resources. This particular one is 10 years old!

Edit: Whoops, it was updated recently. I thought it said last updated 2009, lol. Regardless, it's really too much work to learn how this guys resource works and read that entire wall of text. I know Bribe's stuff works as I use it myself so that's why I recommend it.

If you want I can upload a map with a working Greater Bash skill using Bribe's DDS and Knockback system. I assume this Greater Bash ability is like Spirit Breakers in dota? As in it has a % chance to trigger on attack and deals bonus damage, stuns, and does a knockback effect.
 
Last edited:
Level 11
Joined
Jul 17, 2013
Messages
544
I really think you should just use Bribe's Damage Detection System and GUI Knockback 2D. That map I uploaded for you in your previous knockback thread has the Knockback stuff in it. It's just one folder that you have to import and nothing else. Bribe's stuff is kept up to date so you don't have to worry about weird issues like you'll encounter in these old resources. This particular one is 10 years old!

Edit: Whoops, it was updated recently. I thought it said last updated 2009, lol. Regardless, it's really too much work to learn how this guys resource works and read that entire wall of text. I know Bribe's stuff works as I use it myself so that's why I recommend it.
i checked this map i dont like a bit how this unit isnt pulled into air, and unit always knockbacks every time he attacks but i guess i can make passive ability and chance to hit? for this knockback
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,553
I deleted my previous comment. I think you'll be happy with this as it has everything you wanted.

How to import:
Import the folder called "Copy This". That folder contains the Damage Detection System, Knockback System, Knockback Example folder, and the Greater Bash folder. You will also have to import the Greater Bash Dummy and the Greater Bash Stun ability from the Object Editor.

Also, you can delete the Knockback Example folder as it's only there to give you an idea of how to use the system.

Greater Bash explained:
The Greater Bash ability uses the Damage Detection System in combination with the Knockback System to detect when your Hero deals melee damage to a unit. It then rolls a random number between 1 and 100 to determine whether or not you got a Bash. If you get a Bash then we issue a Knockback and create a Dummy unit that Stuns our knockback target.

EDIT 2: Uploaded a new version (v.5). Fixed a bug. Hopefully this will be the last bug I have to fix, lol.
 

Attachments

  • Knockback Demo v.5.w3x
    77.6 KB · Views: 38
Last edited:
Level 11
Joined
Jul 17, 2013
Messages
544
I deleted my previous comment. I think you'll be happy with this as it has everything you wanted.

How to import:
Import the folder called "Copy This". That folder contains the Damage Detection System, Knockback System, Knockback Example folder, and the Greater Bash folder. You will also have to import the Greater Bash Dummy and the Greater Bash Stun ability from the Object Editor.

Also, you can delete the Knockback Example folder as it's only there to give you an idea of how to use the system.

Greater Bash explained:
The Greater Bash ability uses the Damage Detection System in combination with the Knockback System to detect when your Hero deals melee damage to a unit. It then rolls a random number between 1 and 100 to determine whether or not you got a Bash. If you get a Bash then we issue a Knockback and create a Dummy unit that Stuns our knockback target.

EDIT 2: Uploaded a new version (v.5). Fixed a bug. Hopefully this will be the last bug I have to fix, lol.
I checked this map unit are knockbacked without going into air :( i like in bribe's map knockback only when you use war stomp spell. can you tell me what objects from birbes map i need to copy if i only want to have knockback on war stomp?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,553
Are you sure you tried Knockback Demo v.5? I added in everything you wanted. I added in the Greater Bash ability and made it knock enemies into the air like you wanted. You can choose whether or not units will be launched into the air by changing the Knockback2DMaxHeight variable. This variable determines how high into the unit will fly into the air. Setting this variable to 0 or simply not using it all will stop the "launched into air" effect.

Also, keep in mind that the Greater Bash ability has a 25% chance to trigger so it won't happen every hit. You can edit the trigger yourself to change this. There's a variable called "GreaterBashChance" that is set to 25, simply change it to whatever % you want.

Hopefully that will clear everything up. Make sure to use v.5!
Here is the map again:
 

Attachments

  • Knockback Demo v.5.w3x
    77.6 KB · Views: 34
Last edited:
Level 11
Joined
Jul 17, 2013
Messages
544
Are you sure you tried Knockback Demo v.5? I added in everything you wanted. I added in the Greater Bash ability and made it knock enemies into the air like you wanted. You can choose whether or not units will be launched into the air by changing the Knockback2DMaxHeight variable. This variable determines how high into the unit will fly into the air. Setting it to 0 or simply not using it all will make the unit not fly at all.

Also, keep in mind the Greater Bash ability has a 25% chance to trigger so it won't happen every hit. You can edit the trigger yourself to change this. There's a variable called "GreaterBashChance" that is set to 25, simply change it to whatever % you want.

Hopefully that will clear everything up. Make sure to use v.5!
Here it is again:
Oh well i checked it eralier and i didnt see enemies being able to go into air i will check it now also is it possible to play death animation meanwhile going air?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,553
Remember, the Greater Bash ability will knock enemies into the air but the "Knockback Spell" will NOT. I intentionally did this to show you that you can choose whether or not to have this effect. Just about everything can be modified so you can make the knockback work the way you want it to.

And yes, you could make the unit play its death animation. I personally avoid stuff like that because it looks weird for some units (Lich for example disappears when he dies) but it can be done pretty easily. Just use the "Play unit death animation" action and then after a short duration use the "Reset unit animation" action to set it back to normal.
 
Last edited:
Level 11
Joined
Jul 17, 2013
Messages
544
Remember, the Greater Bash ability will knock enemies into the air but the "Knockback Spell" will NOT. I intentionally did this to show you that you can choose whether or not to have this effect. Just about everything can be modified so you can make the knockback work the way you want it to.

And yes, you could make the unit play its death animation. I personally avoid stuff like that because it looks weird for some units (Lich for example disappears when he dies) but it can be done pretty easily. Just use the "Play unit death animation" action and then after a short duration use the "Reset unit animation" action to set it back to normal.
wow i checked ty very much, cant belive its so simply and no one made knockback like this for publical.well now you need to tell me one more thing xd. what i need to copy from this map if i want to have ONLY GREATER BASH. there are like 10 triggers about knockback. im totaly not interested into bash which pulls unit a bit only WITHOUT unit going into air.
ugh also what about going outside of map for example? is it fixed at this knockback? i noticed it can destroy tress but i belive i can disable it but what if unit is pulled on dooad
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,553
Copy and paste the "Copy This" folder into your map. It contains these folders:
Knockback 2D
Knockback Example
Damage Engine
Greater Bash Example

Then you can delete: Knockback Example
Keep everything else!

Also import the Greater Bash Dummy unit and Greater Bash Stun ability. You can change the duration of Greater Bash Stun in the Object Editor. I set it to 1.50 seconds. It's based on Storm Bolt.

To answer your questions. No, the unit won't be pushed out of the map bounds. Also, doodads cannot be destroyed so the unit will stop when it hits a doodad or it will Bounce off of the doodad (if Bounce is enabled). And with KillTrees enabled it will destroy destructibles like Trees, Crates, Barrels, etc... but it shouldn't destroy anything important like a Bridge.

----------------------------------------

Here are instructions on how to use the Knockback System and what variables you can use to modify each knockback.
  • Knockback Instructions
    • Events
    • Conditions
    • Actions
      • -------- There are four required variables when issuing a knockback --------
      • -------- --------
      • -------- 1. Knockback2DAngle -------- this is the direction angle the unit is knocked back (in degrees) --------
      • -------- 2. Knockback2DTime -------- this is how long the unit will be knocked back (in seconds) --------
      • -------- 3. Knockback2DDistance -------- this is how far the unit will be knocked back --------
      • -------- 4. Knockback2DUnit -------- this is the unit being knocked back --------
      • -------- --------
      • -------- When all four variables are set, you can run the Knockback 2D trigger, ignoring conditions --------
      • -------- --------
      • -------- Copy these actions and paste them into your trigger. --------
      • -------- Then you need to set: CenterPoint, TargetPoint, Time, Distance, and Unit. You can also add in other settings like Set Knockback2DKillTrees = True to make the knockback do special things. --------
      • -------- Make sure that "Trigger - Run Knockback 2D" is the last thing you do. It must be positioned at the bottom like how it is below. --------
      • Set CenterPoint = (Position of (Triggering unit))
      • Set TargetPoint = (Position of (Triggering unit))
      • Set Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
      • Custom script: call RemoveLocation(udg_CenterPoint)
      • Custom script: call RemoveLocation(udg_TargetPoint)
      • Set Knockback2DTime = 0.90
      • Set Knockback2DDistance = 500.00
      • Set Knockback2DUnit = (Triggering unit)
      • Trigger - Run Knockback 2D <gen> (checking conditions)
  • -------- These are some extra settings you may set before issuing a knockback --------
  • -------- If you don't set one of these variables then it will be set to it's default value. Default values can be found in the Knockback 2D Config trigger --------
  • -------- - --------
  • -------- Pause prevents the unit from being able to move --------
  • Set Knockback2DPause = True
  • -------- - --------
  • -------- Bounce makes the unit bounce off of obstacles like terrain and doodads --------
  • Set Knockback2DBounces = True
  • -------- - --------
  • -------- KillTrees makes the unit destroy trees and other destructibles it collides with --------
  • Set Knockback2DKillTrees = True
  • -------- - --------
  • -------- Max Height is how high the unit will launch into the air. If you don't set Max Height it will be set to 0 by default --------
  • Set Knockback2DMaxHeight = 200.00
  • -------- - --------
  • -------- LoopFX is the Special Effect that will play on units. If you don't want a Special Effect to play then delete the text so it says <Empty String> like below --------
  • Set Knockback2DLoopFX = <Empty String>

So as the Instructions explain you can disable/enable whether or not the knockback kills trees and other destructibles (KillTrees), bounces off of terrain/doodads (Bounces), prevents/allows movement during the knockback (Pause), launches units into the air (MaxHeight), and which special effect plays during the knockback (LoopFX).

Make sure to give credit to Bribe if you use this in your map!
 
Last edited:
Level 11
Joined
Jul 17, 2013
Messages
544
Copy and paste the "Copy This" folder into your map. It contains these folders:
Knockback 2D
Knockback Example
Damage Engine
Greater Bash Example

Then you can delete: Knockback Example
Keep everything else!

Also import the Greater Bash Dummy unit and Greater Bash Stun ability. You can change the duration of Greater Bash Stun in the Object Editor. I set it to 1.50 seconds. It's based on Storm Bolt.

To answer your questions. No, the unit won't be pushed out of the map bounds. Also, doodads cannot be destroyed so the unit will stop when it hits a doodad or it will Bounce off of the doodad (if Bounce is enabled). And with KillTrees enabled it will destroy destructibles like Trees, Crates, Barrels, etc... but it shouldn't destroy anything important like a Bridge.

----------------------------------------

Here are instructions on how to use the Knockback System and what variables you can use to modify each knockback.
  • Knockback Instructions
    • Events
    • Conditions
    • Actions
      • -------- There are four required variables when issuing a knockback --------
      • -------- --------
      • -------- 1. Knockback2DAngle -------- this is the direction angle the unit is knocked back (in degrees) --------
      • -------- 2. Knockback2DTime -------- this is how long the unit will be knocked back (in seconds) --------
      • -------- 3. Knockback2DDistance -------- this is how far the unit will be knocked back --------
      • -------- 4. Knockback2DUnit -------- this is the unit being knocked back --------
      • -------- --------
      • -------- When all four variables are set, you can run the Knockback 2D trigger, ignoring conditions --------
      • -------- --------
      • -------- Copy these actions and paste them into your trigger. --------
      • -------- Then you need to set: CenterPoint, TargetPoint, Time, Distance, and Unit. You can also add in other settings like Set Knockback2DKillTrees = True to make the knockback do special things. --------
      • -------- Make sure that "Trigger - Run Knockback 2D" is the last thing you do. It must be positioned at the bottom like how it is below. --------
      • Set CenterPoint = (Position of (Triggering unit))
      • Set TargetPoint = (Position of (Triggering unit))
      • Set Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
      • Custom script: call RemoveLocation(udg_CenterPoint)
      • Custom script: call RemoveLocation(udg_TargetPoint)
      • Set Knockback2DTime = 0.90
      • Set Knockback2DDistance = 500.00
      • Set Knockback2DUnit = (Triggering unit)
      • Trigger - Run Knockback 2D <gen> (checking conditions)
  • -------- These are some extra settings you may set before issuing a knockback --------
  • -------- If you don't set one of these variables then it will be set to it's default value. Default values can be found in the Knockback 2D Config trigger --------
  • -------- - --------
  • -------- Pause prevents the unit from being able to move --------
  • Set Knockback2DPause = True
  • -------- - --------
  • -------- Bounce makes the unit bounce off of obstacles like terrain and doodads --------
  • Set Knockback2DBounces = True
  • -------- - --------
  • -------- KillTrees makes the unit destroy trees and other destructibles it collides with --------
  • Set Knockback2DKillTrees = True
  • -------- - --------
  • -------- Max Height is how high the unit will launch into the air. If you don't set Max Height it will be set to 0 by default --------
  • Set Knockback2DMaxHeight = 200.00
  • -------- - --------
  • -------- LoopFX is the Special Effect that will play on units. If you don't want a Special Effect to play then delete the text so it says <Empty String> like below --------
  • Set Knockback2DLoopFX = <Empty String>

So as the Instructions explain you can disable/enable whether or not the knockback kills trees and other destructibles (KillTrees), bounces off of terrain/doodads (Bounces), prevents/allows movement during the knockback (Pause), launches units into the air (MaxHeight), and which special effect plays during the knockback (LoopFX).

Make sure to give credit to Bribe if you use this in your map!

at greater bash example damageeventsource equals to be paladin 0002. it would bbe bad a bit if it would be possible to make it for only one unit. how can i make it so it counts any unit with greater bash ability. i mean i dont really understant what dagme event source means. am i meant to mention there only 1 unit who can use greater bash lol i would like 20 units to use this.

also do i have to restart untis animation? i just made function animation play anim damage event targets animation. is it fine?

i would like to play thunder clap caster after unit lands i know how to do this but the problem is i dont know how to trigger it so it plays after unit lands i dont want to use wait function cuz i heard its bad.

so i have 3 questions 2 about anims and how to make it so its the unit with greater bash ability :D i hope u can help with that

i also removed all triggers from knockback example i guess u said i dont need them right?
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,553
This is a custom Event. It runs whenever any unit takes damage. Just like how there's an Event that says "A unit is attacked" you can pretend that this Event says "A unit takes damage."
  • Game - DamageEvent becomes Equal to 1.00
So when this Event runs these variables will have been set to the damaging unit and the damaged unit:
DamageEventSource = The unit that dealt the damage. So this would be the bashing unit.
DamageEventTarget = The unit that was damaged. So this would be the unit that gets bashed.

Think of the Greater Bash trigger like a "A unit is attacked" trigger. But instead of "attacking unit" and "attacked unit" you're using "DamageEventSource" and "DamageEventTarget".

Once you understand this the system becomes very easy to use.

This is how easy it is to make Greater Bash work for any unit with the ability. Just replace the condition:
  • DamageEventSource Equal to Paladin 0002 <gen>
With a new condition:
  • (Level of Greater Bash for DamageEventSource) Greater than or equal to 1
So instead of being specific and making it work for 1 unit we check to see if our DamageEventSource (the damaging unit) has the Greater Bash ability.
  • Greater Bash Example
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
      • (Level of Greater Bash for DamageEventSource) Greater than or equal to 1
      • IsDamageMelee Equal to True
    • Actions
      • Set GreaterBashChance = 25
      • Set GreaterBashRoll = (Random integer number between 1 and 100)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • GreaterBashRoll Less than or equal to GreaterBashChance
        • Then - Actions
          • Game - Display to (All players) for 10.00 seconds the text: Greater Bash!
          • Set CenterPoint = (Position of DamageEventSource)
          • Set TargetPoint = (Position of DamageEventTarget)
          • Set Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
          • -------- - --------
          • -------- Here we create our Stun Dummy to apply the 1.5 second stun to the enemy --------
          • Unit - Create 1 Greater Bash Dummy for (Owner of DamageEventSource) at TargetPoint facing Default building facing degrees
          • Unit - Order (Last created unit) to Human Mountain King - Storm Bolt DamageEventTarget
          • Unit - Add a 1.00 second Generic expiration timer to (Last created unit)
          • -------- - --------
          • Custom script: call RemoveLocation(udg_CenterPoint)
          • Custom script: call RemoveLocation(udg_TargetPoint)
          • Set Knockback2DTime = 0.75
          • Set Knockback2DDistance = 300.00
          • Set Knockback2DMaxHeight = 100.00
          • Set Knockback2DPause = True
          • Set Knockback2DBounces = False
          • Set Knockback2DLoopFX = Abilities\Spells\Human\FlakCannons\FlakTarget.mdl
          • Set Knockback2DUnit = DamageEventTarget
          • Trigger - Run Knockback 2D <gen> (checking conditions)
        • Else - Actions
 
Last edited:
Level 11
Joined
Jul 17, 2013
Messages
544
This is a custom Event. It runs whenever any unit takes damage. Just like how there's an Event that says "A unit is attacked" you can pretend that this Event says "A unit takes damage."
  • Game - DamageEvent becomes Equal to 1.00
So when this Event runs these variables will have been set to the damaging unit and the damaged unit:
DamageEventSource = The unit that dealt the damage. So this would be the bashing unit.
DamageEventTarget = The unit that was damaged. So this would be the unit that gets bashed.

Think of the Greater Bash trigger like a "A unit is attacked" trigger. But instead of "attacking unit" and "attacked unit" you're using "DamageEventSource" and "DamageEventTarget".

Once you understand this the system becomes very easy to use.

This is how easy it is to make Greater Bash work for any unit with the ability. Just replace the condition:
  • DamageEventSource Equal to Paladin 0002 <gen>
With a new condition:
  • (Level of Greater Bash for DamageEventSource) Greater than or equal to 1
So instead of being specific and making it work for 1 unit we check to see if our DamageEventSource (the damaging unit) has the Greater Bash ability.
  • Greater Bash Example
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
      • (Level of Greater Bash for DamageEventSource) Greater than or equal to 1
      • IsDamageMelee Equal to True
    • Actions
      • Set GreaterBashChance = 25
      • Set GreaterBashRoll = (Random integer number between 1 and 100)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • GreaterBashRoll Less than or equal to GreaterBashChance
        • Then - Actions
          • Game - Display to (All players) for 10.00 seconds the text: Greater Bash!
          • Set CenterPoint = (Position of DamageEventSource)
          • Set TargetPoint = (Position of DamageEventTarget)
          • Set Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
          • -------- - --------
          • -------- Here we create our Stun Dummy to apply the 1.5 second stun to the enemy --------
          • Unit - Create 1 Greater Bash Dummy for (Owner of DamageEventSource) at TargetPoint facing Default building facing degrees
          • Unit - Order (Last created unit) to Human Mountain King - Storm Bolt DamageEventTarget
          • Unit - Add a 1.00 second Generic expiration timer to (Last created unit)
          • -------- - --------
          • Custom script: call RemoveLocation(udg_CenterPoint)
          • Custom script: call RemoveLocation(udg_TargetPoint)
          • Set Knockback2DTime = 0.75
          • Set Knockback2DDistance = 300.00
          • Set Knockback2DMaxHeight = 100.00
          • Set Knockback2DPause = True
          • Set Knockback2DBounces = False
          • Set Knockback2DLoopFX = Abilities\Spells\Human\FlakCannons\FlakTarget.mdl
          • Set Knockback2DUnit = DamageEventTarget
          • Trigger - Run Knockback 2D <gen> (checking conditions)
        • Else - Actions
pls one last thing :( how can i make this special effect after units land? at which variables i need to put it i also dont want to use wait function for that
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,553
Try this new version out. I added in the Death animation and Thunderclap stuff. I use some custom Events to do this.

KnockbackEvent becomes equal to 1.00 = When a knockback finishes.

DamageEvent becomes equal to 2.00 = When a unit takes 0 damage. I use this to detect when the unit gets hit by the Dummy Stun ability.

So I create the Thunderclap effect when the Knockback finishes. And I play the knockback unit's Death animation when it gets hit by the Dummy unit's Storm Bolt (Greater Bash Stun) ability.

EDIT:
I uploaded v.7. I'm not sure if you're using v.6 already but I found a very minor bug and figured I'd fix it. There was a very rare case that the unit wouldn't play it's death animation.I fixed that in version 7.
 

Attachments

  • Knockback Demo v.7.w3x
    76 KB · Views: 30
Last edited:
Level 11
Joined
Jul 17, 2013
Messages
544
Try this new version out. I added in the Death animation and Thunderclap stuff. I use some custom Events to do this.

KnockbackEvent becomes equal to 1.00 = When a knockback finishes.

DamageEvent becomes equal to 2.00 = When a unit takes 0 damage. I use this to detect when the unit gets hit by the Dummy Stun ability.

So I create the Thunderclap effect when the Knockback finishes. And I play the knockback unit's Death animation when it gets hit by the Dummy unit's Storm Bolt (Greater Bash Stun) ability.

EDIT:
I uploaded v.7. I'm not sure if you're using v.6 already but I found a very minor bug and figured I'd fix it. There was a very rare case that the unit wouldn't play it's death animation.I fixed that in version 7.
ty, i tested map with leak checker and it said this should i worry about it? total leaks effect results improve every time knockback is casted
 
Level 11
Joined
Jul 17, 2013
Messages
544
upload_2019-9-4_19-26-15.png


Edit: i didnt notice effects are cleaned after created so its fine :D anyway ty did u play ever the big fellowship quest? its the map im adding knockback for
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,553
Yeah, there shouldn't be any leaks as far as I can tell. Every location is removed and every special effect is immediately destroyed.

And no, I've never played it. I actually don't play much Warcraft 3 besides the World Editor. But I'm sure I will get back into playing custom games once Reforged comes out.
 
Level 11
Joined
Jul 17, 2013
Messages
544
Yeah, there shouldn't be any leaks as far as I can tell. Every location is removed and every special effect is immediately destroyed.

And no, I've never played it. I actually don't play much Warcraft 3 besides the World Editor. But I'm sure I will get back into playing custom games once Reforged comes out.
ugh i was sure i wouldnt have to ask you again but there is small problem at my map. everything works fine but often unit doesnt get pulled away! he only gets into air and lands at the same place he was before hitting


  • Greater Bash
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
      • (Level of Greater Bash for DamageEventSource) Greater than or equal to 1
      • IsDamageMelee Equal to True
    • Actions
      • Set GreaterBashChance = 100
      • Set GreaterBashRoll = (Random integer number between 1 and 100)
      • -------- - --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • GreaterBashRoll Less than or equal to GreaterBashChance
        • Then - Actions
          • -------- Add the knocked back unit to our Greater Bash Unit Group to keep track of it --------
          • Unit Group - Add DamageEventTarget to GreaterBashUnitGroup
          • -------- - --------
          • Set CenterPoint = (Position of DamageEventSource)
          • Set TargetPoint = (Position of DamageEventTarget)
          • Set Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
          • -------- - --------
          • -------- Here we create our Stun Dummy to apply the 1.5 second stun to the enemy --------
          • Unit - Create 1 Greater Bash Dummy for (Owner of DamageEventSource) at TargetPoint facing Default building facing degrees
          • Unit - Order (Last created unit) to Human Mountain King - Storm Bolt DamageEventTarget
          • Unit - Add a 1.00 second Generic expiration timer to (Last created unit)
          • -------- - --------
          • Custom script: call RemoveLocation(udg_CenterPoint)
          • Custom script: call RemoveLocation(udg_TargetPoint)
          • Set Knockback2DTime = 2.50
          • Set Knockback2DDistance = 480.00
          • Set Knockback2DMaxHeight = 270.00
          • Set Knockback2DPause = True
          • Set Knockback2DBounces = False
          • Set Knockback2DLoopFX = Abilities\Spells\Human\FlakCannons\FlakTarget.mdl
          • Set Knockback2DUnit = DamageEventTarget
          • Trigger - Run Knockback 2D <gen> (checking conditions)


in game its just like Set Knockback2DDistance = 480.00 wouldnt matter. maybe i put too high values or i need to change values at other trigger?

even with values from ur map 300 and 100 distance is broken sometimes. edit i also realised units with 15 pathning are working well and pulled away with distance but on the units with 20 pathning distance doesnt work. also my robus pathning is set to 2. its defeault i didnt chang eit


i also found second bug knockback doesnt really work for units who are using metamorphosis :( for unit that used metamorphosis it kept playing ainimation of dead + didnt knock him at all. and also for second unit who used switch form it knocked him away without pulling into air
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,553
I tested my map and everything works fine. 20 Collision Size, 15, it didn't make a difference. Make sure that the Stun duration of Greater Bash Stun lasts the same duration as the Knockback2DTime. Also, units won't get pushed back if they hit a Doodad/Cliff/Map Bounds.

I don't know about the Metamorphosis bug but that's why I recommend not using "Play Death animation". It's going to produce weird results depending on the Model of the unit. Also, if a unit didn't fly into the air then it probably needs to have Storm Crow Form added/removed from it. [General] - How to make unit fly with trigger? That's what the "GUI AutoFly" trigger does, however, it might lose this effect when a unit uses Metamorphosis.
 
Level 11
Joined
Jul 17, 2013
Messages
544
I tested my map and everything works fine. 20 Collision Size, 15, it didn't make a difference. Make sure that the Stun duration of Greater Bash Stun lasts the same duration as the Knockback2DTime. Also, units won't get pushed back if they hit a Doodad/Cliff/Map Bounds.

I don't know about the Metamorphosis bug but that's why I recommend not using "Play Death animation". It's going to produce weird results depending on the Model of the unit. Also, if a unit didn't fly into the air then it probably needs to have Storm Crow Form added/removed from it. [General] - How to make unit fly with trigger? That's what the "GUI AutoFly" trigger does, however, it might lose this effect when a unit uses Metamorphosis.
Ok i am sleeping now, well Thats the thing i didnt make sure about. If i will have still those issues can i just send u map? Then you can realise whats wrong
 
Level 11
Joined
Jul 17, 2013
Messages
544
well i made sure that the dummy ability has same duration of stun as knockback2dtime but still i have that problem let me show u what i mean unit is knocked into air but lands at same place!
upload_2019-9-6_19-33-45.png
upload_2019-9-6_19-34-15.png


upload_2019-9-6_19-36-32.png
upload_2019-9-6_19-37-5.png
 
Last edited:
Level 11
Joined
Jul 17, 2013
Messages
544
as you can see for first unit it doesnt work! but for second it does. i hope you know lord of the rings so its easier to explain things. knockback durations works only for hobbits frodo sam merry pipin but for aragorn boromir gandalf gimli and legolas it doesnt work they arent pushed away. i will send u map details you can find in private message soon.
 
Status
Not open for further replies.
Top