//TESH.scrollpos=0
//TESH.alwaysfold=0
function test takes nothing returns nothing
endfunction
Name | Type | is_array | initial_value |
bm | unit | No | |
ttt | timer | No |
//==========================================================================
// 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
// -------------------------------------------------------------------------
// *** Spell: Water Explosion
// *** Autor: Dark Dragon
//
//
// * Made on Warcraft III v1.30.4
//
// -------------------------------------------------------------------------
// *** Installation ***
//
// * Copy this trigger and DD Library to your map
// * Copy abilitie 'Water Explosion' to your map
// * Export and import 'WaterExplosion.mdx' from this map to your map (Credits: JetFangInferno)
// * Modify data below to your will, WEX_RAWCODE must be matched to your 'spell rawcode' in your map
// * Add water explosion to unit you want and edit below spell data to your needs
// * Enjoy!
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// *** Main WaterExplosion library ***
//! zinc
library WaterExplosion requires DDLib
{
//********************************************************************************
// Edit following to your wish
//********************************************************************************
constant integer WEX_RAWCODE = 'WTex'; // Ability Water Explosion
constant real EXPLOSION_DELAY = .5; // How often explosions are created (in seconds)
constant integer WATER_SHOOTS = 4; // How much water shoots will happen, water sfx shoots
constant real INITIAL_EXPLOSION_OFFSET = 50.; // Extra offset to NEXT_EXPLOSION_OFFSET, on first water explosion
constant real NEXT_EXPLOSION_OFFSET = 110.; // How far does each next cycle explosion occure (linear damage/effect version)
constant real WATER_EXPLOSION_DENSITY = 160.; // Smaller numbers mean that explosions are closer together, it means how much of perimiter does it use (circular effect version)
//constant real EXPLOSION_ENHANCE_PERC = 120.; // This is correction of units being picked in percentage of NEXT_EXPLOSION_OFFSET, it means it will alternate area in which units are damaged
constant boolean DUMMY_DAMAGE = false; // Does dummy or caster deal damage?
constant boolean USE_TEXTTAG = true; // Enable or disable damage display
constant integer PROJECTILE_N = 12; // Missiles max number
constant real PROJECTILE_SIZE = 1.3; // Missiles size
constant real PROJECTILE_SPEED = 750.; // Missiles speed
constant real PROJECTILE_ARC = 360.; // Missiles max reached z
constant real PROJECTILE_RANGE = 710.; // Missiles max travel distance
constant real PROJECTILE_INIT_Z = 0.; // Missiles offset z
// This are constant defined effects
constant string EXPLOSION_DAMAGE_EFFECT = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl";
constant string EXPLOSION_DAMAGE_EFFECT_ATTACH_POINT = "origin";
constant real EXPLOSION_DAMAGE_EFFECT_INTERVAL = .33;
constant real EXPLOSION_DAMAGE_EFFECT_DURATION = 4.;
constant string WATER_EXPLOSION_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl";
constant string MISSILE_EFFECT = "Abilities\\Weapons\\WaterElementalMissile\\WaterElementalMissile.mdl";
constant string MISSILE_SPAWN_EFFECT = "war3mapImported\\WaterExplosion.mdx";
constant real MISSILE_SPAWN_EFFECT_SIZE = .5;
constant real MISSILE_SPAWN_EFFECT_ANIMATION_SPEED = 200.;
private keyword waterexp;
// *** Amount of damage dealed per level, if you use more or less than 3 levels, just delete or add additional lines in function below
function WaterDamage(integer level) -> real {
real damage[];
// -----------------------------------------
// *** edit this variables to your will
damage[1] = 70.;
damage[2] = 100.;
damage[3] = 140.;
damage[4] = 190.;
damage[5] = 260.;
// -----------------------------------------
return damage[level];
}
// *** area of effect, that is max radius that spell will effect
function WaterAoE(integer level) -> real {
real aoe[];
// -----------------------------------------
// *** edit this variables to your will
// you can use your custom aoe, but this uses n water explosions formula.
// ˇˇ
aoe[1] = INITIAL_EXPLOSION_OFFSET + NEXT_EXPLOSION_OFFSET*(4.);
aoe[2] = INITIAL_EXPLOSION_OFFSET + NEXT_EXPLOSION_OFFSET*(4.);
aoe[3] = INITIAL_EXPLOSION_OFFSET + NEXT_EXPLOSION_OFFSET*(5.);
aoe[4] = INITIAL_EXPLOSION_OFFSET + NEXT_EXPLOSION_OFFSET*(5.);
aoe[5] = INITIAL_EXPLOSION_OFFSET + NEXT_EXPLOSION_OFFSET*(6.);
// ^^
// -----------------------------------------
return aoe[level];
}
function WaterMaxDamage(integer level) -> real {
real max_damage[];
// -----------------------------------------
// *** edit this variables to your will
// the max damage spell can deal per cast, use value of -1. to make spell have no limit of damage
//
max_damage[1] = 500.;
max_damage[2] = 850.;
max_damage[3] = 1100.;
max_damage[4] = 1500.;
max_damage[5] = 1600.;
// ^^
// -----------------------------------------
return max_damage[level];
}
// *** Filtrate units to be damaged
function DamageFilter(unit filterUnit, player casterOwner) -> boolean {
return !IsUnitType(filterUnit, UNIT_TYPE_DEAD) &&
IsUnitEnemy(filterUnit, casterOwner) &&
!IsUnitType(filterUnit, UNIT_TYPE_STRUCTURE) &&
!IsUnitType(filterUnit, UNIT_TYPE_MAGIC_IMMUNE) &&
!IsUnitType(filterUnit, UNIT_TYPE_ANCIENT) &&
!IsUnitType(filterUnit, UNIT_TYPE_FLYING) &&
!DDIsUnitWard(filterUnit) &&
BlzIsUnitSelectable(filterUnit) &&
!BlzIsUnitInvulnerable(filterUnit);
}
// ==============================================================================================================
// *
// * *** Non-Editable code below ***
// *
// ==============================================================================================================
//===============================================
// Do not edit bellow if you don't know jass
//===============================================
// * Timed effect on unit
struct timeffect {
unit u;
effect e;
real timeout;
static timeffect UnitData[];
static method AddWaterEffect(unit u) {
timeffect this;
integer id = H2ID(u);
if (UnitData[H2ID(u)] == p_null) {
this = allocate();
UnitData[id] = this;
this.u = u;
this.e = AddSpecialEffectTarget(EXPLOSION_DAMAGE_EFFECT, u, EXPLOSION_DAMAGE_EFFECT_ATTACH_POINT);
DDStartTim(EXPLOSION_DAMAGE_EFFECT_INTERVAL, true, this, static method() {
timeffect te = DDTimData();
BlzPlaySpecialEffect(te.e, ANIM_TYPE_BIRTH);
te.timeout -= EXPLOSION_DAMAGE_EFFECT_INTERVAL;
if (te.timeout <= 0. || IsUnitType(te.u, UNIT_TYPE_DEAD)) {
DestroyEffect(te.e);
UnitData[H2ID(te.u)] = p_null;
te.e = null;
te.u = null;
te.destroy();
DDQuitTim();
}
});
} else {
this = UnitData[id];
}
this.timeout = EXPLOSION_DAMAGE_EFFECT_DURATION;
}
}
// *** Group of missiles
struct nexus {
ddeffect e[PROJECTILE_N];
effect se[PROJECTILE_N];
real dx[PROJECTILE_N], dy[PROJECTILE_N];
real wz, dist, max_dist;
real offset_range;
// *** Calculates missile z motion, default elliptical motion
method MissileZ() -> real {
return ((-4.*PROJECTILE_ARC/Pw_2(max_dist)) * Pw_2(dist-offset_range)) + PROJECTILE_ARC;
//return SquareRoot( Pw_2(PROJECTILE_ARC) * (1. - Pw_2(((dist-offset_range)/(max_dist/2.))-1.)) );
}
}
//--------------------------------------------------
// *** WaterExplosion main struct ***
struct waterexp {
// Objects
unit caster;
unit dummy;
real x, y;
player owner;
integer level;
integer shoots;
real radius;
real dmg, cdmg, max_dmg;
fogmodifier fm;
// ------------------------------------------
// # Methodes
// ------------------------------------------
// ------------------------------------------
// *** Write constructor ***
static method create() -> waterexp {
waterexp this = allocate();
// *** Assign members ***
this.caster = GetTriggerUnit();
this.owner = GetOwningPlayer(this.caster);
this.level = GetUnitAbilityLevel(this.caster, WEX_RAWCODE);
this.radius = INITIAL_EXPLOSION_OFFSET;
this.x = GetUnitX(this.caster);
this.y = GetUnitY(this.caster);
this.shoots = 0;
this.cdmg = 0.;
this.dmg = WaterDamage(level);
this.max_dmg = WaterMaxDamage(level);
this.fm = CreateFogModifierRadius(this.owner, FOG_OF_WAR_VISIBLE, this.x, this.y, WaterAoE(this.level) + NEXT_EXPLOSION_OFFSET, true, false);
FogModifierStart(this.fm);
static if (DUMMY_DAMAGE) {
this.dummy = DDLoadDummy();
SetUnitOwner(dummy, owner, false);
SetUnitX(this.dummy, this.x);
SetUnitY(this.dummy, this.y);
}
return (this);
}
// ------------------------------------------
// *** Write destructor ***
method destroy() {
static if (DUMMY_DAMAGE)
DDRecycleDummy(this.dummy);
FogModifierStop(this.fm);
DestroyFogModifier(this.fm);
this.fm = null;
this.caster = null;
deallocate();
}
}
//===========================================================================
// **** Init water expl ****
//===========================================================================
function onInit() {
trigger t = CreateTrigger();
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerAddCondition(t, Condition(function() -> boolean {
// Main Condition
if (GetSpellAbilityId() != WEX_RAWCODE)
// *** Skip remainig code
return false;
// *** Condition passed
DDStartTimInst(EXPLOSION_DELAY, true, waterexp.create(), function() {
// -------------------------------------
// *** locals ***
waterexp we = DDTimData();
nexus n;
integer i, max_i;
real rad;
// -------------------------------------
// ================================================================================================
// *** Load water shoots ***
// ================================================================================================
if ( we.shoots < WATER_SHOOTS ) {
// *** Increase shoots ***
we.shoots += 1;
n = nexus.create(); // Missiles
n.dist = we.radius;
n.max_dist = PROJECTILE_RANGE-we.radius;
n.offset_range = we.radius+(n.max_dist/2.);
n.wz = BlzGetUnitZ(we.caster);
// *** Add enum graphics ***
for(i=0; i < PROJECTILE_N; i+=1) {
// *** Do calcs ***
rad = i*(2.*bj_PI/PROJECTILE_N);
// *** Add missiles ***
n.dx[i] = Cos(rad);
n.dy[i] = Sin(rad);
n.e[i] = ddeffect.create(MISSILE_EFFECT, we.x + PROJECTILE_RANGE * n.dx[i], we.y + PROJECTILE_RANGE * n.dy[i], rad, PROJECTILE_SIZE);
n.dx[i] *= -DD_INTERVAL*PROJECTILE_SPEED;
n.dy[i] *= -DD_INTERVAL*PROJECTILE_SPEED;
n.se[i] = AddSpecialEffect(MISSILE_SPAWN_EFFECT, n.e[i].X, n.e[i].Y);
BlzSetSpecialEffectScale(n.se[i], MISSILE_SPAWN_EFFECT_SIZE);
BlzSetSpecialEffectTimeScale(n.se[i], MISSILE_SPAWN_EFFECT_ANIMATION_SPEED/100.);
BlzPlaySpecialEffect(n.se[i], ANIM_TYPE_STAND);
}
DDStartTim(200./MISSILE_SPAWN_EFFECT_ANIMATION_SPEED, false, n, function() {
nexus n = DDTimData();
integer i;
for(i=00; i < PROJECTILE_N; i+=01) {
DestroyEffect(n.se[i]);
n.se[i] = null;
}
if (n.e[00] == p_null)
n.destroy();
DDQuitTim();
});
// ------------------------------------------------------------
// *** Missile motion
DDStartTim(DD_INTERVAL, true, n, function() {
nexus n = DDTimData();
integer i;
// *** vars change apply ***
n.dist += DD_INTERVAL*PROJECTILE_SPEED;
// *** check if we still have a way to go ***
if (n.dist < PROJECTILE_RANGE) {
// *** Do the loop and move missiles
for(i=0; i < PROJECTILE_N; i+=1) {
n.e[i].PositionZ(n.e[i].X + n.dx[i], n.e[i].Y + n.dy[i],
n.wz + PROJECTILE_INIT_Z + n.MissileZ());
}
} else {
// *** Clear missiles and end motion ***
for(i=0; i < PROJECTILE_N; i+=1) {
n.e[i].destroy();
n.e[i] = p_null;
}
if (n.se[00] == null)
n.destroy();
DDQuitTim();
}
});
}
// ================================================================================================
// *** Start Damage and explosions ***
// ================================================================================================
// ***
// *** Increase distance
// ***
we.radius += NEXT_EXPLOSION_OFFSET;
max_i = R2I(2.*we.radius*bj_PI / WATER_EXPLOSION_DENSITY);
// -------------------------------------------
// Loop ( loads water explosion effects )
for(i=0; i < max_i; i+=1) {
rad = (i * 2.*bj_PI) / max_i;
DestroyEffect( AddSpecialEffect(WATER_EXPLOSION_EFFECT,
we.x + we.radius * Cos( rad ),
we.y + we.radius * Sin( rad )) );
}
// *** Start periodic scan for damage
DDGroupFilterArea(we.x, we.y, we.radius+NEXT_EXPLOSION_OFFSET, we, function() -> boolean {
waterexp we = DDGFilterData();
unit f = GetFilterUnit();
real life, damage = we.dmg;
effect e;
// Target unit has to be between target radius and radius - explosion offset
if (DDHypot(GetUnitX(f)-we.x, GetUnitY(f)-we.y) >= Pw_2(we.radius-NEXT_EXPLOSION_OFFSET) && DamageFilter(f, we.owner)) {
if (we.max_dmg != -1.) {
life = GetWidgetLife(f);
if (we.max_dmg-we.cdmg < damage)
damage = we.max_dmg-we.cdmg;
if (life < damage)
damage = life;
we.cdmg += damage;
}
static if (DUMMY_DAMAGE)
DDSpellDamage(we.dummy, f, we.dmg);
else
DDSpellDamage(we.caster, f, we.dmg);
static if (USE_TEXTTAG)
if (damage >= 1.)
DDNewTextTagUnit(f, "+"+I2S(R2I(damage)), 4., 10., 90., 88., 20.);
timeffect.AddWaterEffect(f);
}
f = null;
return false;
});
// ===========================================
// * End when distance is out of area
if (we.radius+1. > WaterAoE(we.level)) {
static if (USE_TEXTTAG)
DDNewTextTagUnit(we.caster, I2S(R2I(we.cdmg))+" damage dealt!", 5., 27., 84., 10., 20.);
we.destroy();
DDQuitTim();
}
// ===============================
// ---- End main timer ------
// ===============================
});
return false; // End Trigger condition
}));
// End onInit
}
}
//! endzinc
// ============================================================================================
// Lua generation of objects!
// 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 WATER_EXPLOSION_ABIL_ID = "WTex"
//! i -- ===================================================
//! i setobjecttype("abilities")
//! i createobject("AHtc", WATER_EXPLOSION_ABIL_ID)
//! i makechange(current,"anam", "Water Explosion")
//! i makechange(current,"alev", "5")
//! i makechange(current,"abpx", "3")
//! i makechange(current,"arpx", "3")
//! i makechange(current,"aart", "ReplaceableTextures\\CommandButtons\\BTNCrushingWave.blp")
//! i makechange(current,"arar", "ReplaceableTextures\\CommandButtons\\BTNCrushingWave.blp")
//! i makechange(current,"ahky", "W")
//! i makechange(current,"arhk", "W")
//! i makechange(current,"acat", nil)
//! i makechange(current,"aret", "Learn |cff1a8cd0W|rater Explosion - [|cff0080c0Level "..string.char(37).."d|r]")
//! i makechange(current,"arut", "|cff008040Deals damage in area around caster.|r|n|n|cff0080ffLevel 1|r -|cff80ffff Each explosion deals 70 damage, up to max 500 damage, in 470 aoe|r. |n|cff0080ffLevel 2|r - |cff80ffffEach explosion deals 100 damage, up to max 850 damage, in 470 aoe|r. |cff80ffff|n|r|cff0080ffLevel 3|r -|cff80ffff Each explosion deals 140 damage, up to max 1100 damage, in 610 aoe|r.|cff80ffff|n|r|cff0080ffLevel 4|r -|cff80ffff Each explosion deals 190 damage, up to max 1500 damage, in 610 aoe|r.|cff80ffff|n|r|cff0080ffLevel 5|r -|cff80ffff Each explosion deals 260 damage, up to max 1600 damage, in 610 aoe|r.")
//! i local i = 0
//! i local dmg = { "70", "100", "140", "190", "260" }
//! i local maxdmg = { "500", "850", "1100", "1500", "1600" }
//! i local aoe = { "470", "470", "610", "610", "610" }
//! i for i=1, 5 do
//! i local si = tostring(i)
//! i makechange(current,"Htc1",si,"0.")
//! i makechange(current,"aare",si,"0.")
//! i makechange(current,"Htc3",si,"0.")
//! i makechange(current,"Htc4",si,"0.")
//! i makechange(current,"abuf",si,nil)
//! i makechange(current,"ahdu",si,".01")
//! i makechange(current,"adur",si,".01")
//! i makechange(current,"amcs",si,tostring(100 + 25*i))
//! i makechange(current,"atp1",si,"|cff1a8cd0W|rater Explosion - [|cffffcc00Level "..si.."|r]")
//! i makechange(current,"aub1",si,"|cff1a8cd0Deals "..dmg[i].." damage per explosion, up to max "..maxdmg[i].." damage, in "..aoe[i].." aoe.|r")
//! i end
//! endexternalblock
*/
// ^
// |
// Change me from */ to // and then back to */ after saving and reopening the map
// ============================================================================================
//TESH.scrollpos=66
//TESH.alwaysfold=0
// -------------------------------------------------------------------------
// *** Spell: Water Explosion
// *** Date: July 19 2009
// *** Autor: Dark Dragon
//
//
// * Requires: JNGPS to compile
//
// -------------------------------------------------------------------------
// *** Installation ***
//
// * Copy this trigger in your map
// * Save your map
// * Close your map
// * Open it again
// * Add water explosion to unit you want
// -------------------------------------------------------------------------
// ------------------------
// *** Requires JNGPS ***
//! import "ddup.j"
// ------------------------
// ----------------------------
// *** Externals ***
//! external ObjectMerger w3a AHtc WTex anam "Water Explosion" abpx 3 arpx 3 acat _ aart "ReplaceableTextures\CommandButtons\BTNCrushingWave.blp" arar "ReplaceableTextures\CommandButtons\BTNCrushingWave.blp" arhk "W" ahky "W" aret "Learn |cff1a8cd0W|rater Explosion - [|cff0080c0Level %d|r]" arut "|cff008040Deals damage in area around caster.|r|n|n|cff0080ffLevel 1|r -|cff80ffff Each explosion deals 30 damage|r. |n|cff0080ffLevel 2|r - |cff80ffffEach explosion deals 45 damage. |n|r|cff0080ffLevel 3|r -|cff80ffff Each explosion deals 60 damage.|r" Htc1 1 0. Htc1 2 0. Htc1 3 0. Htc4 1 0. Htc4 2 0. Htc4 3 0. Htc3 1 0. Htc3 2 0. Htc3 3 0. aare 1 1. aare 2 1. aare 3 1. ahdu 1 .01 ahdu 2 .01 ahdu 3 .01 adur 1 .01 adur 2 .01 adur 3 .01 atp1 1 "|cff1a8cd0W|rater Explosion" atp1 2 "|cff1a8cd0W|rater Explosion" atp1 3 "|cff1a8cd0W|rater Explosion" aub1 1 "|cff1a8cd0Deals 30 damage per explosion.|r" aub1 2 "|cff1a8cd0Deals 45 damage per explosion.|r" aub1 3 "|cff1a8cd0Deals 60 damage per explosion.|r"
// ----------------------------
// -------------------------------------------------------------------------
// *** Main WaterExplosion library ***
library WaterExplosion initializer WaterExplosion_Main requires DDUniversalPack
//********************************************************************************
// Edit following to your wish
//********************************************************************************
globals
private constant integer WEX_RAWCODE = 'WTex' // Water Explosion
private constant integer EXPLOSION_DELAY = 330 // How often explosions are created (in miliseconds)
private constant integer WATER_SHOOTS = 1 // How much water shoots will happen
private constant boolean DAMAGE_ONCE = false // Tells even if unit is in explosion but took damage from last explosion should he be damaged again
private constant real PROJECTILE_SPEED = 900.
private constant real PROJECTILE_ARC = 200.
private constant real PROJECTILE_RANGE = 575.
// This are constant defined effects
private constant string EXPLOSION_DAMAGE_EFFECT = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl"
private constant string WATER_EXPLOSION_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
private constant string MISSILE_EFFECT = "Abilities\\Weapons\\WaterElementalMissile\\WaterElementalMissile.mdl"
// Do not edit this variables here
// edit them below in function SetupWaterExplosion
//{
private real array Damage
private real array SlowDur
private real array AOE
//}
endglobals
// Use this to setup your spell style
private function SetupWaterExplosion takes nothing returns nothing
// Max spell level:
// Default: 3
//! set MaxLevel = 3
// Spell damage
// Default:
// 30, 45, 60
set Damage[1] = 30.
set Damage[2] = 45.
set Damage[3] = 60.
// Spell area of effect
// Default:
// 425, 475, 550
set AOE[1] = 425.
set AOE[2] = 475.
set AOE[3] = 550.
// Spells slow duration in seconds
// for value of 0 slow will not be applied
// Default:
// 0, 0, 0
set SlowDur[1] = 0.
set SlowDur[2] = 0.
set SlowDur[3] = 0.
endfunction
//===============================================
// Do not edit bellow if you don't know jass
//===============================================
// *** Keywords ***
private keyword ExplosionActions
private keyword waterexp
private keyword fx
// *** Globals ***
globals
private waterexp this_waterexp = Null
private real MissileVelFactor = 0.
endglobals
// *** WaterExplosion main struct ***
struct waterexp
// Objects
unit caster
integer level
integer shoots
real dist
player owner
real x
real y
groupex g_once
// Methodes
// Write constructor
static method create takes nothing returns waterexp
local waterexp this = waterexp.allocate()
// *** Assign members ***
set this.caster = GetTriggerUnit()
set this.owner = GetOwningPlayer(this.caster)
set this.level = GetUnitAbilityLevel(this.caster, WEX_RAWCODE)
set this.dist = 50.
set this.x = GetUnitX(this.caster)
set this.y = GetUnitY(this.caster)
set this.shoots = 0
if (DAMAGE_ONCE) then
set this.g_once = CreateGroupEx()
endif
return (this)
endmethod
// Write destructor
private method onDestroy takes nothing returns nothing
set this.caster = null
set this.owner = null
set this.level = 0
set this.dist = 0.
if (DAMAGE_ONCE) then
call DestroyGroupEx(this.g_once)
set this.g_once = Null
endif
endmethod
endstruct
// *** Nexus type ***
private type nexus extends graphic array [12]
// *** Nexus timed blow ***
private function NexusBlowAfter takes nothing returns nothing
local nexus n = nexus(GetSleepData())
local integer i
//! runtextmacro FORX("i", "0", "nexus.size", "call n[i].destroy()")
call n.destroy()
endfunction
private function DestroyNexusTimed takes nexus n, real time returns nothing
call SleepEx(time, function NexusBlowAfter, integer(n))
endfunction
//===========================================================================
// This is a filter which allows only special units to be effected by spell
// Damage and extra effects
private function DmgGroupFilter takes nothing returns nothing
local unit f = GetFilterUnitEx()
local waterexp we = this_waterexp
local boolean result = not IsUnitDead(f) and IsUnitEnemy(f, we.owner) and not IsUnitType(f, UNIT_TYPE_STRUCTURE) and not IsUnitType(f, UNIT_TYPE_MAGIC_IMMUNE) and hypot(GetUnitX(f)-we.x, GetUnitY(f)-we.y) >= (we.dist-150.)*(we.dist-150.)
if (result) then
if (DAMAGE_ONCE) then
if (not GroupExContains(we.g_once, f)) then
call GroupExAddUnit(we.g_once, f)
else
set f = null
return
endif
endif
// Damage
call UnitDamageTarget(we.caster, f, Damage[we.level], true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_COLD, WEAPON_TYPE_WHOKNOWS)
// Slow
//if (GetUnitAbilityLevel(f, 'Bfro') == 0) then
call SlowUnit(f, we.level:SlowDur)
//endif
// Effects
//call DestroyEffect( AddSpecialEffectTarget( EXPLOSION_DAMAGE_EFFECT, f, "origin" ) )
call DestroyEffect( AddSpecialEffectTarget( EXPLOSION_DAMAGE_EFFECT, f, "head" ) )
endif
set f = null
endfunction
// *** math function f(x) ***
private constant function fx takes real x returns real
return PROJECTILE_ARC-(rabs(x-(PROJECTILE_RANGE*0.5))*((2.*PROJECTILE_ARC)/PROJECTILE_RANGE))
endfunction
//Explosions loop actions
private function ExplosionActions takes waterexp we returns boolean
//Init local variables
local integer i = 0
local integer j = 0
local real rad = 0.
local real x = 0.
local real y = 0.
local nexus n = Null
// Waves shoot
if ( we.shoots < WATER_SHOOTS ) then
// *** Allocate dynamic array ***
set n = nexus.create()
// *** Add enum graphics ***
//! runtextmacro FOR("i", "0", "nexus.size")
// *** Do calcs ***
set rad = i*(bj_PI/6.)
// *** Add graphic ***
set n[i] = AddGraphic(MISSILE_EFFECT, we.x + 50. * Cos(rad), we.y + 50. * Sin(rad), 60., rad*RADTODEG, 1.1)
call ForceGraphicMovement(n[i], PROJECTILE_SPEED*MissileVelFactor, 0., PROJECTILE_SPEED, PROJECTILE_RANGE, rad, PI_HALF, math.fx)
//! runtextmacro ENDFOR()
// *** Nexus blow ***
call DestroyNexusTimed(n, (PROJECTILE_RANGE/PROJECTILE_SPEED)+0.1)
set we.shoots = we.shoots + 1
endif
//Increase distance
set we.dist = we.dist + 100.
set j = round((we.dist * PI) / 75.)
// Loop ( loads effects and deals damage )
//! runtextmacro FOR("i", "0", "j")
set rad = (i * TWO_PI) / j
set x = we.x + we.dist * Cos( rad )
set y = we.y + we.dist * Sin( rad )
call DestroyEffect( AddSpecialEffect( WATER_EXPLOSION_EFFECT, x, y ) )
//! runtextmacro ENDFOR()
set this_waterexp = we
call GroupExFilterRange(we.x, we.y, we.dist+150., function DmgGroupFilter)
// End when distance is out of area
if ( we.dist >= AOE[we.level] ) then
call we.destroy()
set this_waterexp = Null
return (true)
endif
// *** return null ***
return (false)
endfunction
// Main Condition
private function WaterExplCond takes nothing returns nothing
if (GetSpellAbilityId() == WEX_RAWCODE) then
// *** Create water explosion ***
call NewThread(EXPLOSION_DELAY, integer(waterexp.create()), callback.ExplosionActions, true)
endif
endfunction
//===========================================================================
//Init water expl
//===========================================================================
private function WaterExplosion_Main takes nothing returns nothing
local dd_parab p
// *** Load on cast trigger ***
call onSpellEffectRegister(function WaterExplCond, null)
// Loads spell user style data
call SetupWaterExplosion()
// *** Velocity factor ***
set p = dd_parab.create(PROJECTILE_RANGE, PROJECTILE_ARC)
set MissileVelFactor = PROJECTILE_RANGE/p.max_len
call p.delete()
//Preloader
call Preload(EXPLOSION_DAMAGE_EFFECT)
call Preload(WATER_EXPLOSION_EFFECT)
call Preload(MISSILE_EFFECT)
endfunction
endlibrary