- Joined
- Mar 17, 2009
- Messages
- 1,350
V3 out eh!?
well i'll check it out tomorrow after I get back from school 
I'm sure it's a job well done!!
I'm sure it's a job well done!!
[/B]
//TESH.scrollpos=33
//TESH.alwaysfold=0
/*
**********************************************************************************************************
Installation:
1) Copy DD Library to your map
2) Copy ElectrolixZap abilities (hero and dummy versions) to your map
3) Export and Import 'thunder1.wav' sound from this map to yours
4) Export and Import 'Great Lightning.mdx' models from this map to yours
5) Copy this trigger to your map and modify it to your liking below
6) Made on Warcraft III v1.30
**********************************************************************************************************
*/
//! zinc
library ElectrolixZap requires DDLib
{
// *******************************************************************************
// * Ability Id found in object editor
// Default: 'elfd'
constant integer ABILITY_ELECTROLIX_ZAP = 'elfd';
// * Sound file being played when spell is cast
// Default: "Custom\\Ambient\\thunder1.wav"
constant string SOUND_THUNDER = "Custom\\Ambient\\thunder1.wav";
// Default: 100.
constant real SOUND_THUNDER_VOL = 100.;
// Default: 3500.
constant real SOUND_MAX_DISTANCE = 3500.;
// * Should camera shake when spell is cast
constant boolean USE_CAMERA_NOISE = true;
// Default: 2.5
constant real CAMERA_NOISE_DURATION = 2.5;
// * Should terrain deformation happen?
constant boolean USE_TERRAIN_DEFORM = true;
// Default: 200.
constant real TERRAIN_DEFORM_DEPTH = 200.;
// Default: 1.
constant real TERRAIN_DEFORM_DURATION = 1.;
// * Primary lightning strike effect created during initial spell cast
// Default: "Lightning\\Great Lightning.mdl"
constant string EFFECT_GREAT_LIGHTNING = "Lightning\\Great Lightning.mdx";
constant real EFFECT_GREAT_LIGHTNING_SIZE = 1.2;
constant real EFFECT_GREAT_LIGHTNING_ANIMATION_SPEED = 50.;
// * Secondary lightning bolts created during initial spell cast
// Default: "Doodads\\Cinematic\\Lightningbolt\\Lightningbolt.mdx"
constant string EFFECT_LIGHTING_BOLT = "Doodads\\Cinematic\\Lightningbolt\\Lightningbolt.mdx";
// Default: 1.
constant real EFFECT_LIGHTING_BOLT_SIZE = 1.;
// Default: 200.
constant real EFFECT_LIGHTING_BOLT_ANIMATION_SPEED = 200.;
// Default: 25
constant integer NUMBER_OF_LIGHTINING_BOLTS = 25;
// Default: 0.15 seconds
constant real LIGHTNING_BOLT_STRIKE_INTERVAL = 0.15;
// Default: "Doodads\\LordaeronSummer\\Props\\SmokeSmudge\\SmokeSmudge0.mdl"
constant string EFFECT_LIGHTING_BOLT_SMOKE_MODEL_1 = "Doodads\\LordaeronSummer\\Props\\SmokeSmudge\\SmokeSmudge0.mdl";
// Default: ""
constant string EFFECT_LIGHTING_BOLT_SMOKE_MODEL_2 = "";//"Doodads\\LordaeronSummer\\Props\\SmokeSmudge\\SmokeSmudge1.mdl";
// Default: ""
constant string EFFECT_LIGHTING_BOLT_SMOKE_MODEL_3 = "";//"Doodads\\LordaeronSummer\\Props\\SmokeSmudge\\SmokeSmudge2.mdl";
// Default: 1.2
constant real EFFECT_LIGHTING_BOLT_SMOKE_MODEL_SIZE = 1.2;
// Default: 20.
constant real EFFECT_LIGHTING_BOLT_SMOKE_MODEL_Z_OFFSET = 20.;
// Default: 15.
constant real EFFECT_LIGHTING_BOLT_SMOKE_MODEL_DURATION = 15.;
// * After initial primary and secondary bolts, wait for a while before damaging units
constant real THUNDER_DAMAGE_DELAY = 0.6;
// * Dealing damage and summon shield/lightning on targets
// Default: "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldTarget.mdx"
constant string EFFECT_LIGHTNING_UNIT_ZAP = "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldTarget.mdx";
// * Load dummies and shock target with forked lightning, add thunder clap effect and purge
// Default: 'efef'
constant integer ABILITY_DUMMY_ELECTRIC_SHOCK = 'efef';
// Default: 1000.
constant real DUMMY_LIGHTNING_HEIGHT = 1000.;
// Default: "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdx"
constant string EFFECT_DUMMY_ELECTRIC_SHOCK = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdx";
// Deafault: 'Apg2'
constant integer ABILITY_DUMMY_PURGE = 'Apg2';
// * Upon unit dying change its color and animation speed
constant integer COLOR_LIGHTNING_BURN_RED = 00;
constant integer COLOR_LIGHTNING_BURN_GREEN = 00;
constant integer COLOR_LIGHTNING_BURN_BLUE = 00;
constant real COLOR_LIGHTNING_BURN_FADE_DURATION = 5.;
constant real LIGHTNING_BURN_ANIMATION_SPEED = .75;
// * When unit gets burned, he gets 'ELECTRIFY_SFX' effect on him
constant integer ELECTRIFY_COUNT = 4;
constant real ELECTRIFY_INTERVAL = .66;
constant string ELECTRIFY_SFX = "Abilities\\Weapons\\Bolt\\BoltImpact.mdx";
// ==============================================================================================================================================
// * Level data
function InitElectrolixZapLevelData() {
// * This is AOE (Area of Effect)/(radius) per level
// Default 1: 610
// Default 2: 660
// Default 3: 705
ELECTROLIX_ZAP_AOE[01] = 610.;
ELECTROLIX_ZAP_AOE[02] = 660.;
ELECTROLIX_ZAP_AOE[03] = 705.;
// This is damage to target per level
// Default 1: 125
// Default 2: 260
// Default 3: 400
ELECTROLIX_ZAP_DAMAGE[01] = 125.;
ELECTROLIX_ZAP_DAMAGE[02] = 260.;
ELECTROLIX_ZAP_DAMAGE[03] = 400.;
// This is the max damage spell can deal to all units
// Default 1: 1000
// Default 2: 2100
// Default 3: 3250
ELECTROLIX_ZAP_MAX_DAMAGE[01] = 1000.;
ELECTROLIX_ZAP_MAX_DAMAGE[02] = 2100.;
ELECTROLIX_ZAP_MAX_DAMAGE[03] = 3250.;
}
//***********************************************************************
// Main Unit Group Filter
function ElecutixZapGroupFilter(unit f, player caster) -> boolean {
return !IsUnitType(f, UNIT_TYPE_DEAD) &&
!IsUnitType(f, UNIT_TYPE_STRUCTURE) &&
!IsUnitType(f, UNIT_TYPE_MAGIC_IMMUNE) &&
!IsUnitType(f, UNIT_TYPE_MECHANICAL) &&
IsUnitEnemy(f, caster) &&
IsUnitVisible(f, caster) &&
!DDIsUnitWard(f) &&
!BlzIsUnitInvulnerable(f);
}
// *******************************************************************************
// * Spell other variables (non-modifiable)
// *******************************************************************************
type smokecluster extends effect[NUMBER_OF_LIGHTINING_BOLTS];
effect LastLigBolt = null;
real ELECTROLIX_ZAP_AOE[];
real ELECTROLIX_ZAP_DAMAGE[];
real ELECTROLIX_ZAP_MAX_DAMAGE[];
smokecluster pSmokeGroup[];
struct ezdata {
unit u;
real aoe;
player owner;
real x, y;
real mxDmg, dmg;
effect e[64];
integer i=00;
group g, dg;
static thistype Temp = p_null;
}
struct dmgdelay {
unit u, f;
real dmg;
integer n;
}
struct unitfade {
unit u;
real r, g, b;
real vr, vg, vb;
boolean burn=false;
static unitfade pUF[];
}
function RawDamage2Damage(unit u, real dmg) -> real {
if (IsUnitType(u, UNIT_TYPE_HERO)) {
if (UnitHasItemOfTypeBJ(u, 'brac'))
return dmg*.5;
return dmg*.75;
}
return dmg;
}
//===========================================================================
// Main init electric field function
function onInit() {
//--------------------->
//* Load init locals
//<--------------------
trigger t = CreateTrigger();
// *** Initialize constants
InitElectrolixZapLevelData();
// ###############################################################
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerAddCondition(t, Condition(function() -> boolean {
real x = GetSpellTargetX(), y = GetSpellTargetY();
unit u = GetTriggerUnit();
integer lvl = GetUnitAbilityLevel(u, GetSpellAbilityId());
integer i;
sound s;
real d;
p_real pr;
effect e;
ezdata ezd;
if (GetSpellAbilityId() != ABILITY_ELECTROLIX_ZAP)
return false;
// Play sound and shake camera for players within spell cast range
DDGenericSound(SOUND_THUNDER, SOUND_THUNDER_VOL, x, y, SOUND_MAX_DISTANCE, 1.);
static if (USE_CAMERA_NOISE)
DDCameraSetSourceNoiseForPlayers(x, y, 70., 1750., SOUND_MAX_DISTANCE, CAMERA_NOISE_DURATION);
/*
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 < SOUND_MAX_DISTANCE) {
s = CreateSound(SOUND_THUNDER, false, false, false, 10, 10, "");
SetSoundVolume(s, R2I(SOUND_THUNDER_VOL-d*(SOUND_THUNDER_VOL/SOUND_MAX_DISTANCE)));
StartSound(s);
KillSoundWhenDone(s);
s = null;
static if (USE_CAMERA_NOISE)
CameraSetSourceNoise(70.-(d*(70./SOUND_MAX_DISTANCE)), 1750.-(d*(1750./SOUND_MAX_DISTANCE)));
}
}
}
static if (USE_CAMERA_NOISE)
DDStartTim(CAMERA_NOISE_DURATION, false, p_null, function() {
CameraSetSourceNoise(0., 0.);
});
*/
// * Terrain deformation crater action
static if (USE_TERRAIN_DEFORM)
TerrainDeformCrater(x, y, ELECTROLIX_ZAP_AOE[lvl], TERRAIN_DEFORM_DEPTH, R2I(TERRAIN_DEFORM_DURATION*1000.), false);
// * Load main big thunder effect
e = AddSpecialEffect(EFFECT_GREAT_LIGHTNING, x, y);
BlzSetSpecialEffectScale(e, EFFECT_GREAT_LIGHTNING_SIZE);
BlzSetSpecialEffectTimeScale(e, EFFECT_GREAT_LIGHTNING_ANIMATION_SPEED/100.);
DestroyEffect(e); e = null;
// * Load extra lightning effects
if (NUMBER_OF_LIGHTINING_BOLTS > 00) {
pr = p_real.create();
pr[00] = Pow(ELECTROLIX_ZAP_AOE[lvl], 1.3333);//*ELECTROLIX_ZAP_AOE[lvl];
pr[01] = x; pr[02] = y;
pSmokeGroup[pr] = smokecluster.create();
DDStartTim(EFFECT_LIGHTING_BOLT_SMOKE_MODEL_DURATION, false, pSmokeGroup[pr], function() {
smokecluster sc = DDTimData();
integer i;
for(i=00; i < NUMBER_OF_LIGHTINING_BOLTS; i+=01) {
DestroyEffect(sc[i]); sc[i] = null;
}
sc.destroy();
DDQuitTim();
});
DDStartTim(LIGHTNING_BOLT_STRIKE_INTERVAL, true, pr, function() {
p_real pr = DDTimData();
real r = Pow( GetRandomReal(50., pr[00]), .75 );
real rad = GetRandomReal(0., bj_PI*2.);
effect smoke;
integer smoke_rnd_n = 00;
real x, y;
DestroyEffect(LastLigBolt);
if (DDTimTick() == NUMBER_OF_LIGHTINING_BOLTS) {
LastLigBolt = null;
pr.destroy();
DDQuitTim();
return;
}
if (EFFECT_LIGHTING_BOLT_SMOKE_MODEL_2 != "")
smoke_rnd_n += 01;
if (EFFECT_LIGHTING_BOLT_SMOKE_MODEL_3 != "")
smoke_rnd_n += 01;
smoke_rnd_n = GetRandomInt(00, smoke_rnd_n);
x = pr[01] + r * Cos(rad); y = pr[02] + r * Sin(rad);
LastLigBolt = AddSpecialEffect(EFFECT_LIGHTING_BOLT, x, y);
BlzSetSpecialEffectScale(LastLigBolt, EFFECT_LIGHTING_BOLT_SIZE);
BlzSetSpecialEffectTimeScale(LastLigBolt, EFFECT_LIGHTING_BOLT_ANIMATION_SPEED/100.);
if (smoke_rnd_n == 00)
smoke = AddSpecialEffect(EFFECT_LIGHTING_BOLT_SMOKE_MODEL_1, x, y);
else if (smoke_rnd_n == 01)
smoke = AddSpecialEffect(EFFECT_LIGHTING_BOLT_SMOKE_MODEL_2, x, y);
else
smoke = AddSpecialEffect(EFFECT_LIGHTING_BOLT_SMOKE_MODEL_3, x, y);
BlzSetSpecialEffectScale(smoke, EFFECT_LIGHTING_BOLT_SMOKE_MODEL_SIZE);
BlzSetSpecialEffectZ(smoke, DDTerrZ(x, y) + EFFECT_LIGHTING_BOLT_SMOKE_MODEL_Z_OFFSET);
pSmokeGroup[pr][00] = smoke;
pSmokeGroup[pr] += smokecluster(01);
});
}
ezd = ezdata.create();
ezd.u = u;
ezd.aoe = ELECTROLIX_ZAP_AOE[lvl];
ezd.owner = GetOwningPlayer(u);
ezd.x = x; ezd.y = y;
ezd.dmg = ELECTROLIX_ZAP_DAMAGE[lvl];
ezd.mxDmg = ELECTROLIX_ZAP_MAX_DAMAGE[lvl];
DDStartTim(THUNDER_DAMAGE_DELAY, false, ezd, function() {
ezdata.Temp = DDTimData();
ezdata.Temp.g = DDLoadGroup();
GroupEnumUnitsInRange(ezdata.Temp.g, ezdata.Temp.x, ezdata.Temp.y, ezdata.Temp.aoe, Filter(function() -> boolean {
ezdata ezd = ezdata.Temp;
if ( ElecutixZapGroupFilter(GetFilterUnit(), ezd.owner) ) {
// * Do pre-damage so taht creeps dont run away from invulnerable dummy units
if (GetPlayerController(GetOwningPlayer(GetFilterUnit())) == MAP_CONTROL_CREEP)
IssuePointOrder(GetFilterUnit(), "attack", GetUnitX(ezd.u), GetUnitY(ezd.u));
ezd.e[ezd.i] = AddSpecialEffectTarget(EFFECT_LIGHTNING_UNIT_ZAP, GetFilterUnit(), "origin");
ezd.i += 01;
}
return true;
}));
ezdata.Temp.dg = DDLoadGroup();
DDStartTim(THUNDER_DAMAGE_DELAY, false, ezdata.Temp, function() {
ezdata ezd = DDTimData();
ezdata.Temp = ezd;
ezd.dmg = RMinBJ(ezd.mxDmg/ezd.i, ezd.dmg);
ForGroup(ezd.g, function() {
ezdata ezd = ezdata.Temp;
unit u = GetEnumUnit();
real x = GetUnitX(u), y = GetUnitY(u);
real rad = GetRandomReal(0., bj_PI*2);
real dist = GetRandomReal(225., 375.);
unit d = DDLoadSpellDummy(ezd.owner, x + dist * Cos(rad), y + dist * Sin(rad), ABILITY_DUMMY_ELECTRIC_SHOCK, 01);
real dmg;
dmgdelay dd;
ezd.i -= 01;
DestroyEffect(ezd.e[ezd.i]);
ezd.e[ezd.i] = null;
if (IsUnitEnemy(u, ezd.owner) && !IsUnitType(u, UNIT_TYPE_DEAD)) {
SetUnitFlyHeight(d, DUMMY_LIGHTNING_HEIGHT, 0.);
IssueTargetOrder(d, "chainlightning", u);
DestroyEffect(AddSpecialEffectTarget(EFFECT_DUMMY_ELECTRIC_SHOCK, u, "origin"));
}
// * if unit dies, then dummy cant cast spell on that unit
GroupAddUnit(ezd.dg, d);
dmg = RawDamage2Damage(u, ezd.dmg);
dd = dmgdelay.create();
dd.u = ezd.u; dd.f = u;
dd.dmg = dmg;
dd.n = ELECTRIFY_COUNT;
DDStartTim(.2, false, dd, function() {
dmgdelay dd = DDTimData();
unitfade uf;
if (IsUnitEnemy(dd.f, GetOwningPlayer(dd.u)) && !IsUnitType(dd.f, UNIT_TYPE_DEAD)) {
UnitDamageTarget(dd.u, dd.f, dd.dmg, true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_UNKNOWN, null);
SetUnitVertexColor(dd.f, COLOR_LIGHTNING_BURN_RED, COLOR_LIGHTNING_BURN_GREEN, COLOR_LIGHTNING_BURN_BLUE, 255);
SetUnitTimeScale(dd.f, LIGHTNING_BURN_ANIMATION_SPEED);
DDStartTim(ELECTRIFY_INTERVAL, true, dd, function() {
dmgdelay dd = DDTimData();
DestroyEffect(AddSpecialEffectTarget(ELECTRIFY_SFX, dd.f, "origin"));
dd.n -= 01;
if (dd.n == 00) {
dd.destroy();
DDQuitTim();
}
});
if (!IsUnitType(dd.f, UNIT_TYPE_DEAD)) {
uf = unitfade.pUF[H2ID(dd.f)];
if (uf == p_null) {
uf = unitfade.create();
uf.u = dd.f;
unitfade.pUF[H2ID(dd.f)] = uf;
DDStartTim(DD_INTERVAL, true, uf, function() {
unitfade uf = DDTimData();
uf.r += uf.vr;
uf.g += uf.vg;
uf.b += uf.vb;
SetUnitVertexColor(uf.u, R2I(255.*Pw_2(uf.r)), R2I(255.*Pw_2(uf.g)), R2I(255.*Pw_2(uf.b)), 255);
if (uf.r > 1. || uf.burn) {
if (uf.burn)
SetUnitVertexColor(uf.u, COLOR_LIGHTNING_BURN_RED, COLOR_LIGHTNING_BURN_GREEN, COLOR_LIGHTNING_BURN_BLUE, 255);
else
SetUnitTimeScale(uf.u, 1.);
unitfade.pUF[H2ID(uf.u)] = p_null;
uf.u = null;
uf.destroy();
DDQuitTim();
}
});
}
uf.r = COLOR_LIGHTNING_BURN_RED / 255.;
uf.g = COLOR_LIGHTNING_BURN_GREEN / 255.;
uf.b = COLOR_LIGHTNING_BURN_BLUE / 255.;
uf.vr = DD_INTERVAL * (1.-uf.r) / COLOR_LIGHTNING_BURN_FADE_DURATION;
uf.vg = DD_INTERVAL * (1.-uf.g) / COLOR_LIGHTNING_BURN_FADE_DURATION;
uf.vb = DD_INTERVAL * (1.-uf.b) / COLOR_LIGHTNING_BURN_FADE_DURATION;
} else if (IsUnitType(dd.f, UNIT_TYPE_HERO)) {
SetUnitVertexColor(dd.f, 255, 255, 255, 255);
} else if (unitfade.pUF[H2ID(dd.f)] != p_null)
unitfade.pUF[H2ID(dd.f)].burn = true;
} else
dd.destroy();
DDQuitTim();
});
d = null;
u = null;
});
DDStartTim(.3, true, ezd, function() {
ezdata.Temp = DDTimData();
// First run, wait for 1 x .3 secs
if (DDTimTick() == 01) {
ForGroup(ezdata.Temp.dg, function() {
unit e = GetEnumUnit(), t = FirstOfGroup(ezdata.Temp.g);
UnitRemoveAbility(e, ABILITY_DUMMY_ELECTRIC_SHOCK);
UnitAddAbility(e, ABILITY_DUMMY_PURGE);
SetUnitX(e, GetUnitX(t)); SetUnitY(e, GetUnitY(t));
IssueTargetOrder(e, "purge", t);
GroupRemoveUnit(ezdata.Temp.g, t);
e = null; t = null;
});
return;
}
// Second run, wait for 2 x .3 secs
ForGroup(ezdata.Temp.dg, function() {
UnitRemoveAbility(GetEnumUnit(), ABILITY_DUMMY_PURGE);
DDRecycleDummy(GetEnumUnit());
});
DDRecycleGroup(ezdata.Temp.g);
DDRecycleGroup(ezdata.Temp.dg);
ezdata.Temp.destroy();
DDQuitTim();
});
DDQuitTim();
});
DDQuitTim();
});
return false;
}));
t = null;
}
}
//! endzinc
[B]
[/B]
//TESH.scrollpos=15
//TESH.alwaysfold=0
//**************************************************************************************
// Installation:
//
// - 1. Copy this trigger and "DD Library" to your map
// - 2. Go to Import Manager (F12) and extract BTNDarkTouch.blp and its DISBTN version
// sword_create.wav, sword_disappere.wav, sword_cut.wav and sword_start.wav
// Frostmourne.mdx and DarkRitualTarget.mdx
// - 3. Import all of this to your map and set ther path to as you can see here (in my map)
// - 4. Go to Object Manager (F6) and copy "Frostmourne Impale Slow(Dummy)" ability and
// main hero ability "Frostmourne Impale". When you are pasting them to your map make sure you
// match there rawcodes with ones below
// - 5. If you want different raw codes edit the below as well
// - 6. Made on Warcraft III v1.30
// - 7. Edit data below to your own will and enjoy the spell
//
//
//**************************************************************************************
//! zinc
library FrostmourneImpale requires DDLib
{
// *****************************************************************************************
// === Main Data ===
// *****************************************************************************************
// This is the raw code of the spell 'Frostmourne Impale'
// Default: 'Drkt'
constant integer ABILITY_FROSTMOURNE_IMPALE = 'Drkt';
// This is the raw code of the sword 'Unit'
// Default: 'drks'
//constant integer DUMMY_SWORD_RAWCODE = 'drks';
// This is the raw code of 'Frostmourne Impale Slow (dummy)' ability
// Default: 'dtsd'
constant integer DUMMY_SLOW_RAWCODE = 'dtsd';
// Issue order for 'DUMMY_SLOW_RAWCODE' ability
// Default: "slow"
constant string DUMMY_SLOW_ISSUEORDER = "slow";
// How offten are swords created, one after another
// Default: 0.2
constant real SWORD_CREATION_INTERVAL = 0.2;
// Distance from caster and closest sword
// Default: 250.
constant real SWORD_CREATION_OFFSET_FROM_CASTER = 250.;
// Distance from sword to sword
// Default: 70.
constant real SWORD_CREATION_OFFSET_FROM_SWORD = 70.;
// Time it takes for sword to launch
// Default: 1.
constant real SWORD_CREATE_LAUNCH_DURATION = 1.;
// Height of sword
// Default: 110.
constant real SWORD_HEIGHT = 110.;
// Collision area around the sword, if units are found within it, they are hit
// Default: 60.
constant real SWORD_COLLISION_SIZE = 60.;
// Sword creation start transparency / Sword disappear end transparency
// Default: 00
constant integer SWORD_CREATE_START_ALPHA = 00;
// Sword creation end transparency / Sword disappear start transparency
// Default: 255
constant integer SWORD_CREATE_END_ALPHA = 255;
// Time it takes for sword to appear
// Default: 0.9
constant real SWORD_CREATE_FADE_DURATION = .9;
// Time it takes for sword to disappear
// Default: 2.5
constant real SWORD_DISAPPEAR_DURATION = 2.5;
// Disappearing speed of sword
// Default: 100.
constant real SWORD_DISAPPEAR_MOVE_SPEED = 100.;
// *****************************************************************************************
// === Effect ===
// *****************************************************************************************
// This is the file path to sword model
// Default: "Spells\\FrostmourneImpale\\Frostmourne.mdx"
constant string SWORD_MODEL_PATH = "Spells\\FrostmourneImpale\\Frostmourne.mdx";
// This is size/scaling of sword
// Default: 1.
constant real SWORD_MODEL_SIZE = 1.;
// This is trail effect missile of each sword
// Default: "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl"
constant string SWORD_AFTER_EFFECT_SFX = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdx";
// Appear and disappear sfx on sword
// Default: "Spells\\FrostmourneImpale\\DarkRitualTarget.mdx"
constant string SWORD_CREATE_SFX = "Spells\\FrostmourneImpale\\DarkRitualTarget.mdx";
// Trail effect and appear/disappear effect height
// Default: 60.
constant real SWORD_CREATE_SFX_HEIGHT = 60.;
// Trail effect and appear/disappear effect size
// Default: 0.4
constant real SWORD_CREATE_SFX_SIZE = .4;
// Blood effect upon sword hit
// Default: "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
constant string BLOOD_EFFECT = "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl";
// *****************************************************************************************
// === Sound ===
// *****************************************************************************************
// Sound path of sword creation
// Default: "Custom\\Spells\\Dark Torture\\sword_create.wav"
constant string SWORD_CREATE_SOUND_PATH = "Custom\\Spells\\Frostmourne Impale\\sword_create.wav";
constant real SWORD_CREATE_SOUND_PATH_VOL = 55.;
// Sound path of sword launch
// Default: "Custom\\Spells\\Dark Torture\\sword_start.wav"
constant string SWORD_SOUND_START_PATH = "Custom\\Spells\\Frostmourne Impale\\sword_start.wav";
constant real SWORD_SOUND_START_PATH_VOL = 80.;
// Sound path of sword hiting the target
// Default: "Custom\\Spells\\Dark Torture\\sword_hit.wav"
constant string SWORD_HIT_PATH = "Custom\\Spells\\Frostmourne Impale\\sword_cut.wav";
constant real SWORD_HIT_PATH_VOL = 70.;
// Sound of swords disappearing
// Default: "Custom\\Spells\\Dark Torture\\sword_disappere.wav"
constant string SWORD_DISAPPEAR_PATH = "Custom\\Spells\\Frostmourne Impale\\sword_disappere.wav";
constant real SWORD_DISAPPEAR_PATH_VOL = 70.;
// This is max sound hear range
// Default: 3500.
constant real SOUND_MAX_DISTANCE = 3500.;
// *****************************************************************************************
// === Level Data ===
// *****************************************************************************************
// --------------------------------------------------------------
// This variables are not supposed to be modified here, modify them below in Setup function
integer MAX_SWORD_COUNT[];
real SWORD_MAX_TRAVEL_DISTANCE[];
real SINGLE_SWORD_DAMAGE[];
real SWORD_MOVE_SPEED[];
// --------------------------------------------------------------
// This part of code is used to define spell constants per level
function FrostmourneImpale_Setup() {
// Default: Level 1 = 8
// Default: Level 2 = 9
// Default: Level 3 = 10
MAX_SWORD_COUNT[01] = 8;
MAX_SWORD_COUNT[02] = 9;
MAX_SWORD_COUNT[03] = 10;
// Default: Level 1 = 125.
// Default: Level 2 = 220.
// Default: Level 3 = 300.
SINGLE_SWORD_DAMAGE[01] = 125.;
SINGLE_SWORD_DAMAGE[02] = 220.;
SINGLE_SWORD_DAMAGE[03] = 300.;
// Default: Level 1 = 1250.
// Default: Level 2 = 1350.
// Default: Level 3 = 1500.
SWORD_MAX_TRAVEL_DISTANCE[01] = 1250.;
SWORD_MAX_TRAVEL_DISTANCE[02] = 1350.;
SWORD_MAX_TRAVEL_DISTANCE[03] = 1500.;
// Default: Level 1 = 900.
// Default: Level 2 = 1100.
// Default: Level 3 = 1200.
SWORD_MOVE_SPEED[01] = 900.;
SWORD_MOVE_SPEED[02] = 1100.;
SWORD_MOVE_SPEED[03] = 1200.;
}
// *****************************************************************************************
// === Unit Damage Filter ===
// *****************************************************************************************
function FrostmourneImpaleUnitFilter(unit f, player owner) -> boolean {
return !IsUnitType(f, UNIT_TYPE_STRUCTURE) &&
!IsUnitType(f, UNIT_TYPE_MECHANICAL) &&
!IsUnitType(f, UNIT_TYPE_DEAD) &&
!IsUnitType(f, UNIT_TYPE_MAGIC_IMMUNE) &&
!IsUnitType(f, UNIT_TYPE_FLYING) &&
!BlzIsUnitInvulnerable(f) &&
IsUnitEnemy(f, owner);
}
// ==========================================================================================
// *** Spell Code ***
// ==========================================================================================
struct sword {
ddeffect s;
player p;
unit dmy;
ddeffect trail;
real x, y;
real vx, vy;
real dist, spd;
integer lvl;
group g;
boolean disappear_off;
static player Owner = null;
static method create(real px, real py, real rad, player owner, integer abilLvl) -> thistype {
thistype this = allocate();
real tz = DDTerrZ(px, py);
g = DDLoadGroup();
s = ddeffect.createZ(SWORD_MODEL_PATH, px, py, SWORD_HEIGHT + tz, rad, SWORD_MODEL_SIZE);
s.Fade(SWORD_CREATE_START_ALPHA/2.55, SWORD_CREATE_END_ALPHA/2.55, SWORD_CREATE_FADE_DURATION);
p = owner;
//s = CreateUnit(owner, DUMMY_SWORD_RAWCODE, px, py, rad*bj_RADTODEG);
//SetUnitFlyHeight(s, SWORD_HEIGHT, 0.);
lvl = abilLvl;
x = px; y = py;
vx = DD_INTERVAL * SWORD_MOVE_SPEED[lvl] * Cos(rad);
vy = DD_INTERVAL * SWORD_MOVE_SPEED[lvl] * Sin(rad);
dist = SWORD_MAX_TRAVEL_DISTANCE[lvl];
spd = DD_INTERVAL*SWORD_MOVE_SPEED[lvl];
disappear_off = true;
trail = ddeffect.createZ(SWORD_AFTER_EFFECT_SFX, x, y, SWORD_HEIGHT + tz, rad, SWORD_CREATE_SFX_SIZE);
DDGenericSound(SWORD_CREATE_SOUND_PATH, SWORD_CREATE_SOUND_PATH_VOL, px, py, SOUND_MAX_DISTANCE, 1.);
//DDFadeUnit(s, SWORD_CREATE_START_ALPHA, SWORD_CREATE_END_ALPHA, SWORD_CREATE_FADE_DURATION);
ddeffect.createZ(SWORD_CREATE_SFX, px, py, SWORD_CREATE_SFX_HEIGHT + tz, rad, SWORD_CREATE_SFX_SIZE).destroyx(SWORD_DISAPPEAR_DURATION);
return this;
}
method SwordLaunch() {
DDGenericSound(SWORD_SOUND_START_PATH, SWORD_SOUND_START_PATH_VOL, x, y, SOUND_MAX_DISTANCE, 1.);
DDStartTim(DD_INTERVAL, true, this, function() {
sword this = DDTimData();
unit fg;
x += vx; y += vy;
s.Position(x, y);
trail.Position(x, y);
/*SetUnitX(s, x);
SetUnitY(s, y);
trail.X = x;
trail.Y = y;*/
if (g != null) {
if (IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)) {
DDRecycleGroup(g);
dist = 0.;
g = null;
return;
}
Owner = p;
GroupEnumUnitsInRange(g, x, y, SWORD_COLLISION_SIZE, Filter(function() -> boolean {
return FrostmourneImpaleUnitFilter(GetFilterUnit(), Owner);
}));
fg = FirstOfGroup(g);
if (fg != null) {
dmy = DDLoadSpellDummy(Owner, x, y, DUMMY_SLOW_RAWCODE, 01);
IssueTargetOrder(dmy, DUMMY_SLOW_ISSUEORDER, fg);
UnitDamageTarget(dmy, fg, SINGLE_SWORD_DAMAGE[lvl], true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_METAL_HEAVY_STAB);
DestroyEffect(AddSpecialEffectTarget(BLOOD_EFFECT, fg, "origin"));
DDGenericSound(SWORD_HIT_PATH, SWORD_HIT_PATH_VOL, x, y, SOUND_MAX_DISTANCE, 1.);
DDRecycleGroup(g);
dist = 0.;
g = null;
}
fg = null;
}
dist -= spd;
if (dist < 0.) {
if (disappear_off) {
trail.destroy();
trail = ddeffect.createZ(SWORD_CREATE_SFX, x, y, SWORD_CREATE_SFX_HEIGHT+DDTerrZ(x, y), trail.Facing, SWORD_CREATE_SFX_SIZE);
//trail.Z = SWORD_HEIGHT;
//trail.Pitch = 00;
DDGenericSound(SWORD_DISAPPEAR_PATH, SWORD_DISAPPEAR_PATH_VOL, x, y, SOUND_MAX_DISTANCE, 1.);
s.Fade(SWORD_CREATE_END_ALPHA/2.55, SWORD_CREATE_START_ALPHA/2.55, SWORD_DISAPPEAR_DURATION);
//DDFadeUnit(s, SWORD_CREATE_END_ALPHA, SWORD_CREATE_START_ALPHA, SWORD_DISAPPEAR_DURATION);
dist = SWORD_DISAPPEAR_MOVE_SPEED*SWORD_DISAPPEAR_DURATION;
vx = DD_INTERVAL*SWORD_DISAPPEAR_MOVE_SPEED*vx/spd;
vy = DD_INTERVAL*SWORD_DISAPPEAR_MOVE_SPEED*vy/spd;
spd = DD_INTERVAL*SWORD_DISAPPEAR_MOVE_SPEED;
disappear_off = false;
if (g != null) {
DDRecycleGroup(g);
g = null;
}
} else {
if (dmy != null) {
UnitRemoveAbility(dmy, DUMMY_SLOW_RAWCODE);
DDRecycleDummy(dmy);
dmy = null;
}
trail.destroy();
s.destroy();
//RemoveUnit(s);
//s = null;
destroy();
DDQuitTim();
}
}
});
}
}
struct data {
unit u;
real x, y;
real tx, ty;
integer lvl;
real rad;
real ox, oy;
integer rnd_mx_n;
integer rnd[32];
}
function onInit() {
trigger t = CreateTrigger();
FrostmourneImpale_Setup();
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerAddCondition(t, Condition(function() -> boolean {
data d;
integer i;
if (GetSpellAbilityId() != ABILITY_FROSTMOURNE_IMPALE)
return false;
d = data.create();
d.u = GetTriggerUnit();
d.x = GetWidgetX(d.u); d.y = GetWidgetY(d.u);
d.tx = GetSpellTargetX(); d.ty = GetSpellTargetY();
d.lvl = GetUnitAbilityLevel(d.u, ABILITY_FROSTMOURNE_IMPALE);
d.rad = Atan2(d.y-d.ty, d.x-d.tx);
d.ox = d.x + SWORD_CREATION_OFFSET_FROM_CASTER * Cos(d.rad);
d.oy = d.y + SWORD_CREATION_OFFSET_FROM_CASTER * Sin(d.rad);
d.rnd_mx_n = MAX_SWORD_COUNT[d.lvl];
d.rad += bj_PI/2.;
for(i=00; i < MAX_SWORD_COUNT[d.lvl]; i+=01) {
d.rnd[i] = i-(MAX_SWORD_COUNT[d.lvl]/2);
}
DDStartTim(SWORD_CREATION_INTERVAL, true, d, function() {
data d = DDTimData();
sword s;
integer rnd_n = GetRandomInt(00, d.rnd_mx_n-01);
s = sword.create(d.ox + SWORD_CREATION_OFFSET_FROM_SWORD*d.rnd[rnd_n] * Cos(d.rad),
d.oy + SWORD_CREATION_OFFSET_FROM_SWORD*d.rnd[rnd_n] * Sin(d.rad),
(d.rad+(bj_PI/2)),
GetOwningPlayer(d.u),
d.lvl);
DDStartTim(SWORD_CREATE_LAUNCH_DURATION, false, s, function() {
sword(DDTimData()).SwordLaunch();
DDQuitTim();
});
d.rnd_mx_n -= 01;
d.rnd[rnd_n] = d.rnd[d.rnd_mx_n];
if (d.rnd_mx_n == 00) {
d.destroy();
DDQuitTim();
}
});
return false;
}));
}
}
//! endzinc
[B]
//TESH.scrollpos=0
//TESH.alwaysfold=0
//**************************************************************************************
// Installation:
//
//
// - 1. Copy this trigger and DD Library to your map.
// - 2. Go to Import Manager (F12) and extract icons: BTNCRFrostShock.blp and its DISBTN version
// sounds: fe_start.mp3, fe_explode1.mp3 and fe_explode2.mp3
// Models: FrozenOrb.mdx, Frost2.mdx, FreezingRing.mdx, BlizzardEruption.mdx and (dummy.mdx -> if not from before)
// - 3. Import all of this to your map and set there path to as you can see here (in my map)
// - 4. Go to Object Manager (F6) and copy:
// Air Freeze (Dummy) ability, Frost Eruption buff and
// main ability Frost Eruption. When you are pasting them to your map make sure you
// type the following rawcodes:
// Frost Eruption: 'Feru'
// Air Freeze (Dummy): 'Afre'
// Frost Eruption {Buff}: 'Bfer'
// - 5. If you want different raw codes edit the below as well:
// - 6. Made on Warcraft III v1.30
// - 7. Edit data below to your own will and enjoy the spell:
//
//**************************************************************************************
//! zinc
library FrostEruption requires DDLib
{
// The raw code of the spell Frost Eruption
// Default: 'Feru'
constant integer ABILITY_FROST_ERUPTION = 'Feru';
// This is scale / size of main orb
// Default: 1.32
constant real ORB_SCALE = 1.32;
constant real ORB_INITIAL_Z = 70.;
constant real ORB_MAX_HEIGHT = 350.;
constant real ORB_TRAVEL_TIME = 2.;
// With this ability air units are slowed
// Default: 'Afre'
constant integer ABILITY_AIR_FREEZE = 'Afre';
constant integer BUFF_AIR_FREEZE = 'Bfer';
// This is orb model path
// Default: "Spells\\FrostEruption\\FrozenOrb.mdl"
constant string ORB_MODEL_PATH = "Spells\\FrostEruption\\FrozenOrb.mdx";
// Main frost eruption effect
// Default: "Spells\\FrostEruption\\BlizzardEruption.mdl"
constant string ORB_ERUPTION_EFFECT = "Spells\\FrostEruption\\BlizzardEruption.mdx";
// Default: 2.
constant real ORB_ERUPTION_EFFECT_SIZE = 2.;
// Default: 70.
constant real ORB_ERUPTION_EFFECT_ANIMATION_SPEED = 70.;
// Frost Eruptions extra explosion effect
// Default: "Spells\\FrostEruption\\FreezingRing.mdl"
constant string ORB_EXPLOSION_EFFECT = "Spells\\FrostEruption\\FreezingRing.mdx";
// Default: 1.6
constant real ORB_EXPLOSION_EFFECT_SIZE = 1.6;
// Default: 55.
constant real ORB_EXPLOSION_EFFECT_ANIMATION_SPEED = 55.;
// Ground units freezed model path
// Default: "Abilities\\Spells\\Undead\\FreezingBreath\\FreezingBreathTargetArt.mdl"
constant string FREEZE_EFFECT = "Abilities\\Spells\\Undead\\FreezingBreath\\FreezingBreathTargetArt.mdx";
// This is orb's trail effect
// Default: "Spells\\FrostEruption\\Frost2.mdx"
constant string TRAIL_MODEL_PATH = "Spells\\FrostEruption\\Frost2.mdx";
// This is orb's trail effect size
// Default: 1.
constant real TRAIL_SCALE = 1.;
// Sound file on load (on spell cast)
// Default: "Custom\\Spells\\Frost Eruption\\fe_start.mp3"
constant string SPELL_START_SOUND = "Custom\\Spells\\Frost Eruption\\fe_start.mp3";
constant real SPELL_START_SOUND_VOL = 75.;
constant real SOUND_MAX_DISTANCE = 3500.;
// Sound file's on explosion
// Default: "Custom\\Spells\\Frost Eruption\\fe_explode1.mp3"
constant string SPELL_EXPLODE_SOUND_1 = "Custom\\Spells\\Frost Eruption\\fe_explode1.mp3";
constant real SPELL_EXPLODE_SOUND_VOL = 75.; // Both 1 and 2 sounds volume
// Default: "Custom\\Spells\\Frost Eruption\\fe_explode2.mp3"
constant string SPELL_EXPLODE_SOUND_2 = "Custom\\Spells\\Frost Eruption\\fe_explode2.mp3";
// Terrain deformation depth
// Default: 180.
constant real EXPLOSION_DEAPTH = 180.;
// Terrain deformatiob timeout
// Default: 0.66
constant real TERRAIN_DEFORMATION_DURATION = 0.66;
// Cameras power magnitude
// Default: 20.
constant real CAMERA_MAGNITUDE = 20.;
// Cameras power velocity
// Default: 1800.
constant real CAMERA_VELOCITY = 1800.;
// Camera noise duration
// Default: 1.85
constant real CAMERA_NOISE_DURATION = 1.85;
// How much time will orb need until reaching target point
// Default: 1.5
constant real ORB_DURATION = 1.5;
// Alter terrain upon explosion?
// Default: true
constant boolean USE_TERRAIN_TYPE_MODIFICATION = true;
// Default: 'Iice'
constant integer TERRAIN_TEXTURE_ID = 'Iice';
// Default: 01 / square
constant integer TERRAIN_TEXTURE_SHAPE = 01;
// Default: 17
constant integer TERRAIN_TEXTURE_VARIATIONS = 17;
// Default: 7.
constant real TERRAIN_TEXTURE_DURATION = 7.;
// Do not edit this variables here edit then below
real FE_RADIUS[];
real FE_DAMAGE[];
real FE_MAX_DAMAGE[];
real FE_DURATION[];
// Edit this to match your level data
function SetupFrostEruption() {
// Spells damage:
// Default: 130 / 215 / 290
FE_DAMAGE[01] = 130.;
FE_DAMAGE[02] = 215.;
FE_DAMAGE[03] = 290.;
// Spells radius:
// Default: 500 / 515 / 540
FE_RADIUS[01] = 500.;
FE_RADIUS[02] = 515.;
FE_RADIUS[03] = 540.;
// Spells max damage:
// Default: 700 / 1300 / 1950
FE_MAX_DAMAGE[01] = 700.;
FE_MAX_DAMAGE[02] = 1300.;
FE_MAX_DAMAGE[03] = 1950.;
// Spells ground units freeze duration:
// Default: 4 / 4.5 / 5.25
FE_DURATION[01] = 4.;
FE_DURATION[02] = 4.5;
FE_DURATION[03] = 5.25;
}
// Main damage filter
function FE_UnitFilter(unit f, player owner) -> boolean {
return IsUnitEnemy(f, owner) &&
!IsUnitType(f, UNIT_TYPE_STRUCTURE) &&
!IsUnitType(f, UNIT_TYPE_MECHANICAL) &&
!IsUnitType(f, UNIT_TYPE_DEAD) &&
!BlzIsUnitInvulnerable(f) &&
!IsUnitType(f, UNIT_TYPE_MAGIC_IMMUNE);
}
// ================================================================================
// ****** ******
// Do not edit below if you don't know jass
// ****** ******
// ================================================================================
constant integer TT_MAX_ARRAY = 64;
struct terrtype {
integer typ[TT_MAX_ARRAY], var[TT_MAX_ARRAY];
boolean blighted[TT_MAX_ARRAY];
real x, y, aoe;
integer n=00;
group g;
static integer BlightAbilId[];
}
function ModifieTerrainType(real x, real y, real aoe, terrtype changeToTT) -> terrtype {
real tx, ty, max_x;
integer n=00;
terrtype tt;
if (changeToTT == p_null) {
tt = terrtype.create();
tt.g = DDLoadGroup();
GroupEnumUnitsInRange(tt.g, x, y, aoe+960., function() -> boolean {
unit u = GetFilterUnit();
if (GetUnitAbilityLevel(u, 'Abgs') == 01) {
terrtype.BlightAbilId[H2ID(u)] = 'Abgs';
UnitRemoveAbility(u, 'Abgs');
u = null;
return true;
} else if (GetUnitAbilityLevel(u, 'Abgl') == 01) {
terrtype.BlightAbilId[H2ID(u)] = 'Abgs';
UnitRemoveAbility(u, 'Abgl');
u = null;
return true;
}
u = null;
return false;
});
tt.x = x; tt.y = y; tt.aoe = aoe;
for(ty=-aoe; ty < aoe; ty+=bj_CELLWIDTH) {
max_x = SquareRoot((aoe*aoe) - (ty*ty));
for(tx=-max_x; tx < max_x; tx+=bj_CELLWIDTH) {
tt.blighted[tt.n] = IsPointBlighted(x+tx, y+ty);
if (!(tt.blighted[tt.n])) {
tt.typ[tt.n] = GetTerrainType(x+tx, y+ty);
tt.var[tt.n] = GetTerrainVariance(x+tx, y+ty);
} else
SetBlightPoint(Player(00), x+tx, y+ty, false);
tt.n += 01;
SetTerrainType(x+tx, y+ty, TERRAIN_TEXTURE_ID, GetRandomInt(00, TERRAIN_TEXTURE_VARIATIONS), 01, TERRAIN_TEXTURE_SHAPE);
}
}
return tt;
}
tt = changeToTT;
x = tt.x; y = tt.y; aoe = tt.aoe;
for(ty=-aoe; ty < aoe; ty+=bj_CELLWIDTH) {
max_x = SquareRoot((aoe*aoe) - (ty*ty));
for(tx=-max_x; tx < max_x; tx+=bj_CELLWIDTH) {
if (!(tt.blighted[n]))
SetTerrainType(x+tx, y+ty, tt.typ[n], tt.var[n], 01, TERRAIN_TEXTURE_SHAPE);
else
SetBlightPoint(Player(00), x+tx, y+ty, true);
n += 01;
}
}
ForGroup(tt.g, function() {
unit u = GetEnumUnit();
UnitAddAbility(u, terrtype.BlightAbilId[H2ID(u)]);
u = null;
});
DDRecycleGroup(tt.g);
tt.destroy();
return p_null;
}
// --------------------------------------------------------------------------------------------
effect FBEffect[];
struct orb {
ddeffect o, ot;
real vx, vy, tz;
real dist, mxDist, a;
real spd;
integer lvl;
player owner;
unit u;
real dmg;
group g;
unit dmy;
integer cnt=00;
terrtype tt;
static method create(unit u, real x, real y, real tx, real ty, player owner, integer lvl) -> thistype {
thistype this = allocate();
real rad = Atan2(ty-y, tx-x);
mxDist = SquareRoot( Pw_2(x-tx) + Pw_2(y-ty) ) / 2.;
dist = -mxDist;
a = -ORB_MAX_HEIGHT/Pw_2(mxDist);
tz = DDTerrZ(x, y);
o = ddeffect.createZ(ORB_MODEL_PATH, x, y, ORB_INITIAL_Z+tz,rad, ORB_SCALE);
ot = ddeffect.createZ(TRAIL_MODEL_PATH, x, y, ORB_INITIAL_Z+tz, rad, TRAIL_SCALE);
//o.Z = ORB_INITIAL_Z;
spd = DD_INTERVAL * (2.*mxDist / ORB_TRAVEL_TIME);
vx = spd * Cos(rad);
vy = spd * Sin(rad);
//tz = o.WZ;
this.owner = owner;
this.lvl = lvl;
this.u = u;
return this;
}
method LaunchOrb() {
DDStartTim(DD_INTERVAL, true, this, function() {
orb this = DDTimData();
ddeffect oe;
effect e;
dist += spd;
o.PositionZ(o.X + vx, o.Y + vy, a*Pw_2(dist) + ORB_MAX_HEIGHT+tz);
ot.PositionZ(o.X, o.Y, o.Z);
/*o.X += vx; o.Y += vy;
o.Z = (tz-o.WZ) + a*Pw_2(dist) + ORB_MAX_HEIGHT;
ot.X = o.X; ot.Y = o.Y; ot.Z = o.Z;*/
if (dist > mxDist) {
g = DDLoadGroup();
DDGFilterDataSet(this);
GroupEnumUnitsInRange(g, o.X, o.Y, FE_RADIUS[lvl], Filter(function() -> boolean {
if (FE_UnitFilter(GetFilterUnit(), orb(DDGFilterData()).owner)) {
orb(DDGFilterData()).cnt += 01;
return true;
}
return false;
}));
if (cnt > 00) {
dmg = FE_MAX_DAMAGE[lvl] / cnt; // division by zero?
if (dmg > FE_DAMAGE[lvl])
dmg = FE_DAMAGE[lvl];
ForGroup(g, function() {
orb o = DDGFilterData();
unit e = GetEnumUnit();
UnitDamageTarget(o.u, e, o.dmg, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null);
if (!IsUnitType(e, UNIT_TYPE_FLYING) && !IsUnitType(e, UNIT_TYPE_DEAD) && FBEffect[H2ID(e)] == null) {
FBEffect[H2ID(e)] = AddSpecialEffectTarget(FREEZE_EFFECT, e, "origin");
PauseUnit(e, true);
}
e = null;
});
dmy = DDLoadSpellDummy(owner, o.X, o.Y, ABILITY_AIR_FREEZE, lvl);
UnitRemoveAbility(dmy, 'Aloc');
IssueTargetOrder(dmy, "frostnova", dmy);
DDStartTim(FE_DURATION[lvl]-.05, false, this, function() {
orb o = DDTimData();
UnitAddAbility(o.dmy, 'Aloc');
UnitRemoveAbility(o.dmy, ABILITY_AIR_FREEZE);
DDRecycleDummy(o.dmy);
ForGroup(o.g, function() {
unit e = GetEnumUnit();
if (!IsUnitType(e, UNIT_TYPE_FLYING) /*&& GetUnitAbilityLevel(e, BUFF_AIR_FREEZE) != 00*/) {
PauseUnit(e, false);
UnitRemoveAbility(e, BUFF_AIR_FREEZE);
DestroyEffect(FBEffect[H2ID(e)]);
FBEffect[H2ID(e)] = null;
}
e = null;
});
DDRecycleGroup(o.g);
o.destroy();
DDQuitTim();
});
} else
DDRecycleGroup(g);
static if (USE_TERRAIN_TYPE_MODIFICATION)
DDStartTim(TERRAIN_TEXTURE_DURATION, false, ModifieTerrainType(o.X, o.Y, FE_RADIUS[lvl]-bj_CELLWIDTH, p_null), function() {
ModifieTerrainType(0., 0., 0., terrtype( DDTimData() ));
DDQuitTim();
});
TerrainDeformCrater(o.X, o.Y, FE_RADIUS[lvl], EXPLOSION_DEAPTH, R2I(TERRAIN_DEFORMATION_DURATION*1000.), false);
DDCameraSetSourceNoiseForPlayers(o.X, o.Y, CAMERA_MAGNITUDE, CAMERA_VELOCITY, SOUND_MAX_DISTANCE, CAMERA_NOISE_DURATION);
oe = ddeffect.create(ORB_ERUPTION_EFFECT, o.X, o.Y, GetRandomReal(0., bj_PI*2.), ORB_ERUPTION_EFFECT_SIZE)/*.destroyx(2.)*/;
oe.Animation(ANIM_TYPE_BIRTH);
oe.AnimationSpeed(ORB_ERUPTION_EFFECT_ANIMATION_SPEED);
oe.destroyx(2.*(ORB_ERUPTION_EFFECT_ANIMATION_SPEED/100.));
//DestroyEffectTimed(AddSpecialEffect(ORB_ERUPTION_EFFECT, o.X, o.Y), 3.);
e = AddSpecialEffect(ORB_EXPLOSION_EFFECT, o.X, o.Y);
BlzSetSpecialEffectScale(e, ORB_EXPLOSION_EFFECT_SIZE);
BlzSetSpecialEffectTimeScale(e, ORB_EXPLOSION_EFFECT_ANIMATION_SPEED/100.);
DestroyEffect(e); e = null;
if (GetRandomInt(00, 01) == 01)
DDGenericSound(SPELL_EXPLODE_SOUND_1, SPELL_EXPLODE_SOUND_VOL, o.X, o.Y, SOUND_MAX_DISTANCE, 1.);
else
DDGenericSound(SPELL_EXPLODE_SOUND_2, SPELL_EXPLODE_SOUND_VOL, o.X, o.Y, SOUND_MAX_DISTANCE, 1.);
//ot.X = DDMaxX; ot.Y = DDMaxY;
o.destroy();
ot.destroy();
DDQuitTim();
}
});
}
}
//====================================================================================
// Main Setup Function For FE
function onInit() {
// Load locals...
trigger tg = CreateTrigger();
// Setup user spell
SetupFrostEruption();
// Register trigger events
TriggerRegisterAnyUnitEventBJ(tg, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerRegisterAnyUnitEventBJ(tg, EVENT_PLAYER_UNIT_DEATH);
TriggerAddCondition(tg, Condition(function() -> boolean {
unit u;
real x, y,
tx, ty;
orb o;
if (GetTriggerEventId() == EVENT_PLAYER_UNIT_DEATH) {
u = GetTriggerUnit();
if (FBEffect[H2ID(u)] != null) {
PauseUnit(u, false);
DestroyEffect(FBEffect[H2ID(u)]);
FBEffect[H2ID(u)] = null;
}
u = null;
return false;
}
if (GetSpellAbilityId() != ABILITY_FROST_ERUPTION)
return false;
u = GetTriggerUnit();
x = GetWidgetX(u); y = GetWidgetY(u);
tx = GetSpellTargetX(); ty = GetSpellTargetY();
o = orb.create(u, x, y, tx, ty, GetOwningPlayer(u), GetUnitAbilityLevel(u, ABILITY_FROST_ERUPTION));
DDGenericSound(SPELL_START_SOUND, SPELL_START_SOUND_VOL, SOUND_MAX_DISTANCE, x, y, 1.);
o.LaunchOrb();
u = null;
return false;
}));
}
}
//! endzinc[/B]
[B]
[/B]
//TESH.scrollpos=24
//TESH.alwaysfold=0
//**************************************************************************************
// Installation:
//
//
// - 1. Made on Warcraft III v1.30
// - 2. Copy this trigger and DD Library to your map.
// - 3. Go to Import Manager (F12) and extract icons: BTNAlgirAura.blp and its: DISBTN, PAS and DISPAS version
// - 4. Import all of this to your map and set there path to as you can see here (in my map)
// - 5. Go to Object Manager (F6) and copy:
// abilities: Algid Aura (Dummy) and main ability Algid Aura
// buffs: Algid Aura (Buff) and Algid Aura (Ability Buff)
// When you are pasting them to your map make sure you
// type the following rawcodes or modify them below:
// Algid Aura: 'Algd'
// Algid Aura (Dummy): 'Aldm'
// Algid Aura (Buff): 'Alga'
// Algid Aura (Ability Buff): 'AlgB'
// - 6. Edit data below to your own will and enjoy the aura:
//
//**************************************************************************************
//! zinc
library AlgidAura requires DDLib
{
// *****************************************************************************************
// === Main Data ===
// *****************************************************************************************
// Default: 'Algd'
constant integer ABILITY_ALGID_AURA = 'Algd';
// Default: 'Aldm'
constant integer ABILITY_ALGID_AURA_DUMMY = 'Aldm';
// Default: "frostnova"
constant string ABILITY_ALGID_AURA_DUMMY_ORDER_ID = "frostnova";
real AA_PULSE_WIDTH[];
// *****************************************************************************************
// Edit this to match your level data
function SetupAlgidAura() {
// Spells damage:
// Default: 5 / 5 / 4
AA_PULSE_WIDTH[01] = 5.;
AA_PULSE_WIDTH[02] = 5.;
AA_PULSE_WIDTH[03] = 4.;
}
// *****************************************************************************************
timer AATim[];
struct data {
unit u;
unit d;
static thistype Data[];
}
//====================================================================================
// Main Setup Function For FE
function onInit() {
// Load locals...
trigger tg = CreateTrigger();
// Setup user spell
SetupAlgidAura();
// Register trigger events
TriggerRegisterAnyUnitEventBJ(tg, EVENT_PLAYER_HERO_SKILL);
TriggerAddCondition(tg, Condition(function() -> boolean {
unit u;
integer id;
data d;
if (GetLearnedSkill() != ABILITY_ALGID_AURA)
return false;
u = GetTriggerUnit();
id = H2ID(u);
if (AATim[id] == null) {
AATim[id] = CreateTimer();
d = data.create();
data.Data[H2ID(AATim[id])] = d;
d.u = u; d.d = DDLoadDummy();
UnitRemoveAbility(d.d, 'Aloc');
SetUnitOwner(d.d, GetOwningPlayer(u), false);
UnitAddAbility(d.d, ABILITY_ALGID_AURA_DUMMY);
} else
d = data.Data[H2ID(AATim[id])];
SetUnitAbilityLevel(d.d, ABILITY_ALGID_AURA_DUMMY, GetLearnedSkillLevel());
TimerStart(AATim[id], AA_PULSE_WIDTH[GetLearnedSkillLevel()], true, function() {
data d = data.Data[H2ID(GetExpiredTimer())];
if (!IsUnitType(d.u, UNIT_TYPE_DEAD)) {
SetUnitX(d.d, GetWidgetX(d.u));
SetUnitY(d.d, GetWidgetY(d.u));
IssueTargetOrder(d.d, ABILITY_ALGID_AURA_DUMMY_ORDER_ID, d.d);
}
});
u = null;
return false;
}));
}
}
//! endzinc
[B]
[/B]
//TESH.scrollpos=30
//TESH.alwaysfold=0
/*
**********************************************************************************************************
Installation:
1) Copy this trigger and 'DD Library' to your map
2) Copy Nature Blessing abilitie to your map
3) Export and Import 'GaiaShield.mdx', 'GaiaMissile.mdx', 'NatureExplosion.mdx' sound from this map to yours
4) Modify constants to your liking below
5) Made on Warcraft III v1.30
**********************************************************************************************************
*/
//! zinc
library NatureBlessing requires DDLib
{
// This is the main raw code of ability Nature Blessing
// Default: 'Nbls'
constant integer NATURE_BLESSING = 'Nbls';
// Natures shield effect model path
// Default: "Spells\\NatureBlessing\\GaiaShield.mdx"
constant string NATURE_SHIELD_EFFECT = "Spells\\NatureBlessing\\GaiaShield.mdx";
// Natures missile effect model path
// Default: "Spells\\NatureBlessing\\GaiaMissle.mdx"
constant string NATURE_MISSILE_EFFECT = "Spells\\NatureBlessing\\GaiaMissle.mdx";
// Natures explosion model effect path
// Default: "Spells\\NatureBlessing\\NatureExplosion.mdx"
constant string NATURE_EXPLOSION_EFFECT = "Spells\\NatureBlessing\\NatureExplosion.mdl";
// Natures missile fire sound path (this sound is played when missiles start to move)
// Default: "Abilities\\Spells\\NightElf\\Tranquility\\TranquilityTarget1.wav"
constant string NATURE_SHOOT_SOUND = "Abilities\\Spells\\NightElf\\Tranquility\\TranquilityTarget1.wav";
constant real NATURE_SHOOT_SOUND_VOL = 100.;
constant real NATURE_SHOOT_SOUND_PITCH = 1.5;
constant real NATURE_SHOOT_SOUND_MAX_DISTANCE = 3500.;
// Nature explosion sound file path (is played when nature explosion is caused)
// Default: "Abilities\\Spells\\NightElf\\Tranquility\\Tranquility.wav"
constant string NATURE_EXPLOSION_SOUND = "Abilities\\Spells\\NightElf\\Tranquility\\Tranquility.wav";
constant real NATURE_EXPLOSION_SOUND_VOL = 90.;
// This is number of nature missiles created
// Default: 12
constant integer MISSILES_COUNT = 12;
// Default: 1.2
constant real MISSILE_SIZE = 1.2;
// This is min and max distance from caster to missiles start point
// Default: 400 / 600
constant real MISSILE_MIN_DISTANCE = 400.;
constant real MISSILE_MAX_DISTANCE = 600.;
// This is min and max finish height / caster position of missiles
// Default: 75 / 400
constant real MISSILE_MIN_HEIGHT = 40.;
constant real MISSILE_MAX_HEIGHT = 400.;
// This is missiles value which tells how much time they need to reach caster and how long it takse for them to manifest
// Default: 0.75 / 0.5
constant real MISSILE_TRAVEL_TIME = .9;
constant real MISSILE_BIRTH_TIME = 0.5;
// Default: 50
constant real MISSILE_INITIAL_HEIGHT = 10.;
// Default: 0.2 seconds
constant real HEAL_INTERVAL = .2;
// --------------------------------------------------------------
// *** Do not edit level variables here edit them below ***
real EXPLOSION_AOE[];
real SHIELD_PROTECT[];
real HEAL_PERC_AMOUNT[];
real SPELL_DURATION[];
// -----------------------------------------------------------------
// *** Edit level data here ***
function NatureBlessingLevelSetup() {
// This is AOE of explosion "effect"
// Default: 410 / 530 / 650
EXPLOSION_AOE[01] = 410.;
EXPLOSION_AOE[02] = 530.;
EXPLOSION_AOE[03] = 650.;
// Shield protection is amount of damage reduced in percentage
// Default: 15 / 25 / 35
SHIELD_PROTECT[01] = 15.;
SHIELD_PROTECT[02] = 25.;
SHIELD_PROTECT[03] = 35.;
// This is heal amount in percentage per point of life missing
// Default: 5 / 7 / 9
HEAL_PERC_AMOUNT[01] = 5.;
HEAL_PERC_AMOUNT[02] = 7.;
HEAL_PERC_AMOUNT[03] = 9.;
// This is how long spell lasts in seconds
// Default: 10 / 10 / 10
SPELL_DURATION[01] = 10.;
SPELL_DURATION[02] = 10.;
SPELL_DURATION[03] = 10.;
}
function PickUnitsFilter(unit f, player owner) -> boolean {
return IsUnitAlly(f, owner) &&
!IsUnitType(f, UNIT_TYPE_DEAD) &&
!IsUnitType(f, UNIT_TYPE_STRUCTURE) &&
!BlzIsUnitInvulnerable(f) &&
!DDIsUnitWard(f) &&
!IsUnitType(f, UNIT_TYPE_MAGIC_IMMUNE) &&
!IsUnitType(f, UNIT_TYPE_MECHANICAL);
}
// #####################################################################################
// #####################################################################################
timer Tim = null;
group Grp = null;
struct unitdata {
real dur, heal, shield;
effect e;
static thistype Att[];
}
struct missiles {
ddeffect m[MISSILES_COUNT];
p_real pos[MISSILES_COUNT];
p_real spd[MISSILES_COUNT];
integer cnt;
real X, Y;
player owner;
integer lvl;
static method create(unit c) -> thistype {
thistype this = allocate();
integer i;
real x = GetWidgetX(c), y = GetWidgetY(c);
real rad = 0., d;
X = x; Y = y;
owner = GetOwningPlayer(c);
lvl = GetUnitAbilityLevel(c, NATURE_BLESSING);
cnt = R2I((MISSILE_TRAVEL_TIME-.1) / DD_INTERVAL);
for(i=00; i < MISSILES_COUNT; i+=01) {
rad += bj_PI/6.;
d = GetRandomReal(MISSILE_MIN_DISTANCE, MISSILE_MAX_DISTANCE);
pos[i] = pVector(x + d * Cos(rad), y + d * Sin(rad), MISSILE_INITIAL_HEIGHT);
spd[i] = pVector(-Cos(rad) * DD_INTERVAL * d / MISSILE_TRAVEL_TIME,
-Sin(rad) * DD_INTERVAL * d / MISSILE_TRAVEL_TIME,
DD_INTERVAL * (GetRandomReal(MISSILE_MIN_HEIGHT, MISSILE_MAX_HEIGHT)-MISSILE_INITIAL_HEIGHT) / MISSILE_TRAVEL_TIME);
m[i] = ddeffect.createZ(NATURE_MISSILE_EFFECT, pos[i][00], pos[i][01], pos[i][02] + DDTerrZ(pos[i][00], pos[i][01]), rad, MISSILE_SIZE);
}
return this;
}
method destroy() {
integer i;
for(i=00; i < MISSILES_COUNT; i+=01) {
m[i].destroy();
pos[i].destroy();
spd[i].destroy();
}
deallocate();
}
method Step() -> boolean {
integer i;
for(i=00; i < MISSILES_COUNT; i+=01) {
pos[i][00] += spd[i][00];
pos[i][01] += spd[i][01];
pos[i][02] += spd[i][02];
m[i].PositionZ(pos[i][00], pos[i][01], pos[i][02]);
//m[i].Y = pos[i][01];
//m[i].Z = pos[i][02];
}
cnt -= 01;
return (cnt == 00);
}
}
// ==========================================================================================
function onInit() {
trigger t = CreateTrigger();
Grp = CreateGroup();
NatureBlessingLevelSetup();
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerAddCondition(t, Condition(function() -> boolean {
missiles ms;
unit u;
if (GetSpellAbilityId() != NATURE_BLESSING)
return false;
u = GetTriggerUnit();
ms = missiles.create(u);
// Play sound and shake camera for players within spell cast range
DDGenericSound(NATURE_SHOOT_SOUND, NATURE_SHOOT_SOUND_VOL, GetWidgetX(u), GetWidgetY(u), NATURE_SHOOT_SOUND_MAX_DISTANCE, NATURE_SHOOT_SOUND_PITCH);
DDStartTim(MISSILE_BIRTH_TIME, false, ms, function() {
DDStartTim(DD_INTERVAL, true, DDTimData(), function() {
missiles ms = DDTimData();
trigger t;
if (ms.Step()) {
DDGenericSound(NATURE_EXPLOSION_SOUND, NATURE_EXPLOSION_SOUND_VOL, ms.X, ms.Y, NATURE_SHOOT_SOUND_MAX_DISTANCE, 1.8);
ddeffect.create(NATURE_EXPLOSION_EFFECT, ms.X, ms.Y, GetRandomReal(0., bj_PI*2.), EXPLOSION_AOE[ms.lvl]/250.).destroyx(3.);
DDGroupFilterArea(ms.X, ms.Y, EXPLOSION_AOE[ms.lvl], ms, function() -> boolean {
unitdata ud;
missiles ms = DDGFilterData();
unit f = GetFilterUnit();
if (PickUnitsFilter(f, ms.owner)) {
if (ud.Att[GetHandleId(f)-0x100000] == p_null) {
ud = unitdata.create();
ud.Att[GetHandleId(f)-0x100000] = ud;
ud.e = AddSpecialEffectTarget(NATURE_SHIELD_EFFECT, f, "origin");
GroupAddUnit(Grp, f);
} else
ud = ud.Att[GetHandleId(f)-0x100000];
ud.heal = HEAL_PERC_AMOUNT[ms.lvl] / 100.;
ud.shield = SHIELD_PROTECT[ms.lvl] / 100.;
ud.dur = SPELL_DURATION[ms.lvl];
}
f = null;
return false;
});
if (Tim == null) {
Tim = DDLoadTim();
TimerStart(Tim, HEAL_INTERVAL, true, function() {
ForGroup(Grp, function() {
unit e = GetEnumUnit();
unitdata ud = unitdata.Att[GetHandleId(e)-0x100000];
SetWidgetLife( e, GetWidgetLife(e) + (HEAL_INTERVAL * ud.heal * (GetUnitState(e, UNIT_STATE_MAX_LIFE)-GetWidgetLife(e))) );
ud.dur -= .2;
if (ud.dur < 0. || IsUnitType(e, UNIT_TYPE_DEAD)) {
GroupRemoveUnit(Grp, e);
ud.Att[GetHandleId(e)-0x100000] = p_null;
DestroyEffect(ud.e);
ud.e = null;
ud.destroy();
if (FirstOfGroup(Grp) == null) {
DDRecycleTim(Tim);
Tim = null;
}
}
e = null;
});
});
}
ms.destroy();
DDQuitTim();
}
});
DDQuitTim();
});
u = null;
return false;
}));
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
t = CreateTrigger();
DDTriggerRegisterAnyUnitDamaged(t);
TriggerAddCondition(t, Condition(function() -> boolean {
unit u = GetTriggerUnit();
unitdata ud = unitdata.Att[GetHandleId(u)-0x100000];
if (ud == p_null) {
u = null;
return false;
}
SetWidgetLife(u, GetWidgetLife(u) + (GetEventDamage() * ud.shield));
u = null;
return false;
}));
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
}
//! endzin
[B]
[/B]
//TESH.scrollpos=15
//TESH.alwaysfold=0
//**************************************************************************************
// Installation:
//
//
// - 1. Made on Warcraft III v1.30
// - 2. Copy 'this trigger' and 'DD Library' to your map.
// - 3. Go to Import Manager (F12) and extract icons: BTNInstinctFlash.blp and DISBTN versions
// sounds: sword_chop.wav, sword_echo.wav, sword_draw.wav, sword_dmg1.wav, sword_dmg2.wav
// - 4. Import all of this to your map and set there path to as you can see here (in my map)
// - 5. Go to Object Manager (F6) and copy:
// units: Illusion Blademaster
// ability: Instinct Flash
// When you are pasting them to your map make sure you type the following rawcodes:
// Illusion Blademaster: 'ssib'
// Instinct Flash: 'InFl'
// - 6. If you want different raw codes you must edit them below as well:
// - 7. Edit data below to your own will and enjoy!
//
//**************************************************************************************
//! zinc
library InstinctFlash requires DDLib
{
// *****************************************************************************************
// === Main Data ===
// *****************************************************************************************
// This is the raw code of the spell
// Default: InFl
constant integer ABILITY_INSTINCT_FLASH = 'InFl';
// This is the raw code of illusion
// Default: ssib
constant integer UNIT_ILLUSION_RAWCODE = 'ssib';
// *****************************************************************************************
// === Effects ===
// *****************************************************************************************
// Model file of spells motion sfx
// Default: Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl
constant string EFFECT_BLADE_ATTACHMENT = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl";
constant string EFFECT_BLADE_ATTACH_POINT = "weapon";
// Default: Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl
constant string EFFECT_WATER_IMPACT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl";
// Default: Objects\\SpawnQModels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl
constant string EFFECT_GROUND_IMPACT = "Objects\\SpawnModels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl";
// Default: Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl
constant string EFFECT_UNIT_SLASH_BLEAD = "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl";
// *****************************************************************************************
// === Sounds ===
// *****************************************************************************************
// Default: Custom\\Spells\\Instinct Flash\\sword_draw.wav
constant string SOUND_DRAW = "Custom\\Spells\\Instinct Flash\\sword_draw.wav";
// Default: 85.
constant real SOUND_DRAW_VOL = 85.;
// Default: Custom\\Spells\\Instinct Flash\\sword_echo.wav
constant string SOUND_ECHO = "Custom\\Spells\\Instinct Flash\\sword_echo.wav";
// Default: 45.
constant real SOUND_ECHO_VOL = 45.;
// Default: Custom\\Spells\\Instinct Flash\\sword_dmg1.wav
constant string SOUND_DMG1 = "Custom\\Spells\\Instinct Flash\\sword_dmg1.wav";
// Default: 45.
constant real SOUND_DMG1_VOL = 85.;
// Default: Custom\\Spells\\Instinct Flash\\sword_dmg2.wav
constant string SOUND_DMG2 = "Custom\\Spells\\Instinct Flash\\sword_dmg2.wav";
// Default: 85.
constant real SOUND_DMG2_VOL = 85.;
// Default: Custom\\Spells\\Instinct Flash\\sword_chop.wav
constant string SOUND_CHOP = "Custom\\Spells\\Instinct Flash\\sword_chop.wav";
// Default: 85.
constant real SOUND_CHOP_VOL = 85.;
// Default: 3500.
constant real SOUND_MAX_DISTANCE = 3500.;
// *****************************************************************************************
// === Initial Illusions ===
// *****************************************************************************************
// Max illusion creation distance (height)
// Default: 300
constant real ILLUSION_HEIGHT = 300.;
// Max illusion creation distance (width)
// Default: 550
constant real ILLUSION_WIDTH = 550.;
// This is duration of illusions in seconds
// Default: 0.6
constant real ILLUSION_DURATION = .6;
// This is the animation speed of illusions in percentage
// Default: 100*(1.4-0.15)*0.4 ( 1.4 = Animation duration, 0.4 = Illusion duration, -0.15 extra constant value )
constant real ILLUSION_ANIMATION_SPEED = 70.;
// This is animation of illusion which should be played
// Default: 9 (stand ready)
constant integer ILLUSION_ANIMATION_ID = 9; // (1.4)
// Maximum number of illusions at same time
// Default: 8
constant integer MAX_ILLUSIONS = 8;
constant integer MAX_ILLUSIONS_ARRAY = 4;
//
constant integer ILLUSION_COLOR_RED = 125;
constant integer ILLUSION_COLOR_GREEN = 125;
constant integer ILLUSION_COLOR_BLUE = 240;
constant integer ILLUSION_COLOR_ALPHA = 128;
// *****************************************************************************************
// === Caster Motion ===
// *****************************************************************************************
// This is casters end animation
// Default: 8 (attack 2)
constant integer CASTER_ATTACK_ANIMATION_ID = 8; // (1.134)
// Speed at which caster is traveling after multiple illusions are fused
// Default: 1000.
constant real CASTER_SPEED = 1000.;
// Default: 150
constant integer CASTER_COLOR_ALPHA = 150;
// Casters end animation speed in percentage
// Default: 200
constant real CASTER_ANIMATION_SPEED = 200.;
// This is the area of damage effect
// Default: 135
constant real CASTER_DAMAGE_RANGE = 135.;
// *****************************************************************************************
// === Killed Units Motion ===
// *****************************************************************************************
// Defines travel distance of units which are pushed
// Default: 330
constant real PUSH_DISTANCE = 330.;
// Defines travel height of units which are pushed
// Default: 230
constant real PUSH_HEIGHT = 230.;
// Defines start push speed
// Default: 500
constant real PUSH_SPEED = 500.;
// *****************************************************************************************
// *** (Aftereffect Illusions From Casters Movement) ***
// *****************************************************************************************
// This is casters end animation
// Default: 8 (attack 2)
constant integer AEFF_ILLUSION_ATTACK_ANIMATION_ID = 8; // (1.134)
// Duration of casters end animation
// Default: 0.66
constant real AEFF_ILLUSION_DURATION = .66;
// Casters end animation speed in percentage
// Default: 100*1.134/0.5 (1.134 = animation duration, 0.5 seconds to finish animation)
constant real AEFF_ILLUSION_ANIMATION_SPEED = 226.8;
// How much distance caster passes to produce a single aftereffect illusion
// Default: 125.
constant real AEFF_ILLUSION_DISTANCE_STEP = 125.;
real DamageAmount[];
// *****************************************************************************************
// === Level Data ===
// *****************************************************************************************
// *** Edit level data below ***
function InstinctFlash_Setup() {
// *** This is the damage per level ***
// Default: 175 / 285 / 400
DamageAmount[01] = 175.;
DamageAmount[02] = 285.;
DamageAmount[03] = 400.;
// *** This is the color of illusions ***
// Arguments: red, green, blue, alpha (from 0 to 255)
// Default: 125, 125, 240, 255
//Color = CreateColor(125, 125, 240, 255)
}
// *****************************************************************************************
// === Unit Filter ===
// *****************************************************************************************
function UnitFilter_Setup(unit u, player p) -> boolean {
return !IsUnitType(u, UNIT_TYPE_STRUCTURE) &&
!IsUnitType(u, UNIT_TYPE_FLYING) &&
!IsUnitType(u, UNIT_TYPE_MECHANICAL) &&
!BlzIsUnitInvulnerable(u) &&
!DDIsUnitWard(u) &&
!IsUnitType(u, UNIT_TYPE_DEAD) &&
IsUnitEnemy(u, p);
}
// ==============================================================================
// DO NOT EDIT BELOW IF YOU DONT KNOW JASS
// ==============================================================================
struct uknockback {
unit u;
real x, y, wz;
real dx, dy;
real a, dist;
static method create(unit u, real rad) -> thistype {
thistype this = allocate();
this.u = u;
UnitAddAbility(u, DD_ABILITY_CROWN_FORM);
x = GetWidgetX(u); y = GetWidgetY(u); wz = DDTerrZ(x, y);
a = PUSH_HEIGHT / Pw_2(PUSH_DISTANCE/2.);
dx = DD_INTERVAL * PUSH_SPEED * Cos(rad);
dy = DD_INTERVAL * PUSH_SPEED * Sin(rad);
dist = PUSH_DISTANCE;
return this;
}
method ApplyMotion() {
DDStartTim(DD_INTERVAL, true, this, function() {
thistype this = DDTimData();
x += dx; y += dy;
SetUnitX(u, x); SetUnitY(u, y);
SetUnitFlyHeight(u, wz - DDTerrZ(x, y) + PUSH_HEIGHT - a*Pw_2(dist-(PUSH_DISTANCE/2.)), 0.);
dist -= DD_INTERVAL * PUSH_SPEED;
if (dist < 0.) {
if (!IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY/*AMPHIBIOUSPATHING*/))
DestroyEffect(AddSpecialEffect(EFFECT_WATER_IMPACT, x, y));
else
DestroyEffect(AddSpecialEffect(EFFECT_GROUND_IMPACT, x, y));
u = null;
destroy();
DDQuitTim();
}
});
}
}
struct iflash {
unit u;
player p;
effect e;
real x, y;
real dx, dy;
real dist, spd;
group g, gd;
integer lvl;
real fac;
static method create(unit u, real tx, real ty, real rad) -> thistype {
thistype this = allocate();
this.u = u;
fac = bj_RADTODEG*rad;
lvl = GetUnitAbilityLevel(u, ABILITY_INSTINCT_FLASH);
p = GetOwningPlayer(u);
x = GetUnitX(u); y = GetUnitY(u);
spd = DD_INTERVAL * CASTER_SPEED;
dx = spd * Cos(rad); dy = spd * Sin(rad);
dist = SquareRoot( DDHypot(tx-x, ty-y) );
g = DDLoadGroup(); gd = DDLoadGroup();
SetUnitPathing(u, false);
SetUnitAnimationByIndex(u, CASTER_ATTACK_ANIMATION_ID);
ShowUnit(this.u, true);
PauseUnit(u, true);
e = AddSpecialEffectTarget(EFFECT_BLADE_ATTACHMENT, this.u, EFFECT_BLADE_ATTACH_POINT);
SetUnitVertexColor(u, 255, 255, 255, CASTER_COLOR_ALPHA);
SetUnitTimeScale(u, CASTER_ANIMATION_SPEED/100.);
return this;
}
method ApplyMotion() {
DDStartTim(DD_INTERVAL, true, this, function() {
thistype this = DDTimData();
unit ae_ill;
x += dx; y += dy;
dist -= spd;
SetUnitX(u, x); SetUnitY(u, y);
if (ModuloReal(dist, AEFF_ILLUSION_DISTANCE_STEP) < spd) {
ae_ill = CreateUnit(Player(15), UNIT_ILLUSION_RAWCODE, x, y, fac);
SetUnitColor(ae_ill, GetPlayerColor(p));
SetUnitVertexColor(ae_ill, ILLUSION_COLOR_RED, ILLUSION_COLOR_GREEN, ILLUSION_COLOR_BLUE, CASTER_COLOR_ALPHA);
SetUnitPathing(ae_ill, false);
SetUnitX(ae_ill, x); SetUnitY(ae_ill, y);
PauseUnit(ae_ill, true);
SetUnitAnimationByIndex(ae_ill, CASTER_ATTACK_ANIMATION_ID);
DestroyEffect(AddSpecialEffectTarget(EFFECT_BLADE_ATTACHMENT, ae_ill, EFFECT_BLADE_ATTACH_POINT));
DDFadeUnit(ae_ill, CASTER_COLOR_ALPHA, 00, AEFF_ILLUSION_DURATION);
}
DDGFilterDataSet(this);
GroupEnumUnitsInRange(g, x, y, CASTER_DAMAGE_RANGE, Filter(function() -> boolean {
thistype this = DDGFilterData();
unit f = GetFilterUnit();
if (UnitFilter_Setup(f, p) && !IsUnitInGroup(f, gd)) {
//UnitDamageTarget(u, f, DamageAmount[lvl], true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null);
DDSpellDamage(u, f, DamageAmount[lvl]);
DestroyEffect(AddSpecialEffectTarget(EFFECT_UNIT_SLASH_BLEAD, f, "origin"));
GroupAddUnit(gd, f);
if (GetRandomInt(00, 01) == 01)
DDGenericSound(SOUND_DMG1, SOUND_DMG1_VOL, x, y, SOUND_MAX_DISTANCE, 1.);
else
DDGenericSound(SOUND_DMG2, SOUND_DMG2_VOL, x, y, SOUND_MAX_DISTANCE, 1.);
if (IsUnitType(f, UNIT_TYPE_DEAD)) {
uknockback.create(f, Atan2(GetWidgetY(f)-GetWidgetY(u), GetWidgetX(f)-GetWidgetX(u))).ApplyMotion();
DDGenericSound(SOUND_CHOP, SOUND_CHOP_VOL, x, y, SOUND_MAX_DISTANCE, 1.);
}
}
f = null;
return false;
}));
if (dist < 0.) {
SetUnitPathing(u, true);
PauseUnit(u, false);
SetUnitTimeScale(u, 1.);
SetUnitVertexColor(u, 255, 255, 255, 255);
DDStartTim(.6, false, New_pUnit(u), function() {
p_unit pu = DDTimData();
SetUnitAnimation(pu[00], "stand");
pu.destroy();
DDQuitTim();
});
if (GetLocalPlayer() == p)
SelectUnit(u, true);
DDRecycleGroup(g);
DDRecycleGroup(gd);
DestroyEffect(e);
e = null;
destroy();
DDQuitTim();
}
});
}
}
struct illusions {
unit il[MAX_ILLUSIONS], u;
real dist[MAX_ILLUSIONS_ARRAY];
effect e[MAX_ILLUSIONS];
real w_spd;
real dx, dy;
real dx2, dy2;
real x, y, tx, ty;
real rad;
static method create(unit u, real tx, real ty) -> thistype {
thistype this = allocate();
integer i;
playercolor pc = GetPlayerColor(GetOwningPlayer(u));
real px, py;
this.u = u;
this.tx = tx; this.ty = ty;
x = GetUnitX(u); y = GetUnitY(u);
w_spd = DD_INTERVAL * ILLUSION_HEIGHT / ILLUSION_DURATION;
rad = Atan2(ty-y, tx-x);
dx = Cos(rad); dy = Sin(rad);
for(i=01; i < MAX_ILLUSIONS_ARRAY+01; i+=01) {
px = x + (i*ILLUSION_WIDTH)/MAX_ILLUSIONS * -dy;
py = y + (i*ILLUSION_WIDTH)/MAX_ILLUSIONS * dx;
px += ( (ILLUSION_HEIGHT / Pw_2(ILLUSION_WIDTH/02)) ) * Pw_2((i-01)*ILLUSION_WIDTH/MAX_ILLUSIONS) * dx;
py += ( (ILLUSION_HEIGHT / Pw_2(ILLUSION_WIDTH/02)) ) * Pw_2((i-01)*ILLUSION_WIDTH/MAX_ILLUSIONS) * dy;
il[i-01] = CreateUnit(Player(15), UNIT_ILLUSION_RAWCODE, px, py, bj_RADTODEG*rad);
e[i-01] = AddSpecialEffectTarget(EFFECT_BLADE_ATTACHMENT, il[i-01], "weapon");
SetUnitVertexColor(il[i-01], ILLUSION_COLOR_RED, ILLUSION_COLOR_GREEN, ILLUSION_COLOR_BLUE, ILLUSION_COLOR_ALPHA);
SetUnitColor(il[i-01], pc);
SetUnitTimeScale(il[i-01], ILLUSION_ANIMATION_SPEED/100.);
SetUnitAnimationByIndex(il[i-01], ILLUSION_ANIMATION_ID);
px = x - (i*ILLUSION_WIDTH)/MAX_ILLUSIONS * -dy;
py = y - (i*ILLUSION_WIDTH)/MAX_ILLUSIONS * dx;
px += ( (ILLUSION_HEIGHT / Pw_2(ILLUSION_WIDTH/02)) ) * Pw_2((i-01)*ILLUSION_WIDTH/MAX_ILLUSIONS) * dx;
py += ( (ILLUSION_HEIGHT / Pw_2(ILLUSION_WIDTH/02)) ) * Pw_2((i-01)*ILLUSION_WIDTH/MAX_ILLUSIONS) * dy;
il[(i-01)+MAX_ILLUSIONS_ARRAY] = CreateUnit(Player(15), UNIT_ILLUSION_RAWCODE, px, py, bj_RADTODEG*rad);
e[i-01+MAX_ILLUSIONS_ARRAY] = AddSpecialEffectTarget(EFFECT_BLADE_ATTACHMENT, il[i-01+MAX_ILLUSIONS_ARRAY], "weapon");
SetUnitVertexColor(il[(i-01)+MAX_ILLUSIONS_ARRAY], ILLUSION_COLOR_RED, ILLUSION_COLOR_GREEN, ILLUSION_COLOR_BLUE, ILLUSION_COLOR_ALPHA);
SetUnitColor(il[(i-01)+MAX_ILLUSIONS_ARRAY], pc);
SetUnitTimeScale(il[(i-01)+MAX_ILLUSIONS_ARRAY], ILLUSION_ANIMATION_SPEED/100.);
SetUnitAnimationByIndex(il[(i-01)+MAX_ILLUSIONS_ARRAY], ILLUSION_ANIMATION_ID);
dist[i-01] = (i*ILLUSION_HEIGHT*2.)/MAX_ILLUSIONS;
}
return this;
}
method ApplyMotion() {
DDStartTim(DD_INTERVAL, true, this, function() {
thistype this = DDTimData();
integer i;
real px, py;
real sq;
for(i=01; i < MAX_ILLUSIONS_ARRAY+01; i+=01) {
dist[i-01] -= w_spd;
sq = SquareRoot(Pw_2(ILLUSION_WIDTH/02)*dist[i-01] / ILLUSION_HEIGHT);
px = x + dist[i-01] * dx;
py = y + dist[i-01] * dy;
px += sq * -dy; // +90 degrees
py += sq * dx;
SetUnitX(il[i-01], px); SetUnitY(il[i-01], py);
px = x + dist[i-01] * dx;
py = y + dist[i-01] * dy;
px -= sq * -dy; // -90 degrees
py -= sq * dx;
SetUnitX(il[i-01+MAX_ILLUSIONS_ARRAY], px); SetUnitY(il[i-01+MAX_ILLUSIONS_ARRAY], py);
if (dist[i-01] < 0. && il[i-01] != null) {
RemoveUnit(il[i-01]);
RemoveUnit(il[i-01+MAX_ILLUSIONS_ARRAY]);
il[i-01] = null;
il[i-01+MAX_ILLUSIONS_ARRAY] = null;
}
}
if (il[MAX_ILLUSIONS_ARRAY-01] == null) {
DDGenericSound(SOUND_ECHO, SOUND_ECHO_VOL, x, y, SOUND_MAX_DISTANCE, 1.);
iflash.create(u, tx, ty, rad).ApplyMotion();
destroy();
DDQuitTim();
}
});
}
}
// *** Main map startup function ***
function onInit() {
// *** Load locals ***
trigger t = CreateTrigger();
// *** Define users setup ***
InstinctFlash_Setup();
// *** Event / cond / act registering ***
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerAddCondition(t, Condition(function() -> boolean {
illusions il;
unit u;
if (GetSpellAbilityId() != ABILITY_INSTINCT_FLASH)
return false;
u = GetTriggerUnit();
il = illusions.create(u, GetSpellTargetX(), GetSpellTargetY());
il.ApplyMotion();
ShowUnit(u, false);
//SetUnitTimeScale(u, ILLUSION_ANIMATION_SPEED/100.);
//SetUnitAnimationByIndex(u, ILLUSION_ANIMATION_ID);
DDGenericSound(SOUND_DRAW, SOUND_DRAW_VOL, GetSpellTargetX(), GetSpellTargetY(), SOUND_MAX_DISTANCE, 1.);
return false;
}));
}
}
//! endzinc
[B]
//TESH.scrollpos=15
//TESH.alwaysfold=0
//**************************************************************************************
// Installation:
//
// - 1. Made on Warcraft III v1.30
// - 2. Go to Import Manager (F12) and extract NecroticBlast.mdx, BTNSummonSkeletons.blp (Credits: PrinceYaser) and its DISBTN version
// - 3. Import all of this to your map and set ther path to as you can see here (in my map)
// - 4. Go to Object Manager (F6) and copy "Summon Skeletons" ability.
// When you are pasting them to your map make sure you match there rawcodes with ones below
// - 5. If you want different raw codes edit the below as well
// - 6. Edit data below to your own will and enjoy the spell
// - 7. Copy this trigger and "DD Library" to your map
//
//
//**************************************************************************************
//! zinc
library SummonSkeletons requires DDLib
{
// *****************************************************************************************
// === Main Data ===
// *****************************************************************************************
// This is the raw code of the spell 'Summon Skeletons'
// Default: 'A000'
constant integer ABILITY_SUMMON_SKELETONS = 'A000';
//constant real SUMMON_SKELETONS_BLIGHT_DURATION = 5.;
constant real SUMMON_SKELETONS_INTERVAL = .33; // How offten are skeletons created
constant real SUMMON_SKELETONS_RISE_TIME = 2.33;
constant string SUMMON_SKELETONS_RISE_ANIMATION = "birth";
constant real SUMMON_SKELETONS_START_TRANSPARENCY = 85.;
constant real SKELETONS_MIN_RISE_DISTANCE = 125.; // Min distance from skeleton and caster
constant real SKELETONS_MIN_SPAWN_RANGE = 100.; // Min distance between skeletons
// *****************************************************************************************
// === Effect ===
// *****************************************************************************************
constant string SUMMON_SKELETON_CASTER_EFFECT = "Spells\\SummonSkeletons\\NecroticBlast.mdx"; // UgoUgo
constant string SUMMON_SKELETON_RISE_EFFECT = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl";
constant string SUMMON_SKELETON_RISE_EFFECT_EX = "Abilities\\Spells\\Undead\\RaiseSkeletonWarrior\\RaiseSkeleton.mdl";
constant string SUMMON_SKELETON_GROUND_EFFECT = "Abilities\\Spells\\Undead\\PlagueCloud\\PlagueCloudCaster.mdl";//"SharedModels\\Smoke1_Green.mdl";
constant real SUMMON_SKELETON_GROUND_EFFECT_SIZE = 1.;
constant real SUMMON_SKELETON_GROUND_EFFECT_HEIGHT = -10.;
constant string SUMMON_SKELETONS_RISE_GRAVE_EFFECT = "Abilities\\Spells\\Undead\\Graveyard\\GraveMarker.mdl";
constant real SUMMON_SKELETONS_RISE_GRAVE_EFFECT_FACING_OFFSET= 30.;
constant real SUMMON_SKELETONS_RISE_GRAVE_DURATION = 6.;
constant real SUMMON_SKELETONS_RISE_GRAVE_DISTANCE_OFFSET = 70.;
constant string SUMMON_SKELETONS_FIRE_EFFECT = "Abilities\\Spells\\NightElf\\Immolation\\ImmolationDamage.mdl";
constant string SUMMON_SKELETONS_FIRE_EFFECT_ATTACH_1 = "foot left";
constant string SUMMON_SKELETONS_FIRE_EFFECT_ATTACH_2 = "foot right";
constant string SUMMON_SKELETONS_FIRE_EFFECT_ATTACH_3 = "hand left";
constant string SUMMON_SKELETONS_FIRE_EFFECT_ATTACH_4 = "hand right";
constant string SUMMON_SKELETONS_FIRE_EFFECT_ATTACH_5 = "head";
constant string SUMMON_SKELETONS_FIRE_EFFECT_ATTACH_6 = "origin";
// *****************************************************************************************
// === Level Data ===
// *****************************************************************************************
// --------------------------------------------------------------
// This variables are not supposed to be modified here, modify them below in Setup function
integer SKELETONS_TYPE_COLOR_RED[];
integer SKELETONS_TYPE_COLOR_GREEN[];
integer SKELETONS_TYPE_COLOR_BLUE[];
integer SKELETONS_COUNT[];
integer SKELETONS_TYPE_ID[];
real SKELETONS_LIFESPAN[];
real SKELETONS_MAX_RISE_DISTANCE[];
real SUMMON_SKELETONS_RISE_GRAVE_SIZE[];
// --------------------------------------------------------------
// This part of code is used to define spell constants per level
function SummonSkeletons_Setup() {
// Default: Level 1 = 'ndr1'
// Default: Level 2 = 'ndr2'
// Default: Level 3 = 'ndr3'
SKELETONS_TYPE_ID[01] = 'ndr1';
SKELETONS_TYPE_ID[02] = 'ndr2';
SKELETONS_TYPE_ID[03] = 'ndr3';
// Default: Level 1 = 50.
// Default: Level 2 = 50.
// Default: Level 3 = 50.
SKELETONS_LIFESPAN[01] = 50.;
SKELETONS_LIFESPAN[02] = 50.;
SKELETONS_LIFESPAN[03] = 50.;
// Default: Level 1 = 230 RGB
// Default: Level 2 = 200 RGB
// Default: Level 3 = 150 RGB
SKELETONS_TYPE_COLOR_RED [01] = 230;
SKELETONS_TYPE_COLOR_GREEN [01] = 230;
SKELETONS_TYPE_COLOR_BLUE [01] = 230;
SKELETONS_TYPE_COLOR_RED [02] = 200;
SKELETONS_TYPE_COLOR_GREEN [02] = 200;
SKELETONS_TYPE_COLOR_BLUE [02] = 200;
SKELETONS_TYPE_COLOR_RED [03] = 150;
SKELETONS_TYPE_COLOR_GREEN [03] = 150;
SKELETONS_TYPE_COLOR_BLUE [03] = 150;
// Default: Level 1 = 4
// Default: Level 2 = 5
// Default: Level 3 = 6
SKELETONS_COUNT[01] = 4;
SKELETONS_COUNT[02] = 5;
SKELETONS_COUNT[03] = 6;
// * Max skeleton rise distance from caster
// Default: Level 1 = 400.
// Default: Level 2 = 400.
// Default: Level 3 = 400.
SKELETONS_MAX_RISE_DISTANCE[01] = 400.;
SKELETONS_MAX_RISE_DISTANCE[02] = 400.;
SKELETONS_MAX_RISE_DISTANCE[03] = 400.;
// Default: Level 1 = 0.9
// Default: Level 2 = 1.1
// Default: Level 3 = 1.3
SUMMON_SKELETONS_RISE_GRAVE_SIZE[01] = 0.9;
SUMMON_SKELETONS_RISE_GRAVE_SIZE[02] = 1.1;
SUMMON_SKELETONS_RISE_GRAVE_SIZE[03] = 1.3;
}
// *****************************************************************************************
// === Main Code ===
// *****************************************************************************************
type cluster extends effect[06];
struct fadeskel {
unit u;
integer lvl;
real trans;
real rate, e_trans, dur;
static constant real INTERVAL = .1;
}
// *** fadeskels unit over time ***
function DDFadeSkel(unit u, integer lvl) {
fadeskel f = fadeskel.create();
f.u = u;
f.lvl = lvl;
f.trans = (100.-SUMMON_SKELETONS_START_TRANSPARENCY)*2.55;
f.rate = ((255.-f.trans)/SUMMON_SKELETONS_RISE_TIME)*fadeskel.INTERVAL;
f.dur = SUMMON_SKELETONS_RISE_TIME;
PauseUnit(u, true);
SetUnitAnimation(u, SUMMON_SKELETONS_RISE_ANIMATION);
SetUnitVertexColor(f.u,
SKELETONS_TYPE_COLOR_RED[lvl],
SKELETONS_TYPE_COLOR_GREEN[lvl],
SKELETONS_TYPE_COLOR_BLUE[lvl],
R2I(f.trans));
// --- Start thread ---
DDStartTim(fadeskel.INTERVAL, true, f, function() {
fadeskel f = DDTimData();
f.trans += f.rate;
f.dur -= fadeskel.INTERVAL;
SetUnitVertexColor(f.u,
SKELETONS_TYPE_COLOR_RED[f.lvl],
SKELETONS_TYPE_COLOR_GREEN[f.lvl],
SKELETONS_TYPE_COLOR_BLUE[f.lvl],
R2I(f.trans));
if (f.dur < 0.) {
PauseUnit(f.u, false);
SetUnitAnimation(f.u, "stand");
SetUnitVertexColor(f.u,
SKELETONS_TYPE_COLOR_RED[f.lvl],
SKELETONS_TYPE_COLOR_GREEN[f.lvl],
SKELETONS_TYPE_COLOR_BLUE[f.lvl],
255);
f.u = null;
f.destroy();
DDQuitTim();
}
});
}
struct data {
player p;
real fac;
real x, y;
integer lvl, ns;
integer nskels;
unit sk[33];
p_real vec;
static boolean DestsFlag = false;
}
/*
struct blightdata {
integer tt, tv;
real x, y;
}*/
function onInit() {
trigger t = CreateTrigger();
SummonSkeletons_Setup();
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerAddCondition(t, Condition(function() -> boolean {
data d;
integer i;
unit u;
if (GetSpellAbilityId() != ABILITY_SUMMON_SKELETONS)
return false;
d = data.create();
u = GetTriggerUnit();
d.p = GetOwningPlayer(u);
d.fac = GetUnitFacing(u);
d.x = GetWidgetX(u); d.y = GetWidgetY(u);
d.lvl = GetUnitAbilityLevel(u, ABILITY_SUMMON_SKELETONS);
d.vec = p_real.create();
d.nskels = 00;
DestroyEffect(AddSpecialEffectTarget(SUMMON_SKELETON_CASTER_EFFECT, u, "origin"));
DDStartTim(SUMMON_SKELETONS_INTERVAL, true, d, function() {
data d = DDTimData();
real dist, cdist, rad;
boolean skeleton_pathing_invalid;
real x, y;
real vx, vy;
unit sk;
effect e;
//blightdata bd;
cluster cl;
integer i;
do {
//skeleton_pathing_invalid = true;
//dist = GetRandomReal(SKELETONS_MIN_RISE_DISTANCE, SKELETONS_MAX_RISE_DISTANCE[d.lvl]);
//rad = GetRandomReal(0., bj_PI*2.);
dist = DDGetRndReal(SKELETONS_MIN_RISE_DISTANCE, SKELETONS_MAX_RISE_DISTANCE[d.lvl]);
rad = GetRandomReal(0., 2.*bj_PI);
if (d.nskels > 00) {
for(i=00; i < d.nskels; i+=01) {
if (IsUnitInRangeXY(d.sk[i], d.x + dist * Cos(rad), d.y + dist * Sin(rad), SKELETONS_MIN_SPAWN_RANGE)) {
//dist = SquareRoot(DDGetRndReal(Pw_2(SKELETONS_MIN_RISE_DISTANCE), Pw_2(SKELETONS_MAX_RISE_DISTANCE[d.lvl])));
//dist = GetRandomReal(SKELETONS_MIN_RISE_DISTANCE, SKELETONS_MAX_RISE_DISTANCE[d.lvl]);
//rad = GetRandomReal(0., bj_PI*2.);
dist = DDGetRndReal(SKELETONS_MIN_RISE_DISTANCE, SKELETONS_MAX_RISE_DISTANCE[d.lvl]);
rad = GetRandomReal(0., 2.*bj_PI);
i = -01;
}
}
}
cdist = 0.;
x = d.x; y = d.y;
vx = 64.*Cos(rad); vy = 64.*Sin(rad);
do {
cdist += 64.;
x += vx; y += vy;
data.DestsFlag = false;
d.vec[00] = x; d.vec[01] = y;
DDEnumDestsInRange(d.vec, bj_CELLWIDTH, null, function() { data.DestsFlag = true; });
skeleton_pathing_invalid = IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) || IsTerrainPathable(x, y, PATHING_TYPE_AMPHIBIOUSPATHING) || data.DestsFlag;
//if (skeleton_pathing_invalid)
//break;
} while(cdist < dist+64. && !skeleton_pathing_invalid);
} while(skeleton_pathing_invalid);
x = d.x + dist * Cos(rad);
y = d.y + dist * Sin(rad);
sk = CreateUnit(d.p, SKELETONS_TYPE_ID[d.lvl], x, y, d.fac);
d.sk[d.nskels] = sk;
cl = cluster.create();
cl[00] = AddSpecialEffectTarget(SUMMON_SKELETONS_FIRE_EFFECT, sk, SUMMON_SKELETONS_FIRE_EFFECT_ATTACH_1);
cl[01] = AddSpecialEffectTarget(SUMMON_SKELETONS_FIRE_EFFECT, sk, SUMMON_SKELETONS_FIRE_EFFECT_ATTACH_2);
cl[02] = AddSpecialEffectTarget(SUMMON_SKELETONS_FIRE_EFFECT, sk, SUMMON_SKELETONS_FIRE_EFFECT_ATTACH_3);
cl[03] = AddSpecialEffectTarget(SUMMON_SKELETONS_FIRE_EFFECT, sk, SUMMON_SKELETONS_FIRE_EFFECT_ATTACH_4);
cl[04] = AddSpecialEffectTarget(SUMMON_SKELETONS_FIRE_EFFECT, sk, SUMMON_SKELETONS_FIRE_EFFECT_ATTACH_5);
cl[05] = AddSpecialEffectTarget(SUMMON_SKELETONS_FIRE_EFFECT, sk, SUMMON_SKELETONS_FIRE_EFFECT_ATTACH_6);
DDStartTim(SKELETONS_LIFESPAN[d.lvl], false, cl, function() {
cluster cl = DDTimData();
DestroyEffect(cl[00]); cl[00] = null;
DestroyEffect(cl[01]); cl[01] = null;
DestroyEffect(cl[02]); cl[02] = null;
DestroyEffect(cl[03]); cl[03] = null;
DestroyEffect(cl[04]); cl[04] = null;
DestroyEffect(cl[05]); cl[05] = null;
cl.destroy();
DDQuitTim();
});
UnitApplyTimedLife(sk, 'Brai', SKELETONS_LIFESPAN[d.lvl]);
DestroyEffect(AddSpecialEffectTarget(SUMMON_SKELETON_RISE_EFFECT, sk, "origin"));
DestroyEffect(AddSpecialEffectTarget(SUMMON_SKELETON_RISE_EFFECT_EX, sk, "origin"));
e = AddSpecialEffect(SUMMON_SKELETON_GROUND_EFFECT, x, y);
BlzSetSpecialEffectZ(e, DDTerrZ(x, y) + SUMMON_SKELETON_GROUND_EFFECT_HEIGHT);
BlzSetSpecialEffectScale(e, SUMMON_SKELETON_GROUND_EFFECT_SIZE);
DDDestroyEffectTimed(e, SUMMON_SKELETONS_RISE_GRAVE_DURATION);
DDFadeSkel(sk, d.lvl);
sk = null;
/*
bd = blightdata.create();
bd.x = x; bd.y = y;
bd.tt = GetTerrainType(x, y);
bd.tv = GetTerrainVariance(x, y);
SetTerrainType(bd.x, bd.y, 'Jdtr', 00, 01, 01);
//SetBlightPoint(Player(00), x, y, true);
DDStartTim(SUMMON_SKELETONS_BLIGHT_DURATION, false, bd, function() {
blightdata bd = DDTimData();
//SetBlightPoint(Player(00), bd.x, bd.y, false);
SetTerrainType(bd.x, bd.y, bd.tt, bd.tv, 01, 01);
bd.destroy();
DDQuitTim();
});*/
//DDDestroyEffectTimed(e, SUMMON_SKELETONS_RISE_GRAVE_DURATION);
x -= SUMMON_SKELETONS_RISE_GRAVE_DISTANCE_OFFSET * Cos(d.fac*bj_DEGTORAD);
y -= SUMMON_SKELETONS_RISE_GRAVE_DISTANCE_OFFSET * Sin(d.fac*bj_DEGTORAD);
e = AddSpecialEffect(SUMMON_SKELETONS_RISE_GRAVE_EFFECT, x, y);
BlzSetSpecialEffectRoll(e, (d.fac+SUMMON_SKELETONS_RISE_GRAVE_EFFECT_FACING_OFFSET)*bj_DEGTORAD);
BlzSetSpecialEffectScale(e, SUMMON_SKELETONS_RISE_GRAVE_SIZE[d.lvl]);
DDDestroyEffectTimed(e, SUMMON_SKELETONS_RISE_GRAVE_DURATION);
e = null;
d.nskels += 01;
if (d.nskels == SKELETONS_COUNT[d.lvl]) {
while(d.nskels > 00) {
d.nskels -= 01;
d.sk[d.nskels] = null;
}
d.vec.destroy();
d.destroy();
DDQuitTim();
}
});
return false;
}));
}
}
//! endzinc
//TESH.scrollpos=0
//TESH.alwaysfold=0
//**************************************************************************************
// Spell: Terror Blade
// Author: Dark Dragon
// Date: 8 June 2009
//
//
// Installation:
//
//
// - 1. Made on Warcraft III v1.30
// - 2. Go to Import Manager (F12) and extract icons: BTNTerrorBlade.blp, DISBTN, PAS and DISPAS versions
// - 3. Import all of this to your map and set there path to as you can see here (in my map)
// - 4. Go to Object Manager (F6) and copy:
// abilities: Terror Blade and Terror Blade Effect
// buffs: Terror Blade (Buff)
// When you are pasting them to your map make sure you
// type the following rawcodes:
// Terror Blade: 'Trbl'
// Terror Blade Effect: 'Tble'
// Terror Blade (Buff): 'Btbl'
// - 5. If you want different raw codes you must edit them below as well:
// - 6. Edit data below to your own will and enjoy!
// - 7. And of course copy 'this trigger' and 'DD Library' to your map.
//
//**************************************************************************************
// ======================================================================
// * Spell: Terror Blade
// * Library Load (Whole code that spell needs is inside the library)
// ======================================================================
//! zinc
library TerrorBlade requires DDLib
{
// ---------------------------------------------------------------------
//
// *** MAIN SPELL CONSTANTS SETUP VALUES ***
//
//
// * The following values are for you to modifly for your own needs
// * Below are constants and after that are level data values
// * Modify all of them as you want!
// ---------------------------------------------------------------------
// --------------------------------------------
// *** Global Constants Scope ***
// --------------------------------------------
// --- The raw code of spell ---
// Default: Trbl
constant integer ABILITY_TERROR_BLADE = 'Trbl';
// --- The raw code of the spells dummy effect ---
// Default: Tble
constant integer ABILITY_TERROR_BLADE_DUMMY = 'Tble';
// --- Dummy ability order string ---
// Default: innerfire
constant string ABILITY_TERROR_BLADE_DUMMY_ORDER = "innerfire";
// --- The raw code of buff ---
// Default: Btbl
constant integer ABILITY_TERROR_BLADE_BUFF = 'Btbl';
// --- The corruption effect path ---
// Default: Abilities\\Spells\\Other\\HowlOfTerror\\HowlTarget.mdl
constant string TARGET_ATTACHMENT_EFFECT_PATH = "Abilities\\Spells\\Other\\HowlOfTerror\\HowlTarget.mdl";
// --- The darkness effect path ---
// Default: Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl
//constant string DARKNESS_EFFECT = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl";
// --- This is the terror movement effect path ---
// Default: Abilities\\Spells\\Other\\HowlOfTerror\\HowlCaster.mdl
constant string TERROR_BLADE_WAVE_EFFECT_PATH = "Abilities\\Spells\\Other\\HowlOfTerror\\HowlCaster.mdl";
// --- Terror wave effect z offset ---
// Default: 65
constant real TERROR_BLADE_WAVE_HEIGHT = 65.;
// --- Terrors effect size in percentage ---
// Default: 100
constant real TERROR_BLADE_WAVE_SIZE = 1.;
// --- Terror effects speed in coordinates per second ---
// Default: 475
constant real TERROR_BLADE_WAVE_SPEED = 475.;
// --- Terror effects travel distance ---
// Default: 200
constant real TERROR_BLADE_WAVE_DISTANCE = 200.;
// --- Tells can this spell be triggered even if target is already effected ---
// Default: false
constant boolean CAN_TRIGGER_AT_SAME_TIME = false;
// --- Can this spell effect undead-units ---
// Default: true
constant boolean CAN_EFFECT_UNDEAD_UNITS = true;
// --- Show floating text ---
// Default: true
constant boolean SHOW_FLOATING_TEXT = true;
// --- How long floating text lasts in seconds ---
// Default: 5.0
constant real FLOATING_TEXT_DURATION = 5.;
// --- Where is an effect attached on caster ---
// Default: weapon
//constant string CASTER_ATTACH_POINT = "weapon";
// --------------------------------------------------------------------
// * Level data do not edit here edit them below in function setup *
//
// {
real TERROR_DURATION[];
integer TERROR_CHANCE[];
real TERROR_EXTRA_DAMAGE[];
// *** Special non-level data ***
string TARGET_ATTACH_POINT[];
real FT_COLOR[];
// }
// -------------------------------------------------------------
// *** Level data is configured here ***
// -------------------------------------------------------------
function TerrorBladeLevel_Setup() {
// --- This is duration (how long the effect lasts per level) ---
// Default: 10 / 10 / 10
TERROR_DURATION[01] = 10.;
TERROR_DURATION[02] = 10.;
TERROR_DURATION[03] = 10.;
// --- Extra damage per level on triggering ---
// Default: 20 / 35 / 50
TERROR_EXTRA_DAMAGE[01] = 20.;
TERROR_EXTRA_DAMAGE[02] = 35.;
TERROR_EXTRA_DAMAGE[03] = 50.;
// --- Chance in percentage to trigger this ability per level ---
// Default: 25 / 30 / 30
TERROR_CHANCE[01] = 25;
TERROR_CHANCE[02] = 30;
TERROR_CHANCE[03] = 30;
// -----------------------------------------
// --- Special non-level multiple data ---
// -----------------------------------------
// --- Attach points on target unit ---
// --- Number of effects attached to target depend ---
// --- on how much attach points u add here ---
// Default: origin, weapon...
TARGET_ATTACH_POINT[01] = "origin";
TARGET_ATTACH_POINT[02] = "weapon";
//set TargetAPoint[3] = "head" ...
// --- This is the color of floating text ---
// --- This has no effect if 'SHOW_FLOATING_TEXT' is off ---
// --- Args: Red / Green / Blue / Transparency
// Default: 60 / 13 / 40 / 0
FT_COLOR[00] = 60.;
FT_COLOR[01] = 13.;
FT_COLOR[02] = 40.;
FT_COLOR[03] = 0.;
}
// *
function TerrorBladeFilter(unit victim, player attackerPlayer) -> boolean {
static if (!CAN_EFFECT_UNDEAD_UNITS) {
return !IsUnitType(victim, UNIT_TYPE_MECHANICAL) &&
!IsUnitType(victim, UNIT_TYPE_STRUCTURE) &&
!IsUnitType(victim, UNIT_TYPE_ANCIENT) &&
!IsUnitType(victim, UNIT_TYPE_MAGIC_IMMUNE) &&
!IsUnitType(victim, UNIT_TYPE_UNDEAD) &&
!IsUnitType(victim, UNIT_TYPE_FLYING) &&
IsUnitEnemy(victim, attackerPlayer) &&
!DDIsUnitWard(victim);
} else {
return !IsUnitType(victim, UNIT_TYPE_MECHANICAL) &&
!IsUnitType(victim, UNIT_TYPE_STRUCTURE) &&
!IsUnitType(victim, UNIT_TYPE_ANCIENT) &&
!IsUnitType(victim, UNIT_TYPE_MAGIC_IMMUNE) &&
!IsUnitType(victim, UNIT_TYPE_FLYING) &&
IsUnitEnemy(victim, attackerPlayer) &&
!DDIsUnitWard(victim);
}
}
// ==============================================================================
// *** DO NOT EDIT BELOW IF YOU DONT KNOW JASS ***
// ==============================================================================
real UnitAttackTime[];
real UnitAttackElapsedTime[];
struct data {
ddeffect wave;
real x, y, dx, dy;
real dist;
}
struct terrorized {
unit v, dmy;
effect e[02];
real dur;
boolean osuccess;
}
function onInit() {
trigger t = CreateTrigger();
TerrorBladeLevel_Setup();
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ATTACKED);
DDTriggerRegisterAnyUnitDamaged(t);
TriggerAddCondition(t, Condition(function() -> boolean {
unit u, v;
integer id;
real at_t;
integer lvl;
data d;
real rad;
terrorized t;
integer ch;
if (GetTriggerEventId() == EVENT_PLAYER_UNIT_ATTACKED) {
if (GetUnitAbilityLevel(GetAttacker(), ABILITY_TERROR_BLADE) > 00)
UnitAttackTime[H2ID(GetAttacker())] = DDGetGameElapsedTime();
return false;
}
u = GetEventDamageSource();
lvl = GetUnitAbilityLevel(u, ABILITY_TERROR_BLADE);
if (lvl > 00) {
id = H2ID(u);
at_t = DDGetGameElapsedTime()-UnitAttackTime[id];
/*DDMsg(R2S(at_t));
DDMsg(R2S(UnitAttackElapsedTime[(id*04)+01]));
DDMsg(R2S(UnitAttackElapsedTime[(id*04)+02]));
DDMsg(R2S(UnitAttackElapsedTime[(id*04)+03]));*/
// *** Is melee damage?
if (R2I(at_t/.05) == R2I(UnitAttackElapsedTime[(id*04)+01]/.05) &&
R2I(UnitAttackElapsedTime[(id*04)+01]/.05) == R2I(UnitAttackElapsedTime[(id*04)+02]/.05) &&
R2I(UnitAttackElapsedTime[(id*04)+02]/.05) == R2I(UnitAttackElapsedTime[(id*04)+03]/.05))
{
if (DDGetRndInt(01, 100) <= TERROR_CHANCE[lvl]) {
v = GetTriggerUnit();
static if (!CAN_TRIGGER_AT_SAME_TIME)
if (GetUnitAbilityLevel(v, ABILITY_TERROR_BLADE_BUFF) > 00) {
u = null; v = null;
UnitAttackElapsedTime[ (id*04) + R2I(UnitAttackElapsedTime[(id*04)]) ] = at_t;
UnitAttackElapsedTime[(id*04)] += 1.;
if (UnitAttackElapsedTime[(id*04)] == 4.) UnitAttackElapsedTime[(id*04)] = 0.;
return false;
}
if (TerrorBladeFilter(v, GetOwningPlayer(u))) {
d = data.create();
d.x = GetWidgetX(u); d.y = GetWidgetY(u);
d.wave = ddeffect.createZ(TERROR_BLADE_WAVE_EFFECT_PATH, d.x, d.y, DDTerrZ(d.x, d.y) + TERROR_BLADE_WAVE_HEIGHT, DDGetRndReal(0., bj_PI*2.), TERROR_BLADE_WAVE_SIZE);
rad = Atan2(GetWidgetY(v)-d.y, GetWidgetX(v)-d.x);
d.dx = TERROR_BLADE_WAVE_SPEED*DD_INTERVAL*Cos(rad);
d.dy = TERROR_BLADE_WAVE_SPEED*DD_INTERVAL*Sin(rad);
d.dist = TERROR_BLADE_WAVE_DISTANCE;
DDStartTim(DD_INTERVAL, true, d, function() {
data d = DDTimData();
d.wave.Position(d.wave.X + d.dx, d.wave.Y + d.dy);
d.dist -= TERROR_BLADE_WAVE_SPEED*DD_INTERVAL;
if (d.dist < 0.) {
d.wave.destroy();
d.destroy();
DDQuitTim();
}
});
BlzSetEventDamage(GetEventDamage() + TERROR_EXTRA_DAMAGE[lvl]);
//UnitDamageTarget(u, v, TERROR_EXTRA_DAMAGE[lvl], true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
static if (SHOW_FLOATING_TEXT)
DDNewTextTagUnit(v, I2S(R2I(TERROR_EXTRA_DAMAGE[lvl]))+"!", FLOATING_TEXT_DURATION, FT_COLOR[00], FT_COLOR[01], FT_COLOR[02], FT_COLOR[03]);
t = terrorized.create();
t.v = v;
t.e[00] = AddSpecialEffectTarget(TARGET_ATTACHMENT_EFFECT_PATH, v, TARGET_ATTACH_POINT[01]);
t.e[01] = AddSpecialEffectTarget(TARGET_ATTACHMENT_EFFECT_PATH, v, TARGET_ATTACH_POINT[02]);
t.dur = TERROR_DURATION[lvl];
t.dmy = DDLoadSpellDummy(GetOwningPlayer(u), d.x, d.y, ABILITY_TERROR_BLADE_DUMMY, lvl);
IssueTargetOrder(t.dmy, ABILITY_TERROR_BLADE_DUMMY_ORDER, v);
DDStartTim(.1, true, t, function() {
terrorized t = DDTimData();
t.dur -= .1;
if (t.dur > 9.)
return;
if (IsUnitType(t.v, UNIT_TYPE_DEAD) || t.dur < 0. || GetUnitAbilityLevel(t.v, ABILITY_TERROR_BLADE_BUFF) == 00) {
UnitRemoveAbility(t.dmy, ABILITY_TERROR_BLADE_DUMMY);
DDRecycleDummy(t.dmy);
t.v = null;
t.dmy = null;
DestroyEffect(t.e[00]); t.e[00] = null;
DestroyEffect(t.e[01]); t.e[01] = null;
t.destroy();
DDQuitTim();
}
});
}
}
}
UnitAttackElapsedTime[ (id*04) + R2I(UnitAttackElapsedTime[(id*04)]) ] = at_t;
UnitAttackElapsedTime[(id*04)] += 1.;
if (UnitAttackElapsedTime[(id*04)] == 4.) UnitAttackElapsedTime[(id*04)] = 0.;
}
u = null; v = null;
return false;
}));
}
}
//! endzinc
[B]
//TESH.scrollpos=0
//TESH.alwaysfold=0
//**************************************************************************************
// Installation:
//
// - 1. Made on Warcraft III v1.30
// - 2. Go to Import Manager (F12) and extract BTNWaterExhausting.blp and its DISBTN version,
// BubbleSpawn.mdx, WaterOrb.mdx, WaterBurst.mdx
// water_wave.wav, water_splash1.wav, water_splash2.wav, water_splash3.wav
// - 3. Import all of this to your map and set ther path to as you can see here (in my map)
// - 4. Go to Object Manager (F6) and copy "Bubble Blast" ability.
// When you are pasting them to your map make sure you match there rawcodes with ones below
// - 5. If you want different raw codes edit the below as well
// - 6. Copy this trigger and "DD Library" to your map
// - 7. Edit data below to your own will and enjoy the spell
//
// Credits:
// MajorSonnwaitts, Pyritie, JetFangInferno, dueeast.com (sounds)
//
//**************************************************************************************
//! zinc
library BubbleBlast requires DDLib
{
// ************************************************************************************************
// --- Ability Constants ---
// ************************************************************************************************
constant integer BUBBLE_BLAST_ABILITY_ID = 'wtex';
constant boolean BUBBLE_BLAST_REMOVE_POSITIVE_BUFFS = true;
constant boolean BUBBLE_BLAST_REMOVE_NEGATIVE_BUFFS = false;
// Effect attached to unit on which buffs are removed and its attach point
constant string BUBBLE_BLAST_REMOVE_BUFFS_MODEL_PATH = "Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl";
constant string BUBBLE_BLAST_REMOVE_BUFFS_ATTACH_POINT = "origin";
constant boolean USE_KNOCKBACK = true;
constant real KNOCKBACK_TRAVEL_DISTANCE = 225.;
constant real KNOCKBACK_TRAVEL_SPEED = 300.;
constant string KNOCKBACK_TRAVEL_GROUND_DRAG_EFFECT_PATH = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl";
constant string KNOCKBACK_DRAG_EFFECT_ATTACH_POINT = "foot";
// How offten is 'KNOCKBACK_TRAVEL_GROUND_DRAG_EFFECT_PATH' effect created
constant real KNOCKBACK_TRAVEL_GROUND_DRAG_EFFECT_INTERVAL= 0.25;
// ************************************************************************************************
// --- Effect Constants ---
// ************************************************************************************************
constant string BUBBLE_SPAWN_MODEL_PATH = "Spells\\BubbleBlast\\BubbleSpawn.mdx";
constant real BUBBLE_SPAWN_MODEL_SIZE = 0.5;
constant real BUBBLE_SPAWN_MODEL_MIN_DURATION = 7.;
constant real BUBBLE_SPAWN_MODEL_MAX_DURATION = 9.;
constant string WATER_ORB_MODEL_PATH = "Spells\\BubbleBlast\\WaterOrb.mdx";
constant real WATER_ORB_MODEL_SIZE = 2.;
constant real WATER_ORB_TRAVEL_SPEED = 330.;
// Apcisa (X) direction only motion. The way ellipse works is that virtual points move around caster
// at x-axes 'WATER_ORB_ELLIPSE_X_TRAVEL_SPEED', while y is calculated. Orb then based on its position
// moves towards that virtual point from its current location
constant real WATER_ORB_ELLIPSE_X_TRAVEL_SPEED = 190.;
// ellipse a = value based on 'BUBBLE_SPAWN_MIN/MAX_AOE', while ellipse b = a*'WATER_ORB_ELLIPSE_FACTOR'
constant real WATER_ORB_ELLIPSE_FACTOR = 0.66;
// gravity actiong on orb when it starts falling defined in coordinates per square second
constant real WATER_ORB_FALL_ACCELERATION = 555.555;
constant real WATER_ORB_MIN_HEIGHT = 100.;
constant real WATER_ORB_MAX_HEIGHT = 400.;
constant string WATER_BLAST_MODEL_PATH = "Spells\\BubbleBlast\\WaterBurst.mdx";
constant real WATER_BLAST_MODEL_SIZE = 0.5;
constant real WATER_BLAST_MODEL_DURATION = 1.33;
constant string WATER_BLAST_AFTEREFFECT_MODEL_PATH = "Doodads\\Icecrown\\Water\\BubbleGeyserSteam\\BubbleGeyserSteam.mdx";
constant real WATER_BLAST_AFTEREFFECT_MODEL_SIZE = 1.15;
constant real WATER_BLAST_AFTEREFFECT_MODEL_DURATION = 6.5;
// ************************************************************************************************
// --- Sounds Constants ---
// ************************************************************************************************
constant string BUBBLE_SPWAN_START_SOUND_PATH = "Custom\\Spells\\BubbleBlast\\water_wave.wav";
constant real BUBBLE_SPWAN_START_SOUND_VOLUME = 70.;
constant string WATER_ORB_IMPACT_SOUND_PATH_01 = "Custom\\Spells\\BubbleBlast\\water_splash1.wav";
constant real WATER_ORB_IMPACT_SOUND_VOLUME_01 = 70.;
constant string WATER_ORB_IMPACT_SOUND_PATH_02 = "Custom\\Spells\\BubbleBlast\\water_splash2.wav";
constant real WATER_ORB_IMPACT_SOUND_VOLUME_02 = 70.;
constant string WATER_ORB_IMPACT_SOUND_PATH_03 = "Custom\\Spells\\BubbleBlast\\water_splash3.wav";
constant real WATER_ORB_IMPACT_SOUND_VOLUME_03 = 70.;
constant real SOUND_MAX_DISTANCE = 3500.;
// ---------------------------------------------------------------------
// "non-EDITABLE", just declarations!
// ---------------------------------------------------------------------
integer BUBBLE_SPWAN_COUNT[];
real WATER_ORB_MIN_DURATION[], WATER_ORB_MAX_DURATION[];
real BUBBLE_SPAWN_MIN_AOE[], BUBBLE_SPAWN_MAX_AOE[];
real WATER_BLAST_DAMAGE[], WATER_BLAST_AOE[];
// ---------------------------------------------------------------------
function BubbleBlastLevelSetup() {
// How much orbs are created per level?
// Level 01 Default: 9
// Level 02 Default: 11
// Level 03 Default: 12
BUBBLE_SPWAN_COUNT[01] = 9;
BUBBLE_SPWAN_COUNT[02] = 11;
BUBBLE_SPWAN_COUNT[03] = 12;
// Within min and max range from caster, orbs and bubbles spawn!
// Level 01 Default: MIN = 200, MAX = 400
// Level 02 Default: MIN = 200, MAX = 450
// Level 03 Default: MIN = 200, MAX = 500
BUBBLE_SPAWN_MIN_AOE[01] = 200.; BUBBLE_SPAWN_MAX_AOE[01] = 400.;
BUBBLE_SPAWN_MIN_AOE[02] = 200.; BUBBLE_SPAWN_MAX_AOE[02] = 450.;
BUBBLE_SPAWN_MIN_AOE[03] = 200.; BUBBLE_SPAWN_MAX_AOE[03] = 500.;
// Once water orbs spawn, how long does each one of them last,
// if min and max are not equal, then each orb lasts for its own duration.
// Level 01: Min Default: 4.5, Max Default: 6.5
// Level 02: Min Default: 3.75, Max Default: 5.75
// Level 03: Min Default: 2.75, Max Default: 4.5
WATER_ORB_MIN_DURATION[01] = 4.5; WATER_ORB_MAX_DURATION[01] = 6.5;
WATER_ORB_MIN_DURATION[02] = 3.75; WATER_ORB_MAX_DURATION[02] = 5.75;
WATER_ORB_MIN_DURATION[03] = 2.75; WATER_ORB_MAX_DURATION[03] = 4.5;
// Upon water burst explosion, how much damage is dealt?
// Level 01 Default: 100
// Level 02 Default: 150
// Level 03 Default: 210
WATER_BLAST_DAMAGE[01] = 100.;
WATER_BLAST_DAMAGE[02] = 150.;
WATER_BLAST_DAMAGE[03] = 210.;
// Upon water burst explosion, what is the radius in which units are damaged?
// Level 01 Default: 150
// Level 02 Default: 150
// Level 03 Default: 150
WATER_BLAST_AOE[01] = 150.;
WATER_BLAST_AOE[02] = 150.;
WATER_BLAST_AOE[03] = 150.;
}
// *** Which units should be damaged? '!' represents not / inverted
function WaterBlastUnitFilter(unit f, player castingPlayer) -> boolean {
return !IsUnitType(f, UNIT_TYPE_STRUCTURE) &&
!IsUnitType(f, UNIT_TYPE_DEAD) &&
!IsUnitType(f, UNIT_TYPE_MAGIC_IMMUNE) &&
!IsUnitType(f, UNIT_TYPE_MECHANICAL) &&
!IsUnitType(f, UNIT_TYPE_FLYING) &&
IsUnitEnemy(f, castingPlayer) &&
!BlzIsUnitInvulnerable(f) &&
!DDIsUnitWard(f);
}
// ===========================================================================================
// ***************************************************************
// *** Spell Code | "non-EDITABLE" ***
// ***************************************************************
// ===========================================================================================
/*public function testAA(unit f) {
if (IsUnitType(f, UNIT_TYPE_FLYING))
DDMsg("a");
if (IsUnitType(f, UNIT_TYPE_MAGIC_IMMUNE))
DDMsg("b");
if (BlzIsUnitInvulnerable(f))
DDMsg("c");
}*/
struct knockback {
unit u;
real tim=0.;
real dist, spd, dx, dy;
static boolean Flag[];
}
struct waterorb {
unit u;
ddeffect orb;
integer lvl;
real el_x, el_y, z;
real x_spd, y_sign, rad_offset;
real a, b;
real dur;
method destroy() {
u = null;
orb.destroy();
deallocate();
}
method GetCoords() {
el_x += x_spd;
if (x_spd > 0.) {
if (el_x > a) {
x_spd = -x_spd;
y_sign = -y_sign;
}
} else {
if (el_x < -a) {
x_spd = -x_spd;
y_sign = -y_sign;
}
}
el_y = b*y_sign*SquareRoot(1.-Pw_2(el_x/a));
}
method ApplyMotion() {
real x, y;
real dist;
this.GetCoords();
x = GetWidgetX(u);
y = GetWidgetY(u);
x += el_x * Cos(rad_offset);
y += el_x * Sin(rad_offset);
x += el_y * Cos(rad_offset+(bj_PI/2.));
y += el_y * Sin(rad_offset+(bj_PI/2.));
//rad = Atan2(y-orb.Y, x-orb.X);
dist = SquareRoot(DDHypot(x-orb.X, y-orb.Y) + Pw_2(z-orb.Z));
orb.PositionZ(orb.X + (WATER_ORB_TRAVEL_SPEED*DD_INTERVAL*((x-orb.X)/dist)),
orb.Y + (WATER_ORB_TRAVEL_SPEED*DD_INTERVAL*((y-orb.Y)/dist)),
orb.Z + (WATER_ORB_TRAVEL_SPEED*DD_INTERVAL*((z-orb.Z)/dist)));
}
method ApplyFall() {
x_spd += WATER_ORB_FALL_ACCELERATION*DD_INTERVAL*DD_INTERVAL;
orb.Z -= x_spd;
}
method OrbDetonate() {
integer rnd_snd = DDGetRndInt(01, 03);
effect e;
if (rnd_snd == 01)
DDGenericSound(WATER_ORB_IMPACT_SOUND_PATH_01, WATER_ORB_IMPACT_SOUND_VOLUME_01, orb.X, orb.Y, SOUND_MAX_DISTANCE, 1.);
else if (rnd_snd == 02)
DDGenericSound(WATER_ORB_IMPACT_SOUND_PATH_02, WATER_ORB_IMPACT_SOUND_VOLUME_02, orb.X, orb.Y, SOUND_MAX_DISTANCE, 1.);
else
DDGenericSound(WATER_ORB_IMPACT_SOUND_PATH_03, WATER_ORB_IMPACT_SOUND_VOLUME_03, orb.X, orb.Y, SOUND_MAX_DISTANCE, 1.);
e = AddSpecialEffect(WATER_BLAST_MODEL_PATH, orb.X, orb.Y);
BlzPlaySpecialEffect(e, ANIM_TYPE_STAND);
BlzSetSpecialEffectScale(e, WATER_BLAST_MODEL_SIZE);
DDDestroyEffectTimed(e, WATER_BLAST_MODEL_DURATION);
e = AddSpecialEffect(WATER_BLAST_AFTEREFFECT_MODEL_PATH, orb.X, orb.Y);
BlzSetSpecialEffectScale(e, WATER_BLAST_AFTEREFFECT_MODEL_SIZE);
DDDestroyEffectTimed(e, WATER_BLAST_AFTEREFFECT_MODEL_DURATION); e = null;
DDGroupFilterArea(orb.X, orb.Y, WATER_BLAST_AOE[lvl], this, function() {
waterorb wo = DDGFilterData();
unit f = GetFilterUnit();
knockback kb;
real rad;
if (WaterBlastUnitFilter(f, GetOwningPlayer(wo.u))) {
UnitRemoveBuffs(f, BUBBLE_BLAST_REMOVE_POSITIVE_BUFFS, BUBBLE_BLAST_REMOVE_NEGATIVE_BUFFS);
//bj_lastCreatedUnit = DDLoadDummy();
DDSpellDamage(wo.u, f, WATER_BLAST_DAMAGE[wo.lvl]);
//DDSpellDamage(bj_lastCreatedUnit, f, WATER_BLAST_DAMAGE[wo.lvl]);
//DDRecycleDummy(bj_lastCreatedUnit);
DestroyEffect(AddSpecialEffectTarget(BUBBLE_BLAST_REMOVE_BUFFS_MODEL_PATH, f, BUBBLE_BLAST_REMOVE_BUFFS_ATTACH_POINT));
static if (USE_KNOCKBACK) {
if (knockback.Flag[H2ID(f)]) {
f = null;
return;
}
knockback.Flag[H2ID(f)] = true;
kb = knockback.create();
PauseUnit(f, true);
kb.u = f;
kb.dist = KNOCKBACK_TRAVEL_DISTANCE;
kb.spd = KNOCKBACK_TRAVEL_SPEED*DD_INTERVAL;
rad = Atan2(GetWidgetY(f)-GetWidgetY(wo.u), GetWidgetX(f)-GetWidgetX(wo.u));
kb.dx = kb.spd * Cos(rad);
kb.dy = kb.spd * Sin(rad);
DDStartTim(DD_INTERVAL, true, kb, function() {
knockback kb = DDTimData();
kb.tim += DD_INTERVAL;
if (ModuloReal(kb.tim, KNOCKBACK_TRAVEL_GROUND_DRAG_EFFECT_INTERVAL) <= DD_INTERVAL)
DestroyEffect(AddSpecialEffectTarget(KNOCKBACK_TRAVEL_GROUND_DRAG_EFFECT_PATH, kb.u, KNOCKBACK_DRAG_EFFECT_ATTACH_POINT));
SetUnitPosition(kb.u, GetWidgetX(kb.u) + kb.dx, GetWidgetY(kb.u) + kb.dy);
kb.dist -= kb.spd;
if (kb.dist < 0.) {
knockback.Flag[H2ID(kb.u)] = false;
PauseUnit(kb.u, false);
kb.u = null;
kb.destroy();
DDQuitTim();
}
});
}
}
f = null;
});
}
}
//b*sqrt(1-(x/a)^2) = y
function onInit() {
trigger t = CreateTrigger();
BubbleBlastLevelSetup();
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerAddCondition(t, Condition(function() -> boolean {
unit u;
integer i, lvl;
effect e;
real x, y, tx, ty;
real dist, rad;
waterorb wo;
if (GetSpellAbilityId() != BUBBLE_BLAST_ABILITY_ID)
return false;
u = GetTriggerUnit();
x = GetWidgetX(u);
y = GetWidgetY(u);
lvl = GetUnitAbilityLevel(u, BUBBLE_BLAST_ABILITY_ID);
DDGenericSound(BUBBLE_SPWAN_START_SOUND_PATH, BUBBLE_SPWAN_START_SOUND_VOLUME, x, y, SOUND_MAX_DISTANCE, 1.);
for(i=00; i < BUBBLE_SPWAN_COUNT[lvl]; i+=01) {
dist = DDGetRndReal(BUBBLE_SPAWN_MIN_AOE[lvl], BUBBLE_SPAWN_MAX_AOE[lvl]);
rad = GetRandomReal(0., 2.*bj_PI);
tx = x + dist * Cos(rad);
ty = y + dist * Sin(rad);
e = AddSpecialEffect(BUBBLE_SPAWN_MODEL_PATH, tx, ty);
BlzSetSpecialEffectScale(e, BUBBLE_SPAWN_MODEL_SIZE);
DDDestroyEffectTimed(e, DDGetRndReal(BUBBLE_SPAWN_MODEL_MIN_DURATION, BUBBLE_SPAWN_MODEL_MAX_DURATION));
wo = waterorb.create();
wo.u = u;
wo.orb = ddeffect.create(WATER_ORB_MODEL_PATH, tx, ty, GetRandomReal(0., 2.*bj_PI), WATER_ORB_MODEL_SIZE);
wo.a = dist; wo.b = dist*WATER_ORB_ELLIPSE_FACTOR;
wo.rad_offset = rad;
wo.y_sign = 1.; if (GetRandomInt(00, 01) == 01) wo.y_sign = -1.;
wo.x_spd = -WATER_ORB_ELLIPSE_X_TRAVEL_SPEED*DD_INTERVAL;
wo.z = DDTerrZ(tx, ty) + DDGetRndReal(WATER_ORB_MIN_HEIGHT, WATER_ORB_MAX_HEIGHT);
wo.dur = DDGetRndReal(WATER_ORB_MIN_DURATION[lvl], WATER_ORB_MAX_DURATION[lvl]);
wo.lvl = lvl;
DDStartTim(DD_INTERVAL, true, wo, function() {
waterorb wo = DDTimData();
if (wo.dur > 0. && !IsUnitType(wo.u, UNIT_TYPE_DEAD)) {
wo.ApplyMotion();
wo.dur -= DD_INTERVAL;
} else {
wo.ApplyFall();
if (wo.orb.Z < 5.) {
wo.OrbDetonate();
wo.destroy();
DDQuitTim();
}
}
});
}
u = null;
e = null;
return false;
}));
}
}
//! endzinc
//==========================================================================
// 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