• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Spell - Max Bash 2.1d

Well just another (DotA) spell made by me!
This time i was requested to make it.


(Passive ability)

Gives a percent chance to deal extra damage, send target to air and push target afterwards. Upon landing units take extra damage. Sliding unit deals splash bash that slightly pushes units and deals 30% damage from original bash.

Level 1 - 15% chance, 2.6x damage, 30 landing damage, 200 distance push.
Level 2 - 17% chance, 3.4x damage, 40 landing damage, 230 distance push.
Level 3 - 20% chance, 4.5x damage, 55 landing damage, 270 distance push.



Cant push a building!





IceFrog: Used it in dota
Venom: Icon (seems that DIS one does not work)
nerovesper: Reason why spell is here (request)

Hive for hosting this spell
Blizzard for making game




Version 2.0 - Max Bash Remasterd
- Totally recoded in zinc
- Spell now has option for splash bash
- Spell does not trigger when spell damage is dealt
- Now has bounce effect, so that units do not get stuck in unpathable areas



Version 2.1 - Spell Update
- Added option for height launching
- Spell can be adjusted to have just land slide, land slide with parabolic motion or just height launch

(This update was requested by
hemmedo)


Version 2.1b - Spell Update
- Melee units now can't attack airborn units


Version 2.1c - Spell Update
- Uses new DD Library and compatible with Warcraft 3 v1.30



Version 2.1d - Spell Update
- Fixed desync sound bug



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

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

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

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


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

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

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

If you have any future questions, suggestions or found any bug feel free to comment!

Enjoy!
~Dark Dragon


Keywords:
passive, bash, dota, knockback
Previews
Contents

Spell - Max Bash 2.1d (Map)

Reviews
19:03, 3rd Jun 2009 hvo-busterkomo: A decently developed knockback spell, with an effect on the acceptable level. Here's my complaints: 1. You shouldn't be using the readonly syntax. I don't see much reason to use it, as most people probably aren't...

Moderator

M

Moderator

19:03, 3rd Jun 2009
hvo-busterkomo:
A decently developed knockback spell, with an effect on the acceptable level. Here's my complaints:
1. You shouldn't be using the readonly syntax. I don't see much reason to use it, as most people probably aren't very familiar with it.
2. You should place your trigger in the globals block. It is currently not private. On that note, you shouldn't be using the default name, either.
3. You should be using functions instead of arrays for configuration (more of a personal preference).
4. Personally, I find the entire way your system is setup unnecessarily overcomplicated, with custom functions only calling a single function themselves. I suggest taking a look at Silvenon's knockback system to see how you could make easy improvements.

Tha quality is acceptable, but I'd like you to keep improving this.
 
Level 5
Joined
Mar 5, 2009
Messages
138
I dont really see why this needs to be in vJASS, but I think it would be much more simpler to just use a GUI/Trigger knockback system and use a critical strike passive.
 
Level 17
Joined
Mar 17, 2009
Messages
1,350
Elsheen said:
I dont really see why this needs to be in vJASS, but I think it would be much more simpler to just use a GUI/Trigger knockback system and use a critical strike passive.
It's all about the efficiency :p + user-friendliness + easier to work with if you know Jass


@ Dark_Dragon:
Nice use of trigonometry :p However, I've got a question:
I've seen you used four BJ's in your script, couldn't those be avoidable? I mean I don't know about RMaxBJ and RMinBJ, but I've notice that SetTextTagVelocityBJ has a native SetTextTagVelocity...

Works as it should and the knockback is nice and smooth.

Good job DD!
 
Level 14
Joined
Nov 18, 2007
Messages
816
I could have done this with 8 libraries in less than 75 lines. Interested?
You do know the point of libraries is to actually USE them, dont you?

Edit: I suppose using a Critical Strike based ability may acutally reduce the number of used libraries by 5 (while still providing nearly the exact same behaviour).
 
Since this is from DotA, I am guessing that this is Barathrum's "Greater Bash" Skill right?
Anyways, Barathrum's greater bash is just a passive knockback with a greater distance per level and more damage. It's actually easy to make this in GUI. The only hard part about it is the distance change per level.
Just read this link: http://forums.dota-allstars.com/index.php?showtopic=220166

That's how I made mine but it's in GUI and doesn't have the distance change. (It can be solved but I'm just too lazy to do it)

Try making Leviathan's Ravage Spell in vJass. I already made one in GUI and submitted it. (But no one downloaded it :cry:)
 
Elsheen said:
Your warm reaction makes me feel all fuzzy inside =) Btw, I didnt say how I feel about it, I think its a nice spell ;)

thanks thats why i made it ;)

Deuterium said:
I've seen you used four BJ's in your script, couldn't those be avoidable? I mean I don't know about RMaxBJ and RMinBJ, but I've notice that SetTextTagVelocityBJ has a native SetTextTagVelocity...

ohh yeah xD RMin and RMax are actually short functions which just return which of this numbers are bigger or smaller. so the reason why i used them is coz i dont know what will user setup in script above (example:) in current script START_SPEED > END_SPEED so it is something like defaults and i dont need that RMinBJ. however if user makes END_SPEED > START_SPEED (well duno xD maybe someone will) and if he does i dont want my spell to bug ;) so thats why i used that if then else with Min, Max BJ-s

ofc i used them coz i know how coded they are, and they do not leak. well they will waste few nanoseconds like any other function or method call ;)

About texttag well i just wanted to simple use that bj_lastCretedTextTag. and ofc none of this functions are single lined (thats why i used them).

note as well that all of this BJ-s Inside are supposed to be inlined by JH

JASS:
function CreateTextTagUnitBJ takes string s, unit whichUnit, real zOffset, real size, real red, real green, real blue, real transparency returns texttag
    set bj_lastCreatedTextTag = CreateTextTag()
    call SetTextTagTextBJ(bj_lastCreatedTextTag, s, size)
    call SetTextTagPosUnitBJ(bj_lastCreatedTextTag, whichUnit, zOffset)
    call SetTextTagColorBJ(bj_lastCreatedTextTag, red, green, blue, transparency)

    return bj_lastCreatedTextTag
endfunction

JASS:
function SetTextTagVelocityBJ takes texttag tt, real speed, real angle returns nothing
    local real vel = TextTagSpeed2Velocity(speed)
    local real xvel = vel * Cos(angle * bj_DEGTORAD)
    local real yvel = vel * Sin(angle * bj_DEGTORAD)

    call SetTextTagVelocity(tt, xvel, yvel)
endfunction

well reason why i used it is coz i hate to use something i dont know (why does it like like it dose)! what do i mean? well that function TextTagSpeed2Velocity() is a BJ and it does some calculations i really have no idea why they look like that. its just that i am pissed when i dont know something expecially from math, physics which this 100% is :S

anyway dont worry its just a two BJ-s called once per spell getting triggerd ;)

Deuterium said:
Works as it should and the knockback is nice and smooth.

Good job DD!

thanks i am glade you like it!

Deaod said:
I could have done this with 8 libraries in less than 75 lines. Interested?
You do know the point of libraries is to actually USE them, dont you?

Edit: I suppose using a Critical Strike based ability may acutally reduce the number of used libraries by 5 (while still providing nearly the exact same behaviour).

ohh y i know! well its just that during the time when i was lerining programming i always hadded to do everything myself since it was hard to find someone who could have teach me everything i wanted to know :S

and well when i am codining in c++ the only includes i use are the one which register natives for my use... well when it comes to school and examples we all ofc use printf(const char*, ...) well its quite a long functions since it has to read all that %d, %f, %lf, %c and a lot more... so i would really not code it myself + ofc we use #include <iostream> which u know has full of stuff which is not just registering natives... cout, cin, are the most i use xD

so i know its better not to write my own library coz it has longer code but is as well efficient as much as it could be.

all in all ty for ur comment but i just like to code all the code myself since it my spell :S

btw i have a question for u!
did you try to uncomment that line which changes RandomSeed? if so then u will see quite good randomize for short amount of time and then it will start to generate only 96 and 69... omg some kinde of blizzard bug or better to say bad algorithm i guess... heh that blizz :D

once again! everyone ty for all your feedback and greets!
~Dark Dragon
 
Actually, it wasn't IceFrogs idea. Someone other postet the hero in Hero Suggestions Thread and it got accepted. Back to topic, this spell is nice and good coded. I give it a 4/5 because this seems to be useful but its not that new.

:S i did not know that! well IceFrog sure takes some spells as well, well thats fine too xD

ofc its 4/5 spell which has not my idea in it does not worth 5/5 coz its not fully my work ;)

and ofc it was requested thats just another reason that idea does not belong to me :D

Greets!
~Dark Dragon
 
Level 14
Joined
Nov 18, 2007
Messages
816
Good c++ compilers delete code not used. And Vex's Optimizer does that as well.

This being your spell doesnt justify anything. You can still use other peoples resources, since those are useful for many kinds of applications while your reinvented wheel being a knockback system maybe has a few features less that others will miss.

Point is, stop reinventing the wheel over and over again. It's already been done. And while other peoples code may not be as efficient as it could be, why not optimize it yourself in your own map and let others optimize it themselves? Its better to use standard libraries than to reinvent a wheel a thousand times over and over again. It saves you development time, it avoids all kinds of bugs that are likely to occur in your code simply because they had more time to find and fix all those rare bugs. It helps developing standards for certain things, like Table or TimerUtils are now.

Did you try and uncheck "Use fixed random seed" in one of the option panels of the world editor?
 
Level 17
Joined
Mar 17, 2009
Messages
1,350
Deaod said:
Point is, stop reinventing the wheel over and over again. It's already been done.
Why depend on others when you're able of doing the whole job easily yourself? :p
If DarkDragon can easily do the whole spell without using libraries and have it as efficient as possible... then there's no reason for using them :p

Dark_Dragon said:
:S i did not know that! well IceFrog sure takes some spells as well, well thats fine too xD
Yup he sure does. He's always mentioning in his blog how he appreciates people sending him emails containing ideas :)
 
Deaod said:
Good c++ compilers delete code not used. And Vex's Optimizer does that as well.

Deaod said:
This being your spell doesnt justify anything. You can still use other peoples resources, since those are useful for many kinds of applications while your reinvented wheel being a knockback system maybe has a few features less that others will miss.

a well i did not mean that i have no right to use other people resources! hmm i just dont know how to explain what my problem was :S well its just that i never used anyones code coz i hadded to learn myself from the start to the end... and if i where using other people work, well simple to say i would know less then i know now.

ofc i understand that there resources are made for me/anyone else to use them so i just need to give credits.

:O i did not know that vexs optimizer does that! so if i make too much or better to say multiple private H2I funcs vexs optimizer removes them and use only a main one?

well thats cool ;)

Deaod said:
Point is, stop reinventing the wheel over and over again. It's already been done. And while other peoples code may not be as efficient as it could be, why not optimize it yourself in your own map and let others optimize it themselves? Its better to use standard libraries than to reinvent a wheel a thousand times over and over again. It saves you development time, it avoids all kinds of bugs that are likely to occur in your code simply because they had more time to find and fix all those rare bugs. It helps developing standards for certain things, like Table or TimerUtils are now.

o well i get your point and well its true that i could even optimize it as i want + it would save me time and make it more stable coz once i made an library i can always use it instead of rewriting same code hundret times. i know you are correct about this so ill see what i can do, maybe at leat write my own librarys which ill use whole the time insted of writing same thing again and again. ofc its not just the extra time i need to write all that but as well taking space in RAM for extra functions... well even if we today have about 2 GB RAM i have 1.5 GB :S anyway i still will take ur suggestion and will see what i can do ;)

Deaod said:
Did you try and uncheck "Use fixed random seed" in one of the option panels of the world editor?

wot i never even knew about this! so i just went to check it now and y it is not enabled so it is supposed to work... but it does not, it crashes at least for me :S

however ty for telling me abot this, did not know it exists!

Deuterium said:
Why depend on others when you're able of doing the whole job easily yourself? :p
If DarkDragon can easily do the whole spell without using libraries and have it as efficient as possible... then there's no reason for using them :p

well that depends on who is writing the code and how well is it done...
example struct.allocate() is a well coded method with fast algorithm as well as destroy method. since many people who are now programming in vjass dont even know about making some fast algorithms there is this standart. so you dont need to know everything complex by urself but you can use others people script which is faster then that autors code...

the same goes for librarys Deaod spoke of! there are quite well coded standards already but well they are made to work with most of stuff you need. however even that sometimes is not enough so you still need to code some stuff urself... the good thing is that it saves ur time, decreases lines of code and simple does not require you to register new functions again. so this is quite good but speed might be only a little slower for example:

JASS:
// My
constant function QMod takes integer a, integer b returns integer
    return a - (a/b) * b
endfunction

// Blizzard
function ModuloInteger takes integer a, integer b returns integer
    local integer mod = a - (a/b) * b
    if (mod < 0) then
        set mod = mod + b
    endif
    return mod
endfunction

blizzards one works for any inputed numbers but my works only for positive ones. and thats enough for me in this spell so i made it faster which matches my spell.

the same goes for other people librarys they must make all there stuff work in any situation, which makes it a bit slower... but still all other things like time and rewriting bla bla is as well important... so its the reason i dont know which one to choose xD

Deuterium said:
Yup he sure does. He's always mentioning in his blog how he appreciates people sending him emails containing ideas :)

well i never talked to him or posted anything in forums...
the only thing i did for dota is reported bug with razors ultimate not changing damage per level until he dies or gets stunned...

ofc i posted in that suggestion like Multi Kill rather then just playing tripple kill even if u kill more then 3 but he never gave replay nor anything else... since he saw in that map that i was a GUI noob... triggers full of leaks non-MUI stuff... so he just took my bug report and nothing else... well thats fine with me xD

Greets!
~Dark Dragon
 
Level 14
Joined
Nov 18, 2007
Messages
816
Deuterium, read the rest of my post. I could reinvent the wheel a thousand times over and over again. I could make each spell of mine include a group and timer recycling system. But after a few times of implementing all that crap you get tired of it. So you just reuse code you wrote before, this allows you to use that time you previously needed to implement the recycling to code new, creative things.

Dark_Dragon, seems like i didnt express myself correctly. The optimizer does not delete functions that are referenced later in the map script, it only deletes completely unused functions that are never called or executed. It also wont merge multiple instances of the H2I function.

WC3 typically uses ~90 MB of RAM. WC3 will be completely unusable long before you get anywhere near 1.5 GB of used RAM.
 
Level 17
Joined
Mar 17, 2009
Messages
1,350
off-topic:
Dark_Dragon said:
i hadded to learn
Dark_Dragon said:
never gave replay
I've seen say these things more then once, so thought you'd like to know that what ur saying is wrong literaly...
The past tense of have is had not hadded.
It's reply and not replay...
replay is when you re-watch a movie or re-listen to a song - basically playing something again: re-play :p

Dark_Dragon said:
the only thing i did for dota is reported bug with razors ultimate not changing damage per level until he dies or gets stunned...
This bug must have been before i even knew warcraft existed... and i cant imagine any situation in which you could possibly have noticed that bug :p
 
Deaod said:
Dark_Dragon, seems like i didnt express myself correctly. The optimizer does not delete functions that are referenced later in the map script, it only deletes completely unused functions that are never called or executed. It also wont merge multiple instances of the H2I function.

ahh soo... sry i got confused for a sec yy u explained it well its just me who got it wrong. well optimizer removes only funcs which are never called xD

Deaod said:
WC3 typically uses ~90 MB of RAM. WC3 will be completely unusable long before you get anywhere near 1.5 GB of used RAM.

o yeah well i actually did not even think about using 1.5 GB! i only thought that well for each function it has to use some small amount of RAM but i dont think i would break the limits :D

Anyway thanks for explaining things +rep!

Deuterium said:
I've seen say these things more then once, so thought you'd like to know that what ur saying is wrong literaly...
The past tense of have is had not hadded.
It's reply and not replay...
replay is when you re-watch a movie or re-listen to a song - basically playing something again: re-play :p

ahh thanks for telling me about this mistakes, ill make sure not to write that bad ;)

Deuterium said:
This bug must have been before i even knew warcraft existed... and i cant imagine any situation in which you could possibly have noticed that bug :p

ahh you are right that was an hard bug to find and it existed from beginning...
well i cant remember correctly but i know that i found it out when i was making my own ability from passive immolation. then it did not work when i level up, but when i died it was again fine :S so i checked it in dota and saw that this bugs there as well xD

greets and will give u rep when i can ;)
~Dark Dragon
 
Nice, i just love when you get lucky enough to "cast" it twice in a row ;D

It's a useful spell for any kind of maps.

Try to add some more epic special effects =)

ty for comment xD!

well i was requested to do it like this but i could add maybe some extra special effects for user setup.

will see about that ;)

Greets!
~Dark Dragon
 
Level 8
Joined
Jul 14, 2010
Messages
235
Yeah what is
// *** Include ddup.j ***
//! import "ddup.j"
Getting error saying it cant find the file.

I also remember I gave up on importing DD Universal Spell Pack cus of this strange mysterious file. The trigger back then had something with "Get ddup.j trigger", and the trigger that was named ddup.j had nothing in it except " //! import "ddup.j" ". What is it? It doesnt make sense..
You can even save the TEST MAP and it doesnt work.. -5/5
 
Last edited:
Level 11
Joined
Jul 17, 2013
Messages
544
was my job and if u are happy with it then its great ;)
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
 

Hi,

1) are there any triggers in your map which run at exactlly 20 minutes after game starts?
if so can you check them or post them here?

2) You need dummy unit in the map because dummy is used to check if destructable is tree, since you set 'DESTROY_TREES' to false, you dont need dummy for this spell.

3) This spell is made on WC3 1.30.4 patch, which WC3 version do you use?

The code in between /* */ should not be used!

Regards!
~DD
 
Last edited:
Level 11
Joined
Jul 17, 2013
Messages
544
Hi,

1) are there any triggers in your map which run at exactlly 20 minutes after game starts?
if so can you check them or post them here?

2) You need dummy unit in the map because dummy is used to check if destructable is tree, since you set 'DESTROY_TREES' to false, you dont need dummy for this spell.

Regards!
~DD
Sorry for being such desperated but i love this system so much and i am sad this wont work. well i also copied unit named dumy from object editor is this what you mean? i just didnt see model mdx with dummy to import

Well sadly no :( there are only triggers which works every minute. they check if anyone left game and if it should share leavers gold to allies.I have removed them and some other custom spells im using im gonna test now.

If you want i can send you my map so you can check it out better as i have no idea what can cause it maybe im just nub and i installed it wrongly.In read me there was also said to copy unit chill ability but what it means? there was only max bash ability to copy.
 
i send u private message so no one else can dl map. is it fine if i uploaded to other webside? i find pastebin bit wierd i didnt see option to upload file just text.

Hi,

i downloaded the map, but i cant open it!
https://www.hiveworkshop.com/attachments/clipboard01-jpg.332023/

the reason might be that it uses different WC3 version, my is v1.30.4

it is true that dummy model is missing from import manager, i am going to fix that!

It could be that timers running every minute, after 20 executions do something...

Regards!
~DD
 
Level 11
Joined
Jul 17, 2013
Messages
544
Level 11
Joined
Jul 17, 2013
Messages
544
Unless it is timers running every minute, after 20 executions i would not know due to high complexity of the map and new patch.

Regards!
~DD
wait but you mean its problem of this system that this system has timers which are executed 20 times and it bugs this yes? did i understant correctly. i would like to fix it myself idk how any ideas
 
wait but you mean its problem of this system that this system has timers which are executed 20 times and it bugs this yes? did i understant correctly. i would like to fix it myself idk how any ideas

Hi,

no what i meant is that you said that you have timers in your map running every minute.
so if things start to happen every 20 minutes, it could be that its in some of this triggers in your map.
i cant open your map or test it in v1.31.1 because i coded this spell in v1.30.4 and i still use older version, switching is ineffective for me now.
i would recomend to try and copy and paste this spell again to your map.
there might be other memebers here as you mentioned which will test this spell in your map if you send them the link.

Diegoit, hemmedo
Maximum_Evil
aider
EvilNico
Raziel_br
Elsheen
Deuterium
baassee

i am sorry that i cant be of more help.
Regards!
~DD
 
Level 11
Joined
Jul 17, 2013
Messages
544
Hi,

no what i meant is that you said that you have timers in your map running every minute.
so if things start to happen every 20 minutes, it could be that its in some of this triggers in your map.
i cant open your map or test it in v1.31.1 because i coded this spell in v1.30.4 and i still use older version, switching is ineffective for me now.
i would recomend to try and copy and paste this spell again to your map.
there might be other memebers here as you mentioned which will test this spell in your map if you send them the link.

Diegoit, hemmedo
Maximum_Evil
aider
EvilNico
Raziel_br
Elsheen
Deuterium
baassee

i am sorry that i cant be of more help.
Regards!
~DD
Still no support for the latest patch? :( i dont get why wouldnt you update if majority is using offical wc3 latest patch
 
Still no support for the latest patch? :( i dont get why wouldnt you update if majority is using offical wc3 latest patch

Hi,

yes there is no support for latest version. Some new'er versions of warcraft have modified 'BlzGetUnitZ' native function behavior, lua scripting has been added, but i am lazy to learn all that and modify all of this.
futuremore there could be future changes in native functions or bugs in future patches which mess up the spells. this is standard reason why settle for single patch is recommended and switch when some bigger usefull changes occur, such as adding of new natives...

Regards!
~DD
 
How make this ability for 10 levels, not 3 levels?

Hi,

In trigger editor go to "Spells/Max Bash" trigger, find below code and add more levels for each data type.
In object editor you also need to change Max Bash spell from 3 to 10 levels.

for example if you want that at level 4 critical bash damage is 5x times add this line to "BashDamage" function
JASS:
bash_damage[4] = 5.0;


JASS:
// -------------------------------------------------------------
    //          *** Level data setup here ***
    
    
    // -------------------------------------------
    // *** The bash damage per level ***
    // Default: 2.6 / 3.4 / 4.5
    function BashDamage(integer level) -> real {
        real bash_damage[];
        
        bash_damage[1] = 2.6;
        bash_damage[2] = 3.4;
        bash_damage[3] = 4.5;
        
        return bash_damage[level];
    }
    
    // -------------------------------------------
    // *** The bash push distance per level ***
    // Default: 200. / 230. / 270.
    function BashPushDistance(integer level) -> real {
        real bash_range[];
        
        bash_range[1] = 200.;
        bash_range[2] = 230.;
        bash_range[3] = 270.;
        
        return bash_range[level];
    }
    
    // -------------------------------------------
    // *** The bash push height per level ***
    // Default: 90. / 110. / 130.
    function BashPushHeight(integer level) -> real {
        real bash_height[];
        
        bash_height[1] = 90.;
        bash_height[2] = 110.;
        bash_height[3] = 130.;
        
        return bash_height[level];
    }
    
    // -------------------------------------------
    // *** The bash push height travel percentage per level ***
    // This setting says at which percentage of path will unit land
    // Default: 30. / 34. / 39.
    function BashPushHeightTravelPerc(integer level) -> real {
        real bash_height_travel_perc[];
        
        bash_height_travel_perc[1] = 30.;
        bash_height_travel_perc[2] = 34.;
        bash_height_travel_perc[3] = 39.;
        
        return bash_height_travel_perc[level];
    }
    
    // -------------------------------------------
    // *** The damaged dealt by impact
    // Default: 30. / 40. / 55.
    function BashPushGroundImpactDamage(integer level) -> real {
        real bash_ground_impact_damage[];
        
        bash_ground_impact_damage[1] = 30.;
        bash_ground_impact_damage[2] = 40.;
        bash_ground_impact_damage[3] = 55.;
        
        return bash_ground_impact_damage[level];
    }

Regards!
~Dark Dragon
 
Top