Moderator
M
Moderator
Diablo-dk: [Approved]
The spell is MUI and works fine ingame.
The spell is MUI and works fine ingame.
// -------------------------------------------------------------------------
// *** 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
// ============================================================================================