Name | Type | is_array | initial_value |
//==========================================================================
// 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) {
integer sh = StringHash(file),
snd_n = LoadInteger(GenSndTable, sh, 04);
sound s = LoadSoundHandle(GenSndTable, sh, snd_n);
real d;
integer i;
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);
snd_n += 01;
if (snd_n == 04)
snd_n = 00;
SaveInteger(GenSndTable, sh, 04, snd_n);
// 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( DDHypot(GetCameraTargetPositionX()-x, GetCameraTargetPositionY()-y) );
if (d < mxDist) {
SetSoundVolume(s, R2I((vol-d*(vol/mxDist))*1.27));
StartSound(s);
}
}
}
}
public function DDGetGameElapsedTime() -> real {
return TimerGetElapsed(GameElapsedTimer);
}
public function DDGetRndReal(real min, real max) -> real {
real rnd = ((max-min)/1000000.)*I2R(RndInt[RndIntReadN]);
debug if (max > 1000000.)
DDMsg("ERROR: \"DDGetRndNumber\" - 'max' variable is greater than 1000000!");
RndIntReadN += 01; if (RndIntReadN == RND_INT_MAX_ARRAY_N) RndIntReadN = 00;
return min + rnd;
}
public function DDGetRndInt(integer min, integer max) -> integer {
return R2I( DDGetRndReal(I2R(min), I2R(max)) );
}
// ====================================================================
function onInit() {
InitDamageDetection();
InitDestTreeCheck();
InitEnumDests();
DDMinX = GetRectMinX(bj_mapInitialPlayableArea);
DDMinY = GetRectMinY(bj_mapInitialPlayableArea);
DDMaxX = GetRectMaxX(bj_mapInitialPlayableArea);
DDMaxY = GetRectMaxY(bj_mapInitialPlayableArea);
GenSndTable = InitHashtable();
ErrorSound = CreateSound( "Sound\\Interface\\Error.wav", false, false, false, 10, 10, "" );
SetSoundParamsFromLabel( ErrorSound, "InterfaceError" );
SetSoundDuration( ErrorSound, 614 );
SetSoundVolume(ErrorSound, 127);
GameElapsedTimer = CreateTimer();
TimerStart(GameElapsedTimer, 10800., false, null);
for(RndIntWriteN=00; RndIntWriteN < RND_INT_MAX_ARRAY_N; RndIntWriteN+=01)
RndInt[RndIntWriteN] = GetRandomInt(00, 1000000);
RndIntWriteN = 00;
RndGenForce = CreateForce();
TrigMouseEvent = CreateTrigger();
ForForce(bj_FORCE_ALL_PLAYERS, function() {
if (GetPlayerController(GetEnumPlayer()) == MAP_CONTROL_USER)
TriggerRegisterPlayerEvent(TrigMouseEvent, GetEnumPlayer(), EVENT_PLAYER_MOUSE_MOVE);
});
TriggerAddCondition(TrigMouseEvent, Condition(function() -> boolean {
real mouseN;
boolean xFirst = GetRandomInt(00, 01) == 01;
if (!IsPlayerInForce(GetTriggerPlayer(), RndGenForce)) {
// example: input x = 578.4571496
// output rnd n = 4571498
if (xFirst)
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
else
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
if (mouseN == 0.)
return false;
//mouseN *= 100.;
RndInt[RndIntWriteN] = R2I((mouseN - I2R(R2I(mouseN))) * 1000.);
//DDMsg(I2S(RndInt[RndIntWriteN]));
//RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
if (xFirst)
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
else
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
RndInt[RndIntWriteN] += R2I((mouseN - I2R(R2I(mouseN))) * 1000.)*1000;
//DDMsg(I2S(RndInt[RndIntWriteN]));
RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
ForceAddPlayer(RndGenForce, GetTriggerPlayer());
}
if (DDGetGameElapsedTime()-RndElapsedTime > .125) {
ForceClear(RndGenForce);
RndElapsedTime = DDGetGameElapsedTime();
}
return false;
}));
}
}
//! endzinc
//TESH.scrollpos=0
//TESH.alwaysfold=0
// *******************************************************************************
// Spell: Max Bash by Dark Dragon
//
// Credits: IceFrog -> Used in Dota! Idea is by someone...
// Venom -> Icon
// nerovesper -> Request
//
//
// Installation:
//
// 1. Copy 'DD Library' and this trigger to your map
// 2. Copy abilitie and buff 'Max Bash' to your map
// 3. Export and import "DDSpells\MaxBashHit.mdx" and icons with same path to your map if you are going to use them
// 4. Modifie data below to mach your map and other constants
//
// * Enjoy!
// *******************************************************************************
//! zinc
// *** This is the max bash spell library ***
library MaxBash requires DDLib
{
// ======================================================================
// *** Editable constants ***
// ======================================================================
// -------------------------------------------------------
// This is the raw code of max bash ability
// Default: 'MBsh'
constant integer MAX_BASH_ID = 'MBsh';
// -------------------------------------------------------
// This is the raw code of max bash buff
// Default: 'MBed'
constant integer MAX_BASH_BUFF_ID = 'MBed';
// ----------------------------------------------------------
// Push model file effect
// Default: Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl
constant string PUSH_MODEL_FILE = "Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl";
// ----------------------------------------------------------
// Push model file effect of sliding unit, attach point
// Default: foot
constant string PUSH_EFFECT_ATT_POINT = "foot";
// ----------------------------------------------------------
// If custom water effect is wanted set this to true
// Default: true
constant boolean USE_WATER_FILE = true;
// ----------------------------------------------------------
// Push model file effect on water
// Default: Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl
constant string PUSH_MODEL_WATER_FILE = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl";
// -------------------------------------------------------
// On push file (when ability is triggerd) applay this effect
// Default: DDSpells\\MaxBashHit.mdx
constant string ON_PUSH_FILE = "DDSpells\\MaxBashHit.mdx";
// -------------------------------------------------------
// How often to create "PUSH_MODEL_FILE" effect
// Default: .1
constant real PUSH_EFFECT_INTERVAL = .15;
// -------------------------------------------------------
// On which point of attacker "ON_PUSH_FILE" is added?
// Default: weapon
constant string ATTACH_POINT = "weapon";
// -------------------------------------------------------
// Effect generated on target unit when its hit
// Default: none
constant string BASH_HIT_EFFECT_FILE = "";
constant string BASH_HIT_EFFECT_FILE_ATT_POINT = "origin";
// -------------------------------------------------------
// Effect generated when unit hits the ground
// Default: Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl
constant string BASH_HIT_GROUND_EFFECT_FILE = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl";
// -------------------------------------------------------
// Effect generated when unit hits/impacts the water, requires USE_WATER_FILE to be true
// Default: Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl
constant string BASH_IMPACT_WATER_EFFECT_FILE = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl";
// -------------------------------------------------------
// While unit is sliding, what will be its animation speed in percentage
// Default: 65.
constant real UNIT_ANIM_SPEED = 65.;
// -------------------------------------------------------
// While unit is sliding, what will be its animation
// Default: attack
constant string UNIT_ANIM_NAME = "attack";
// -------------------------------------------------------
// Extra damage that bash deals seted up by level "function BashDamage" will
// be treated as extra/bonus damage or as percentage of dealt damage,
// if true percent damage will be dealt
// Default: true
constant boolean BASH_DAMAGE_AS_PERCENTAGE = true;
// -------------------------------------------------------
// This is speed in game coordinates, of how fast unit is pushed away
// defined start and end speeds of pushed unit
// start speed = initial speed, end speed = speed before stopping
// Default: 350 / 50
constant real PUSH_SPEED_START = 350.;
constant real PUSH_SPEED_END = 50.;
// -------------------------------------------------------
// Enemy units that collide with sliding unit will be slightly pushed too?
// Default: true
constant boolean ENABLE_SPLASH_BASH = true;
// -------------------------------------------------------
// Damage splashed on extra colliding units in percentage
// Default: 30%
constant real SPLASH_DAMAGE_PERCENTAGE = 30.;
// -------------------------------------------------------
// The min speed at which sliding unit has to be to cause pushing other unit
// Default: 175.
constant real SPLASH_BASH_NEED_SPEED = 175.;
// -------------------------------------------------------
// The area/radius around sliding unit to check for splash damage
// Default: 120.
constant real SPLASH_BASH_SCAN_R = 120.;
// -------------------------------------------------------
// How often to check for splash bash damage
// Default: .2
constant real SPLASH_BASH_INTERVAL = .2;
// -------------------------------------------------------
// Some units have default ground offset height, what is that max height
// so that bash does treat this units as grounded
// Default: 50.
constant real SPLASH_BASH_MAX_HEIGHT = 50.;
// -------------------------------------------------------
// Should trees be destroyed?
// Default: true
constant boolean DESTROY_TREES = true;
// -------------------------------------------------------
// How often to clear trees
// Default: .2
constant real CLEAR_TREES_INTERVAL = .2;
// -------------------------------------------------------
// Range in which to clear trees
// Default: 160.
constant real CLEAR_TREES_RADIUS = 160.;
// -------------------------------------------------------
// Range at which to check if pathing is unpathable, for bounce code to be triggerd
// Default: 60.
constant real BOUNCE_DETECT_RANGE = 60.;
// -------------------------------------------------------
// Should floating text be created?
// Default: true
constant boolean ALLOW_FLOATING_TEXT = true;
// -------------------------------------------------------
// When units are killed by bash display KO or damage dealt
// Default: true
constant boolean USE_KO_TEXT = true;
// -------------------------------------------------------
// The duration of floating text in seconds
// Default: 4.25
constant real FLOATING_TEXT_DURATION = 3.;
// -------------------------------------------------------
// The color of text rgb transparency in percentage
// Default: 100. / 100. / 100. / 20.
constant real FLOATING_TEXT_RED = 100.;
constant real FLOATING_TEXT_GREEN = 100.;
constant real FLOATING_TEXT_BLUE = 100.;
constant real FLOATING_TEXT_TRANS = 20.;
// -------------------------------------------------------------
constant key UNIT_BASH_KEY;
// -------------------------------------------------------------
// *** Level data setup here ***
// -------------------------------------------
// *** The bash damage per level ***
// Default: 2.6 / 3.4 / 4.5
function BashDamage(integer level) -> real {
real bash_damage[];
bash_damage[1] = 2.6;
bash_damage[2] = 3.4;
bash_damage[3] = 4.5;
return bash_damage[level];
}
// -------------------------------------------
// *** The bash push distance per level ***
// Default: 200. / 230. / 270.
function BashPushDistance(integer level) -> real {
real bash_range[];
bash_range[1] = 200.;
bash_range[2] = 230.;
bash_range[3] = 270.;
return bash_range[level];
}
// -------------------------------------------
// *** The bash push height per level ***
// Default: 90. / 110. / 130.
function BashPushHeight(integer level) -> real {
real bash_height[];
bash_height[1] = 90.;
bash_height[2] = 110.;
bash_height[3] = 130.;
return bash_height[level];
}
// -------------------------------------------
// *** The bash push height travel percentage per level ***
// This setting says at which percentage of path will unit land
// Default: 30. / 34. / 39.
function BashPushHeightTravelPerc(integer level) -> real {
real bash_height_travel_perc[];
bash_height_travel_perc[1] = 30.;
bash_height_travel_perc[2] = 34.;
bash_height_travel_perc[3] = 39.;
return bash_height_travel_perc[level];
}
// -------------------------------------------
// *** The damaged dealt by impact
// Default: 30. / 40. / 55.
function BashPushGroundImpactDamage(integer level) -> real {
real bash_ground_impact_damage[];
bash_ground_impact_damage[1] = 30.;
bash_ground_impact_damage[2] = 40.;
bash_ground_impact_damage[3] = 55.;
return bash_ground_impact_damage[level];
}
// -------------------------------------------
// *** If splash bash is activated, this will filter which are proper units to be pushed.
function SplashBashFilter(unit filter_unit, player bashPlayer) -> boolean {
return !IsUnitType(filter_unit, UNIT_TYPE_DEAD) &&
IsUnitEnemy(filter_unit, bashPlayer) &&
IsUnitType(filter_unit, UNIT_TYPE_GROUND) &&
!BlzIsUnitInvulnerable(filter_unit) &&
!IsUnitType(filter_unit, UNIT_TYPE_STRUCTURE) &&
!DDIsUnitWard(filter_unit);
}
// ==================================================================================
// *** End spell modification ***
// ==================================================================================
// ---------------------------------------------------------
// *** Bash data structure
struct bash {
private {
unit b, s;
real dist, mxdist, impdist;
real dx, dy, initZ;
p_real z_parab;
real dmg;
p_real vec;
integer p, lvl;
boolean hit_ground;
}
/// ----------------------------------------------------------------
// ***
static method create(integer castplayer, unit bashingUnit, unit whichUnit, integer lvl, real distance, real radians, real damage) -> bash {
bash this = allocate();
s = bashingUnit;
b = whichUnit;
dist = distance;
mxdist = SquareRoot(distance);
dx = Cos(radians);
dy = Sin(radians);
vec = pVector(GetUnitX(b), GetUnitY(b), GetUnitFlyHeight(b));
p = castplayer;
dmg = damage;
hit_ground = BashPushGroundImpactDamage(lvl) < 1.;
// -------------------------
// a(x-x0)^2 + y0 = a(x^2 - 2x*x0 + x0^2) + y0 = a*(x)^2 - 2*a*x0*(x) + a*x0^2+y0
// -------------------------
// x0 = mxDist(100-perc/2)/100
// y0 = vec[2] + h
// a = -h / (mtd/2)^2
// b = -2*a*x0
// c = y0+a*x0^2
z_parab = p_real.create();
z_parab[0] = -BashPushHeight(lvl) / Pw_2(BashPushHeightTravelPerc(lvl)*distance/200.); // a = -h / (mtd/2)^2
z_parab[1] = -2. * z_parab[0] * ((100.-BashPushHeightTravelPerc(lvl)/2.)*distance/100.); // b = -2*a*x0
z_parab[2] = (BashPushHeight(lvl) + vec[2]) + z_parab[0]*Pw_2((100.-BashPushHeightTravelPerc(lvl)/2.)*distance/100.); // c = a*x0^2+y0
this.lvl = lvl;
impdist = (100.-BashPushHeightTravelPerc(lvl))*distance/100.;
initZ = vec[2];
DDSet(b, UNIT_BASH_KEY, this);
UnitAddAbility(b, 'Amrf');
UnitRemoveAbility(b, 'Amrf');
SetUnitTimeScale(b, UNIT_ANIM_SPEED*.01);
if (!IsUnitType(b, UNIT_TYPE_DEAD)) {
SetUnitAnimation(b, UNIT_ANIM_NAME);
QueueUnitAnimation(b, "stand");
}
return this;
}
// ***
method destroy() {
DDSet(b, UNIT_BASH_KEY, p_null);
SetUnitTimeScale(b, 1.);
vec.destroy();
z_parab.destroy();
s = null;
b = null;
deallocate();
}
method operator IsAirborn() -> boolean { return !hit_ground; }
/// ----------------------------------------------------------------
//* splash code
private method TryDoSplashBash() {
integer i;
unit u;
// *** Skip splash if unit is in air ***
if (vec[2] > SPLASH_BASH_MAX_HEIGHT)
return;
// --------------------------------------
// *** pick units to be splash bashed
DDGroupFillMemArea(vec[0], vec[1], SPLASH_BASH_SCAN_R, p, function() -> boolean {
return SplashBashFilter(GetFilterUnit(), Player(DDMemUnitData())) && DDGet(GetFilterUnit(), UNIT_BASH_KEY) == p_null;
});
// --------------------------------------------------------------
// *** Now launch them in there respective directions and speeds
for(i=0; i < DDMemUnitN(); i+=1) {
u = DDMemUnit(i);
// *** Do damage and bash the target ***
UnitDamageTarget(s, u, dmg*SPLASH_DAMAGE_PERCENTAGE*.01, true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
BashExecute.evaluate(p, s, u, lvl, dist, Atan2(GetUnitY(u)-vec[1], GetUnitX(u)-vec[0]), dmg);
// *** Floating text add? ***
static if (ALLOW_FLOATING_TEXT)
static if (USE_KO_TEXT)
if (IsUnitType(u, UNIT_TYPE_DEAD))
DDNewTextTagUnit(u, "|cffff0000KO!!!|r", FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
else
DDNewTextTagUnit(u, "+"+I2S(R2I(dmg*SPLASH_DAMAGE_PERCENTAGE*.01)), FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
else
DDNewTextTagUnit(u, "+"+I2S(R2I(dmg*SPLASH_DAMAGE_PERCENTAGE*.01)), FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
}
}
// -------------------------------------------------------------------------
// * checks and performs ground impact as well as dealing damage and displaying floating text
method CheckGroundHit() {
if (impdist > dist) {
vec[2] = initZ;
if (!hit_ground) {
hit_ground = true;
static if (USE_WATER_FILE)
if (IsTerrainPathable(vec[0], vec[1], PATHING_TYPE_FLOATABILITY))
DestroyEffect(AddSpecialEffect(BASH_HIT_GROUND_EFFECT_FILE, vec[0], vec[1]));
else
DestroyEffect(AddSpecialEffect(BASH_IMPACT_WATER_EFFECT_FILE, vec[0], vec[1]));
else
DestroyEffect(AddSpecialEffect(BASH_HIT_GROUND_EFFECT_FILE, vec[0], vec[1]));
if (!IsUnitType(b, UNIT_TYPE_DEAD)) {
UnitDamageTarget(s, b, BashPushGroundImpactDamage(lvl), true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
// *** Floating text add? ***
static if (ALLOW_FLOATING_TEXT)
static if (USE_KO_TEXT)
if (IsUnitType(b, UNIT_TYPE_DEAD))
DDNewTextTagUnit(b, "|cffff0000KO!!!|r", FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
else
DDNewTextTagUnit(b, "+"+I2S(R2I(BashPushGroundImpactDamage(lvl))), FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
else
DDNewTextTagUnit(b, "+"+I2S(R2I(BashPushGroundImpactDamage(lvl))), FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
}
}
}
}
/// ----------------------------------------------------------------
//* primary push code
method Launch() {
DDStartTim(DD_INTERVAL, true, this, static method() {
bash this = bash( DDTimData() );
integer tick = DDTimTick();
real speed = DD_INTERVAL * (PUSH_SPEED_END + ( (PUSH_SPEED_START-PUSH_SPEED_END)/mxdist )*SquareRoot(dist));
// basic formula for speed: y = a*sqrt(x) + b
// ----------------------------------
// *** Position calcs
dist -= speed;
vec[0] += speed * dx;
vec[1] += speed * dy;
vec[2] = (z_parab[0]*Pw_2(dist)) + (z_parab[1]*dist) + (z_parab[2]);
// ------------------------------------
// *** Check if hit ground ***
CheckGroundHit();
// ----------------------------------
// *** Motion
SetUnitX(b, vec[0]);
SetUnitY(b, vec[1]);
SetUnitFlyHeight(b, vec[2], 0.);
// ----------------------------------
// *** Push Effects
if (ModuloReal(tick*DD_INTERVAL, PUSH_EFFECT_INTERVAL) < DD_INTERVAL)
static if (USE_WATER_FILE)
if (IsTerrainPathable(vec[0], vec[1], PATHING_TYPE_FLOATABILITY) || vec[2] > SPLASH_BASH_MAX_HEIGHT)
DestroyEffect(AddSpecialEffectTarget(PUSH_MODEL_FILE, b, PUSH_EFFECT_ATT_POINT));
else
DestroyEffect(AddSpecialEffectTarget(PUSH_MODEL_WATER_FILE, b, PUSH_EFFECT_ATT_POINT));
else
DestroyEffect(AddSpecialEffectTarget(PUSH_MODEL_FILE, b, PUSH_EFFECT_ATT_POINT));
// ----------------------------------------
// *** Check if trees are in the way
static if (DESTROY_TREES)
if (ModuloReal(tick*DD_INTERVAL, CLEAR_TREES_INTERVAL) < DD_INTERVAL) {
DDEnumDestsInRange(vec, CLEAR_TREES_RADIUS, null, function() {
if (DDIsDestructableTree(GetEnumDestructable()))
KillDestructable(GetEnumDestructable());
});
}
// ---------------------------------------------------------
// *** Check for bounce/collision with unpathable points
if (ModuloReal(tick*DD_INTERVAL, CLEAR_TREES_INTERVAL) < DD_INTERVAL) {
if (IsTerrainPathable(vec[0]+BOUNCE_DETECT_RANGE*dx, vec[1], PATHING_TYPE_WALKABILITY))
dx = -dx;
else if (IsTerrainPathable(vec[0], vec[1]+BOUNCE_DETECT_RANGE*dy, PATHING_TYPE_WALKABILITY))
dy = -dy;
}
// ----------------------------------------
// *** Check if there are any units to be splash bashed
static if (ENABLE_SPLASH_BASH)
if (speed > SPLASH_BASH_NEED_SPEED*DD_INTERVAL && ModuloReal(tick*DD_INTERVAL, SPLASH_BASH_INTERVAL) < DD_INTERVAL)
TryDoSplashBash();
// ----------------------------------
// *** Check to quit push
if (dist <= 0.) {
PauseUnit(b, false);
destroy();
DDQuitTim();
}
});
}
}
// *** Bash function that will be called from above and below
function BashExecute(integer p, unit basher, unit u, integer lvl, real dist, real radians, real dmg) {
// *** Now pause the victim ***
PauseUnit(u, true);
DestroyEffect(AddSpecialEffectTarget(BASH_HIT_EFFECT_FILE, u, BASH_HIT_EFFECT_FILE_ATT_POINT));
bash.create(p, basher, u, lvl, dist, radians, dmg).Launch(); /// *** Will be auto destroyed
}
// ==================================================================================
// *** Initialization ***
function onInit() {
trigger t = CreateTrigger();
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ATTACKED);
DDTriggerRegisterAnyUnitDamaged(t);
TriggerAddCondition(t, function() -> boolean {
unit u;
unit v;
integer lvl;
real dmg = 0.;
bash b;
// *** Attac
if (GetTriggerEventId() == EVENT_PLAYER_UNIT_ATTACKED) {
u = GetAttacker();
v = GetTriggerUnit();
b = DDGet(v, UNIT_BASH_KEY);
if (b != p_null)
if (IsUnitType(u, UNIT_TYPE_MELEE_ATTACKER) && b.IsAirborn)
IssueImmediateOrder(u, "stop");
u = null;
v = null;
return false;
}
u = GetEventDamageSource(); // Damages source unit
v = GetTriggerUnit(); // Victim unit
lvl = GetUnitAbilityLevel(u, MAX_BASH_ID); // Spell level
// --------------------------------------
// *** Condition
if (GetUnitAbilityLevel(v, MAX_BASH_BUFF_ID) == 0 || DDGet(v, UNIT_BASH_KEY) != p_null) {
UnitRemoveAbility(v, MAX_BASH_BUFF_ID);
u = null;
v = null;
return false;
}
// *** No future exec, prevent exec this trigger when we call "UnitDamageTarget" below ***
DisableTrigger(GetTriggeringTrigger());
// *** Clear the buff ***
UnitRemoveAbility(v, MAX_BASH_BUFF_ID);
// *** Primary damage ***
static if (BASH_DAMAGE_AS_PERCENTAGE)
dmg = GetEventDamage()*BashDamage(lvl);
else
dmg = BashDamage(lvl);
BlzSetEventDamage(GetEventDamage() + dmg);
//UnitDamageTarget(u, v, dmg, true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
// *** Apply custom effect ***
DestroyEffect(AddSpecialEffectTarget(ON_PUSH_FILE, u, ATTACH_POINT));
// *** Floating text add? ***
static if (ALLOW_FLOATING_TEXT)
static if (USE_KO_TEXT)
if (IsUnitType(v, UNIT_TYPE_DEAD))
DDNewTextTagUnit(v, "|cffff0000KO!!!|r", FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
else
DDNewTextTagUnit(v, "+"+I2S(R2I(dmg)), FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
else
DDNewTextTagUnit(v, "+"+I2S(R2I(dmg)), FLOATING_TEXT_DURATION, FLOATING_TEXT_RED, FLOATING_TEXT_GREEN, FLOATING_TEXT_BLUE, FLOATING_TEXT_TRANS);
// *** Load main bash struct ***
BashExecute(GetPlayerId(GetOwningPlayer(u)), u, v, lvl, BashPushDistance(lvl), Atan2(GetUnitY(v)-GetUnitY(u), GetUnitX(v)-GetUnitX(u)), dmg);
// *** Allow next exec ***
EnableTrigger(GetTriggeringTrigger());
// *** Null locals ***
u = null;
v = null;
return false;
});
}
}
//! endzinc
// ============================================================================================
// Change me from /* to // and then back to /* after saving and reopening the map
// |
// ˇ
/*
// Credits: PurgeandFire for lua tutorial
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i -- =================================================================
//! i MAX_BASH_ABIL_ID = "MBsh"
//! i MAX_BASH_BUFF_ID = "MBed"
//! i -- =================================================================
//! i setobjecttype("buffs")
//! i createobject("BSTN", MAX_BASH_BUFF_ID)
//! i makechange(current, "fart", "ReplaceableTextures\\CommandButtons\\PASBash.blp")
//! i makechange(current, "ftat", "")
//! i makechange(current, "fta0", "")
//! i makechange(current, "fnam", "Max Bash")
//! i makechange(current, "ftip", "Max Bash")
//! i setobjecttype("abilities")
//! i createobject("AHbh", MAX_BASH_ABIL_ID)
//! i makechange(current,"anam", "Max Bash")
//! i makechange(current,"aart", "ReplaceableTextures\\CommandButtons\\PASBash.blp")
//! i makechange(current,"arar", "ReplaceableTextures\\CommandButtons\\PASBash.blp")
//! i makechange(current,"abpx", "3")
//! i makechange(current,"arpx", "3")
//! i -- makechange(current,"alev", "3")
//! i -- makechange(current,"arlv", "1")
//! i -- makechange(current,"ahky", "H")
//! i makechange(current,"arhk", "X")
//! i makechange(current,"aret", "Learn Ma|cff109da9x|r Bash - [|cffffcc00Level ".. string.char(37) .."d|r]")
//! i makechange(current,"arut", "|cffc0c0c0Gives a percent chance to deal extra damage and push target. Sliding unit deals splash bash that slightly pushes units and deals 30% damage from original bash. |r|n|n|cffffcc00Level 1|r - 15% chance, 2.6x damage, 200 distance push. |n|cffffcc00Level 2|r - 17% chance, 3.4x damage, 230 distance push. |n|cffffcc00Level 3|r - 20% chance, 4.5x damage, 270 distance push.")
//! i makechange(current,"aub1","1","|cffc0c0c0Gives a 15% chance to deal extra 2.6x damage, push target 200 offset distance and deal 30% splash bash.|r")
//! i makechange(current,"aub1","2","|cffc0c0c0Gives a 17% chance to deal extra 3.4x damage, push target 230 offset distance and deal 30% splash bash.|r")
//! i makechange(current,"aub1","3","|cffc0c0c0Gives a 20% chance to deal extra 4.5x damage, push target 270 offset distance and deal 30% splash bash.|r")
//! i local i = 0
//! i for i=1, 3 do
//! i local si = tostring(i)
//! i makechange(current,"atar",si,"Enemy,Ground,Organic")
//! i -- makechange(current,"aran",si,"99999.")
//! i makechange(current,"Hbh1",si,tostring(13+(2*i)))
//! i makechange(current,"abuf",si,"MBed")
//! i makechange(current,"Hbh5",si,"1")
//! i makechange(current,"Hbh3",si,"0.")
//! i makechange(current,"atp1",si,"Max Bash - [|cffffcc00Level ".. si .."|r]")
//! i end
//! i makechange(current,"Hbh1","3","20.")
//! endexternalblock
*/
// ^
// |
// Change me from */ to // and then back to */ after saving and reopening the map
// ============================================================================================
//TESH.scrollpos=20
//TESH.alwaysfold=0
// *******************************************************************************
// Spell: Max Bash by Dark Dragon
//
// Credits: IceFrog -> Used in Dota! Idea is by someone...
// Venom -> Icon
// nerovesper -> Request
//
//
// Installation:
//
// 1. Make sure you have opend JNGPS!
// 2. Copy and paste following:
// - icons from import manager if u want to use them
// - ability max bash and while pasting set its raw code to MBsh
// - this trigger
// 3. Now as you can see this constants below are the values you need to edit to your own will
// then even more below are level data setup values. change them to your will as well
//
// * You dont need to give me credits but it would be nice that other people get credits
// * If you need extra help or found a bug in this spell feel free to comment
//
// * Enjoy!
// *******************************************************************************
// *** Include ddup.j ***
//! import "ddup.j"
// *** This is the max bash spell library ***
library MaxBash initializer Init requires DDUniversalPack
globals
// This is the raw code of max bash ability
// Default: 'MBsh'
private constant integer MAX_BASH = 'MBsh'
// Push model file
// Default: Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl
private constant string PUSH_FILE = "Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl"
// On push file (when ability is triggerd) applay this effect
// Default: Abilities\\Spells\\NightElf\\ThornsAura\\ThornsAuraDamage.mdl
private constant string ON_PUSH_FILE = "Abilities\\Spells\\NightElf\\ThornsAura\\ThornsAuraDamage.mdl"
// On which point of attacker "ON_PUSH_FILE" is added?
// Default: weapon
private constant string ATTACH_POINT = "weapon"
// This is speed in game coordinates, of how fast unit is pushed away
// defined star and end speeds of pushed unit
// Default: 300 / 150
private constant real PUSH_SPEED_START = 300.
private constant real PUSH_SPEED_END = 150.
// This is deacceleration of unit. This value must never be negative
// Default: 250
private constant real PUSH_DEACC = 250.
// Should trees be destroyed?
// Default: true
private constant boolean DESTROY_TREES = true
// Should floating text be created?
// Default: true
private constant boolean ALLOW_FLOATING_TEXT = true
// The duration of floating text in seconds
// Default: 4.25
private constant real FLOATING_TEXT_DURATION = 4.25
// *** Do not edit this vars here, edit them below ***
// {
private integer Levels = 0
private dd_color Clr = Null
private integer array BashChance[8191]
private real array BashDamage[8191]
private boolean array BashAsPerc[8191]
private real array BashPRange[8191]
// }
endglobals
// -------------------------------------------------------------
// *** Level data setup here ***
private function SetupMaxBash takes nothing returns nothing
// *** Max level of the spell ***
// Default: 3
set Levels = 3
// *** The bash chance per level ***
// Default: 15 / 17 / 20
set BashChance[1] = 15
set BashChance[2] = 17
set BashChance[3] = 20
// *** Defines should dealed damage be a percentage of
// dealed damage or should it be extra damage ***
// Default: true / true / true
set BashAsPerc[1] = true
set BashAsPerc[2] = true
set BashAsPerc[3] = true
// *** The bash damage per level ***
// Default: 3, 4.5, 6
set BashDamage[1] = 3.0
set BashDamage[2] = 4.5
set BashDamage[3] = 6.0
// *** This is the push range of unit ***
// Default: 200 / 250 / 310
set BashPRange[1] = 200.
set BashPRange[2] = 250.
set BashPRange[3] = 310.
// *** Color of floating text ***
// *** This only works if floating text is allowed ***
// *** Arguments: Red, Green, Blue, Alpha
// Default: 255, 255, 255, 225
set Clr = CreateColor(255, 255, 255, 225)
endfunction
// ======================================================================
// DO NOT EDIT BELOW IF YOU DONT KNOW JASS
// ======================================================================
// *** Keywords ***
private keyword MaxBashPeriod
// *** Main global vars ***
globals
// *** Constants ***
private constant real TREES_PICK_RANGE = 192.
private constant real DELAY = THREAD_DELAY*.001
endglobals
// ----------------------
// --- Custom types ---
// ----------------------
// *** Main Bash Type ***
private struct bash
// *** Members ***
private unit u // victim
private float dist // push distance
private float vel // current velocity
private real acc // acceleration
private dd_vector v // direction vector
private integer exec // current execution
// *** Pointer ***
implement optional dd_pointer
// *** Constructor ***
//! runtextmacro constr("create", "unit u, real rad, integer lvl")
// *** Assign members ***
set this.u = u
set this.dist = Float(lvl:BashPRange)
set this.vel = Float(PUSH_SPEED_START)
set this.acc = PUSH_DEACC*rsign(PUSH_SPEED_END-PUSH_SPEED_START)*DELAY
set this.v = CreateVectorDD(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u), rad, 0.)
// *** Pointer apply ***
call this.SetPtrObj(u)
// *** Apply member actions ***
call FloatLimits(this.vel, rmin(PUSH_SPEED_START, PUSH_SPEED_END), rmax(PUSH_SPEED_START, PUSH_SPEED_END), true)
call FloatLimits(this.dist, 0., GetFloat(this.dist), true)
//! runtextmacro endconstr()
// *** Destructor ***
//! runtextmacro destr()
call DestroyFloat(this.vel)
call DestroyFloat(this.dist)
call DestroyVectorDD(this.v)
call bash.ClrPtrObj(this.u)
set this.u = null
set this.exec = 0
//! runtextmacro enddestr()
// ---------------------------------------------------------------------------
// *** If spell is triggered at same time then (reset) ***
public method Reset takes real rad, integer lvl returns nothing
// *** Reset bash data ***
call SetFloat(this.dist, lvl:BashPRange)
call SetFloat(this.vel, PUSH_SPEED_START)
call SetVDirection(this.v, rad, 0.)
endmethod
// *** Knockback main method ***
// *** Returns true if its done movement ***
public method Push takes nothing returns boolean
// *** Acceleration apply & vector offset move & distance decrease ***
call this.vel.Add(this.acc)
call this.dist.Sub(GetFloat(this.vel)*DELAY)
call this.v.OffsetMove(GetFloat(this.vel)*DELAY)
// *** Next exec ***
set this.exec = this.exec + 1
// *** Move main unit ***
call SetUnitVPos(this.u, this.v)
// *** All distance passed? ***
return (GetFloat(this.dist) == 0.)
endmethod
// ========================================================
// *** Operators ***
public method operator victim takes nothing returns unit
return (this.u)
endmethod
public method operator vector takes nothing returns dd_vector
return (this.v)
endmethod
public method operator execution_count takes nothing returns integer
return (this.exec)
endmethod
// *** End Operators ***
// ========================================================
endstruct
// *** Clears nerby trees ***
private function EnumDests takes nothing returns nothing
if (GetWidgetLife(GetEnumDestructable()) > .405) then
if (IsDestructableTree(GetEnumDestructable())) then
call KillDestructable(GetEnumDestructable())
endif
endif
endfunction
private function TryClearTrees takes dd_vector v, integer count returns nothing
// *** If trees can be destroyed ***
if (DESTROY_TREES and QMod(count, 4) == 0) then
// *** Clear all trees ***
// === NOTE: My functions do not leak (null) boolexper's ===
call EnumDestructablesInRange(v.x, v.y, TREES_PICK_RANGE, null, function EnumDests)
endif
endfunction
// *** Loads or resets bash ***
private function BashExecute takes unit u, real ang, integer lvl returns nothing
// *** Get ptr ***
local bash pb = bash.GetPtrObj(u)
// *** Is valid? ***
if (pb == Null) then
// *** Load new bash ***
call NewThread(THREAD_DELAY, bash.create(u, ang, lvl), callback.MaxBashPeriod, true)
else
// *** Reset if in second cast ***
call pb.Reset(ang, lvl)
endif
endfunction
// -------------------------------------------------------------------
// *** Main loop function ***
private function MaxBashPeriod takes bash b returns boolean
// *** Push ***
if (b.Push()) then
// *** Done? ***
call PauseUnit(b.victim, false)
call b.delete()
return (true)
endif
// *** Clear trees if wanted ***
call TryClearTrees(b.vector, b.execution_count)
// *** Push effect ***
call DestroyEffect(AddSpecialEffect(PUSH_FILE, b.vector.x, b.vector.y))
// *** Spell not done jet ***
return (false)
endfunction
// ===================================================================
// *** Main spell function (condition) ***
private function Conditions takes nothing returns boolean
return ( GetRandomChance(GetUnitAbilityLevel(GetEventDamageSource(), MAX_BASH):BashChance) and IsDamageTypeMelee() and not IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) )
endfunction
// ===================================================================
// *** Main spell function (action) ***
private function MaxBash takes nothing returns nothing
local unit u = GetEventDamageSource() // Damages source unit
local unit v = GetTriggerUnit() // Victim unit
local integer lvl = GetUnitAbilityLevel(u, MAX_BASH) // Spell level
local real dmg = 0. // Spells extra damage
// *** No future exec ***
call DisableTrigger(GetTriggeringTrigger())
// *** Deal extra damage ***
if (lvl:BashAsPerc) then
set dmg = GetEventDamage()*lvl:BashDamage
else
set dmg = lvl:BashDamage
endif
call UnitDamageTarget(u, v, dmg, true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
// *** Apply custom effect ***
call DestroyEffect(AddSpecialEffectTarget(ON_PUSH_FILE, u, ATTACH_POINT))
// *** Floating text add? ***
if (ALLOW_FLOATING_TEXT) then
call CreateTextTagUnit(v, "+"+I2S(round(dmg)), 10., FLOATING_TEXT_DURATION, Clr)
endif
// *** Now pause the victim ***
call PauseUnit(v, true)
// *** Load main bash struct ***
call BashExecute(v, E_AngWidget(u, v)*DEGTORAD, lvl)
// *** Allow next exec ***
call EnableTrigger(GetTriggeringTrigger())
// *** Null locals ***
set u = null
set v = null
endfunction
// *** Main init function ***
private function Init takes nothing returns nothing
// ---> Register event
// ---> Add cond/act
call TriggerRegisterAnyUnitDamaged(onTriggerEvent(null, function Conditions, function MaxBash))
// *** Now call user setup ***
call SetupMaxBash()
// Preload!
call DestroyEffect(AddSpecialEffect(PUSH_FILE, 0., 0.))
call DestroyEffect(AddSpecialEffect(ON_PUSH_FILE, 0., 0.))
endfunction
endlibrary