/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Fireball v.9.9.5
// created by Darkt3mpl3r
//
//
// ° Mapmaking since 2006...
// °
// °
// °
// °°°ÛÜ
// °°X°ÛÛÛ ÛÛÛÛÛÛÛÛ ÿÜÛÛÛÛÛÛÛÛÜ
// °°XX° ÛÛÛ ßÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛ
// °XX ° ÛÛÛÛ ÛÛÛÛÜ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
// °XX ° ÛÛÛÛÛ ÛÛÛÛ ÛÛÛÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
// °°XXX°° ÛÛÛÛÛÛÜ ÛÛÛÜ ÛÛÛÛÛ ßÛÛÛÛÛ ßÛÛÛÛ ÛÛÛÛÛÛ
// °°°°°°°° ÛÛÛÛÛÛÛ ÛÛÛÛ ÛÛÛÛ ÛÛÛÛ ÛÛÛÛÜ
// ÛÛÛÛÛÛÛÛ ÛÛÛÛ ÛÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛÛÛÜ ÛÛÛÛ
// °°°°°°°° ÛÛÛÛÛÛÛÛÛ ÛÛÛÛÜ ÛÛÛÛÜ ÛÛÛÛÛÛÛÛ ÛÛÛÛÛÜ ÜÛÛÛÛÛÛ
// °°XXXXX° ÛÛÛÛ ÛÛÛÜ ÛÛÛÛÛ ÛÛÛÛÛÛÜ ßÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
// °°XXXXX °° ÛÛÛÛ ÛÛÛÛÜ ÛÛÛÛ °°°ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ°°° ÛÛÛÛÛÛÛÛÛÛÛÛÛß
// ° XXXXX °°ÛÛÛÛÛ°°°°ÛÛÛÛÛÛÛÛ°°XX ÛÛÛÛÛÛÛÛÛÛÛÛÛÛ XXX°°ÛÛÛÛÛÛÛÛÛÛÛ ÛÛÛ
// °° XXXXX ÜÛÛÛÛÛÛ X ÛÛÛÛÛÛÛÛÛ XXX ÛÛÛÛÛÛÛÛÛÛÛ XXXXXX ÛÛÛÛÛÛÛ° ÛÛÛ
// °° XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX °°°°°° °°°°
// °° XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX °°°°°X°°
// °°° XXXXXXXXXX °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° XXXXXXXXXXXXXXXXX°°
// °°°°°°°°°°°°°°°° °°°°°°°° XXXXXXX °°°
// °°°°°° XX °°
// ...2009 and still moving on. °°°°°°°
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//! zinc
library fireball requires Knockback
{ //=========================== Setup ===========================
// Main Configuration
constant integer SPELL_ID = 'Afir'; /* Fireball Spell Rawcode*/
constant integer UNIT_ID = 'dsfx'; /* Dummy Unit Rawcode */
constant integer MISSILE_COUNT = 3; /* Number of Missiles */
constant real FPS = 32.0; /* Iterations per second */
// Effects
constant string ATTACH_FX = "Abilities\\Spells\\Other\\Volcano\\VolcanoMissile.mdl"; // model attachment
constant string ATTACH_SX = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl"; // Another attachment
constant string VANISH_FX = "Abilities\\Spells\\Other\\Incinerate\\IncinerateBuff.mdl"; // Random Appearing Effect
constant string GROUND_FX = "Abilities\\Spells\\Other\\Volcano\\VolcanoDeath.mdl"; // Impact(ground) Effect
constant string HIT_FX = "Abilities\\Weapons\\LavaSpawnMissile\\LavaSpawnBirthMissile.mdl";
constant string HIT_POINT = "chest"; /* Attachment Point of HIT_FX */
constant real UNITSCALE = 0.85; /* Missile Scaling */
// Knockback & AoE
constant real KNOCKBACK = 40.0; /* Minimal Knockback Distance */
constant real KNOCKBACK_AMP = 200.; /* Max = KNOCKBACK + KNOCKBACK_AMP */
constant real KNOCKBACK_DUR = 1.00; /* Time in seconds */
constant real GROUND_RADIUS = 256.; /* Area of Effect */
// Missile Behaviour
constant real DEFAULT_HEIGHT = 48.0;
constant real DEFAULT_OFFSET = 64.0;
constant real DEFAULT_SPEED = 300.;
constant real ACCELERATION = 10.0;
constant real HEIGHT_FACTOR = 0.40; /* MAX_DISTANCE* FACTOR */
constant real ROTATIONS = 1.00; /* Rotations per second */
constant real CASTRANGE = 1200; /* Maximum Cast Range */
constant real AMPLITUDE = 15.0; /* Better keep this low */
// Damage Function. Use "return VALUE*level;" to set your spell damage
function dmg(integer level) ->integer{return 5+level*25;}
constant real BUILDING_FACTOR = 0.5; /* Damage to buildings */
constant boolean DESTROY_TREES = true;
//========================End of Setup ========================
real pass_x, pass_y; rect pass_r;
unit pass_c; group g; integer pass_i;
location tmploc=Location(0,0);
function callback()
{
data d;
integer i=data.N;
real x;
real y;
real z;
while (i>0)
{
i=i-1;
d=data.V[i];
d.s=d.s+ACCELERATION/FPS;
d.d=d.d+d.s;
d.t=d.t+(2*bj_PI)/(FPS/ROTATIONS);
MoveLocation(tmploc, GetUnitX(d.c), GetUnitY(d.c));
z=GetLocationZ(tmploc)+GetUnitFlyHeight(d.c);
x=GetUnitX(d.c) + d.s*Cos(d.a) + d.q*Cos(d.t);
y=GetUnitY(d.c) + d.s*Sin(d.a) + d.q*Sin(d.t);
z=Sin(d.d/d.md*bj_PI)*d.h+DEFAULT_HEIGHT;
MoveLocation(tmploc, x, y);
z=z-GetLocationZ(tmploc);
SetUnitX(d.c, x);
SetUnitY(d.c, y);
SetUnitFlyHeight(d.c, z, 0.);
if (GetRandomInt(0,3)==0)
{
DestroyEffect(AddSpecialEffectTarget(VANISH_FX, d.c, "origin"));
}
if (d.d>=d.md)
{
DestroyEffect(AddSpecialEffect(GROUND_FX, x, y));
pass_x=x;
pass_y=y;
pass_c=d.x;
pass_i=d.i;
pass_r=Rect(x-GROUND_RADIUS, y-GROUND_RADIUS, x+GROUND_RADIUS, y+GROUND_RADIUS);
GroupEnumUnitsInRange(g, x, y, GROUND_RADIUS, null);
ForGroup(g, function()
{
if (IsUnitEnemy(GetEnumUnit(), GetOwningPlayer(pass_c)) && (GetWidgetLife(GetEnumUnit())>=0.405))
{
if (IsUnitType(GetEnumUnit(), UNIT_TYPE_STRUCTURE)==false)
{
Knockback(GetEnumUnit(),(KNOCKBACK_AMP/SquareRoot((GetUnitY(GetEnumUnit())-pass_y)*(GetUnitY(GetEnumUnit())-pass_y)+(GetUnitX(GetEnumUnit())-pass_x)*(GetUnitX(GetEnumUnit())-pass_x)))+KNOCKBACK,Atan2(GetUnitY(GetEnumUnit())-pass_y,GetUnitX(GetEnumUnit())-pass_x),KNOCKBACK_DUR);
UnitDamageTarget(pass_c, GetEnumUnit(), dmg(pass_i), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
DestroyEffect(AddSpecialEffectTarget(HIT_FX, GetEnumUnit(), HIT_POINT));
}
else
{
UnitDamageTarget(pass_c, GetEnumUnit(), BUILDING_FACTOR*dmg(pass_i), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
DestroyEffect(AddSpecialEffectTarget(HIT_FX, GetEnumUnit(), "origin"));
}
}
} );
EnumDestructablesInRect(pass_r, null, function()
{
destructable d = GetEnumDestructable();
unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), UNIT_ID, GetWidgetX(d), GetWidgetY(d), 0);
UnitAddAbility(u, 'Ahrl');
if (IssueTargetOrder(u, "harvest", d)) {KillDestructable(d);} // achieved by Pitzermike
RemoveUnit(u);
u=null;
d=null;
} );
d.destroy();
data.N=data.N-1;
data.V[i]=data.V[data.N];
}
}
}
struct data
{
unit c;
unit x;
effect fx;
effect sx;
integer i;
real a;
real s;
real h;
real md;
real d;
real t;
real q;
static integer N=0;
static data V[];
static timer T=CreateTimer();
static method create (unit caster, real x, real y, real a) -> data
{
data d=data.allocate();
real cx=GetUnitX(caster)+DEFAULT_OFFSET*Cos(a);
real cy=GetUnitY(caster)+DEFAULT_OFFSET*Sin(a);
d.x = caster;
d.c = CreateUnit(GetOwningPlayer(caster), UNIT_ID, cx, cy, a*bj_RADTODEG);
d.i = GetUnitAbilityLevel(d.x, SPELL_ID);
d.fx= AddSpecialEffectTarget(ATTACH_FX, d.c, "origin");
d.sx= AddSpecialEffectTarget(ATTACH_SX, d.c, "origin");
d.md= SquareRoot((cx-x)*(cx-x)+(cy-y)*(cy-y));
d.d = 0;
d.a = a;
d.s = DEFAULT_SPEED/FPS;
d.h = d.md*HEIGHT_FACTOR;
d.t = a;
if (GetRandomInt(0, 1) == 1) d.q =(d.md/(CASTRANGE+DEFAULT_OFFSET))*AMPLITUDE;
else d.q =-(d.md/(CASTRANGE+DEFAULT_OFFSET))*AMPLITUDE;
SetUnitScale(d.c, UNITSCALE, UNITSCALE, UNITSCALE);
UnitAddAbility(d.c, 'Amrf');
UnitRemoveAbility(d.c, 'Amrf');
SetUnitX(d.c, cx);
SetUnitY(d.c, cy);
SetUnitFlyHeight(d.c, DEFAULT_HEIGHT, 0.);
data.V[data.N]=d;
data.N=data.N+1;
if (data.N==1)
{
TimerStart(data.T, 1/FPS, true, function callback);
}
return d;
}
method onDestroy()
{
UnitApplyTimedLife(this.c, 'BTLF', 5.0);
DestroyEffect(this.fx);
DestroyEffect(this.sx);
this.c=null;
this.x=null;
}
}
function onSpellCast()
{
integer i=MISSILE_COUNT;
unit u=GetTriggerUnit();
real x=GetSpellTargetX();
real y=GetSpellTargetY();
real a;
while (i>0)
{
a=Atan2(y-GetUnitY(u), x-GetUnitX(u));
data.create(u,x,y,a);
TriggerSleepAction(0.1);
i=i-1;
}
u=null;
}
function spellIdMatch() -> boolean
{
return (GetSpellAbilityId() == SPELL_ID);
}
function onInit()
{
trigger t = CreateTrigger();
TriggerAddAction(t, function onSpellCast);
TriggerAddCondition(t, Condition(function spellIdMatch));
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
g=CreateGroup();
}
}
//! endzinc