//! zinc
library BlinkMove requires EventData, AutoIndex
{
// Order ID of your ability
constant integer SPELL_ID = 'A00A';
// Adjust to taste
constant real DISTANCE_PER_LEVEL = 300.0;
constant real END_DISTANCE = 200.0; // Blink this close to the end point
// Will work at most once every this many seconds
constant integer COOLDOWN = 4;
// Effects to show on start and end points
constant string EFFECT_START = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl";
constant string EFFECT_END = "Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl";
//set this higher if you are allowing more users of this ability than 1 per player
constant integer MAX_BLINKERS = bj_MAX_PLAYERS;
// Leave as is
integer ORDER_SMART;
integer ORDER_ATTACK;
data blinkers[MAX_BLINKERS];
struct data
{
unit caster;
integer cd;
timer t;
boolean active = true;
integer unitIndex;
method cooldown()
{
cd = cd -1;
}
static method create(unit u) -> data
{
data d = data.allocate();
d.caster = u;
d.cd = COOLDOWN;
d.t = CreateTimer();
d.unitIndex = GetUnitId(u);
return data;
}
static method onDestroy()
{
DestroyTimer(t);
}
}
function GetBlinkerData(integer id) -> data
{
data ret = 0;
integer i;
for (0 <= i < MAX_BLINKERS)
{
if (blinkers[i] != 0)
{
if (blinkers[i].unitIndex == id)
{
ret = blinkers[i];
break;
}
}
}
return ret;
}
// The unit needs the spell and must have been ordered to move or attack-move (Right-click or "A")
// Additionally, it only works once every COOLDOWN seconds
function MoveConds() -> boolean
{
unit u = GetTriggerUnit();
data d = GetBlinkerData(GetUnitId(u));
if (d == 0) //The ordered unit doesnt have the ability
{
return false;
}
else if (d.cd > 0 || !d.active) //still on cd or deactivated for last order
{
return false;
}
//now lets check the order since we know we have a blinker
return GetIssuedOrderId() == ORDER_SMART || GetIssuedOrderId() == ORDER_ATTACK;
}
function MoveActions()
{
EventData ed = EventData.create();
unit u = ed.u;
real d = ed.distanceXY();
real a = ed.angle();
real maxDist = DISTANCE_PER_LEVEL * GetUnitAbilityLevel(u, SPELL_ID);
real tx = 0.0;
real ty = 0.0;
data dat = GetBlinkerData(GetUnitData(u));
dat.cd = COOLDOWN;
DestroyEffect(AddSpecialEffect(EFFECT_START, ed.x, ed.y)); // Eye-candy starting point
d = d - END_DISTANCE; // This close to the end point
if (d > maxDist) // but respect the maximum distance
{
d = maxDist;
}
tx = ed.x + d * Cos(a); // Point that is nearly there
ty = ed.y + d * Sin(a);
DestroyEffect(AddSpecialEffect(EFFECT_END, tx, ty)); // Eye-candy target point
SetUnitPosition(u, tx, ty);
dat.active = false;
if (GetIssuedOrderId() == ORDER_SMART) // ...and order the unit to (attack-)move the rest
{
IssuePointOrder(u, "move", ed.tx, ed.ty);
}
else
{
IssuePointOrder(u, "attack", ed.tx, ed.ty);
}
dat.active = true;
}
//Only call this on the intial learning of the spell
function LearnConds() -> boolean
{
return GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID) == 1;
}
//Create the data struct and attach it to its timer and start the cd timer
function LearnActions()
{
integer i;
unit u = GetTriggerUnit();
for (0 <= i < MAX_BLINKERS)
{
if (blinkers[i] == 0)
{
blinkers[i] = data.create(u);
TimerStart(blinkers[i].t, 1, true, function blinkers[i].cooldown);
break;
}
}
debug
{
//If we iterated through the whole loop then we are trying to add a new blinker beyond the max....
if (i == MAX_BLINKERS)
{
BJDebugMsg("ERROR: Trying to add a new blinker over the max.");
}
}
}
function onInit()
{
trigger learn = CreateTrigger();
trigger move = CreateTrigger();
ORDER_SMART = OrderId("smart");
ORDER_ATTACK = OrderId("attack");
TriggerRegisterAnyUnitEventBJ(learn, EVENT_PLAYER_HERO_SKILL ); //A unit learns a skill
TriggerAddCondition(learn, Condition(function LearnConds));
TriggerAddAction(learn, function LearnActions);
TriggerRegisterAnyUnitEventBJ(move, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER); // A unit is issued an order targeting a point
TriggerAddCondition(move, Condition(function MoveConds));
TriggerAddAction(move, function MoveActions);
}
}
//! endzinc