Moderator
M
Moderator
PurplePoot: Good enough, then, I s'pose. (I'll let you off on the MPI)
~approved~
~approved~
//==========================================================================
// Dark Dragon Library Code v1.3
//
// * Made on Warcraft III v1.30.4
//
// Installation:
//
// 1) Export instantdummy.mdx from this map and import it to your map, leave path at "war3mapImported/instantdummy.mdx"
// 2) Copy this trigger to your map, save your map and then change below line "// external ... " or copy "DD Dummy" and paste it in your map
// 3) Copy and paste "Unit Chill" ability from this map to your map
// 4) Match the rawcodes below to your map or use same ones as below
//
// Credits:
// ('Vexorian' - dummy.mdx)
//============================================================================
// *** Change "// external" to "//! external", save your map, close map, change back from "//!" to "//" and save map again.
// *** This will create dummy in your map
//
// ==================================
// external ObjectMerger w3u ushd dumy uabi "Aloc,Amrf" uble 0 ucbs 0 ucpt 0 umxp 0 umxr 0 umdl "war3mapImported\instantdummy.mdx" ushu "None" umvh 0 umvs 1 umas 1 umis 1 ucol 0 ufoo 0 uhom 1 umpi 10000 umpm 10000 usid 1 usin 1 unam "DD Dummy"
// ==================================
//! zinc
library DDLib requires optional TimerUtils, optional GroupUtils
{
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// *** Lib constants ***
public {
// ----------------------------------------------------------------------
// * Start modify/match rawcodes to your map
constant integer DD_DUMMYCODE = 'dumy';
constant integer DD_ABILITY_CROWN_FORM = 'Amrf';
constant integer DD_CHILL = 'Achl';
constant integer DD_CHILL_BUFF = 'Bfro';
// * End modify
// ----------------------------------------------------------------------
constant integer p_null = (0x0);
constant real DD_INTERVAL = .017;
// map min and max coords
real DDMinX = 0.;
real DDMinY = 0.;
real DDMaxX = 0.;
real DDMaxY = 0.;
}
private {
constant integer HARVEST_ID = 'Ahrl';
constant real TRIGGER_REFRESH_RATE = (60.)*3.; /// damage detection trigger
unit TreeChecker = null;
trigger TempTrig = null;
integer NTrig = 0;
trigger DmgTrig[];
p_real EnumVec = p_null;
boolexpr EnumFilter = null;
sound ErrorSound = null;
timer GameElapsedTimer = null;
constant integer RND_INT_MAX_ARRAY_N = 100;
integer RndInt[], RndIntWriteN = 00, RndIntReadN = 00;
trigger TrigMouseEvent = null;
force RndGenForce = null;
real RndElapsedTime = 0.;
}
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// * types
public {
// *** pointer to list of data ***
type p_integer extends integer[8];
type p_real extends real[8];
type p_unit extends unit[8];
function H2ID(handle h) -> integer {
return GetHandleId(h) - 0x100000;
}
function New_pInteger(integer i) -> p_integer
{ p_integer p = p_integer.create(); p[0] = i; return p; }
function New_pReal(real r) -> p_real
{ p_real p = p_real.create(); p[0] = r; return p; }
function New_pUnit(unit u) -> p_unit
{ p_unit p = p_unit.create(); p[0] = u; return p; }
function pVector(real x, real y, real z) -> p_real {
p_real v = p_real.create();
v[0] = x; v[1] = y; v[2] = z;
return v;
}
// --------------------------------------------------------------------------------------
function DDMsg(string str) {
DisplayTimedTextFromPlayer(GetLocalPlayer(), 0., 0., 30., str);
}
// --------------------------------------------------------------------------------------
function DisplayErrorMsgPlayer(player p, real dur, string msg) {
if (GetLocalPlayer() == p) {
StartSound(ErrorSound);
DisplayTimedTextFromPlayer(p, 0., 0., dur, "|cffffcc00"+ msg +"|r");
}
}
}
// -----------------------------------------------------------------------
// -> ***** private globals *****
// -----------------------------------------------------------------------
private {
location TempLoc = Location(0., 0.);
timer TimerStack[];
integer TimN = 0;
group GroupStack[];
integer GrpN = 0;
unit DummyStack[];
integer DumN = 0;
integer TimTicks[];
integer TimData[];
timer TimTim1[];
timer TimTim2[];
integer UnitStackData = 0;
unit UnitStack[];
integer US_N = 0;
public hashtable DDHT = InitHashtable();
}
// -----------------------------------------------------------------------
public {
// *** Global funcs
function Pw_2(real x) -> real {
return x*x;
}
function DDHypot(real x, real y) -> real {
return (x*x) + (y*y);
}
function DDTerrZ(real x, real y) -> real {
MoveLocation(TempLoc, x, y);
return GetLocationZ(TempLoc);
}
function DDWidgetTerrZ(widget w) -> real {
MoveLocation(TempLoc, GetWidgetX(w), GetWidgetY(w));
return GetLocationZ(TempLoc);
}
function DDEffectTerrZ(effect e) -> real {
MoveLocation(TempLoc, BlzGetLocalSpecialEffectX(e), BlzGetLocalSpecialEffectY(e));
return GetLocationZ(TempLoc);
}
function DDGetUnitZ(unit u) -> real {
return BlzGetUnitZ(u) + GetUnitFlyHeight(u);
}
// =================================================================
// *** Save Handle data ****
// =================================================================
function DDSet(handle h, integer id, integer val) {
SaveInteger(DDHT, id+1, GetHandleId(h), val);
}
function DDGet(handle h, integer id) -> integer {
return LoadInteger(DDHT, id+1, GetHandleId(h));
}
function DDHas(handle h, integer id) -> boolean {
return HaveSavedInteger(DDHT, id+1, GetHandleId(h));
}
function DDFlush(integer id) {
FlushChildHashtable(DDHT, id+1);
}
// =================================================================
// *** Timer Handling ****
// =================================================================
// -> check if timer is recycled
function DDIsTimRecycled(timer t) -> boolean {
integer i;
for(i=TimN-01; i >= 00; i-=01)
if (TimerStack[i] == t)
return true;
return false;
}
// -> Load timer for recycling
function DDLoadTim() -> timer {
static if (LIBRARY_TimerUtils) { return NewTimer(); }
else {
if (TimN > 0) {
TimN -= 1;
return TimerStack[TimN];
}
return CreateTimer();
}
}
// -> recycle loaded timer
function DDRecycleTim(timer t) {
static if (LIBRARY_TimerUtils) { ReleaseTimer(t); }
else {
static if (DEBUG_MODE)
if (DDIsTimRecycled(t)) {
DDMsg("Multiple recycle of timer!");
return;
}
TimerStack[TimN] = t;
TimN += 1;
}
}
// ** Get data stored on expired timer
function DDTimData() -> integer {
return TimData[H2ID(GetExpiredTimer())];
}
// *** Custom timer tick
function DDCTimTick(timer t) -> integer {
return TimTicks[H2ID(t)];
}
// *** Gets current tick and adds next one ***
function DDTimTick() -> integer {
integer id = H2ID(GetExpiredTimer());
TimTicks[id] += 01;
return TimTicks[id];
}
// ** start timer with data storage
function DDStartTim(real secs, boolean looping, integer pdata, code func) -> timer {
timer t = DDLoadTim();
TimData[H2ID(t)] = pdata;
TimerStart(t, secs, looping, func);
return t;
}
// ** start timer with data storage, and launches it instantly
function DDStartTimInst(real secs, boolean looping, integer pdata, code func) -> timer {
timer t1 = DDLoadTim(), t2 = DDLoadTim(), t3 = DDLoadTim();
TimData[H2ID(t2)] = pdata;
TimerStart(t2, 0., false, func);
TimTim1[H2ID(t3)] = t1;
TimTim2[H2ID(t3)] = t2;
TimerStart(t3, .005, false, function() {
timer t = GetExpiredTimer();
integer id = H2ID(t);
PauseTimer(t);
static if (LIBRARY_TimerUtils)
ReleaseTimer(t);
else {
TimerStack[TimN] = t;
TimN += 1;
}
t = TimTim2[id];
if (DDIsTimRecycled(t))
t = TimTim1[id];
TimTicks[H2ID(t)] = 00;
PauseTimer(t);
static if (LIBRARY_TimerUtils)
ReleaseTimer(t);
else {
TimerStack[TimN] = t;
TimN += 1;
}
});
TimData[H2ID(t1)] = pdata;
TimerStart(t1, secs, looping, func);
return t1;
}
// *** Quit expired timer ***
function DDQuitTim() {
timer t = GetExpiredTimer();
TimTicks[H2ID(t)] = 00;
PauseTimer(t);
static if (LIBRARY_TimerUtils)
ReleaseTimer(t);
else {
TimerStack[TimN] = t;
TimN += 1;
}
}
function DDQuitTimEx(timer t) {
TimTicks[H2ID(t)] = 00;
PauseTimer(t);
static if (LIBRARY_TimerUtils)
ReleaseTimer(t);
else {
TimerStack[TimN] = t;
TimN += 1;
}
}
// =================================================================
// *** Group Handling ****
// =================================================================
// -> Load timer for recycling
function DDLoadGroup() -> group {
static if (LIBRARY_GroupUtils) { return NewGroup(); }
else {
if (GrpN > 0) {
GrpN -= 1;
return GroupStack[GrpN];
}
return CreateGroup();
}
}
// -> Recycle group
function DDRecycleGroup(group g) {
static if (LIBRARY_GroupUtils) { ReleaseGroup(g); }
else {
GroupClear(g);
GroupStack[GrpN] = g;
GrpN += 1;
}
}
// --------------------------------------------------------
// -- Quick filter area
private integer GroupFilterData = 00;
function DDGroupFilterArea(real x, real y, real radius, integer data, code func) {
group g = DDLoadGroup();
GroupFilterData = data;
GroupEnumUnitsInRange(g, x, y, radius, Filter(func));
DDRecycleGroup(g);
}
// --------------------------------------------------------
// -- Quick filter rect
function DDGroupFilterRect(rect r, integer data, code func) {
group g = DDLoadGroup();
GroupFilterData = data;
GroupEnumUnitsInRect(g, r, Filter(func));
DDRecycleGroup(g);
}
function DDGFilterData() -> integer {
return GroupFilterData;
}
function DDGFilterDataSet(integer data) {
GroupFilterData = data;
}
// --------------------------------------------------------
// *** Filtrates and fills units in to memory
function DDGroupFillMemArea(real x, real y, real radius, integer data, code filter) {
group g = DDLoadGroup();
boolexpr exp = And(Filter(filter), Filter(function() -> boolean {
UnitStack[US_N] = GetFilterUnit();
US_N += 1;
return false;
}));
US_N = 0;
UnitStack[0] = null;
UnitStackData = data;
GroupEnumUnitsInRange(g, x, y, radius, exp);
DDRecycleGroup(g);
DestroyBoolExpr(exp);
exp = null;
}
function DDGroupFillMemRect(rect r, integer data, code filter) {
group g = DDLoadGroup();
boolexpr exp = And(Filter(filter), Filter(function() -> boolean {
UnitStack[US_N] = GetFilterUnit();
US_N += 1;
return false;
}));
US_N = 0;
UnitStack[0] = null;
UnitStackData = data;
GroupEnumUnitsInRect(g, r, exp);
DDRecycleGroup(g);
DestroyBoolExpr(exp);
exp = null;
}
function DDMemUnitN() -> integer { return US_N; }
function DDMemUnitData() -> integer { return UnitStackData; }
function DDMemUnit(integer index) -> unit {
if (US_N == 0) return null;
debug {
if (index < 0) {
BJDebugMsg("DDMemUnit: index less than 0");
index = 0;
} else if (index >= US_N) {
BJDebugMsg("DDMemUnit: index greater than units alloc size");
index = 0;
}
}
return UnitStack[index];
}
// --------------------------------------------------------
// --------------------------------------------------------
// ***
// =================================================================
// *** Dummy Handling ****
// =================================================================
// -> Load dummy for recycling
function DDLoadDummy() -> unit {
if (DumN > 0) {
DumN -= 1;
PauseUnit(DummyStack[DumN], false);
return DummyStack[DumN];
}
return CreateUnit(Player(0xF), DD_DUMMYCODE, DDMaxX, DDMaxY, 0.);
}
// *** prepares/setups dummy for spell casting
function DDLoadSpellDummy(player owner, real x, real y, integer abil, integer abilLevel) -> unit {
unit dummy = DDLoadDummy();
SetUnitOwner(dummy, owner, false);
SetUnitX(dummy, x);
SetUnitY(dummy, y);
if (abil != p_null) {
UnitAddAbility(dummy, abil);
SetUnitAbilityLevel(dummy, abil, abilLevel);
}
return dummy;
}
// -> Recycle dummy
function DDRecycleDummy(unit u) {
PauseUnit(u, true);
DummyStack[DumN] = u;
DumN += 1;
}
// -> Recycle dummy timed
function DDRecycleDummyTimed(unit u, real secs) {
DDStartTim(secs, false, New_pUnit(u), function() {
DDRecycleDummy(p_unit(DDTimData())[0]);
p_unit(DDTimData()).destroy();
DDQuitTim();
});
}
// *** shares vision for timed amount, usually for dummy casting
function DDUnitShareVisionTimed(unit u, player toP, real secs) {
p_integer pi = p_integer.create();
pi[0] = New_pUnit(u);
pi[1] = GetPlayerId(toP);
UnitShareVision(u, toP, true);
DDStartTim(secs, false, pi, function() {
p_integer pi = DDTimData();
UnitShareVision(p_unit(pi[0])[0], Player(pi[1]), false);
p_unit(pi[0])[0] = null;
p_unit(pi[0]).destroy();
pi.destroy();
DDQuitTim();
});
}
// *** Remove ability timed ***
private struct uratimed {
private {
unit u;
integer abil;
}
static method create(unit whichUnit, integer id, real time) -> uratimed {
thistype this = allocate();
u = whichUnit;
abil = id;
DDStartTim(time, false, this, function() {
thistype this = DDTimData();
UnitRemoveAbility(u, abil);
DDQuitTim();
deallocate();
});
return 0;
}
}
function DDRemoveAbilityTimed(unit u, integer abil, real secs) { uratimed.create(u, abil, secs); }
function DDSpellDamage(unit u, unit v, real dmg) {
real life = GetWidgetLife(v);
real dmgfactor = 1.;
if (IsUnitType(v, UNIT_TYPE_HERO)) {
if (UnitHasItemOfTypeBJ(v, 'brac'))
dmgfactor = .5;
else
dmgfactor = .75;
}
if (life > dmg*dmgfactor) {
SetWidgetLife(v, life-(dmg*dmgfactor));
} else
UnitDamageTarget(u, v, 99999., false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
}
// -------------------------------------------------------------------------------------
// *** Chills target unit
private struct chill {
unit u, dmy;
real dur;
static chill Data[];
//static key CHILL_KEY;
}
function DDUnitChill(player p, unit u, real dur) -> boolean {
//chill c = DDGet(u, chill.CHILL_KEY);
chill c = chill.Data[H2ID(u)];
unit d;
real rad;
if (c == p_null) {
c = chill.create();
c.u = u; c.dur = dur;
chill.Data[H2ID(u)] = c;
//DDSet(u, chill.CHILL_KEY, c);
d = DDLoadDummy();
c.dmy = d;
rad = GetUnitFacing(d) * bj_DEGTORAD;
SetUnitOwner(d, p, false);
UnitAddAbility(d, DD_CHILL);
SetUnitX(d, GetUnitX(u) - 20.*Cos(rad));
SetUnitY(d, GetUnitY(u) - 20.*Sin(rad));
if (IssueTargetOrder(d, "frostnova", u)) {
DDStartTim(.1, true, c, function() {
chill c = DDTimData();
c.dur -= .1;
if (c.dur <= 0. || GetUnitAbilityLevel(c.u, DD_CHILL_BUFF) == 00) {
UnitRemoveAbility(c.u, DD_CHILL_BUFF);
UnitRemoveAbility(c.dmy, DD_CHILL);
DDRecycleDummy(c.dmy);
chill.Data[H2ID(c.u)] = p_null;
//DDSet(c.u, chill.CHILL_KEY, p_null);
c.u = null;
c.destroy();
DDQuitTim();
}
});
return true;
}
return false;
}
c.dur = dur;
return true;
}
// ------------------------------------------------------------------------------------------------
struct fade {
unit u;
real trans;
real rate, e_trans, dur;
static constant real INTERVAL = .1;
static method create(unit u, real dur, real s_trans, real e_trans) -> fade {
fade this = allocate();
this.u = u;
this.trans = s_trans;
this.rate = ((e_trans-s_trans)/dur)*fade.INTERVAL;
this.e_trans = e_trans;
this.dur = dur;
return this;
}
}
// *** Fades unit over time ***
public function DDFadeUnit(unit u, integer from_alpha, integer to_alpha, real duration) {
fade f = fade.create(u,
duration,
from_alpha/2.55,
to_alpha/2.55);
SetUnitVertexColor(u, 255, 255, 255, from_alpha);
// --- Start thread ---
DDStartTim(fade.INTERVAL, true, f, function() {
fade f = DDTimData();
f.trans += f.rate;
f.dur -= fade.INTERVAL;
SetUnitVertexColor(f.u, 255, 255, 255, R2I(f.trans*2.55));
if (f.dur < 0.) {
SetUnitVertexColor(f.u, 255, 255, 255, R2I(f.e_trans*2.55));
f.u = null;
f.destroy();
DDQuitTim();
}
});
}
// ------------------------------------------------------------------------------------------------
// Check if unit is invulnerable
function DDIsUnitInvulnerable(unit u) -> boolean {
unit d = DDLoadDummy();
real hp = GetWidgetLife(u);
boolean flag;
UnitDamageTarget(d, u, 1., true, false, null, null, null);
flag = GetWidgetLife(u) == hp;
SetWidgetLife(u, hp);
DDRecycleDummy(d);
return flag;
}
// *** check if unit is ward
function DDIsUnitWard(unit whichUnit) -> boolean {
return GetUnitDefaultMoveSpeed(whichUnit) == 0.;
}
// =================================================================
// *** Effect Handling ****
// =================================================================
// -----------------------------------------------
// *** Define movable effect
struct ddeffect {
private {
effect e;
real fac; // facing angle in radians
real effZ; // pitch in radians
real decay;
real stepTrans, cTrans, eTrans;
static constant real EFFECT_DECAY = 5.;
}
// =========================================================================================
// =========================================================================================
static method create(string mdl, real x, real y, real facRad, real size) -> ddeffect {
ddeffect this = allocate();
this.e = AddSpecialEffect(mdl, x, y);
this.fac = facRad;
this.effZ = 0.;
BlzSetSpecialEffectRoll(this.e, facRad);
BlzSetSpecialEffectScale(this.e, size);
return this;
}
static method createZ(string mdl, real x, real y, real z, real facRad, real size) -> ddeffect {
ddeffect this = allocate();
this.e = AddSpecialEffect(mdl, x, y);
this.fac = facRad;
this.effZ = z-DDTerrZ(x, y);
BlzSetSpecialEffectRoll(this.e, facRad);
BlzSetSpecialEffectScale(this.e, size);
BlzSetSpecialEffectZ(this.e, z);
return this;
}
// -----------------
method destroy() {
DestroyEffect(this.e);
this.e = null;
deallocate();
}
// *** destroys effect timed
method destroyx(real decayTime) {
DDStartTim(decayTime, false, this, function() {
ddeffect se = DDTimData();
BlzSetSpecialEffectPosition(se.e, DDMaxX, DDMaxY, 0.);
DestroyEffect(se.e);
se.e = null;
se.deallocate();
DDQuitTim();
});
}
// =========================================================================================
// =========================================================================================
method operator Z=(real z) { BlzSetSpecialEffectZ(this.e, z); }
method operator X() -> real { return BlzGetLocalSpecialEffectX(this.e); }
method operator Y() -> real { return BlzGetLocalSpecialEffectY(this.e); }
method operator Z() -> real { return BlzGetLocalSpecialEffectZ(this.e); }
method operator WZ() -> real { return DDEffectTerrZ(this.e); }
method operator Height() -> real { return this.Z-this.WZ; }
method operator Facing=(real facRad) { BlzSetSpecialEffectRoll(this.e, facRad); this.fac = facRad; }
method operator Facing() -> real { return this.fac; }
method Position(real x, real y) { BlzSetSpecialEffectPosition(this.e, x, y, this.effZ+this.WZ); }
method PositionZ(real x, real y, real z) { BlzSetSpecialEffectPosition(this.e, x, y, z); }
method Animation(animtype at) { BlzPlaySpecialEffect(this.e, at); }
method AnimationSpeed(real animSpeed) { BlzSetSpecialEffectTimeScale(this.e, animSpeed/100.); }
//method operator Pitch=(integer pitch) { SetUnitAnimationByIndex(u, pitch); }
//method Face(widget w) { Facing = Atan2(GetWidgetY(w)-Y, GetWidgetX(w)-X)*bj_RADTODEG; }
method Fade(real startTransparency, real endTransparency, real duration) {
this.cTrans = startTransparency;
this.eTrans = endTransparency;
this.stepTrans = .1*(endTransparency-startTransparency) / duration;
BlzSetSpecialEffectAlpha(this.e, R2I(startTransparency*2.55));
DDStartTim(.1, true, this, function() {
ddeffect dde = DDTimData();
dde.cTrans += dde.stepTrans;
if (dde.stepTrans > 0.)
if (dde.cTrans >= dde.eTrans) {
BlzSetSpecialEffectAlpha(dde.e, R2I(dde.eTrans*2.55));
DDQuitTim();
return;
}
else
if (dde.cTrans <= dde.eTrans) {
BlzSetSpecialEffectAlpha(dde.e, R2I(dde.eTrans*2.55));
DDQuitTim();
return;
}
BlzSetSpecialEffectAlpha(dde.e, R2I(dde.cTrans*2.55));
});
}
}
private type timedeffect extends effect[01];
function DDDestroyEffectTimed(effect e, real secs) {
timedeffect te = timedeffect.create();
te[00] = e;
DDStartTim(secs, true, te, function() {
timedeffect te = DDTimData();
DestroyEffect(te[00]);
te.destroy();
DDQuitTim();
});
}
}
// ----------------------------------------------------------------------------
// *** Main damage detection function, registers any damage dealt to units ***
public function DDTriggerRegisterAnyUnitDamaged(trigger t) {
DmgTrig[NTrig] = t;
NTrig += 1;
}
function InitDamageDetection() {
code c = function() {
if (TempTrig != null)
DestroyTrigger(TempTrig);
TempTrig = CreateTrigger();
TriggerRegisterEnterRectSimple(TempTrig, bj_mapInitialPlayableArea);
TriggerAddCondition(TempTrig, function() -> boolean {
integer i;
// *** Check if we need to exec triggers or register an unit ***
if (GetTriggerEventId() == EVENT_UNIT_DAMAGED) {
for(i=0; i < NTrig; i+=1)
if (IsTriggerEnabled(DmgTrig[i]))
if (TriggerEvaluate(DmgTrig[i]))
TriggerExecute(DmgTrig[i]);
}
else
// *** Register new unit ***
TriggerRegisterUnitEvent(GetTriggeringTrigger(),
GetTriggerUnit(),
EVENT_UNIT_DAMAGED);
return false;
});
DDGroupFilterRect(bj_mapInitialPlayableArea, 00, function() -> boolean {
TriggerRegisterUnitEvent(TempTrig, GetFilterUnit(), EVENT_UNIT_DAMAGED);
return false;
});
};
trigger t = CreateTrigger();
TriggerAddAction(t, c);
TriggerExecute(t);
DestroyTrigger(t);
TimerStart(CreateTimer(), TRIGGER_REFRESH_RATE, true, c);
t = null;
}
// ---------------------------------------------------------------------------------
// *** Main enum dests in range function ***
public function DDEnumDestsInRange(p_real vec, real radius, boolexpr filter, code pfunc) {
rect r = Rect(vec[0]-radius, vec[1]-radius, vec[0]+radius, vec[1]+radius);
EnumVec[0] = vec[0];
EnumVec[1] = vec[1];
EnumVec[2] = radius;
if (filter != null) filter = And(EnumFilter, filter);
else filter = EnumFilter;
EnumDestructablesInRect(r, filter, pfunc);
if (filter != EnumFilter) { DestroyBoolExpr(filter); filter = null; }
RemoveRect(r);
r = null;
}
function InitEnumDests() {
EnumVec = p_real.create();
EnumFilter = Filter(function() -> boolean {
return Pw_2(EnumVec[0]-GetDestructableX(GetFilterDestructable())) + Pw_2(EnumVec[1]-GetDestructableY(GetFilterDestructable())) < Pw_2(EnumVec[2]);
});
}
// --------------------------------------------------------------------------------------
// *** checks is destruct tree ***
public function DDIsDestructableTree(destructable d) -> boolean {
if (d != null) {
PauseUnit(TreeChecker, false);
if (IssueTargetOrder(TreeChecker, "harvest", d)) {
PauseUnit(TreeChecker, true);
return true;
}
PauseUnit(TreeChecker, true);
}
return false;
}
function InitDestTreeCheck() {
TreeChecker = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), DD_DUMMYCODE, DDMaxX, DDMaxY, 0.);
UnitAddAbility(TreeChecker, HARVEST_ID);
PauseUnit(TreeChecker, true);
}
// --------------------------------------------------------------------------------------
public function DDNewTextTagUnit(unit whichUnit, string text, real dur, real red, real green, real blue, real transparency) {
CreateTextTagUnitBJ( text, whichUnit, 0., 11.00, red, green, blue, transparency );
SetTextTagPermanentBJ( bj_lastCreatedTextTag, false );
SetTextTagVelocityBJ( bj_lastCreatedTextTag, 48.00, 90 );
SetTextTagFadepointBJ( bj_lastCreatedTextTag, dur-1.25 );
SetTextTagLifespanBJ( bj_lastCreatedTextTag, dur );
}
// --------------------------------------------------------------------------------------
struct cameranoisedata {
player p[12];
integer n=00;
}
public function DDCameraSetSourceNoiseForPlayers(real x, real y, real mag, real vel, real maxDist, real duration) {
integer i;
real d;
cameranoisedata cnd = cameranoisedata.create();
for (i=00; i < bj_MAX_PLAYERS; i+=01) {
if (GetLocalPlayer() == Player(i)) {
d = SquareRoot( Pw_2(GetCameraTargetPositionX()-x) + Pw_2(GetCameraTargetPositionY()-y) );
if (d < maxDist) {
cnd.p[cnd.n] = Player(i);
CameraSetSourceNoise(mag-(d*(mag/maxDist)), vel-(d*(vel/maxDist)));
CameraSetTargetNoise(mag-(d*(mag/maxDist)), vel-(d*(vel/maxDist)));
cnd.n += 01;
}
}
}
DDStartTim(duration, false, cnd, function() {
cameranoisedata cnd = DDTimData();
while(cnd.n != 00) {
cnd.n -= 01;
if (GetLocalPlayer() == cnd.p[cnd.n])
CameraSetSourceNoise(0., 0.);
CameraSetTargetNoise(0., 0.);
}
cnd.destroy();
DDQuitTim();
});
}
// --------------------------------------------------------------------------------------
hashtable GenSndTable = null;
public function DDGenericSound(string file, real vol, real x, real y, real mxDist, real pitch) {
sound s;
real d;
integer i;
integer snd_n, sh;
// Play sound and shake camera for players within spell cast range
for (i=00; i < bj_MAX_PLAYERS; i+=01) {
if (GetLocalPlayer() == Player(i)) {
d = SquareRoot( Pw_2(GetCameraTargetPositionX()-x) + Pw_2(GetCameraTargetPositionY()-y) );
if (d < mxDist) {
sh = StringHash(file);
snd_n = LoadInteger(GenSndTable, sh, 04);
s = LoadSoundHandle(GenSndTable, sh, snd_n);
if (s == null) {
s = CreateSound(file, false, false, false, 10, 10, "");
SaveSoundHandle(GenSndTable, sh, snd_n, s);
} else if (GetSoundIsPlaying(s)) {
StopSound(s, false, false);
}
SetSoundPitch(s, pitch);
SetSoundVolume(s, R2I((vol-d*(vol/mxDist))*1.27));
StartSound(s);
snd_n += 01;
if (snd_n == 04)
snd_n = 00;
SaveInteger(GenSndTable, sh, 04, snd_n);
}
}
}
}
public function DDGetGameElapsedTime() -> real {
return TimerGetElapsed(GameElapsedTimer);
}
public function DDGetRndReal(real min, real max) -> real {
real rnd = ((max-min)/1000000.)*I2R(RndInt[RndIntReadN]);
debug if (max > 1000000.)
DDMsg("ERROR: \"DDGetRndNumber\" - 'max' variable is greater than 1000000!");
RndIntReadN += 01; if (RndIntReadN == RND_INT_MAX_ARRAY_N) RndIntReadN = 00;
return min + rnd;
}
public function DDGetRndInt(integer min, integer max) -> integer {
return R2I( DDGetRndReal(I2R(min), I2R(max)) );
}
// ====================================================================
function onInit() {
InitDamageDetection();
InitDestTreeCheck();
InitEnumDests();
DDMinX = GetRectMinX(bj_mapInitialPlayableArea);
DDMinY = GetRectMinY(bj_mapInitialPlayableArea);
DDMaxX = GetRectMaxX(bj_mapInitialPlayableArea);
DDMaxY = GetRectMaxY(bj_mapInitialPlayableArea);
GenSndTable = InitHashtable();
ErrorSound = CreateSound( "Sound\\Interface\\Error.wav", false, false, false, 10, 10, "" );
SetSoundParamsFromLabel( ErrorSound, "InterfaceError" );
SetSoundDuration( ErrorSound, 614 );
SetSoundVolume(ErrorSound, 127);
GameElapsedTimer = CreateTimer();
TimerStart(GameElapsedTimer, 10800., false, null);
for(RndIntWriteN=00; RndIntWriteN < RND_INT_MAX_ARRAY_N; RndIntWriteN+=01)
RndInt[RndIntWriteN] = GetRandomInt(00, 1000000);
RndIntWriteN = 00;
RndGenForce = CreateForce();
TrigMouseEvent = CreateTrigger();
ForForce(bj_FORCE_ALL_PLAYERS, function() {
if (GetPlayerController(GetEnumPlayer()) == MAP_CONTROL_USER)
TriggerRegisterPlayerEvent(TrigMouseEvent, GetEnumPlayer(), EVENT_PLAYER_MOUSE_MOVE);
});
TriggerAddCondition(TrigMouseEvent, Condition(function() -> boolean {
real mouseN;
boolean xFirst = GetRandomInt(00, 01) == 01;
if (!IsPlayerInForce(GetTriggerPlayer(), RndGenForce)) {
// example: input x = 578.4571496
// output rnd n = 4571498
if (xFirst)
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
else
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
if (mouseN == 0.)
return false;
//mouseN *= 100.;
RndInt[RndIntWriteN] = R2I((mouseN - I2R(R2I(mouseN))) * 1000.);
//DDMsg(I2S(RndInt[RndIntWriteN]));
//RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
if (xFirst)
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
else
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
RndInt[RndIntWriteN] += R2I((mouseN - I2R(R2I(mouseN))) * 1000.)*1000;
//DDMsg(I2S(RndInt[RndIntWriteN]));
RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
ForceAddPlayer(RndGenForce, GetTriggerPlayer());
}
if (DDGetGameElapsedTime()-RndElapsedTime > .125) {
ForceClear(RndGenForce);
RndElapsedTime = DDGetGameElapsedTime();
}
return false;
}));
}
}
//! endzinc
//TESH.scrollpos=0
//TESH.alwaysfold=0
// =====================================================================================================
// * Spell - Shimmering Portal
// by Dark Dragon
//
//
// Installation:
//
// (1) Install "DD Library" to your map if you have not done so already from my previous spells
// (2) Copy "Shimmering Portal" trigger to your map
// (3) Copy "Shimmering Portal" and "Portal (Dummy)" to your map
// (4) Edit spell to your will in object editor and here below in trigger editor
// (5) Enjoy!
// =====================================================================================================
//! zinc
library ShimmeringPortal requires DDLib
{
// =============================================================
// *** Spell editable constants ***
// =============================================================
// ==========================================================================
//
// *** Primary Spell Constants ***
//
// ==========================================================================
// ----------------------------------------------------------------
// *** The id of shimmering portal spell
constant integer SPELL_ID = 'SPrt';
// ----------------------------------------------------------------
// *** The id of shimmering portal dummy
constant integer PORTAL_ID = 'port';
// ----------------------------------------------------------------
// *** The length of portal, shrinking the value will force units to enter closer to center of portal
constant real PORTAL_LENGTH = 450.;
// ----------------------------------------------------------------
// *** Width of portal, units will enter the portal more far away if value is increased,
// decreassing this value too much can make unit pass portal faster than check interval can scan, thous causing spell to bug
constant real PORTAL_WIDTH = 110.;
// ----------------------------------------------------------------
// *** Units that are teleported to target portal, will walk away from there position offseted by random value between this (min and max)
constant real UNIT_PORT_WALK_OFFSET_MIN = 200.;
constant real UNIT_PORT_WALK_OFFSET_MAX = 350.;
// ----------------------------------------------------------------
// *** The offset of first portal near caster
constant real FIRST_PORTAL_OFFSET = 300.;
// ----------------------------------------------------------------
// *** The minimum casting range for shimmering portal spell
constant real SECOND_PORTAL_MIN_RANGE = 1000.;
// ----------------------------------------------------------------
// *** Time it takes to destroy portal after it expires or last unit exits
constant real DESTROY_PORTAL_DELAY = .7;
// ----------------------------------------------------------------
// *** The warning message, displayed in red color, and sound played with it, volume is in percentage
constant string PORTAL_WARNING_MESSAGE = "Warning: Too close cast of Shimmering portal!";
constant string PORTAL_WARNING_SOUND_FILE = "Sound\\Interface\\Error.wav";
constant real PORTAL_WARNING_SOUND_VOLUME = 90.;
// ----------------------------------------------------------------
// *** The duration of warning message
constant real PORTAL_WARNING_MESSAGE_DURATION = 5.;
// ----------------------------------------------------------------
// *** This is animation of dummy portal, birth duration must be equeal to spell channel time
constant string PORTAL_ANIM_BIRTH = "birth";
constant string PORTAL_ANIM_DEATH = "death";
constant string PORTAL_ANIM_STAND = "stand";
constant real PORTAL_ANIM_BIRTH_DURATION = 7.5;
// ----------------------------------------------------------------
// *** How often to scan for units entry, greater value slows down CPU, while too low can cause units entered to not be registerd
constant real PORT_CHECK_INTERVAL = .1;
// ----------------------------------------------------------------
// *** Prevents units that are teleported to target portal to be teleported back, causing infinite loop of teleporting,
// this timing says that any unit will not be able to use any portals (MUI included)
constant real UNIT_NEXT_PORT_DELAY = 2.;
// ----------------------------------------------------------------
// *** Portal custom sounds files and volume in percentage
constant real SOUND_MAX_DISTANCE = 3500.;
constant string PORTAL_BIRTH_SOUND_FILE = "Sound\\Ambient\\DoodadEffects\\ShimmeringPortalBirth.wav";
constant real PORTAL_BIRTH_SOUND_VOLUME = 95.;
constant string PORTAL_DEATH_SOUND_FILE = "Sound\\Ambient\\DoodadEffects\\ShimmeringPortalDeath.wav";
constant real PORTAL_DEATH_SOUND_VOLUME = 95.;
constant string PORTAL_ENTRANCE_SOUND_FILE = "Sound\\Ambient\\DoodadEffects\\ShimmeringPortalEntrance.wav";
constant real PORTAL_ENTRANCE_SOUND_VOLUME = 95.;
constant string PORTAL_AMBIENCE_SOUND_FILE = "Sound\\Ambient\\DoodadEffects\\CityScapeMagicRunesLoop1.wav";
constant real PORTAL_AMBIENCE_SOUND_VOLUME = 100.;
// ==========================================================================
//
// *** Minimap Ping Constants ***
//
// ==========================================================================
// ----------------------------------------------------------------
// *** Do use minimap ping system for portals?
constant boolean PORTAL_USE_MINIMAP_PING = true;
// ----------------------------------------------------------------
// *** How often to ping minimap
constant real PORTAL_MINIMAP_PING_INTERVAL = 4.5;
// ----------------------------------------------------------------
// *** How long to ping minimap
constant real PORTAL_MINIMAP_PING_DURATION = 3.;
// ----------------------------------------------------------------
// *** The color of minimap ping
constant integer PORTAL_MINIMAP_PING_RED = 180;
constant integer PORTAL_MINIMAP_PING_GREEN = 23;
constant integer PORTAL_MINIMAP_PING_BLUE = 240;
// ==========================================================================
//
// *** Camera pan Constants ***
//
// ==========================================================================
// ----------------------------------------------------------------
// *** Camera pan is triggered when there are no more player units in area around source portal
constant boolean DO_CAMERA_PAN = true;
// ----------------------------------------------------------------
// *** This is the radius of camera scanning, where it will search for player units
constant real CAMERA_PAN_AREA_SCAN = 700.;
// ----------------------------------------------------------------
// *** The time it takes after last unit enters the portal to pan the camera
constant real CAMERA_PAN_DELAY = 1.5;
// ==========================================================================
//
// *** After Image Constants ***
//
// ==========================================================================
// ----------------------------------------------------------------
// *** Do use after imaging, will show transparent unit entering the portal
constant boolean USE_UNIT_AFTER_IMAGING = true;
// ----------------------------------------------------------------
// *** The time image lasts
constant real UNIT_AFTER_IMAGE_DURATION = .85;
// ----------------------------------------------------------------
// *** Animation speed of image, 1. is 100. percent
constant real UNIT_AFTER_IMAGE_TIMESCALE = .5;
// ----------------------------------------------------------------
// *** The color of image
constant integer UNIT_AFTER_IMAGE_RED = 128;
constant integer UNIT_AFTER_IMAGE_GREEN = 128;
constant integer UNIT_AFTER_IMAGE_BLUE = 128;
// ----------------------------------------------------------------
// *** The starting transparency in percentage of image, 100. is fully visible
constant real UNIT_AFTER_IMAGE_TRANSPARENCY_MIN = 60.;
constant real UNIT_AFTER_IMAGE_TRANSPARENCY_MAX = 70.;
// =================================================================================
//
// *** Enable custom selection, units that are teleported to target portal get
// selected by owning player if they where selected when they entered source portal
constant boolean DO_UNIT_SELECTION_JOB = true;
// ----------------------------------------------------------------
// *** How often to check should units be selected, that is did all units enter source portal
constant real SELECTION_CHECK_INTERVAL = .5;
// ----------------------------------------------------------------
// *** non-Editable, do not change ***
//constant integer BLIZZARD_MAX_SOUND_INSTANCES = 4;
constant key CASTER_FACING_KEY;
constant key UNIT_PORT_KEY;
constant key CASTER_PORTS_KEY;
constant key UNIT_SELECTED_KEY;
constant key UNIT_SELECTION_GROUPING_KEY;
// -------------------------------------------
// *** Portal level constants
// -------------------------------------------
// Portal duration in seconds
function PORTAL_DURATION(integer level) -> real {
real dur[];
dur[1] = 10.;
dur[2] = 15.;
dur[3] = 20.;
return dur[level];
}
// Portal units transition time in seconds
function PORTAL_TRANSITION_TIME(integer level) -> real {
real trans_tim[];
trans_tim[1] = 5.;
trans_tim[2] = 4.;
trans_tim[3] = 2.;
return trans_tim[level];
}
// Portal max units pass
function PORTAL_MAX_PASS(integer level) -> integer {
integer max_pass[];
max_pass[1] = 3;
max_pass[2] = 7;
max_pass[3] = 15;
return max_pass[level];
}
// ----------------------------------------
// **** Unit filter for portal
function PortFilter(unit filter_unit, player cast_player) -> boolean {
return !IsUnitType(filter_unit, UNIT_TYPE_DEAD) &&
!IsUnitType(filter_unit, UNIT_TYPE_STRUCTURE) &&
!IsUnitType(filter_unit, UNIT_TYPE_ANCIENT) &&
!IsUnitType(filter_unit, UNIT_TYPE_POLYMORPHED) &&
!IsUnitType(filter_unit, UNIT_TYPE_FLYING) &&
!DDIsUnitWard(filter_unit) &&
IsUnitAlly(filter_unit, cast_player);
}
// ==================================================================================================
// -------------- --------------
// *** End constant spell modification, start spell code ***
// -------------- --------------
// ==================================================================================================
/// Check if target unit is in rectangle, x, y and degrees depend on portal, while length and width are constants
function IsUnitInRectangle(unit u, real x, real y, real degrees) -> boolean {
real ux = GetUnitX(u), uy = GetUnitY(u);
real minx = x-(PORTAL_LENGTH*.5), miny = y-(PORTAL_WIDTH*.5); // Min and max x,y from normal rect
real maxx = x+(PORTAL_LENGTH*.5), maxy = y+(PORTAL_WIDTH*.5);
real range = SquareRoot(Pw_2(x-ux) + Pw_2(y-uy)); // Distance between center of rect and target unit
real radians = Atan2(uy-y, ux-x); // Angle between center of rect and target unit
/// convert unit angle to rect normal
ux = x + range * Cos(radians-(degrees*bj_DEGTORAD));
uy = y + range * Sin(radians-(degrees*bj_DEGTORAD));
return (ux > minx && ux < maxx) && (uy > miny && uy < maxy);
}
// --------------------------------------------------------
// Since portals are rects under angle, proper movement/teleportation from one portal to another has to be calculated
// sx = source rectangle x
// tx = target rectangle x
function TranslateUnitFromRectangleToRectangle( unit u,
real sx, real sy, real sdegrees,
real tx, real ty, real tdegrees)
{
real ux = GetUnitX(u), uy = GetUnitY(u);
real range = SquareRoot(Pw_2(sx-ux) + Pw_2(sy-uy)); // Distance between center of source rect and target unit
real tradians_shift = -Atan2(uy-sy, ux-sx) + (tdegrees+sdegrees+180.)*bj_DEGTORAD; // Target angle between center of source rect and target unit shifted to target rect
/*
BJDebugMsg(R2S(sdegrees));
BJDebugMsg(R2S(tdegrees));
BJDebugMsg(R2S(-Atan2(uy-sy, ux-sx)*bj_RADTODEG));
BJDebugMsg(R2S(tradians_shift*bj_RADTODEG));
*/
/// convert unit to target rect
ux = tx + range * Cos(tradians_shift);
uy = ty + range * Sin(tradians_shift);
// ** Set unit position and shift his angle to cause portal unit shattering, so that units are more organised
// * Unit will continue moving in direction of his shift facing when ported
SetUnitX(u, ux); SetUnitY(u, uy);
SetUnitFacing(u, -GetUnitFacing(u) + (tdegrees+sdegrees));
}
// ========================================================================
// *** After imaging code
struct unitimage {
private {
unit u;
real trans;
real t_spd;
static constant player IMAGE_OWNER = Player(15);
}
public static method CreateAfterImage(unit target, real x, real y) {
thistype this = allocate();
real fac = GetUnitFacing(target);
u = CreateUnit(IMAGE_OWNER, GetUnitTypeId(target), 0., 0., fac);
UnitAddAbility(u, 'Aloc');
SetUnitPathing(u, false);
//PauseUnit(u, true);
SetUnitTimeScale(u, UNIT_AFTER_IMAGE_TIMESCALE);
SetUnitX(u, x - 90. * Cos(fac*bj_DEGTORAD)); SetUnitY(u, y - 90. * Sin(fac*bj_DEGTORAD));
SetUnitVertexColor(u, UNIT_AFTER_IMAGE_RED, UNIT_AFTER_IMAGE_GREEN, UNIT_AFTER_IMAGE_BLUE, 255);
//SetUnitAnimation(u, UNIT_AFTER_IMAGE_ANIMATION);
//QueueUnitAnimation(u, UNIT_AFTER_IMAGE_ANIMATION);
SetUnitMoveSpeed(u, 1.);
IssuePointOrder(u, "move", x + 100. * Cos(fac*bj_DEGTORAD), y + 100. * Sin(fac*bj_DEGTORAD));
SetUnitColor(u, GetPlayerColor(GetOwningPlayer(target)));
trans = GetRandomReal(UNIT_AFTER_IMAGE_TRANSPARENCY_MIN, UNIT_AFTER_IMAGE_TRANSPARENCY_MAX);
t_spd = DD_INTERVAL*trans/UNIT_AFTER_IMAGE_DURATION;
DDStartTim(DD_INTERVAL, true, this, function() {
thistype this = DDTimData();
trans -= t_spd;
SetUnitVertexColor(u, UNIT_AFTER_IMAGE_RED, UNIT_AFTER_IMAGE_GREEN, UNIT_AFTER_IMAGE_BLUE, R2I(trans*2.55));
if (trans <= 0.) {
RemoveUnit(u);
u = null;
destroy();
DDQuitTim();
}
});
}
}
// ========================================================================
// *** Primary portal code
struct portal {
private {
unit p;
real x, y, degrees;
real tx, ty, tdegrees;
integer lvl;
integer cpass; // current unit passed through portal
timer t;
rect pr; // pick rect
portal lnk;
sound amb;
integer trans_units; // number of units being transiting at a time
real cpass_timeout;
static constant real DIAGONAL = SquareRoot((PORTAL_LENGTH*PORTAL_LENGTH) + (PORTAL_WIDTH*PORTAL_WIDTH));
static constant real ALPHA = Atan(PORTAL_WIDTH/PORTAL_LENGTH);
//static sound SList[];
//static integer SN = 0;
//static player TempP = null;
static boolean TempBool = false;
// *** selection vars ***
static group SctG = CreateGroup();
static timer SctT = CreateTimer();
}
private static method Min4x(real a, real b, real c, real d) -> real {
if (a < b && a < c && a < d)
return a;
else if (b < c && b < d)
return b;
else if (c < d)
return c;
return d;
}
private static method Max4x(real a, real b, real c, real d) -> real {
if (a > b && a > c && a > d)
return a;
else if (b > c && b > d)
return b;
else if (c > d)
return c;
return d;
}
private static method RectangleToRect(real x, real y, real rrad) -> rect {
real x1 = x + (DIAGONAL * Cos(rrad+ALPHA)*.5),
x2 = x + (DIAGONAL * Cos(rrad-ALPHA)*.5),
x3 = x + (DIAGONAL * Cos(rrad+bj_PI+ALPHA)*.5),
x4 = x + (DIAGONAL * Cos(rrad+bj_PI-ALPHA)*.5);
real y1 = y + (DIAGONAL * Sin(rrad+ALPHA)*.5),
y2 = y + (DIAGONAL * Sin(rrad-ALPHA)*.5),
y3 = y + (DIAGONAL * Sin(rrad+bj_PI+ALPHA)*.5),
y4 = y + (DIAGONAL * Sin(rrad+bj_PI-ALPHA)*.5);
return Rect(Min4x(x1, x2, x3, x4), Min4x(y1, y2, y3, y4),
Max4x(x1, x2, x3, x4), Max4x(y1, y2, y3, y4));
}
// ----------------------------------
// * Constructor
static method create(player p, integer lvl, real x, real y, real degrees, real tx, real ty, real tdegrees) -> thistype {
thistype this = allocate();
this.p = CreateUnit(p, PORTAL_ID, x, y, degrees+90.);
PauseUnit(this.p, true);
SetUnitAnimation(this.p, PORTAL_ANIM_BIRTH);
QueueUnitAnimation(this.p, PORTAL_ANIM_STAND);
this.x = x;
this.y = y;
this.degrees = degrees;
this.tx = tx;
this.ty = ty;
this.tdegrees = tdegrees;
this.lvl = lvl;
this.cpass = 0;
this.trans_units = 0;
this.pr = RectangleToRect(x, y, bj_DEGTORAD*degrees);
this.cpass_timeout = PORTAL_TRANSITION_TIME(lvl);
//test();
return this;
}
// ----------------------------------
// * Destructor
method destroy() {
if (t != null) {
PauseTimer(t);
DDRecycleTim(t);
t = null;
}
StopSound(amb, true, true);
KillUnit(p);
RemoveRect(pr);
p = null;
pr = null;
amb = null;
deallocate();
}
// ------------------------------------------
private method PlayPortalEntranceSound() {
DDGenericSound(PORTAL_ENTRANCE_SOUND_FILE, PORTAL_ENTRANCE_SOUND_VOLUME, x, y, SOUND_MAX_DISTANCE, 1.);
/*if (GetSoundIsPlaying(SList[SN]))
StopSound(SList[SN], true, false);
SList[SN] = CreateSound(PORTAL_ENTRANCE_SOUND_FILE, false, true, true, 10, 10, "DefaultEAXON");
SetSoundVolume(SList[SN], R2I(PORTAL_ENTRANCE_SOUND_VOLUME*1.27));
SetSoundPosition(SList[SN], x, y, 0.);
StartSound(SList[SN]);
KillSoundWhenDone(SList[SN]);
SN = ModuloInteger(SN + 1, BLIZZARD_MAX_SOUND_INSTANCES);*/
}
// ------------------------------------------
public method PlayPortalDeathSound() {
DDGenericSound(PORTAL_DEATH_SOUND_FILE, PORTAL_DEATH_SOUND_VOLUME, x, y, SOUND_MAX_DISTANCE, 1.);
//PlayBasicSound.evaluate(PORTAL_DEATH_SOUND_FILE, true, PORTAL_DEATH_SOUND_VOLUME, x, y, null);
}
// ------------------------------------------
private method PlayPortalAmbienceSound() {
if (amb == null) {
amb = CreateSound(PORTAL_AMBIENCE_SOUND_FILE, true, true, true, 10, 10, "DoodadsEAX");
SetSoundVolume(amb, R2I(PORTAL_AMBIENCE_SOUND_VOLUME*1.27));
SetSoundPosition(amb, x, y, 0.);
SetSoundDistanceCutoff(amb, 3500.);
StartSound(amb);
}
}
// *** Checks if player units are around source portal
private method IsSrcPortalClear(player p) -> boolean {
//TempP = p;
TempBool = false;
// *** Check if there are any remaining units
DDGroupFilterArea(x, y, CAMERA_PAN_AREA_SCAN, GetPlayerId(p), function() -> boolean {
TempBool = TempBool || (GetUnitAbilityLevel(GetFilterUnit(), 'Aloc') == 0 && PortFilter(GetFilterUnit(), Player(DDGFilterData())));
return false;
});
return !TempBool;
}
// ------------------------------------------
private method DoSelectJob(unit u, boolean select) {
player p = GetOwningPlayer(u);
if (!select) {
DDSet(u, UNIT_SELECTED_KEY, 0);
if (IsUnitSelected(u, p)) {
DDSet(u, UNIT_SELECTED_KEY, 1);
SelectUnit(u, false);
if (IsUnitInGroup(u, SctG))
GroupRemoveUnit(SctG, u);
}
} else {
if (DDGet(u, UNIT_SELECTED_KEY) == 1) {
if (IsSrcPortalClear(p)) {
if (GetLocalPlayer() == p)
SelectUnit(u, true);
} else {
// *** more units are awaiting to be ported delay the selection
//BJDebugMsg(GetUnitName(FirstOfGroup(SctG)));
if (FirstOfGroup(SctG) == null) {
TimerStart(SctT, SELECTION_CHECK_INTERVAL, true, function() {
//integer i;
// ------------------------------------
// *** reset player portal clear vars
//for(i=0; i < 0xD; i+=1)
// SctPClr[i] = 0;
// ------------------------------------
// *** loop through units ***
ForGroup(SctG, function() {
unit u = GetEnumUnit();
player p = GetOwningPlayer(u);
portal pt = DDGet(u, UNIT_SELECTION_GROUPING_KEY);
//integer pid = GetPlayerId(p);
// *** here we do not use just 0 and 1, but 0, 1 and 2, to optimize the code
// *** requiering only min calls of "IsSrcPortalClear"
/*
if (SctPClr[pid] == 0) {
SctPClr[pid] = 1;
if (IsSrcPortalClear(p))
SctPClr[pid] = 2;
}
*/
// *** if portal is clear, do selection now
if (pt.IsSrcPortalClear(p) || IsUnitType(pt.p, UNIT_TYPE_DEAD)) {
if (GetLocalPlayer() == p)
SelectUnit(u, true);
GroupRemoveUnit(SctG, u);
}
u = null;
});
// *** pause timer???
if (FirstOfGroup(SctG) == null) {
PauseTimer(SctT);
}
});
}
DDSet(u, UNIT_SELECTION_GROUPING_KEY, this);
GroupAddUnit(SctG, u);
}
}
}
}
// ------------------------------------------
private method AttemptCameraPan(player p) {
p_integer pi;
// *** If no more units remains do pan camera
if (IsSrcPortalClear(p)) {
pi = p_integer.create();
pi[0] = lnk;
pi[1] = GetPlayerId(p);
DDStartTim(CAMERA_PAN_DELAY, false, pi, function() {
p_integer pi = DDTimData();
if (GetLocalPlayer() == Player(pi[1]))
SetCameraPosition(portal(pi[0]).x, portal(pi[0]).y);
pi.destroy();
});
}
}
// --------------------------------------------
// *** Periodic port timer system
//
public method StartPortSys() {
t = DDStartTim(PORT_CHECK_INTERVAL, true, this, static method()
{
thistype this = DDTimData();
real elapsed = DDTimTick()*PORT_CHECK_INTERVAL;
integer i = 0;
real dur = PORTAL_DURATION(lvl);
real trans_time = PORTAL_TRANSITION_TIME(lvl);
integer max_pass = PORTAL_MAX_PASS(lvl);
p_integer pi;
// ------------------------------------------------------
// *** Add the code for minimap ping if wanted
static if (PORTAL_USE_MINIMAP_PING) {
if (ModuloInteger(R2I(elapsed*10.), R2I(PORTAL_MINIMAP_PING_INTERVAL*10.)) == 1)
if (GetLocalPlayer() == GetOwningPlayer(p)) {
PingMinimapEx(x, y, PORTAL_MINIMAP_PING_DURATION, PORTAL_MINIMAP_PING_RED, PORTAL_MINIMAP_PING_GREEN, PORTAL_MINIMAP_PING_BLUE, false);
PingMinimapEx(tx, ty, PORTAL_MINIMAP_PING_DURATION, PORTAL_MINIMAP_PING_RED, PORTAL_MINIMAP_PING_GREEN, PORTAL_MINIMAP_PING_BLUE, false);
}
}
// ------------------------------------------------------
// --------------------
// *** Can port?, elapsed must be between bortal finish birth and from that point + portal duration
// *** number of passed units between linked portals must be lower than constant specifies depending on level
if (elapsed > PORTAL_ANIM_BIRTH_DURATION && elapsed < dur+PORTAL_ANIM_BIRTH_DURATION &&
cpass+lnk.cpass < max_pass)
{
PlayPortalAmbienceSound();
// --------------------------------------------------
// *** pick all units to be ported ***
DDGroupFillMemRect(pr, this, static method() -> boolean {
portal this = DDMemUnitData();
// *** Unit is in rectangle and whas not ported reacently
if (IsUnitInRectangle(GetFilterUnit(), x, y, degrees) && DDGet(GetFilterUnit(), UNIT_PORT_KEY) == 0)
return PortFilter(GetFilterUnit(), GetOwningPlayer(p));
return false;
});
// --------------------------------------------------
// *** Port all units in portal area ***
while(i < DDMemUnitN()) {
if (!IssueImmediateOrder(DDMemUnit(i), "stop"))
debug BJDebugMsg("Portal: Fail to stop unit!");
static if (DO_UNIT_SELECTION_JOB)
DoSelectJob(DDMemUnit(i), false);
static if (USE_UNIT_AFTER_IMAGING)
unitimage.CreateAfterImage(DDMemUnit(i), GetUnitX(DDMemUnit(i)), GetUnitY(DDMemUnit(i)));
TranslateUnitFromRectangleToRectangle(DDMemUnit(i), x, y, degrees, tx, ty, tdegrees);
ShowUnit(DDMemUnit(i), false);
PlayPortalEntranceSound();
trans_units += 1;
lnk.trans_units += 1;
static if (DO_CAMERA_PAN)
AttemptCameraPan(GetOwningPlayer(DDMemUnit(i)));
DDSet(DDMemUnit(i), UNIT_PORT_KEY, 1);
// ---------------------------------------------------------
// *** Transition time
pi = p_integer.create();
pi[0] = New_pUnit(DDMemUnit(i));
pi[1] = this.lnk;
pi[2] = this;
DDStartTim(trans_time, false, pi, static method() {
p_integer pi = DDTimData();
p_unit pu = pi[0];
real dist = GetRandomReal(UNIT_PORT_WALK_OFFSET_MIN, UNIT_PORT_WALK_OFFSET_MAX);
real fac = GetUnitFacing(pu[0]);
portal(pi[1]).trans_units -= 1;
portal(pi[2]).trans_units -= 1;
portal(pi[1]).PlayPortalEntranceSound();
ShowUnit(pu[0], true);
IssuePointOrder(pu[0], "move", GetUnitX(pu[0]) + dist * Cos(fac*bj_DEGTORAD),
GetUnitY(pu[0]) + dist * Sin(fac*bj_DEGTORAD));
// *** Make unit to be able to port again ***
TimerStart(GetExpiredTimer(), UNIT_NEXT_PORT_DELAY, false, static method() {
p_integer pi = DDTimData();
p_unit pu = pi[0];
DDSet(pu[0], UNIT_PORT_KEY, 0);
pi.destroy();
pu.destroy();
DDQuitTim();
});
static if (DO_UNIT_SELECTION_JOB) {
thistype(pi[2]).DoSelectJob(p_unit(pi[0])[0], true);
}
});
// ---------------------------------------------------------
cpass += 1;
i += 1;
}
}
// *** Portal end if max pass reached or
if ((elapsed > PORTAL_ANIM_BIRTH_DURATION + dur /*+ trans_time*/) || (cpass + lnk.cpass >= max_pass)) {
/*
if (cpass + lnk.cpass >= max_pass)
cpass_timeout -= PORT_CHECK_INTERVAL;
else
cpass_timeout = 0.;
*/
//debug BJDebugMsg(I2S(trans_units));
if (/*cpass_timeout <= 0. ||*/ trans_units == 0) {
DDStartTim(DESTROY_PORTAL_DELAY, false, this, function() {
portal this = DDTimData();
PlayPortalDeathSound();
t = null;
this.destroy();
DDQuitTim();
});
DDQuitTim();
}
}
});
}
// *** links with other portal
method operator link=(portal p) { lnk = p; }
method operator Elapsed() -> real { return DDCTimTick(t)*PORT_CHECK_INTERVAL; }
}
// ================================================================================
//
// *** Extra spell functions ***
//
// ================================================================================
// ------------------------------------------
// *** Rounds positive integer
function Round(real x) -> integer {
if (x-I2R(R2I(x)) > .5)
return R2I(x)+1;
return R2I(x);
}
function onInit() {
trigger t = CreateTrigger();
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST);
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST);
TriggerAddCondition(t, function() -> boolean {
// ----------------------------------------
// *** Locals ***
unit u;
sound s;
player p;
integer lvl;
real ux, uy;
real tx, ty;
real x, y, rads;
p_real fac;
p_integer pt;
// ----------------------------------------
// *** Condition ***
if (GetSpellAbilityId() != SPELL_ID)
return false;
// ----------------------------------------
// *** Actions ***
// ====================================================================
// *** Start cast event ***
// ====================================================================
if (GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_CAST) {
u = GetTriggerUnit();
if (IsUnitInRangeXY(u, GetSpellTargetX(), GetSpellTargetY(), SECOND_PORTAL_MIN_RANGE)) {
IssuePointOrder(u, "move", GetUnitX(u) + 50. * Cos(GetUnitFacing(u)*bj_DEGTORAD), GetUnitY(u) + 50. * Sin(GetUnitFacing(u)*bj_DEGTORAD));
DisplayTimedTextFromPlayer(GetOwningPlayer(u), 0., 0., PORTAL_WARNING_MESSAGE_DURATION, "|cffff0020"+PORTAL_WARNING_MESSAGE+"|r");
if (GetLocalPlayer() == GetOwningPlayer(u)) {
s = CreateSound(PORTAL_WARNING_SOUND_FILE, false, false, false, 10, 10, "");
SetSoundVolume(s, R2I(PORTAL_WARNING_SOUND_VOLUME*1.27));
StartSound(s);
KillSoundWhenDone(s);
s = null;
}
//PlayBasicSound(PORTAL_WARNING_SOUND_FILE, false, PORTAL_WARNING_SOUND_VOLUME, 0., 0., GetOwningPlayer(u));
DDSet(u, CASTER_FACING_KEY, 0);
} else
DDSet(u, CASTER_FACING_KEY, New_pReal(GetUnitFacing(u)));
u = null;
return false;
}
// ====================================================================
// *** End cast event ***
// ====================================================================
if (GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_ENDCAST) {
u = GetTriggerUnit();
// *** Fail to cast spell?
pt = DDGet(u, CASTER_PORTS_KEY);
if (pt == 00) {
u = null;
return false;
}
// *** End spell if portal whas not channeled fully
// * Use round and increase precission by 10x, meaning that cast times like 7.64 will be 7.6, as real values are unsafe to be compared
if (Round(portal(pt[0]).Elapsed*10.) < Round(PORTAL_ANIM_BIRTH_DURATION*10.)) {
portal(pt[0]).PlayPortalDeathSound();
portal(pt[1]).PlayPortalDeathSound();
portal(pt[0]).destroy();
portal(pt[1]).destroy();
}
pt.destroy();
u = null;
return false;
}
// ====================================================================
// ====================================================================
// *** Start cast effect event ***
// ====================================================================
u = GetTriggerUnit();
p = GetOwningPlayer(u);
lvl = GetUnitAbilityLevel(u, SPELL_ID);
fac = DDGet(u, CASTER_FACING_KEY);
ux = GetUnitX(u); uy = GetUnitY(u);
tx = GetSpellTargetX(); ty = GetSpellTargetY();
rads = Atan2(-uy+ty, -ux+tx);
x = ux + FIRST_PORTAL_OFFSET * Cos(rads);
y = uy + FIRST_PORTAL_OFFSET * Sin(rads);
pt = p_integer.create();
pt[0] = portal.create(p, lvl, x, y, (rads*bj_RADTODEG)+90., tx, ty, fac[0]-90.);
pt[1] = portal.create(p, lvl, tx, ty, fac[0]-90., x, y, (rads*bj_RADTODEG)+90.);
portal(pt[0]).link = pt[1];
portal(pt[1]).link = pt[0];
portal(pt[0]).StartPortSys();
portal(pt[1]).StartPortSys();
DDSet(u, CASTER_PORTS_KEY, pt);
DDGenericSound(PORTAL_BIRTH_SOUND_FILE, PORTAL_BIRTH_SOUND_VOLUME, x, y, SOUND_MAX_DISTANCE, 1.);
DDGenericSound(PORTAL_BIRTH_SOUND_FILE, PORTAL_BIRTH_SOUND_VOLUME, tx, ty, SOUND_MAX_DISTANCE, 1.);
fac.destroy();
u = 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 SHIMMERING_PORTAL_ABIL_ID = "SPrt"
//! i SHIMMERING_PORTAL_DUMMY_ID = "port"
//! i -- =========================================================
//! i -- -----------------------------------------------
//! i --Ability Create Code
//! i setobjecttype("abilities")
//! i createobject("ANto", SHIMMERING_PORTAL_ABIL_ID)
//! i makechange(current,"anam", "Shimmering Portal")
//! i makechange(current,"aart", "ReplaceableTextures\\CommandButtons\\BTNPortal.blp")
//! i makechange(current,"arar", "ReplaceableTextures\\CommandButtons\\BTNPortal.blp")
//! i makechange(current,"aani", "channel")
//! i makechange(current,"alev", "3")
//! i makechange(current,"arlv", "1")
//! i makechange(current,"ahky", "H")
//! i makechange(current,"arhk", "H")
//! i makechange(current,"aret", "Learn S|cff8000ffh|rimmering Portal - [|cffffcc00Level ".. string.char(37) .."d|r]")
//! i makechange(current,"arut", "|cffb300b3Opens portal allowing any ground unit to pass through.|n|r|n|cff408080[Level 1]|r - Portal lasts 10 seconds, up to 3 units can pass, 5 seconds transition time, cooldown 30 seconds.|n|cff408080[Level 2]|r - Portal lasts 15 seconds, up to 7 units can pass, 4 seconds transition time, cooldown 20 seconds.|n|cff408080[Level 3]|r - Portal lasts 20 seconds, up to 12 units can pass, 2 seconds transition time, cooldown 10 seconds.|n|n|cffb00000Channeling|r")
//! i makechange(current,"aub1","1","|cffb300b3Portal lasts 10 seconds, up to 3 units can pass, 5 seconds transition time, cooldown 30 seconds.|r")
//! i makechange(current,"aub1","2","|cffb300b3Portal lasts 15 seconds, up to 7 units can pass, 4 seconds transition time, cooldown 20 seconds.|r")
//! i makechange(current,"aub1","3","|cffb300b3Portal lasts 20 seconds, up to 12 units can pass, 2 seconds transition time, cooldown 10 seconds.|r")
//! i local i = 0
//! i for i=1, 3 do
//! i local si = tostring(i)
//! i makechange(current,"Ntou",si,"")
//! i makechange(current,"aran",si,"99999.")
//! i makechange(current,"acdn",si,tostring(10*(4-i)))
//! i makechange(current,"adur",si,"7.5")
//! i makechange(current,"ahdu",si,"7.5")
//! i makechange(current,"amcs",si,"100")
//! i makechange(current,"atp1",si,"S|cff8000ffh|rimmering Portal - [|cffffcc00Level ".. si .."|r]")
//! i end
//! i -- -----------------------------------------------
//! i --Dummy Create Code
//! i setobjecttype("units")
//! i createobject("hpea", SHIMMERING_PORTAL_DUMMY_ID)
//! i makechange(current, "uabi", "Avul,Aloc")
//! i makechange(current, "udtm", "5.77")
//! i makechange(current, "uico", "ReplaceableTextures\\CommandButtons\\BTNPortal.blp")
//! i makechange(current, "umdl", "Doodads\\Cinematic\\ShimmeringPortal\\ShimmeringPortal.mdl")
//! i makechange(current, "umxp", "0.")
//! i makechange(current, "umxr", "0.")
//! i makechange(current, "ushu", "")
//! i makechange(current, "ua1t", "")
//! i makechange(current, "udea", string.char(2))
//! i makechange(current, "umvt", "")
//! i makechange(current, "ucol", "0.")
//! i makechange(current, "ufoo", "0")
//! i makechange(current, "usid", "1200")
//! i makechange(current, "usin", "1200")
//! i makechange(current, "unam", "Portal")
//! i makechange(current, "unsf", " (Dummy)")
//! endexternalblock
*/
// ^
// |
// Change me from */ to // and then back to */ after saving and reopening the map
// ============================================================================================