// xternal FileExporter .\ddup\
library FogSelect initializer Init
globals
private trigger Trig = null
private triggeraction TrigAct = null
private dialog Dial = null
private button Yes = null
endglobals
function UserSelectFog takes nothing returns nothing
set Dial = DialogCreate()
call DialogSetMessage(Dial, "Disable fog of war")
set Yes = DialogAddButton(Dial, "|cffffffffY|res", 'Y')
call DialogAddButton(Dial, "|cffffffffN|ro", 'N')
call TriggerRegisterDialogEvent(Trig, Dial)
call DialogDisplay(Player(0), Dial, true)
endfunction
private function onClick takes nothing returns nothing
local button b = GetClickedButton()
if (b==Yes) then
call FogMaskEnable(false)
call FogEnable(false)
endif
call DialogClear(Dial)
call DialogDestroy(Dial)
call TriggerRemoveAction(Trig, TrigAct)
call DestroyTrigger(Trig)
set b = null
set Yes = null
set Dial = null
set Trig = null
set TrigAct = null
endfunction
private function Init takes nothing returns nothing
set Trig = CreateTrigger()
set TrigAct = TriggerAddAction(Trig, function onClick)
endfunction
endlibrary
//! zinc
library UnitsRevive requires DDLib
{
public struct unitsrevivedata {
private {
integer id;
real x, y;
real fac;
unit hero=null;
real rev_dur;
player p;
static thistype Units[];
}
static method create(unit u) -> thistype {
thistype this = allocate();
id = GetUnitTypeId(u);
p = GetOwningPlayer(u);
x = GetWidgetX(u); y = GetWidgetY(u);
fac = GetUnitFacing(u);
rev_dur = 35.;
if (IsUnitType(u, UNIT_TYPE_HERO)) {
hero = u;
rev_dur = 3.5;
}
Units[GetHandleId(u)-0x100000] = this;
return this;
}
static method Revive(unit u) {
thistype this = Units[GetHandleId(u)-0x100000];
DDStartTim(rev_dur, false, this, function() {
thistype this = DDTimData();
if (hero != null) {
ReviveHero(hero, x, y, false);
SetUnitFacing(hero, fac);
SetUnitState(hero, UNIT_STATE_MANA, GetUnitState(hero, UNIT_STATE_MAX_MANA));
SelectUnit(hero, true);
PanCameraToTimed(x, y, .66);
} else
bj_lastCreatedUnit = CreateUnit(p, id, x, y, fac);
Units[GetHandleId(bj_lastCreatedUnit)-0x100000] = this;
DDQuitTim();
});
}
}
}
//! endzinc
Name | Type | is_array | initial_value |
RndTime | real | No | |
unt | unit | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
//**************************************************************************************
// Spell: Slash Strike
// Author: Dark Dragon
// Date: 14 October 2008
//
// Other: Spell made for fun testing vJass
//
//
// Installation:
//
// - 1. Make sure you have open the JassNewGen Pack editor not normal one and make sure you have last version
// Some suggestion first read all of this then if you want to use this spell, do as it says below
// - 2. Now when you open this map in newgen the first thing you want to do is go to
// object editor (F6) and there copy unit named Illusion Blademaster and under Abilities
// tab copy spell named Slash Strike when you did that copy this trigger Slash Strike
// - 3. Now press Ctrl+O or click on open map icon, select your map and past all of the stuff
// you copied, note while pasting you will be asked for raw codes, type next as i tell you
// For Unit Illusion Blademaster type SSib and for Ability Slash Strike
// type SStr the case matches.
// - 4. Now when you pasted this trigger again open my map and paste DDUniversal Pack trigger
// as this trigger is used in all spells.
// - 5. Go back to my map after you paste DDUniversal Pack trigger and go to Import Manager
// (F12) and export BTN_CR_Amultsrtike.tga and DISBTN_CR_Amultsrtike.tga icons, now
// open your map and import this 2 icons to your map change path to as in my map
// war3mapImported or war3campImported must be deleted and replaced to ReplaceableTextures\CommandButtons\BTN_CR_Amultsrtike.tga
// and for other one DISBTN_CR_Amultsrtike.tga to this ReplaceableTextures\CommandButtonsDisabled\DISBTN_CR_Amultsrtike.tga
// now save your map, go to object editor, find spell Slash Strike and change icon to
// this new one.
// If icon does not appear its ok, you can close my and your map then open your
// map again and now select icon, you are done.
// - 6. Last thing you want to do is setup this spell to your style, just simple
// go and read below on how to change spell style, down below are described all
// values you can change, just read, and after that have fun!
//
//**************************************************************************************
// *** This is Slash Strike main library
library SlashStrike initializer SlashStrike_Main requires DDUniversalPack
// **************************************************************************
// ** SPELL SETUP **
// ** **
// ** This variables are constants and are made to easy change spell style **
// ** Define them as you like. **
// **************************************************************************
globals
// Before all you should know that this spell has limit of 128 instant casts
// that means there cant be more than 128 casts at same time.
// By that I mean that if by trigger or something you load 129 units and order all
// to cast this spell it wont work!
// Then if we know limit of jass array size which is 2^13 and limit of instant casts
// then we know how much units can be damaged in one cast 2^13 = 2^7 * x
// x = 2^13 / 2^7 | x = 2^6 {2^6 = 64} so 64 units can be damaged in instant cast only
// one of casts. If this spell is cast 4 times at same time there will be 2^6 * 2^2
// units damaged (up to max) which is 2^6 * 2^2 = 2^8 = (256) units damaged
// If you think that the limits are not enough for you and you need more then please
// don't use this spell.
// Otherwise enjoy!
// Ok! Its time to setup your new spell!
// Follow the comments below and do what they say
// This is the raw code of dummy unit (change it in DDUniversal Pack trigger)
// Default: 'dumy'
private constant integer DUMMY_CODE = DD_DUMMY_CODE
// This is the raw code of Slash Strike Ability
// Default: 'SStr'
private constant integer ABILITY_SLASH_STRIKE = 'SStr'
// Units (dummy) raw code image (illusion) of caster
// Default: 'ssib'
private constant integer SS_CASTER_IMAGE = 'ssib'
// This is the length of blade (unit caster) with weapon, if you change caster you want
// to change this value to.
// Default: 220.
private constant real SS_BLADE_WIDTH = 220.
// This is supposed to be height of blade when blade stands horizontally
// looking at its z coordinate (this is its height)
// Default: 60.
private constant real SS_BLADE_HEIGHT = 60.
// This is the radius of circle slash the unit will pass.
// Suggestion dont put this value more than "this spell" (maxCastRange - 200.)
// Default: 250.
private constant real SS_SLASH_RADIUS = 250.
// This is duration in seconds of caster ready animation
// Default: 1.40
private constant real CASTER_READY_DURATION = 1.4
// This is casters spell cast animation
// this animation is played during cast, and
// given animation should be looping animation
// Default: "ready"
private constant string CASTER_SPELL_ANIMATION = "ready"
// This is casters ending animation (or so called)
// reset animation, I guess that every unit reset animation
// is stand animation 'stand_1'
// Default: "stand"
private constant string CASTER_RESET_ANIMATION = "stand"
// The speed of caster during slash cast (in coordinates)
// this speed means that it will pass x coordinates per second
// Default: 1300.
private constant real CASTER_MOVE_SPEED = 1300.
// This is your unit (caster) defined collision size
// Default: 32.
private constant real CASTER_COLLISION_SIZE = 32.
// How often illusions will be created
// n Illusions per second
// Put value of -1 to disable illusion creation
// Default: 10
private constant integer ILLUSION_COUNTER = 10
// The distance from target when circling around
// Default: 90.
private constant real DISTANCE_FROM_TARGET = 90.
// This is casters end animation of quick slash
// the animation index, this anim index 8 is
// his 'Attack - 2' animation
// Default: 8
private constant integer CASTER_QSLASH_ANIM_INDEX = 8
// This is duration of animation above (indexed)
// so this is duration of 'Attack - 2' animation
// Default: 1.134
private constant real CASTER_QSLASH_ANIM_DURATION = 1.134
// This two varibles define Illusion min and
// max animation interval
// Default Min: 0.9
// Default Max: 1.3
private constant real ANIM_MIN_POS = 0.9
private constant real ANIM_MAX_POS = 1.3
// If enabled kills any destructable near caster
// while he moves during spell.
// Default: TRUE
private constant boolean CAN_KILL_TRESS = TRUE
// This value is used to allow users simple enable, disable
// little physics engine in this spell which could
// slow down this system a bit, so if you receive a
// little of lag or (HEAVY) lag you might want to disable this.
// Default: TRUE
private constant boolean USE_PHYSICS_ENGINE = TRUE
// This variables are not supposed to be touched here
// Go down below to SlashStrike_Setup
//{
private integer array TreeType[32]
private integer NTreeTypes = 0
private real array Damage[32]
private integer Levels = 0
private integer array QuickSlashes[32]
//}
endglobals
// Here edit this values to your will
private function SlashStrike_Setup takes nothing returns nothing
// This is the number of levels spell has
// Default: 3
set Levels = 3
// This is the number of different tree types in
// your map
// Default: 2
set NTreeTypes = 2
// This are tree types in your map
// This tree types are used to know
// should we play sounds or not when
// destructable dies
// Default 1: 'ATtr'
// Default 2: 'ATtc'
set TreeType[1] = 'ATtr' // Ashenvale Tree
set TreeType[2] = 'ATtc' // Ashenvale Canopy Tree
// This is the damage spell deals at each level
// Default 1: 50.
// Default 2: 80.
// Default 3: 115.
set Damage[1] = 50.
set Damage[2] = 80.
set Damage[3] = 115.
// set Damage[4] = 155... for each level add your damage if changed
// When Quick Slash is activated, caster will move n times
// arond target unit. This is n times set.
// Default 1: 3
// Default 2: 3
// Default 3: 4
set QuickSlashes[1] = 3
set QuickSlashes[2] = 3
set QuickSlashes[3] = 4
// set QuickSlashes[4] = 5
endfunction
// ****************************************************
// Dont edit below if you are not a jass programmer
// If you know jass you can edit anything you want
// ****************************************************
//--------------------------------------------------------
// Global Variables
//--------------------------------------------------------
globals
// Constant variables
//private constant real PI = 3.141592653589793
private constant real PI_HALF = bj_PI/2.
private constant real bj_2PI = bj_PI*2.
private constant integer INT_NULL = 0
private constant real REAL_NULL = 0.
private constant location LOC_Z = Location(REAL_NULL, REAL_NULL)
private constant real MIN_SLEEP = -1.00
private constant sound ERROR_SOUND = CreateSound("Sound\\Interface\\Error.wav", false, false, false, 10, 10, "")
private constant integer CROW_FORM_RAWCODE = 'Amrf'
private constant string BLINK_CASTER_QModEL = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"
private constant string BLINK_TARGET_QModEL = "Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl"
private constant real TIMER_MOVE_DELAY = 0.01
private constant integer ERROR = -1
private constant integer ABILITY_LOCUST = 'Aloc'
private constant integer MAX_HEX_RGB = 0xFF
private constant player PLAYER_NP = Player(PLAYER_NEUTRAL_PASSIVE)
private constant string DUST_EFFECT = "Objects\\SpawnModels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
private constant string WATER_EFFECT = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl"
private constant string BLOOD_EFFECT = "Objects\\SpawnQModels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
private constant string ATTACH_POINT_ORIGIN = "origin"
private constant real QSLASH_TARGET_DISTANCE = 100.
private constant real TARGET_BLOW_SPEED = 600.
private constant rect DEST_RECT = Rect(REAL_NULL, REAL_NULL, REAL_NULL, REAL_NULL)
// Globals
private real array TargetPosX[128] // Position X To Where Are We Moving
private real array TargetPosY[128] // Position Y To Where Are We Moving
private integer array StoredValue[128] // The Stored Value
private integer NCast = INT_NULL // N'th Cast
private unit array Caster[128] // Main Caster Variable
private boolean array CircleMove[128] // Check For Circle Move
private real array ConstSin[128] // Constant Sine
private real array ConstCos[128] // Constant Cosine
private real array BackRadians[128] // Back Angle In Radians
private real array CasterRadians[128] // Casters Facing In Radians
private real array CasterDistance[128] // Store Caster Distance From Circle Move
private integer array TimerExec[128] // N'th Timer Execution
private group array IllusionGroup[128] // All Illusions Are Stored In This Group
private group array BlowedUnitsGroup[128] // All Units That Are Killed By Caster / Damaged
private timer SSTimer = null // This Is Main Timer
private integer TriggerCount = INT_NULL // This Counts First n Triggers
private triggeraction array TAction[128] // Stored Trigger Action
private group array DamagedUnitsGroup[128] // All Units That Taked Damage
private filterfunc array DamageFilter[128] // Rather Loade One ff Than Every timer_move_delay Seconds
private rect array DamageRect[128] // Damage Region
private boolean array UnitDamaged[8191] // This Is The Check Is Unit Damaged
private integer array SS_SpellLevel[128] // Spell Level During Cast
private boolexpr array DestFilter[128] // Destructables Filter
private real array PhysicsUnitForce[8191] // Force Actually Acceleration, Down, Up Ground
private real array PhysicsBlowAngle[8191] // This Is Blow Angle In Radians
private real array PhysicsUnitVelocity[8191]// Velocity Or Speed Of Blowed Unit
private real array PhysicsFloorZ[8191] // Z Of Floor That Changes During Air Move
private integer TempId = INT_NULL // Temporary Variable To Store Id
private boolean array QuickSlash[128] // Checks If Close Combat Is Executed
private integer array NQuickSlashes[128] // Remaining Quick Slashes
private unit array TargetUnit[128] // This Is Target Unit Which Is Used In Quick Slash
private boolean array PushTarget[128] // Checks Is Target Unit Supposed To Be Pushed
endglobals
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//x Other Spell Used Custom Functions x
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//************************************************************************************
//
// Returns x^2 - power of number
//
private constant function Pow2 takes real x returns real
return (x*x)
endfunction
//************************************************************************************
//
// Converts 1D array to 2D
//
private constant function Array2D takes integer x, integer y, integer size returns integer
return x+(size*y)+y
endfunction
//************************************************************************************
//
// Clears Damaged Units Variable
// So That They Can Be Damaged Again
//
private function FlushUnitsBoolean takes nothing returns nothing
local integer size = 64
set UnitDamaged[ Array2D(QMod(GetHandleId(GetEnumUnit()), size), TempId, size-1) ] = false
endfunction
//************************************************************************************
//
// Clears Physics Stored Values
//
private function FlushUnitsPhysics takes nothing returns nothing
local integer size = 64
local unit u = GetEnumUnit()
local integer arrayx = QMod(GetHandleId(u), size)
set PhysicsUnitForce[ Array2D(arrayx, TempId, size-1) ] = REAL_NULL
set PhysicsBlowAngle[ Array2D(arrayx, TempId, size-1) ] = REAL_NULL
set PhysicsUnitVelocity[ Array2D(arrayx, TempId, size-1) ] = REAL_NULL
set PhysicsFloorZ[ Array2D(arrayx, TempId, size-1) ] = REAL_NULL
set u = null
endfunction
//************************************************************************************
//
// End spell null variables action
//
private function EndSlashStrike takes nothing returns nothing
// Ending Locals
local trigger this = GetTriggeringTrigger()
local integer id = StoredValue[QMod(GetHandleId(this) - 0xFFFFF, 128)]
local unit u = Caster[id]
local triggeraction ta = null
local boolean cond = false
// Release Unit (caster)
call PauseUnit(u, false)
call SetUnitTimeScale(u, 1.)
call SetUnitAnimation(u, CASTER_RESET_ANIMATION)
call SetUnitPathing(u, true)
set TempId = id
call ForGroup(DamagedUnitsGroup[id], function FlushUnitsBoolean)
set Caster[id] = null
// Sleep For Condition (when trigger can be destroyed)
loop
exitwhen (cond)
call Sleep(MIN_SLEEP)
if (USE_PHYSICS_ENGINE) then
set cond = (FirstOfGroup(IllusionGroup[id]) == null) and (FirstOfGroup(BlowedUnitsGroup[id]) == null and not PushTarget[id])
else
set cond = (FirstOfGroup(IllusionGroup[id]) == null)
endif
endloop
// All Used Objects Get Destroyed Now
call DestroyGroup(DamagedUnitsGroup[id])
call DestroyFilter(DamageFilter[id])
call RemoveRect(DamageRect[id])
set ta = TAction[id]
if (ILLUSION_COUNTER != ERROR) then
call DestroyGroup(IllusionGroup[id])
endif
if (USE_PHYSICS_ENGINE) then
set TempId = id
call ForGroup(BlowedUnitsGroup[id], function FlushUnitsPhysics)
call DestroyGroup(BlowedUnitsGroup[id])
endif
if (CAN_KILL_TRESS) then
call DestroyBoolExpr(DestFilter[id])
endif
// No more triggers left
// Kill timer
set TriggerCount = TriggerCount - 1
if (TriggerCount == INT_NULL) then
call PauseTimer(SSTimer)
call DestroyTimer(SSTimer)
set SSTimer = null
endif
// Now null all used variables
set TargetPosX[id] = REAL_NULL
set TargetPosY[id] = REAL_NULL
set StoredValue[id] = INT_NULL
set CircleMove[id] = FALSE
set ConstSin[id] = REAL_NULL
set ConstCos[id] = REAL_NULL
set BackRadians[id] = REAL_NULL
set CasterRadians[id] = REAL_NULL
set CasterDistance[id] = REAL_NULL
set TimerExec[id] = INT_NULL
set IllusionGroup[id] = null
set TAction[id] = null
set BlowedUnitsGroup[id] = null
set DamageFilter[id] = null
set DamagedUnitsGroup[id] = null
set DamageRect[id] = null
set SS_SpellLevel[id] = INT_NULL
set DestFilter[id] = null
set QuickSlash[id] = FALSE
set TargetUnit[id] = null
set QuickSlash[id] = false
set NQuickSlashes[id] = INT_NULL
set PushTarget[id] = false
// Finaly destroy trigger and his action
call TriggerRemoveAction(this, ta)
call DestroyTrigger(this)
set ta = null
set this = null
set u = null
// We can see that this is usles function call, but however
// I did not wanted to make mega long code only coz this function
// is called like 4 - 5 times...
endfunction
//---------------------------------------------------------------------
//
// Damaged Units Cond Filter
//
private function DamageFilterFunc takes nothing returns boolean
local unit f = GetFilterUnit()
local integer id = StoredValue[QMod(GetHandleId(GetTriggeringTrigger()) - 0xFFFFF, 128)]
return (not IsUnitType(f, UNIT_TYPE_DEAD)) and (IsUnitType(f, UNIT_TYPE_GROUND)) and (not IsUnitType(f, UNIT_TYPE_STRUCTURE)) and (IsUnitEnemy(f, GetOwningPlayer(Caster[id]))) and (not IsUnitInGroup(f, DamagedUnitsGroup[id])) and (GetOwningPlayer(f) != PLAYER_NP and IsUnitVisible(f, GetOwningPlayer(Caster[id])))
// We can see that f is not nulled however its much faster this way
// than calling GetFilterUnit() n times
endfunction
//---------------------------------------------------------------------
//
// Damaged Units Action
//
private function DamageEnumUnit takes nothing returns nothing
// Define all used locals in this func
local unit u = GetEnumUnit()
local unit dummy
local real x
local real y
local real cx
local real cy
local integer id
local real dist
local real rad
local real ops
local real adj
local real life
local integer size = 64
local integer arrayx = QMod(GetHandleId(u), size)
set id = TempId
// Check is unit already damaged
if (UnitDamaged[ Array2D(arrayx, id, size-1) ]) then
set u = null
return
endif
// Unit is not damaged
// Calculate: is unit at blade position?
set x = GetUnitX(u)
set y = GetUnitY(u)
set cx = GetUnitX(Caster[id])
set cy = GetUnitY(Caster[id])
set dist = SquareRoot(Pow2(cx-x) + Pow2(cy-y))
set rad = (CasterRadians[id]-PI_HALF) - Atan2(y-cy, x-cx)
set ops = dist * Sin(rad)
set adj = ops / Tan(rad)
if ((ops <= SS_BLADE_HEIGHT*0.5) and (adj <= SS_BLADE_WIDTH)) then
// Unit is at balde position, deal damage
set UnitDamaged[ Array2D(arrayx, id, size-1) ] = true
set life = GetUnitState(u, UNIT_STATE_LIFE)
call UnitDamageTarget(Caster[id], u, Damage[SS_SpellLevel[id]], true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_METAL_HEAVY_SLICE)
call DestroyEffect(AddSpecialEffectTarget(BLOOD_EFFECT, u, ATTACH_POINT_ORIGIN))
// Error occurred while trying to damage target
if (GetUnitState(u, UNIT_STATE_LIFE) == life) then
set dummy = CreateUnit(GetOwningPlayer(Caster[id]), DUMMY_CODE, x, y, bj_UNIT_FACING)
call UnitDamageTarget(dummy, u, Damage[SS_SpellLevel[id]], true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_METAL_HEAVY_SLICE)
call RemoveUnit(dummy)
set dummy = null
endif
// Now we can for sure check if unit is dead
// And if he is we can blow him up using little physics
// engine if enabled
if (USE_PHYSICS_ENGINE) then
if (not IsUnitType(u, UNIT_TYPE_DEAD)) then
set u = null
return
endif
call GroupAddUnit(BlowedUnitsGroup[id], u)
call SetUnitPathing(u, false)
call MoveLocation(LOC_Z, x, y)
if (CircleMove[id]) then
set PhysicsBlowAngle[ Array2D(arrayx, id, size-1) ] = CasterRadians[id] - (adj * (PI_HALF / SS_BLADE_WIDTH))
else
set PhysicsBlowAngle[ Array2D(arrayx, id, size-1) ] = CasterRadians[id]
endif
// We cant use damage as argument here as in some supernatural maps
// damage could be 1000. or something and in normal maps to 150.
// So using it will make sn maps blow unit to heaven...
set PhysicsUnitForce[ Array2D(arrayx, id, size-1) ] = GetRandomReal(200., 290.)
set PhysicsUnitVelocity[ Array2D(arrayx, id, size-1) ] = GetRandomReal(450., 600.)
set PhysicsFloorZ[ Array2D(arrayx, id, size-1) ] = GetLocationZ(LOC_Z)
call UnitAddAbility(u, CROW_FORM_RAWCODE)
call UnitRemoveAbility(u, CROW_FORM_RAWCODE)
endif
endif
set u = null
endfunction
//---------------------------------------------------------------------
//
// Illusions group work action
//
private function IllusionsFadeIn takes nothing returns nothing
local unit u = GetEnumUnit()
local integer alpha = GetUnitUserData(u)
if (alpha < 128) then
call SetUnitTimeScale(u, REAL_NULL)
endif
set alpha = alpha - 2
if (alpha == INT_NULL) then
call SetUnitUserData(u, INT_NULL)
call GroupRemoveUnit(IllusionGroup[TempId], u)
call RemoveUnit(u)
else
call SetUnitVertexColor(u, MAX_HEX_RGB, MAX_HEX_RGB, MAX_HEX_RGB, alpha)
call SetUnitUserData(u, alpha)
endif
set u = null
endfunction
//---------------------------------------------------------------------
//
// This is boolexpr filter used to check is destructable dead
//
private function IsDestAlive takes nothing returns boolean
return (GetWidgetLife(GetFilterDestructable()) > REAL_NULL)
endfunction
//---------------------------------------------------------------------
//
// Simple enum destructables kill func
//
private function KillEnumDestructables takes nothing returns nothing
local boolean b = false
local integer i = 1
local destructable d = GetEnumDestructable()
local sound s
loop
set b = (GetDestructableTypeId(d) == TreeType[i])
set i = i + 1
exitwhen ((b) or (i > NTreeTypes))
endloop
if (b) then
set s = CreateSound("Sound\\Destructibles\\TreeFall"+I2S(GetRandomInt(1, 3))+".wav", false, true, true, 10, 10, "")
call SetSoundPosition(s, GetDestructableX(d), GetDestructableY(d), REAL_NULL)
call StartSound(s)
call KillSoundWhenDone(s)
set s = null
endif
call KillDestructable(d)
set d = null
endfunction
//---------------------------------------------------------------------
//
// Physics function which periodically
// moves all blowed units
//
private function PhysicsBlowUnit takes nothing returns nothing
// Locals
local unit u = GetEnumUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real z = GetUnitFlyHeight(u)
local integer array2d = Array2D(QMod(GetHandleId(u), 64), TempId, 63)
local real vel = PhysicsUnitVelocity[array2d] - 25. * TIMER_MOVE_DELAY
local real rad = PhysicsBlowAngle[array2d]
set PhysicsUnitForce[array2d] = PhysicsUnitForce[array2d] - 850. * TIMER_MOVE_DELAY
set PhysicsUnitVelocity[array2d] = vel
set vel = vel * TIMER_MOVE_DELAY
// Move Unit
set x = x + vel * Cos(rad)
set y = y + vel * Sin(rad)
call SetUnitX(u, x)
call SetUnitY(u, y)
call MoveLocation(LOC_Z, x, y)
// Change Height
set z = z + (PhysicsUnitForce[array2d]*TIMER_MOVE_DELAY) + (PhysicsFloorZ[array2d]-GetLocationZ(LOC_Z))
call SetUnitFlyHeight(u, z, REAL_NULL)
set PhysicsFloorZ[array2d] = GetLocationZ(LOC_Z)
// End When On Floor
if (GetUnitFlyHeight(u) <= .333) then
call SetUnitFlyHeight(u, REAL_NULL, REAL_NULL)
call GroupRemoveUnit(BlowedUnitsGroup[TempId], u)
if (IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)) then
call DestroyEffect(AddSpecialEffect(DUST_EFFECT, x, y))
else
call DestroyEffect(AddSpecialEffect(WATER_EFFECT, x, y))
endif
endif
endfunction
//////////////////////////////////////////////////////////////////
// Slash Strike Main Condition //
//////////////////////////////////////////////////////////////////
private constant function SlashStrikeCond takes nothing returns boolean
return ( GetSpellAbilityId() == ABILITY_SLASH_STRIKE )
endfunction
//----------------------------------------------------------------
//-- Slash Strike Periodic Action --
//----------------------------------------------------------------
private function SlashStrikePeriodicMove takes nothing returns nothing
// Define all local variable we will use
local trigger this = GetTriggeringTrigger()
local integer id = StoredValue[QMod(GetHandleId(this) - 0xFFFFF, 128)]
local unit u = Caster[id]
local real tx = TargetPosX[id]
local real ty = TargetPosY[id]
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real tdist = SquareRoot(Pow2(tx-x) + Pow2(ty-y))
local real rad = CasterRadians[id]
local fogmodifier fm = null
local unit illusion = null
local real minx = REAL_NULL
local real miny = REAL_NULL
local real maxx = REAL_NULL
local real maxy = REAL_NULL
local real bwx = REAL_NULL
local real bwy = REAL_NULL
local real ax
local real ay
local real bx
local real by
local real cx
local real cy
local real dx
local real dy
local real cos
local real sin
set TimerExec[id] = TimerExec[id] + 1
// Check For Target Movement
// Here we push target if it is blowed
// with quick slash
if (PushTarget[id]) then
set tx = GetUnitX(TargetUnit[id]) + TARGET_BLOW_SPEED*TIMER_MOVE_DELAY * Cos(rad)
set ty = GetUnitY(TargetUnit[id]) + TARGET_BLOW_SPEED*TIMER_MOVE_DELAY * Sin(rad)
set CasterDistance[id] = CasterDistance[id] - (TARGET_BLOW_SPEED*TIMER_MOVE_DELAY)
// Add Target Move Effects
if (QMod(TimerExec[id], 25) == INT_NULL) then
if (IsTerrainPathable(tx, ty, PATHING_TYPE_FLOATABILITY)) then
call DestroyEffect(AddSpecialEffect(DUST_EFFECT, tx, ty))
else
call DestroyEffect(AddSpecialEffect(WATER_EFFECT, tx, ty))
endif
endif
call SetUnitPosition(TargetUnit[id], tx, ty)
// If target has no more distance left to pass
// then end spell
if (CasterDistance[id] <= REAL_NULL) then
set PushTarget[id] = false
call PauseUnit(TargetUnit[id], false)
call SetUnitTimeScale(TargetUnit[id], 1.)
endif
endif
// We first check is caster still alive
if (GetUnitState(u, UNIT_STATE_LIFE) <= REAL_NULL and u != null) then
call EndSlashStrike()
return
endif
// Damage Deal Action
//*****************************************
set rad = -PI_HALF + rad
set cos = Cos(rad)
set sin = Sin(rad)
set bwx = x + SS_BLADE_WIDTH * cos
set bwy = y + SS_BLADE_WIDTH * sin
//*****************************************
set ax = x + SS_BLADE_HEIGHT*0.5 * Cos(PI_HALF+rad)
set ay = y + SS_BLADE_HEIGHT*0.5 * Sin(PI_HALF+rad)
set cx = ax + SS_BLADE_WIDTH * cos
set cy = ay + SS_BLADE_WIDTH * sin
set bx = ax + SS_BLADE_HEIGHT * Cos(-PI_HALF+rad)
set by = ax + SS_BLADE_HEIGHT * Sin(-PI_HALF+rad)
set dx = bx + SS_BLADE_WIDTH * cos
set dy = by + SS_BLADE_WIDTH * sin
// Now get minX, minY, maxX, maxY
// Of course it could be done in loop but this way
// its much faster
set minx = ax
set maxx = ax
if (minx > bx) then
set minx = bx
endif
if (maxx < bx) then
set maxx = bx
endif
if (minx > cx) then
set minx = cx
endif
if (maxx < cx) then
set maxx = cx
endif
if (minx > dx) then
set minx = dx
endif
if (maxx < dx) then
set maxx = dx
endif
set miny = ay
set maxy = ay
if (miny > by) then
set miny = by
endif
if (maxy < by) then
set maxy = by
endif
if (miny > cy) then
set miny = cy
endif
if (maxy < cy) then
set maxy = cy
endif
if (miny > dy) then
set miny = dy
endif
if (maxy < dy) then
set maxy = dy
endif
set TempId = id
if (NQuickSlashes[id] != ERROR) then
call SetRect(DamageRect[id], minx, miny, maxx, maxy)
else
call SetRect(DamageRect[id], REAL_NULL, REAL_NULL, REAL_NULL, REAL_NULL)
endif
call GroupEnumUnitsInRect(DamagedUnitsGroup[id], DamageRect[id], DamageFilter[id])
call ForGroup(DamagedUnitsGroup[id], function DamageEnumUnit)
set rad = PI_HALF + rad
// Do illusion works (actions) if wanted
if (ILLUSION_COUNTER != ERROR) then
// This must be loaded only if caster is not null
if (QMod(TimerExec[id], R2I(1./TIMER_MOVE_DELAY)/ILLUSION_COUNTER) == INT_NULL and u != null and NQuickSlashes[id] != ERROR) then
// Load new illusion
set illusion = CreateUnit(PLAYER_NP, SS_CASTER_IMAGE, x, y, rad*bj_RADTODEG)
call SetUnitTimeScale(illusion, GetRandomReal(ANIM_MIN_POS, ANIM_MAX_POS)/TIMER_MOVE_DELAY)
call SetUnitAnimation(illusion, CASTER_SPELL_ANIMATION)
call SetUnitColor(illusion, PLAYER_COLOR_LIGHT_BLUE)
call UnitAddAbility(illusion, ABILITY_LOCUST)
call SetUnitVertexColor(illusion, MAX_HEX_RGB, MAX_HEX_RGB, MAX_HEX_RGB, 128)
call SetUnitUserData(illusion, 128)
call GroupAddUnit(IllusionGroup[id], illusion)
call SetUnitX(illusion, x)
call SetUnitY(illusion, y)
endif
// Fade illusions
call ForGroup(IllusionGroup[id], function IllusionsFadeIn)
endif
// If we are using physics engine then check does group
// has any unit that is dead
if (USE_PHYSICS_ENGINE) then
if (FirstOfGroup(BlowedUnitsGroup[id]) != null) then
call ForGroup(BlowedUnitsGroup[id], function PhysicsBlowUnit)
endif
endif
// If spell has ended skip remaining actions
if (u == null or NQuickSlashes[id] == ERROR) then
return
endif
// If we are allowed to kill destructables then kill them
if (CAN_KILL_TRESS) then
call SetRect(DEST_RECT, x-100., y-100., x+100., y+100.)
call EnumDestructablesInRect(DEST_RECT, DestFilter[id], function KillEnumDestructables)
endif
// During Move Check Terrain Type To Add Effects
if (IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)) then
if (QMod(TimerExec[id], 17) == INT_NULL) then
call DestroyEffect(AddSpecialEffect(DUST_EFFECT, x, y))
endif
else
if (QMod(TimerExec[id], 6) == INT_NULL) then
call DestroyEffect(AddSpecialEffect(WATER_EFFECT, x, y))
endif
endif
// We have to check is qucik slash enabled at close
// combat and do close combat actions
if (QuickSlash[id]) then
if (NQuickSlashes[id] > INT_NULL) then
set x = x + CASTER_MOVE_SPEED*TIMER_MOVE_DELAY * ConstCos[id]
set y = y + CASTER_MOVE_SPEED*TIMER_MOVE_DELAY * ConstSin[id]
// Check Distance
if (SquareRoot(Pow2(tx-x) + Pow2(ty-y)) <= CASTER_MOVE_SPEED*TIMER_MOVE_DELAY or SquareRoot(Pow2(GetUnitX(id:TargetUnit)-GetUnitX(u)) + Pow2(GetUnitY(id:TargetUnit)-GetUnitY(u))) >= SS_SLASH_RADIUS*1.5) then
debug call Msg("exec")
// Set next line / position move
set NQuickSlashes[id] = NQuickSlashes[id] - 1
set tdist = GetRandomReal(SS_SLASH_RADIUS/2., SS_SLASH_RADIUS*1.5)
set rad = GetRandomReal(REAL_NULL, bj_2PI)
set tx = GetUnitX(TargetUnit[id])
set ty = GetUnitY(TargetUnit[id])
set tx = tx + tdist * Cos(rad)
set ty = ty + tdist * Sin(rad)
set TargetPosX[id] = tx
set TargetPosY[id] = ty
set CasterRadians[id] = Atan2(ty-y, tx-x)
set ConstCos[id] = Cos(CasterRadians[id])
set ConstSin[id] = Sin(CasterRadians[id])
call SetUnitFacing(u, CasterRadians[id]*bj_RADTODEG)
// We now know that caster will start to move
// to target position
if (NQuickSlashes[id] == INT_NULL) then
call SetUnitTimeScale(u, 2.)
call SetUnitAnimationByIndex(u, CASTER_QSLASH_ANIM_INDEX)
endif
endif
elseif (NQuickSlashes[id] == INT_NULL) then
// Move to target
set tx = GetUnitX(TargetUnit[id])
set ty = GetUnitY(TargetUnit[id])
set rad = Atan2(ty-y, tx-x)
call SetUnitFacing(u, rad*bj_RADTODEG)
set CasterRadians[id] = rad
set x = x + CASTER_MOVE_SPEED*TIMER_MOVE_DELAY * Cos(rad)
set y = y + CASTER_MOVE_SPEED*TIMER_MOVE_DELAY * Sin(rad)
// Check Disatnce (final)
if (SquareRoot(Pow2(tx-x) + Pow2(ty-y)) <= 2.*CASTER_COLLISION_SIZE) then
// Disable Group Damage Or Any Other
// While Caster Is Hidden
set NQuickSlashes[id] = ERROR
call SetUnitInvulnerable(u, true)
set fm = CreateFogModifierRadius(GetOwningPlayer(u), FOG_OF_WAR_VISIBLE, tx, ty, 512., true, false)
call FogModifierStart(fm)
call ShowUnit(u, false)
call PauseUnit(TargetUnit[id], true)
call SetUnitTimeScale(TargetUnit[id], REAL_NULL)
call DestroyEffect(AddSpecialEffect(BLINK_CASTER_QModEL, x, y))
call Sleep(GetRandomReal(0.5, 0.75))
// Show caster and order an a attack
// this attack blows target away
call ShowUnit(u, true)
set rad = GetRandomReal(REAL_NULL, bj_2PI)
set x = tx + QSLASH_TARGET_DISTANCE * Cos(rad)
set y = ty + QSLASH_TARGET_DISTANCE * Sin(rad)
call SetUnitX(u, x)
call SetUnitY(u, y)
set rad = Atan2(ty-y, tx-x)
call SetUnitFacing(u, rad*bj_RADTODEG)
call DestroyEffect(AddSpecialEffect(BLINK_TARGET_QModEL, x, y))
if (not IsUnitType(TargetUnit[id], UNIT_TYPE_DEAD)) then
// Target is still alive, if dies now will be pushed
// even if it stays alive it will be pushed
call SetUnitAnimationByIndex(u, CASTER_QSLASH_ANIM_INDEX)
call Sleep(MIN_SLEEP)
if (USE_PHYSICS_ENGINE) then
set CasterDistance[id] = 250.
set CasterRadians[id] = rad
set PushTarget[id] = true
endif
// If target already taked damage it will take once more
// this is only possible in quick slash on target unit
call UnitDamageTarget(u, TargetUnit[id], Damage[SS_SpellLevel[id]], true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_METAL_HEAVY_SLICE)
call DestroyEffect(AddSpecialEffectTarget(BLOOD_EFFECT, TargetUnit[id], ATTACH_POINT_ORIGIN))
endif
// As said before caster is invulnerable while hidden to
// prevent possible bugs...
call SetUnitInvulnerable(u, false)
call SetUnitTimeScale(TargetUnit[id], 1.)
// Select back caster as while hidden its deselected
if (GetLocalPlayer() == GetOwningPlayer(u)) then
call SelectUnit(u, true)
endif
call Sleep(CASTER_QSLASH_ANIM_DURATION/2.-0.25)
call FogModifierStop(fm)
call DestroyFogModifier(fm)
set fm = null
// End it
call EndSlashStrike()
return
endif
endif
// This action is executed while caster is moving
// to target position where taregt is v (victim)
call SetUnitPosition(u, x, y)
return
endif
//*****************************************************************
// Here we know that its far normal slash cast
//*****************************************************************
// Second check do we have to change move type
if (tdist <= 100. and not CircleMove[id]) then
call SetUnitPosition(u, tx, ty)
set x = tx
set y = ty
set rad = rad + PI_HALF
set CircleMove[id] = true
set TargetPosX[id] = tx + SS_SLASH_RADIUS * Cos(rad)
set TargetPosY[id] = ty + SS_SLASH_RADIUS * Sin(rad)
// This is end of spell
if (CasterRadians[id] == BackRadians[id]) then
call EndSlashStrike()
endif
return
// If not do constant move
elseif (not CircleMove[id]) then
set x = x + CASTER_MOVE_SPEED*TIMER_MOVE_DELAY * ConstCos[id]
set y = y + CASTER_MOVE_SPEED*TIMER_MOVE_DELAY * ConstSin[id]
// Check can we move to new point
if (IsTerrainPathable(x + CASTER_COLLISION_SIZE*TIMER_MOVE_DELAY * ConstCos[id], y + CASTER_COLLISION_SIZE*TIMER_MOVE_DELAY * ConstSin[id], PATHING_TYPE_WALKABILITY)) then
call EndSlashStrike()
return
endif
call SetUnitPosition(u, x, y)
// Circle Move
else
set CasterRadians[id] = rad + (CASTER_MOVE_SPEED*TIMER_MOVE_DELAY)*(1. / SS_SLASH_RADIUS)
call SetUnitFacing(u, rad*bj_RADTODEG)
set rad = Atan2(y-TargetPosY[id], x-TargetPosX[id]) + (CASTER_MOVE_SPEED*TIMER_MOVE_DELAY)*(1. / SS_SLASH_RADIUS)
set x = TargetPosX[id] + SS_SLASH_RADIUS * Cos(rad)
set y = TargetPosY[id] + SS_SLASH_RADIUS * Sin(rad)
// Check can we move to new point
if (IsTerrainPathable(x + CASTER_COLLISION_SIZE * Cos(rad), y + CASTER_COLLISION_SIZE * Sin(rad), PATHING_TYPE_WALKABILITY)) then
call EndSlashStrike()
return
endif
call SetUnitPosition(u, x, y)
// End Circle Move
if (CasterRadians[id] >= BackRadians[id]) then
set CasterRadians[id] = BackRadians[id]
set CircleMove[id] = false
set CasterRadians[id] = BackRadians[id]
set ConstCos[id] = Cos(BackRadians[id])
set ConstSin[id] = Sin(BackRadians[id])
set TargetPosX[id] = x + CasterDistance[id] * ConstCos[id]
set TargetPosY[id] = y + CasterDistance[id] * ConstSin[id]
endif
endif
endfunction
//////////////////////////////////////////////////////////////////
// Slash Strike Main Action //
//////////////////////////////////////////////////////////////////
private function SlashStrikeAction takes nothing returns nothing
//-----------------------------
// Load all local variables
//-----------------------------
local unit u = GetTriggerUnit()
local unit v = GetSpellTargetUnit()
local player p = GetOwningPlayer(u)
local real x1 = GetUnitX(u)
local real x2 = GetUnitX(v)
local real y1 = GetUnitY(u)
local real y2 = GetUnitY(v)
local real dist = SquareRoot(Pow2(x1-x2) + Pow2(y1-y2))
local real rad = Atan2(y2-y1, x2-x1)
local real x = REAL_NULL
local real y = REAL_NULL
local integer id = NCast
local trigger this = null
local integer lvl = GetUnitAbilityLevel(u, ABILITY_SLASH_STRIKE)
// Not supposed to happen, however we will check to make sure
if (lvl > Levels) then
call DisplayTimedTextToPlayer(GetLocalPlayer(), REAL_NULL, REAL_NULL, 15., "|cffff0000Spell: Slash Strike can't be cast because level of spell is greater then defined.|r")
call StartSound(ERROR_SOUND)
call SetSoundVolume(ERROR_SOUND, 127)
return
endif
set NCast = QMod(NCast+1, 128)
// If we have enough distance then start quick move slash
if (dist > SS_SLASH_RADIUS+DISTANCE_FROM_TARGET) then
set rad = rad - PI_HALF
set x = x1 + SS_SLASH_RADIUS * Cos(rad)
set y = y1 + SS_SLASH_RADIUS * Sin(rad)
set rad = rad + PI_HALF
set CasterDistance[id] = (dist - SS_SLASH_RADIUS - DISTANCE_FROM_TARGET)
set TargetPosX[id] = x + CasterDistance[id] * Cos(rad)
set TargetPosY[id] = y + CasterDistance[id] * Sin(rad)
set CircleMove[id] = false
set QuickSlash[id] = false
set NQuickSlashes[id] = 0
else
// If we dont have enough distance then start quick slash
set rad = GetRandomReal(REAL_NULL, bj_2PI)
set dist = GetRandomReal(SS_SLASH_RADIUS/2., SS_SLASH_RADIUS*1.5)
set x = x2 + dist * Cos(rad)
set y = y2 + dist * Sin(rad)
set rad = GetRandomReal(REAL_NULL, bj_2PI)
call SetRandomSeed(R2I(GetRandomReal(0., rad*10000.)))
set dist = GetRandomReal(SS_SLASH_RADIUS/2., SS_SLASH_RADIUS*1.5)
set TargetPosX[id] = x2 + dist * Cos(rad)
set TargetPosY[id] = y2 + dist * Sin(rad)
set rad = Atan2(TargetPosY[id]-y, TargetPosX[id]-x)
set NQuickSlashes[id] = QuickSlashes[lvl]
set TargetUnit[id] = v
set QuickSlash[id] = true
endif
// Check can we move caster
if (IsTerrainPathable(x + CASTER_COLLISION_SIZE * Cos(rad), y + CASTER_COLLISION_SIZE * Sin(rad), PATHING_TYPE_WALKABILITY)) then
if (GetLocalPlayer() == p) then
call SetSoundVolume(ERROR_SOUND, 110)
call StartSound(ERROR_SOUND)
endif
call DisplayTimedTextToPlayer(p, REAL_NULL, REAL_NULL, 15., "|cffffcc00Invalid cast position!|r")
return
endif
// Make sure that we can change z of caster
call UnitAddAbility(u, CROW_FORM_RAWCODE)
call UnitRemoveAbility(u, CROW_FORM_RAWCODE)
// Add effects
call SetUnitInvulnerable(u, true)
call DestroyEffect(AddSpecialEffect(BLINK_CASTER_QModEL, x1, y1))
call Sleep(MIN_SLEEP)
call DestroyEffect(AddSpecialEffect(BLINK_TARGET_QModEL, x, y))
call SetUnitInvulnerable(u, false)
// Change Position & Setup Caster
call SetUnitPosition(u, x, y)
call SetUnitFacing(u, rad*bj_RADTODEG)
call PauseUnit(u, true)
call SetUnitAnimation(u, CASTER_SPELL_ANIMATION)
call SetUnitTimeScale(u, 1.78324)
call Sleep(CASTER_READY_DURATION/1.78324-0.25)
call SetUnitTimeScale(u, .0316)
// Caster is dead
if (GetUnitState(u, UNIT_STATE_LIFE) <= REAL_NULL) then
call PauseUnit(u, false)
call SetUnitTimeScale(u, 1.)
return
endif
// Store Current Value And Get Next + Start Timer + Load Trigger
set TriggerCount = TriggerCount + 1
set this = CreateTrigger()
set StoredValue[QMod(GetHandleId(this) - 0xFFFFF, 128)] = id
set ConstSin[id] = Sin(rad)
set ConstCos[id] = Cos(rad)
set BackRadians[id] = rad + bj_PI
set TimerExec[id] = INT_NULL
set PushTarget[id] = false
set CasterRadians[id] = rad
set Caster[id] = u
if (ILLUSION_COUNTER != ERROR) then
set IllusionGroup[id] = CreateGroup()
endif
if (USE_PHYSICS_ENGINE) then
set BlowedUnitsGroup[id] = CreateGroup()
endif
if (CAN_KILL_TRESS) then
set DestFilter[id] = Filter(function IsDestAlive)
endif
set DamagedUnitsGroup[id] = CreateGroup()
set DamageFilter[id] = Filter(function DamageFilterFunc)
set DamageRect[id] = Rect(REAL_NULL, REAL_NULL, REAL_NULL, REAL_NULL)
set SS_SpellLevel[id] = lvl
if (SSTimer == null) then
set SSTimer = CreateTimer()
call TimerStart(SSTimer, TIMER_MOVE_DELAY, TRUE, null)
endif
call TriggerRegisterTimerExpireEvent(this, SSTimer)
set TAction[id] = TriggerAddAction(this, function SlashStrikePeriodicMove)
call SetUnitPathing(u, false)
// Simple null locals
set p = null
set u = null
set v = null
set this = null
endfunction
//===========================================================================
private function SlashStrike_Main takes nothing returns nothing
// Init locals
local trigger t = CreateTrigger()
local sound s = null
// Get All Players And Register Spell Cast Events
// Add Condition And Action
call TriggerRegisterAnyUnitEvent(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function SlashStrikeCond))
call TriggerAddAction(t, function SlashStrikeAction)
// *** Preload Files ***
// Load Sounds
call SetSoundVolume(ERROR_SOUND, 1)
call StartSound(ERROR_SOUND)
if (CAN_KILL_TRESS) then
set s = CreateSound("Sound\\Destructibles\\TreeFall1.wav", false, true, true, 10, 10, "")
call SetSoundVolume(s, 1)
call SetSoundPosition(s, REAL_NULL, REAL_NULL, REAL_NULL)
call StartSound(s)
call KillSoundWhenDone(s)
set s = CreateSound("Sound\\Destructibles\\TreeFall2.wav", false, true, true, 10, 10, "")
call SetSoundVolume(s, 1)
call SetSoundPosition(s, REAL_NULL, REAL_NULL, REAL_NULL)
call StartSound(s)
call KillSoundWhenDone(s)
set s = CreateSound("Sound\\Destructibles\\TreeFall3.wav", false, true, true, 10, 10, "")
call SetSoundVolume(s, 1)
call SetSoundPosition(s, REAL_NULL, REAL_NULL, REAL_NULL)
call StartSound(s)
call KillSoundWhenDone(s)
endif
// Load Files / QModels
call Preload(BLINK_CASTER_QModEL)
call Preload(BLINK_TARGET_QModEL)
call Preload(DUST_EFFECT)
call Preload(WATER_EFFECT)
call Preload(BLOOD_EFFECT)
call PreloadStart()
// *** Finish Preload ***
// Load User Spell Values
call SlashStrike_Setup()
set s = null
endfunction
endlibrary
//TESH.scrollpos=355
//TESH.alwaysfold=0
//**************************************************************************************
// Spell: Electric Field
// Author: Dark Dragon
// Date: 15 November 2008
//
// Other: Spell made for fun testing vJass
//
//
// Installation:
//
// - 1. Make sure you have open the JassNewGen Pack editor not normal one and make sure you have last version
// - 2. Go to Import Manager (F12) and extract BTN_CR_Alightings.tga and its DISBTN version
// Great Lightning.mdx and thunder1.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 dummy unit {Dummy (DD Universal Pack)}
// Note: if you have copied you dont need to copy it again, only one dummy is needed.
// Copy ability Electric Field (Dummy Effect) and Electric Field main ability.
// When you are asked for raw codes type following for:
// Dummy: 'dumy'
// Electric Field: 'elfd'
// Electric Field (Dummy): 'efef'
// - 5. If you want different raw codes edit the below as well:
// - 6. Edit date below to your own will and enjoy the spell:
// - 7. And of course copy this trigger and DDUniversal Pack to your map.
//
//**************************************************************************************
// The electric field library
library ElectricField initializer ElectricField_Main requires DDUniversalPack
// **************************************************************************
// ** SPELL SETUP **
// ** **
// ** This variables are constants and are made to easy change spell style **
// ** Define them as you like. **
// **************************************************************************
globals
// This is the raw code of Electric Field
// Default: 'elfd'
private constant integer ABILITY_ELECTRIC_FIELD = 'elfd'
// This is the raw code of Electric Field (Dummy Effect)
// Default: 'efef'
private constant integer ABILITY_DUMMY_ELECTRIC_FIELD = 'efef'
// This is the raw code of Dummy (DD Universal Pack) (change it in DDUniversal Pack trigger)
// Default: 'dumy'
private constant integer UNIT_DUMMY_CODE = DD_DUMMY_CODE
// This is purge ability rawcode
// Deafault: 'Apg2'
private constant integer PURGE_CODE = 'Apg2'
// This is thunder sounds max distance that it can
// be heard
// Default: 6000.
private constant real SOUND_MAX_DISTANCE = 6000.
// The delay of thunder when casting, will wait x seconds
// and after that time units will be striked with lightning
// Default: 0.5
private constant real THUNDER_DELAY = 0.5
// The unit velocity of z-axis during quick move
// Default: 436.
private constant real UNIT_DISAPPEAR_VELOCITY_QUICK = 436.
// The unit velocity of z-axis during slow move
// Default: 73.
private constant real UNIT_DISAPPEAR_VELOCITY_SLOW = 73.
// When lightning reaches ground this effect
// will be loaded
// Default: "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdx"
private constant string EF_EXPLOSION_EFFECT = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdx"
// This are random thunderbolt effects around the field
// Default: "Doodads\\Cinematic\\Lightningbolt\\Lightningbolt.mdl"
private constant string EF_THUNDERBOLT_EFFECT = "Doodads\\Cinematic\\Lightningbolt\\Lightningbolt.mdx"
// Effect which is loaded from the sky
// Default: "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldTarget.mdl"
private constant string EF_LIGHTNING_EFFECT = "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldTarget.mdx"
// Effect which is loaded when unit disappears
// Default: "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
private constant string EF_LIGHTNING_IMPACT = "Abilities\\Weapons\\Bolt\\BoltImpact.mdx"
// The main custom lightning QModel
// Default: "Lightning\\Great Lightning.mdl"
private constant string GREAT_LIGHTNING_QModEL = "Lightning\\Great Lightning.mdx"
// Sound file of lightning
// Default: "Custom\\Ambient\\thunder1.wav"
private constant string SOUND_THUNDER = "Custom\\Ambient\\thunder1.wav"
private constant real SOUND_THUNDER_VOL = 100.
// Duration of upper lightning effect
// Default: 0.6
private constant real LIGHTNING_EFFECT_DURATION = 0.6
// This is max camera noise range that players will be effected
// Default: 2000.
private constant real CAMERA_NOISE_RANGE = 2000.
// Set it to true or false, if you want camera noise set it to true
// else to false
// Default: TRUE
private constant boolean USE_CAMERA_NOISE = TRUE
// This is the radius of terrain deform
// Default: 512.
private constant real TERRAIN_RADIUS = 512.
// This is the depth of terrain deform
// Default: 128.
private constant real TERRAIN_DEPTH = 128.
// Duration of terrain deform in seconds
// Default: 0.75
private constant real TERRAIN_DEFORM_DURATION = 0.75
// This is number of how much bolts are created
// Default: 15
private constant integer NUMBER_OF_BOLTS = 15
// How often are the bolts created
// Default: 0.1
private constant real SINGLE_BOLT_DELAY = 0.1
// This variables are not supposed to be touched here
// Go down below to edit them
//{
private integer Levels = 0
private real array EF_AOE
private real array TargetMaxDamage
private real array SpellMaxDamage
//}
endglobals
//***********************************************************************
//
// This is where you setup your spell, level data
//
private function ElectricField_Setup takes nothing returns nothing
// This is number of levels your spell has
// Default: 3
set Levels = 3
// This is AOE (Area of Effect)
// per level set
// Default 1: 610
// Default 2: 660
// Default 3: 705
set EF_AOE[1] = 610.
set EF_AOE[2] = 660.
set EF_AOE[3] = 705.
// This is max damage per target
// Default 1: 100
// Default 2: 160
// Default 3: 210
set TargetMaxDamage[1] = 125.
set TargetMaxDamage[2] = 260.
set TargetMaxDamage[3] = 400.
// This is the max damage spell can
// deal to all units
// Default 1: 1000
// Default 2: 1650
// Default 3: 2270
set SpellMaxDamage[1] = 1000.
set SpellMaxDamage[2] = 1650.
set SpellMaxDamage[3] = 3260.
endfunction
//********************************************************************
// Do not edit below if you are not a jass
// programmer
//********************************************************************
//----------------
// Spell globals
//----------------
globals
// Constants
private constant real PI = 3.14159
private constant real TPI = 2*PI
private constant real PI_HALF = PI/2
private constant integer INT_NULL = 0
private constant real REAL_NULL = 0.
private constant string ATTACH_POINT_ORIGIN = "origin"
private constant integer ABILITY_LOCUST = 'Aloc'
private constant real LIGHTNING_HEIGHT = 1024.
private constant integer GENERIC_CODE = 'BTLF'
private constant integer ABILITY_CROW_FORM = 'Amrf'
private constant real TIMER_MOVE_DELAY = 0.01
// Other changeable variables
private timer EFTimer = null // Main Spell Timer
private integer TGCount = INT_NULL // Counts Triggers
private effect array TimedEffect[8191] // Effects stored on units
endglobals
// ************************************************************************
// New Types
// ************************************************************************
private struct bolts
public trigger t
private triggeraction ta
public real x
public real y
public integer lvl
private static bolts array B_Ptr[8191]
// *** Constructor ***
static method create takes code fptr, real x, real y, integer lvl returns bolts
local bolts this = bolts.allocate()
set this.t = CreateTrigger()
set this.ta = TriggerAddAction(this.t, fptr)
set this.x = x
set this.y = y
set this.lvl = lvl
set this.B_Ptr[ArrayId(this.t)] = (this)
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call TriggerRemoveAction(this.t, this.ta)
call DestroyTrigger(this.t)
set this.ta = null
set this.t = null
endmethod
public static method GetTriggering takes nothing returns bolts
return bolts.B_Ptr[ArrayId(GetTriggeringTrigger())]
endmethod
endstruct
private type punit extends unit array[1]
private struct unitx
public unit u
public real sd
public real dx
public real dy
private static unitx array U_Ptr[8191]
// *** Constructor ***
static method create takes unit u, real rad, real sd returns unitx
local unitx this = unitx.allocate()
set this.u = u
set this.dx = Cos(rad)
set this.dy = Sin(rad)
set this.sd = sd
set this.U_Ptr[ArrayId(this.u)] = this
return (this)
endmethod
static method ResetElectrifiedHero takes nothing returns nothing
local punit pu = GetSleepData()
call ShowUnit(pu[0], true)
set pu[0] = null
call pu.destroy()
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
local punit pu
if (not IsUnitType(this.u, UNIT_TYPE_HERO)) then
call RemoveUnit(this.u)
else
call SetUnitTimeScale(this.u, 1.)
call SetUnitVertexColor(this.u, 255, 255, 255, 255)
call PauseUnit(this.u, false)
call SetUnitFlyHeight(this.u, 0., 0.)
call ShowUnit(this.u, false)
set pu = punit.create()
set pu[0] = this.u
call SleepEx(3., function unitx.ResetElectrifiedHero, pu)
endif
set this.u = null
endmethod
public static method GetEnum takes nothing returns unitx
return unitx.U_Ptr[ArrayId(GetEnumUnit())]
endmethod
endstruct
private struct triggerx
public trigger t
public triggeraction ta
public group g
public integer i
private static triggerx array T_Ptr[8191]
public static triggerx TriggerX = 0
// *** Constructor ***
static method create takes code pf, group g returns triggerx
local triggerx this = triggerx.allocate()
set this.t = CreateTrigger()
set this.ta = TriggerAddAction(this.t, pf)
set this.g = g
set this.i = 0
set this.T_Ptr[ArrayId(this.t)] = this
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call TriggerRemoveAction(this.t, this.ta)
call DestroyTrigger(this.t)
call DestroyGroup(this.g)
set this.t = null
set this.ta = null
set this.g = null
endmethod
public static method GetTriggering takes nothing returns triggerx
local triggerx this = triggerx.T_Ptr[ArrayId(GetTriggeringTrigger())]
set this.i = this.i + 1
return (this)
endmethod
endstruct
private struct timerx
public timer t
public integer lvl
public group g
public unit u
public player p
public real dmg
public unit d
public triggerx tx
private static timerx array T_Ptr[8191]
public static timerx TimerX = 0
// *** Constructor ***
static method create takes integer lvl, group g, unit u returns timerx
local timerx this = timerx.allocate()
set this.t = CreateTimer()
set this.lvl = lvl
set this.g = g
set this.u = u
set this.p = GetOwningPlayer(u)
set this.tx = 0
set this.T_Ptr[ArrayId(this.t)] = (this)
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call PauseTimer(this.t)
call DestroyTimer(this.t)
call RemoveUnit(this.d)
set this.t = null
set this.g = null
set this.p = null
set this.d = null
endmethod
public static method GetExpired takes nothing returns timerx
return timerx.T_Ptr[ArrayId(GetExpiredTimer())]
endmethod
endstruct
//-------------------------------------------------------------------------
// ***** Other Spell Defined Functions ******
//-------------------------------------------------------------------------
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Spell Filters Conditions And Other Functions
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//***********************************************************************
// Main Unit Group Filter
private function ELFieldGroupFilter takes nothing returns boolean
local unit f = GetFilterUnit()
local player p = GetOwningPlayer(GetTriggerUnit())
local boolean filter = (not IsUnitType(f, UNIT_TYPE_DEAD) and not IsUnitType(f, UNIT_TYPE_STRUCTURE) and IsUnitEnemy(f, p) and IsUnitVisible(f, p) and GetUnitTypeId(f) != UNIT_DUMMY_CODE)
set f = null
set p = null
return (filter)
endfunction
//***********************************************************************
// Add Effects Main Function
private function EFGroupAction takes nothing returns nothing
set TimedEffect[ArrayId(GetEnumUnit())] = AddSpecialEffectTarget(EF_LIGHTNING_EFFECT, GetEnumUnit(), ATTACH_POINT_ORIGIN)
endfunction
//***********************************************************************
// Move Function
private function EFMoveActionEnum takes nothing returns nothing
local unitx ux = unitx.GetEnum()
local real x
local real y
local real z
local integer exec = triggerx.TriggerX.i
// Define movement coordiantes
if (exec <= ux.sd) then
set x = GetUnitX(ux.u) + 2.5 * ux.dx
set y = GetUnitY(ux.u) + 2.5 * ux.dy
set z = GetUnitFlyHeight(ux.u) + UNIT_DISAPPEAR_VELOCITY_QUICK*TIMER_MOVE_DELAY
else
set x = GetUnitX(ux.u) + ux.dx
set y = GetUnitY(ux.u) + ux.dy
set z = GetUnitFlyHeight(ux.u) + UNIT_DISAPPEAR_VELOCITY_SLOW*TIMER_MOVE_DELAY
endif
// Apply movement coordinates
call SetUnitX(ux.u, x)
call SetUnitY(ux.u, y)
call SetUnitFlyHeight(ux.u, z, REAL_NULL)
// Load lightning effects on this execs
if (exec == 125 or exec == 75 or exec == 25 or exec == 175) then
call DestroyEffect(AddSpecialEffectTarget(EF_LIGHTNING_IMPACT, ux.u, ATTACH_POINT_ORIGIN))
endif
// Clear values if this is the last loop
if (exec == 200) then
call ux.destroy()
endif
endfunction
private function EFMoveAction takes nothing returns nothing
local triggerx tx = triggerx.GetTriggering()
// Main Movement Loop
set tx.TriggerX = tx
call ForGroup(tx.g, function EFMoveActionEnum)
// End spell here
if (tx.i == 200) then
set TGCount = TGCount - 1
if (TGCount == INT_NULL) then
call PauseTimer(EFTimer)
call DestroyTimer(EFTimer)
set EFTimer = null
endif
call tx.destroy()
endif
endfunction
//***********************************************************************
// Deal Damage Main Function
private function EFLightningDamageEnum takes nothing returns nothing
local timerx tx = timerx.TimerX
local unit u = GetEnumUnit()
call UnitDamageTarget(tx.d, u, tx.dmg, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_LIGHTNING, WEAPON_TYPE_WHOKNOWS)
call DestroyEffect(AddSpecialEffect(EF_EXPLOSION_EFFECT, GetUnitX(u), GetUnitY(u)))
// Unit is now dead do disappear action
if (IsUnitType(u, UNIT_TYPE_DEAD)) then
// Load new trigger if needed
if (tx.tx == 0) then
set tx.tx = triggerx.create(function EFMoveAction, tx.g)
endif
call unitx.create(u, GetRandomReal(0., TPI), GetRandomReal(25, 40))
call SetUnitVertexColor(u, INT_NULL, INT_NULL, INT_NULL, 255)
call SetUnitTimeScale(u, 0.2)
call SetUnitPathing(u, false)
call UnitAddAbility(u, ABILITY_CROW_FORM)
call UnitRemoveAbility(u, ABILITY_CROW_FORM)
else // Purge
// Load dummy caster
set bj_lastCreatedUnit = CreateUnit(tx.p, UNIT_DUMMY_CODE, GetUnitX(u), GetUnitY(u), bj_UNIT_FACING)
// Setup this dummy
call UnitAddAbility(bj_lastCreatedUnit, ABILITY_LOCUST)
call UnitAddAbility(bj_lastCreatedUnit, PURGE_CODE)
call UnitApplyTimedLife(bj_lastCreatedUnit, GENERIC_CODE, 0.75)
// Order to purge target
call IssueTargetOrder(bj_lastCreatedUnit, "purge", u)
call GroupRemoveUnit(tx.g, u)
endif
set u = null
endfunction
private function EFLightningDamage takes nothing returns nothing
// Load locals, init them
local timerx tx = timerx.GetExpired()
// Setup main dummy
set tx.d = CreateUnit(tx.p, UNIT_DUMMY_CODE, REAL_NULL, REAL_NULL, bj_UNIT_FACING)
call UnitAddAbility(tx.d, ABILITY_LOCUST)
// Setup damage
set tx.dmg = rmin(SpellMaxDamage[tx.lvl] / I2R(CountUnitsInGroup(tx.g)), TargetMaxDamage[tx.lvl])
// Deal damage loop
set tx.TimerX = tx
call ForGroup(tx.g, function EFLightningDamageEnum)
// Load trigger setup (if needed)
if (tx.tx != 0) then
if (EFTimer == null) then
set EFTimer = CreateTimer()
call TimerStart(EFTimer, TIMER_MOVE_DELAY, TRUE, null)
endif
call TriggerRegisterTimerExpireEvent(tx.tx.t, EFTimer)
set TGCount = TGCount + 1
else
call DestroyGroup(tx.g)
endif
// Null variables and destroy objects
call tx.destroy()
endfunction
//***********************************************************************
// Add Dummy Effects Main Function
private function EFBlowGroundEnum takes nothing returns nothing
local unit u = GetEnumUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real rad = GetRandomReal(0., TPI)
local real dist = GetRandomReal(225., 375.)
local unit d = CreateUnit(timerx.TimerX.p, UNIT_DUMMY_CODE, x + dist * Cos(rad), y + dist * Sin(rad), (rad-bj_PI)*bj_RADTODEG)
call SetUnitFlyHeight(d, LIGHTNING_HEIGHT, REAL_NULL)
call UnitAddAbility(d, ABILITY_DUMMY_ELECTRIC_FIELD)
call UnitApplyTimedLife(d, GENERIC_CODE, 0.5)
call UnitAddAbility(d, ABILITY_LOCUST)
call IssueTargetOrder(d, "chainlightning", u)
call DestroyEffect(TimedEffect[ArrayId(u)])
set TimedEffect[ArrayId(u)] = null
set d = null
set u = null
endfunction
private function EFBlowGround takes nothing returns nothing
local timerx tx = timerx.GetExpired()
// Main effects loop...
set tx.TimerX = tx
call ForGroup(tx.g, function EFBlowGroundEnum)
// Continue spell...
call TimerStart(tx.t, 0.2, false, function EFLightningDamage)
endfunction
//***********************************************************************
// This function is trigger action which loads extra lig effects
private function LoadThunderbolts takes nothing returns nothing
// Load basic locals
local bolts bl = bolts.GetTriggering()
local integer exec = GetTriggerExecCount(bl.t)
local real x = bl.x
local real y = bl.y
local real r = EF_AOE[bl.lvl]
local real d = GetRandomReal(0., r)
local real rad = GetRandomReal(0., TPI)
local sound s = null
local effect e
// Gonna improve this QMod a little... (rnd_QMod)
call SetRandomSeed(QMod(GetRandomInt(57, 37521), GetRandomInt(9, 771)))
// This all's gonna blow the whole field...
// Load thunderbolt
set x = x+d*Cos(rad)
set y = y+d*Sin(rad)
set e = AddSpecialEffect(EF_THUNDERBOLT_EFFECT, x, y)
// Is last exec
if (exec == NUMBER_OF_BOLTS) then
// Destroy trigger
call DisableTrigger(bl.t)
// Sleep for effect destroy
call Sleep(1.5)
call DestroyEffect(e)
call bl.destroy()
// Null
set e = null
return
endif
// Destroy effect timed
call Sleep(0.75)
call DestroyEffect(e)
// Null locals
set e = null
endfunction
////////////////////////////////////////////////////////////////////////
// Electric Field Main Condition //
////////////////////////////////////////////////////////////////////////
private constant function ELFieldCondition takes nothing returns boolean
return ( GetSpellAbilityId() == ABILITY_ELECTRIC_FIELD )
endfunction
////////////////////////////////////////////////////////////////////////
// Electric Field Main Action //
////////////////////////////////////////////////////////////////////////
private function ELFieldAction takes nothing returns nothing
// ***********************************************
// Load locals, here we have to use location
local unit u = GetTriggerUnit()
local location l = GetSpellTargetLoc()
local real x = GetLocationX(l)
local real y = GetLocationY(l)
local integer i = INT_NULL
local sound s = null
local real cx = REAL_NULL
local real cy = REAL_NULL
local real d = REAL_NULL
local group g = null
local integer lvl = GetUnitAbilityLevel(u, ABILITY_ELECTRIC_FIELD)
local boolexpr bx = null
local timerx t
local bolts bl
// Not supposed to happen
if (lvl > Levels) then
call BJDebugMsg("|cffff0000Unable to cast spell: Electric Field, current spell level is greater then defined.|r")
return
endif
// Play Thunder Sound
set s = CreateSound(SOUND_THUNDER, false, false, false, 10, 10, "")
// Only users can hear sound
loop
exitwhen (i >= bj_MAX_PLAYERS)
// We set sound volume for each player (matching)
// distance from casted point
if (GetLocalPlayer() == DD_p[i]) then
set cx = GetCameraTargetPositionX()
set cy = GetCameraTargetPositionY()
set d = SquareRoot(hypot(cx-x, cy-y))
if (d > SOUND_MAX_DISTANCE) then
set d = SOUND_MAX_DISTANCE
endif
call SetSoundVolume(s, R2I(SOUND_THUNDER_VOL-d*(SOUND_THUNDER_VOL/SOUND_MAX_DISTANCE)))
// Camera Noise
if (d <= CAMERA_NOISE_RANGE and USE_CAMERA_NOISE) then
call CameraSetSourceNoise(70.-(d*(70./CAMERA_NOISE_RANGE)), 1750.-(d*(1750./CAMERA_NOISE_RANGE)))
endif
endif
set i = i + 1
endloop
// Terrain deformation crater action
call TerrainDeformCrater(x, y, TERRAIN_RADIUS, TERRAIN_DEPTH, R2I(TERRAIN_DEFORM_DURATION*1000.), false)
// Start the sound
call StartSound(s)
call KillSoundWhenDone(s)
set s = null
// Load main big thunder effect
call DestroyEffect(AddSpecialEffect(GREAT_LIGHTNING_QModEL, x, y))
// Load extra lightning effects
if (NUMBER_OF_BOLTS > INT_NULL) then
set bl = bolts.create(function LoadThunderbolts, x, y, lvl)
call TriggerRegisterTimerEvent(bl.t, SINGLE_BOLT_DELAY, TRUE)
endif
// Sleep here if wanted
if (THUNDER_DELAY > REAL_NULL) then
call Sleep(THUNDER_DELAY)
endif
// Load all valid units in to damage group
set g = CreateGroup()
set bx = Filter(function ELFieldGroupFilter)
call GroupEnumUnitsInRange(g, x, y, EF_AOE[lvl], bx)
call DestroyBoolExpr(bx)
// Add Effect To Enum Unit
call ForGroup(g, function EFGroupAction)
// Load Lightning Effects
set t = timerx.create(lvl, g, u)
call TimerStart(t.t, LIGHTNING_EFFECT_DURATION, false, function EFBlowGround)
// Sleep for camera noise
call Sleep(2.00)
// Reset camera noise
if (TGCount <= 1 and USE_CAMERA_NOISE) then
set i = 0
loop
exitwhen (i >= bj_MAX_PLAYERS)
if (GetLocalPlayer() == DD_p[i]) then
call CameraSetSourceNoise(REAL_NULL, REAL_NULL)
endif
set i = i + 1
endloop
endif
// Null locals
call RemoveLocation(l)
set l = null
set u = null
set g = null
set bx = null
endfunction
//===========================================================================
// Main init electric field function
private function ElectricField_Main takes nothing returns nothing
//--------------------->
//* Load init locals
//<--------------------
local trigger t = CreateTrigger()
// Register any unit spell effect event
// Add condition and action
call TriggerRegisterAnyUnitEvent(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function ELFieldCondition))
call TriggerAddAction(t, function ELFieldAction)
// Preload Files
call Preload(GREAT_LIGHTNING_QModEL)
call Preload(EF_EXPLOSION_EFFECT)
call Preload(EF_LIGHTNING_EFFECT)
call Preload(EF_LIGHTNING_IMPACT)
call Preload(EF_THUNDERBOLT_EFFECT)
//call Preload(SOUND_THUNDER)
call GenericSoundDD(SOUND_THUNDER, 0., 0., 0., 1.)
call PreloadStart()
// For Player Neutral Extra make map visible
//call FogQModifierStart(CreateFogQModifierRect(DD_p[14], FOG_OF_WAR_VISIBLE, bj_mapInitialPlayableArea, true, false))
// Call for user defined stuff that will setup this spell
call ElectricField_Setup()
set t = null
endfunction
endlibrary
//TESH.scrollpos=12
//TESH.alwaysfold=0
//**************************************************************************************
// Spell: Dark Torture
// Author: Dark Dragon
// Date: 20 November 2008
//
// Other: Spell made for fun testing vJass
//
//
// Installation:
//
// - 1. Make sure you have open the JassNewGen Pack editor not normal one and make sure you have last version
// - 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 (dummy.mdx -> if not from before)
// - 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 dummy unit {Dummy (DD Universal Pack)}
// sword unit {Sword (DD Universal Pack)}, Dark Torture Slow (Dummy) ability and
// main ability Dark Torture. When you are pasting them to your map make sure you
// type the following rawcodes:
// Dummy: 'dumy'
// Dark Torture: 'Drkt'
// Dark Torture Slow: 'dtsd'
// Sword (DDUniversalPack):'drks'
// - 5. If you want different raw codes edit the below as well:
// - 6. Edit date below to your own will and enjoy the spell:
// - 7. And of course copy this trigger and DDUniversal Pack to your map.
//
//**************************************************************************************
// Dark Torture library
library DarkTorture initializer DarkTorture_Main requires DDUniversalPack
// **************************************************************************
// ** SPELL SETUP **
// ** **
// ** This variables are constants and are made to easy change spell style **
// ** Define them as you like. **
// **************************************************************************
globals
// This is the raw code of the spell 'Dark Torture'
// Default: 'Drkt'
private constant integer DARK_TORTURE_RAWCODE = 'Drkt'
// This is the raw code of the sword
// Default: 'drks'
private constant integer SWORD_RAWCODE = 'drks'
// This the raw code of dummy (DDUniversalPack) (change it in DDUniversal Pack trigger)
// Default: 'dumy'
private constant integer DUMMY_RAWCODE = DD_DUMMY_CODE
// This is the raw code of dark torture slow (dummy) ability
// Default: 'dtsd'
private constant integer SLOW_RAWCODE = 'dtsd'
// This is max sound hear range.
// Default: 3500.
private constant real SOUND_MAX_DISTANCE = 3500.
// This is effect missile of each sword
// Default: "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl"
private constant string SWORD_EFFECT_MODEL = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdx"
// Blood effect QModel path
// Default: "Objects\\SpawnQModels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
private constant string BLOOD_EFFECT = "Objects\\SpawnQModels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdx"
// This is the path of sword start sound. This is called for each sword when he is fired
// Default: "Custom\\Spells\\Dark Torture\\sword_start.wav"
private constant string SWORD_SOUND_START_PATH = "Custom\\Spells\\Dark Torture\\sword_start.wav"
private constant real SWORD_SOUND_START_PATH_VOL = 80.
// This is the path of start sound
// Default: "Custom\\Spells\\Dark Torture\\sword_create.wav"
private constant string SWORD_CREATE_SOUND_PATH = "Custom\\Spells\\Dark Torture\\sword_create.wav"
private constant real SWORD_CREATE_SOUND_PATH_VOL = 55.
// This is the path of start sound
// Default: "Custom\\Spells\\Dark Torture\\sword_hit.wav"
private constant string SWORD_HIT_PATH = "Custom\\Spells\\Dark Torture\\sword_cut.wav"
private constant real SWORD_HIT_PATH_VOL = 70.
// This is sound of swords disappere action
// Default: "Custom\\Spells\\Dark Torture\\sword_disappere.wav"
private constant string SWORD_DISAPPERE_PATH = "Custom\\Spells\\Dark Torture\\sword_disappere.wav"
private constant real SWORD_DISAPPERE_PATH_VOL = 70.
// This is the dust effect when moving
// Default: "Objects\\SpawnQModels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
private constant string DUST_EFFECT = "Objects\\SpawnQModels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdx"
// This is effect loaded on water when pushed
// Default: "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl"
private constant string WATER_EFFECT = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdx"
// Delay of sword creation
// If you want fast sword creation set this value to 0.01
// Warning this value must not be zero
// Default: 0.2
private constant real SWORD_CREATION_DELAY = 0.15
// Delay in seconds, how much time must pass until next sword is shooted
// Default: 0.25
private constant real SWORD_FIRE_DELAY = 0.2
// This tells how much beyond or after casters swords will start loading
// Default: 0.
private constant real SWORD_LOAD_OFFSET = -525.
// This is missile speed (sword speed)
// Speed is defined in game coordinates per second
// Default: 1000.
private constant real SWORDS_SPEED = 1400.
// This is defined max distance of all swords.
// When swords pass this amount of distance they will be
// removed even if they did not effect any target.
// Default: 1700.
private constant real SWORDS_MAX_DISTANCE = 1700.
// This is swords collision size or AoE of
// how much units are effected
// Default: 70.
private constant real SWORD_COLLISION = 70.
// This is swords height (at which z sword is created)
// Default: 80.
private constant real SWORD_HEIGHT = 80.
// This is distance of effects (how much distance will they pass until removed)
// Note: changing this value to negative will turn in different direction
// For negative value {Degress}, for positive value {Degress-180} where Degress
// is spell cast degress
// Default: 1000.
private constant real EFFECTS_MAX_DISTANCE = -1000.
// This is value of how much of speed will fall when sword hits a single target
// Default: 400.
private constant real ON_ADD_SPEED_DECREASE = 500.
// This variables are not supposed to be touched here
//{
private integer Levels = 0
private integer array MaxSwordCount
private real array SwordsMaxDamage
private real array SingleSwordDamage
private real array SwordLineWidth
private real array PushCrashDamage
private real array MaxPushRange
//}
endglobals
// This part of code is used to define spell constants per level
private function DarkTorture_Setup takes nothing returns nothing
// This is spell max level
// This spell max level is 8191, for more than that value game will crash
// I guess, but you dont need such a high level
// Default: 3
set Levels = 3
// Max sword count per level, creates n swords on cast to shoot
// Warning for value of greather then 16 spell will crash
// The value must not be larger then 16
// Default: Level 1 = 6
// Default: Level 2 = 7
// Default: Level 3 = 9
set MaxSwordCount[1] = 6
set MaxSwordCount[2] = 7
set MaxSwordCount[3] = 9
// This is the damage of single sword at specific level
// How much one sword deals damage
// Default: Level 1 = 125.
// Default: Level 2 = 220.
// Default: Level 3 = 300.
set SingleSwordDamage[1] = 125.
set SingleSwordDamage[2] = 220.
set SingleSwordDamage[3] = 300.
// This is spells max damage at one cast
// If this value is low and you create many swords
// this swords will not cause any damage because this defines
// how much damage can spell deal at one cast
// If you want to make all swords effect enemies (all swords deal full damage)
// then do it like that:
// set SwordMaxDamage[level] = SingleSwordDamage[level] * MaxSwordCount[level]
// Of course you can calculate that in your head or on calculator and just type
// value here. So for MaxSwordsCount[1] = 6 and SingleSwordDamage[1] = 125.
// set SwordMaxDamage[1] = 6 * 125 or set SwordMaxDamage[1] = 750 on level 3 it
// will be 9 * 300 which is 2700.
// Default: Level 1 = 625.
// Default: Level 2 = 1320.
// Default: Level 3 = 2100.
set SwordsMaxDamage[1] = 625.
set SwordsMaxDamage[2] = 1320.
set SwordsMaxDamage[3] = 2100.
// This is the value which tells on how big area will the swords
// be shooted and created at.
// Example for low numbers such as 10, almost all swords will be created at same
// place and will cause damage in that small AoE. Of course for spell to
// be better if here is put large number such as 700-800 then more swords
// should be created as well.
// Basically we could say that this is AoE of spell.
// Default: Level 1 = 450.
// Default: Level 2 = 450.
// Default: Level 3 = 450.
set SwordLineWidth[1] = 450.
set SwordLineWidth[2] = 450.
set SwordLineWidth[3] = 450.
// This values are the one that are executed when
// group of pushed units crash in to another unit or wall.
// Just when crashing to wall all units will be damaged, but when some unit gots
// crushed then only that unit will takes this small damage
// Default: Level 1 = 35.
// Default: Level 2 = 45.
// Default: Level 3 = 55.
set PushCrashDamage[1] = 35.
set PushCrashDamage[2] = 45.
set PushCrashDamage[3] = 55.
// This is max push range until main target stops moving with other units
// So after this distance calculated in (coords) all pushed units stop moving
// Default: Level 1 = 300.
// Default: Level 2 = 325.
// Default: Level 3 = 350.
set MaxPushRange[1] = 300.
set MaxPushRange[2] = 325.
set MaxPushRange[3] = 350.
endfunction
//----------------------------------------------------------
// Do not edit below if you are not a jass programmer
//----------------------------------------------------------
// Spell globals which are not supposed to be edited
// in other words should be constants
globals
// << Some spell constants >>
// Pi / 2
private constant real PI_HALF = bj_PI/2.
// Temp location z
private constant location LOC_Z = Location(0., 0.)
// Ability locust
private constant integer LOCUST_RAWCODE = 'Aloc'
// Ability crow form
private constant integer CROWFORM_RAWCODE = 'Amrf'
// Timer min delay
private constant real TIMER_MIN_DELAY = 0.01
// This is object null (sword)
private constant integer NULL = 0
// Target attach point origin
private constant string ATTACH_POINT = "origin"
// This is temp_group
private constant group TempGroup = CreateGroup()
// << Other spell globals >>
private integer TempId = 0 // Spell id cast
private timer DTTimer = null // Main spell timer
private boolexpr GroupFilter = null // Filter used for picking sword targets
private boolean array UnitIsPushed[8191] // Check is unit pushed by another sword
private integer TempSword = NULL // This is used as temp_sword
private integer TGCount = 0 // Counts current triggers
private integer array TriggerSword[15][511] // Swords stored on trigger
private sound array KillSoundVar[8191] // Temp sounds
endglobals
//-------------------------------------------------------------------------------------------
// This spell is different then other ones. Here we will define new object type.
// Sword type which is a new object set of other data.
// So this spell will use struct to define this new type even if it is a little slower then
// working directly with globals
// However its beautiful to code with structs and this language become more object orientated
//-------------------------------------------------------------------------------------------
// ==============================================================
// *** This is our triggerx type which has some extra usage ***
private struct triggerx
public trigger t // Main trigger
private triggeraction ta // Non-leaking trigger action
public group array g[2] // Group trigger pointers
public real dmg // Damage of the single sword
public boolean started // Did spell start?
public integer lvl // Spells level
public triggerx tx // Pointer to other triggerx
public integer array sw[16] // Swords
public integer faliure
private static triggerx array TX_Ptr[8191]
// Allocate (create) method for triggerx
static method create takes code pfunc, real dmg returns triggerx
local triggerx this = triggerx.allocate()
set this.t = CreateTrigger()
set this.ta = TriggerAddAction(this.t, pfunc)
set this.g[0] = CreateGroup()
set this.g[1] = CreateGroup()
set this.dmg = dmg
set this.started = false
set this.TX_Ptr[ArrayId(this.t)] = (this)
return (this)
endmethod
// Allocate with other option
static method new takes code pfunc, integer lvl, triggerx tx, integer faliure returns triggerx
local triggerx this = triggerx.allocate()
set this.t = CreateTrigger()
set this.ta = TriggerAddAction(this.t, pfunc)
set this.lvl = lvl
set this.tx = tx
set this.faliure = faliure
set this.TX_Ptr[ArrayId(this.t)] = (this)
return (this)
endmethod
// *** Destructor method ***
method onDestroy takes nothing returns nothing
if (this.g[0] != null) then
call DestroyGroup(this.g[0])
call DestroyGroup(this.g[1])
endif
call TriggerRemoveAction(this.t, this.ta)
call DestroyTrigger(this.t)
set this.t = null
set this.ta = null
set this.g[0] = null
set this.g[1] = null
endmethod
// Execute method
public method Execute takes nothing returns nothing
call TriggerExecute(this.t)
endmethod
// Register method
public method RegisterTimedExec takes real secs returns nothing
call TriggerRegisterTimerEvent(this.t, secs, true)
endmethod
// Our GetTriggeringTriggerx method
public static method triggeringx takes nothing returns triggerx
return triggerx.TX_Ptr[ArrayId(GetTriggeringTrigger())]
endmethod
endstruct
// ==============================================================
// *** This is our timer storage helper struct type ***
private struct timerx
public timer t // Our main timer
public real x // X of cast point
public real y // Y of cast point
public real rad // Radians from cast point to target point
public real exec // Current execution (its real so i don't have to convert it)
public integer lvl // Spell's level
public integer pid // Player's id
public real fz // The floor z
public triggerx tx // Our triggerx
public integer faliure
private static timerx array TX_Ptr[8191]
// Our constructor timerx()
static method create takes real x, real y, real rad, integer lvl, integer pid, real fz, triggerx tx returns timerx
local timerx this = timerx.allocate()
set this.t = CreateTimer()
set this.x = x
set this.y = y
set this.rad = rad
set this.exec = 0.
set this.lvl = lvl
set this.pid = pid
set this.fz = fz
set this.tx = tx
set this.faliure = 0
set this.TX_Ptr[ArrayId(this.t)] = (this)
return (this)
endmethod
// Our destructor ~timerx()
method onDestroy takes nothing returns nothing
call PauseTimer(this.t)
call DestroyTimer(this.t)
set this.t = null
endmethod
public static method expiredx takes nothing returns timerx
return timerx.TX_Ptr[ArrayId(GetExpiredTimer())]
endmethod
endstruct
// ==============================================================
// *** Finally our main type sword (this is a single sword) ***
private struct sword
// Here define parts "objects" of type sword
unit Sword // This is the main unit (Sword)
unit Victim // Main target of the sword
group PushGroup // This are all effected units
real Height // This is height of blade
boolean CanMove // Tells can sword move
real Speed // Speed of sword
integer Level // Level of spell
real posX // Position x of sword
real posY // Position y of sword
real x // Direction of sword x
real y // Direction of sword y
real PushRange // The current pushed range
integer id // This is player id of sword
real Damage // Every sword damage
real Distance // Distance of sword
triggerx pt // This is pointer at main loop trigger
// with this pointer same variables are
// accesible in all structs of type sword
// pt = pointer trigger <trigger* pt>
// This are sword effects created at begining
unit DummyEffect1
unit DummyEffect2
effect Effect1
effect Effect2
real EffectDist
real Effect1Height
real Effect2Height
// Write sword methods
// Constructor method
static method create takes integer playerId, real x, real y, real z, real rad, real floorz, integer level, triggerx pt returns sword
local sword this = sword.allocate()
local real ex
local real ey
// Used to detect height of floor
call MoveLocation(LOC_Z, x, y)
// Init variables - parts of struct "object"
set this.Sword = CreateUnit(DD_p[playerId], SWORD_RAWCODE, x, y, rad*bj_RADTODEG)
set this.Victim = null
set this.PushGroup = CreateGroup()
set this.Height = z + floorz
set this.CanMove = false
set this.Speed = SWORDS_SPEED
set this.Level = level
set this.x = Cos(rad)
set this.y = Sin(rad)
set this.posX = x
set this.posY = y
set this.PushRange = MaxPushRange[level]
set this.id = playerId
set this.Damage = SingleSwordDamage[level]
set this.Distance = SWORDS_MAX_DISTANCE
set this.pt = pt
call UnitAddAbility(this.Sword, LOCUST_RAWCODE)
call UnitAddAbility(this.Sword, CROWFORM_RAWCODE)
call SetUnitUserData(this.Sword, integer(this))
call SetUnitFlyHeight(this.Sword, this.Height-GetLocationZ(LOC_Z), 0.)
// Load effects
// Effect One
set ex = x + SWORD_COLLISION*0.5 * Cos(rad-PI_HALF)
set ey = y + SWORD_COLLISION*0.5 * Sin(rad-PI_HALF)
set this.DummyEffect1 = CreateUnit(DD_p[playerId], DUMMY_RAWCODE, ex, ey, (rad-bj_PI)*bj_RADTODEG)
set this.Effect1 = AddSpecialEffectTarget(SWORD_EFFECT_MODEL, this.DummyEffect1, ATTACH_POINT)
set this.Effect1Height = z + floorz
call UnitAddAbility(this.DummyEffect1, LOCUST_RAWCODE)
call UnitAddAbility(this.DummyEffect1, CROWFORM_RAWCODE)
call MoveLocation(LOC_Z, ex, ey)
call SetUnitFlyHeight(this.DummyEffect1, this.Effect1Height-GetLocationZ(LOC_Z), 0.)
call SetUnitScale(this.DummyEffect1, .4, .4, .4)
// Effect Two
set ex = x + SWORD_COLLISION*0.5 * Cos(rad+PI_HALF)
set ey = y + SWORD_COLLISION*0.5 * Sin(rad+PI_HALF)
set this.DummyEffect2 = CreateUnit(DD_p[playerId], DUMMY_RAWCODE, ex, ey, (rad-bj_PI)*bj_RADTODEG)
set this.Effect2 = AddSpecialEffectTarget(SWORD_EFFECT_MODEL, this.DummyEffect2, ATTACH_POINT)
set this.Effect2Height = z + floorz
call UnitAddAbility(this.DummyEffect2, LOCUST_RAWCODE)
call UnitAddAbility(this.DummyEffect2, CROWFORM_RAWCODE)
call MoveLocation(LOC_Z, ex, ey)
call SetUnitFlyHeight(this.DummyEffect2, this.Effect2Height-GetLocationZ(LOC_Z), 0.)
call SetUnitScale(this.DummyEffect2, .4, .4, .4)
// For all effects
set this.EffectDist = 1.
return (this)
endmethod
// Picks any unit for this sword to push
method PickUnits takes nothing returns nothing
local unit e // Enum unit
set TempId = this.id
call GroupClear(TempGroup)
call GroupEnumUnitsInRange(TempGroup, this.posX, this.posY, SWORD_COLLISION, GroupFilter)
loop
set e = FirstOfGroup(TempGroup)
exitwhen (e == null)
call GroupRemoveUnit(TempGroup, e)
call UnitDamageTarget(this.Sword, e, PushCrashDamage[this.Level], true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_DEATH, WEAPON_TYPE_WHOKNOWS)
call DestroyEffect(AddSpecialEffectTarget(BLOOD_EFFECT, e, ATTACH_POINT))
if (not IsUnitType(e, UNIT_TYPE_DEAD)) then
call GroupAddUnit(this.PushGroup, e)
set UnitIsPushed[ArrayId(e)] = true
call SetUnitTimeScale(e, .1)
call SetUnitAnimation(e, "death")
set this.Speed = rmax(this.Speed - ON_ADD_SPEED_DECREASE, ON_ADD_SPEED_DECREASE)
call PauseUnit(e, true)
endif
endloop
endmethod
// Move sword method
method MoveSword takes nothing returns nothing
local real vel = TIMER_MIN_DELAY*this.Speed
local real newX = GetUnitX(this.Sword) + vel * this.x
local real newY = GetUnitY(this.Sword) + vel * this.y
call MoveLocation(LOC_Z, newX, newY)
call SetUnitX(this.Sword, newX)
call SetUnitY(this.Sword, newY)
call SetUnitFlyHeight(this.Sword, this.Height-GetLocationZ(LOC_Z), 0.)
set this.posX = newX
set this.posY = newY
set this.Distance = this.Distance - vel
endmethod
// This method moves sword effects
method MoveEffects takes nothing returns nothing
local real vel
local real rad
local real x
local real y
if (this.EffectDist == 0.) then
return
endif
// Set locals
set vel = TIMER_MIN_DELAY*SWORDS_SPEED
set rad = GetUnitFacing(this.DummyEffect1)*bj_DEGTORAD
set x = Cos(rad)
set y = Sin(rad)
// If user changes direction of movement to negative value
if (EFFECTS_MAX_DISTANCE > 0.) then
set this.EffectDist = this.EffectDist + vel
else
set this.EffectDist = this.EffectDist - vel
set vel = -vel
endif
// sqrt(x^2) = abs(x) | leave the x^2 is always positive
if (this.EffectDist*this.EffectDist >= EFFECTS_MAX_DISTANCE*EFFECTS_MAX_DISTANCE) then
set this.EffectDist = 0.
endif
// Effect 1
call SetUnitX(this.DummyEffect1, GetUnitX(this.DummyEffect1)+vel*x)
call SetUnitY(this.DummyEffect1, GetUnitY(this.DummyEffect1)+vel*y)
// Effect 2
call SetUnitX(this.DummyEffect2, GetUnitX(this.DummyEffect2)+vel*x)
call SetUnitY(this.DummyEffect2, GetUnitY(this.DummyEffect2)+vel*y)
endmethod
// This method will destroy all sowrds effects
method DestroyEffects takes nothing returns nothing
if (this.Effect1 == null) then
return
endif
call DestroyEffect(this.Effect1)
call DestroyEffect(this.Effect2)
call RemoveUnit(this.DummyEffect1)
call RemoveUnit(this.DummyEffect2)
// Must null this struct data as we know they are globals
set this.Effect1 = null
set this.Effect2 = null
set this.DummyEffect1 = null
set this.DummyEffect2 = null
set this.Effect1Height = 0.
set this.Effect2Height = 0.
set this.EffectDist = 0.
endmethod
// Destructor method
method onDestroy takes nothing returns nothing
// onDestroy locals
local sound onDeath
local real cx
local real cy
local real d
local integer i = 0
// Now fade sword out
call SetUnitUserData(this.Sword, float.create(100.))
call DDStoreValue(this.Sword, float.create(this.Height))
call GroupAddUnit(this.pt.g[1], this.Sword)
call DestroyGroup(this.PushGroup)
call this.DestroyEffects()
// Load onDeath sound
set onDeath = CreateSound(SWORD_DISAPPERE_PATH, false, false, false, 12700, 12700, "")
loop
exitwhen (i >= bj_MAX_PLAYERS)
if (GetLocalPlayer() == DD_p[i]) then
set cx = GetCameraTargetPositionX()
set cy = GetCameraTargetPositionY()
set d = SquareRoot((cx-this.posX)*(cx-this.posX) + (cy-this.posY)*(cy-this.posY))
call SetSoundVolume(onDeath, R2I(SWORD_DISAPPERE_PATH_VOL-d*(SWORD_DISAPPERE_PATH_VOL/SOUND_MAX_DISTANCE)))
endif
set i = i + 1
endloop
call StartSound(onDeath)
call KillSoundWhenDone(onDeath)
set onDeath = null
// Must null all variables as they are globals
set this.Sword = null
set this.Victim = null
set this.PushGroup = null
set this.Height = 0.
set this.CanMove = false
set this.Speed = 0.
set this.Distance = 0.
set this.Level = 0
set this.Damage = 0.
set this.id = 0
set this.PushRange = 0.
set this.x = 0.
set this.y = 0.
set this.posX = 0.
set this.posY = 0.
set this.pt = NULL
endmethod
endstruct
//*********************************************************************************
// --- Other Spell Functions ---
//*********************************************************************************
// Killing sound timed must be called because there is a limit of 2^2 sounds of
// same type playing at same time. So we need this timed kill, dont worry its fast
// Such slow timers will not cause any lag
private function KillSound takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = ArrayId(t)
call StopSound(KillSoundVar[id], true, false)
call PauseTimer(t)
call DestroyTimer(t)
set KillSoundVar[id] = null
set t = null
endfunction
private function KillSoundTimed takes sound whichSound, real delay returns nothing
local timer t = CreateTimer()
set KillSoundVar[ArrayId(t)] = whichSound
call TimerStart(t, delay, false, function KillSound)
set t = null
endfunction
// Functions sets single sound volume for all players to specific values
private function SetSoundVolumeByDistance takes sound s, real max_volume, real x, real y returns nothing
local integer i = 0
local real cx
local real cy
local real d
loop
exitwhen (i >= bj_MAX_PLAYERS)
if (GetLocalPlayer() == DD_p[i]) then
// Calculate distance
set cx = GetCameraTargetPositionX()
set cy = GetCameraTargetPositionY()
set d = SquareRoot((cx-x)*(cx-x) + (cy-y)*(cy-y))
// For each player set sound volume depending on distance
call SetSoundVolume(s, R2I((max_volume-d*(max_volume/SOUND_MAX_DISTANCE))*1.27))
endif
set i = i + 1
endloop
endfunction
// This function will return nearest unit (victim / target)
private function GetNearestUnit takes real x, real y, real radius, integer id returns unit
local unit u
local unit r
local real md
local real d
local real ux
local real uy
set TempId = id
call GroupClear(TempGroup)
call GroupEnumUnitsInRange(TempGroup, x, y, radius, GroupFilter)
set md = -1.
set r = null
loop
set u = FirstOfGroup(TempGroup)
exitwhen (u == null)
set ux = GetUnitX(u)
set uy = GetUnitY(u)
set d = SquareRoot((ux-x)*(ux-x) + (uy-y)*(uy-y))
if (md == -1.) then
set md = d
set r = u
endif
if (md > d) then
set md = d
set r = u
endif
call GroupRemoveUnit(TempGroup, u)
endloop
return (r)
endfunction
//----------------------------------------------------------------------------------
//|*********************************************************************************|
//|--- Spell Main Functions ---|
//|*********************************************************************************|
//----------------------------------------------------------------------------------
// This is main condition of the spell
private constant function DarkTorture_Condition takes nothing returns boolean
return GetSpellAbilityId() == DARK_TORTURE_RAWCODE
endfunction
// On single sword moves all units
private function MoveEnumSwordUnits takes nothing returns nothing
// This group locals
local sword s = sword(TempSword)
local unit u = GetEnumUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
// For main victim we decrease max push range
if (u == s.Victim) then
set s.PushRange = s.PushRange - (s.Speed*TIMER_MIN_DELAY)
endif
// Move enum units (all of them)
if (s.PushRange > 0.) then
call SetUnitX(u, x+((s.Speed*TIMER_MIN_DELAY)*s.x))
call SetUnitY(u, y+((s.Speed*TIMER_MIN_DELAY)*s.y))
// Load movement effects
if (QMod(GetTriggerExecCount(GetTriggeringTrigger()), 10) == 0) then
// On Ground Dust
if (IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)) then
call DestroyEffect(AddSpecialEffect(DUST_EFFECT, x, y))
else // On Ground Water
call DestroyEffect(AddSpecialEffect(WATER_EFFECT, x, y))
endif
endif
else
// Release all effected units when run out of range
call SetUnitAnimation(u, "stand")
call SetUnitTimeScale(u, 1.)
call PauseUnit(u, false)
set UnitIsPushed[ArrayId(u)] = false
// Slow down targets
set bj_lastCreatedUnit = CreateUnit(DD_p[s.id], DUMMY_RAWCODE, x, y, 0.)
call UnitAddAbility(bj_lastCreatedUnit, SLOW_RAWCODE)
call UnitAddAbility(bj_lastCreatedUnit, LOCUST_RAWCODE)
call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', 0.25)
call IssueTargetOrder(bj_lastCreatedUnit, "slow", u)
endif
if (not s.CanMove) then
// Floor is not pathable
// blow units away by dealing damage
call UnitDamageTarget(s.Sword, u, PushCrashDamage[s.Level], true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_DEATH, WEAPON_TYPE_WHOKNOWS)
call DestroyEffect(AddSpecialEffectTarget(BLOOD_EFFECT, u, ATTACH_POINT))
endif
// Null locals
set u = null
set s = NULL
endfunction
// ForGroup action function (MAIN MOVE ALL SWORDS IN SINGLE CAST)
// Picks all swords
private function MoveEnumUnits takes nothing returns nothing
// Load movement locals
local sword s = sword(GetUnitUserData(GetEnumUnit()))
local triggerx tx = triggerx.triggeringx()
local sound snd
local real d
// Effects movement
if (s.EffectDist != 0.) then
call s.MoveEffects()
if (s.EffectDist == 0.) then
call s.DestroyEffects()
endif
endif
// Sword move
if (s.CanMove) then
call s.MoveSword()
// When sword moves we try to find target or group of targets
if (s.Victim == null) then
set s.Victim = GetNearestUnit(s.posX, s.posY, SWORD_COLLISION, s.id)
if (s.Victim != null) then
// Deal damage
set d = rmin(SingleSwordDamage[s.Level], tx.dmg)
if (d > 0.) then
// Damage target here
call UnitDamageTarget(s.Sword, s.Victim, d, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_DEATH, WEAPON_TYPE_WHOKNOWS)
// Decrease damage here
set tx.dmg = rmax(tx.dmg - d, 0.)
// Load hit sound
set snd = CreateSound(SWORD_HIT_PATH, false, false, false, 12700, 12700, "")
call SetSoundVolumeByDistance(snd, SWORD_HIT_PATH_VOL, s.posX, s.posY)
call StartSound(snd)
call KillSoundWhenDone(snd)
set snd = null
endif
// Dont do anything if target is dead
if (IsUnitType(s.Victim, UNIT_TYPE_DEAD)) then
set s.Victim = null
return
endif
// Set new target (add)
set UnitIsPushed[ArrayId(s.Victim)] = true
call GroupAddUnit(s.PushGroup, s.Victim)
call PauseUnit(s.Victim, true)
call DestroyEffect(AddSpecialEffectTarget(BLOOD_EFFECT, s.Victim, ATTACH_POINT))
set s.Speed = rmax(s.Speed-ON_ADD_SPEED_DECREASE, ON_ADD_SPEED_DECREASE)
call SetUnitTimeScale(s.Victim, 0.1)
call SetUnitAnimation(s.Victim, "death")
endif
else
// Add any unit who comes near
call s.PickUnits()
endif
endif
// Now pick all units on sword and move them with sword
set TempSword = integer(s)
if (not IsTerrainPathable(s.posX+SWORD_COLLISION*s.x, s.posY+SWORD_COLLISION*s.y, PATHING_TYPE_WALKABILITY)) then
call ForGroup(s.PushGroup, function MoveEnumSwordUnits)
else
// Not pathable (stop movement)
set s.PushRange = 0.
set s.CanMove = false
call ForGroup(s.PushGroup, function MoveEnumSwordUnits)
endif
// This sword is done no more range on pushing has left
// This one tells that pushed victim with other units
// reached its last position change
if (s.PushRange <= 0.) then
// When s.PushRange us zero or less this will reset units
call ForGroup(s.PushGroup, function MoveEnumSwordUnits)
// Slow down victim (add killlllllling slow)
set bj_lastCreatedUnit = CreateUnit(DD_p[s.id], DUMMY_RAWCODE, GetUnitX(s.Victim), GetUnitY(s.Victim), 0.)
call UnitAddAbility(bj_lastCreatedUnit, SLOW_RAWCODE)
call UnitAddAbility(bj_lastCreatedUnit, LOCUST_RAWCODE)
call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', 0.25)
call IssueTargetOrder(bj_lastCreatedUnit, "slow", s.Victim)
// Destroy
call s.destroy()
call GroupRemoveUnit(tx.g[0], GetEnumUnit())
return
endif
// The sword reach its end anyway did he pick any unit
// or not or if victim did not pass push range.
// This will be end of spell of this sword better to say
if (s.Distance <= 0.) then
// When this is set to zero next for group action will unpause
// all units inside group
set s.PushRange = 0.
call ForGroup(s.PushGroup, function MoveEnumSwordUnits)
// Remove sword from this group
call GroupRemoveUnit(tx.g[0], s.Sword)
// Slow down victim (add killlllllling slow)
set bj_lastCreatedUnit = CreateUnit(DD_p[s.id], DUMMY_RAWCODE, GetUnitX(s.Victim), GetUnitY(s.Victim), 0.)
call UnitAddAbility(bj_lastCreatedUnit, SLOW_RAWCODE)
call UnitAddAbility(bj_lastCreatedUnit, LOCUST_RAWCODE)
call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', 0.25)
call IssueTargetOrder(bj_lastCreatedUnit, "slow", s.Victim)
call s.destroy()
endif
endfunction
// This is fade in sword function (ForGroup action)
private function SwordFadeIn takes nothing returns nothing
local unit u = GetEnumUnit()
local float trans = float( GetUnitUserData(u) )
local float height = float( DDGetValue(u) )
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real rad = bj_DEGTORAD * GetUnitFacing(u)
// Fade in
set trans.val = trans.val - 0.66667
// End trans here
if (trans.val <= 0.) then
call trans.delete()
call height.delete()
call DDFlushValue(u)
call GroupRemoveUnit(triggerx.triggeringx().g[1], u)
call RemoveUnit(u)
set u = null
return
endif
// Slow fade in and movement
set x = x + Cos(rad)
set y = y + Sin(rad)
call SetUnitX(u, x)
call SetUnitY(u, y)
call MoveLocation(LOC_Z, x, y)
call SetUnitFlyHeight(u, height.val-GetLocationZ(LOC_Z), 0.)
call SetUnitVertexColor(u, 255, 255, 255, R2I(trans.val*2.55))
set u = null
endfunction
//*********************************************************************
// This is main movement function [Trigger Action]
//*********************************************************************
private function DarkTortureMovement takes nothing returns nothing
// Load trigger locals
local triggerx tx = triggerx.triggeringx()
// Now move all units and all other stuff
if (FirstOfGroup(tx.g[0]) != null) then
call ForGroup(tx.g[0], function MoveEnumUnits)
elseif (tx.started and FirstOfGroup(tx.g[1]) == null) then
// Here spell ends (Ends 100%)
call tx.destroy()
// Clearing timer
set TGCount = TGCount - 1
if (TGCount == 0) then
call PauseTimer(DTTimer)
call DestroyTimer(DTTimer)
set DTTimer = null
endif
endif
// Fade sword out
if (FirstOfGroup(tx.g[1]) != null) then
call ForGroup(tx.g[1], function SwordFadeIn)
endif
endfunction
// Trigger action shoot blades at random time
private function ShootBlades takes nothing returns nothing
// Define locals
local triggerx tx = triggerx.triggeringx()
local integer ex = GetTriggerExecCount(tx.t)
local integer sw_id = GetRandomInt(0, MaxSwordCount[tx.lvl]-ex-tx.faliure)
local sound snd = null
local sword s = sword(tx.tx.sw[sw_id])
// *** this is omg ***
if (tx.faliure == MaxSwordCount[tx.lvl]) then
set tx.tx.started = true
call tx.destroy()
return
endif
debug call BJDebugMsg(I2S(s))
// Sort swords for next exec (random one is replaced with last one)
set tx.tx.sw[sw_id] = tx.tx.sw[MaxSwordCount[s.Level]-ex-tx.faliure]
// Random sleep
//call Sleep(GetRandomReal(0.1, 0.4))
//call SetRandomSeed(QMod(GetRandomInt(1000, 100000), GetRandomInt(1, 334)))
// Allow sword to move
set s.CanMove = true
// Play shoot sound
set snd = CreateSound(SWORD_SOUND_START_PATH, false, false, false, 12700, 12700, "")
call SetSoundVolumeByDistance(snd, SWORD_SOUND_START_PATH_VOL, s.posX, s.posY)
call StartSound(snd)
call KillSoundTimed(snd, 0.325)
// Above we dont want to play full sound
// Its because I dont have that good sounds and to find
// free ones that are good is hard
// Is last exec then end
if (ex == MaxSwordCount[tx.lvl]-tx.faliure) then
// Set spell has started
set tx.tx.started = true
// Blow triggerx
call tx.destroy()
//else
// Not last exec again
//call tx.Execute()
endif
// Now last one gets cleared
set tx.tx.sw[MaxSwordCount[s.Level]-ex-tx.faliure] = NULL
// Null locals
set s = NULL
endfunction
// This is main function for creating swords
private function CreateSwords takes nothing returns nothing
// Load all locals
local timerx tmx = timerx.expiredx()
local sword s = NULL
local real x = tmx.x
local real y = tmx.y
local real radx = (tmx.rad - PI_HALF)
local sound snd = null
local real d = 0.
local real cx = 0.
local real cy = 0.
local integer i = 0
local real sc = I2R(MaxSwordCount[tmx.lvl])
local triggerx tx = NULL
// Here we end the spell
if (tmx.exec >= MaxSwordCount[tmx.lvl]) then
// Start moving blades by random time
set tx = triggerx.new(function ShootBlades, tmx.lvl, tmx.tx, tmx.faliure)
call tx.RegisterTimedExec(SWORD_FIRE_DELAY)
call tx.Execute()
// Destroy our timerx
call tmx.destroy()
return
endif
// Create sword
set radx = radx + (bj_PI/(sc-1))*tmx.exec
//set radx = radx + (bj_PI/(sc*sc))*tmx.exec
set x = x + (SwordLineWidth[tmx.lvl]*0.5 * Cos(radx)) - (SWORD_LOAD_OFFSET*Cos(tmx.rad-PI))
set y = y + (SwordLineWidth[tmx.lvl]*0.5 * Sin(radx)) - (SWORD_LOAD_OFFSET*Sin(tmx.rad-PI))
// Here we call for constructor
set s = sword.create(tmx.pid, x, y, SWORD_HEIGHT, tmx.rad, tmx.fz, tmx.lvl, tmx.tx)
// Here we make sure that effects are destroyed
if (IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)) then
// This as well destroys all effects
call s.destroy()
set tmx.faliure = (tmx.faliure + 1)
else
// Limit of 16 swords --- Here we store all created swords
// data[exec][tgid] = whichSword
set tmx.tx.sw[R2I(tmx.exec)-tmx.faliure] = integer(s)
call GroupAddUnit(tmx.tx.g[0], s.Sword)
endif
// Load sword create sound
set snd = CreateSound(SWORD_CREATE_SOUND_PATH, false, false, false, 12700, 12700, "")
call SetSoundVolumeByDistance(snd, SWORD_CREATE_SOUND_PATH_VOL, x, y)
call StartSound(snd)
// Kill sound (exec starts at zero)
if (tmx.exec + 5. < MaxSwordCount[tmx.lvl]) then
// Sword creation delay times 2^2
// This is called n_swords - 4 times
call KillSoundTimed(snd, (SWORD_CREATION_DELAY*4.)-0.02)
else
// Last 4 loops do just normal sound kill
call KillSoundWhenDone(snd)
endif
set snd = null
// Exec counter
set tmx.exec = tmx.exec + 1.
endfunction
///////////////////////////////////////////////////////////////////////
// *This is main action of the spell* //
///////////////////////////////////////////////////////////////////////
private function DarkTorture_Action takes nothing returns nothing
// Load all main locals
local unit u = GetTriggerUnit()
local real ux = GetUnitX(u)
local real uy = GetUnitY(u)
local location l = GetSpellTargetLoc()
local real x = GetLocationX(l)
local real y = GetLocationY(l)
local integer lvl = GetUnitAbilityLevel(u, DARK_TORTURE_RAWCODE)
local real floorz = 0.
local triggerx tx = NULL
local timerx tmx = NULL
// This should never be executed
if (lvl > Levels) then
call BJDebugMsg("|cffff0000The spell Dark Torture canot be executed because spell level is greater then defined!|r")
call DestroyTrigger(GetTriggeringTrigger())
return
endif
// Get floorz
call MoveLocation(LOC_Z, ux, uy)
set floorz = GetLocationZ(LOC_Z)
// Start move timer and load spell trigger and other objects
if (DTTimer == null) then
set DTTimer = CreateTimer()
call TimerStart(DTTimer, TIMER_MIN_DELAY, true, null)
endif
// Trigger counter
set TGCount = TGCount + 1
// This is main timer executable triggerx
// -------------------------------------------------------------------------------
// *** Here we create new trigger, store this func as triggeraction, load two groups
// *** store damage and boolean did spell started
// *** Group 1 is swords group and Group 2 is fade group
set tx = triggerx.create(function DarkTortureMovement, SwordsMaxDamage[lvl])
call TriggerRegisterTimerExpireEvent(tx.t, DTTimer)
// Here we will load swords, using timer as it is precious.
// Dont worry timer of 0.1 seconds or more exec per second do not cause any lag
// Even if there are 20 timer at same time.
set tmx = timerx.create(ux, uy, Atan2(y-uy, x-ux), lvl, GetPlayerId(GetOwningPlayer(u)), floorz, tx)
call TimerStart(tmx.t, SWORD_CREATION_DELAY, true, function CreateSwords)
// Null locals
call RemoveLocation(l)
set l = null
set u = null
endfunction
//===========================================================================
// Filter for picking push units
private function FuncGroupFilter takes nothing returns boolean
local unit f = GetFilterUnit()
local boolean b = false
set b = (not IsUnitType(f, UNIT_TYPE_FLYING) and not IsUnitType(f, UNIT_TYPE_STRUCTURE) and not IsUnitType(f, UNIT_TYPE_DEAD) and not IsUnitType(f, UNIT_TYPE_MECHANICAL) and not (UnitIsPushed[ArrayId(f)]) and IsUnitEnemy(f, DD_p[TempId]))
set f = null
return (b)
endfunction
private function DarkTorture_Main takes nothing returns nothing
// Make this trigger private
local trigger t = CreateTrigger()
local integer i = 0
// First setup spell
call DarkTorture_Setup()
loop
exitwhen (i >= Levels)
if (MaxSwordCount[i] > 16) then
// This spell will end here
call DestroyTrigger(t)
return
endif
set i = i + 1
endloop
// Register spell cast event
// Add condition and action
call TriggerRegisterAnyUnitEvent(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function DarkTorture_Condition))
call TriggerAddAction(t, function DarkTorture_Action)
// Preload files
call Preload(SWORD_EFFECT_MODEL)
call Preload(BLOOD_EFFECT)
call Preload(DUST_EFFECT)
call Preload(WATER_EFFECT)
//call Preload(SWORD_SOUND_START_PATH)
//call Preload(SWORD_CREATE_SOUND_PATH)
//call Preload(SWORD_HIT_PATH)
//call Preload(SWORD_DISAPPERE_PATH)
call GenericSoundDD(SWORD_SOUND_START_PATH, 0., 0., 0., 1.)
call GenericSoundDD(SWORD_CREATE_SOUND_PATH, 0., 0., 0., 1.)
call GenericSoundDD(SWORD_HIT_PATH, 0., 0., 0., 1.)
call GenericSoundDD(SWORD_DISAPPERE_PATH, 0., 0., 0., 1.)
call Preload("war3mapImported\\Frostmourne.mdx")
call Preload("Objects\\SpawnQModels\\Human\\HumanBlood\\HeroBloodElfBlood.mdx")
call PreloadStart()
// Group filter define
set GroupFilter = Filter(function FuncGroupFilter)
// Null "init" locals
set t = null
endfunction
endlibrary
//TESH.scrollpos=253
//TESH.alwaysfold=0
//**************************************************************************************
// Spell: Frost Eruption
// Author: Dark Dragon
// Date: 17 February 2009
//
// Other: Spell made for fun testing vJass
//
//
// Installation:
//
// - 1. Make sure you have open the JassNewGen Pack editor not normal one and make sure you have last version
// - 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
// QModels: 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 dummy unit {Dummy (DD Universal Pack)}
// 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:
// Dummy: 'dumy'
// Frost Eruption: 'Feru'
// Air Freeze (Dummy): 'Afre'
// Frost Eruption {Buff}: 'Bfer'
// - 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. And of course copy this trigger and DDUniversal Pack to your map.
//
//**************************************************************************************
// Frost Eruption main library
library FrostEruption initializer FrostEruption_Main requires DDUniversalPack
//**************************************
// --- Load spell constant globals ---
//**************************************
globals
// The raw code of the spell Frost Eruption
// Default: 'Feru'
private constant integer FE_RAWCODE = 'Feru'
// The raw code of dummy (change it in DDUniversal Pack trigger)
// Default: 'dumy'
private constant integer DUM_RAWCODE = DD_DUMMY_CODE
// With this ability air units are slowed
// Default: 'Afre'
private constant integer AIR_FREEZE_RAWCODE = 'Afre'
// This is orb QModel path
// Default: "Spells\\FrostEruption\\FrozenOrb.mdl"
private constant string ORB_QModEL_PATH = "Spells\\FrostEruption\\FrozenOrb.mdx"
// Main frost eruption effect
// Default: "Spells\\FrostEruption\\BlizzardEruption.mdl"
private constant string ORB_ERUPTION_EFFECT = "Spells\\FrostEruption\\BlizzardEruption.mdx"
// Frost Eruptions extra explosion effect
// Default: "Spells\\FrostEruption\\FreezingRing.mdl"
private constant string ORB_EXPLOSION_EFFECT = "Spells\\FrostEruption\\FreezingRing.mdx"
// Ground units freezed QModel path
// Default: "Abilities\\Spells\\Undead\\FreezingBreath\\FreezingBreathTargetArt.mdl"
private constant string FREEZE_EFFECT = "Abilities\\Spells\\Undead\\FreezingBreath\\FreezingBreathTargetArt.mdx"
// This is orb's trail effect
// Default: "Spells\\FrostEruption\\Frost2.mdx"
private constant string TRAIL_QModEL_PATH = "Spells\\FrostEruption\\Frost2.mdx"
// Sound file on load (on spell cast)
// Default: "Custom\\Spells\\Frost Eruption\\fe_start.mp3"
private constant string SPELL_START_SOUND = "Custom\\Spells\\Frost Eruption\\fe_start.mp3"
private constant real SPELL_START_SOUND_VOL = 75.
// Sound file's on explosion
// Default: "Custom\\Spells\\Frost Eruption\\fe_explode1.mp3"
private constant string SPELL_EXPLODE_SOUND_1 = "Custom\\Spells\\Frost Eruption\\fe_explode1.mp3"
private constant real SPELL_EXPLODE_SOUND_VOL = 75. // Both 1 and 2 sounds volume
// Default: "Custom\\Spells\\Frost Eruption\\fe_explode2.mp3"
private constant string SPELL_EXPLODE_SOUND_2 = "Custom\\Spells\\Frost Eruption\\fe_explode2.mp3"
// This is scale / size of main orb
// Default: 1.32
private constant real ORB_SCALE = 1.32
// Terrain deformation depth
// Default: 128.
private constant real EXPLOSION_DEAPTH = 128.
// Terrain deformatiob timeout
// Default: 0.35
private constant real TERRAIN_DEFORMATION_DURATION = 0.35
// Cameras power magnitude
// Default: 2.25
private constant real CAMERA_MAGNITUDE = 2.25
// Camera noise duration
// Default: 1.85
private constant real CAMERA_NOISE_DURATION = 1.85
// How much time will orb need until reaching target point
// Default: 1.5
private constant real ORB_DURATION = 1.5
// Do not edit this variables here edit then below
private integer Level = 0
private real array FE_Radius
private real array FE_Damage
private real array FE_MaxDamage
private real array FE_Duration
endglobals
// Edit this to match your level data
private function SetupFrostEruption takes nothing returns nothing
// Level data:
// Max spell level:
// Default: 3
set Level = 3
// Spells damage:
// Default: 130 / 215 / 290
set FE_Damage[1] = 130.
set FE_Damage[2] = 215.
set FE_Damage[3] = 290.
// Spells radius:
// Default: 500 / 515 / 540
set FE_Radius[1] = 500.
set FE_Radius[2] = 515.
set FE_Radius[3] = 540.
// Spells max damage:
// Default: 700 / 1300 / 1950
set FE_MaxDamage[1] = 700.
set FE_MaxDamage[2] = 1300.
set FE_MaxDamage[3] = 1950.
// Spells ground units freeze duration:
// Default: 4 / 4.5 / 5.25
set FE_Duration[1] = 4.
set FE_Duration[2] = 4.5
set FE_Duration[3] = 5.25
endfunction
// ================================================================================
// ****** ******
// Do not edit below if you don't know jass
// ****** ******
// ================================================================================
//***********************************
// <<< Uneditable spells globals >>>
//***********************************
globals
// --- Constants ---
private constant string ATTACH_POINT = "origin"
private constant real TIMER_MOVE_DELAY = 0.025
private constant real TRAIL_DISTANCE = 80.
private timer Timer = null
private integer array Triggerx[64]
private integer ExecId = 0
private boolean array UnitFrozen[8191]
private effect array FreezeEffect[8191]
endglobals
//////////////////////////////////////////////////////////////////////////////
// Spells Main Functions //
//////////////////////////////////////////////////////////////////////////////
// Creates Dummy
private function CreateDummy takes player p, real x, real y returns unit
local unit dummy = CreateUnit(p, DUM_RAWCODE, x, y, 0.)
call UnitAddAbility(dummy, 'Aloc')
return dummy
endfunction
// Calculates x^2
private constant function Pow2 takes real x returns real
return (x*x)
endfunction
// Plays sound for all players
private function PlayGenericSound takes string file, real max_volume, real x, real y, real max_distance returns nothing
local integer i = 0
local sound s = CreateSound(file, false, false, false, 12700, 12700, "")
local real d
local real cx
local real cy
// Set sound volume for players
loop
exitwhen (i >= bj_MAX_PLAYERS)
if (GetLocalPlayer() == i:DD_p) then
set cx = GetCameraTargetPositionX()
set cy = GetCameraTargetPositionY()
set d = SquareRoot(hypot(cx-x, cy-y))
call SetSoundVolume(s, R2I( (max_volume-(d*(max_volume/max_distance)))*1.27 ))
endif
set i = i + 1
endloop
// Start sound
call StartSound(s)
call KillSoundWhenDone(s)
set s = null
endfunction
// Gives limits to given x number
private constant function RLimit takes real x, real min, real max returns real
if (x < min) then
set x = min
elseif (x > max) then
set x = max
endif
return x
endfunction
// Will create camera noise for x seconds
private function ResetNoise takes nothing returns nothing
if (ExecId == 0) then
call CameraSetTargetNoise(0., 0.)
call CameraSetSourceNoise(0., 0.)
endif
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
endfunction
private function CameraSetEQNoise takes real magnitude, real timeout returns nothing
local real richter = RLimit(magnitude, 2., 5.)
call CameraSetTargetNoiseEx(magnitude*2., magnitude*Pow(10., richter), true)
call CameraSetSourceNoiseEx(magnitude*2., magnitude*Pow(10., richter), true)
call TimerStart(CreateTimer(), timeout, false, function ResetNoise)
endfunction
// Freeze ground unit
private function FreezeUnit takes unit whichUnit, boolean flag returns nothing
if (flag) then
debug call SetUnitMoveSpeed(whichUnit, 0.)
call SetUnitVertexColor(whichUnit, 100, 100, 255, 255)
call PauseUnit(whichUnit, true)
set FreezeEffect[ArrayId(whichUnit)] = AddSpecialEffectTarget(FREEZE_EFFECT, whichUnit, ATTACH_POINT)
set ArrayId(whichUnit):UnitFrozen = TRUE
elseif (ArrayId(whichUnit):UnitFrozen) then
debug call SetUnitMoveSpeed(whichUnit, GetUnitDefaultMoveSpeed(whichUnit))
call SetUnitVertexColor(whichUnit, 255, 255, 255, 255)
call PauseUnit(whichUnit, false)
call DestroyEffect(FreezeEffect[ArrayId(whichUnit)])
set FreezeEffect[ArrayId(whichUnit)] = null
set ArrayId(whichUnit):UnitFrozen = FALSE
if (GetPlayerController(GetOwningPlayer(whichUnit)) != MAP_CONTROL_COMPUTER) then
call IssueImmediateOrder(whichUnit, "stop")
endif
endif
endfunction
//********************************************************************
// Main Spell Damage Action Functions
//********************************************************************
//=======================================================================
// Damage data struct
private struct damagedata
public player p
public groupex g
public real dmg
public integer lvl
public unit dmy
public real x
public real y
public unit caster
static method create takes player owner, integer level, unit dmy, unit caster returns damagedata
local damagedata this = damagedata.allocate()
set this.p = owner
set this.lvl = level
set this.dmy = dmy
set this.caster = caster
set this.g = CreateGroupEx()
return (this)
endmethod
method onDestroy takes nothing returns nothing
call RemoveUnit(this.dmy)
call DestroyGroupEx(this.g)
set this.p = null
set this.g = 0
set this.dmy = null
set this.caster = null
endmethod
endstruct
globals
private damagedata TempDamageData = 0
endglobals
//=======================================================================
// All units unfreeze
private function UnfreezeUnitsEnum takes nothing returns nothing
call FreezeUnit(GetEnumUnitEx(), false)
endfunction
// Timer function unfreeze units
private function UnfreezeUnits takes nothing returns nothing
local timer t = GetExpiredTimer()
local damagedata dd = DDGetValue(t)
call ForGroupEx(dd.g, function UnfreezeUnitsEnum)
call dd.destroy()
call PauseTimer(t)
call DestroyTimer(t)
set t = null
endfunction
// Main damage filter
private function FE_DamageFilter takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(), TempDamageData.p) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) and GetWidgetLife(GetFilterUnit()) > 0.405 and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE)
endfunction
// Main damage unit function
private function DamageEnum takes nothing returns nothing
// Locals
local unit u = GetEnumUnitEx()
local unit d
local damagedata dd = TempDamageData
// Deal damage
call UnitDamageTarget(dd.dmy, u, dd.dmg, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_COLD, WEAPON_TYPE_WHOKNOWS)
// Flying units slowed
if (IsUnitType(u, UNIT_TYPE_FLYING) == true) then
call GroupExRemoveUnit(dd.g, u)
set d = CreateDummy(dd.p, GetUnitX(u), GetUnitY(u))
call UnitAddAbility(d, AIR_FREEZE_RAWCODE)
call SetUnitAbilityLevel(d, AIR_FREEZE_RAWCODE, dd.lvl)
call IssueTargetOrder(d, "frostnova", u)
call UnitApplyTimedLife(d, 'BTLF', 1.)
if (GetPlayerController(GetOwningPlayer(u)) != MAP_CONTROL_USER and not IsUnitType(u, UNIT_TYPE_PEON)) then
call IssuePointOrder(u, "attack", GetUnitX(dd.caster), GetUnitY(dd.caster))
debug call BJDebugMsg("exec")
endif
set d = null
else
if ( GetWidgetLife(u) > 0.405 ) then
call FreezeUnit(u, true)
else
call GroupExRemoveUnit(dd.g, u)
endif
endif
set u = null
endfunction
// Damages any enemy unit
private function DamageGroupRadius takes real x, real y returns nothing
// Load locals
local boolexpr bx = Filter(function FE_DamageFilter)
local damagedata dd = TempDamageData
local real n
local timer t = CreateTimer()
// Vars set
set dd.dmg = dd.lvl:FE_Damage
// Insert units
call GroupExEnumUnitsInRange(dd.g, x, y, dd.lvl:FE_Radius, bx)
// Damage setup
set n = I2R(CountUnitsInGroupEx(dd.g))
if (dd.dmg*n > dd.lvl:FE_MaxDamage) then
set dd.dmg = dd.lvl:FE_MaxDamage/n
endif
// Do action for all units
call ForGroupEx(dd.g, function DamageEnum)
// Timer load and unfreeze units after...
set TempDamageData = damagedata(0)
call DDStoreValue(t, integer(dd))
call TimerStart(t, dd.lvl:FE_Duration, false, function UnfreezeUnits)
// Destroy objects (clear leaks and nulls)
call DestroyBoolExpr(bx)
set bx = null
set t = null
endfunction
// Damage group after time
private function DmgGrpRad takes nothing returns nothing
local timer t = GetExpiredTimer()
set TempDamageData = DDGetValue(t)
call DamageGroupRadius(TempDamageData.x, TempDamageData.y)
call DDFlushValue(t)
call PauseTimer(t)
call DestroyTimer(t)
set t = null
endfunction
private function DamageGroupRadiusTimed takes real x, real y, real time returns nothing
local timer t = CreateTimer()
set TempDamageData.x = x
set TempDamageData.y = y
call DDStoreValue(t, integer(TempDamageData))
call TimerStart(t, time, false, function DmgGrpRad)
set t = null
endfunction
//===========================================================
//=== ===
// Struct Triggerx
//=== ===
//===========================================================
globals
private integer array StoredTriggerx[8191]
endglobals
private struct triggerx
public trigger tg
private triggeraction ta
private integer val
// Constructor
static method create takes code cond, code act returns triggerx
local triggerx this = triggerx.allocate()
set this.val = 0
set this.tg = CreateTrigger()
if (cond != null) then
call TriggerAddCondition(this.tg, Condition(cond))
endif
if (act != null) then
set this.ta = TriggerAddAction(this.tg, act)
endif
set QMod(GetHandleId(this.tg), JASS_MAX_ARRAY_SIZE):StoredTriggerx = (this)
return (this)
endmethod
// Sets new custom value
public method SetValue takes integer val returns nothing
set this.val = val
endmethod
// Gets custom value
public method GetValue takes nothing returns integer
return (this.val)
endmethod
// Destructor
method onDestroy takes nothing returns nothing
if (this.ta != null) then
call TriggerRemoveAction(this.tg, this.ta)
endif
set QMod(GetHandleId(this.tg), JASS_MAX_ARRAY_SIZE):StoredTriggerx = 0
call DestroyTrigger(this.tg)
set this.ta = null
set this.tg = null
endmethod
endstruct
// Create new triggerx
function CreateTriggerx takes code cond, code act returns triggerx
return triggerx.create(cond, act)
endfunction
// Register timer event
function TriggerxRegisterTimerEvent takes triggerx whichTriggerx returns event
return TriggerRegisterTimerExpireEvent(whichTriggerx.tg, Timer)
endfunction
// Destroy triggerx
function DestroyTriggerx takes triggerx whichTriggerx returns nothing
call whichTriggerx.destroy()
endfunction
// New Value
function SetTriggerxValue takes triggerx tx, integer newValue returns nothing
call tx.SetValue(newValue)
endfunction
// Get Value
function GetTriggerxValue takes triggerx tx returns integer
return tx.GetValue()
endfunction
// Triggering triggerx
private function GetTriggeringTriggerx takes nothing returns triggerx
return QMod(GetHandleId(GetTriggeringTrigger()), JASS_MAX_ARRAY_SIZE):StoredTriggerx
endfunction
//===========================================================
//=== ===
// Struct Parabola
//=== ===
//===========================================================
private struct parabola
private real a
private real c
private real md
// b is zero
// Loads new parabola
static method create takes real max_distance, real max_height returns parabola
local parabola this = parabola.allocate()
set this.a = -max_height/Pow2(0.5*max_distance)
set this.c = max_height
set this.md = max_distance
return (this)
endmethod
// Calculates parabolic movement
private method Parabola takes real x returns real
return this.a*Pow2(x) + this.c // b is zero
endmethod
// Returns parabola height at given distance
public method Height takes real dist returns real
return this.Parabola(RLimit(dist - (this.md/2.), -this.md/2., this.md/2.))
endmethod
endstruct
//===========================================================
//=== ===
// Struct Orb
//=== ===
//===========================================================
private struct orb
private unit o // Main unit Orb
private effect e // Main effect orb
private unit tr // Trail dummy unit
private effect te // Trail dummy effect
private unit caster // Casting unit
private real x // Orbs x position
private real y // Orbs y position
private real z // Orbs z position
private real vel // Orbs velocity
private real acc // Orbs acceleration
private real tx // Target point x
private real ty // Target point y
private real rad // Target point radians
private real dx // Direction x
private real dy // Direction y
private real fz // Floor Z
private real dist // Distance
private integer lvl // Spell level
private parabola p // Our parabola movement
// Constructor method
static method create takes real x, real y, location tloc returns orb
local orb this = orb.allocate()
set this.tx = GetLocationX(tloc)
set this.ty = GetLocationY(tloc)
set this.rad = Atan2(this.ty-y, this.tx-x)
set this.o = CreateUnit(GetOwningPlayer(GetTriggerUnit()), DUM_RAWCODE, x, y, this.rad*bj_RADTODEG)
set this.e = AddSpecialEffectTarget(ORB_QModEL_PATH, this.o, ATTACH_POINT)
set this.x = x
set this.y = y
set this.z = 0.
set this.dx = Cos(this.rad)
set this.dy = Sin(this.rad)
set this.fz = DD_GetFloorZ(this.x, this.y)
set this.lvl = GetUnitAbilityLevel(GetTriggerUnit(), GetSpellAbilityId())
set this.tr = CreateUnit(GetOwningPlayer(GetTriggerUnit()), DUM_RAWCODE, x-TRAIL_DISTANCE*this.dx, y-TRAIL_DISTANCE*this.dy, this.rad*bj_RADTODEG)
set this.te = AddSpecialEffectTarget(TRAIL_QModEL_PATH, this.tr, ATTACH_POINT)
set this.caster = GetTriggerUnit()
set this.dist = SquareRoot(hypot(this.tx-this.x, this.ty-this.y)) + 1. // to make sure we dont divide by zero
set this.acc = this.dist/Pow2(ORB_DURATION/2.)
set this.vel = this.acc*ORB_DURATION*0.5
set this.p = parabola.create(this.dist, (4.25/6.5)*this.dist)
debug call BJDebugMsg("acc="+R2S(this.acc))
debug call BJDebugMsg("vel="+R2S(this.vel))
call SetUnitScale(this.o, ORB_SCALE, ORB_SCALE, ORB_SCALE)
call UnitAddAbility(this.o, 'Aloc')
call UnitAddAbility(this.tr, 'Aloc')
call RemoveLocation(tloc)
return (this)
endmethod
// Moves orb at given point
private method Move takes real x, real y, real z returns nothing
local real fz = DD_GetFloorZ(x, y)
call SetUnitX(this.o, x)
call SetUnitY(this.o, y)
call SetUnitFlyHeight(this.o, z-fz+this.fz, 0.)
set this.x = x
set this.y = y
set x = x - TRAIL_DISTANCE * this.dx
set y = y - TRAIL_DISTANCE * this.dy
call SetUnitX(this.tr, x)
call SetUnitY(this.tr, y)
call SetUnitFlyHeight(this.tr, this.p.Height(this.dist+TRAIL_DISTANCE)-DD_GetFloorZ(x, y)+this.fz, 0.)
set this.z = z-fz+this.fz
endmethod
// Calculated movement
private method MoveEx takes real offset_dist returns nothing
set this.dist = this.dist - offset_dist
call this.Move(this.x+offset_dist*this.dx, this.y+offset_dist*this.dy, this.p.Height(this.dist))
endmethod
// Returns velocity converted to single distance movement
private method Velocity2Distance takes nothing returns real
set this.vel = this.vel - (this.acc*TIMER_MOVE_DELAY)
return rabs(this.vel*TIMER_MOVE_DELAY)
endmethod
// Offset movement
public method OffsetMove takes nothing returns nothing
call this.MoveEx(this.Velocity2Distance())
endmethod
// Orb explosion
public method Explode takes nothing returns nothing
local unit u = CreateDummy(this.getowner, this.tx, this.ty)
local string array file
call SetUnitScale(u, 2.25, 2.25, 1.75)
call UnitApplyTimedLife(u, 'BTLF', 3.)
// Load explosion effects
call DestroyEffectTimed(AddSpecialEffectTarget(ORB_ERUPTION_EFFECT, u, ATTACH_POINT), 2.5)
call DestroyEffect(AddSpecialEffectTarget(ORB_EXPLOSION_EFFECT, u, ATTACH_POINT))
// Terrain & camera blow
call TerrainDeformCrater(this.tx, this.ty, this.lvl:FE_Radius, EXPLOSION_DEAPTH, R2I(TERRAIN_DEFORMATION_DURATION*1000.), false)
call CameraSetEQNoise(CAMERA_MAGNITUDE, CAMERA_NOISE_DURATION)
// Play explosion sound
set file[0] = SPELL_EXPLODE_SOUND_1
set file[1] = SPELL_EXPLODE_SOUND_2
call PlayGenericSound(GetRandomInt(0, 1):file, SPELL_EXPLODE_SOUND_VOL, this.tx, this.ty, 3000.)
// Deal damage
set TempDamageData = damagedata.create(this.getowner, this.lvl, CreateDummy(this.getowner, this.tx, this.ty), this.caster)
call DamageGroupRadiusTimed(this.tx, this.ty, 0.2)
// Destroy this orb
call this.destroy()
set u = null
endmethod
//====================
// *** Operators ***
//====================
// Checks did orb land on ground
public method operator landed takes nothing returns boolean
return (this.dist <= 0.) or (this.z <= 0.)
endmethod
// Returns orb owner
public method operator getowner takes nothing returns player
return GetOwningPlayer(this.o)
endmethod
// Destructor method
method onDestroy takes nothing returns nothing
call DestroyEffect(this.e)
call UnitApplyTimedLife(this.o, 'BTLF', 1.25)
call DestroyEffect(this.te)
call UnitApplyTimedLife(this.tr, 'BTLF', 1.25)
call this.p.destroy()
set this.e = null
set this.o = null
set this.tr = null
set this.te = null
set this.caster = null
endmethod
endstruct
//********************************************************************************
// ------ ------
// Spell's Main Code
// ------ ------
//********************************************************************************
//===========================================================================
// Spell's Main Condition
private constant function FrostEruptionCondition takes nothing returns boolean
return (GetSpellAbilityId() == FE_RAWCODE)
endfunction
// Timer Periodic Action
private function FrostEruptionPeriodic takes nothing returns nothing
// ::: Load Locals :::
local triggerx tx = GetTriggeringTriggerx()
local orb o = orb( GetTriggerxValue(tx) )
// Orb movement
call o.OffsetMove()
// Reached ground?
if (o.landed) then
// Explode
call o.Explode()
// End single cast
set ExecId = ExecId - 1
if (ExecId == 0) then
call PauseTimer(Timer)
call DestroyTimer(Timer)
set Timer = null
endif
// Blow triggerx
call DestroyTriggerx(tx)
endif
endfunction
//===========================================================================
// Spell's Main Action
private function FrostEruptionAction takes nothing returns nothing
// *** Load locals ***
local unit u = GetTriggerUnit()
local integer id = ExecId
local triggerx Triggerx
local orb o = orb.create(GetUnitX(u), GetUnitY(u), GetSpellTargetLoc())
// Never execute
if (GetUnitAbilityLevel(u, FE_RAWCODE) > Level) then
call BJDebugMsg("|cffff0000Spell: Frost Eruption|r - Can't cast spell. Spell level greater than defined!")
call DestroyTrigger(GetTriggeringTrigger())
return
endif
// Play sound
call PlayGenericSound(SPELL_START_SOUND, SPELL_START_SOUND_VOL, GetUnitX(u), GetUnitY(u), 3000.)
// New timer load if current does not exist
if (Timer == null) then
set Timer = CreateTimer()
call TimerStart(Timer, TIMER_MOVE_DELAY, true, null)
endif
// Load triggerx
set Triggerx = CreateTriggerx(null, function FrostEruptionPeriodic)
call TriggerxRegisterTimerEvent(Triggerx)
call SetTriggerxValue(Triggerx, integer(o))
// Next exec count id
set ExecId = id + 1
// Null locals
set u = null
endfunction
// Extra trigger action
private function UnfreezeUnitsAction takes nothing returns nothing
local unit u = GetTriggerUnit()
if (QMod(GetHandleId(u), JASS_MAX_ARRAY_SIZE):UnitFrozen) then
call FreezeUnit(u, false)
endif
set u = null
endfunction
//====================================================================================
// Main Setup Function For FE
private function FrostEruption_Main takes nothing returns nothing
// Load locals...
local trigger tg = CreateTrigger()
local trigger tgx = CreateTrigger()
local integer i = 0
// Setup user spell
call SetupFrostEruption()
// Register trigger events
call TriggerRegisterAnyUnitEvent(tg, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(tg, Condition(function FrostEruptionCondition))
call TriggerAddAction(tg, function FrostEruptionAction)
call TriggerRegisterAnyUnitEvent(tgx, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddAction(tgx, function UnfreezeUnitsAction)
// Preload all files
call Preload(ORB_QModEL_PATH)
call Preload(ORB_ERUPTION_EFFECT)
call Preload(ORB_EXPLOSION_EFFECT)
call Preload(FREEZE_EFFECT)
call Preload(TRAIL_QModEL_PATH)
//call Preload(SPELL_START_SOUND)
//call Preload(SPELL_EXPLODE_SOUND_1)
//call Preload(SPELL_EXPLODE_SOUND_2)
call GenericSoundDD(SPELL_START_SOUND, 0., 0., 0., 1.)
call GenericSoundDD(SPELL_EXPLODE_SOUND_1, 0., 0., 0., 1.)
call GenericSoundDD(SPELL_EXPLODE_SOUND_2, 0., 0., 0., 1.)
call PreloadStart()
// Null locals
set tg = null
set tgx = null
endfunction
endlibrary
//TESH.scrollpos=1450
//TESH.alwaysfold=0
//**************************************************************************************
// Spell: Shadow Crossing
// Author: Dark Dragon
// Date: 19 April 2009
//
// Other: Spell made using vJass and coding every part of game in it
//
//
// Installation:
//
// - 1. Make sure you opend Jass NewGen as well that you have at least (0.9.G.1) version of jasshelper
// - 2. Go to Import Manager (F12) and extract icons: BTNNecromancy.blp and its DISBTN version
// models: SkullBuff.mdx, DarkNova.mdx, DarkPillar.mdx, DeathMark.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 dummy unit "Dummy (DD Universal Pack)" and
// main ability Shadow Crossing. When you are pasting them to your map make sure you
// type the following rawcodes:
// Dummy: 'dumy'
// Shadow Crossing: 'scrs'
// - 5. If you want different raw codes you must edit them below as well:
// - 6. Edit data below to your own will and enjoy the spell:
// - 7. And of course copy "this" trigger and "DDUniversal Pack" to your map.
//
//**************************************************************************************
// ------ Shadow Crossing Library ------
library ShadowCrossing initializer Init_ShadowCrossing requires DDUniversalPack
// #############################################################################
// * Spell Setup: Shadow Crossing
//
// * Edit following values to match your map and your stile just simple
// * read comments for each part of code and then change values to your
// * wish and so until you reach part of code which tells you do not edit
// * below if you dont know jass
// > Enjoy!
// #############################################################################
globals
// This is the rawcode of shadow crossing ability
// this is a must change if you did not set it to scrs while pasting ability
// Default: 'scrs'
private constant integer SHADOW_CROSSING = 'scrs'
// This is the path to shadow nova explosion
// Default: "Spells\\ShadowCrossing\\DarkNova.mdl"
private constant string SHADOW_NOVA = "Spells\\ShadowCrossing\\DarkNova.mdx"
// This is the path to shadow pillar explosion
// Default: "Spells\\ShadowCrossing\\DarkPillar.mdl"
private constant string SHADOW_PILLAR = "Spells\\ShadowCrossing\\DarkPillar.mdx"
// This is the path to shadow buff
// Default: "Spells\\ShadowCrossing\\SkullBuff.mdl"
private constant string SHADOW_BUFF = "Spells\\ShadowCrossing\\SkullBuff.mdx"
// This is the path to shadow mark
// Default: "Spells\\ShadowCrossing\\DeathMark.MDL"
private constant string SHADOW_MARK = "Spells\\ShadowCrossing\\DeathMark.MDX"
// This are cast effects which are created around caster
// Default: "Abilities\\Spells\\Undead\\DarkRitual\\DarkRitualTarget.mdl"
private constant string SHADOW_RITUAL = "Abilities\\Spells\\Undead\\DarkRitual\\DarkRitualTarget.mdx"
// This is the path to shadow missile model
// Default: "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
private constant string SHADOW_MISSILE = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdx"
// This is model path to effect which is created when missile dies
// Default: "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl"
private constant string MISSILE_DEATH = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdx"
// This is effect which is attached to heros when they take difference damage
// Default: "Abilities\\Spells\\Undead\\DeathandDecay\\DeathandDecayTarget.mdl"
private constant string SHADOW_DIFFERENCE = "Abilities\\Spells\\Undead\\DeathandDecay\\DeathandDecayTarget.mdx"
// This is path to clones raise effect
// Default: "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
private constant string CLONE_RAISE = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdx"
// This is the effect which is loaded when clone avoids damage
// Default: "Abilities\\Spells\\Items\\SpellShieldAmulet\\SpellShieldCaster.mdl"
private constant string CLONE_AVOID = "Abilities\\Spells\\Items\\SpellShieldAmulet\\SpellShieldCaster.mdx"
// This is a sound file which is played when spell is casted
// Default: "Buildings\\Undead\\SacrificialPit\\SacrificialPitWhat1.wav"
private constant string SHADOW_VOICE = "Buildings\\Undead\\SacrificialPit\\SacrificialPitWhat1.wav"
private constant real SHADOW_VOICE_VOL = 90.
// This sound is played when missiles are fired
// Default: "Buildings\\Undead\\TempleOfTheDamned\\TempleOfTheDamnedWhat.wav"
private constant string MISSILES_FIRE = "Buildings\\Undead\\TempleOfTheDamned\\TempleOfTheDamnedWhat.wav"
private constant real MISSILES_FIRE_VOL = 65.
// This is explosion sound file (its played on pillar eruption)
// Default: "Sound\\Buildings\\Death\\UndeadBuildingDeathLarge1.wav"
private constant string SHADOW_BLOW = "Sound\\Buildings\\Death\\UndeadBuildingDeathLarge1.wav"
private constant real SHADOW_BLOW_VOL = 90.
// Should terrain deformation be used ?
// Default: true
private constant boolean USE_TERRAIN_DEFORMATION = true
// Terrain deformation depth... has no effect if 'USE_TERRAIN_DEFORMATION' is false
// Default: 32
private constant real DEFORM_DEPTH = 32.
// Terrain deformation duration... has no effect if 'USE_TERRAIN_DEFORMATION' is false
// Default: 2.2
private constant real DEFORM_DURATION = 2.2
// *** Use custom AI? custom ai forces ai units not to run on cast ***
// Default: true
private constant boolean USE_CUSTOM_AI = true
// Usually shadow spells like this one do not damage enemy undead units
// but this one by default damages enemy units, so if you dont want that
// enemy undead units are damaged set this to 'false'
// Default: true
private constant boolean CAN_DAMAGE_UNDEAD = true
// The number of missiles loaded
// Default: 8
private constant integer MISSILES_COUNT = 8
// The min height of missile (if min and max are 0)
// missile movement will be linear
// Default: 150
private constant real MISSILE_MIN_HEIGHT = 150.
// The max height of missile
// Default: 225
private constant real MISSILE_MAX_HEIGHT = 225.
// Missile start height
// Default: 140
private constant real MISSILE_START_HEIGHT = 140.
// Missiles fire rate in seconds. Must be > 0.
// Default: 0.1
private constant real MISSILE_FIRE_RATE = 0.1
// Missiles speed in game coordinates (units movement is ranged from)
// 270 to 320 on default units. If missiles do not move linear they will
// travel at faster speed then you think they will
// Default: 800 - 1000
private constant real MISSILE_MIN_SPEED = 800.
private constant real MISSILE_MAX_SPEED = 1000.
// --------------------------------------------------------
// --- Do not edit this level vars here edit them below ---
// {
private integer Levels = 0
private real array ShadowDamage [8191]
private real array ShadowAOE [8191]
private integer array CloneMissChance [8191]
private real array CloneDamageDealPerc [8191]
private real array CloneDamageTakePerc [8191]
private real array CloneDuration [8191]
private real array SelfDamage [8191]
private real array StrengthPerc [8191]
private real array AgilityPerc [8191]
private real array IntelligencePerc [8191]
private integer array MovementSlow [8191]
private real array SlowDuration [8191]
private constant integer DONT_USE_DIFFERENCE = 0
private constant integer USE_DIFFERENCE_CASTER = 1
private constant integer USE_DIFFERENCE_TARGET = -1
private integer UseDifference = -2
private dd_color CloneColor = 0
// }
endglobals
// ***************************************************************
// ** Fell free to edit spells level data to your map needs **
// ***************************************************************
private function ShadowCrossing_Setup takes nothing returns nothing
// ---------------------------------------------------
// --- Set this variable to your spells max level ---
// --> Default: 3
set Levels = 3
// ---------------------------------------
// --- This is spells damage per level ---
// --> Default: 60 / 100 / 160
set ShadowDamage[1] = 60.
set ShadowDamage[2] = 100.
set ShadowDamage[3] = 160.
// ------------------------------------------------
// --- This is spells area of effect per level ---
// --> Default: 375 / 425 / 485
set ShadowAOE[1] = 375.
set ShadowAOE[2] = 425.
set ShadowAOE[3] = 485.
// ------------------------------------------
// --- This is raised clones miss chance ---
// --> Default: 15 / 20 / 25
set CloneMissChance[1] = 15
set CloneMissChance[2] = 20
set CloneMissChance[3] = 25
// --------------------------------------------------------------
// --- This is the percentage of how much damage clones deal ---
// --> Default: 40 / 40 / 40
set CloneDamageDealPerc[1] = 40.
set CloneDamageDealPerc[2] = 40.
set CloneDamageDealPerc[3] = 40.
// --------------------------------------------------------------
// --- This is the percentage of how much damage clones take ---
// --> Default: 170 / 170 / 170
set CloneDamageTakePerc[1] = 170.
set CloneDamageTakePerc[2] = 170.
set CloneDamageTakePerc[3] = 170.
// ----------------------------------------------------------
// --- The duration of clones until they die / disappear ---
// --> Default: 20 / 20 / 20 (in seconds)
set CloneDuration[1] = 20.
set CloneDuration[2] = 20.
set CloneDuration[3] = 20.
// -------------------------------------------------------
// --- This is special (its a color of raised clones) ---
// --- All values are integers from 0 to 255
// --> Default: 60, 0, 100, 170 arguments(red, green, blue, alpha)
set CloneColor = CreateColor(60, 0, 100, 170)
// --------------------------------
// --- The self damage on cast ---
// --> Default: 200 / 250 / 325
set SelfDamage[1] = 200.
set SelfDamage[2] = 250.
set SelfDamage[3] = 325.
// ------------------------------------------------------------------------------------------
// --- This is how much slowed units are who where effected by explosion but did not die ---
// --> Default: 40 / 45 / 50
set MovementSlow[1] = 40
set MovementSlow[2] = 45
set MovementSlow[3] = 50
// ----------------------------------------------------
// --- This is how long they are slowed in seconds ---
// --> Default: 3.00 / 3.25 / 3.6
set SlowDuration[1] = 3.00
set SlowDuration[2] = 3.25
set SlowDuration[3] = 3.60
// -------------------------------------------------
// --- Strength percentage damage between heros ---
// --> Default: 2.25 / 3.00 / 4.00
set StrengthPerc[1] = 2.25
set StrengthPerc[2] = 3.00
set StrengthPerc[3] = 4.00
// ------------------------------------------------
// --- Agility percentage damage between heros ---
// --> Default: 0 / 0 / 0
set AgilityPerc[1] = 0.
set AgilityPerc[2] = 0.
set AgilityPerc[3] = 0.
// -----------------------------------------------------
// --- Intelligence percentage damage between heros ---
// --> Default: 0 / 0 / 0
set IntelligencePerc[1] = 0.
set IntelligencePerc[2] = 0.
set IntelligencePerc[3] = 0.
// --- Tells should difference between heros be calculated ---
// --- DONT_USE_DIFFERENCE tells you that it will use full attributes no difference calculation ---
// --- USE_DIFFERENCE_CASTER tells that it will substract from caster given / setuped attributes above ---
// --- USE_DIFFERENCE_TARGET tells that it will substract from target given / setuped attributes above ---
// *** Example:
// Caster -> strength: 28 | agility 17 | intelligence 32
// Target -> strength: 20 | agility 12 | intelligence 22
//
// if DONT_USE_DIFFERENCE and spell level 1 damage will be { (28-0) * 2.25 + (17-0) * 0 + (32-0) * 0 }
// if USE_DIFFERENCE_CASTER and spell level 1 damage will be { (28-20) * 2.25 + (17-12) * 0 + (32-22) * 0 }
// if USE_DIFFERENCE_TARGET and spell level 1 damage will be { (20-28) * 2.25 + (12-17) * 0 + (22-32) * 0 }
// *** Note that damage cant be lesser then zero
//
// --> Default: USE_DIFFERENCE_CASTER
set UseDifference = USE_DIFFERENCE_CASTER
endfunction
// =============================================================================================
// *********************************************************************************************
// -------------------------------------------------------
// --- DO NOT EDIT BELOW IF YOU DONT KNOW JASS ---
// -------------------------------------------------------
// *********************************************************************************************
// =============================================================================================
// -----------------------------------------------------------------------------
// ---> Other Spells globals <---
// -----------------------------------------------------------------------------
globals
// **********************************************
// *** Constants ***
// **********************************************
private constant integer NULL = 0 // NULL for struct types
private constant real TIMER_DELAY = 0.025 // Timer precision in movement
private constant real SPELL_DELAY = 0.35 // How much to wait till dealing damage...
private constant integer GRAPHIC_TYPE_NOVA = 0 // Graphic type 1
private constant integer GRAPHIC_TYPE_PILLAR = 1 // Graphic type 2
private constant string ATTACH_POINT = "origin" // Effects attach points
private constant string DIFFERENCE_ATTACH_POINT = "overhead"// Effects attach point for hero difference damage
private constant real PI_HALF = bj_PI / 2.
private constant real PI_4 = PI_HALF / 2.
private constant integer LOC_RAWCODE = 'Aloc'
private constant real SOUNDS_PITCH = 2.
private timer Countdown = null // Main movement timer
private integer CastN = 0 // Current number of this spell casts
endglobals
// ---------------------------------------------------------
// Module Executor: This module is used for n executions to occure
// while at same time pointing to its base type which was created from
// Simple performing actions over time by given code pointer 'fptr'
// ---------------------------------------------------------
private module executor
// --- Our executor ---
private timer exc
private boolean act
private integer cnt
// *** Ptr at object ***
private static thistype array ptrAt[8191]
// *** Activates executor ***
public method ActiveExecutor takes code fptr, boolean permanent, real timeout returns boolean
if (this.act) then
return false
endif
set this.exc = CreateTimer()
call TimerStart(this.exc, timeout, permanent, fptr)
set thistype.ptrAt[ArrayId(this.exc)] = (this)
set this.act = true
set this.cnt = 0
return true
endmethod
// *** Returns executor -> in executed function / method ***
public static method Executor takes nothing returns thistype
return thistype.ptrAt[ArrayId(GetExpiredTimer())]
endmethod
// *** Returns current execution of this executor ***
public method operator xcount takes nothing returns integer
set this.cnt = this.cnt + 1
return this.cnt
endmethod
// *** Destroys executor ***
public method DestroyExecutor takes nothing returns nothing
call PauseTimer(this.exc)
call DestroyTimer(this.exc)
set this.ptrAt[ArrayId(this.exc)] = NULL
set this.exc = null
set this.act = false
set this.cnt = 0
endmethod
endmodule
// ===========================================================================================
// Here we define custom actions which are needed for shadow crossing spell
// ===========================================================================================
// -----------------------------------------------------------------
// Here we define actions on how unit should be animated
// -----------------------------------------------------------------
scope AnimateDead
// ----------------------------------
// AnimateDead Globals
// ----------------------------------
globals
private constant string ANIM_ON_LOAD = "birth"
private constant integer LIFE_BUFF = 'BTLF'
private constant unitevent ENTER_RECT = ConvertUnitEvent(5)
endglobals
// ----------------------------------------------------------------------
// Type Animate: This type holds all data needed for our animated unit
// ----------------------------------------------------------------------
private struct animate
private unit u
private integer miss
private real dtake
private real ddeal
private real dur
private trigger t
private triggeraction ta
// *** Ptr to this struct ***
private static thistype array ptrAt[8191]
// *** Constant damage trigger ***
private static trigger TakeDamage = null
// *** This is current number of clones ***
private static integer NClones = 0
// *** Add executor ***
implement optional executor
// ----------------------------------------------------------------------
// *** Actions when unit takes damage ***
//
// *** Main method for clones (miss / deal and take damage) ***
// ----------------------------------------------------------------------
private static method Actions takes nothing returns nothing
local thistype an
local unit u
local unit s
local texttag tt
// *** Here unit taked damage ***
// *** Make sure that this is executed only when number of clones is > 0 ***
//! runtextmacro IF("thistype.NClones == 0", "return", "")
call DisableTrigger(thistype.TakeDamage)
// *** We try to get type form attacked unit ***
set u = GetTriggerUnit()
set s = GetEventDamageSource()
set an = thistype.ptrAt[ArrayId(u)]
// *** Both are clones do nothing ***
if (an != NULL and thistype.ptrAt[ArrayId(s)] != NULL) then
// *** Clear memory ***
set u = null
set s = null
// *** Skip Remaining ***
call EnableTrigger(thistype.TakeDamage)
return
endif
// *** If taking damage unit is a clone then ***
if (an != NULL) then
// *** Clone took damage ***
// *** Lets see can he avoid damage ***
if (GetRandomInt(1, 100) <= an.miss) then
// *** Avoid damage ***
set tt = CreateTextTagUnitBJ("|cff777777avoid|r", u, 0., 10., 100., 100., 100., 0.)
call SetTextTagVelocityBJ(tt, 40., 90.)
call SetUnitState(u, UNIT_STATE_LIFE, GetUnitState(u, UNIT_STATE_LIFE) + GetEventDamage())
call DestroyEffect(AddSpecialEffectTarget(CLONE_AVOID, u, ATTACH_POINT))
// *** Delay 2 seconds ***
call Sleep(2.)
// *** Blow texttag and clear memory ***
call DestroyTextTag(tt)
set tt = null
set u = null
set s = null
// *** Skip remaining actions ***
call EnableTrigger(thistype.TakeDamage)
return
endif
// *** If damage was not avoided then do here damage setup ***
call UnitDamageTarget(s, u, GetEventDamage()*((an.dtake/100.) - 1.), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
// *** We are done setuping taking damage to animated clones ***
// *** Clear memory ***
set u = null
set s = null
// *** Skip remaining actions ***
call EnableTrigger(thistype.TakeDamage)
return
endif
// *** If we are here target was not clone ***
set an = thistype.ptrAt[ArrayId(s)]
// *** If source is a clone then ***
if (an != NULL) then
// *** We shall perform damage dealing here ***
call SetUnitState(u, UNIT_STATE_LIFE, GetUnitState(u, UNIT_STATE_LIFE) + GetEventDamage()*(an.ddeal/100.))
// *** Clear memory ***
set s = null
set u = null
endif
call EnableTrigger(thistype.TakeDamage)
endmethod
// *** This actions are executed when clone dies ***
private static method CloneOnDeath takes nothing returns nothing
// *** Get triggering animated clone ***
local thistype an = thistype.ptrAt[ArrayId(GetTriggerUnit())]
// *** Blow it away ***
call an.destroy()
endmethod
// -------------------------------------------------
// *** Init this struct ***
// -------------------------------------------------
static method onInit takes nothing returns nothing
set thistype.TakeDamage = CreateTrigger()
// *** Add enter rect so when new unit is loaded its event is as well registerd ***
// *** As well add that main action to our animate.TakeDamage trigger ***
call TriggerRegisterAnyUnitDamaged(thistype.TakeDamage)
call TriggerAddAction(thistype.TakeDamage, function thistype.Actions)
endmethod
// -------------------------------------------------------------
// *** Stop clone death animation and give controll to user ***
private static method CloneAllowControll takes nothing returns nothing
// *** Get executor ***
local thistype an = thistype.Executor()
// *** Give controll now ***
call PauseUnit(an.u, false)
call SetUnitAnimation(an.u, "stand")
call SetUnitAnimation(an.u, ANIM_ON_LOAD)
call CloneColor.VertexUnit(an.u)
call UnitApplyTimedLife(an.u, LIFE_BUFF, an.dur)
call DestroyEffectTimed(AddSpecialEffectTarget(CLONE_RAISE, an.u, ATTACH_POINT), 2.5)
// *** Blow executor ***
call an.DestroyExecutor()
endmethod
// *** Constructor ***
static method create takes unit origin, player owner, integer miss, real dmgTake, real dmgDeal, real dur returns thistype
local thistype this = thistype.allocate()
// *** Associate members with there values ***
// set this.u = CreateUnit(owner, GetUnitTypeId(origin), GetUnitX(origin), GetUnitY(origin), GetUnitFacing(origin))
set this.u = origin
set this.miss = miss
set this.dtake = dmgTake
set this.ddeal = dmgDeal
set this.dur = dur
// *** Set pointer on target clone and increase current clones number ***
set this.ptrAt[ArrayId(this.u)] = (this)
set this.NClones = this.NClones + 1
// *** Setup clone ***
call UnitRemoveBuffs(this.u, true, true)
call PauseUnit(this.u, true)
call SetUnitAnimation(this.u, "death")
call SetWidgetLife(this.u, GetUnitState(this.u, UNIT_STATE_MAX_LIFE))
call SetUnitOwner(this.u, owner, true)
call SetUnitExploded(this.u, true)
// *** Setup clone death trigger ***
set this.t = CreateTrigger()
call TriggerRegisterUnitEvent(this.t, this.u, EVENT_UNIT_DEATH)
set this.ta = TriggerAddAction(this.t, function thistype.CloneOnDeath)
// *** Active executor ***
call this.ActiveExecutor(function thistype.CloneAllowControll, false, 0.25)
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call TriggerRemoveAction(this.t, this.ta)
call DestroyTrigger(this.t)
set this.NClones = this.NClones - 1
set this.ptrAt[ArrayId(this.u)] = NULL
set this.t = null
set this.ta = null
set this.u = null
endmethod
endstruct
// --------------------------------------------------------------------------------
// *** This is main function of AnimateDead and its accessible in this library ***
public function AnimateUnit takes unit whichUnit, player owner, integer missChance, real dmgTake, real dmgDeal, real duration returns nothing
call animate.create(whichUnit, owner, missChance, dmgTake, dmgDeal, duration)
endfunction
endscope
// -----------------------------------------------------------------------
// *** Here are defined functions on how to slow target ***
// -----------------------------------------------------------------------
scope SlowDown
// ================================================
// --- Slow down globals ---
// ================================================
globals
// *** Since in current version of jasshelper i cant declare ***
// *** static array in struct which extends array ***
private integer array ptrAt[8191]
endglobals
// -----------------------------------------------------------------
// Type Victim: Contains all data for slowed unit
// -----------------------------------------------------------------
private struct victim
private unit u
private integer slow
private effect array e[2]
private effect skl
// *** Constructor ***
static method create takes unit u, integer slow returns victim
// *** Allocate new victim ***
local victim this = victim.allocate()
// *** Associate our members with there proper values ***
set this.u = u
set this.slow = slow
set this.skl = AddSpecialEffectTarget(SHADOW_BUFF, u, ATTACH_POINT)
set this.e[0] = AddSpecialEffectTarget(SHADOW_MISSILE, u, "hand right")
set this.e[1] = AddSpecialEffectTarget(SHADOW_MISSILE, u, "hand left")
// *** Now setup our unit ***
call SetUnitMoveSpeed(this.u, GetUnitMoveSpeed(this.u) - this.slow)
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call SetUnitMoveSpeed(this.u, GetUnitMoveSpeed(this.u) + this.slow)
call DestroyEffect(this.skl)
call DestroyEffect(this.e[0])
call DestroyEffect(this.e[1])
set this.skl = null
set this.e[0] = null
set this.e[1] = null
set this.u = null
endmethod
endstruct
// *** Type for victim group ***
private type nexus extends victim array [64]
// ------------------------------------------------------------------
// Type Slow: Its a main type of slowing units and contains all data
// ------------------------------------------------------------------
private struct slow extends array
private timer t
private nexus n
private integer c
private static integer Count = 0
// *** Constructor ***
static method create takes real duration, code fptr returns slow
// *** Define slow local ***
local slow this = NULL
local integer i
// *** Check do we already have slow types ***
if (this.Count == 0) then
set this = slow(1)
else
// *** Loop through them and if some one can be used then use it now ***
//! runtextmacro FOR("this", "1", "this.Count + 1")
if (TimerGetRemaining(this.t) == duration) then
return (this)
endif
//! runtextmacro ENDFOR()
// *** We must create new one as all are already unuseable ***
//set this = slow( integer(this) )
endif
// *** If we are here that means we now have new id ***
set this.t = CreateTimer()
set this.n = nexus.create()
set this.c = 0
//! runtextmacro FORX("i", "0", "nexus.size", "set this.n[i] = NULL")
call TimerStart(this.t, duration, false, fptr)
set ptrAt[ArrayId(this.t)] = (this)
set slow.Count = slow.Count + 1
return (this)
endmethod
// *** Destructor ***
method destroyx takes nothing returns nothing
local thistype thisx = slow.Count
local integer i
// *** Clear current pointer "this" in memory ***
call PauseTimer(this.t)
call DestroyTimer(this.t)
//! runtextmacro FORX("i", "0", "this.c", "call victim(this.n[i]).destroy()")
call this.n.destroy()
set ptrAt[ArrayId(this.t)] = NULL
// *** Now replace last slow with this one ***
set this.t = thisx.t
set this.n = thisx.n
set this.c = thisx.c
set ptrAt[ArrayId(thisx.t)] = (this)
// *** Once replaced clear last slow ***
set thisx.t = null
set thisx.n = NULL
set thisx.c = 0
set slow.Count = slow.Count - 1
endmethod
// *** Get slow in executed function ***
public static method Get takes nothing returns thistype
return ptrAt[ArrayId(GetExpiredTimer())]
endmethod
// *** Adds new unit as victim and stores it in its nexus ***
public method AddUnit takes unit whichUnit, integer slowf returns nothing
local victim v = victim.create(whichUnit, slowf)
set this.n[this.c] = v
set this.c = this.c + 1
endmethod
endstruct
// *** This function is executed when
private function EndSlowNexus takes nothing returns nothing
// *** Here we get out slow ***
local slow s = slow.Get()
// *** Clear memory and reset units ***
call s.destroyx()
endfunction
// *** This is the main function on how to slow target down ***
public function SlowTarget takes unit whichUnit, integer slowf, real duration returns nothing
// *** Define locals (slow is created only when needed) ***
local slow s = slow.create(duration, function EndSlowNexus)
// *** Now add that unit to slow ***
call s.AddUnit(whichUnit, slowf)
endfunction
endscope
// -----------------------------------------------------------------------
// Custom Structs / Types
// -----------------------------------------------------------------------
// ===================================================================================
// --- Type Arc: Arc is a type defined for missile movement it returns line of z height
// --- at given distance. Arc is defined by constructor arguments which are max distance
// --- and max height. Arc starts at zero length then at half of max distance is exactly
// --- the max height and then starts to fall down again to zero
// ===================================================================================
private struct arc
private real max_dist
private real max_height
private real dist
private real x
private real y
// *** Constructor ***
static method create takes real max_dist, real max_height returns thistype
local thistype this = thistype.allocate()
// ----------------------------------------------------
// <<< Setuping our members to there start values >>>
// ----------------------------------------------------
set this.dist = 0.
set this.max_dist = max_dist
set this.max_height = max_height
set this.y = max_height
set this.x = 1. - (this.y/(max_dist/2))
// Formula: r = abs(conv(dist)) * x + y -> conv() is function which rounds dist from -max_dist_half to max_dist_half
return (this)
endmethod
// *** Calculates absolute value of x |x| ***
private static constant method Abs takes real x returns real
//! runtextmacro IFX("x < 0.", "return -x")
return x
endmethod
// *** Gives limits to real value ***
private static constant method Limits takes real x, real min, real max returns real
//! runtextmacro IFX("x < min", "set x = min")
//! runtextmacro IFX("x > max", "set x = max")
return x
endmethod
// *** Increase / Decreasse distance ***
public method AddDist takes real range returns nothing
set this.dist = arc.Limits(this.dist + range, 0., this.max_dist)
endmethod
// *** Converts distance to radius length value ***
// *** Since this is an irregular arc radius is not a constant ***
private method operator radius takes nothing returns real
return this.y + ( arc.Abs((this.max_dist / 2.) - this.dist) * this.x )
endmethod
// --- Main arc calculation function ---
// *** Math f(x) = Line Arc, this writes arc in graph ***
public method Arc takes nothing returns real
return SquareRoot( Pow(this.radius, 2.) - Pow((this.max_dist * 0.5) - this.dist, 2.) )
endmethod
// --- Returns current distance of arc movement ---
public method operator getdist takes nothing returns real
return this.dist
endmethod
// --- Returns max distance of arc ---
public method operator getmaxdist takes nothing returns real
return this.max_dist
endmethod
endstruct
// ===================================================================================
// --- Type Missile: Missile is a single one of missiles at field and its main core is
// --- type graphic which allows it to be moved. It as well points to our arc movement
// --- for its own arc movement. It as well is part of its own start coordinates (x, y, z)
// --- and its own velocity as well as all 3D directions requied for arc movement
// ===================================================================================
private struct missile
private graphic m // Missile
private arc a // Arc
private real sx // Start x
private real sy // Start y
private real dx // Direction x
private real dy // Direction y
private real ox // Offset x
private real oy // Offset y
private real zx // Direction z (x)
private real zy // Direction z (y)
private real vel // Velocity of missile
// *** Constructor ***
static method create takes real x, real y, real tx, real ty returns thistype
local thistype this = thistype.allocate()
local real rad
local real w
local real h
set rad = Atan2(ty-y, tx-x)
set this.sx = x
set this.sy = y
set this.dx = Cos(rad)
set this.dy = Sin(rad)
set this.ox = Cos(rad-PI_HALF)
set this.oy = Sin(rad-PI_HALF)
set this.m = AddGraphic(SHADOW_MISSILE, x, y, MISSILE_START_HEIGHT, rad*bj_RADTODEG, 1.)
set rad = GetRandomReal(0., 6.28319)
set this.zx = Cos(rad)
set this.zy = Sin(rad)
set w = SquareRoot(Pow(tx-x, 2.) + Pow(ty-y, 2.))
set h = GetRandomReal(MISSILE_MIN_HEIGHT, MISSILE_MAX_HEIGHT)
set this.a = arc.create(w, h)
set this.vel = GetRandomReal(MISSILE_MIN_SPEED, MISSILE_MAX_SPEED) * SquareRoot(0.5 + ( (w*w)/(4.*h*h) ))
//! novjass
/\
/ \
arc / \ by using that SquareRoot in velocity i am able to make movement more precious
/ \ to arc movement since arc is longer then distance and we want our
/ \ missile to move on arc (irregular arc) not on single line
/ \
/___________________\
dist
//! endnovjass
return (this)
endmethod
// --- Done? operator ---
// --- The distance must be >= to current distance ---
public method operator done takes nothing returns boolean
return this.a.getdist >= this.a.getmaxdist
endmethod
// --- When missile ends its movement ---
public method Kill takes nothing returns nothing
call DestroyEffect(AddSpecialEffect(MISSILE_DEATH, this.m.GetX(), this.m.GetY()))
call this.m.Kill()
endmethod
// --- Move: Is main method for moving missile ---
// --- Returns: Did missile reach its final position ---
public method Move takes nothing returns boolean
local real x = this.sx
local real y = this.sy
local real z = MISSILE_START_HEIGHT
local real ac
// *** Do calculations ***
call this.a.AddDist(this.vel*TIMER_DELAY)
set ac = this.a.Arc()
set x = x + (this.a.getdist * this.dx) + (ac * this.ox * this.zx)
set y = y + (this.a.getdist * this.dy) + (ac * this.oy * this.zy)
set z = z + (ac * this.zy)
// *** Move missile ***
call this.m.Move(x, y, z)
return (this.a.getdist >= this.a.getmaxdist)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call DestroyGraphic(this.m)
call this.a.destroy()
set this.m = NULL
set this.a = NULL
endmethod
endstruct
// ===================================================================================
// ---------------------------------------------------------
// --> True coded cluster is an group for our missiles
// -- all missiles can be found in cluster and are accessed
// -- as extension tells arrays list arrays max value is MISSILE_COUNT
// -- defined by user and ranges from 0 to MISSILE_COUNT - 1
// ---------------------------------------------------------
private type cluster extends missile array [MISSILES_COUNT]
// --------------------------------------------------------------
// Type Shoot: This type is simple a pack of data which is requied
// to fire all missiles from start caster location
// to random point near targeted location
//
// This type requires the executor module
// --------------------------------------------------------------
private struct shoot
private unit c // Caster
private real x // Cast x
private real y // Cast y
private real tx // Target x
private real ty // Target y
private real rad // Radians from caster to target
private integer mc // Missiles count
private integer lvl // Spell level
public cluster cl // Missiles cluster
// Implement (executor) as if possible
implement optional executor
// *** Constructor ***
static method create takes unit c, location loc, code fptr, integer lvl returns thistype
local thistype this = thistype.allocate()
// *** Setup our members ***
set this.c = c
set this.x = GetUnitX(c)
set this.y = GetUnitY(c)
set this.tx = GetLocationX(loc)
set this.ty = GetLocationY(loc)
set this.rad = Atan2(this.ty-this.y, this.tx-this.x)
set this.mc = 0
set this.cl = cluster.create()
set this.lvl = lvl
// Arguments: code fptr, boolean permanent, real delay
call this.ActiveExecutor(fptr, true, MISSILE_FIRE_RATE)
return (this)
endmethod
// *** Adds missile to cluster ***
// *** Returns is cluster full ***
private method AddMissile takes missile whichMissile returns boolean
set this.cl[this.mc] = whichMissile
set this.mc = this.mc + 1
return (this.mc == MISSILES_COUNT)
endmethod
// *******************************
// *** Will create new missile ***
private method CreateMissile takes nothing returns missile
local real d
local real a
local real x
local real y
set d = GetRandomReal(50., 200.)
set a = GetRandomReal(this.rad-PI_4, this.rad+PI_4)
set x = this.x + d * Cos(a)
set y = this.y + d * Sin(a)
set d = GetRandomReal(50., ShadowAOE[this.lvl]-100.)
set a = GetRandomReal(0., 6.28319)
return missile.create(x, y, this.tx + d * Cos(a), this.ty + d * Sin(a))
endmethod
// *********************************************
// This method will create and add new missile
public method AddNewMissile takes nothing returns boolean
return this.AddMissile(this.CreateMissile())
endmethod
// *** Returns x of caster ***
public method operator getx takes nothing returns real
return this.x
endmethod
// *** Returns y of caster ***
public method operator gety takes nothing returns real
return this.y
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
set this.c = null
// call this.cl.destroy() -> destroyed ~via movement onDestroy
call this.DestroyExecutor()
endmethod
endstruct
// -----------------------------------------------------------------------
// Type Triggerx: triggerx is a type which does not leak triggeractions
// -----------------------------------------------------------------------
private struct triggerx
public trigger t
private triggeraction ta
private integer value
private static thistype array ptrAt[8191]
// *** Constructor ***
static method create takes code cond, code act, integer value returns thistype
local thistype this = thistype.allocate()
set this.t = CreateTrigger()
set this.value = value
set this.ptrAt[ArrayId(this.t)] = (this)
//! runtextmacro IFX("cond != null", "call TriggerAddCondition(this.t, Condition(cond))")
//! runtextmacro IFX("act != null", "set this.ta = TriggerAddAction(this.t, act)")
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
if (this.ta != null) then
call TriggerRemoveAction(this.t, this.ta)
set this.ta = null
endif
call DestroyTrigger(this.t)
set this.ptrAt[ArrayId(this.t)] = NULL
set this.t = null
endmethod
// *** TriggeringTriggerx ***
public static method GetTriggering takes nothing returns thistype
return thistype.ptrAt[ArrayId(GetTriggeringTrigger())]
endmethod
// *** Returns custom value of this triggerx ***
public method GetValue takes nothing returns integer
return this.value
endmethod
endstruct
// -----------------------------------------------------------------------
// Type Movement: Its a struct of movement data needed for single trigger
// -----------------------------------------------------------------------
private struct movement
public unit c
public real x
public real y
public integer lvl
public cluster cl
// *** Constructor ***
static method create takes unit c, cluster cl, location loc, integer lvl returns thistype
local thistype this = thistype.allocate()
set this.c = c
set this.x = GetLocationX(loc)
set this.y = GetLocationY(loc)
set this.cl = cl
set this.lvl = lvl
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
local integer i
//! runtextmacro FOR("i", "0", "this.cl.size")
call this.cl[i].destroy()
set this.cl[i] = NULL
//! runtextmacro ENDFOR()
call this.cl.destroy()
set this.c = null
endmethod
endstruct
// -----------------------------------------------------------------------
// Type Explosion: Its a type which holds all data requied for a explosion
// to occure, as well as timed explosion
// -----------------------------------------------------------------------
private struct explosion
private unit c
private real x
private real y
private integer lvl
private player p
private unit d
// --- This is single Exp pointer ---
private static thistype Exp = NULL
// *** Attempt to add executor ***
implement optional executor
// --- Returns scale of effects for each aoe ---
public static constant method GetScale takes integer grpType, real aoe returns real
if (grpType == GRAPHIC_TYPE_NOVA) then
return aoe / 657.
elseif (grpType == GRAPHIC_TYPE_PILLAR) then
return aoe / 240.
endif
return 0.
endmethod
// --- Returns sign of a number (note sign of 0 is 0) but i need it to be 1 in my example ---
private static constant method Sign takes integer i returns integer
//! runtextmacro IFX("i < 0", "return -1")
return 1
endmethod
// --- Calculates extra damage for heros ---
private method AttributeDamage takes unit target returns real
local integer sg = this.Sign(UseDifference)
return rmax( ( StrengthPerc[this.lvl] * I2R((sg*GetHeroStr(this.c, true)) - (GetHeroStr(target, true)*UseDifference)) ) + ( AgilityPerc[this.lvl] * I2R((sg*GetHeroAgi(this.c, true)) - (GetHeroAgi(target, true)*UseDifference)) ) + ( IntelligencePerc[this.lvl] * I2R((sg*GetHeroInt(this.c, true)) - (GetHeroInt(target, true)*UseDifference)) ), 0. )
endmethod
// -------------------------------------------------------------------------
// This is main damage method, which damages enum units as they are picked
// -------------------------------------------------------------------------
private static method DamageEnum takes nothing returns nothing
// *** Define locals ***
local thistype exp = thistype.Exp
local unit f = GetFilterUnit()
local real dmg = ShadowDamage[exp.lvl]
local boolean undead = true
// *** If user does not want to damage undead then check that here ***
//! runtextmacro IF("not CAN_DAMAGE_UNDEAD", "set undead = not IsUnitType(f, UNIT_TYPE_UNDEAD)", "")
// *** This is our main filter check here can unit be damaged ***
if ( IsUnitEnemy(f, exp.p) and not IsUnitDead(f) and not IsUnitType(f, UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(f, UNIT_TYPE_STRUCTURE) and not IsUnitType(f, UNIT_TYPE_MECHANICAL) and undead ) then
// *** Do not kill unit animate it if possible ***
if (GetUnitState(f, UNIT_STATE_LIFE) <= dmg and not IsUnitType(f, UNIT_TYPE_FLYING) and not IsUnitType(f, UNIT_TYPE_HERO)) then
call AnimateDead_AnimateUnit(f, exp.p, CloneMissChance[exp.lvl], CloneDamageTakePerc[exp.lvl], CloneDamageDealPerc[exp.lvl], CloneDuration[exp.lvl])
else
// *** If target is a hero then calculate bonus damage ***
if (IsUnitType(f, UNIT_TYPE_HERO)) then
set dmg = dmg + exp.AttributeDamage(f)
call DestroyEffect(AddSpecialEffectTarget(SHADOW_DIFFERENCE, f, DIFFERENCE_ATTACH_POINT))
endif
call UnitDamageTarget(exp.d, f, dmg, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_DEATH, WEAPON_TYPE_WHOKNOWS)
// *** Custom AI Enabled? ***
if (USE_CUSTOM_AI and GetPlayerController(GetOwningPlayer(f)) != MAP_CONTROL_USER and not IsUnitType(f, UNIT_TYPE_PEON)) then
call IssuePointOrder(f, "attack", GetUnitX(exp.c), GetUnitY(exp.c))
endif
// *** After taking damage check is unit dead, if not slow it ***
if (not IsUnitDead(f)) then
// *** Slow target now it its alive ***
call SlowDown_SlowTarget(f, MovementSlow[exp.lvl], SlowDuration[exp.lvl])
endif
endif
endif
// *** Null locals ***
set f = null
endmethod
// ------------------------------------------------
// --- This is main explosion of this spell ---
// ------------------------------------------------
private static method Explode takes nothing returns nothing
// *** Associate explosion with our executor ***
local thistype exp = thistype.Executor()
// *** Load nova explode final ***
set GRAPHIC_DURATION = .1
call DestroyGraphicTimed( AddGraphic(SHADOW_NOVA, exp.x, exp.y, 0., GetRandomReal(0., 360.), explosion.GetScale(GRAPHIC_TYPE_NOVA, ShadowAOE[exp.lvl])), 1.25 )
// *** Deal damage here to all enemy living units ***
set thistype.Exp = (exp)
call groupex.FilterRadius(exp.x, exp.y, ShadowAOE[exp.lvl], function thistype.DamageEnum)
// *** Clear leaks ***
call exp.destroy()
endmethod
// *** The constructor of explosion type ***
static method create takes unit c, real x, real y, integer lvl returns thistype
local thistype this = thistype.allocate()
// *** Associate members to there values ***
set this.c = c
set this.x = x
set this.y = y
set this.lvl = lvl
set this.p = GetOwningPlayer(c)
set this.d = CreateUnit(this.p, DD_DUMMY_CODE, x, y, 0.)
// *** This will execute given method after SPELL_DELAY seconds ***
call this.ActiveExecutor(function thistype.Explode, false, SPELL_DELAY)
// *** Now setup our dummy ***
call UnitAddAbility(this.d, LOC_RAWCODE)
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call RemoveUnit(this.d)
call this.DestroyExecutor()
set this.d = null
set this.p = null
set this.c = null
endmethod
endstruct
// -----------------------------------------------------------------------
// Type Ritual: This is type which holds data for ritual effects on cast
// -----------------------------------------------------------------------
private struct ritual
private real x
private real y
private real rad
private real dist
// *** How often effects are created ***
private static constant real RITUAL_DELAY = 0.2
// *** Whats the first effect offset distance in x axes at caster facing degrees ***
private static constant real OFFSET_DIST_X = 125.
// *** Whats the first effect offset distance in y axes at caster facing degrees +- 90 ***
private static constant real OFFSET_DIST_Y_1 = 80.
// *** Whats the second effect offset distance in y axes at caster facing degrees +- 90 ***
private static constant real OFFSET_DIST_Y_2 = 155.
// *** How much distance is decreased per ritual load in x axes ***
private static constant real DIST_BETWEEN = 100.
// *** Add executor here ***
implement optional executor
// *** Main method for loading ritual effects ***
private static method CreateRitual takes nothing returns nothing
// *** Get this ritual from executor ***
local ritual rt = ritual.Executor()
local integer xcount = rt.xcount
local real offsety
local real rad
local real dist
// *** If its first cast then do offsets for first ritual ***
//! runtextmacro IF("xcount == 1", "set offsety = rt.OFFSET_DIST_Y_1", "set offsety = rt.OFFSET_DIST_Y_2")
// *** Do few calculations for ritual effects ***
set rad = Atan(offsety / rt.dist)
set dist = SquareRoot( rt.dist*rt.dist + offsety*offsety ) * RSignBJ(rt.dist)
// *** Now load ritual effects ***
call DestroyEffect(AddSpecialEffect(SHADOW_RITUAL, rt.x + dist * Cos(rt.rad+rad), rt.y + dist * Sin(rt.rad+rad)))
call DestroyEffect(AddSpecialEffect(SHADOW_RITUAL, rt.x + dist * Cos(rt.rad-rad), rt.y + dist * Sin(rt.rad-rad)))
// *** Get next distance ***
set rt.dist = rt.dist - ritual.DIST_BETWEEN
// *** If this was the last ritual load then destroy this struct ***
//! runtextmacro IF("xcount == 3", "call rt.destroy()", "")
endmethod
// *** Constructor ***
static method create takes unit c, location loc returns ritual
local ritual this = thistype.allocate()
// *** Setup ritual members ***
set this.x = GetUnitX(c)
set this.y = GetUnitY(c)
set this.rad = Atan2(GetLocationY(loc)-this.y, GetLocationX(loc)-this.x)
set this.dist = thistype.OFFSET_DIST_X
call this.ActiveExecutor(function thistype.CreateRitual, true, thistype.RITUAL_DELAY)
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call this.DestroyExecutor()
endmethod
endstruct
// -----------------------------------------------------------------------
// Custom Functions
// -----------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// *** This is the main function which causes explosion on targeted position
// *** It is executed when all of missiles are "dead"
private function DarkExplosion takes unit c, integer lvl, real x, real y returns nothing
// *** Add pillar explosion and set length to 2 seconds ***
set GRAPHIC_DURATION = .1
call DestroyGraphicTimed( AddGraphic(SHADOW_PILLAR, x, y, 0., GetRandomReal(0., 360.), explosion.GetScale(GRAPHIC_TYPE_PILLAR, ShadowAOE[lvl])), 2. )
// *** As it says it will create explosion after x seconds ***
// *** explosion is created inside structs static method Explode ***
// *** which is executed by implemented executor ***
call explosion.create(c, x, y, lvl)
// *** Play sound file here ***
call GenericSoundDD(SHADOW_BLOW, SHADOW_BLOW_VOL, x, y, 1.5)
// *** If use terrain deformation? then do it now ***
if (USE_TERRAIN_DEFORMATION) then
call TerrainDeformRipple(x, y, ShadowAOE[lvl], DEFORM_DEPTH, R2I(DEFORM_DURATION * 1000.), 1, 0.55, 14., 0.5, false)
endif
endfunction
// -----------------------------------------------------------------------
// *** This is trigger action for triggerx which is executed by main
// *** countdown timer and loops until all missiles are done
private function MovementPeriodic takes nothing returns nothing
// *** Load locals ***
local triggerx tx = triggerx.GetTriggering()
local movement mv = tx.GetValue()
local integer i = 0
local boolean done = true
// --- Now loop through all missiles ---
//! runtextmacro FOR("i", "0", "mv.cl.size")
exitwhen (mv.cl[i] == NULL) // missiles are still loading?
// --- Move each missile as picked if its movement did ---
// --- not reach final position ---
set done = done and mv.cl[i].done
if (not mv.cl[i].done) then
if (mv.cl[i].Move()) then
// *** Missile reached its final position ***
debug call IMsg(i)
call mv.cl[i].Kill()
endif
endif
//! runtextmacro ENDFOR()
// *** If by any means local variable done is true and moved all missiles ***
// --- spells movement of missiles is over 100% ---
if (done and i == MISSILES_COUNT) then
// *** Main call for explosion ***
call DarkExplosion(mv.c, mv.lvl, mv.x, mv.y)
// *** Here reduce cast n coz we dont need timer anymore ***
set CastN = CastN - 1
if (CastN == 0) then
call PauseTimer(Countdown)
call DestroyTimer(Countdown)
set Countdown = null
endif
// *** Destroy structs ***
call mv.destroy()
call tx.destroy()
endif
endfunction
// ******************************************************************
// --- Main Spell Delayed Function ---
// ******************************************************************
// -----------------------------------------------------------------------
// *** This is main function where missiles are created and fired away
private function FireMissiles takes nothing returns nothing
local shoot st = shoot.Executor()
// *** For every 3 fired missiles play sound ***
if (QMod(st.xcount+1, 3) == 0) then
call GenericSoundDD(MISSILES_FIRE, MISSILES_FIRE_VOL, st.getx, st.gety, SOUNDS_PITCH)
endif
// *** If this is the last missile then destroy shoot struct ***
if (st.AddNewMissile()) then
// *** Blow this struct ***
call st.destroy()
endif
set st = NULL
endfunction
// ==============================================================
// Main Trigger Conditions
// ==============================================================
private constant function Conditions takes nothing returns boolean
return GetSpellAbilityId() == SHADOW_CROSSING
endfunction
// ==============================================================
// Main Trigger Actions
// ==============================================================
private function Actions takes nothing returns nothing
// *** Define all main action locals ***
local unit u = GetTriggerUnit()
local location l = GetSpellTargetLoc()
local integer lvl = GetUnitAbilityLevel(u, SHADOW_CROSSING)
local shoot st = shoot.create(u, l, function FireMissiles, lvl)
local movement mv = movement.create(u, st.cl, l, lvl)
local ritual rt = ritual.create(u, l)
local triggerx tx = NULL
// --- To caster add cast effects ---
set GRAPHIC_DURATION = 3.25
call DestroyGraphic( AddGraphic(SHADOW_MARK, GetUnitX(u), GetUnitY(u), 120., GetUnitFacing(u), 1.25) )
// call DestroyEffect( AddSpecialEffectTarget(SHADOW_MARK, u, ATTACH_POINT) ) --> old
// --- Now suck casters life ---
call UnitDamageTarget(u, u, SelfDamage[lvl], true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
// *** Load main timer if needed and increase cast n ***
if (Countdown == null) then
set Countdown = CreateTimer()
call TimerStart(Countdown, TIMER_DELAY, true, null)
endif
set CastN = CastN + 1
// *** Load our movement trigger ***
set tx = triggerx.create(null, function MovementPeriodic, mv)
call TriggerRegisterTimerExpireEvent(tx.t, Countdown)
// *** Play cast sounds now ***
call GenericSoundDD(SHADOW_VOICE, SHADOW_BLOW_VOL, GetUnitX(u), GetUnitY(u), SOUNDS_PITCH)
// --- Null locals ---
call RemoveLocation(l)
set u = null
set l = null
endfunction
// **************************************************************
// Extra actions which will allow casting this spell only
// when caster has enough life
private function ActionsEx takes nothing returns nothing
local unit u
local integer lvl
// *** Detect is this ability casted ***
if (GetSpellAbilityId() == SHADOW_CROSSING) then
// *** Get needed data ***
set u = GetTriggerUnit()
set lvl = GetUnitAbilityLevel(u, SHADOW_CROSSING)
// *** Caster does not have enough life ***
if (GetUnitState(u, UNIT_STATE_LIFE) < SelfDamage[lvl]) then
// *** Do actions only for casting player ***
if (GetLocalPlayer() == GetOwningPlayer(u)) then
call ClearTextMessages()
call DisplayTimedTextToPlayer(GetOwningPlayer(u), 0., 0., 10., "|cffffcc00Unable to cast spell, you have not enough life!|r")
call StartSound(bj_questWarningSound)
endif
// *** Do not allow caster to cast spell ***
call IssueImmediateOrder(u, "stop")
call Sleep( MinSleep() )
call SetUnitAnimation(u, "stand")
endif
// *** Null vars ***
set u = null
endif
endfunction
// ==============================================================
// Main Initialization
private function Init_ShadowCrossing takes nothing returns nothing
// *** Define / init locals ***
local trigger t // - Local trigger
// -----------------------------
// --- Load our main trigger ---
// -----------------------------
set t = CreateTrigger()
call TriggerRegisterAnyUnitEvent(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function Conditions))
call TriggerAddAction(t, function Actions)
// -------------------------------
// Load life check trigger
// -------------------------------
set t = CreateTrigger()
call TriggerRegisterAnyUnitEvent(t, EVENT_PLAYER_UNIT_SPELL_CAST)
call TriggerAddAction(t, function ActionsEx)
// *********************************
// *** Define user setup here ***
// *********************************
call ShadowCrossing_Setup()
// --------------------------------------
// *** Preload files ***
// --------------------------------------
call Preload(SHADOW_MARK)
call Preload(SHADOW_BUFF)
call Preload(SHADOW_PILLAR)
call Preload(SHADOW_NOVA)
call Preload(SHADOW_MISSILE)
call Preload(SHADOW_RITUAL)
call Preload(SHADOW_DIFFERENCE)
call Preload(MISSILE_DEATH)
call Preload(CLONE_RAISE)
call Preload(CLONE_AVOID)
call Preload(SHADOW_VOICE)
call Preload(SHADOW_BLOW)
//call Preload(MISSILES_FIRE)
//call Preload(SHADOW_VOICE)
//call Preload(SHADOW_BLOW)
call GenericSoundDD(SHADOW_BLOW, 0., 0., 0., 1.)
call GenericSoundDD(SHADOW_VOICE, 0., 0., 0., 1.)
call GenericSoundDD(MISSILES_FIRE, 0., 0., 0., 1.)
call Preload(MISSILES_FIRE)
call PreloadStart()
endfunction
endlibrary
//TESH.scrollpos=12
//TESH.alwaysfold=0
//**************************************************************************************
// Spell: Frost Eruption
// Author: Dark Dragon
// Date: 21 April 2009
//
// Other: Spell made as my first vJass aura
//
//
// Installation:
//
// - 1. Make sure you have opend Jass NewGen as well that you have (0.9.G.1) version of jasshelper
// - 2. Go to Import Manager (F12) and extract icons: BTNAlgirAura.blp and its: DISBTN, PAS and DISPAS version
// models: (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:
// units: Dummy (DD Universal Pack)
// abilities: Algid Slow (Dummy) and main ability Algid Aura
// buffs: Algid Aura (Buff) and Slowed (Algid)
// When you are pasting them to your map make sure you
// type the following rawcodes:
// Dummy: 'dumy'
// Algid Aura: 'Algd'
// Algid Slow (Dummy): 'Algs'
// Algid Aura (Buff): 'Alga'
// Slowed (Algid): 'Algb'
// - 5. If you want different raw codes you must edit the below as well:
// - 6. Edit data below to your own will and enjoy the aura:
// - 7. And of course copy this trigger and DDUniversal Pack to your map.
//
//**************************************************************************************
// ****** Main Algid Aura Library ******
library AlgidAura initializer Init_AlgidAura requires DDUniversalPack
// ###########################################################
// Edit spell data here below...
// Read comments on what each value does and
// set it for you own needs
// ###########################################################
globals
// This is the raw code of the spell AlgidAura
// Default: 'Algd'
private constant integer ALGID_AURA = 'Algd'
// This is the raw code of the spells (Algid Aura) buff
// Default: 'Alga'
private constant integer ALGID_BUFF = 'Alga'
// This is the slow buff of the spell (Algid Aura)
// Default: 'Algb'
private constant integer ALGID_SLOW_BUFF = 'Algb'
// This is the raw code of dummy ability slow (Algid Slow Dummy)
// Default: 'Algs'
private constant integer ALGID_SLOW_ABILITY = 'Algs'
// This is area of effect (aoe) of algid aura
// Default: 900
private constant real ALGID_AOE = 900.
// If attacked unit is a mechanical can it be slowed?
// Default: false
private constant boolean SLOW_MECHANICAL = false
// If attacked unit is magic immune can it be effected?
// Default: false
private constant boolean SLOW_MAGIC_IMMUNE = false
// Which effect is created when unit is slowed?
// Default: "Abilities\\Weapons\\ZigguratFrostMissile\\ZigguratFrostMissile.mdl"
private constant string SLOW_EFFECT = "Abilities\\Weapons\\ZigguratFrostMissile\\ZigguratFrostMissile.mdl"
// -------------------------------------------------------------------
// *** This is level data. Do not edit them here, edit them below ***
// {
private integer Levels = 0
private integer array AlgidChance[8191]
private integer array AlgidDamage[8191]
private real array AlgidDuration[8191]
// }
endglobals
// -------------------------------------------------------
// *** Edit spells level data here ***
private function Setup_AlgidAura takes nothing returns nothing
// *** This is the max level of this spell ***
// Default: 3
set Levels = 3
// ------------------------------------------------------------
// *** This is n chance that unit has to active this spell ***
// Default: 6 / 9 / 13
set AlgidChance[1] = 6
set AlgidChance[2] = 9
set AlgidChance[3] = 13
// -------------------------------------------
// *** This is extra damage dealt on slow ***
// Default: 4 / 7 / 10
set AlgidDamage[1] = 4
set AlgidDamage[2] = 7
set AlgidDamage[3] = 10
// ----------------------------------------
// *** This is duration of slow effect ***
// Default: 3.00 / 4.50 / 6.00
set AlgidDuration[1] = 3.00
set AlgidDuration[2] = 4.50
set AlgidDuration[3] = 6.00
endfunction
// =================================================================
// --- DO NOT EDIT BELOW IF YOU DONT KNOW JASS ---
// =================================================================
// *** Spells Interface ***
//! novjass
interface
// ----------------------------------------------------------------
// *** Slows target and returns false if target cant be slowed ***
private function SlowTarget takes unit target returns boolean
// --------------------------------------------------------------------------------
// *** This function adds given unit to slow group (making its hp reg to stop) ***
private function AddUnitToSlow takes unit u returns nothing
// ---------------------------------------------------------------------------------------
// *** This function removes given unit from slow group (making its hp reg to start) ***
private function RemoveUnitFromSlow takes unit u returns nothing
// ------------------------------------
// *** Returns random percentage ***
private function GetRndPerc takes nothing returns integer
endinterface
//! endnovjass
// ----------------------------
// *** Spells Globals ***
// ----------------------------
globals
// *** Constants ***
private constant real INTERVAL = 0.05
private constant integer LOC_CODE = 'Aloc'
private constant integer GEN_CODE = 'BTLF'
private constant string SLOW_ORDER_ID = "attack"
private constant string ATTACH_POINT = "head"
// *** Globals ***
// *** Spells Main Globals ***
private integer AlgidLevel = 0
private integer NVictims = 0
private groupex AlgidGroup = 0
private trigger AlgidTrigger = null
private triggeraction AlgidAction = null
private code AlgidCode = null
// *** Extra Storage Globals ***
private integer array UnitValue[8191]
private boolean array UnitExist[8191]
endglobals
// -----------------------------------------------------------------
// *** Spells custom types ***
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
// Type Algid: Thats main type which points to all needed data for auras slow
private struct algid
public unit u
public real dur
public real life
// *** Constructor ***
static method create takes unit u, real dur returns thistype
local thistype this = thistype.allocate()
set this.u = u
set this.dur = dur
set this.life = GetUnitState(u, UNIT_STATE_LIFE)
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
set this.u = null
endmethod
endstruct
// -----------------------------------------------------------------
//
// --- Spells Custom Functions ---
//
// -----------------------------------------------------------------
// ------------------------------------------------------------
// *** We would like custom storage methods for our units ***
private function SetUnitValue takes unit u, integer val returns boolean
local integer p = ArrayId(u)
//! runtextmacro IF("p:UnitExist", "return false", "")
set p:UnitValue = val
set p:UnitExist = true
return true
endfunction
private function GetUnitValue takes unit u returns integer
local integer p = ArrayId(u)
//! runtextmacro IF("p:UnitExist", "return p:UnitValue", "")
return 0
endfunction
private function FlushUnitValue takes unit u returns nothing
local integer p = ArrayId(u)
set p:UnitValue = 0
set p:UnitExist = false
endfunction
// *** Here end storage methodes ***
// ------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------
// *** Slows target forever! this must be called in main trigger action (where GetAttacker is accessible) ***
private function SlowTarget takes unit target returns boolean
// --- Load locals ---
local unit u
local player p
// *** Allow mechanical units to be slowed? ***
//! runtextmacro IF("not SLOW_MECHANICAL and IsUnitType(target, UNIT_TYPE_MECHANICAL)", "return false", "")
// *** Allow magic immune to be slowed? ***
//! runtextmacro IF("not SLOW_MAGIC_IMMUNE and IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)", "return false", "")
// *** First check is unit already slowed? and if it is return true, since he is slowed ***
//! runtextmacro IF("GetUnitAbilityLevel(target, ALGID_SLOW_BUFF) > 0", "return true", "")
// *** Now do check can unit be slowed ***
set p = GetOwningPlayer(GetEventDamageSource())
if (not IsUnitType(target, UNIT_TYPE_STRUCTURE) and IsUnitEnemy(target, p) and not IsUnitDead(target)) then
// *** Unit can be slowed ***
// *** So slow him now ***
set u = CreateUnit(p, DD_DUMMY_CODE, GetUnitX(target), GetUnitY(target), 0.)
call DestroyEffect(AddSpecialEffectTarget(SLOW_EFFECT, target, ATTACH_POINT))
call UnitAddAbility(u, LOC_CODE)
call UnitAddAbility(u, ALGID_SLOW_ABILITY)
call IssueTargetOrder(u, SLOW_ORDER_ID, target)
call UnitApplyTimedLife(u, GEN_CODE, 1.)
set u = null
return true
endif
// *** If true was not jet returned then it means unit cant be slowed ***
// *** and infact this must now return false ***
return false
endfunction
// --------------------------------------------------------------------------------
// *** This function adds given unit to slow group (making its hp reg to stop) ***
private function AddUnitToSlow takes unit u returns nothing
// *** First of all do check if objects in game do exist (if not create them) ***
if (NVictims == 0) then
// *** Allocate algid objects ***
set AlgidGroup = CreateGroupEx()
set AlgidTrigger = CreateTrigger()
set AlgidAction = TriggerAddAction(AlgidTrigger, AlgidCode)
// *** Work with algid objects ***
call TriggerRegisterTimerEvent(AlgidTrigger, INTERVAL, true)
endif
// *** Add unit to our group and increase number of victims ***
set NVictims = NVictims + 1
call AlgidGroup.AddUnit(u)
endfunction
// ---------------------------------------------------------------------------------------
// *** This function removes given unit from slow group (making its hp reg to start) ***
private function RemoveUnitFromSlow takes unit u returns nothing
// *** Decrease number of victims ***
set NVictims = NVictims - 1
call AlgidGroup.RemoveUnit(u)
// *** Now check is this the last unit and clear memory ***
if (NVictims == 0) then
// *** Clear memory ***
call DestroyGroupEx(AlgidGroup)
call TriggerRemoveAction(AlgidTrigger, AlgidAction)
call DestroyTrigger(AlgidTrigger)
set AlgidTrigger = null
set AlgidAction = null
endif
endfunction
// ------------------------------------
// *** Returns random percentage ***
/*private function GetRndPerc takes nothing returns integer
local integer rnd = GetRandomInt(1, 100)
call SetRandomSeed( GetRandomInt(1, (100-rnd) * IAbsBJ(GetRandomInt(-1000, 1000))) )
return (rnd)
endfunction*/
// -----------------------------------------------------------------
// *** This are some special loop functions ***
// -----------------------------------------------------------------
// *** Loops through units and gets highest level of there buff ***
private function GetAlgidLevel takes nothing returns boolean
set AlgidLevel = imax(AlgidLevel, GetUnitAbilityLevel(GetFilterUnit(), ALGID_AURA))
return false
endfunction
// ---------------------------------------------------------------
// *** This is enum function which does action for all units ***
private function StopHpRegEnum takes nothing returns nothing
// *** Load locals ***
local unit e = GetEnumUnitEx()
local algid al = GetUnitValue(e)
// -------------------------------
// *** Decrease duration ***
set al.dur = al.dur - INTERVAL
// ---------------------------------------------
// *** Reset life ***
set al.life = rmin(al.life, GetWidgetLife(e))
call SetWidgetLife(e, al.life)
// ------------------------------------------
// *** Now do check is his duration over ***
if (al.dur <= 0. or al.life <= .405) then
// *** Reset (free) unit now, duration is over ***
call UnitRemoveAbility(e, ALGID_SLOW_BUFF)
call al.destroy()
call FlushUnitValue(e)
call RemoveUnitFromSlow(e)
endif
// *** Clear memory ***
set e = null
endfunction
// ------------------------------------------------------------------
// *** This is main trigger loop function ***
// *** It is triggerd every x seconds ***
private function StopHpReg takes nothing returns nothing
// *** Loop through all of units in slowed group ***
call AlgidGroup.For(function StopHpRegEnum)
endfunction
// =================================================================
// --- Main Trigger Conditions ---
private function Conditions takes nothing returns boolean
return ( GetUnitAbilityLevel(GetEventDamageSource(), ALGID_BUFF) > 0 and IsDamageTypeMelee() )
endfunction
// =================================================================
// --- Main Trigger Actions ---
private function Actions takes nothing returns nothing
// *** Define Locals ***
local unit u = GetEventDamageSource()
local unit v = GetTriggerUnit()
local algid al
// *** What we know is that he is under effect of Algid Aura and
// *** that he got a chance to slow its target. But currently we do not
// *** know whats level of aura (in fact we do not know) are there some ally
// *** units which have greater level of this ability so this unit is under effect
// *** of highest level ability of ally not ours
// *** Here we will find out who is that target and get the level
set AlgidLevel = 0
call groupex.FilterRadius(GetUnitX(u), GetUnitY(u), ALGID_AOE, function GetAlgidLevel)
// ------------------------------
// *** Now we know the level ***
// *** If level is zero that means "aura giver" ran away and this unit will lose aura soon ***
// *** So do nothing if thats the case ***
if (AlgidLevel > 0) then
// *** Since we know the level we can check will he (attacker) trigger this action
// *** So we will use user defined variable and check percentage here
if (GetRandomChance(AlgidChance[AlgidLevel])) then
// *** Since we know know that level is defined and in fact we have highest level
// *** or better to say true buff level of this attacking unit, we can preform actions
// *** However we are still not sure can target even be slowed?
// *** Check that out and if target can be slowed preform all needed actions
if (SlowTarget(v)) then
// *** Finally we know target can be slowed so preform all needed actions ***
// *** Now do damage since if he can be slowed he can be damaged as well ***
set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(u), DD_DUMMY_CODE, 0., 0., 0.)
call UnitDamageTarget(bj_lastCreatedUnit, v, AlgidDamage[AlgidLevel], true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_COLD, WEAPON_TYPE_WHOKNOWS)
call RemoveUnit(bj_lastCreatedUnit)
// *** First check is unit already slowed and edit duration ***
if (GetUnitValue(v) != 0) then
// *** Since unit is already slowed we need to edit duration ***
set algid(GetUnitValue(v)).dur = AlgidDuration[AlgidLevel]
else
// *** Since here unit is not jet effected we want to effect him now ***
set al = algid.create(v, AlgidDuration[AlgidLevel])
call SetUnitValue(v, al)
call AddUnitToSlow(v)
endif
endif
endif
endif
// --------------------------------------------
// *** Clear leaks now ***
// --------------------------------------------
set u = null
set v = null
endfunction
// =================================================================
// *** Initialize Algid Aura ***
private function Init_AlgidAura takes nothing returns nothing
// *** Init Locals ***
local trigger t = CreateTrigger()
// *** Define our trigger ***
call TriggerRegisterAnyUnitDamaged(t)
call TriggerAddCondition(t, Condition(function Conditions))
call TriggerAddAction(t, function Actions)
// *** Predefine algid code ***
set AlgidCode = function StopHpReg
// *** Init this spells user data ***
call Setup_AlgidAura()
// *** Clear leaks ***
set t = null
endfunction
endlibrary
//TESH.scrollpos=675
//TESH.alwaysfold=0
//**************************************************************************************
// Spell: Nature Blessing
// Author: Dark Dragon
// Date: 28 April 2009
//
// Other: New type spell done with vJass (Nature/Heal)
//
//
// Installation:
//
// - 1. Make sure you have opened Jass NewGen as well that you have (0.9.G.1) version of jasshelper or greater
// - 2. Go to Import Manager (F12) and extract icons: BTNNatureBlessing.blp and its DISBTN version
// models: GaiaMissile.mdx, GaiaShield.mdx, NatureExplosion.mdx (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:
// units: Dummy (DD Universal Pack)
// abilities: Nature Blessing
// When you are pasting them to your map make sure you
// type the following rawcodes:
// Dummy: 'dumy'
// Nature Blessing: 'Nbls'
// - 5. If you want different raw codes you must edit the below as well:
// - 6. Edit data below to your own will and enjoy!
// - 7. And of course copy this trigger and DDUniversal Pack to your map.
//
//**************************************************************************************
// *** Nature Blessings code library ***
library NatureBlessing initializer Init_NatureBlessing requires DDUniversalPack
// ####################################################################
// Spell Setup: Nature Blessing
//
// * The following variables (values) are free for you to edit them
// * for your own needs. Read comments before each value
// * and set it to value which will match your map.
// * Enjoy!
// ####################################################################
globals
// This is the main raw code of ability Nature Blessing
// Default: 'Nbls'
private constant integer NATURE_BLESSING = 'Nbls'
// Natures shield effect model path
// Default: "Spells\\NatureBlessing\\GaiaShield.mdx"
private constant string NATURE_SHIELD_EFFECT = "Spells\\NatureBlessing\\GaiaShield.mdx"
// Natures missile effect model path
// Default: "Spells\\NatureBlessing\\GaiaMissle.mdx"
private constant string NATURE_MISSILE_EFFECT = "Spells\\NatureBlessing\\GaiaMissle.mdx"
// Natures explosion model effect path
// Default: "Spells\\NatureBlessing\\NatureExplosion.mdx"
private constant string NATURE_EXPLOSION_EFFECT = "Spells\\NatureBlessing\\NatureExplosion.mdx"
// Natures missile fire sound path (this sound is played when missiles start to move)
// Default: "Abilities\\Spells\\NightElf\\Tranquility\\TranquilityTarget1.wav"
private constant string NATURE_SHOOT_SOUND = "Abilities\\Spells\\NightElf\\Tranquility\\TranquilityTarget1.wav"
private constant real NATURE_SHOOT_SOUND_VOL = 100.
// Nature explosion sound file path (is played when nature explosion is caused)
// Default: "Abilities\\Spells\\NightElf\\Tranquility\\Tranquility.wav"
private constant string NATURE_EXPLOSION_SOUND = "Abilities\\Spells\\NightElf\\Tranquility\\Tranquility.wav"
private constant real NATURE_EXPLOSION_SOUND_VOL = 90.
// This is number of nature missiles created
// Default: 12
private constant integer MISSILES_COUNT = 12
// This is min and max distance from caster to missiles start point
// Default: 400 / 600
private constant real MISSILE_MIN_DISTANCE = 400.
private constant real MISSILE_MAX_DISTANCE = 600.
// This is min and max height of missiles
// Default: 50 / 250
private constant real MISSILE_MIN_HEIGHT = 50.
private constant real MISSILE_MAX_HEIGHT = 250.
// This is missiles value which tells how much time they need
// to reach there max height
// Default: 0.35
private constant real MISSILE_HEIGHT_TIME = 0.35
// This is missiles value which tells how much time they need
// to reach caster
// Default: 0.75
private constant real MISSILE_WIDTH_TIME = 0.75
// This is casters origin height (height at which missiles will be
// at there final point (at casters position))
// Default: 150
private constant real MISSILE_FINAL_HEIGHT = 150.
// --------------------------------------------------------------
// *** Do not edit level variables here edit them below ***
// {
private integer Levels = 0
private real array ExplosionAOE[8191]
private real array ShieldProtect[8191]
private real array HealPercAmount[8191]
private real array SpellDuration[8191]
// }
endglobals
// -----------------------------------------------------------------
// *** Edit level data here ***
private function Setup_NatureBlessing takes nothing returns nothing
// This is the max level of this spell
// Default: 3
set Levels = 3
// This is AOE of explosion "effect"
// Default: 300 / 350 / 410
set ExplosionAOE[1] = 410.
set ExplosionAOE[2] = 530.
set ExplosionAOE[3] = 650.
// Shield protection is amount of damage reduced in percentage
// Default: 15 / 25 / 35
set ShieldProtect[1] = 15.
set ShieldProtect[2] = 25.
set ShieldProtect[3] = 35.
// This is heal amount in percentage per point of life missing
// Default: 5 / 7 / 9
set HealPercAmount[1] = 5.
set HealPercAmount[2] = 7.
set HealPercAmount[3] = 9.
// This is how long spell lasts in seconds
// Default: 10 / 10 / 10
set SpellDuration[1] = 10.
set SpellDuration[2] = 10.
set SpellDuration[3] = 10.
endfunction
// --------------------------------------------------------------------
// *** DO NOT EDIT BELOW IF YOU DONT KNOW JASS ***
// --------------------------------------------------------------------
// -------------------------------------------------
// *** Spells extra globals ***
globals
// This is timers precision (delay) of execution
private constant real TIMER_DELAY = 0.025
private constant real HEAL_PERIOD = 0.1
private constant string ATTACH_POINT = "origin"
private code NatureCode = null
private timer Countdown = null
private integer CastId = 0
private unit TempUnit = null
private trigger TempTrigger = null
private integer TempLevel = 0
endglobals
// ====================================================================
// --- Spells Custom Struct Types ---
// ====================================================================
// --------------------------------------------------------------------
// Type Missile: This is natures raised missile type
// --------------------------------------------------------------------
private struct missile
// *** Structs members ***
private graphic g
private real maxz
private real xy_vel
private real z_vel
private real dx
private real dy
private real dz
private boolean dir
private static constant real MISSILE_SIZE = 1.25
// *** Constructor ***
static method create takes real x, real y, real rad, real dist returns thistype
local thistype this = thistype.allocate()
// *** Associate members with there values ***
set this.g = AddGraphic(NATURE_MISSILE_EFFECT, x, y, 0., rad*bj_RADTODEG, thistype.MISSILE_SIZE)
set this.maxz = GetRandomReal(MISSILE_MIN_HEIGHT, MISSILE_MAX_HEIGHT)
set this.xy_vel = dist / MISSILE_WIDTH_TIME
set this.z_vel = this.maxz / MISSILE_HEIGHT_TIME
set this.dx = Cos(rad)
set this.dy = Sin(rad)
set this.dir = false
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call DestroyGraphic(this.g)
endmethod
// *** Move missile method ***
public method Move takes nothing returns nothing
// *** Only Z movement ***
if (not this.dir) then
call this.g.Move(this.g.GetX(), this.g.GetY(), this.g.GetZ() + this.z_vel * TIMER_DELAY)
// *** Z movement ended here? ***
if (this.g.GetZ() >= this.maxz) then
set this.dir = true
set this.z_vel = (MISSILE_FINAL_HEIGHT - this.maxz) / MISSILE_HEIGHT_TIME
endif
else
// *** XY and Z movement is happening now ***
call this.g.Move(this.g.GetX() + this.xy_vel * this.dx * TIMER_DELAY, this.g.GetY() + this.xy_vel * this.dy * TIMER_DELAY, this.g.GetZ() + this.z_vel * TIMER_DELAY)
endif
endmethod
endstruct
// --------------------------------------------------------------------
// Type Nexus: This is missiles group dynamic array
// --------------------------------------------------------------------
private type nexus extends missile array [MISSILES_COUNT]
// --------------------------------------------------------------------
// Type Nature: Contains all data needed for this spell
// --------------------------------------------------------------------
private struct nature
private unit u
private nexus nx
private integer i
private integer lvl
private trigger t
private triggeraction ta
private static constant integer LAST_EXEC = R2I((MISSILE_HEIGHT_TIME + MISSILE_WIDTH_TIME) / TIMER_DELAY)
private static thistype array ptrAt[8191]
// *** Constructor ***
static method create takes unit u, nexus nx, integer lvl returns thistype
local thistype this = thistype.allocate()
// *** Set our members start value ***
set this.u = u
set this.nx = nx
set this.lvl = lvl
set this.i = 0
set this.t = CreateTrigger()
set this.ta = TriggerAddAction(this.t, NatureCode)
// *** Setup trigger and pointing ***
call TriggerRegisterTimerExpireEvent(this.t, Countdown)
set thistype.ptrAt[ArrayId(this.t)] = (this)
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
local integer i
//! runtextmacro FORX("i", "0", "nexus.size", "call this.nx[i].destroy()")
call TriggerRemoveAction(this.t, this.ta)
call DestroyTrigger(this.t)
set this.t = null
set this.ta = null
set this.u = null
call this.nx.destroy()
endmethod
// *** Moves all missiles in nexus ***
// *** Returns is spell done with movement ***
public method MoveAll takes nothing returns boolean
local integer i = 0
set this.i = this.i + 1
// *** Loop through all missiles and move them ***
loop
exitwhen (i >= nexus.size)
call this.nx[i].Move()
set i = i + 1
endloop
// *** Returns is this last movement ***
return this.i >= thistype.LAST_EXEC
endmethod
// *** Returns this id ***
public static method GetTriggering takes nothing returns thistype
return thistype.ptrAt[ArrayId(GetTriggeringTrigger())]
endmethod
// *** Returns casting unit member (u) ***
public method operator getcaster takes nothing returns unit
return this.u
endmethod
// *** Returns level of nature blessing for caster ***
public method operator getlevel takes nothing returns integer
return this.lvl
endmethod
endstruct
// --------------------------------------------------------------------
// Type Triggerx: Extra type for handling triggers
// --------------------------------------------------------------------
private struct triggerx
public trigger t
public integer i
private triggeraction ta
private static thistype array ptrAt[8191]
// *** Constructor ***
static method create takes code fptr, integer val returns thistype
local thistype this = thistype.allocate()
set this.t = CreateTrigger()
set this.i = val
set this.ta = TriggerAddAction(this.t, fptr)
set this.ptrAt[ArrayId(this.t)] = this
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call TriggerRemoveAction(this.t, this.ta)
call DestroyTrigger(this.t)
set this.t = null
set this.ta = null
endmethod
// *** Returns triggering trigger x ***
static method GetTriggering takes nothing returns thistype
return thistype.ptrAt[ArrayId(GetTriggeringTrigger())]
endmethod
endstruct
// --------------------------------------------------------------------
// Type Protect: Protect holds all data for handling units timed actions
// --------------------------------------------------------------------
private struct protect
private groupex g
private triggerx tx
public boolean done
// *** Constructor ***
static method create takes code fptr, integer lvl returns thistype
local thistype this = thistype.allocate()
// *** Set start values ***
set this.g = CreateGroupEx()
set this.tx = triggerx.create(fptr, this)
set this.done = false
set this.g.value = this
// set this.timeout = SpellDuration[lvl]
// *** Add periodic event ***
set TempTrigger = this.tx.t
call TriggerRegisterAnyUnitDamaged(this.tx.t)
call TriggerRegisterTimerEvent(this.tx.t, HEAL_PERIOD, true)
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call DestroyGroupEx(this.g)
call TriggerUnregisterAnyUnitDamaged(this.tx.t)
call this.tx.destroy()
set this.g = 0
endmethod
// *** returns this group ***
public method operator grp takes nothing returns groupex
return this.g
endmethod
endstruct
// --------------------------------------------------------------------
// Type Unitx: Holds all data for singel unit under shield effect
// --------------------------------------------------------------------
private struct unitx
private unit u
private effect e
public real heal
public real prot
public real timeout
private static thistype array ptrAt[8191]
// *** Constructor ***
static method create takes unit u, effect e, integer lvl returns thistype
local thistype this = thistype.allocate()
set this.u = u
set this.e = e
set this.heal = HealPercAmount[lvl]
set this.prot = ShieldProtect[lvl]
set this.timeout = SpellDuration[lvl]
set thistype.ptrAt[ArrayId(u)] = this
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
set thistype.ptrAt[ArrayId(this.u)] = thistype(0)
call DestroyEffect(this.e)
set this.u = null
set this.e = null
endmethod
// *** Resets this unitxs cast values ***
public method Reset takes integer lvl returns nothing
set this.heal = HealPercAmount[lvl]
set this.prot = ShieldProtect[lvl]
set this.timeout = SpellDuration[lvl]
endmethod
// *** Converts unit to unitx ***
public static method Convert takes unit u returns thistype
return thistype.ptrAt[ArrayId(u)]
endmethod
// *** Gets this unitx life ***
public method operator life takes nothing returns real
return GetWidgetLife(this.u)
endmethod
// *** Gets this unitx max life ***
public method operator maxlife takes nothing returns real
return GetUnitState(this.u, UNIT_STATE_MAX_LIFE)
endmethod
// *** Sets this unitx life ***
public method operator life= takes real hp returns nothing
call SetWidgetLife(this.u, hp)
endmethod
endstruct
// ====================================================================
// *** Spells Custom Functions ***
// ====================================================================
// *** Loads all missiles at spells cast ***
private function LoadMissiles takes real x, real y, nexus nx returns nothing
// *** Load locals ***
local integer i = 0
local real sa = GetRandomReal(0., 6.28319)
local real ma = 6.28319 / nexus.size
local real d = 0.
local real a = 0.
// *** Create all missiles ***
loop
exitwhen (i >= nexus.size)
set d = GetRandomReal(MISSILE_MIN_DISTANCE, MISSILE_MAX_DISTANCE)
set a = sa + GetRandomReal((i-0.5)*ma, (i+0.5)*ma)
set nx[i] = missile.create(x+d*Cos(a), y+d*Sin(a), a-bj_PI, d)
set i = i + 1
endloop
endfunction
// *** Activates timer if it is not jet activated at spells cast ***
private function AttemptActiveTimer takes nothing returns nothing
if (Countdown == null) then
set Countdown = CreateTimer()
call TimerStart(Countdown, TIMER_DELAY, true, null)
endif
set CastId = CastId + 1
endfunction
// *** Dectivates timer if it is activated at spells end cast ***
private function AttemptDeactiveTimer takes nothing returns nothing
if (CastId == 1) then
call PauseTimer(Countdown)
call DestroyTimer(Countdown)
set Countdown = null
endif
set CastId = CastId - 1
endfunction
// *** Enum periodic heal group ***
private function PeriodicEnumUnits takes nothing returns nothing
// *** Define locals ***
local groupex g = GetExecutedGroupEx()
local protect pt = g.value
local unit u = GetEnumUnitEx()
local unitx ux = unitx.Convert(u)
local real x = ux.heal * 0.01 * HEAL_PERIOD
// -------------------------------------------------
// *** Reduce timeout ***
set ux.timeout = ux.timeout - HEAL_PERIOD
// *** Check are we done ***
if (ux.timeout > 0.) then
set pt.done = false
endif
// *** Heal target ***
if (ux.timeout > 0.) then
set ux.life = (ux.maxlife*x) - (ux.life*(x-1.))
else
call GroupExRemoveUnit(g, u)
call ux.destroy()
endif
// *** Clear memory ***
set u = null
endfunction
// *** Performs timed and damage takes actions ***
private function PeriodicProtectActions takes nothing returns nothing
local triggerx tx = triggerx.GetTriggering()
local protect pt = tx.i
local unitx ux
local unit u
// *** If event is taking damage ***
// *** Damage reduction ***
if (GetTriggerEventId() == EVENT_UNIT_DAMAGED) then
set u = GetTriggerUnit()
set ux = unitx.Convert(u)
if (ux == null) then
set u = null
return
endif
set ux.life = ux.life + (GetEventDamage() * ux.prot * 0.01)
// *** If unit died ***
if (IsUnitDead(u)) then
call GroupExRemoveUnit(pt.grp, u)
call ux.destroy()
endif
// *** Clear memory ***
set u = null
else
// *** Was executed by periodic timer ***
// *** Loop throught all units ***
set pt.done = true
call ForGroupEx(pt.grp, function PeriodicEnumUnits)
// *** Is final loop ***
if (pt.done) then
call pt.destroy()
endif
endif
endfunction
// *** Picks all units that can be protected ***
private function PickProtectUnits takes nothing returns boolean
local unit f = GetFilterUnit()
local boolean filter = IsUnitAlly(f, GetOwningPlayer(TempUnit)) and not IsUnitDead(f) and not IsUnitType(f, UNIT_TYPE_STRUCTURE) and not IsUnitType(f, UNIT_TYPE_MECHANICAL)
// *** If unit will pass then perform needed actions ***
if (filter) then
// -----------------------------------------------------
// *** Check is unit already effected by this spell ***
if (unitx.Convert(f) == 0) then
call unitx.create(f, AddSpecialEffectTarget(NATURE_SHIELD_EFFECT, f, ATTACH_POINT), TempLevel)
// call TriggerRegisterUnitEvent(TempTrigger, f, EVENT_UNIT_DAMAGED)
else
debug call Msg(GetUnitName(f))
// --------------------------------------------------------
// *** Reset this spell since unit is already effected ***
call unitx.Convert(f).Reset(TempLevel)
set filter = false
endif
endif
set f = null
return (filter)
endfunction
// *** Main explosion function (causes nature explosion) ***
private function NatureExplosion takes unit u, integer lvl returns nothing
// *** Define locals ***
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local boolexpr bx = Filter(function PickProtectUnits)
local protect pt = protect.create(function PeriodicProtectActions, lvl)
// *** Explosion graphic ***
call DestroyGraphicTimed(AddGraphic(NATURE_EXPLOSION_EFFECT, x, y, 0., GetRandomReal(0., 360.), ExplosionAOE[lvl]/180.), 3.)
// *** Pick all nerby units ***
set TempUnit = u
set TempLevel = lvl
call GroupExEnumUnitsInRange(pt.grp, x, y, ExplosionAOE[lvl], bx)
// *** Play explosion sound ***
call GenericSoundDD(NATURE_EXPLOSION_SOUND, NATURE_EXPLOSION_SOUND_VOL, x, y, 1.8)
// *** Clear memory ***
call DestroyBoolExpr(bx)
set TempTrigger = null
set TempUnit = null
set bx = null
endfunction
// ====================================================================
// *** This is main movement periodic function ***
private function NatureMovement takes nothing returns nothing
local nature nt = nature.GetTriggering()
// *** If movement is done then end it ***
if (nt.MoveAll()) then
// *** Cause explosion now ***
call NatureExplosion(nt.getcaster, nt.getlevel)
// *** If this is the last cast shut down timer ***
call AttemptDeactiveTimer()
// *** Destroy this nature ***
call nt.destroy()
endif
endfunction
// ====================================================================
// *** Spells Main Condition ***
private constant function Conditions takes nothing returns boolean
return GetSpellAbilityId() == NATURE_BLESSING
endfunction
// ====================================================================
// *** Spells Main Actions ***
private function Actions takes nothing returns nothing
// *** Load locals ***
local unit u = GetTriggerUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local integer i = 0
local nexus nx = nexus.create()
// *** Active timer if needed now ***
call AttemptActiveTimer()
// *** Now load all missiles ***
call LoadMissiles(x, y, nx)
// *** Play missile fire sounds ***
call GenericSoundDD(NATURE_SHOOT_SOUND, GetRandomReal(NATURE_SHOOT_SOUND_VOL-10., NATURE_SHOOT_SOUND_VOL), x+500., y, GetRandomReal(1.5, 2.))
call GenericSoundDD(NATURE_SHOOT_SOUND, GetRandomReal(NATURE_SHOOT_SOUND_VOL-10., NATURE_SHOOT_SOUND_VOL), x, y+500., GetRandomReal(1.5, 2.))
call GenericSoundDD(NATURE_SHOOT_SOUND, GetRandomReal(NATURE_SHOOT_SOUND_VOL-10., NATURE_SHOOT_SOUND_VOL), x-500., y, GetRandomReal(1.5, 2.))
call GenericSoundDD(NATURE_SHOOT_SOUND, GetRandomReal(NATURE_SHOOT_SOUND_VOL-10., NATURE_SHOOT_SOUND_VOL), x, y+500., GetRandomReal(1.5, 2.))
// *** Effect adds wings to caster ***
// *** By creating this type we need to have timer activated ***
call nature.create(u, nx, GetUnitAbilityLevel(u, NATURE_BLESSING))
// *** Null locals ***
set u = null
endfunction
// ====================================================================
// --- Main Init Function ---
private function Init_NatureBlessing takes nothing returns nothing
// *** Load Locals ***
local trigger t = CreateTrigger()
// *** Register trigger ***
call TriggerRegisterAnyUnitEvent(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function Conditions))
call TriggerAddAction(t, function Actions)
// *** Preset codes ***
set NatureCode = function NatureMovement
// *** Call users setup ***
call Setup_NatureBlessing()
// *** Preload spells data ***
call Preload(NATURE_SHIELD_EFFECT)
call Preload(NATURE_MISSILE_EFFECT)
call Preload(NATURE_EXPLOSION_EFFECT)
//call Preload(NATURE_SHOOT_SOUND)
//call Preload(NATURE_EXPLOSION_SOUND)
call GenericSoundDD(NATURE_SHOOT_SOUND, 0., 0., 0., 1.)
call GenericSoundDD(NATURE_EXPLOSION_SOUND, 0., 0., 0., 1.)
call PreloadStart()
// *** Null ***
set t = null
endfunction
endlibrary
//TESH.scrollpos=45
//TESH.alwaysfold=0
//**************************************************************************************
// Spell: Water Exhausting
// Author: Dark Dragon
// Date: 3 May 2009
//
// Other: New type spell done with vJass (Water)
//
//
// Installation:
//
// - 1. Make sure you have opened Jass NewGen as well that you have (0.9.G.1) version of jasshelper or greater
// - 2. Go to Import Manager (F12) and extract icons: BTNWaterExhausting.blp and its DISBTN version
// models: WaterExplosion.mdx, WaterFountain.mdx (dummy.mdx -> if not from before)
// sounds: water_splash1.wav, water_splash2.wav, water_splash3.wav and water_wave.wav
// - 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:
// units: Dummy (DD Universal Pack), Water Missile (DD Universal Pack)
// abilities: Water Exhausting, Water Slow (Dummy)
// buffs: Water Exhausting
// When you are pasting them to your map make sure you
// type the following rawcodes:
// Dummy: 'dumy'
// Water Missile: 'wmis'
// Water Exhausting: 'wtex'
// Water Slow (Dummy): 'wslw'
// Water Exhausting (Buff):'bwex'
// - 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 DDUniversal Pack to your map.
//
//**************************************************************************************
library WaterExhausting initializer Init_WaterEx requires DDUniversalPack
// ============================================================================
// Edit this globals to match your own needs
// ============================================================================
globals
// This is main spells raw code
// Default: 'wtex'
private constant integer WATER_EXHAUSTING = 'wtex'
// This raw code is a missile that this spell uses
// Default: 'wmis'
private constant integer WATER_MISSILE = 'wmis'
// Waters slow raw code
// Default: 'wslw'
private constant integer WATER_SLOW = 'wslw'
// This is amount of missiles per cast
// Default: 20
private constant integer MISSILES_AMOUNT = 20
// Follow target means missiles will try to move to there target point
// else they will instantly to there supposed points
// Default: true
private constant boolean FOLLOW_TARGET = true
// All missiles raise time (how much seconds is needed to raise all missiles)
// Default: 1
private constant real RAISE_TIME = 1.
// Path to water damage effect. its loaded on spells explosion
// Default: Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl
private constant string WATER_DAMAGE = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
// Path to water explosion effect
// Default: Spells\\WaterExhausting\\WaterExplosion.mdx
private constant string WATER_EXPLOSION = "Spells\\WaterExhausting\\WaterExplosion.mdx"
// Path to water fountain moving effect
// Default: Spells\\WaterExhausting\\WaterFountain.mdx
private constant string WATER_FOUNTAIN = "Spells\\WaterExhausting\\WaterFountain.mdx"
// Sliding effect on unts which get pushed
// Default: Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl
private constant string WATER_SLIDE = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl"
// Path to water explosion sound
// Default: Custom\\Spells\\Water Exhausting\\water_wave.wav
private constant string WATER_WAVE_SOUND = "Custom\\Spells\\Water Exhausting\\water_wave.wav"
private constant real WATER_WAVE_SOUND_VOL = 65.
// Path to water splash sounds
// Default: Custom\\Spells\\Water Exhausting\\water_splash.wav
private constant string WATER_SPLASH_SOUND_1 = "Custom\\Spells\\Water Exhausting\\water_splash1.wav"
private constant string WATER_SPLASH_SOUND_2 = "Custom\\Spells\\Water Exhausting\\water_splash2.wav"
private constant string WATER_SPLASH_SOUND_3 = "Custom\\Spells\\Water Exhausting\\water_splash3.wav"
private constant real WATER_SPLASH_SOUND_VOL = 80.
// This is explosions count
// Default: 6
private constant integer EXPLOSION_COUNT = 6
// This is explosions min and max movement speed
// Default: 300 / 400
private constant real EXPLOSION_MIN_SPEED = 300.
private constant real EXPLOSION_MAX_SPEED = 400.
// Offset from caster to explosion
// Default: 60
private constant real EXPLOSION_OFFSET = 60.
// This tells should all explosions be created exactlly at constant distance or randomized
// Default: true
private constant boolean RANDOM_EXPLOSION = true
// On explosion whats the push deacceleration per second in game coordinates
// Default: 425
private constant real PUSH_DEACCELERATION = 425.
// ------------------------------------------------------------------------------------
// *** Missile data setup ***
// ------------------------------------------------------------------------------------
// Min xy distance from caster to missile (max distance is spells AoE)
// Default: 75
private constant real MISSILE_MIN_DISTANCE = 75.
// Missiles max height (min height is 0)
// Default: 375
private constant real MISSILE_MAX_HEIGHT = 375.
// Missiles min and max speed (in game coordinates)
// Default: 400 / 1100
private constant real MISSILE_MIN_SPEED = 400.
private constant real MISSILE_MAX_SPEED = 1100.
// Missiles min and max acceleration (in game coordinates per second)
// Default: 550 / 925
private constant real MISSILE_MIN_ACCELERATION = 550.
private constant real MISSILE_MAX_ACCELERATION = 925.
// Level data variables (do not edit them here edit then below)
// {
private integer Levels = 3
private real array WaterAOE[8191]
private real array WaterDamage[8191]
private real array WaterDuration[8191]
private real array WaterMaxDmg[8191]
private real array WaterPushDist[8191]
// }
endglobals
// *** Do edit level data here to your own will ***
private function Setup_WaterExhausting takes nothing returns nothing
// *** Max level of this spell ***
// Default: 3
set Levels = 3
// *** Max water area of effect ***
// Default: 375 / 400 / 440
set WaterAOE[1] = 375.
set WaterAOE[2] = 400.
set WaterAOE[3] = 440.
// *** Waters damage on ground impact ***
// Default: 110 / 185 / 265
set WaterDamage[1] = 110.
set WaterDamage[2] = 185.
set WaterDamage[3] = 265.
// *** Duration of water floating around the caster ***
// Default: 6 / 6 / 6
set WaterDuration[1] = 6.
set WaterDuration[2] = 6.
set WaterDuration[3] = 6.
// *** This is max possible damage per cast ***
// Default: 450 / 775 / 1150
set WaterMaxDmg[1] = 450.
set WaterMaxDmg[2] = 775.
set WaterMaxDmg[3] = 1150.
// *** This is value on how much fare away units will be pushed ***
// Default: 175 / 225 / 280
set WaterPushDist[1] = 175.
set WaterPushDist[2] = 225.
set WaterPushDist[3] = 280.
endfunction
// ==============================================================================
// DO NOT EDIT BELOW IF YOU DONT KNOW JASS
// ==============================================================================
// -----------------------------------------------------------------------------
// *** Other spell globals ***
// -----------------------------------------------------------------------------
globals
// *** Constants ***
private constant integer LOC_RAWCODE = 'Aloc'
private constant integer CROWFORM_RAWCODE = 'Amrf'
private constant real TIMER_DELAY = 0.025
private constant real TWO_PI = 6.28319
private constant real PI_HALF = 1.5708
private constant real SOUND_MAX_DISTANCE = 3500.
private constant integer MAX_UNITS = 128
// *** Globals ***
private timer Countdown = null
private integer CastId = 0
endglobals
// ------------------------------------------------------------------------------
// *** Custom defined water exhausting text macros ***
// ------------------------------------------------------------------------------
// ------------------------------------------------
// *** Quick if then ***
//! textmacro WX_IF takes CONDITION, ACTION
if ($CONDITION$) then
$ACTION$
endif
//! endtextmacro
// ==============================================================================
// *** Define custom WaterEx types / structs ***
// ==============================================================================
// ------------------------------------------------------------------------------
// Type Missile: Main missile type which holds one missile values
// ------------------------------------------------------------------------------
// Members:
// m - main missile unit model
// x - x of missile
// y - y of missile
// z - z of missile
// vel - xy velocity while circling around
// acc - xy acceleration
// adddir - direction of acc which is -1 or 1 meaning is acceleration increasing or decreasing
// dir - as well +1 or -1 which tells is missile moving right or left on circle
// minz - minimum z of missile
// maxz - maximum z of missile
// zvel - z movement speed
// zdir - in z movement is missile going up or down
// rvel - circle radius velocity change
// rdir - radius direction -> moving from caster to end or from end to caster
// rad - radians from caster to missile
// radius - current radius (distance between caster and missile)
private struct missile
private unit m
private real x
private real y
private real z
private real vel
private real acc
private real accdir
private real dir
private real minz
private real maxz
private real zvel
private real zdir
private real rvel
private real rdir
private real rad
private real radius
private integer lvl
// *** Constructor ***
static method create takes player p, real center_x, real center_y, integer lvl returns thistype
local missile this = thistype.allocate()
// *** Associate members with there proper values ***
set this.radius = GetRandomReal(MISSILE_MIN_DISTANCE, WaterAOE[lvl])
set this.rad = GetRandomReal(0., TWO_PI)
set this.dir = (GetRandomInt(0, 1)*2.) - 1.
set this.x = center_x + this.radius * Cos(this.rad)
set this.y = center_y + this.radius * Sin(this.rad)
set this.z = 0.
set this.m = CreateUnit(p, WATER_MISSILE, this.x, this.y, (this.rad+PI_HALF*this.dir)*bj_RADTODEG)
set this.vel = GetRandomReal(MISSILE_MIN_SPEED, MISSILE_MAX_SPEED)
set this.acc = GetRandomReal(MISSILE_MIN_ACCELERATION, MISSILE_MAX_ACCELERATION)
set this.accdir = (GetRandomInt(0, 1)*2.) - 1.
set this.minz = MISSILE_MAX_HEIGHT / GetRandomReal(6., 10.)
set this.maxz = MISSILE_MAX_HEIGHT / GetRandomReal(1., 2.)
set this.zdir = 1.
set this.zvel = GetRandomReal(MISSILE_MIN_SPEED / 3., MISSILE_MAX_SPEED / 3.)
set this.rvel = GetRandomReal(MISSILE_MIN_SPEED / 2.5, MISSILE_MAX_SPEED / 2.5)
set this.rdir = (GetRandomInt(0, 1)*2.) - 1.
set this.lvl = lvl
// *** Setup missile ***
call UnitAddAbility(this.m, CROWFORM_RAWCODE)
call UnitRemoveAbility(this.m, CROWFORM_RAWCODE)
call UnitAddAbility(this.m, LOC_RAWCODE)
call UnitAddAbility(this.m, WATER_SLOW)
call SetUnitAbilityLevel(this.m, WATER_SLOW, this.lvl)
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call KillUnit(this.m)
set this.m = null
endmethod
// *** Move missile ***
public method Move takes real center_x, real center_y, real center_fz returns nothing
// *** locals ***
local real d
local real a
local real x
local real y
// *** Radius movement setup ***
set this.radius = this.radius + (this.rvel*this.rdir)*TIMER_DELAY
if (this.radius >= WaterAOE[this.lvl] or this.radius <= MISSILE_MIN_DISTANCE) then
set this.rdir = -this.rdir
endif
// *** Circle xy movement setup ***
set this.vel = this.vel + this.acc*this.accdir*TIMER_DELAY
if (this.vel >= MISSILE_MAX_SPEED or this.vel <= MISSILE_MIN_SPEED) then
set this.accdir = -this.accdir
endif
set this.rad = this.rad + (this.vel*this.dir*TIMER_DELAY) / this.radius
// *** Circle z movement setup ***
if (this.z >= this.maxz) then
set this.zdir = -1.
elseif (this.z <= this.minz) then
set this.zdir = 1.
endif
// *** Calculate new point for missile ***
set this.x = center_x + this.radius * Cos(this.rad)
set this.y = center_y + this.radius * Sin(this.rad)
set this.z = this.z + this.zvel*this.zdir*TIMER_DELAY
// *** Now move missile ***
if (not FOLLOW_TARGET) then
call SetUnitX(this.m, this.x)
call SetUnitY(this.m, this.y)
call SetUnitFlyHeight(this.m, this.z+center_fz-DD_GetFloorZ(this.x, this.y), 0.)
else
set x = GetUnitX(this.m)
set y = GetUnitY(this.m)
set d = RMinBJ(SquareRoot(Pow(this.x-x, 2.) + Pow(this.y-y, 2.)), this.vel*TIMER_DELAY)
set a = Atan2(this.y-y, this.x-x)
call SetUnitX(this.m, x+d*Cos(a))
call SetUnitY(this.m, y+d*Sin(a))
call SetUnitFlyHeight(this.m, this.z+center_fz-DD_GetFloorZ(this.x, this.y), 0.)
endif
endmethod
// *** Now we will use vel and acc for z movement ***
public method ResetForZ takes nothing returns nothing
set this.vel = 0.
set this.acc = GetRandomReal(MISSILE_MIN_ACCELERATION, MISSILE_MAX_ACCELERATION)
set this.z = GetUnitFlyHeight(this.m)
endmethod
// *** Moves missile (only changing z) returns true when it reaches 0 (ground) ***
public method MoveZ takes nothing returns boolean
set this.vel = this.vel + this.acc*TIMER_DELAY
set this.z = this.z - this.vel*TIMER_DELAY
call SetUnitFlyHeight(this.m, this.z, 0.)
return (this.z <= 0.)
endmethod
// *** Returns missiles x ***
public method GetX takes nothing returns real
return GetUnitX(this.m)
endmethod
// *** Returns missiles y ***
public method GetY takes nothing returns real
return GetUnitY(this.m)
endmethod
endstruct
// ------------------------------------------------------------------------------
// Type Cluster: Group of missiles
// ------------------------------------------------------------------------------
private type cluster extends missile array [MISSILES_AMOUNT]
// ------------------------------------------------------------------------------
// Type WaterEx: This type holds all data needed for water exhausting spell
// ------------------------------------------------------------------------------
private struct waterex
public unit u
public cluster cl
public integer ph
public player p
public real timeout
public trigger t
public triggeraction ta
public integer i
public integer lvl
public real maxdmg
private static thistype array ptrAt[8191]
public static thistype Temp = 0
// *** Constructor ***
static method create takes code func returns thistype
local waterex this = thistype.allocate()
set this.u = GetTriggerUnit()
set this.p = GetOwningPlayer(this.u)
set this.cl = cluster.create()
set this.t = CreateTrigger()
set this.ta = TriggerAddAction(this.t, func)
set this.lvl = GetUnitAbilityLevel(this.u, WATER_EXHAUSTING)
set this.i = 0
set this.timeout = WaterDuration[this.lvl]
set this.maxdmg = WaterMaxDmg[this.lvl]
set thistype.ptrAt[ArrayId(this.t)] = this
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call this.cl.destroy()
call TriggerRemoveAction(this.t, this.ta)
call DestroyTrigger(this.t)
set this.u = null
set this.p = null
set this.t = null
set this.ta = null
endmethod
// *** Casters x ***
public method operator x takes nothing returns real
return GetUnitX(this.u)
endmethod
// *** Casters y ***
public method operator y takes nothing returns real
return GetUnitY(this.u)
endmethod
// *** Casters floor z ***
public method operator fz takes nothing returns real
return DD_GetFloorZ(this.x, this.y)
endmethod
// *** Triggering waterex ***
public static method GetTriggering takes nothing returns thistype
return thistype.ptrAt[ArrayId(GetTriggeringTrigger())]
endmethod
endstruct
// ------------------------------------------------------------------------------
// Type VectorGraphic: Advanced graphic type with a movement addons
// ------------------------------------------------------------------------------
private struct vectorgraphic extends graphic
private real dx
private real dy
private real vel
private real pvel
// *** Constants ***
public static constant real DURATION = 1.
private static constant real SCALE = 1.3
private static constant real FALL_DEGREES = 45.
// *** Constructor ***
static method create takes string file, real x, real y, real rad, real pitch returns thistype
local vectorgraphic this = thistype.allocate(file, x, y, 0., rad*bj_RADTODEG, thistype.SCALE, 90.)
// *** Set members values ***
set this.dx = Cos(rad)
set this.dy = Sin(rad)
set this.vel = GetRandomReal(EXPLOSION_MIN_SPEED, EXPLOSION_MAX_SPEED)
set this.pvel = thistype.FALL_DEGREES / thistype.DURATION
// *** Setup graphic ***
call this.SetPitch(pitch)
return (this)
endmethod
// *** Main Movement method ***
public method MoveEx takes nothing returns nothing
call this.Move(this.GetX()+this.vel*TIMER_DELAY*this.dx, this.GetY()+this.vel*TIMER_DELAY*this.dy, 0.)
call this.SetPitch(this.GetPitch()-this.pvel*TIMER_DELAY)
endmethod
endstruct
// ------------------------------------------------------------------------------
// Type Triggerx: Advanced trigger type
// ------------------------------------------------------------------------------
private struct triggerx
public trigger t
public real timeout
private integer val
private triggeraction ta
private static thistype array ptrAt[8191]
// *** Constructor ***
static method create takes code func, integer val returns thistype
local triggerx this = thistype.allocate()
// *** Set members ***
set this.t = CreateTrigger()
set this.ta = TriggerAddAction(this.t, func)
set this.val = val
set thistype.ptrAt[ArrayId(this.t)] = this
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call TriggerRemoveAction(this.t, this.ta)
call DestroyTrigger(this.t)
set this.ta = null
set this.t = null
endmethod
// *** Triggering triggerx ***
public static method GetTriggering takes nothing returns thistype
return thistype.ptrAt[ArrayId(GetTriggeringTrigger())]
endmethod
// *** Returns this value ***
public method operator value takes nothing returns integer
return this.val
endmethod
// *** Returns true if timeout has reached 0 ***
public method operator done takes nothing returns boolean
return this.timeout <= 0.
endmethod
endstruct
// ------------------------------------------------------------------------------
// Type Visual: Points to multiple vectorgraphic
// ------------------------------------------------------------------------------
private type visual extends vectorgraphic array [EXPLOSION_COUNT]
// ------------------------------------------------------------------------------
// Type Unitx: This type holds data which allows us to move unit in some direction
// ------------------------------------------------------------------------------
private struct unitx
private unit u // Main Unit
private real vel // Current velocity
private real x // Current x
private real y // Currnet y
private real dx // Direction x
private real dy // Direction y
private integer offset // Offset which tells every n+offset loops create sliding effects
public static integer MAX_OFFSET = R2I(0.2 / TIMER_DELAY)
private static thistype array ptrAt[8191]
// *** Constructor ***
static method create takes unit u, real rad, integer lvl returns thistype
local unitx this = thistype.allocate()
// *** Associate members with there proper values ***
set this.u = u
set this.x = GetUnitX(u)
set this.y = GetUnitY(u)
set this.vel = SquareRoot(2.*WaterPushDist[lvl]*PUSH_DEACCELERATION)
set this.dx = Cos(rad)
set this.dy = Sin(rad)
set this.offset = GetRandomInt(0, unitx.MAX_OFFSET)
set thistype.ptrAt[ArrayId(this.u)] = this
// *** Setup unit which is pushed ***
call PauseUnit(this.u, true)
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call PauseUnit(this.u, false)
set this.ptrAt[ArrayId(this.u)] = 0
set this.u = null
endmethod
// *** Safe create tries to check is unit already pushed ***
static method new takes unit u, real rad, integer lvl returns thistype
local thistype this = thistype.ptrAt[ArrayId(u)]
// *** Is valid ***
if (this != null) then
set this.vel = this.vel + SquareRoot(2.*WaterPushDist[lvl]*PUSH_DEACCELERATION)
return Null
endif
// *** It requires new one so create it now ***
return thistype.create(u, rad, lvl)
endmethod
// *** Main move method, returns is this unit done movement ***
public method OffsetMove takes nothing returns boolean
local real x = this.x + this.vel*TIMER_DELAY * this.dx
local real y = this.y + this.vel*TIMER_DELAY * this.dy
local boolean b = not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
// -------------------------
// *** Move here ***
if (b) then
call SetUnitX(this.u, x)
call SetUnitY(this.u, y)
set this.x = x
set this.y = y
endif
set this.vel = this.vel - PUSH_DEACCELERATION*TIMER_DELAY
return not b or this.vel <= 0.
endmethod
// *** Attempts to add sliding effect ***
public method AttemptAddSlide takes integer exec returns nothing
if (QMod(exec+this.offset, thistype.MAX_OFFSET) == 0) then
call DestroyEffect(AddSpecialEffect(WATER_SLIDE, this.x, this.y))
endif
endmethod
// *** Returns triggering unitx ***
public static method GetTriggering takes nothing returns thistype
return thistype.ptrAt[ArrayId(GetTriggerUnit())]
endmethod
endstruct
// ------------------------------------------------------------------------------
// Type Push: Contains all current pushed units(x)
// ------------------------------------------------------------------------------
private type pushcluster extends unitx array [MAX_UNITS]
private struct push
// *** Delegate push cluster ***
delegate pushcluster del
// *** Members ***
private integer i = 0
// *** Destructor ***
method onDestroy takes nothing returns nothing
loop
set this.i = this.i - 1
exitwhen (this.i < 0)
set this.del[this.i] = 0
endloop
endmethod
// *** Next will do push here ***
public method operator next= takes unitx ux returns nothing
set this.del[this.i] = ux
set this.i = this.i + 1
endmethod
// *** Index returns max used index (size) ***
public method operator index takes nothing returns integer
return this.i
endmethod
// *** Swaps the last one with this one ***
public method operator swap= takes integer i returns nothing
set this.i = this.i - 1
set this.del[i] = this.del[this.i]
endmethod
endstruct
// ==============================================================================
// *** Custom spell functions ***
// ==============================================================================
// *** Sets non-3D sound volume for all players ***
private function SetSoundVolumeByDistance takes sound s, integer max_volume, real x, real y returns nothing
local integer i = 0
local real cx
local real cy
local real d
loop
exitwhen (i >= bj_MAX_PLAYERS)
if (GetLocalPlayer() == DD_p[i]) then
// Calculate distance
set cx = GetCameraTargetPositionX()
set cy = GetCameraTargetPositionY()
set d = SquareRoot((cx-x)*(cx-x) + (cy-y)*(cy-y))
// For each player set sound volume depending on distance
call SetSoundVolume(s, max_volume-R2I(d*(I2R(max_volume)/SOUND_MAX_DISTANCE)))
endif
set i = i + 1
endloop
endfunction
// *** Plays generic sound for all players ***
private function PlaySoundEx takes string file, real vol, real pitch, real x, real y returns nothing
local sound s = CreateSound(file, false, false, false, 10, 10, "")
call SetSoundVolumeByDistance(s, R2I(vol*1.27), x, y)
call SetSoundPitch(s, pitch)
call StartSound(s)
call KillSoundWhenDone(s)
set s = null
endfunction
// *** Attempts to active main timer ***
private function AttemptActiveTimer takes nothing returns nothing
if (Countdown == null) then
set Countdown = CreateTimer()
call TimerStart(Countdown, TIMER_DELAY, true, null)
endif
set CastId = CastId + 1
endfunction
// *** Attempts to deactivate main timer ***
private function AttemptDeactiveTimer takes nothing returns nothing
set CastId = CastId - 1
if (CastId == 0) then
call PauseTimer(Countdown)
call DestroyTimer(Countdown)
set Countdown = null
endif
endfunction
// *** Timer Storage ***
private function SetTimerValue takes timer t, integer val returns nothing
call DDStoreValue(t, val)
endfunction
// *** Timer Get Stored ***
private function GetTimerValue takes timer t returns integer
return DDGetValue(t)
endfunction
// *** Clear timer value ***
private function FlushTimerValue takes timer t returns nothing
call DDFlushValue(t)
endfunction
// *** Random splash sound ***
private function RndSplash takes nothing returns string
local integer i = GetRandomInt(1, 3)
if (i == 1) then
return WATER_SPLASH_SOUND_1
elseif (i == 2) then
return WATER_SPLASH_SOUND_2
endif
return WATER_SPLASH_SOUND_3
endfunction
// *** Creates effects around target ***
private function WaterBlow takes real x, real y returns nothing
local integer i
local real rad
local real d = 100.
//! runtextmacro FOR("i", "0", "4")
set rad = (i-0.5)*PI_HALF
call DestroyGraphic(AddGraphic(WATER_DAMAGE, x+d*Cos(rad), y+d*Sin(rad), 0., 270., 0.4))
//! runtextmacro ENDFOR()
endfunction
// -----------------------------------------------------------------
// *** Pushes all units in push group ***
private function PushEnumUnits takes nothing returns nothing
// *** Get locals ***
local triggerx tx = triggerx.GetTriggering()
local push ph = tx.value
local integer exec = GetTriggerExecCount(tx.t)
local integer i
// *** Here loop and move all ***
//! runtextmacro FOR("i", "0", "ph.index")
// *** If its movement is done ***
if(ph[i].OffsetMove()) then
// *** Reduce counter by one ***
call ph[i].destroy()
set ph.swap = i
else
call ph[i].AttemptAddSlide(exec)
endif
//! runtextmacro ENDFOR()
// *** Did we finish? ***
if (ph.index == null) then
call tx.destroy()
call ph.destroy()
call AttemptDeactiveTimer()
endif
endfunction
// *** This is periodic explosion movement ***
private function ExplosionMovement takes nothing returns nothing
// *** Define locals ***
local triggerx tx = triggerx.GetTriggering()
local visual vs = tx.value
local integer i
// *** Reduce timeout ***
set tx.timeout = tx.timeout - TIMER_DELAY
// *** Preform actions for all vectorgraphics ***
//! runtextmacro FORX("i", "0", "visual.size", "call vs[i].MoveEx()")
debug call Msg(R2S(vs[0].GetPitch()))
// *** Check are we done ***
if (tx.done) then
// --- Clear memory and end the spell ***
//! runtextmacro FORX("i", "0", "visual.size", "call vs[i].destroy()")
call vs.destroy()
call tx.destroy()
call AttemptDeactiveTimer()
endif
endfunction
// *** Loads all visual vector graphics ***
private function LoadVisual takes real x, real y returns visual
// *** Locals ***
local visual vs = visual.create()
local triggerx tx = triggerx.create(function ExplosionMovement, integer(vs))
local real srad = GetRandomReal(0., TWO_PI)
local integer i
local real rad
local real d = TWO_PI / visual.size
// *** Setup Triggerx ***
set tx.timeout = vectorgraphic.DURATION
// *** Load all visuals ***
//! runtextmacro FOR("i", "0", "visual.size")
//! runtextmacro IF("RANDOM_EXPLOSION", "set rad = srad + GetRandomReal((i-0.5)*d, (i+0.5)*d)", "set rad = srad + i*d")
set vs[i] = vectorgraphic.create(WATER_EXPLOSION, x+EXPLOSION_OFFSET*Cos(rad), y+EXPLOSION_OFFSET*Sin(rad), rad, GetRandomReal(65., 80.))
//! runtextmacro ENDFOR()
// *** Now to our triggerx register periodic movement ***
call TriggerRegisterTimerExpireEvent(tx.t, Countdown)
return (vs)
endfunction
// ------------------------------------------------------------
// *** Main water explosion actions ***
private function PickEnumUnits takes nothing returns nothing
local unit f = GetFilterUnit()
local waterex wx = waterex.Temp
local unitx ux
local real dmg
// *** Check is valid unit ***
if (not IsUnitDead(f) and IsUnitEnemy(f, wx.p) and not IsUnitType(f, UNIT_TYPE_STRUCTURE) and not IsUnitType(f, UNIT_TYPE_MECHANICAL) and not IsUnitType(f, UNIT_TYPE_MAGIC_IMMUNE)) then
// *** Its valid unit ***
// *** Now get damage and clear buffs ***
set dmg = rmin(rmin(WaterDamage[wx.lvl], wx.maxdmg), GetWidgetLife(f))
call UnitRemoveBuffs(f, true, false)
if (dmg > 0.) then
call WaterBlow(GetUnitX(f), GetUnitY(f))
call UnitDamageTarget(wx.u, f, WaterDamage[wx.lvl], true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
set wx.maxdmg = wx.maxdmg - dmg
endif
// *** Try to add this unit to push group ***
set ux = unitx.new(f, Atan2(GetUnitY(f)-GetUnitY(wx.u), GetUnitX(f)-GetUnitX(wx.u)), wx.lvl)
if (ux != null) then
set push(wx.ph).next = ux
endif
endif
set f = null
endfunction
private function WaterExplosion takes waterex wx returns nothing
// *** Load locals ***
local triggerx tx
local push ph
// *** Pick all nerby units ***
set ph = push.create()
set wx.ph = ph
set waterex.Temp = wx
call groupex.FilterRadius(wx.x, wx.y, WaterAOE[wx.lvl], function PickEnumUnits)
// *** If any unit was added then we must push all of them ***
if (ph.index != null) then
set tx = triggerx.create(function PushEnumUnits, ph)
// *** Make sure to use timer ***
call AttemptActiveTimer()
// *** Register periodic movement event ***
call TriggerRegisterTimerExpireEvent(tx.t, Countdown)
else
// *** If not then just blow it up ***
call ph.destroy()
endif
// *** Load effects ***
call DestroyGraphicTimed(AddGraphic(WATER_EXPLOSION, wx.x, wx.y, 0., GetRandomReal(0., 360.), WaterAOE[wx.lvl] / 300.), 3.)
// *** Now load visual graphics ***
call LoadVisual(wx.x, wx.y)
// *** Clear leaks ***
call wx.destroy()
endfunction
// ==============================================================================
// ==============================================================================
//
// * Main coding of spell is below
// * It contains main actions and conditions
// * As well as looping functions executed by main trigger
//
// ==============================================================================
// ==============================================================================
// ==============================================================================
// *** Main Looping Functions ***
// *** Main movement of missiles ***
private function PeriodicMovement takes nothing returns nothing
// *** Load locals ***
local waterex wx = waterex.GetTriggering()
local real x = wx.x
local real y = wx.y
local real fz = wx.fz
local integer i
// -------------------------------------------------------------------------
// *** Do loop here and move each missile ***
// *** This checks did time run out, if so then do fall all missiles ***
// *** If we still have time simple move all missiles around ***
// -------------------------------------------------------------------------
if (wx.timeout > 0.) then
//! runtextmacro FOR("i", "0", "wx.i")
call wx.cl[i].Move(x, y, fz)
//! runtextmacro WX_IF("wx.timeout <= TIMER_DELAY", "call wx.cl[i].ResetForZ()")
//! runtextmacro ENDFOR()
set wx.timeout = wx.timeout - TIMER_DELAY
//! runtextmacro WX_IF("wx.timeout <= 0.", "call PlaySoundEx(WATER_WAVE_SOUND, WATER_WAVE_SOUND_VOL, 1.25, x, y)")
else
// *** Preform actions for all falling missiles ***
//! runtextmacro FOR("i", "0", "wx.i")
if (wx.cl[i].MoveZ()) then
// --- Play splash sound and add death effect ---
call PlaySoundEx(RndSplash(), WATER_SPLASH_SOUND_VOL, 1., x, y)
call DestroyGraphicTimed(AddGraphic(WATER_FOUNTAIN, wx.cl[i].GetX(), wx.cl[i].GetY(), 0., 0., 1.), 2.5)
//call DDDestroyEffectTimed(AddSpecialEffect(WATER_FOUNTAIN, wx.cl[i].GetX(), wx.cl[i].GetY()), 2.5)
// --- Missile reached ground destroy it ***
call wx.cl[i].destroy()
// --- Decrease counter ---
set wx.i = wx.i - 1
// --- Swap missiles ---
set wx.cl[i] = wx.cl[wx.i]
set wx.cl[wx.i] = missile(0)
endif
//! runtextmacro ENDFOR()
endif
// *** Now check did we finish our movement ***
// *** Are remaining missiles equal to 0 ***
//! runtextmacro WX_IF("wx.i == 0 and wx.timeout <= 0.", "call WaterExplosion(wx)")
endfunction
// *** Main summon of missiles ***
private function RaiseMissiles takes nothing returns nothing
// *** Get locals ***
local timer t = GetExpiredTimer()
local waterex wx = GetTimerValue(t)
// *** Add new missile ***
set wx.cl[wx.i] = missile.create(wx.p, wx.x, wx.y, wx.lvl)
// *** Increase counter ***
set wx.i = wx.i + 1
// *** If this is the last one then destroy timer ***
if (wx.i == MISSILES_AMOUNT) then
call FlushTimerValue(t)
call PauseTimer(t)
call DestroyTimer(t)
endif
// *** Null locals here ***
set t = null
endfunction
// ==============================================================================
// *** Main Trigger Conditions ***
private constant function Conditions takes nothing returns boolean
return GetSpellAbilityId() == WATER_EXHAUSTING
endfunction
// ==============================================================================
// *** Main Trigger Actions ***
private function Actions takes nothing returns nothing
// *** Define locals ***
local waterex wx = waterex.create(function PeriodicMovement)
local timer t = CreateTimer()
// *** Attempt to load our timer here ***
call AttemptActiveTimer()
// *** Register period event ***
call TriggerRegisterTimerExpireEvent(wx.t, Countdown)
// *** Now to our local timer store this waterex ***
call SetTimerValue(t, wx)
call TimerStart(t, RAISE_TIME / MISSILES_AMOUNT, true, function RaiseMissiles)
// *** Null locals ***
set t = null
endfunction
// ==============================================================================
// *** Init Function ***
private function Init_WaterEx takes nothing returns nothing
// *** Allocate locals ***
local trigger t = CreateTrigger()
// *** Do event register, add condition and action ***
call TriggerRegisterAnyUnitEvent(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function Conditions))
call TriggerAddAction(t, function Actions)
// *** Setup user data ***
call Setup_WaterExhausting()
// *** Preload files ***
call Preload(WATER_EXPLOSION)
call Preload(WATER_DAMAGE)
call Preload(WATER_FOUNTAIN)
call Preload(WATER_SLIDE)
//call Preload(WATER_SPLASH_SOUND_1)
//call Preload(WATER_SPLASH_SOUND_2)
//call Preload(WATER_SPLASH_SOUND_3)
//call Preload(WATER_WAVE_SOUND)
call GenericSoundDD(WATER_SPLASH_SOUND_1, 0., 0., 0., 1.)
call GenericSoundDD(WATER_SPLASH_SOUND_2, 0., 0., 0., 1.)
call GenericSoundDD(WATER_SPLASH_SOUND_3, 0., 0., 0., 1.)
call GenericSoundDD(WATER_WAVE_SOUND, 0., 0., 0., 1.)
call Preload("Abilities\\Weapons\\Missiles\\WaterElementalMissile\\WaterElementalMissile.mdx")
call Preload("Textures\\WaterBlobs1.blp")
call Preload("Textures\\RibbonBlur1.blp")
call Preload("Textures\\White_64_Foam1.blp")
call Preload("Textures\\WaterWake3.blp")
call Preload("Textures\\Dust3x.blp")
call Preload("Textures\\Clouds8x8.blp")
call Preload("Textures\\Bubble.blp")
call Preload("Textures\\ShockwaveWater1.blp")
call PreloadStart()
// *** Clear leaks ***
set t = null
endfunction
endlibrary
//TESH.scrollpos=461
//TESH.alwaysfold=0
//**************************************************************************************
// Spell: Instinct Flash
// Author: Dark Dragon
// Date: 29 June 2009
//
// Other: First spell made using my new library v2
//
//
// Installation:
//
// - 1. Make sure you have opened Jass NewGen Editor as well that you have (0.9.H.3) version of jasshelper or greater
// - 2. 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
// - 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:
// units: Illusion Blademaster (if not from before, however replace old one with this new one)
// abiliti: Instinct Flash
// When you are pasting them to your map make sure you
// type the following rawcodes:
// Illusion Blademaster: 'ssib'
// Instinct Flash: 'InFl'
// - 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 DDUniversal Pack to your map.
//
//**************************************************************************************
library InstinctFlash initializer Init_InstinctFlash requires DDUniversalPack
// ########################################################################
// *** User spell setup values ***
// ########################################################################
globals
// This is the raw code of the spell
// Default: InFl
private constant integer INSTINCT_FLASH = 'InFl'
// This is the raw code of illusion
// Default: ssib
private constant integer ILLUSION_RAWCODE = 'ssib'
// ------------------------------------------------------------------------
// *** spells SFX setup ***
// ------------------------------------------------------------------------
// Model file of spells motion sfx
// Default: Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl
private constant string MOTION_SFX = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl"
// When unit falls in water area
// Default: Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl
private constant string WATER_SFX = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
// When unit falls in ground (dust) area
// Default: Objects\\SpawnQModels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl
private constant string DUST_SFX = "Objects\\SpawnModels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
// Blood effect file
// Default: Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl
private constant string BLOOD_SFX = "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
// ------------------------------------------------------------------------
// *** Spells sound files ***
// ------------------------------------------------------------------------
// Sound on cast event
// Default: Custom\\Spells\\Instinct Flash\\sword_draw.wav
private constant string SOUND_DRAW = "Custom\\Spells\\Instinct Flash\\sword_draw.wav"
private constant real SOUND_DRAW_VOL = 85.
// Sound on start damage dealing (movement)
// Default: Custom\\Spells\\Instinct Flash\\sword_echo.wav
private constant string SOUND_ECHO = "Custom\\Spells\\Instinct Flash\\sword_echo.wav"
private constant real SOUND_ECHO_VOL = 45.
// Sound when dealing damage 1
// Default: Custom\\Spells\\Instinct Flash\\sword_dmg1.wav
private constant string SOUND_DMG1 = "Custom\\Spells\\Instinct Flash\\sword_dmg1.wav"
private constant real SOUND_DMG1_VOL = 85.
// Sound when dealing damage 2
// Default: Custom\\Spells\\Instinct Flash\\sword_dmg2.wav
private constant string SOUND_DMG2 = "Custom\\Spells\\Instinct Flash\\sword_dmg2.wav"
private constant real SOUND_DMG2_VOL = 85.
// Sound when killing target
// Default: Custom\\Spells\\Instinct Flash\\sword_chop.wav
private constant string SOUND_CHOP = "Custom\\Spells\\Instinct Flash\\sword_chop.wav"
private constant real SOUND_CHOP_VOL = 85.
// ------------------------------------------------------------------------
// *** Beginning spell illusions setup ***
// ------------------------------------------------------------------------
// Max illusion creation distance (height)
// Default: 300
private constant real ILLUSION_HEIGHT = 300.
// Max illusion creation distance (width)
// Default: 550
private constant real ILLUSION_WIDTH = 550.
// This is duration of illusion in seconds
// Default: 0.4
private constant real ILLUSION_DURATION = 0.4
// Start velocity of illusions
// Default: 100.
private constant real ILLUSION_SVEL = 100.
// 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 )
private constant real ILLUSION_MOTION = 50.
// This is animation of illusion which should be played
// Default: 9 (stand ready)
private constant integer ILLUSION_ANIMATION = 9 // (1.4)
// ------------------------------------------------------------------------
// *** spells center setup ***
// ------------------------------------------------------------------------
// Units which die! should they be pushed?
// Default: true
private constant boolean ALLOW_KNOCKBACK = true
// --------------------------------------------
// This only works if knockback is enabled!
// Defines travel distance of units which are pushed
// Default: 400
private constant real PUSH_DIST = 400.
// Defines travel height of units which are pushed
// Default: 180
private constant real PUSH_HEIGHT = 180.
// Defines start push speed
// Default: 900
private constant real PUSH_VEL = 900.
// ------------------------------------------------------------------------
// *** (End / movement) spell setup ***
// ------------------------------------------------------------------------
// Speed of caster while moving
// Default: 2500
private constant real CASTER_SPEED = 2500.
// This is casters end animation
// Default: 8 (attack 2)
private constant integer CASTER_EANIM = 8 // (1.134)
// Duration of casters end animation
// Default: 0.5
private constant real CASTER_EANIM_DURATION = 0.5
// Casters end animation speed in percentage
// Default: 100*1.134/0.5 (1.134 = animation duration, 0.5 seconds to finish animation)
private constant real CASTER_EANIM_MOTION = 226.8
// This is the area of damage effect
// Default: 135
private constant real DAMAGE_AREA = 135.
// How much movement steps are passed to create a new illusion in movement
// Default: 4
private constant integer ILLUSION_LOAD_SKIP = 4
// Maximum number of illusions at same time
// Default: 6
private constant integer MAX_ILLUSIONS = 6
// ---------------------------------------------------------------------
//
// *** Do not edit this level data variables here
// *** Edit them below in setup function
//
// {
private real array DamageAmount[8191]
private dd_color Color
// }
endglobals
// *** Edit level data below ***
private function InstinctFlash_Setup takes nothing returns nothing
// *** This is the max level of the spell ***
// Default: 3
//! set MaxLevel = 3
// *** This is the damage per level ***
// Default: 175 / 285 / 400
set DamageAmount[1] = 175.
set DamageAmount[2] = 285.
set DamageAmount[3] = 400.
// *** This is the color of illusions ***
// Arguments: red, green, blue, alpha (from 0 to 255)
// Default: 125, 125, 240, 255
set Color = CreateColor(125, 125, 240, 255)
endfunction
// ==============================================================================
// DO NOT EDIT BELOW IF YOU DONT KNOW JASS
// ==============================================================================
// -------------------------------------------
// *** Globals ***
// -------------------------------------------
globals
// This is push timers delay
private constant real TIMER_DELAY = 0.025
// Offset z
private constant real SFX_Z = 65.
// Scale
private constant real SFX_SCALE = 0.7
// FogModifier half length
private constant real FOGM_LEN = 250.
// Effects move distance (how fare away will it be moved)
private constant real EFFECT_DISTANCE = 350.
// Non-Constants
private rect TempRect = null
private timer Tim = null
private groupex GrpX = Null
private real VelOffset = 0.
private integer PtrCnt = 0
private unit array PtrUnit [8191]
endglobals
// -------------------------------------------------------------------------------
//
// *** Type Instant Slash ***
//
//
// * Type islash: Main data type which holds all data requied to perform
// timed action.
//
// -------------------------------------------------------------------------------
// *** Define islash ***
private struct islash
// ----------------------------------
// *** Members load ***
//
//
// * u -> Main unit (caster)
// * rc -> Rectangle in which units will be damaged
// * v -> Vector from cast point to start point
// * p -> Casters owner
// * dmg -> How much damage is supposed to be dealt max
// * dist -> Distance from caster to target point
// * gx -> Units which where damaged
// * ecount -> Current execution count
public unit u
public real sx
public real sy
public real tx
public real ty
public real rad
public player p
public real dmg
public real dist
public groupex gx
public integer ecount
public fogmodifier fm
// *** Static members ***
public static islash Temp = Null
// ================================
// --- Constructor ---
static method create takes unit u, real x, real y, real rad, integer lvl, rect r returns thistype
// *** Locals ***
local islash this = islash.allocate()
// *** Assign members with there values ***
set this.u = u
set this.sx = GetUnitX(u)
set this.sy = GetUnitY(u)
set this.tx = x
set this.ty = y
set this.rad = rad
set this.p = GetOwningPlayer(u)
set this.dmg = DamageAmount[lvl]
set this.dist = SquareRoot(hypot(this.sx-x, this.sy-y))
set this.gx = CreateGroupEx()
set this.ecount = 0
set this.fm = CreateFogModifierRect(this.p, FOG_OF_WAR_VISIBLE, r, true, false)
// *** Constr actions ***
call FogModifierStart(this.fm)
// *** Return this ***
return (this)
endmethod
// ================================
// --- Destructor ---
method delete takes nothing returns nothing
call DestroyGroupEx(this.gx)
call FogModifierStop(this.fm)
call DestroyFogModifier(this.fm)
set this.u = null
set this.fm = null
call this.destroy()
endmethod
endstruct
// -------------------------------------------------------------------------------
//
// *** Type Knockback Data ***
//
//
// * Type kdata: Main data type which holds all data requied for units movement
//
// -------------------------------------------------------------------------------
private struct kdata
// ------------------------
// *** Structs members ***
//
public unit u
public dd_vector v
public dd_parab p
public float d
public real fz
// *** Add pointers to this struct ***
implement optional dd_pointer
// *** Constructor define ***
//! runtextmacro constr("create", "unit f, real dist, real height, real rad")
local real x = GetUnitX(f)
local real y = GetUnitY(f)
// *** assign members ***
set this.u = f
set this.v = dd_vector.create(x, y, 0., rad, 0.)
set this.p = dd_parab.create(dist, height)
set this.d = float.new()
set this.fz = DD_GetFloorZ(x, y) + GetUnitFlyHeight(f)
// *** pointer to unit ***
call this.SetPtrObj(f)
//! runtextmacro endconstr()
// *** Destructor define ***
//! runtextmacro destr()
// *** Clear pointer ***
call this.ClrPtrObj(this.u)
// *** Null and clear memory ***
set this.u = null
call this.v.delete()
call this.p.delete()
call this.d.delete()
//! runtextmacro enddestr()
endstruct
// ------------------------------------------------------------------------------
// Custom spell defined functions
// ------------------------------------------------------------------------------
// --------------------------------------------------------------------------
// *** Convert acceleration ***
private constant function ConvAcc takes real v, real d, real t returns real
// *** Simple physics formula for acceleration convertion ****
// *** Requires: v = velocity, d = distance, t = time
return (2.*(d-v*t))/(t*t)
endfunction
// --------------------------------------------------------------------------
// *** Convert rectangle region ***
private function ConvRect takes real sx, real sy, real ex, real ey, real rad returns rect
local real d = SquareRoot(hypot(sx-ex, sy-ey))
local double x = CreateDouble(4)
local double y = CreateDouble(4)
set x[0] = sx+FOGM_LEN*Cos(rad-PI_HALF)
set y[0] = sx+d*FOGM_LEN*Sin(rad-PI_HALF)
set x[1] = sx-d*0.5*Cos(rad-PI_HALF)
set y[1] = sx-d*0.5*Sin(rad-PI_HALF)
set x[2] = x[0]+d*Cos(rad)
set y[2] = y[0]+d*Sin(rad)
set x[3] = x[1]+d*Cos(rad)
set y[3] = y[1]+d*Sin(rad)
set TempRect = Rect(DMin(x), DMin(y), DMax(x), DMax(y))
call DestroyDouble(x)
call DestroyDouble(y)
return (TempRect)
endfunction
// --------------------------------------------------------------------------
// *** Pointer Unit Works ***
//
private function U2I takes unit u returns integer
set PtrUnit[PtrCnt] = u
set PtrCnt = PtrCnt + 1
return (PtrCnt)
endfunction
// *** Get unit from pointer ***
private constant function I2U takes integer ptr returns unit
return PtrUnit[ptr-1]
endfunction
// *** Flush I2U pointer ***
private function FreeI2U takes integer ptr returns nothing
set PtrUnit[ptr-1] = PtrUnit[PtrCnt-1]
set PtrUnit[PtrCnt-1] = null
set PtrCnt = PtrCnt - 1
endfunction
// --------------------------------------------------------------------------
// *** Timed units knockback ***
private function PushUnitsEnum takes nothing returns nothing
// *** Locals ***
local integer i = 0
local kdata kd = Null
local real x = 0.
local real y = 0.
local string file
local location l
// *** Loop through all units and push them ***
//! runtextmacro FOR("i", "1", "GrpX.size+1")
// *** Get k data ***
set kd = kdata.GetPtrObj(GrpX[i])
// *** Now do calculations for unit movement ***
set x = kd.v.x + VelOffset * kd.v.dx
set y = kd.v.y + VelOffset * kd.v.dy
call kd.d.Add(VelOffset)
call kd.v.Move(x, y, kd.fz-DD_GetFloorZ(x, y)+kd.p.GetHeight(kd.d.val))
// *** Add blood effects ***
if (kd.d.val < kd.p.max_dist and rmod(kd.d.val, 75.) + 25. < VelOffset) then
set l = GetUnitLoc(kd.u)
call E_AddSpecialEffectZ(BLOOD_SFX, l, kd.v.z)
call DestroyEffect( bj_lastCreatedEffect )
call RemoveLocation(l)
set l = null
endif
// *** Now move unit ***
call SetUnitVPos(kd.u, kd.v)
// *** Did movement end? ***
if (kd.d.val >= kd.p.max_dist) then
// *** Now check if units height is stil high ***
//! runtextmacro IFX("GetUnitFlyHeight(GrpX[i]) > 10.", "call SetUnitFlyHeight(GrpX[i], 0., PUSH_VEL)")
// *** Play damage sound ***
//! runtextmacro IF("GetRandomInt(0, 1) == 1", "set file = SOUND_DMG1", "set file = SOUND_DMG2")
set bj_lastPlayedSound = CreateSound(file, false, false, false, 12700, 12700, "")
call SetSoundVolumeAs3D(bj_lastPlayedSound, x, y, SOUND_DMG1_VOL, 3500.)
call StartSound(bj_lastPlayedSound)
call KillSoundWhenDone(bj_lastPlayedSound)
// *** Check which point is it ***
if (IsPointWater(x, y)) then
call DestroyEffect( AddSpecialEffect(WATER_SFX, x, y) )
else
call DestroyEffect( AddSpecialEffect(DUST_SFX, x, y) )
endif
// *** Unit reached end movement ***
call kd.delete()
call GrpX.RemoveUnitByIndex(i)
// *** Decrease counter ***
set i = i - 1
endif
//! runtextmacro ENDFOR()
// *** Check do we need to change movement ***
if (GrpX.first == null) then
// *** Clear mem ***
call DestroyGroupEx(GrpX)
call PauseTimer(Tim)
call DestroyTimer(Tim)
// *** Clear vm structs ***
set Tim = null
set GrpX = Null
endif
endfunction
// --------------------------------------------------------------------------
// *** Enum damage units ***
private function ISlashDealDamage takes nothing returns nothing
// *** Locals ***
local unit f = GetFilterUnit()
local islash s = islash.Temp
local groupex gx
local cluster cl
local real x
local real y
local real rad
local real time
local real life
local real d
local real a
local integer i
local string file
local location l
// *** Filter the units ***
if (not GroupExContains(s.gx, f) and not IsUnitType(f, UNIT_TYPE_STRUCTURE) and not IsUnitDead(f) and IsUnitEnemy(f, s.p) and not IsUnitType(f, UNIT_TYPE_MECHANICAL) and IsUnitType(f, UNIT_TYPE_GROUND)) then
// *** Now add it to group ***
call GroupExAddUnit(s.gx, f)
// *** Store life ***
set life = GetWidgetLife(f)
// *** Explode? ***
if (life <= s.dmg*0.375) then
call SetUnitExploded(f, true)
set life = -1.
endif
// *** Deal damager here ***
call UnitDamageTarget(s.u, f, s.dmg, true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
// *** Do math calcs here for sfx movement ***
set x = GetUnitX(f)
set y = GetUnitY(f)
set rad = Atan2(y-s.sy, x-s.sx)
set time = 0.01 + ( (-CASTER_SPEED*0.1 + SquareRoot(rpow(CASTER_SPEED*0.1, 2) + CASTER_SPEED*EFFECT_DISTANCE)) / (CASTER_SPEED*0.5) )
// *** Load objects needed for movement ***
set gx = CreateGroupEx()
set cl = CreateCluster()
// *** Sfx add here (graphic effect) ***
// *** First one ***
call GroupExAddUnit(gx, CreateUnit(DD_p[0xF], DD_DUMMY_CODE, x, y, GetRandomReal(0., 360.)))
call MakeUnitFlyable(gx[1])
call MakeUnitUnselectable(gx[1])
call SetUnitFlyHeight(gx[1], SFX_Z, 0.)
call SetUnitScale(gx[1], SFX_SCALE, SFX_SCALE, SFX_SCALE)
call ClusterAddEffect(cl, AddSpecialEffectTarget(MOTION_SFX, gx[1], "origin"))
call PushUnit(gx[1], CASTER_SPEED*0.1, CASTER_SPEED*0.5, CASTER_SPEED, EFFECT_DISTANCE, rad+GetRandomReal(0., PI/6.), PI/24., LineData())
// *** Second one ***
call GroupExAddUnit(gx, CreateUnit(DD_p[0xF], DD_DUMMY_CODE, x, y, GetRandomReal(0., 360.)))
call MakeUnitFlyable(gx[2])
call MakeUnitUnselectable(gx[2])
call SetUnitFlyHeight(gx[2], SFX_Z, 0.)
call SetUnitScale(gx[2], SFX_SCALE, SFX_SCALE, SFX_SCALE)
call ClusterAddEffect(cl, AddSpecialEffectTarget(MOTION_SFX, gx[2], "origin"))
call PushUnit(gx[2], CASTER_SPEED*0.1, CASTER_SPEED*0.5, CASTER_SPEED, EFFECT_DISTANCE, rad-GetRandomReal(0., PI/6.), PI/24., LineData())
// *** Now do timed memory clear ***
call DestroyClusterTimed(cl, time)
call E_DestroyGroupExTimed(gx, time+1.)
// *** Add blood effects ***
if (life != -1.) then
// *** Unit took nice damage, add blood ***
call DestroyEffect( AddSpecialEffectTarget(BLOOD_SFX, f, "origin") )
call DestroyEffect( AddSpecialEffectTarget(BLOOD_SFX, f, "chest") )
call DestroyEffect( AddSpecialEffectTarget(BLOOD_SFX, f, "head") )
else
// *** Unit took heavy damage, explode and add area of blood effects ***
//! runtextmacro FOR("i", "1", "5")
set d = GetRandomReal(0., 50.)
set a = GetRandomReal(0., 2.*PI)
set l = Location(x+d*Cos(a), y+d*Sin(a))
call E_AddSpecialEffectZ(BLOOD_SFX, l, GetRandomReal(10., 65.))
call DestroyEffect( bj_lastCreatedEffect )
call RemoveLocation(l)
//! runtextmacro ENDFOR()
// *** Clear buffer ***
set l = null
endif
// ====================================================================
// *** Now after effects are created we check is target dead ***
// *** As well we check is knockback allowed ***
if (IsUnitDead(f) and ALLOW_KNOCKBACK and life != -1.) then
// *** Play kill sound ***
set bj_lastPlayedSound = CreateSound(SOUND_CHOP, false, false, false, 12700, 12700, "")
call SetSoundVolumeAs3D(bj_lastPlayedSound, x, y, SOUND_CHOP_VOL, 3500.)
call StartSound(bj_lastPlayedSound)
call KillSoundWhenDone(bj_lastPlayedSound)
// *** Allow flyability ***
call UnitAddAbility(f, 'Amrf')
call UnitRemoveAbility(f, 'Amrf')
// *** Push target here ***
call kdata.create(f, PUSH_DIST, PUSH_HEIGHT, rad)
// *** Check is group loaded ***
if (GrpX == Null) then
// *** Load objects ***
set GrpX = CreateGroupEx()
set Tim = CreateTimer()
// *** Execute timer ***
call TimerStart(Tim, TIMER_DELAY, TRUE, function PushUnitsEnum)
endif
// *** Add filterd unit to push group ***
call GrpX.AddUnit(f)
endif
elseif (not IsUnitDead(f)) then
// *** Unit has divine armor? ***
//! runtextmacro IFX("life == -1.", "call SetUnitExploded(f, false)")
// *** Play damage sound ***
//! runtextmacro IF("GetRandomInt(0, 1) == 1", "set file = SOUND_DMG1", "set file = SOUND_DMG2")
set bj_lastPlayedSound = CreateSound(file, false, false, false, 12700, 12700, "")
call SetSoundVolumeAs3D(bj_lastPlayedSound, x, y, 100., 3500.)
call StartSound(bj_lastPlayedSound)
call KillSoundWhenDone(bj_lastPlayedSound)
endif
// *** Null locals ***
set f = null
endfunction
// -------------------------------------------------------------
// *** Resets caster ***
private function ResetCaster takes nothing returns nothing
local unit u = I2U(GetSleepData())
call PauseUnit(u, false)
call SetUnitAnimation(u, "stand")
call SelectUnitForPlayerSingle(u, GetOwningPlayer(u))
call FreeI2U(GetSleepData())
set u = null
endfunction
// -------------------------------------------------------------------------
// *** Vector Data ***
// * v[0] = start pos
// * v[1] = where unit is moved
// * f[0] = type_cast<islash>
// * b = exit when movement is done
//
private keyword fPushCaster
private function fPushCaster takes vector_d vd, real x returns nothing
// *** Locals ***
local islash s = islash(vd.f[0])
local real t
// *** Increase execution counter ***
set s.ecount = s.ecount + 1
// *** Move our main vector ***
call vd.v[1].Move(vd.v[0].x + x * vd.v[0].dx, vd.v[0].y + x * vd.v[0].dy, 0.)
// *** Load illusions ***
if (QMod(s.ecount, ILLUSION_LOAD_SKIP) == 0) then
set t = MAX_ILLUSIONS*ILLUSION_LOAD_SKIP*TimerGetTimeout(GetExpiredTimer())
set bj_lastCreatedUnit = CreateUnit(DD_p[0xF], ILLUSION_RAWCODE, vd.v[1].x, vd.v[1].y, vd.v[0].alpha*RADTODEG)
call UnitAddAbility(bj_lastCreatedUnit, 'Aloc')
call E_FadeUnit(bj_lastCreatedUnit, t, 60., 100.)
call RemoveUnitTimed(bj_lastCreatedUnit, t)
call SetUnitAnimationByIndex(bj_lastCreatedUnit, CASTER_EANIM)
call SetUnitTimeScale(bj_lastCreatedUnit, CASTER_EANIM_MOTION*0.01)
set bj_lastCreatedUnit = null
endif
// *** Now damage picked unit ***
set islash.Temp = s
call groupex.FilterRadius(vd.v[1].x, vd.v[1].y, DAMAGE_AREA, function ISlashDealDamage)
// *** End caster movement here ***
if (x >= s.dist) then
// *** Caster setup ***
call SetUnitTimeScale(s.u, 1.)
call SleepEx(0.5, function ResetCaster, U2I(s.u))
// *** Clear Memory ***
call s.delete()
set vd.f[0] = 0
// *** End Movement ***
set vd.b = true
endif
endfunction
// ---------------------------------------------------------
// *** Generates new vector data ***
private function PushData takes islash s returns vector_d
local vector_d vd = vector_d.create()
set vd.vf = fPushCaster
set vd.v[1] = dd_vector.new()
set vd.f[0] = float(s)
set vd.b = false
return (vd)
endfunction
// -------------------------------------------------------------------------
// *** Does actions after time ***
// -------------------------------------------------------------------------
private function AfterTimeAction takes nothing returns nothing
// *** Load timed locals ***
local timer t = GetExpiredTimer()
local islash s = DDGetValue(t)
// *** Caster Setup ***
call ShowUnit(s.u, true)
call E_FadeUnit(s.u, CASTER_EANIM_DURATION, 70., 0.)
call PauseUnit(s.u, true)
call SetUnitAnimationByIndex(s.u, CASTER_EANIM)
call SetUnitTimeScale(s.u, CASTER_EANIM_MOTION*0.01)
// *** Push the caster ***
call PushUnit(s.u, CASTER_SPEED, 0., CASTER_SPEED, s.dist, s.rad, 0., PushData(s))
// -----------------------------------------
// *** Play kill sound ***
set bj_lastPlayedSound = CreateSound(SOUND_ECHO, false, false, false, 12700, 12700, "")
call SetSoundVolumeAs3D(bj_lastPlayedSound, GetUnitX(s.u)+s.dist*0.5*Cos(s.rad), GetUnitY(s.u)+s.dist*0.5*Sin(s.rad), SOUND_ECHO_VOL, 3500.)
call StartSound(bj_lastPlayedSound)
call KillSoundWhenDone(bj_lastPlayedSound)
endfunction
// -------------------------------------------------------------------------
// *** Illusions which are created in rectangle ***
// *** Are faded here ***
// -------------------------------------------------------------------------
private keyword FadeIllusions
private function FadeIllusions takes hashtable table, real trans returns nothing
local unit u = LoadUnitHandle(table, 0, 0)
call SetUnitVertexColor(u, Color.r, Color.g, Color.b, R2I(trans*2.55))
if (trans >= 40.) then
call DestroyEffect(LoadEffectHandle(table, 0, 1))
call RemoveUnit(u)
endif
set u = null
endfunction
// ------------------------------------------------
// *** Loads an new illusion ***
private function CreateIllusion takes real x, real y, real rad, real s_trans, real e_trans returns unit
local hashtable ht = InitHashtable()
set bj_lastCreatedUnit = CreateUnit(DD_p[0xF], ILLUSION_RAWCODE, x, y, rad*RADTODEG)
call UnitAddAbility(bj_lastCreatedUnit, 'Aloc')
call SaveUnitHandle(ht, 0, 0, bj_lastCreatedUnit)
call SaveEffectHandle(ht, 0, 1, AddSpecialEffectTarget(MOTION_SFX, bj_lastCreatedUnit, "origin"))
call FadeObject(ht, s_trans, e_trans, ILLUSION_DURATION, FadeIllusions)
call SetUnitTimeScale(bj_lastCreatedUnit, ILLUSION_MOTION*0.01)
call SetUnitAnimationByIndex(bj_lastCreatedUnit, ILLUSION_ANIMATION)
set ht = null
return (bj_lastCreatedUnit)
endfunction
// -----------------------------------------------------------------------------
// *** Loads and moves all illusions ***
//
private function LoadIllusions takes real x, real y, real rad returns nothing
// -------------------------
// *** Locals ***
//
// * i -> Loop integer
// * w -> Current width
// * h -> Current parabolic height of current width (height takes width from 0 to ILLUSION_WIDTH)
// Since our parabola looks in different direction its ILLUSION_HEIGHT - parab_height...
// * d -> Hypotenuse of (width, height) or distance from top of parabola to current point of parabola
// * acc -> Is acceleration of illusion movement
// * tp -> Temp parabola (main parabola which is defined by (WIDTH, HEIGHT) of illusion movement)
// * p -> Temp non-permanent parabola which changes for each illusion
local integer i
local real w
local real h
local real d
local real acc
local real r
local dd_parab tp = dd_parab.create(ILLUSION_WIDTH, ILLUSION_HEIGHT)
local dd_parab p = dd_parab.new()
local unit array u
// *** Main loop through all illusions
//! runtextmacro FOR("i", "0", "3")
// *** Do main math calculations here ***
set w = i*(ILLUSION_WIDTH / 6.)
set h = ILLUSION_HEIGHT - tp.GetHeight(w)
set w = w - ILLUSION_WIDTH*0.5
set d = SquareRoot(w*w + h*h)
set r = Atan(w/h)
// *** Create illusion ***
set u[0] = CreateIllusion( x+d*Cos(r+rad), y+d*Sin(r+rad), rad, 10., 40.)
set u[1] = CreateIllusion( x+d*Cos(r+rad), y+d*Sin(r+rad), rad, 10., 40.)
// *** Change parabola scale for each illusion ***
call p.SetScale(-w, h)
// *** Setup other values ***
set acc = ConvAcc(ILLUSION_SVEL, p.max_len/2., ILLUSION_DURATION)
// *** Push enum illusions ***
call PushUnit(u[0], ILLUSION_SVEL, acc, ILLUSION_SVEL+(acc*ILLUSION_DURATION), p.max_len/2., rad+PI_HALF, 0., ParabolaData(p, 0.))
call PushUnit(u[1], -ILLUSION_SVEL, acc, -(ILLUSION_SVEL+(acc*ILLUSION_DURATION)), -p.max_len/2., rad+PI_HALF, 0., ParabolaData(p, p.max_len))
//! runtextmacro ENDFOR()
// *** Load center illusion ***
call CreateIllusion(x, y, rad, 10., 40.)
// *** Clear memory here ***
call tp.delete()
call p.delete()
set u[0] = null
set u[1] = null
endfunction
// ==============================================================================
// Main spell functions
// ==============================================================================
// ---------------------------------------------------------------
// *** Condition ***
private constant function Conditions takes nothing returns boolean
return GetSpellAbilityId() == INSTINCT_FLASH
endfunction
// ---------------------------------------------------------------
// *** Action ***
private function Actions takes nothing returns nothing
// ---------------------------------
// *** Declare locals ***
//
local unit u = GetTriggerUnit()
local location l = GetSpellTargetLoc()
local timer t = CreateTimer()
local real x1 = GetUnitX(u)
local real y1 = GetUnitY(u)
local real x2 = GetLocationX(l)
local real y2 = GetLocationY(l)
local real rad = Atan2(y2-y1, x2-x1)
local islash is = Null
// -----------------------------------
// *** Main unit actions ***
call SetUnitFacing(u, rad*RADTODEG)
call ShowUnit(u, false)
// -----------------------------------------
// *** Load illusions ***
call LoadIllusions(x1, y1, rad)
// -----------------------------------------
// *** Play start (draw sword) sound ***
set bj_lastPlayedSound = CreateSound(SOUND_DRAW, false, false, false, 12700, 12700, "")
call SetSoundVolumeAs3D(bj_lastPlayedSound, x1, y1, SOUND_DRAW_VOL, 3500.)
call StartSound(bj_lastPlayedSound)
call KillSoundWhenDone(bj_lastPlayedSound)
// --------------------------------------------
// *** Load islash and after time actions ***
set is = islash.create(u, x2, y2, rad, GetUnitAbilityLevel(u, INSTINCT_FLASH), ConvRect(x1, y1, x2, y2, rad))
call DDStoreValue(t, integer(is))
call TimerStart(t, ILLUSION_DURATION, false, function AfterTimeAction)
// *** Clear leaks ***
call RemoveRect(TempRect)
call RemoveLocation(l)
set TempRect = null
set t = null
set l = null
set u = null
endfunction
// *** Main map startup function ***
private function Init_InstinctFlash takes nothing returns nothing
// *** Load locals ***
local trigger t = CreateTrigger()
local dd_parab p
// *** Event / cond / act registering ***
call TriggerRegisterAnyUnitEvent(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddConditionEx(t, function Conditions)
call TriggerAddAction(t, function Actions)
// *** Define users setup ***
call InstinctFlash_Setup()
// *** Preload files ***
call Preload(MOTION_SFX)
call Preload(WATER_SFX)
call Preload(DUST_SFX)
call Preload(BLOOD_SFX)
//call Preload(SOUND_ECHO)
//call Preload(SOUND_CHOP)
//call Preload(SOUND_DRAW)
//call Preload(SOUND_DMG1)
//call Preload(SOUND_DMG2)
call GenericSoundDD(SOUND_ECHO, 0., 0., 0., 1.)
call GenericSoundDD(SOUND_CHOP, 0., 0., 0., 1.)
call GenericSoundDD(SOUND_DRAW, 0., 0., 0., 1.)
call GenericSoundDD(SOUND_DMG1, 0., 0., 0., 1.)
call GenericSoundDD(SOUND_DMG2, 0., 0., 0., 1.)
call Preload("Textures\\Ghost2.blp")
call Preload("Textures\\Flame4.blp")
call Preload("Textures\\OrcBloodTailParticle0.blp")
// *** Calculate velocity offset ***
set p = dd_parab.create(PUSH_DIST, PUSH_HEIGHT)
set VelOffset = TIMER_DELAY*PUSH_VEL*PUSH_DIST/p.max_len
call p.delete()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//**************************************************************************************
// Spell: Terror Blade
// Author: Dark Dragon
// Date: 8 June 2009
//
// Other: First spell / passive made using my new library v2
//
//
// Installation:
//
// - 1. Make sure you have opened Jass NewGen Editor as well that you have (0.9.G.3) version of jasshelper or greater
// - 2. Go to Import Manager (F12) and extract icons: BTNTerrorBlade.blp, DISBTN, PAS and DISPAS versions
// models: (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:
// units: Dummy (DD Universal Pack)
// 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:
// Dummy: 'dumy'
// 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 DDUniversal Pack to your map.
//
//**************************************************************************************
// ======================================================================
// * Spell: Terror Blade
// * Library Load (Whole code that spell needs is inside the library)
// ======================================================================
library TerrorBlade initializer Init_TerrorBlade requires DDUniversalPack
// ---------------------------------------------------------------------
//
// *** 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 ***
// --------------------------------------------
globals
// --- The raw code of spell ---
// Default: Trbl
private constant integer TERROR_BLADE = 'Trbl'
// --- The raw code of the spells dummy effect ---
// Default: Tble
private constant integer TERROR_BLADE_EFFECT = 'Tble'
// --- The raw code of buff ---
// Default: Btbl
private constant integer TERROR_BLADE_BUFF = 'Btbl'
// --- The corruption effect path ---
// Default: Abilities\\Spells\\Other\\HowlOfTerror\\HowlTarget.mdl
private constant string CORRUPTION_EFFECT = "Abilities\\Spells\\Other\\HowlOfTerror\\HowlTarget.mdl"
// --- The darkness effect path ---
// Default: Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl
private constant string DARKNESS_EFFECT = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
// --- This is the terror movement effect path ---
// Default: Abilities\\Spells\\Other\\HowlOfTerror\\HowlCaster.mdl
private constant string TERROR_EFFECT = "Abilities\\Spells\\Other\\HowlOfTerror\\HowlCaster.mdl"
// --- Tells can this spell be triggered even if target is already effected ---
// Default: false
private constant boolean CAN_TRIGGER_AT_SAME_TIME = false
// --- Can this spell effect undead-units ---
// Default: true
private constant boolean CAN_EFFECT_UNDEAD_UNITS = true
// --- Show floating text ---
// Default: true
private constant boolean SHOW_FLOATING_TEXT = true
// --- Where is an effect attached on caster ---
// Default: weapon
private constant string CASTER_ATTACH_POINT = "weapon"
// --- Which dummy ability should be an order ---
// Default: innerfire
private constant string ISSUE_ORDER = "innerfire"
// --- Terrors effect start z offset ---
// Default: 65
private constant real TERROR_Z = 65.
// --- Terrors effect size in percentage ---
// Default: 100
private constant real TERROR_SIZE = 100.
// --- Terror effects speed in coordinates ---
// Default: 475
private constant real TERROR_SPEED = 475.
// --------------------------------------------------------------------
// * Level data do not edit here edit them below in function setup *
//
// {
private real array Duration [8191]
private integer array Chance [8191]
private real array ExtraDamage [8191]
// *** Special non-level data ***
private strings TargetAPoint = Null
private dd_color FTColor = Null
// }
endglobals
// -------------------------------------------------------------
// *** Level data is configured here ***
// -------------------------------------------------------------
private function TerrorBlade_Setup takes nothing returns nothing
// --- This is the max level of the spell ---
// Default: 3
//! set MaxLevel = 3
// --- This is duration (how long the effect lasts per level) ---
// Default: 10 / 10 / 10
set Duration[1] = 10.
set Duration[2] = 10.
set Duration[3] = 10.
// --- Extra damage per level on triggering ---
// Default: 10 / 20 / 30
set ExtraDamage[1] = 10.
set ExtraDamage[2] = 20.
set ExtraDamage[3] = 30.
// --- Chance in percentage to trigger this ability per level ---
// Default: 15 / 15 / 15
set Chance[1] = 15
set Chance[2] = 15
set Chance[3] = 15
// -----------------------------------------
// --- 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...
set TargetAPoint = strings.create()
set TargetAPoint[1] = "origin"
set TargetAPoint[2] = "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 / Alpha (Alpha of 255 is fully visible and 0 is invisible)
// Default: 153 / 34 / 102 / 255
set FTColor = CreateColor(153, 34, 102, 255)
endfunction
// ==============================================================================
// *** DO NOT EDIT BELOW IF YOU DONT KNOW JASS ***
// ==============================================================================
// ------------------------------------------------------------------
// *** Spells Main Globals ***
// ------------------------------------------------------------------
globals
// *** Constant values ***
private constant integer GENTIM_CODE = 'BTLF'
private constant integer LOC_CODE = 'Aloc'
private code TerrorEndAction = null
endglobals
// ----------------------------------------------------------------------
//
// *** Terror Blade: Custom types declare section ***
//
// --------------------
// * List of types:
// --------------------
//
// * Type 'terror': Main target type data while effected by terror
// ----------------------------------------------------------------------
// *** Declare terror struct ***
private struct terror
// ---------------------------------------------------------------------------------------
// ---- ----
// * Terror Members:
// ---- ----
//
// * u -> Main target unit which is effected by terror
// * t -> Main timer which is counting time on how long is he effected by terror
// * c -> Holds effects stored on target unit effected by terror
//
// ---------------------------------------------------------------------------------------
private unit u
private timer t
private cluster c
// ----------------------------------------------------------
// * Modules are added here:
//
// * dd_pointer -> Points to this struct
// ----------------------------------------------------------
implement optional dd_pointer
// ---------------------------------------------------------
//
// ---- ----
// * Terror Methodes:
// ---- ----
//
// * Constructor()
// * ~Destructor()
// * Swap() -> Main method to replace data if spell
// is activated at same time
//
// --------------------
// * Operators
// --------------------
//
// * target -> Returns main unit
// (target which is effected by terror)
// ---------------------------------------------------------
// ---------------------------------------------
// *** Main constructor of the terror ***
//! runtextmacro constr("create", "unit u, real timeout, cluster c")
// *** Assign members to there values ***
set this.u = u
set this.t = CreateTimer()
set this.c = c
// *** Pointer works ***
call this.SetPtrObj(this.u)
call this.SetPtrObj(this.t)
// *** Setup members ***
call TimerStart(this.t, timeout, false, TerrorEndAction)
//! runtextmacro endconstr()
// ---------------------------------------------
// *** Main destructor of the terror ***
//! runtextmacro destr()
// *** Free ***
call PauseTimer(this.t)
call DestroyTimer(this.t)
call DestroyCluster(this.c)
// *** Pointer release ***
call this.ClrPtrObj(this.t)
call this.ClrPtrObj(this.u)
// *** Mem Null ***
set this.t = null
set this.u = null
set this.c = Null
//! runtextmacro enddestr()
// *** Swaps data if needed (if its target is effected at same time) ***
public static method Swap takes unit u, real newtime returns boolean
// *** Get this terror ***
local terror this = terror.GetPtrObj(u)
// *** If its invalid means spell did not start jet ***
//! runtextmacro IFX("this == null", "return false")
// *** Do swap here ***
call PauseTimer(this.t)
call TimerStart(this.t, newtime, false, TerrorEndAction)
return true
endmethod
// *** Returns target of terror effect ***
public method operator target takes nothing returns unit
return (this.u)
endmethod
endstruct
// ---------------------------------------------------------
// *** Terror Main Functions ***
// ---------------------------------------------------------
// *** This is a main function which defines end of spell ***
private function EndTerrorAction takes nothing returns nothing
// -------------------------------
// *** Load data at pointer ***
//
local terror tr = terror.GetPtrObj(GetExpiredTimer())
// *** Now we need to remove main effect on this target ***
call UnitRemoveAbility(tr.target, TERROR_BLADE_BUFF)
// *** Blow terror data now ***
call tr.delete()
endfunction
// ------------------------------------------------------
// *** Main Conditions ***
// ------------------------------------------------------
private function Conditions takes nothing returns boolean
// *** Condition locals ***
// * Declare local variable and read it insted of each time calling function *
local integer lvl = GetUnitAbilityLevel(GetEventDamageSource(), TERROR_BLADE)
local unit u = GetTriggerUnit()
local boolean cond = GetRandomChance(lvl:Chance)
// *** Detect some of users setup ***
//! runtextmacro IFX("not CAN_TRIGGER_AT_SAME_TIME", "set cond = cond and GetUnitAbilityLevel(u, TERROR_BLADE_BUFF) == 0")
//! runtextmacro IFX("not CAN_EFFECT_UNDEAD_UNITS", "set cond = cond and not IsUnitType(u, UNIT_TYPE_UNDEAD)")
// *** Set condition vars ***
set cond = cond and IsDamageTypeMelee() and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitEnemy(u, GetOwningPlayer(GetEventDamageSource())) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(u, UNIT_TYPE_MECHANICAL)
set u = null
// *** Return result ***
return (cond)
endfunction
// ------------------------------------------------------
// *** Main Actions ***
// ------------------------------------------------------
private function Actions takes nothing returns nothing
// --------------------------------------------------------
// *** Action Locals ***
//
// * u -> Main unit (attacker)
// * v -> Victim (unit which took damage)
// * lvl -> Level of terror blade ability for attacker
// * x -> X of attacked unit
// * y -> Y of attacked unit
// * x2 -> X of attacker
// * y2 -> Y of attacker
// * d -> Dummy unit
// * c -> Cluster (effects which are on target)
// * clu -> Additional cluster for darkness effects destroying
// * g -> Main terror movement graphic
// * i -> Loop integer index
local unit u = GetEventDamageSource()
local unit v = GetTriggerUnit()
local integer lvl = GetUnitAbilityLevel(u, TERROR_BLADE)
local real x = GetUnitX(v)
local real y = GetUnitY(v)
local real x2 = GetUnitX(u)
local real y2 = GetUnitY(u)
local real rad = Atan2(y-y2, x-x2)
local unit d = null
local cluster c = 0
local cluster clu = 0
local graphic g = 0
local integer i
// *** Ability was triggerd so now we want to change the seed ***
// *** In order to be more sure that ability is not triggered ***
// *** Every time (at same time) but to trigger in other ***
// *** period of time. We will use Quick Modulo for nice ***
// *** randomizing algorithm ***
//call SetRandomSeed( QMod(GetRandomInt(100, 1000000), GetRandomInt(63, 837)) )
// *** Now deal extra damage and load texttag if user wants ***
call UnitDamageTarget(u, v, lvl:ExtraDamage, true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
if (SHOW_FLOATING_TEXT) then
// *** Load floating text here ***
call CreateTextTagUnit(v, "+"+I2S(R2I(lvl:ExtraDamage))+"!", 10., 3., FTColor)
endif
// *** Load dummy and set him up for main spell event ***
set d = CreateUnit(GetOwningPlayer(u), DD_DUMMY_CODE, x, y, 0.)
call UnitAddAbility(d, TERROR_BLADE_EFFECT) // Adding main ability
call UnitAddAbility(d, LOC_CODE) // Making dummy unselectable
call UnitApplyTimedLife(d, GENTIM_CODE, 0.5) // Make dummy disappear in half of second
call SetUnitAbilityLevel(d, TERROR_BLADE_EFFECT, lvl) // Set level of dummy ability to level of attackers terror blade
// *** Dummy is set up for the cast ***
// *** Do cast now ***
call IssueTargetOrder(d, ISSUE_ORDER, v)
// *** Load caster darkness effect and cluster ***
set clu = CreateCluster()
call ClusterAddEffect( clu, AddSpecialEffectTarget(DARKNESS_EFFECT, u, CASTER_ATTACH_POINT) )
// *** Load terror movement effect from caster to target ***
set g = AddGraphic(TERROR_EFFECT, x2, y2, TERROR_Z, rad*RADTODEG, TERROR_SIZE*0.01)
call SetGraphicFacing(g, rad, PI)
// *** Now push graphic to caster ***
call PushGraphic(g, TERROR_SPEED, 0., TERROR_SPEED, TERROR_SPEED*2.5, rad, 0., LineData())
// *** This graphic destroy after time ***
call DestroyGraphicTimed(g, 2.5)
// ----------------------------------------------------
// *** Now do check if target is already effected ***
// ----------------------------------------------------
// *** If it cant swap (it means target is not jet effected) ***
// *** so effect target with terror now ***
if (not terror.Swap(v, lvl:Duration)) then
// *** Here load effects on target and cluster ***
set c = CreateCluster()
//! runtextmacro FOR("i", "0", "S2I(TargetAPoint[0])")
call ClusterAddEffect( clu, AddSpecialEffectTarget(DARKNESS_EFFECT, v, TargetAPoint[i+1]) )
call ClusterAddEffect( c, AddSpecialEffectTarget(CORRUPTION_EFFECT, v, TargetAPoint[i+1]) )
//! runtextmacro ENDFOR()
// *** Finally load the terror ***
call terror.create(v, lvl:Duration, c)
endif
// ---------------------------------------
// *** Clear extra loaded effects ***
call DestroyClusterTimed(clu, 1.25)
// ---------------------------------------
// *** Spell ends here so clear mem ***
//
set u = null
set v = null
set d = null
endfunction
// =========================================================================
// *** When unit dies actions ***
private function onDeath takes nothing returns boolean
// *** Locals ***
local terror tr = terror.GetPtrObj(GetTriggerUnit())
// *** Is unit under terror effect? ***
if (tr != null) then
// *** Remvove buff ***
call UnitRemoveAbility(tr.target, TERROR_BLADE_BUFF)
// *** Free stuff ***
call tr.delete()
endif
// *** dummy return ***
return (false)
endfunction
// =========================================================================
private function Init_TerrorBlade takes nothing returns nothing
// *** Init locals ***
local trigger t = CreateTrigger()
local integer i
// ---------------------------------------------------
// *** Register Event, Add Condition and Action ***
call TriggerRegisterAnyUnitDamaged( t )
call TriggerAddConditionEx( t, function Conditions )
call TriggerAddAction( t, function Actions )
// ---------------------------------------------------
// *** Register Event, Add Action To onDeath Trigger ***
set t = CreateTrigger()
call TriggerRegisterAnyUnitEvent( t, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddConditionEx( t, function onDeath )
// ---------------------------
// *** Call users setup ***
call TerrorBlade_Setup()
// --- Get attach points n ---
//! runtextmacro FORX("i", "1", "strings.size", "exitwhen (TargetAPoint[i] == null)")
set TargetAPoint[0] = I2S(i-1)
// *** Terror Code Set ***
set TerrorEndAction = function EndTerrorAction
// ---------------------------
// *** Preload used files ***
call Preload(CORRUPTION_EFFECT)
call Preload(DARKNESS_EFFECT)
call Preload(TERROR_EFFECT)
call Preload("Textures\\firering2A.blp")
call Preload("Textures\\GenericGlow2b.blp")
call Preload("Textures\\GenericGlowFaded.blp")
call Preload("Abilities\\Weapons\\AvengerMissile\\Ghost1Mod.blp")
call Preload("Abilities\\Spells\\Other\\HowlOfTerror\\Skull1.blp")
call Preload("Abilities\\Spells\\Other\\HowlOfTerror\\HowlOfTerror.wav")
call Preload("Abilities\\Weapons\\AvengerMissile\\DestroyerMissile.wav")
endfunction
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
//**************************************************************************************
// Spell: Black Magic
// Author: Dark Dragon
// Date: 18 July 2009
//
// Other: Spell made testing new features of my new library v2
//
//
// Installation:
//
// - 1. Make sure you have opened Jass NewGen Editor as well that you have (0.9.I.2) version of jasshelper or greater
// - 2. Go to Import Manager (F12) and extract icons: BTNBlackMagic.blp and DISBTN versions
// models: (dummy.mdx -> if not from before), (Frostmourne.mdx -> if not from before)
// sounds: sword_start.wav, sword_cut.wav and sword_disappear.wav (if not already imported 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:
// units: Dummy (DD Universal Pack)
// abilities: Black Magic and Black Magic (Dummy)
// buffs: Black Magic (Buff)
// When you are pasting them to your map make sure you
// type the following rawcodes:
// Dummy: 'dumy'
// Black Magic: 'Bmgc'
// Black Magic (Dummy): 'blrt'
// Black Magic (Buff): 'Bbmg'
// - 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 DDUniversal Pack to your map.
//
//**************************************************************************************
// =============================================================
// *** Spell: Black Magic Library ***
library BlackMagic initializer Init requires DDUniversalPack
// ####################################################################
// ::: USER SETUP PART :::
// ####################################################################
globals
// Black Magic spell raw code
// Default: Bgmc
private constant integer BMAGIC_RAWCODE = 'Bmgc'
// Swords raw code
// Default: drks
private constant integer SWORD_RAWCODE = 'drks'
// Dummy roots raw code
// Default: blrt
private constant integer BROOTS_RAWCODE = 'blrt'
// Dummy roots buff raw code
// Default: Bbmg
private constant integer BROOTS_BUFF_RAWCODE = 'Bbmg'
// Lightning type ***
// Default: DRAM
private constant string LIGHTNING_TYPE = "DRAM"
// Model file of sword
// Default: war3mapImported\\Frostmourne.mdx
private constant string SWORD_MODEL_FILE = "war3mapImported\\Frostmourne.mdx"
// Blood effect
// Default: Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl
private constant string BLOOD_EFFECT = "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
// Blood effects attach point to target
// Default: origin
private constant string ATTACH_POINT = "origin"
// Dummy ability order id
// Default: entanglingroots
private constant string DUMMY_ORDER = "entanglingroots"
// Sound sword on fire (main swords start sound)
// Default: Custom\\Spells\\Dark Torture\\sword_start.wav
private constant string SOUND_ON_START = "Custom\\Spells\\Dark Torture\\sword_start.wav"
private constant real SOUND_ON_START_VOL = 85.
// Sound sword deal damage (swords sound while dealing damage)
// Default: Custom\\Spells\\Dark Torture\\sword_cut.wav
private constant string SOUND_ON_STAB = "Custom\\Spells\\Dark Torture\\sword_cut.wav"
private constant real SOUND_ON_STAB_VOL = 70.
// Sound sword disappear (when sword disappears)
// Default: Custom\\Spells\\Dark Torture\\sword_disappere.wav
private constant string SOUND_ON_DISAPPEAR = "Custom\\Spells\\Dark Torture\\sword_disappere.wav"
private constant real SOUND_ON_DISAPPEAR_VOL = 90.
// Extra swords distance from target
// Default: 300.
private constant real EXTRA_SWORDS_OFFSET = 300.
// Pitch angle of extra swords
// Default: 45
private constant integer EXTRA_SWORDS_PITCH = 45
// Extra swords appear / disappear time in seconds
// Default: 0.9
private constant real EXTRA_SWORDS_APPEAR_TIME = 0.9
// Spells interval
// Default: 0.025
private constant real INTERVAL = 0.025
// Swords speed
// Default: 1000.
private constant real SWORD_SPEED = 1000.
// Every n intervals load blades old motion
// Default: 8
private constant integer BLADE_MOTION_FACTOR = 8
// Units stab z offset
// Default: 70.
private constant real STAB_Z_OFFSET = 70.
// Main swords height
// Default: 65.
private constant real SWORD_Z = 65.
// Level data should be edited below in function setup, not here
// {
private dd_color LightningColor = Null
private integer array NumberOfSwords
private real array SwordDamage
private real array SwordDamageExtra
private real array RootDuration
// }
endglobals
// -------------------------------------------------------------
// *** User spell level setup ***
private function Setup takes nothing returns nothing
// Max Spell Level
// Default: 3
//! set MaxLevel = 3
// Main swords damage on hit (percentage of targets max_life)
// Default: 20% / 25% / 30%
set SwordDamage[1] = 20.
set SwordDamage[2] = 25.
set SwordDamage[3] = 30.
// Extra swords damage
// Default: 50 / 60 / 65
set SwordDamageExtra[1] = 50.
set SwordDamageExtra[2] = 60.
set SwordDamageExtra[3] = 65.
// Duration of blood root
// Default: 2. / 2.75 / 4.
set RootDuration[1] = 2.00
set RootDuration[2] = 2.75
set RootDuration[3] = 4.00
// Number of smaller swords created per level
// Number of swords must be less or equal to 16
// Default: 3 / 3 / 4
set NumberOfSwords[1] = 3
set NumberOfSwords[2] = 3
set NumberOfSwords[3] = 4
// This is the color of the lightning.
// Default: Red 255, Green 255, Blue 255, Alpha 192
set LightningColor = CreateColor(255, 255, 255, 192)
endfunction
// =======================================================================
// DO NOT EDIT BELOW IF YOU DONT KNOW JASS
// =======================================================================
// *** Custom spells globals ***
globals
private integer array NRoots[8191]
endglobals
// ===================================================================
// *** Custom Spell Types / Structs ***
// ===================================================================
// ----------------------------------------------------------------
// *** Type: Sword ***
// - Description:
//
// *** Its an main sword traveling from start to end ***
//
// - Members:
// u -> Main sword unit
// v -> Units current position
// tr -> Target to which sword is traveling
// exec -> Current timed execution
// lig -> Travel path lightning
// pow -> Power of sword (ability level)
// ----------------------------------------------------------------
private struct sword
// *** Members ***
private unit u
private dd_vector v
private unit tr
private integer exec
private lightningex lig
private integer pow
// *** Static vars ***
private static constant real START_OFFSET = 115.
private static constant real FADE_TIME = 1.5
private static constant real MOTION_FADE_TIME = 1.25
private static constant real FADE_VELOCITY = 150.
// *** Constructor ***
//! runtextmacro constr("create", "unit caster")
// *** Assign members ***
set this.tr = GetSpellTargetUnit()
set this.v = CreateVectorDD(GetUnitX(caster), GetUnitY(caster), SWORD_Z, E_AngWidget(caster, this.tr)*DEGTORAD, 0.)
set this.u = CreateUnit(GetOwningPlayer(caster), SWORD_RAWCODE, 0., 0., this.v.alpha*RADTODEG)
set this.lig = CreateLightningAngle(LIGHTNING_TYPE, this.v.x, this.v.y, this.v.z, 0., 0., 0.)
set this.pow = GetUnitAbilityLevel(caster, BMAGIC_RAWCODE)
set this.exec = 0
// *** Offset move vector ***
call this.v.OffsetMove(sword.START_OFFSET)
// *** Lightning paint ***
call ChangeLightningColor(this.lig, LightningColor.r, LightningColor.g, LightningColor.b, LightningColor.a)
// *** Move Sword ***
call MakeUnitUnselectable(this.u)
call MakeUnitFlyable(this.u)
call SetUnitVPos(this.u, this.v)
//! runtextmacro endconstr()
// *** Destructor ***
//! runtextmacro destr()
// *** Sword fade and push timed ***
call E_FadeUnit(this.u, sword.FADE_TIME, 0., 100.)
call PushUnit(this.u, sword.FADE_VELOCITY, 0., sword.FADE_VELOCITY, sword.FADE_VELOCITY*sword.FADE_TIME, this.v.alpha, 0., LineData())
call RemoveUnitTimed(this.u, sword.FADE_TIME)
// *** Clear leaks ***
call FadeLightning(this.lig, LightningColor.a, 0, sword.FADE_TIME)
call DestroyLightningTimedEx(this.lig, sword.FADE_TIME+0.1)
call this.v.delete()
set this.u = null
set this.tr = null
//! runtextmacro enddestr()
// ---------------------------------------------------------------------
// *** Sword Methods ***
// ---------------------------------------------------------------------
// *** Moves sword offset and returns true if it reached target ***
public method OffsetMove takes real offset returns boolean
// *** Do main movement ***
call this.v.Direction(E_AngWidget(this.u, this.tr)*DEGTORAD, 0.)
call this.v.OffsetMove(offset)
call SetUnitVPos(this.u, this.v)
// *** Lightning move ***
call SetLightningPartPos(this.lig, this.v.x, this.v.y, this.v.z, 2)
// *** Every n execs load sword's illusion ***
if (QMod(this.exec, BLADE_MOTION_FACTOR) == 0) then
set bj_lastCreatedUnit = CreateUnit(DD_p[15], SWORD_RAWCODE, 0., 0., this.v.alpha*RADTODEG)
call MakeUnitFlyable(bj_lastCreatedUnit)
call MakeUnitUnselectable(bj_lastCreatedUnit)
call SetUnitVPos(bj_lastCreatedUnit, this.v)
call E_FadeUnit(bj_lastCreatedUnit, sword.MOTION_FADE_TIME, 35., 100.)
call RemoveUnitTimed(bj_lastCreatedUnit, sword.MOTION_FADE_TIME+0.1)
endif
// *** Next exec ***
set this.exec = this.exec + 1
// *** Is done? ***
return hypot(GetUnitX(this.tr)-this.v.x, GetUnitY(this.tr)-this.v.y) <= 2500.
endmethod
// ---------------------------------
// *** Operators ***
// ---------------------------------
// *** Returns owner of sword / casting unit
public method operator owner takes nothing returns player
return GetOwningPlayer(this.u)
endmethod
// *** Returns strength of sword ***
public method operator power takes nothing returns integer
return this.pow
endmethod
// *** Returns target unit ***
public method operator target takes nothing returns unit
return this.tr
endmethod
endstruct
// -------------------------------------------------------------------------
// *** Type: Extra Swords Data
//
// - eswords contains data which is requied for extra swords actions
// -------------------------------------------------------------------------
private struct eswords
// *** Members ***
public graphic array grp[16]
public player pl
public unit target
public integer lvl
public integer n
//! runtextmacro destr()
local real apptime = EXTRA_SWORDS_APPEAR_TIME*0.66667
local real time = ((EXTRA_SWORDS_OFFSET - (EXTRA_SWORDS_OFFSET/Pow(apptime*SQRT_2, 2.)))*(apptime/EXTRA_SWORDS_OFFSET)) + (1./apptime)
local integer alpha = GetGraphicAlpha(this.grp[0])
set this.target = null
debug call RMsg(time)
// *** Graphic free ***
loop
set this.n = this.n - 1
if (alpha >= 0xFA) then
call PushGraphic(this.grp[this.n], 0., EXTRA_SWORDS_OFFSET, EXTRA_SWORDS_OFFSET/apptime, EXTRA_SWORDS_OFFSET, this.grp[this.n].GetFacing()*DEGTORAD, EXTRA_SWORDS_PITCH*DEGTORAD, LineData())
endif
call FadeGraphic(this.grp[this.n], alpha, 0, time)
set GRAPHIC_DURATION = 0.1
call DestroyGraphicTimed(this.grp[this.n], time+0.1)
set this.grp[this.n] = Null
exitwhen (this.n == 0)
endloop
//! runtextmacro enddestr()
endstruct
// ------------------------------------------
// *** Other spell neccessary functions ***
// ------------------------------------------
// ------------------------------------------------------------
// *** Plays sword start sound if possible ***
private function TryPlaySwordStartSound takes nothing returns nothing
local unit u = GetTriggerUnit()
if (E_DistWidget(u, GetSpellTargetUnit()) > 300.) then
call GenericSoundDD(SOUND_ON_START, SOUND_ON_START_VOL, 0., 0., 1.)
call SetSoundVolumeAs3D(bj_lastPlayedSound, GetUnitX(u), GetUnitY(u), SOUND_ON_START_VOL, 3500.)
endif
set u = null
endfunction
// ------------------------------------------------------------
// *** Plays given sword sound at given position ***
private function PlaySwordSound takes integer sndType, real x, real y returns nothing
local real vol
if (sndType == 'stab') then
set vol = SOUND_ON_STAB_VOL
call GenericSoundDD(SOUND_ON_STAB, SOUND_ON_STAB_VOL, 0., 0., 1.)
elseif (sndType == 'disp') then
set vol = SOUND_ON_DISAPPEAR_VOL
call GenericSoundDD(SOUND_ON_DISAPPEAR, SOUND_ON_DISAPPEAR_VOL, 0., 0., 1.)
endif
call SetSoundVolumeAs3D(bj_lastPlayedSound, x, y, vol, 3500.)
endfunction
// ------------------------------------------------------------
// *** Causes extra swords to deal damage ***
private function ExtraSwordsDamage takes nothing returns nothing
// *** Locals list ***
local eswords es = GetSleepData()
// *** Deal damage ***
call UnitDamageTarget(DDCreateDummy(es.pl, 0.1), es.target, es.n*SwordDamageExtra[es.lvl], true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_METAL_HEAVY_SLICE)
// *** Play swords stab sound ***
call PlaySwordSound('stab', GetUnitX(es.target), GetUnitY(es.target))
// *** Load blood effects ***
call DestroyEffect(AddSpecialEffectTarget(BLOOD_EFFECT, es.target, ATTACH_POINT))
// *** Unpause target ***
call PauseUnit(es.target, false)
endfunction
// ------------------------------------------------------------
// *** Loads extra swords on hit ***
private function LoadSwords takes player pl, location pos, integer lvl, unit target returns eswords
// *** Locals ***
local real s_rad = GetRandomReal(0., TWO_PI)
local real cz = Cos(EXTRA_SWORDS_PITCH*DEGTORAD)
local real sz = SquareRoot(1.-(cz*cz))
local real rad
local real apptime = EXTRA_SWORDS_APPEAR_TIME*0.66667
local eswords es = eswords.create()
// *** Add swords ***
//! runtextmacro FOR("es.n", "0", "NumberOfSwords[lvl]")
set rad = s_rad + (es.n * TWO_PI / NumberOfSwords[lvl])
set es.grp[es.n] = AddGraphicPitch(SWORD_MODEL_FILE, GetLocationX(pos) + EXTRA_SWORDS_OFFSET * Cos(rad)*cz, GetLocationY(pos) + EXTRA_SWORDS_OFFSET * Sin(rad)*cz, STAB_Z_OFFSET + EXTRA_SWORDS_OFFSET * sz, rad*RADTODEG, 1., Real(-EXTRA_SWORDS_PITCH))
call PushGraphic(es.grp[es.n], EXTRA_SWORDS_OFFSET/EXTRA_SWORDS_APPEAR_TIME, 0., EXTRA_SWORDS_OFFSET/EXTRA_SWORDS_APPEAR_TIME, EXTRA_SWORDS_OFFSET, rad-PI, -EXTRA_SWORDS_PITCH*DEGTORAD, LineData())
call FadeGraphic(es.grp[es.n], 64, 255, apptime)
//! runtextmacro ENDFOR()
// Set eswords members
set es.target = target
set es.lvl = lvl
set es.pl = pl
// *** Pause target now ***
call PauseUnit(target, true)
// *** Deal extra swords damage ***
call SleepEx(apptime, function ExtraSwordsDamage, integer(es))
// *** End func ***
return (es)
endfunction
// ------------------------------------------------------------
// *** Summon blood roots on target ***
private function UnsummonRoots takes nothing returns nothing
// *** Locals ***
local eswords es = GetSleepData()
local integer id = HtoI(es.target)
// *** Remove one root ***
set NRoots[id] = NRoots[id] - 1
// *** If unit is alive ***
if (not IsUnitDead(es.target)) then
// *** No roots? ***
if (NRoots[id] == 0) then
call UnitRemoveAbility(es.target, BROOTS_BUFF_RAWCODE)
endif
// *** Blood effect ending ***
call DestroyEffect(AddSpecialEffectTarget(BLOOD_EFFECT, es.target, ATTACH_POINT))
endif
// *** Disappear sword sound ***
call PlaySwordSound('disp', GetUnitX(es.target), GetUnitY(es.target))
// *** Clear memory ***
call es.delete()
endfunction
private function SummonRoots takes eswords es returns nothing
// *** Locals ***
local integer id = HtoI(es.target)
// *** Dummy load ***
call UnitDamageTarget(DDCreateDummy(es.pl, 0.5), es.target, GetUnitState(es.target, UNIT_STATE_MAX_LIFE)*SwordDamage[es.lvl]*0.01, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_METAL_HEAVY_SLICE)
call PlaySwordSound('stab', GetUnitX(es.target), GetUnitY(es.target))
if (IsUnitDead(es.target)) then
call es.delete()
return
endif
call UnitAddAbility(bj_lastCreatedUnit, BROOTS_RAWCODE)
call IssueTargetOrder(bj_lastCreatedUnit, DUMMY_ORDER, es.target)
call DestroyEffect(AddSpecialEffectTarget(BLOOD_EFFECT, es.target, ATTACH_POINT))
// *** Store Target ***
set NRoots[id] = NRoots[id] + 1
// *** Do free unit ***
call SleepEx(RootDuration[es.lvl], function UnsummonRoots, integer(es))
endfunction
// ------------------------------------------------------------
// *** Main periodic movement of sword ***
private function MovementPeriodic takes nothing returns nothing
// *** Locals ***
local timer t = GetExpiredTimer()
local sword sw = DDGetValue(t)
local eswords es = Null
local location loc
// *** Check are we done movement ***
if (sw.OffsetMove(SWORD_SPEED*INTERVAL)) then
// *** Free timer ***
call PauseTimer(t)
call DestroyTimer(t)
// *** Load swords and get eswords ***
set loc = GetUnitLoc(sw.target)
set es = LoadSwords(sw.owner, loc, sw.power, sw.target)
// *** Summon roots and deal damage now ***
call SummonRoots(es)
// *** Disappear sound play ***
call PlaySwordSound('disp', GetUnitX(es.target), GetUnitY(es.target))
// *** Clear leaks ***
call RemoveLocation(loc)
call sw.delete()
set loc = null
endif
// *** Clear leaks ***
set t = null
endfunction
// ------------------------------------------------------------
// *** Main Trigger Condition ***
private constant function Conditions takes nothing returns boolean
return GetSpellAbilityId() == BMAGIC_RAWCODE
endfunction
// ------------------------------------------------------------
// *** Main Trigger Actions ***
private function Actions takes nothing returns nothing
// *** Locals ***
local timer t = CreateTimer()
local sword sw = sword.create(GetTriggerUnit())
// *** Play start sound ***
call TryPlaySwordStartSound()
// *** Start movement ***
call DDStoreValue(t, integer(sw))
call TimerStart(t, INTERVAL, true, function MovementPeriodic)
// *** Clear leaks ***
set t = null
endfunction
// =============================================================
// *** Init function ***
private function Init takes nothing returns nothing
// *** Events / Conditions / Actions ***
call onSpellEffectRegister(function Conditions, function Actions)
// *** Define user setup here ***
call Setup()
// *** Preloads ***
call Preload(SWORD_MODEL_FILE)
call Preload(BLOOD_EFFECT)
//call Preload(SOUND_ON_START)
//call Preload(SOUND_ON_STAB)
//call Preload(SOUND_ON_DISAPPEAR)
call GenericSoundDD(SOUND_ON_START, 0., 0., 0., 1.)
call GenericSoundDD(SOUND_ON_STAB, 0., 0., 0., 1.)
call GenericSoundDD(SOUND_ON_DISAPPEAR, 0., 0., 0., 1.)
call Preload("Textures\\DemonRune2.blp")
call Preload("Textures\\DemonRune3.blp")
call Preload("UI\\Glues\\SinglePlayer\\Undead3D_Exp\\FrostMorne.blp")
endfunction
endlibrary
//TESH.scrollpos=2
//TESH.alwaysfold=0
/*
-------------------------------------------------------------------------------------------------------
*** Dark Dragon Universal Pack Librarys v2 ***
-------------------------------------------------------------------------------------------------------
Installation:
- Copy this trigger / code in your map
- Copy units and abilities from this map to your map:
Dummy, Dummy Angle, Stun, Life Edit, Mana Edit and Slow
- Export and import dummy.mdx from this map to your map
- Or use the externals below to get this job done for you, by changing // to //!
- Edit this DD_DUMMY_CODE to your dummy code requires (universal dummy type)
Universal dummy type is an type of dummy which can be used by any type of spell
About:
- DDUPL v2 is a massive group of usefull and top coded systems
Features:
*** Main Features ***
- Fully leakless, easy usable and fast unit groups
- Dynamic / temporary DamageDetection system which is extremely fast
- New type stack -> unlimited array size of type integer (methods push, pop, peek, free, operators [], size and more....)
- Type float which is real coded struct, allows you to give limits to it which means real cant go to any outside range
- Knockback systems
- Fade Object system (using hashtable) store any objects and fade it via fade_f function pointer type
- Timed Damage Deal system
- Area of effect filter damage system
- Destructable death detection system
- Advanced string handling and type strings which is string array as well as stringx which is advanced string core type
- Additional fast math functions and constants
- Quick memory access via HtoI (very fast, limited) or ArrayId (fast, unlimited)
- Object storage functions: DDStoreValue DDGetValue DDFlushValue, as well as there Ex version which allows local storage (dynamic works in both functions)
- Type cluster which is effects group
- Type graphic which is advanced effect type. graphic can be moved, change scale...
- Perfect threading available ~via StartThread, ExecuteCode, NewThread (top ever coded for quick executions like 100 FPS)
- Full item fuse/combine as well as stacking system for items
- And more types, functions, methods and macros...
*/
// --------------------------------------------------------------------------------------------------------------
// Units
// external ObjectMerger w3u ushd dmy1 unam "Dummy" unsf " (DD Universal Pack)" uabi _ ucbs 0. ucpt 0. udtm 0.1 umxp 0. umxr 0. umdl "dummy.mdx" ushu _ uacq 2500. ua1t "spells" ua1c 3600. ua1b 1 ua1d 1 ua1s 1 umh1 1 ua1z 10000 ua1r 2500 urb1 250. ua1g "air,enemies,ground,friend" ua1w "missile" uaen 1 udty "divine" umvh 0. umvs 1 umas 1 umis 1 umvt "fly" ucol 0. ufoo 0 uhom 1 umpi 1000 umpm 1000 usid 1 usin 1
// external ObjectMerger w3u ushd dmy2 unam "Dummy Angle" unsf " (DD Universal Pack)" uabi _ ucbs 0. ucpt 0. udtm 0.1 umxp -180. umxr 0. umdl "dummy.mdx" ushu _ uacq 2500. ua1t "spells" ua1c 3600. ua1b 1 ua1d 1 ua1s 1 umh1 1 ua1z 10000 ua1r 2500 urb1 250. ua1g "air,enemies,ground,friend" ua1w "missile" uaen 1 udty "divine" umvh 0. umvs 1 umas 1 umis 1 umvt "fly" ucol 0. ufoo 0 uhom 1 umpi 1000 umpm 1000 usid 1 usin 1
// Abilities
// external ObjectMerger w3a AHtb stun anam "Stun" ansf " (DD Universal Pack)" aart "ReplaceableTextures\WorldEditUI\DoodadPlaceholder.blp" amat _ amsp 10000 Htb1 1 0. aran 1 99999. ahdu 1 0. adur 1 0. aher 0 aite 1 arac "other" alev 1 amcs 1 0 atar 1 "notself"
// external ObjectMerger w3a AIlf lfed anam "Life Edit" ansf " (DD Universal Pack)" alev 21 Ilif 1 0 Ilif 2 -1 Ilif 3 -2 Ilif 4 -4 Ilif 5 -8 Ilif 6 -16 Ilif 7 -32 Ilif 8 -64 Ilif 9 -128 Ilif 10 -256 Ilif 11 -512 Ilif 12 1 Ilif 13 2 Ilif 14 4 Ilif 15 8 Ilif 16 16 Ilif 17 32 Ilif 18 64 Ilif 19 128 Ilif 20 256 Ilif 21 512
// external ObjectMerger w3a AImb mned anam "Mana Edit" ansf " (DD Universal Pack)" alev 21 Iman 1 0 Iman 2 -1 Iman 3 -2 Iman 4 -4 Iman 5 -8 Iman 6 -16 Iman 7 -32 Iman 8 -64 Iman 9 -128 Iman 10 -256 Iman 11 -512 Iman 12 1 Iman 13 2 Iman 14 4 Iman 15 8 Iman 16 16 Iman 17 32 Iman 18 64 Iman 19 128 Iman 20 256 Iman 21 512
// external ObjectMerger w3a Afra slow anam "Slow" ansf " (DD Universal Pack)" aart "ReplaceableTextures\WorldEditUI\DoodadPlaceholder.blp" amat _ ahdu 1 0. adur 1 0. aite 1 arac "other" atar 1 "notself"
// All
// external ObjectMerger .\ddup\lib_v2\war3map.w3a
// external ObjectMerger .\ddup\lib_v2\war3map.w3u
// external FileImporter .\ddup\lib_v2\dummy.mdx dummy.mdx
// --------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------
library_once NativeDDUniversalPack initializer main requires DD, DDMath, DDKnockback, DDFadeObject, DDGroup, DDDamageDetect, DDDamageOverTime, DDCluster, DDDestructableDeathDetection, DDStrings, DDArray, DDThreading, DDFlock
globals
// -------------------------------------------------------------
// *** Editable constants ***
// *** Raw Code Constants ***
// -------------------------------------------------------------
constant integer DD_DUMMY_CODE = 'dmy1'
constant integer DD_DUMMY_CODE2 = 'dmy2'
private constant integer DD_STUN_CODE = 'stun'
private constant integer DD_SLOW_CODE = 'slow'
private constant integer DD_LIFE_EDIT_CODE = 'lfed'
private constant integer DD_MANA_EDIT_CODE = 'mned'
// -------------------------------------------------------------
// *** non-Editable constants ***
// -------------------------------------------------------------
// *** Object Constants ***
constant hashtable DDTable = InitHashtable()
constant string DD_NO_FILE = ""
constant integer THREAD_DELAY = 25
private constant timer sleepTimer = CreateTimer()
private constant location LOC_Z = Location(0., 0.)
// *** Private non-constants ***
private trigger Trig
private unit DD_Dummy
//private integer DD_Seed
private unit array StunnedUnit[8191]
private timer array StunnedTimer[8191]
private unit array SlowedUnit[8191]
private timer array SlowedTimer[8191]
private boolean array DoRemoveUnit[8191]
private string array PreldSnd[8191]
private integer PreldSndN = 0
// *** Non-Constants used in whole user script ***
player array DD_p[15]
real GRAPHIC_DURATION = 0.
real DDMinX = 0.
real DDMinY = 0.
real DDMaxX = 0.
real DDMaxY = 0.
endglobals
// ======================================================================
// ------ TextMacros
//
// Single lined if
//! textmacro IF takes CONDITION, THENACTION, ELSEACTION
if ($CONDITION$) then
$THENACTION$
else
$ELSEACTION$
endif
//! endtextmacro
// Single lined if x
//! textmacro IFX takes CONDITION, ACTION
if ($CONDITION$) then
$ACTION$
endif
//! endtextmacro
// For start
//! textmacro FOR takes VAR, START, END
set $VAR$ = $START$ - 1
loop
set $VAR$ = $VAR$ + 1
exitwhen ($VAR$ >= $END$)
//! endtextmacro
// For end
//! textmacro ENDFOR
endloop
//! endtextmacro
// Single lined for
//! textmacro FORX takes VAR, START, END, ACTION
set $VAR$ = $START$
loop
exitwhen ($VAR$ >= $END$)
$ACTION$
set $VAR$ = $VAR$ + 1
endloop
//! endtextmacro
// --------------------------------------------------------
// *** Struct macros ***
// *** Constructor start ***
//! textmacro constr takes NAME, ARG
static method $NAME$ takes $ARG$ returns thistype
local thistype this = thistype.allocate()
//! endtextmacro
// *** Constructor end ***
//! textmacro endconstr
return this
endmethod
//! endtextmacro
// *** Destructor start ***
//! textmacro destr
public method delete takes nothing returns nothing
//! endtextmacro
// *** Destructor end ***
//! textmacro enddestr
call this.destroy()
endmethod
//! endtextmacro
// *** End struct macros ***
// --------------------------------------------------------------------------------
// *** Modules ***
module dd_pointer
// *** Main pointer ***
private static thistype array ptr[8191]
// *** Set main object ***
public method SetPtrObj takes handle h returns nothing
set thistype.ptr[HtoI(h)] = this
endmethod
// *** Returns pointer from main object ***
public static method GetPtrObj takes handle h returns thistype
return thistype.ptr[HtoI(h)]
endmethod
// *** Clears pointer at given object ***
public static method ClrPtrObj takes handle h returns nothing
set thistype.ptr[HtoI(h)] = Null
endmethod
endmodule
// --------------------------------------------------------------------------------
// *** Object storage functions ***
function DDStoreValue takes handle object, integer value returns nothing
call SaveInteger(DDTable, GetHandleId(object), 0, value)
endfunction
function DDGetValue takes handle object returns integer
return LoadInteger(DDTable, GetHandleId(object), 0)
endfunction
function DDFlushValue takes handle object returns nothing
call RemoveSavedInteger(DDTable, GetHandleId(object), 0)
endfunction
function DDStoreValueEx takes handle object, integer kkey, integer value returns nothing
call SaveInteger(DDTable, GetHandleId(object), kkey, value)
endfunction
function DDGetValueEx takes handle object, integer kkey returns integer
return LoadInteger(DDTable, GetHandleId(object), kkey)
endfunction
function DDFlushValueEx takes handle object, integer kkey returns nothing
call RemoveSavedInteger(DDTable, GetHandleId(object), kkey)
endfunction
globals
private integer HandleCount = 0
private handle array pHandle[8191]
private integer array pData[8191]
endglobals
private constant function GetHandleDataId takes handle object returns integer
local integer i = 0
loop
exitwhen (i >= HandleCount)
if (pHandle[i/2] == object) then
return (i/2)
elseif (pHandle[HandleCount-1-(i/2)] == object) then
return (HandleCount-1-(i/2))
endif
set i = i + 2
endloop
return -1
endfunction
function SetHandleData takes handle object, integer data returns nothing
local integer id = GetHandleDataId(object)
if (id != -1) then
set pData[id] = data
return
endif
set pHandle[HandleCount] = object
set pData[HandleCount] = data
set HandleCount = HandleCount + 1
endfunction
constant function GetHandleData takes handle object returns integer
local integer id = GetHandleDataId(object)
if (id != -1) then
return pData[id]
endif
return (0)
endfunction
function FlushHandleData takes handle object returns nothing
local integer id = GetHandleDataId(object)
if (id != -1) then
set HandleCount = HandleCount - 1
set pHandle[id] = pHandle[HandleCount]
set pData[id] = pData[HandleCount]
set pHandle[HandleCount] = null
set pData[HandleCount] = 0
endif
endfunction
private function PreloadSoundsGeneric takes string file returns nothing
set PreldSnd[PreldSndN] = file
set PreldSndN = PreldSndN + 1
if (PreldSndN == 1) then
call TriggerSleepAction(0.)
loop
exitwhen (PreldSndN == 0)
set PreldSndN = PreldSndN - 1
set bj_lastPlayedSound = CreateSound(PreldSnd[PreldSndN], false, false, false, 10, 10, "")
call SetSoundVolume(bj_lastPlayedSound, 1)
call StartSound(bj_lastPlayedSound)
call KillSoundWhenDone(bj_lastPlayedSound)
endloop
endif
endfunction
// --------------------------------------------------------------------------------
// *** Sound functions ***
function GenericSoundDD takes string file, real vol, real x, real y, real pitch returns sound
local boolean is3d = x != 0. or y != 0.
if (not bj_gameStarted) then
call PreloadSoundsGeneric.execute(file)
endif
set bj_lastPlayedSound = CreateSound(file, false, is3d, is3d, 10, 10, "")
call SetSoundVolume(bj_lastPlayedSound, R2I(vol*1.27))
call SetSoundPitch(bj_lastPlayedSound, pitch)
if (is3d) then
call SetSoundPosition(bj_lastPlayedSound, x, y, 0.)
endif
call StartSound(bj_lastPlayedSound)
call KillSoundWhenDone(bj_lastPlayedSound)
return bj_lastPlayedSound
endfunction
// *** Sets sound volume as 3D
function SetSoundVolumeAs3D takes sound s, real x, real y, real max_vol, real max_dist returns nothing
local integer i
local real cx
local real cy
local real d
local real vol
//! runtextmacro FOR("i", "0", "bj_MAX_PLAYERS")
if (GetLocalPlayer() == Player(i)) then
set cx = GetCameraTargetPositionX()
set cy = GetCameraTargetPositionY()
set d = hypot(cx-x, cy-y)
set vol = max_vol-((max_vol*d)/(max_dist*max_dist))
call SetSoundVolume(s, R2I(vol*1.27))
endif
//! runtextmacro ENDFOR()
endfunction
constant function MinSleep takes nothing returns real
return (-1.00)
endfunction
// --------------------------------------------------------
// *** The most precius/best sleep that I coded ***
function Sleep takes real seconds returns nothing
local real timRem = 0.
local real sleep = 0.
local real timRemMain = 0.
local integer n = 0
local boolean timFinish = FALSE
if (seconds <= 0. and seconds != MinSleep()) then
return
endif
if (seconds <= bj_WAIT_FOR_COND_MIN_INTERVAL) then
call TriggerSleepAction(MinSleep())
return
endif
set timRemMain = TimerGetRemaining(sleepTimer) - seconds
set timRem = seconds
if (timRemMain < 0.) then
//Someone will play map more then 3 hours
//Wait few seconds until timer repeats
//Its almost imposible to reach this line of code
//This function has to be called exactly when 3 hours of map
//playing pass
call TriggerSleepAction(seconds)
return
endif
if (seconds - .5 > 0.) then
set sleep = seconds - .5
loop
exitwhen (timRem <= .5)
call TriggerSleepAction( sleep )
set timRem = TimerGetRemaining(sleepTimer) - timRemMain
//Delay in game happened
if (timRem > .5) then
set sleep = timRem - .5
endif
endloop
else
set timRem = seconds
endif
set sleep = timRem * bj_WAIT_FOR_COND_MIN_INTERVAL
set n = R2I(timRem / bj_WAIT_FOR_COND_MIN_INTERVAL)
if (timRem / bj_WAIT_FOR_COND_MIN_INTERVAL == n) then
set n = n - 1
endif
loop
set timFinish = ( (timRem <= 0.026) and (n <= 0) ) or (timRem <= sleep)
exitwhen (timFinish)
call TriggerSleepAction(MinSleep())
set n = n - 1
set timRem = TimerGetRemaining(sleepTimer) - timRemMain
endloop
endfunction
// *** Debug precision test ***
function SleepDebug takes integer miliseconds returns nothing
debug local timer t = CreateTimer()
debug call TimerStart(t, (miliseconds/1000.)+0.5, false, null)
call Sleep(miliseconds/1000.)
debug call RMsg.execute(TimerGetElapsed(t))
debug call PauseTimer(t)
debug call DestroyTimer(t)
debug set t = null
endfunction
// *** Runs the trigger ***
function RunTrigger takes trigger trig returns nothing
if (trig != null) then
if (IsTriggerEnabled(trig)) then
if (TriggerEvaluate(trig)) then
call TriggerExecute(trig)
endif
endif
endif
endfunction
// Runs trigger after given seconds
private function RunTriggerExpired takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
call RunTrigger(LoadTriggerHandle(DDTable, id, 0))
call FlushChildHashtable(DDTable, id)
call PauseTimer(t)
call DestroyTimer(t)
set t = null
endfunction
function RunTriggerTimed takes trigger which, real secs returns nothing
local timer t
if (which != null) then
if (secs > 0.) then
set t = CreateTimer()
call SaveTriggerHandle(DDTable, GetHandleId(t), 0, which)
call TimerStart(t, secs, false, function RunTriggerExpired)
set t = null
else
call RunTrigger(which)
endif
endif
endfunction
// *** Destroys effect after x secs ***
private function AfterEffectDestroy takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
call DestroyEffect(LoadEffectHandle(DDTable, id, 0))
call RemoveSavedHandle(DDTable, id, 0)
call PauseTimer(t)
call DestroyTimer(t)
set t = null
endfunction
function DestroyEffectTimed takes effect whichEffect, real duration returns nothing
local timer t = CreateTimer()
call SaveEffectHandle(DDTable, GetHandleId(t), 0, whichEffect)
call TimerStart(t, duration, false, function AfterEffectDestroy)
set t = null
endfunction
// --------------------------------------------------------------------------------
// *** Custom types ***
// ****************************************************************
// --- Color struct
// ****************************************************************
// *** Color globals ***
globals
private dd_color array UClr[8191]
endglobals
struct dd_color
// *** Members ***
readonly integer r
readonly integer g
readonly integer b
readonly integer a
// *** Constructror ***
static method create takes integer r, integer g, integer b, integer a returns dd_color
local dd_color this = thistype.allocate()
set this.r = limits(r, 0, 0xFF)
set this.g = limits(g, 0, 0xFF)
set this.b = limits(b, 0, 0xFF)
set this.a = limits(a, 0, 0xFF)
return (this)
endmethod
//! runtextmacro constr("copy", "dd_color c")
set this.r = c.r
set this.g = c.g
set this.b = c.b
set this.a = c.a
//! runtextmacro endconstr()
// *** Set new color ***
public method SetColor takes integer r, integer g, integer b, integer a returns nothing
set this.r = limits(r, 0, 0xFF)
set this.g = limits(g, 0, 0xFF)
set this.b = limits(b, 0, 0xFF)
set this.a = limits(a, 0, 0xFF)
endmethod
// *** Will paint the unit ***
public method VertexUnit takes unit whichUnit returns nothing
call SetUnitVertexColor(whichUnit, this.r, this.g, this.b, this.a)
endmethod
// *** Sets red ***
public method operator red= takes integer red returns nothing
set this.r = limits(red, 0, 0xFF)
endmethod
// *** Sets green ***
public method operator green= takes integer green returns nothing
set this.g = limits(green, 0, 0xFF)
endmethod
// *** Sets blue ***
public method operator blue= takes integer blue returns nothing
set this.b = limits(blue, 0, 0xFF)
endmethod
// *** Sets alpha ***
public method operator alpha= takes integer alpha returns nothing
set this.a = limits(alpha, 0, 0xFF)
endmethod
endstruct
// *** Color actions ***
function CreateColor takes integer r, integer g, integer b, integer a returns dd_color
return dd_color.create(r, g, b, a)
endfunction
// *** Will copy color ***
function CopyColor takes dd_color clr returns dd_color
return dd_color.copy(clr)
endfunction
// *** Set new color ***
function ChangeColor takes dd_color c, integer r, integer g, integer b, integer a returns nothing
call c.SetColor(r, g, b, a)
endfunction
// *** Destroys color ***
function DestroyColor takes dd_color c returns nothing
call c.SetColor(0, 0, 0, 0)
call c.destroy()
endfunction
// *** Sets unit color ***
function SetUnitVertexColorEx takes unit u, dd_color c returns nothing
// *** If its null then clear memory ***
if (c == Null) then
call UClr[ArrayId(u)].destroy()
set UClr[ArrayId(u)] = Null
// *** Else allocate new one ***
elseif (UClr[ArrayId(u)] == Null) then
set ArrayId(u):UClr = dd_color.copy(c)
else
call ChangeColor(ArrayId(u):UClr, c.r, c.g, c.b, c.a)
endif
// *** Paint unit ***
call SetUnitVertexColor(u, c.r, c.g, c.b, c.a)
endfunction
// *** Reads unit color ***
function GetUnitVertexColorEx takes unit u returns dd_color
if (ArrayId(u):UClr != Null) then
return dd_color.copy(ArrayId(u):UClr)
endif
return 0
endfunction
// =======================================================
// *** Type Graphic Is Advanced Effect Type ***
// =======================================================
struct graphic
private unit u
private effect e
private dd_vector v
private real scale
private real poffset
private string file
private dd_color c
private static constant string ATTACH_POINT = "origin"
private static constant real EFFECT_DURATION = 5.
// *** Constructor ***
static method create takes string path, real x, real y, real z, real facing, real scale, real pitch returns graphic
local graphic this = graphic.allocate()
set pitch = rmod(pitch, 360.)
if (pitch <= 180.) then
set this.u = CreateUnit(DD_p[15], DD_DUMMY_CODE, x, y, facing)
set this.poffset = 0.
else
set this.u = CreateUnit(DD_p[15], DD_DUMMY_CODE2, x, y, facing)
set this.poffset = 180.
endif
set this.e = AddSpecialEffectTarget(path, this.u, graphic.ATTACH_POINT)
set this.file = path
set this.v = dd_vector.create(x, y, z, facing*DEGTORAD, pitch*DEGTORAD)
set this.scale = scale
set this.c = CreateColor(0xFF, 0xFF, 0xFF, 0xFF)
call UnitAddAbility(this.u, 'Aloc')
call UnitAddAbility(this.u, 'Amrf')
call UnitRemoveAbility(this.u, 'Amrf')
call SetUnitX(this.u, this.v.x)
call SetUnitY(this.u, this.v.y)
call SetUnitFlyHeight(this.u, this.v.z, 0.)
call SetUnitScale(this.u, scale, scale, scale)
call SetUnitAnimationByIndex(this.u, R2I(pitch-this.poffset))
return (this)
endmethod
// *** Destructor ***
private method onDestroy takes nothing returns nothing
if (this.e != null) then
call DestroyEffect(this.e)
if (GRAPHIC_DURATION == 0.) then
call RemoveUnitTimed.evaluate(this.u, graphic.EFFECT_DURATION)
elseif (GRAPHIC_DURATION != -1.) then
call RemoveUnit(this.u)
else
call RemoveUnitTimed.evaluate(this.u, GRAPHIC_DURATION)
endif
else
call RemoveUnit(this.u)
endif
set GRAPHIC_DURATION = 0.
set this.e = null
set this.u = null
call this.v.delete()
endmethod
// Change color
public method SetColor takes dd_color c returns nothing
call c.VertexUnit(this.u)
call ChangeColor(this.c, c.r, c.g, c.b, c.a)
endmethod
// Returns color
public method GetColor takes nothing returns dd_color
return CopyColor(this.c)
endmethod
// Simple kills effect but does not destroy graphic
public method Kill takes nothing returns nothing
call DestroyEffect(this.e)
set this.e = null
endmethod
// Move graphic
public method Move takes real newx, real newy, real newz returns nothing
call this.v.Move(newx, newy, newz)
call SetUnitVPos(this.u, this.v)
endmethod
// Offset move
public method OffsetMove takes real offset returns nothing
call this.v.Move(this.v.x+offset*Cos(this.v.alpha), this.v.y+offset*Sin(this.v.alpha), this.v.z)
call SetUnitVPos(this.u, this.v)
endmethod
// Returns x
public method GetX takes nothing returns real
return (this.v.x)
endmethod
// Returns y
public method GetY takes nothing returns real
return (this.v.y)
endmethod
// Returns z
public method GetZ takes nothing returns real
return (this.v.z)
endmethod
// Will set new facing angle in degress
public method SetFacing takes real facing returns nothing
call SetUnitFacing(this.u, facing)
call this.v.Direction(facing*DEGTORAD, this.v.beta)
endmethod
// Returns current facing of graphic
public method GetFacing takes nothing returns real
return this.v.alpha*RADTODEG
endmethod
// Sets new scale for graphic
public method SetScale takes real scale returns nothing
call SetUnitScale(this.u, scale, scale, scale)
set this.scale = scale
endmethod
// Returns current scale
public method GetScale takes nothing returns real
return this.scale
endmethod
// Set pitch
public method SetPitch takes real pitch returns nothing
local real cpitch = this.v.beta*RADTODEG
set pitch = rmod(pitch, 360.)
if not (pitch >= this.poffset and pitch <= this.poffset+180.) then
call this.Kill()
call RemoveUnit(this.u)
set this.poffset = rabs(this.poffset - 180.)
if (this.poffset == 0.) then
set this.u = CreateUnit(DD_p[15], DD_DUMMY_CODE, this.v.x, this.v.y, this.v.alpha*RADTODEG)
else
set this.u = CreateUnit(DD_p[15], DD_DUMMY_CODE2, this.v.x, this.v.y, this.v.alpha*RADTODEG)
endif
set this.e = AddSpecialEffectTarget(this.file, this.u, graphic.ATTACH_POINT)
endif
call this.v.Direction(this.v.alpha, pitch*DEGTORAD)
call SetUnitAnimationByIndex(this.u, R2I(pitch-this.poffset))
endmethod
// Get pitch
public method GetPitch takes nothing returns real
return this.v.beta*RADTODEG
endmethod
// Has effect?
public method operator haseffect takes nothing returns boolean
return this.e != null
endmethod
// Push graphic
public method Push takes real vel, real acc, real max_vel, real dist, real a, real b, vector_d data returns nothing
call PushUnit(this.u, vel, acc, max_vel, dist, a, b, data)
endmethod
// Force graphic
public method ForceMovement takes real vel, real acc, real max_vel, real dist, real a, real b, math m, boolean ifl returns nothing
call ForceUnitMovement(this.u, vel, acc, max_vel, dist, a, b, m, ifl)
endmethod
endstruct
// =====================================================================================================
// *** Graphic functions ***
// =====================================================================================================
// *** Adds graphic ***
function AddGraphic takes string path, real x, real y, real z, real facing, real scale returns graphic
return graphic.create(path, x, y, z, facing, scale, 90.)
endfunction
// *** Adds graphic with angle ***
function AddGraphicPitch takes string modelFile, real x, real y, real z, real facing, real scale, real pitch returns graphic
return graphic.create(modelFile, x, y, z, facing, scale, pitch)
endfunction
// *** Destroys graphic ***
function DestroyGraphic takes graphic whichGraphic returns nothing
call whichGraphic.destroy()
endfunction
// *** Destroys graphic with remove time ***
function DestroyGraphicEx takes graphic g, real removeTime returns nothing
if (removeTime == 0.) then
set GRAPHIC_DURATION = -1.
elseif (removeTime < 0.) then
return
endif
set GRAPHIC_DURATION = removeTime
call g.destroy()
endfunction
// ----------------------------------------------------------------------------
// *** Destroys graphic timed ***
private function DestroyGraphicTimedAfter takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
set GRAPHIC_DURATION = LoadReal(DDTable, id, 1)
call DestroyGraphic(graphic( LoadInteger(DDTable, id, 0) ))
call FlushChildHashtable(DDTable, id)
call PauseTimer(t)
call DestroyTimer(t)
set t = null
endfunction
function DestroyGraphicTimed takes graphic whichGraphic, real timeout returns nothing
local timer t = CreateTimer()
local integer id = GetHandleId(t)
call SaveInteger(DDTable, id, 0, integer(whichGraphic))
call SaveReal(DDTable, id, 1, GRAPHIC_DURATION)
call TimerStart(t, timeout, false, function DestroyGraphicTimedAfter)
set t = null
endfunction
// ----------------------------------------------------------------------------
// *** Change graphic position ***
function SetGraphicPos takes graphic grp, dd_vector v returns nothing
call grp.Move(v.x, v.y, v.z)
endfunction
// *** Changes graphic position by given offset ***
function SetGraphicPosOffset takes graphic grp, real distance returns nothing
call grp.OffsetMove(distance)
endfunction
// *** Sets graphic facing ***
function SetGraphicFacing takes graphic grp, real alpha, real beta returns nothing
call grp.SetFacing(alpha)
call grp.SetPitch(beta)
endfunction
// *** Set graphic scale ***
function SetGraphicScale takes graphic grp, real scale returns nothing
call grp.SetScale(scale)
endfunction
// *** Sets graphic color ***
function SetGraphicColor takes graphic grp, integer r, integer g, integer b, integer a returns nothing
local dd_color c = CreateColor(r, g, b, a)
call grp.SetColor(c)
call DestroyColor(c)
endfunction
// *** Push graphic ***
function PushGraphic takes graphic grp, real vel, real acc, real mvel, real d, real a, real b, vector_d v returns nothing
call grp.Push(vel, acc, mvel, d, a, b, v)
endfunction
// *** Forces graphic movement ***
function ForceGraphicMovement takes graphic grp, real vel, real acc, real mvel, real d, real a, real b, math mth returns nothing
call grp.ForceMovement(vel, acc, mvel, d, a, b, mth, false)
endfunction
// *** Returns current vec pos of graphic ***
function GetGraphicPos takes graphic grp returns dd_vector
return dd_vector.create(grp.GetX(), grp.GetY(), grp.GetZ(), grp.GetFacing()*DEGTORAD, grp.GetPitch()*DEGTORAD)
endfunction
// *** Returns graphic facing ***
function GetGraphicFacing takes graphic grp returns real
return grp.GetFacing()
endfunction
// *** Returns pitch ***
function GetGraphicPitch takes graphic grp returns real
return grp.GetPitch()
endfunction
// *** Returns graphic red ***
function GetGraphicRed takes graphic grp returns integer
local dd_color c = grp.GetColor()
local integer val = c.r
call DestroyColor(c)
return (val)
endfunction
// *** Returns graphic green ***
function GetGraphicGreen takes graphic grp returns integer
local dd_color c = grp.GetColor()
local integer val = c.g
call DestroyColor(c)
return (val)
endfunction
// *** Returns graphic blue ***
function GetGraphicBlue takes graphic grp returns integer
local dd_color c = grp.GetColor()
local integer val = c.b
call DestroyColor(c)
return (val)
endfunction
// *** Returns graphic alpha ***
function GetGraphicAlpha takes graphic grp returns integer
local dd_color c = grp.GetColor()
local integer val = c.a
call DestroyColor(c)
return (val)
endfunction
// *** Fades graphic timed ***
private keyword FadeGraphicPeriodic
function FadeGraphicPeriodic takes hashtable ht, real trans returns nothing
local graphic grp = LoadInteger(ht, 0, 0)
local dd_color clr = grp.GetColor()
set clr.alpha = R2I(trans*2.55)
call grp.SetColor(clr)
call DestroyColor(clr)
endfunction
// *** Will fade graphic ***
function FadeGraphic takes graphic grp, integer s_alpha, integer e_alpha, real timeout returns nothing
local hashtable ht = InitHashtable()
call SaveInteger(ht, 0, 0, integer(grp))
call FadeObject(ht, s_alpha/2.55, e_alpha/2.55, timeout, FadeGraphicPeriodic)
set ht = null
endfunction
globals
private graphic array AttGrp[8191]
private unit array AttUnit[8191]
private integer array pGrp[8191]
private dd_vector array AttVec[8191]
private integer NGrp = 0
endglobals
// *** Will attach graphic to given unit ***
private function AttachedGraphicLock takes integer i returns boolean
loop
//! textmacro attgrp__LOCK_GRAPHIC
call AttGrp[i].Move(GetUnitX(AttUnit[i])+AttVec[i].x, GetUnitY(AttUnit[i])+AttVec[i].y, GetUnitFlyHeight(AttUnit[i])+AttVec[i].z)
set i = i + 1
exitwhen (i == NGrp)
//! endtextmacro
//! runtextmacro attgrp__LOCK_GRAPHIC()
//! runtextmacro attgrp__LOCK_GRAPHIC()
//! runtextmacro attgrp__LOCK_GRAPHIC()
//! runtextmacro attgrp__LOCK_GRAPHIC()
endloop
return (NGrp == 0)
endfunction
function UnitAttachGraphic takes unit whichUnit, graphic grp, real ox, real oy, real oz returns nothing
// *** Already in movement, want chage unit/offset ***
if (pGrp[grp] != 0 and whichUnit != null) then
set AttUnit[pGrp[grp]-1] = whichUnit
call AttVec[pGrp[grp]-1].Move(ox, oy, oz)
// *** Null unit and is already moving! so stop ***
elseif (whichUnit == null and pGrp[grp] != 0) then
set NGrp = NGrp - 1
set AttGrp[pGrp[grp]-1] = AttGrp[NGrp]
set AttUnit[pGrp[grp]-1] = AttUnit[NGrp]
call AttVec[pGrp[grp]-1].delete()
set AttVec[pGrp[grp]-1] = AttVec[NGrp]
set pGrp[AttGrp[NGrp]] = pGrp[grp]
call grp.Move(grp.GetX()+ox, grp.GetY()+oy, grp.GetZ()+oz)
// *** Want new movement ***
elseif (pGrp[grp] == 0) then
// *** New thread load if needed ***
if (NGrp == 0) then
call NewThread(THREAD_DELAY, 0, callback.AttachedGraphicLock, false)
endif
// *** No? Generate new one ***
set AttGrp[NGrp] = grp
set AttUnit[NGrp] = whichUnit
set AttVec[NGrp] = dd_vector.create(ox, oy, oz, 0., 0.)
set NGrp = NGrp + 1
set pGrp[grp] = NGrp
endif
endfunction
// Random chance triggering
function GetRandomChance takes integer perc returns boolean
//set DD_Seed = DD_Seed + (GetRandomInt(1, 3) * GetRandomInt(-1, 0))
//if (QMod(DD_Seed, GetRandomInt(1, 3)) != 0) then
// call SetRandomSeed(DD_Seed)
//endif
return (QMod(GetRandomInt(1, 100), 100/perc) == 0)
endfunction
// *** Trigger handling ***
// *** Actions declare ***
private type actions extends triggeraction array [4]
// *** Actions ***
globals
private integer array NActions
private actions array Actions
endglobals
// *** Adds action to trigger ***
function TriggerAddActionDynamic takes trigger whichTrigger, code action returns triggeraction
// *** Locals ***
local integer id = HtoI(whichTrigger)
local actions act = Actions[id]
// *** Check do we have actions ***
if (act == Null) then
set act = actions.create()
set Actions[id] = act
set act[0] = TriggerAddAction(whichTrigger, action)
set NActions[integer(act)] = 1
return (act[0])
elseif (NActions[integer(act)] < actions.size) then
set act[NActions[integer(act)]] = TriggerAddAction(whichTrigger, action)
set NActions[integer(act)] = NActions[integer(act)] + 1
return (act[NActions[integer(act)]-1])
endif
// *** More then max_size of actions needed ***
return (null)
endfunction
// *** Destroy trigger ex ***
private function DestroyTriggerActions takes trigger whichTrigger returns nothing
local integer id = HtoI(whichTrigger)
local actions act = Actions[id]
if (act != Null) then
// *** Loop through all and remove actions ***
loop
set NActions[integer(act)] = NActions[integer(act)] - 1
call TriggerRemoveAction(whichTrigger, act[NActions[integer(act)]])
set act[NActions[integer(act)]] = null
exitwhen (NActions[integer(act)] == 0)
endloop
// *** Clear actions ***
call act.destroy()
set Actions[id] = Null
endif
endfunction
// *** Hook correct trigger destroy ***
private function DestroyTrigger_hook takes trigger whichTrigger returns nothing
set Trig = whichTrigger
call DestroyTriggerActions(whichTrigger)
call TriggerUnregisterAnyUnitDamaged(whichTrigger)
call TriggerUnregisterAnyDestructableDeath(whichTrigger)
call ExecuteFunc("DD_DestroyTrigger_hooked")
set Trig = null
/*
call TriggerUnregisterAnyMissileEvent.evaluate(whichTrigger)
call E_TriggerUnregisterAnyItemFused.evaluate(whichTrigger)
call onUnitCollideUnregister.evaluate(whichTrigger)
*/
endfunction
// temp get trig func
function get_dd_trig takes nothing returns trigger
return (Trig)
endfunction
// *** Hook it now ***
hook DestroyTrigger DestroyTrigger_hook
// *** Type thread which will execute new thread ***
struct dd_thread extends array
private static method ThreadOnEnd takes nothing returns nothing
call DestroyTrigger(GetTriggeringTrigger())
endmethod
static method create takes code func returns dd_thread
local trigger t = CreateTrigger()
call TriggerAddActionDynamic(t, func)
call TriggerAddActionDynamic(t, function dd_thread.ThreadOnEnd)
set t = null
return (Null)
endmethod
endstruct
// *** Starts new thread ***
function StartThread takes code func returns nothing
call dd_thread.create(func)
endfunction
// *** Returns sleep ex stored data ***
function GetSleepData takes nothing returns integer
return DDGetValue(GetTriggeringTrigger())
endfunction
// *** Loads new thread timed ***
private function SleepFuncEnd takes nothing returns nothing
call DestroyTrigger(GetTriggeringTrigger())
call DDFlushValue(GetTriggeringTrigger())
endfunction
function SleepEx takes real secs, code func, integer data returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterTimerEvent(t, secs, false)
call DDStoreValue(t, data)
call TriggerAddActionDynamic(t, func)
call TriggerAddActionDynamic(t, function SleepFuncEnd)
set t = null
endfunction
// -----------------------------------------------------
// *** Timed unit remove works ***
/*private function RemUnitTimed takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = HtoI(t)
call RemoveUnit(LoadUnitHandle(DDTable, id, 0))
call RemoveSavedHandle(DDTable, id, 0)
call PauseTimer(t)
call DestroyTimer(t)
set t = null
endfunction*/
private function DoRemoveUnitOnDeath takes nothing returns nothing
local unit u = GetTriggerUnit()
if (DoRemoveUnit[HtoI(u)]) then
set DoRemoveUnit[HtoI(u)] = false
call RemoveUnit(u)
endif
set u = null
endfunction
function RemoveUnitTimed takes unit u, real dur returns nothing
/*local timer t = CreateTimer()
call SaveUnitHandle(DDTable, HtoI(t), 0, u)
call TimerStart(t, dur, false, function RemUnitTimed)
set t = null*/
if (dur > 0.) then
call UnitApplyTimedLife(u, 'BTLF', dur)
set DoRemoveUnit[HtoI(u)] = true
else
call RemoveUnit(u)
endif
endfunction
// *** Checks is unit in an line ***
// *** Arguments: whichUnit, bottom x, bottom y, length, height and angle in radians
function IsUnitOnLine takes unit u, real bx, real by, real length, real height, real radians returns boolean
// *** Locals ***
local real ux = GetUnitX(u)
local real uy = GetUnitY(u)
local real rad = Atan2(uy-by, ux-bx)
local real hyp
// *** Unit is not on line ***
if (rad < radians or rad > radians+PI_HALF) then
return false
endif
// *** Set remaining locals ***
set hyp = (ux-bx)*(ux-bx) + (uy-by)*(uy-by)
set rad = rad - radians
if (rad < 0.) then
set rad = -rad
endif
return (hyp * Cos(rad) <= length*length) and (hyp * Sin(rad) <= height*height)
endfunction
// *** Unit flyable allow ***
function MakeUnitFlyable takes unit whichUnit returns nothing
call UnitAddAbility(whichUnit, 'Amrf')
call UnitRemoveAbility(whichUnit, 'Amrf')
endfunction
// *** Makes unit unselectable ***
function MakeUnitUnselectable takes unit whichUnit returns nothing
call UnitAddAbility(whichUnit, 'Aloc')
endfunction
// *** Loads new dummy ***
function DDCreateDummy takes player owner, real timeout returns unit
set bj_lastCreatedUnit = CreateUnit(owner, DD_DUMMY_CODE, 0., 0., 0.)
call MakeUnitFlyable(bj_lastCreatedUnit)
call MakeUnitUnselectable(bj_lastCreatedUnit)
if (timeout > 0.) then
call RemoveUnitTimed(bj_lastCreatedUnit, timeout)
endif
return (bj_lastCreatedUnit)
endfunction
// *** Will stun unit ***
private function UnstunUnit takes nothing returns nothing
local integer t_id = HtoI(GetExpiredTimer())
local integer u_id = HtoI(StunnedUnit[t_id])
call UnitRemoveAbility(StunnedUnit[t_id], 'BPSE')
call IssueImmediateOrder(StunnedUnit[t_id], "stop")
call PauseTimer(StunnedTimer[u_id])
call DestroyTimer(StunnedTimer[u_id])
set StunnedUnit[t_id] = null
set StunnedTimer[u_id] = null
endfunction
function StunUnit takes unit whichUnit, real secs returns nothing
local integer id = HtoI(whichUnit)
if (secs > 0.) then
if (StunnedTimer[id] == null) then
call UnitAddAbility(DDCreateDummy(DD_p[0], 0.4), DD_STUN_CODE)
call IssueTargetOrder(bj_lastCreatedUnit, "thunderbolt", whichUnit)
set StunnedTimer[id] = CreateTimer()
set StunnedUnit[HtoI(StunnedTimer[id])] = whichUnit
call TimerStart(StunnedTimer[id], secs, false, function UnstunUnit)
elseif (secs > TimerGetRemaining(StunnedTimer[id])) then
call PauseTimer(StunnedTimer[id])
call TimerStart(StunnedTimer[id], secs, false, function UnstunUnit)
endif
endif
endfunction
// *** Will slow given unit ***
private function UnslowUnit takes nothing returns nothing
local integer t_id = HtoI(GetExpiredTimer())
local integer u_id = HtoI(SlowedUnit[t_id])
call UnitRemoveAbility(SlowedUnit[t_id], 'Bfro')
call PauseTimer(SlowedTimer[u_id])
call DestroyTimer(SlowedTimer[u_id])
set SlowedUnit[t_id] = null
set SlowedTimer[u_id] = null
endfunction
function SlowUnit takes unit whichUnit, real secs returns nothing
local integer id = HtoI(whichUnit)
if (secs > 0.) then
if (SlowedTimer[id] == null) then
call UnitAddAbility(DDCreateDummy(DD_p[0], 1.), DD_SLOW_CODE)
call SetUnitPosition(bj_lastCreatedUnit, GetUnitX(whichUnit), GetUnitY(whichUnit))
call IssueTargetOrder(bj_lastCreatedUnit, "attack", whichUnit)
set SlowedTimer[id] = CreateTimer()
set SlowedUnit[HtoI(SlowedTimer[id])] = whichUnit
call TimerStart(SlowedTimer[id], secs, false, function UnslowUnit)
elseif (secs > TimerGetRemaining(SlowedTimer[id])) then
call PauseTimer(SlowedTimer[id])
call TimerStart(SlowedTimer[id], secs, false, function UnslowUnit)
endif
endif
endfunction
// *** Non-Slow actions ***
private function SlowLimits takes nothing returns nothing
if (GetUnitTypeId(GetAttacker()) == DD_DUMMY_CODE and GetUnitAbilityLevel(GetAttacker(), DD_SLOW_CODE) > 0 and SlowedTimer[HtoI(GetTriggerUnit())] == null) then
call UnitRemoveAbility(GetAttacker(), DD_DUMMY_CODE)
call IssueImmediateOrder(GetAttacker(), "stop")
endif
endfunction
// *** Will change units max life/mana ***
function SetUnitStateEx takes unit whichUnit, unitstate whichState, integer value returns nothing
local integer abil = DD_LIFE_EDIT_CODE
local integer slvl = 2
local integer lvl
local integer offset
local integer ln
local integer state = R2I(GetUnitState(whichUnit, whichState))
// *** Basic state change ***
if (whichState == UNIT_STATE_LIFE or whichState == UNIT_STATE_MANA) then
call SetUnitState(whichUnit, whichState, Real(value))
return
elseif (state == value) then
// *** Same values? ***
return
endif
// *** Get ability ***
//! runtextmacro IFX("whichState == UNIT_STATE_MAX_MANA", "set abil = DD_MANA_EDIT_CODE")
// *** Get start level ***
if (state > value) then
set slvl = 12
set state = state + value
set value = state - value
set state = state - value
endif
// *** Do calcs here ***
set ln = imin(log(2, value-state), 9)
set lvl = slvl + ln
set offset = ipow(2, ln)
loop
// *** Main loop 4x speed macro ***
//! textmacro ddup__UNIT_STATE_EDIT
call UnitAddAbility(whichUnit, abil)
call SetUnitAbilityLevel(whichUnit, abil, lvl)
call UnitRemoveAbility(whichUnit, abil)
set state = state + offset
if (state == value) then
exitwhen (true) // we could do that inside here but we want speed, since if is faster then exitwhen
endif
if (value-state < offset) then
set ln = log(2, value-state)
set lvl = slvl + ln
set offset = ipow(2, ln)
endif
//! endtextmacro
//! runtextmacro ddup__UNIT_STATE_EDIT()
//! runtextmacro ddup__UNIT_STATE_EDIT()
//! runtextmacro ddup__UNIT_STATE_EDIT()
//! runtextmacro ddup__UNIT_STATE_EDIT()
endloop
endfunction
// *** Checks is unit dead ***
constant function IsUnitDead takes unit whichUnit returns boolean
return GetUnitState(whichUnit, UNIT_STATE_LIFE) <= 0.405 or IsUnitType(whichUnit, UNIT_TYPE_DEAD)
endfunction
// -----------------------------------------------------------------------------
// *** Checks is unit invulnerable ***
function GetDamageFactorEx takes unit whichUnit, real factor returns real
local real life = GetWidgetLife(whichUnit)
local real newlife
call UnitDamageTarget(DD_Dummy, whichUnit, factor, true, false, null, null, null)
set newlife = GetWidgetLife(whichUnit)
call SetWidgetLife(whichUnit, life)
return (life - newlife)
endfunction
function IsUnitInvulnerable takes unit whichUnit returns boolean
return GetDamageFactorEx(whichUnit, 0.1) == 0.
endfunction
// Returns the dummy
constant function DDGetDummy takes nothing returns unit
return (DD_Dummy)
endfunction
// Sets dummy owner
function DDSetDummyOwner takes player owner returns nothing
call SetUnitOwner(DD_Dummy, owner, false)
endfunction
// *** Simple checks is point water ***
function IsPointWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
endfunction
// *** Damages area ***
function UnitDamageArea takes unit source, real damage, real x, real y, real radius, attacktype at, damagetype dt, weapontype wt, boolexpr filter returns nothing
// *** Load locals ***
local integer i
local groupex gx = CreateGroupEx()
// *** Enum units pick ***
call gx.EnumRadius(x, y, radius, filter)
// *** Do loop ***
//! runtextmacro FORX("i", "1", "gx.size+1", "call UnitDamageTarget(source, gx[i], damage, true, false, at, dt, wt)")
// *** Clear leaks ***
call DestroyGroupEx(gx)
endfunction
// *** Returns locations z ***
function DD_GetFloorZ takes real x, real y returns real
call MoveLocation(LOC_Z, x, y)
return GetLocationZ(LOC_Z)
endfunction
// *** Sets units foor z ***
function SetUnitFloorZ takes unit u, real z returns nothing
call UnitAddAbility(u, 'Amrf')
call UnitRemoveAbility(u, 'Amrf')
call MoveLocation(LOC_Z, GetUnitX(u), GetUnitY(u))
call SetUnitFlyHeight(u, z-GetLocationZ(LOC_Z), 0.)
endfunction
// -------------------------------------------------------------------------------
// *** TextTag Functions ***
function CreateTextTagUnit takes unit whichUnit, string text, real size, real timeout, dd_color clr returns texttag
set bj_lastCreatedTextTag = CreateTextTag()
call SetTextTagText(bj_lastCreatedTextTag, text, size*0.0023)
call SetTextTagPosUnit(bj_lastCreatedTextTag, whichUnit, 0.)
call SetTextTagColor(bj_lastCreatedTextTag, clr.r, clr.g, clr.b, clr.a)
call SetTextTagPermanent(bj_lastCreatedTextTag, false)
call SetTextTagFadepoint(bj_lastCreatedTextTag, timeout*0.66667)
call SetTextTagLifespan(bj_lastCreatedTextTag, timeout)
call SetTextTagVelocity(bj_lastCreatedTextTag, 0., 0.0355)
return (bj_lastCreatedTextTag)
endfunction
// *** Debug msg ***
function Msg takes string s returns nothing
call DisplayTimedTextToPlayer(DD_p[0], 0, 0, 30, s)
endfunction
function IMsg takes integer i returns nothing
call Msg(I2S(i))
endfunction
function RMsg takes real r returns nothing
call Msg(R2SW(r, 1, 5))
endfunction
function BMsg takes boolean flag returns nothing
if (flag) then
call Msg("true")
else
call Msg("false")
endif
endfunction
function HMsg takes handle h returns nothing
if (h == null) then
call Msg("(null)")
else
call IMsg(GetHandleId(h)-0x100000)
endif
endfunction
// ************************************************************************************
function TriggerRegisterAnyUnitEvent takes trigger trig, playerunitevent whichEvent returns nothing
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0x0], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0x1], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0x2], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0x3], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0x4], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0x5], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0x6], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0x7], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0x8], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0x9], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0xa], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0xb], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0xc], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0xd], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0xe], whichEvent, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(trig, DD_p[0xf], whichEvent, GetDummyFilter())
endfunction
function onTriggerEvent takes playerunitevent onEvent, code condition, code action returns trigger
set Trig = CreateTrigger()
//! runtextmacro IFX("onEvent != null", "call TriggerRegisterAnyUnitEvent(Trig, onEvent)")
//! runtextmacro IFX("condition != null", "call TriggerAddCondition(Trig, Condition(condition))")
//! runtextmacro IFX("action != null", "call TriggerAddActionDynamic(Trig, action)")
return (Trig)
endfunction
function onSpellCastRegister takes code cond, code act returns trigger
return onTriggerEvent(EVENT_PLAYER_UNIT_SPELL_CAST, cond, act)
endfunction
function onSpellEffectRegister takes code cond, code act returns trigger
return onTriggerEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, cond, act)
endfunction
function onSpellStopRegister takes code cond, code act returns trigger
return onTriggerEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, cond, act)
endfunction
function onSpellFinishRegister takes code cond, code act returns trigger
return onTriggerEvent(EVENT_PLAYER_UNIT_SPELL_FINISH, cond, act)
endfunction
function onUnitDeathRegister takes code cond, code act returns trigger
return onTriggerEvent(EVENT_PLAYER_UNIT_DEATH, cond, act)
endfunction
function onUnitAttackRegister takes code cond, code act returns trigger
return onTriggerEvent(EVENT_PLAYER_UNIT_ATTACKED, cond, act)
endfunction
// *** Our collide struct ***
private keyword PeriodicExecFunc
private struct collide
// *** Members ***
private unit u
private real radius
private groupex array gx[2]
private trigger t
private integer id
// *** Static members ***
private static integer Count
private static boolexpr Flt
private static integer i
private static unit array TrigUnit
private static collide Temp
private static collide array Grp[8191]
// *** pointer implement ***
implement optional dd_pointer
// *** constr ***
public static method create takes unit u, real r, trigger t returns collide
local collide this = collide.allocate()
set this.u = u
set this.radius = r
set this.t = t
set this.id = collide.Count
set this.gx[0] = groupex.create()
set this.gx[1] = groupex.create()
set collide.Grp[collide.Count] = this
set collide.Count = collide.Count + 1
if (collide.Count == 1) then
call NewThread(THREAD_DELAY, 0, callback.PeriodicExecFunc, false)
endif
call this.SetPtrObj(t)
return (this)
endmethod
// *** Main collide filter (when units collide) ***
private static method CollFilter takes nothing returns boolean
local unit u = GetFilterUnitEx()
local collide c = collide.Temp
if (not c.gx[0].Contains(u) and not (u == c.u)) then
call c.gx[0].AddUnit(u)
set collide.TrigUnit[0] = c.u
set collide.TrigUnit[1] = u
if (IsTriggerEnabled(c.t)) then
if (TriggerEvaluate(c.t)) then
call TriggerExecute(c.t)
endif
endif
endif
set u = null
return true
endmethod
// *** Clear action (units out of range) ***
private static method ClearAction takes nothing returns nothing
local collide c = collide.Temp
local unit u = GetEnumUnitEx()
if (not c.gx[1].Contains(u)) then
call c.gx[0].RemoveUnit(u)
endif
set u = null
endmethod
// *** Main exec code ***
public static method PeriodicExec takes nothing returns boolean
local collide c
set collide.i = 0
loop
//! textmacro collide__loop__COLLIDE_ACTIONS
if (collide.i >= collide.Count) then
exitwhen (true)
endif
set c = collide.Grp[collide.i]
set collide.Temp = c
call c.gx[1].EnumRadius(GetUnitX(c.u), GetUnitY(c.u), c.radius, collide.Flt)
call c.gx[0].For(function collide.ClearAction)
set collide.i = collide.i + 1
//! endtextmacro
//! runtextmacro collide__loop__COLLIDE_ACTIONS()
//! runtextmacro collide__loop__COLLIDE_ACTIONS()
//! runtextmacro collide__loop__COLLIDE_ACTIONS()
//! runtextmacro collide__loop__COLLIDE_ACTIONS()
//! runtextmacro collide__loop__COLLIDE_ACTIONS()
//! runtextmacro collide__loop__COLLIDE_ACTIONS()
//! runtextmacro collide__loop__COLLIDE_ACTIONS()
//! runtextmacro collide__loop__COLLIDE_ACTIONS()
endloop
// *** Clear trig units ***
set collide.TrigUnit[0] = null
set collide.TrigUnit[1] = null
set collide.i = -1
return (collide.Count == 0)
endmethod
// *** opers ***
public static constant method operator [] takes integer index returns unit
return collide.TrigUnit[index]
endmethod
// *** Destructor ***
public method delete takes nothing returns nothing
if (collide.i != -1) then
set collide.i = collide.i - 1
endif
call this.gx[0].delete()
call this.gx[1].delete()
set this.t = null
set this.u = null
set collide.Count = collide.Count - 1
set collide.Grp[this.id] = collide.Grp[collide.Count]
call this.destroy()
endmethod
// *** Initializer method ***
private static method onInit takes nothing returns nothing
set collide.Flt = Filter(function collide.CollFilter)
set collide.i = -1
set collide.Count = 0
endmethod
endstruct
// *** Main exec action
private function PeriodicExecFunc takes integer i returns boolean
return collide.PeriodicExec()
endfunction
constant function GetMainCollider takes nothing returns unit
return collide[0]
endfunction
constant function GetTriggerCollider takes nothing returns unit
return collide[1]
endfunction
function onUnitCollideUnregister takes trigger trig returns boolean
local collide cld = collide.GetPtrObj(trig)
if (cld != Null) then
call cld.delete()
return true
endif
return false
endfunction
function onUnitCollideRegister takes unit whichUnit, real radius, code cond, code act returns trigger
set Trig = CreateTrigger()
call collide.create(whichUnit, radius, Trig)
//! runtextmacro IFX("cond != null", "call TriggerAddCondition(Trig, Condition(cond))")
//! runtextmacro IFX("act != null", "call TriggerAddActionDynamic(Trig, act)")
return (Trig)
endfunction
// ====================================================================================
private function main takes nothing returns nothing
local integer i
// *** Set map min max values ***
set DDMinX = GetRectMinX(bj_mapInitialPlayableArea)+64.
set DDMinY = GetRectMinY(bj_mapInitialPlayableArea)+64.
set DDMaxX = GetRectMaxX(bj_mapInitialPlayableArea)-64.
set DDMaxY = GetRectMaxY(bj_mapInitialPlayableArea)-64.
// Sleep function timer start
call TimerStart(sleepTimer, 10800., true, null)
// Load all players
//! runtextmacro FORX("i", "0", "bj_MAX_PLAYER_SLOTS", "set i:DD_p = Player(i)")
// Seed
//set DD_Seed = 0
// Dummy
set DD_Dummy = DDCreateDummy(DD_p[0xf], 0.)
// OnDeath
call onUnitDeathRegister(null, function DoRemoveUnitOnDeath)
// onAttack
call onUnitAttackRegister(null, function SlowLimits)
endfunction
endlibrary
// *** An group of triggers ***
library_once DDFlock requires DDArray
// *** Main struct ***
struct flock
private integer n = 0
private static constant hashtable HT = InitHashtable()
private static integer array TrigId[8191]
/*
static method create takes nothing returns flock
local flock this = .allocate()
return (this)
endmethod
*/
// *** Destructor ***
public method delete takes nothing returns nothing
call FlushChildHashtable(flock.HT, (this))
set this.n = 0
call this.destroy()
endmethod
// *** Adds new trigger ***
public method Add takes trigger t returns boolean
local integer id = GetHandleId(t)
if (HaveSavedInteger(flock.HT, this, id) or t == null) then
return false
endif
set this.n = this.n + 1
call SaveInteger(flock.HT, this, id, this.n)
call SaveTriggerHandle(flock.HT, this, this.n, t)
return true
endmethod
// *** Removes added trigger ***
public method Remove takes trigger t returns boolean
local integer id = GetHandleId(t)
local integer id2 = LoadInteger(flock.HT, this, id)
if (id2 == 0 or t == null) then
return false
endif
set t = LoadTriggerHandle(flock.HT, this, this.n)
call RemoveSavedInteger(flock.HT, this, id)
call SaveTriggerHandle(flock.HT, this, id2, t)
call SaveInteger(flock.HT, this, GetHandleId(t), id2)
call RemoveSavedHandle(flock.HT, this, this.n)
set this.n = this.n - 1
return true
endmethod
// *** Clears all triggers ***
public method Clear takes nothing returns nothing
call FlushChildHashtable(flock.HT, this)
set this.n = 0
endmethod
// *** Does flock contain given trigger ***
public method Contains takes trigger t returns boolean
return HaveSavedInteger(flock.HT, this, GetHandleId(t))
endmethod
// *** Counts triggers in this flock ***
public method Count takes nothing returns integer
return this.n
endmethod
// *** Evaluate all triggers ***
public method Evaluate takes nothing returns nothing
local integer i = 1
local trigger t
loop
//! textmacro flock__Eval
exitwhen (i > this.n)
set t = LoadTriggerHandle(flock.HT, this, i)
if (IsTriggerEnabled(t)) then
call TriggerEvaluate(t)
endif
set i = i + 1
//! endtextmacro
// *** Macro 8x speed ***
//! runtextmacro flock__Eval()
//! runtextmacro flock__Eval()
//! runtextmacro flock__Eval()
//! runtextmacro flock__Eval()
//! runtextmacro flock__Eval()
//! runtextmacro flock__Eval()
//! runtextmacro flock__Eval()
//! runtextmacro flock__Eval()
endloop
set t = null
endmethod
// *** Executes all triggers ***
public method Execute takes nothing returns nothing
local integer i = 1
local trigger t
loop
//! textmacro flock__Exec
exitwhen (i > this.n)
set t = LoadTriggerHandle(flock.HT, this, i)
if (IsTriggerEnabled(t)) then
if (TriggerEvaluate(t)) then
call TriggerExecute(t)
endif
endif
set i = i + 1
//! endtextmacro
// *** Macro 8x speed ***
//! runtextmacro flock__Exec()
//! runtextmacro flock__Exec()
//! runtextmacro flock__Exec()
//! runtextmacro flock__Exec()
//! runtextmacro flock__Exec()
//! runtextmacro flock__Exec()
//! runtextmacro flock__Exec()
//! runtextmacro flock__Exec()
endloop
set t = null
endmethod
endstruct
// *** Flock Functions ***
function CreateFlock takes nothing returns flock
return flock.create()
endfunction
function DestroyFlock takes flock fl returns nothing
call fl.delete()
endfunction
function FlockAddTrigger takes flock fl, trigger trig returns boolean
return fl.Add(trig)
endfunction
function FlockRemoveTrigger takes flock fl, trigger trig returns boolean
return fl.Remove(trig)
endfunction
function FlockContainsTrigger takes flock fl, trigger trig returns boolean
return fl.Contains(trig)
endfunction
function FlockCountTriggers takes flock fl returns integer
return fl.Count()
endfunction
function FlockClear takes flock fl returns nothing
call fl.Clear()
endfunction
function FlockEvaluate takes flock fl returns nothing
call fl.Evaluate()
endfunction
function FlockExecute takes flock fl returns nothing
call fl.Execute()
endfunction
endlibrary
library_once DDArray
// *** Define dynamic array type ***
struct stack extends array
// ----------------------
// *** Members ***
// n -> N values
// size -> Max number of values
// isStack -> Type safe (check is stack given) (flag: 0 -> invalid typecast, 1 = created valid, 2 -> destroyed)
//
private integer n
private integer xsize
private integer isStack
// *** Static vars ***
private static constant hashtable TABLE = InitHashtable()
private static constant integer MID = 0x10000 // (2^16) = 65536
private static stack array Free
private static integer FCount = 0
private static stack Allocate = 0
// *** Constructor ***
static method create takes integer size returns stack
// *** Allocate ***
local stack this = stack.Allocate
// *** Immaginary number (non-existant stack) ***
if (size < 0) then
return (0)
endif
// *** First check if we have an free id ***
if (stack.FCount > 0) then
set stack.FCount = stack.FCount - 1
set this = stack.Free[stack.FCount]
else
// *** Get next id ***
set stack.Allocate = stack.Allocate + 1
endif
// *** Check is valid, do we have any more space? ***
if (this == JASS_MAX_ARRAY_SIZE) then
debug call Msg.execute("|cffff0000WARNING: Unable to allocate space for (stack). Stack is full|r")
set stack.Allocate = JASS_MAX_ARRAY_SIZE
return (0)
endif
// *** Assign values ***
set this.n = 0
set this.isStack = 1
set this.xsize = size
// *** Return our stack ***
return (this + stack.MID)
endmethod
// *** Destructor ***
public method delete takes nothing returns nothing
// *** This pointer get ***
set this = this - stack.MID
// *** Check is valid ***
if (this.isStack != 1) then
debug if (this.isStack == 0) then
debug call Msg.execute("|cffff0000WARNING: Invalid type_casted<> as stack!|r")
debug elseif (this.isStack == 2) then
debug call Msg.execute("|cffff0000WARNING: Double free of stack!|r")
debug endif
return
endif
// *** Free this stack ***
set this.n = 0
set this.xsize = 0
set this.isStack = 2
call FlushChildHashtable(stack.TABLE, (this))
// *** Destroy it and free ***
set stack.Free[stack.FCount] = this
set stack.FCount = stack.FCount + 1
endmethod
// *** Push method ***
public method push takes integer value returns nothing
// *** This pointer get ***
set this = this - stack.MID
// *** Type safe ***
if (this.isStack != 1) then
debug call Msg.execute("|cffff0000WARNING: Invalid stack inputed!|r")
return
endif
// *** Push value ***
call SaveInteger(stack.TABLE, (this), this.n, value)
set this.n = this.n + 1
if (this.n > this.xsize) then
set this.xsize = this.xsize + 1
endif
endmethod
// *** Pop method ***
public method pop takes nothing returns nothing
// *** This pointer get ***
set this = this - stack.MID
// *** Type safe ***
if (this.isStack != 1) then
debug call Msg.execute("|cffff0000WARNING: Invalid stack inputed!|r")
return
endif
// *** Pop value ***
set this.n = this.n - 1
//call SaveInteger(stack.TABLE, (this), this.n, 0)
call RemoveSavedInteger(stack.TABLE, (this), this.n)
endmethod
// *** Peek method ***
public method peek takes nothing returns integer
// *** This pointer get ***
set this = this - stack.MID
// *** Type safe ***
if (this.isStack != 1) then
debug call Msg.execute("|cffff0000WARNING: Invalid stack inputed!|r")
return 0
endif
// *** Return value ***
if (this.n > 0) then
return LoadInteger(stack.TABLE, (this), this.n-1)
endif
return (0)
endmethod
// *** Free method ***
public method free takes nothing returns nothing
// *** This pointer get ***
set this = this - stack.MID
// *** Type safe ***
if (this.isStack != 1) then
debug call Msg.execute("|cffff0000WARNING: Invalid stack inputed!|r")
return
endif
// *** Free stack here ***
set this.n = 0
set this.xsize = 0
call FlushChildHashtable(stack.TABLE, (this))
endmethod
// --------------------
// *** Operators ***
// --------------------
// *** Returns value on index ***
public method operator [] takes integer index returns integer
// *** This pointer get ***
set this = this - stack.MID
// *** Type safe ***
if (this.isStack != 1) then
debug call Msg.execute("|cffff0000WARNING: Invalid stack inputed!|r")
return 0
endif
// *** Valid index? ***
if (index >= 0 and index < this.xsize and HaveSavedInteger(stack.TABLE, (this), index)) then
return LoadInteger(stack.TABLE, (this), index)
endif
return 0
endmethod
// *** Sets or swaps value ***
public method operator []= takes integer index, integer value returns nothing
// *** This pointer get ***
set this = this - stack.MID
// *** Type safe ***
if (this.isStack != 1) then
debug call Msg.execute("|cffff0000WARNING: Invalid stack inputed!|r")
return
endif
// *** Valid index? ***
if (index >= 0 and index <= this.n) then
if (index == this.n) then
set this.n = this.n + 1
endif
call SaveInteger(stack.TABLE, (this), index, value)
endif
endmethod
// *** Size setup ***
public method operator size takes nothing returns integer
return stack(this-stack.MID).xsize
endmethod
// *** Size set ***
public method operator size= takes integer size returns nothing
if (size >= 0) then
set stack(this-stack.MID).xsize = size
endif
endmethod
// *** Length of stack ***
public method operator length takes nothing returns integer
return stack(this-stack.MID).n
endmethod
endstruct
// -------------------------------------------------------------------------
// *** Stack functions ***
// -------------------------------------------------------------------------
// *** Allocates an new stack ***
function CreateStack takes integer size returns stack
return stack.create(size)
endfunction
// *** Destroys stack ***
function DestroyStack takes stack whichStack returns nothing
call whichStack.delete()
endfunction
// *** Push value ***
function StackPush takes stack st, integer value returns nothing
call st.push(value)
endfunction
// *** Pops value ***
function StackPop takes stack st returns nothing
call st.pop()
endfunction
// *** Peek stack ***
function StackPeek takes stack st returns integer
return st.peek()
endfunction
// *** Frees stack ***
function FreeStack takes stack st returns nothing
call st.free()
endfunction
// *** Sets new size ***
function StackSetSize takes stack st, integer sizex returns nothing
set st.size = sizex
endfunction
// *** Returns stacks size ***
function StackGetSize takes stack st returns integer
return st.size
endfunction
// *** Returns length ***
function StackGetLength takes stack st returns integer
return st.length
endfunction
// *** Stacks value ***
function StackGetValue takes stack st, integer index returns integer
return st[index]
endfunction
// *** Stack set value (push) ***
function StackSetValue takes stack st, integer index, integer value returns nothing
set st[index] = value
endfunction
endlibrary
library_once DDMissileEngine initializer Init requires DDFlock, DDMath, DDDamageDetect, DDArray, DDGroup, NativeDDUniversalPack
private keyword data
// *** Editable stuff ***
globals
// *** Editable constants ***
private constant integer INTERVAL = 50
private real array DamagePoint[8191]
private real array MissileSpeed[8191]
private integer array UnitType[8191]
private integer UN = 0
endglobals
//! textmacro DEFINE_MISSILE_UNIT takes ID, DP, MS
set UN = UN + 1
set UnitType[UN] = '$ID$'
set DamagePoint[UN] = $DP$
set MissileSpeed[UN] = $MS$
//! endtextmacro
// *** Predefined ***
private function Define takes nothing returns nothing
// *** Human ***
//! runtextmacro DEFINE_MISSILE_UNIT("hrif", ".17", "1900.")
//! runtextmacro DEFINE_MISSILE_UNIT("hgry", ".8", "450.")
//! runtextmacro DEFINE_MISSILE_UNIT("hmpr", ".59", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("hsor", ".75", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("hspt", ".5", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("hdhw", ".43", "1100.")
//! runtextmacro DEFINE_MISSILE_UNIT("Hamg", ".55", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("Hblm", ".55", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("hwat", ".4", "1300.")
//! runtextmacro DEFINE_MISSILE_UNIT("hwt2", ".4", "1300.")
//! runtextmacro DEFINE_MISSILE_UNIT("hwt3", ".4", "1300.")
//! runtextmacro DEFINE_MISSILE_UNIT("hphx", ".43", "1800.")
// *** Orc ***
//! runtextmacro DEFINE_MISSILE_UNIT("ohun", ".31", "1300.")
//! runtextmacro DEFINE_MISSILE_UNIT("okod", ".85", "1200.")
//! runtextmacro DEFINE_MISSILE_UNIT("owyv", ".6", "1200.")
//! runtextmacro DEFINE_MISSILE_UNIT("otbr", ".6", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("odoc", ".73", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("oshm", ".43", "1200.")
//! runtextmacro DEFINE_MISSILE_UNIT("ospw", ".5", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("Ofar", ".3", "1200.")
//! runtextmacro DEFINE_MISSILE_UNIT("Oshd", ".3", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("osp1", ".3", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("osp2", ".3", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("osp3", ".3", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("ospm", ".5", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("otbk", ".31", "1300.")
// *** Undead ***
//! runtextmacro DEFINE_MISSILE_UNIT("ucry", ".64", "500.")
//! runtextmacro DEFINE_MISSILE_UNIT("uban", ".56", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("unec", ".53", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("uobs", ".53", "1200.")
//! runtextmacro DEFINE_MISSILE_UNIT("ufro", ".5", "800.")
//! runtextmacro DEFINE_MISSILE_UNIT("ulic", ".46", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("uskm", ".5", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("ubsp", ".633", "900.")
// *** Night Elf ***
//! runtextmacro DEFINE_MISSILE_UNIT("earc", ".72", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("esen", ".46", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("edry", ".3", "1000.")
//! runtextmacro DEFINE_MISSILE_UNIT("ehpr", ".633", "1500.")
//! runtextmacro DEFINE_MISSILE_UNIT("edot", ".5", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("efdr", ".5", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("Ekee", ".4", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("Emoo", ".3", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("Edmm", ".26", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("edtm", ".6", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("espv", ".33", "1200.")
//! runtextmacro DEFINE_MISSILE_UNIT("even", ".33", "1200.")
// *** Neutral Heros ***
//! runtextmacro DEFINE_MISSILE_UNIT("Nngs", ".73", "1200.")
//! runtextmacro DEFINE_MISSILE_UNIT("Ntin", ".35", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("Nbrn", ".7", "900.")
//! runtextmacro DEFINE_MISSILE_UNIT("Nfir", ".5", "1200.")
endfunction
globals
private flock Trigs = Null
private flock array UnitTrigs[8191]
private boolean array SingleEvent[8191]
private boolean EventType = false
private data Dat = Null
private integer array PtrId[8191]
endglobals
// ------------------------------------------------------------------
// ------------------------------------------------------------------------------
private function GetUnitId takes unit whichUnit returns integer
local integer id = HtoI(whichUnit)
local integer utyp = GetUnitTypeId(whichUnit)
local integer i
if (PtrId[id] == 0) then
//! runtextmacro FOR("i", "1", "UN+1")
if (UnitType[i] == utyp) then
set PtrId[id] = i
return (i)
endif
//! runtextmacro ENDFOR()
else
if (UnitType[id:PtrId] != utyp) then
set PtrId[id] = 0
return GetUnitId(whichUnit)
endif
endif
return (PtrId[id])
endfunction
// ---------------------------------------------------------------------
// *** Basic functions for ranged units damage point and missile speed
function GetUnitDamagePoint takes unit whichUnit returns real
return DamagePoint[GetUnitId(whichUnit)]
endfunction
function GetUnitMissileSpeed takes unit whichUnit returns real
return MissileSpeed[GetUnitId(whichUnit)]
endfunction
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// *** User defines some constants ***
function DefineUnitMissile takes integer uid, real missile_speed, real damage_point returns nothing
local integer i
//! runtextmacro FOR("i", "0", "UN")
//! runtextmacro IFX("i:UnitType == uid", "return")
//! runtextmacro ENDFOR()
set UnitType[UN] = uid
set MissileSpeed[UN] = missile_speed
set DamagePoint[UN] = damage_point
set UN = UN + 1
endfunction
// ---------------------------------------------------------------------
// ------------------------------------------------------------------------------
private struct data
unit shooter
unit target
dd_vector vec
real vel
real dist
static real Radians = 0.
static method create takes unit u returns data
local data this = .allocate()
set this.shooter = u
set this.target = GetTriggerUnit()
set this.vec = GetUnitVPos(u)
set this.vel = GetUnitMissileSpeed(u)*INTERVAL*.001
set this.dist = 0.
return (this)
endmethod
method delete takes nothing returns nothing
set this.shooter = null
set this.target = null
call this.vec.delete()
call this.destroy()
endmethod
method operator angle takes nothing returns real
return Atan2(GetUnitY(this.target)-this.vec.y, GetUnitX(this.target)-this.vec.x)
endmethod
endstruct
private function Travel takes data d returns boolean
local integer id
if (not IsUnitInRangeXY(d.target, d.vec.x, d.vec.y, d.vel)) then
set data.Radians = d.angle
set d.dist = d.dist + d.vel
call d.vec.Move(d.vec.x+d.vel*Cos(data.Radians), d.vec.y+d.vel*Sin(data.Radians), 0.)
else
set Dat = d
set EventType = false
set id = HtoI(d.shooter)
set data.Radians = d.angle
set d.dist = d.dist + SquareRoot(hypot(d.vec.x-GetUnitX(d.target), d.vec.y-GetUnitY(d.target)))
if (FlockCountTriggers(Trigs) > 0) then
call FlockExecute(Trigs)
endif
if (id:UnitTrigs != Null) then
call FlockExecute(id:UnitTrigs)
endif
call DDFlushValueEx(d.shooter, GetHandleId(d.target))
call d.delete()
set Dat = Null
return (true)
endif
return (false)
endfunction
private function FireMissile takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id
set Dat = DDGetValue(t)
set EventType = true
set id = HtoI(Dat.shooter)
if (FlockCountTriggers(Trigs) > 0) then
call FlockExecute(Trigs)
endif
if (id:UnitTrigs != Null) then
call FlockExecute(id:UnitTrigs)
endif
call NewThread(INTERVAL, Dat, callback.Travel, false)
call DDFlushValue(t)
call PauseTimer(t)
call DestroyTimer(t)
set Dat = Null
set t = null
endfunction
private function Actions takes nothing returns boolean
local unit u
local timer t
if (GetTriggerEventId() == EVENT_PLAYER_UNIT_ATTACKED) then
set u = GetAttacker()
if (GetUnitMissileSpeed(u) != 0.) then
if (HtoI(u):UnitTrigs != Null or FlockCountTriggers(Trigs) > 0) then
set t = CreateTimer()
call DDStoreValue(t, data.create(u))
call TimerStart(t, GetUnitDamagePoint(u), false, function FireMissile)
set t = null
endif
endif
set u = null
else
set u = GetEventDamageSource()
if (GetUnitMissileSpeed(u) != 0.) then
if (FlockCountTriggers(Trigs) > 0 or HtoI(u):UnitTrigs != Null) then
call DDStoreValueEx(u, GetHandleId(GetTriggerUnit()), 1)
endif
endif
set u = null
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEvent(t, EVENT_PLAYER_UNIT_ATTACKED)
call TriggerRegisterAnyUnitDamaged(t)
call TriggerAddCondition(t, Condition(function Actions))
// *** Presets ***
call Define()
endfunction
// ===================================================================================================
// *** Main global functions ***
// ===================================================================================================
// *** Registers any ranged unit missile fire event, if no single events are registerd ***
function TriggerRegisterAnyMissileEvent takes trigger trig returns boolean
if (not HtoI(trig):SingleEvent) then
return FlockAddTrigger(Trigs, trig)
endif
return (false)
endfunction
// *** Registers single event if "any" is not registerd ***
function TriggerRegisterMissileEvent takes trigger trig, unit whichUnit returns boolean
local integer id = HtoI(whichUnit)
local flock fl = UnitTrigs[id]
if (not FlockContainsTrigger(Trigs, trig)) then
if (fl == Null) then
set fl = CreateFlock()
set UnitTrigs[id] = fl
endif
set HtoI(trig):SingleEvent = true
return FlockAddTrigger(fl, trig)
endif
return (false)
endfunction
// *** Unregisters any missile fire event ***
function TriggerUnregisterAnyMissileEvent takes trigger trig returns boolean
return FlockRemoveTrigger(Trigs, trig)
endfunction
// *** Unregisters single missile event ***
function TriggerUnregisterMissileEvent takes trigger trig, unit whichUnit returns boolean
local integer id = HtoI(whichUnit)
local flock fl = UnitTrigs[id]
local boolean flag = false
if (fl != Null) then
set flag = FlockRemoveTrigger(fl, trig)
if (FlockCountTriggers(fl) == 0) then
call DestroyFlock(fl)
set UnitTrigs[id] = Null
endif
endif
return (flag)
endfunction
// *** Returns true if missile is fired or false if missile hit the target
constant function IsMissileFiredEvent takes nothing returns boolean
return (EventType)
endfunction
// *** if missile missed or smth then this returns false
function IsMissileImpactSuccess takes nothing returns boolean
return (DDGetValueEx(Dat.shooter, GetHandleId(Dat.target)) != 0 and Dat.dist > 0.)
endfunction
// *** Returns target which missile did hit ***
constant function GetMissileTarget takes nothing returns unit
return Dat.target
endfunction
// *** Returns missile shooter ***
constant function GetMissileShooter takes nothing returns unit
return Dat.shooter
endfunction
// *** Returns missile travel distance ***
constant function GetMissileTravelDistance takes nothing returns real
return Dat.dist
endfunction
// *** From which angle in radians did missile hit the target ***
constant function GetMissileImpactAngle takes nothing returns real
return data.Radians
endfunction
// *** Returns missile travel time ***
constant function GetMissileTravelTime takes nothing returns real
return Dat.dist/(Dat.vel/INTERVAL/.001)
endfunction
endlibrary
library_once DDStrings requires DDMath
// --- Cheks is string inside string ---
function StringContainsStringByPos takes string source, string search, boolean left, integer pos returns integer
// *** Locals ***
local integer maxi = StringLength(source)
local integer strl = StringLength(search)
// Searched string is longer?
//! runtextmacro IFX("strl > maxi", "return -1")
if (left) then
loop
exitwhen (pos+strl > maxi)
if (SubString(source, pos, pos+strl) == search) then
return (pos)
endif
set pos = pos + 1
exitwhen (pos+strl > maxi)
if (SubString(source, pos, pos+strl) == search) then
return (pos)
endif
set pos = pos + 1
endloop
else
loop
exitwhen (pos-strl < 0)
if (SubString(source, pos-strl, pos) == search) then
return (pos-strl)
endif
set pos = pos - 1
exitwhen (pos-strl < 0)
if (SubString(source, pos-strl, pos) == search) then
return (pos-strl)
endif
set pos = pos - 1
endloop
endif
return -1
endfunction
// --- Cheks is string inside string ---
function StringContainsString takes string source, string search, boolean left returns integer
if (left) then
return StringContainsStringByPos(source, search, true, 0)
endif
return StringContainsStringByPos(source, search, false, StringLength(source))
endfunction
// *** String contains string bool version? ***
function StringContainsStringEx takes string source, string search, boolean left returns boolean
return StringContainsString(source, search, left) != -1
endfunction
// *** Replace given file with new string ***
function SwapString takes string file, string old, string new returns string
local integer len = StringLength(file)
local integer nlen = StringLength(new)
local integer olen = StringLength(old)
local integer i
//! runtextmacro FOR("i", "0", "len-olen+1")
if (SubString(file, i, i+olen) == old) then
set file = SubString(file, 0, i)+new+SubString(file, i+olen, len)
set len = len + (nlen-olen)
set i = i - 1
endif
//! runtextmacro ENDFOR()
return (file)
endfunction
// *** Counts substrings ***
function CountSubString takes string source, string count returns integer
local integer i
local integer len = StringLength(source)
local integer clen = StringLength(count)
local integer cnt = 0
//! runtextmacro IFX("len < clen", "return 0")
//! runtextmacro FOR("i", "0", "len-clen+1")
//! runtextmacro IFX("SubString(source, i, i+clen) == count", "set cnt = cnt + 1")
//! runtextmacro ENDFOR()
return cnt
endfunction
// *** Removes spaces from given string at start and end ***
function TrimString takes string source returns string
local boolean array check
local integer len = StringLength(source)
set check[0] = true
set check[1] = true
loop
if (check[0]) then
if (SubString(source, 0, 1) == " ") then
set source = SubString(source, 1, len)
set len = len - 1
else
set check[0] = false
endif
endif
if (check[1]) then
if (SubString(source, len-1, len) == " ") then
set source = SubString(source, 0, len-1)
set len = len - 1
else
set check[1] = false
endif
endif
exitwhen ( not (check[0] or check[1]) )
endloop
return (source)
endfunction
// *** Injects string ***
function InjectString takes string source, string inj, integer start, integer end returns string
return SubString(source, 0, start)+inj+SubString(source, end, StringLength(source))
endfunction
// *** Main custom type for strings ***
struct stringx
// *** Members ***
private string str
private integer len
// *** Constructors ***
//! runtextmacro constr("new", "nothing")
set this.str = null
set this.len = 0
//! runtextmacro endconstr()
//! runtextmacro constr("create", "string str")
set this.str = str
set this.len = StringLength(str)
//! runtextmacro endconstr()
//! runtextmacro constr("copy", "stringx strx")
set this.str = strx.str
set this.len = strx.len
//! runtextmacro endconstr()
// *** Destructor ***
//! runtextmacro destr()
set this.str = null
set this.len = 0
//! runtextmacro enddestr()
// *** Display this string ***
public method Display takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 30., this.str)
endmethod
// *** SubString Set ***
public method SetSub takes string new, integer start returns nothing
local integer len = StringLength(new)
set this.str = SubString(this.str, 0, start)+new+SubString(this.str, start+len, this.len)
set this.len = imax(start+len, this.len)
endmethod
// *** SubString Get ***
public method GetSub takes integer start, integer end returns string
return SubString(this.str, start, end)
endmethod
// *** Counts string ***
public method CountSub takes string sub returns integer
return CountSubString(this.str, sub)
endmethod
// *** Set string ***
public method operator string= takes string str returns nothing
set this.str = str
set this.len = StringLength(str)
endmethod
// *** Get string ***
public method operator string takes nothing returns string
return this.str
endmethod
// *** Get length ***
public method operator length takes nothing returns integer
return this.len
endmethod
// *** Char get oper ***
public method operator [] takes integer index returns string
return SubString(this.str, index, index+1)
endmethod
// *** Char set oper ***
public method operator []= takes integer index, string char returns nothing
set this.str = SubString(this.str, 0, index)+char+SubString(this.str, index+1, this.len)
endmethod
// *** Oper < ***
public method operator < takes stringx strx returns boolean
return this.len < strx.len
endmethod
endstruct
// *** Array string ***
type strings extends string array [16]
// *** Allocates new strings ***
function CreateStrings takes nothing returns strings
return strings.create()
endfunction
// *** Deletes given strings ***
function DestroyStrings takes strings strs returns nothing
local integer i
//! runtextmacro FORX("i", "0", "strings.size", "set strs[i] = null")
call strs.destroy()
endfunction
//! textmacro constant_strings__PARSE_STRING_CHAR
set temp = SubString(file, i, i+1)
if (temp != " " and temp != ",") then
if (strs[j] != null) then
set strs[j] = strs[j] + temp
else
set strs[j] = temp
endif
elseif (temp == ",") then
set j = j + 1
endif
set i = i + 1
// *** exit when we reach end or strings are full ***
exitwhen ((i >= len) or (j >= 0xF))
//! endtextmacro
function ParseString takes string file returns strings
// *** Locals ***
local strings strs = strings.create()
local integer len = StringLength(file)
local string temp
local integer i=0
local integer j=0
// *** Loop through file ***
loop
//! runtextmacro constant_strings__PARSE_STRING_CHAR()
//! runtextmacro constant_strings__PARSE_STRING_CHAR()
//! runtextmacro constant_strings__PARSE_STRING_CHAR()
//! runtextmacro constant_strings__PARSE_STRING_CHAR()
//! runtextmacro constant_strings__PARSE_STRING_CHAR()
//! runtextmacro constant_strings__PARSE_STRING_CHAR()
//! runtextmacro constant_strings__PARSE_STRING_CHAR()
//! runtextmacro constant_strings__PARSE_STRING_CHAR()
endloop
// *** Now return our strings ***
return (strs)
endfunction
endlibrary
// *** library_once which is requied by all others ***
// *** Handles memory and functions for quick memory access ***
// *** Main Dark Dragon library_once ***
library_once DD initializer Init
// *** Globals vars ***
globals
// *** Objects null ***
constant integer Null = 0
// *** non-leaking filter ***
private filterfunc this_ff = null
// *** exec code force ***
private constant force CODE_FORCE = CreateForce()
// *** Handle to integer conversion ***
private integer Id_Count = 0
private constant hashtable ID_TABLE = InitHashtable()
endglobals
//! INLINED!!
// *** Converts handle id to array id ***
function HtoI takes handle h returns integer
// return GetHandleId(h) - 0x100000
local integer id = GetHandleId(h) - 0x100000
local integer id2 = LoadInteger(ID_TABLE, id, 0)
if (id2 != 0) then
return (id2)
endif
set Id_Count = Id_Count + 1
call SaveInteger(ID_TABLE, id, 0, Id_Count)
return (Id_Count)
endfunction
//! INLINED!!
// *** Converts integer to real ***
constant function Real takes integer int returns real
return (int*1.)
endfunction
// *** Quick Modulo Integer ***
constant function QMod takes integer a, integer b returns integer
return a-(a/b)*b
endfunction
// *** Array id read ***
function ArrayId takes handle h returns integer
local integer id = GetHandleId(h)
return id - (id/JASS_MAX_ARRAY_SIZE) * JASS_MAX_ARRAY_SIZE
endfunction
// *** Non-leaking function for filter ***
private constant function DummyBool takes nothing returns boolean
return (true)
endfunction
// *** Allows to set code insted of condition ***
function TriggerAddConditionEx takes trigger t, code c returns triggercondition
return TriggerAddCondition(t, Condition(c))
endfunction
// *** Returns dummy filter ***
constant function GetDummyFilter takes nothing returns filterfunc
return this_ff
endfunction
// *** Will execute given code ***
// *** It is important to know that this cant be called twice "ExecuteCode inside ExecuteCode" ***
function ExecuteCode takes code func returns nothing
call ForForce(CODE_FORCE, func)
endfunction
// *** Init function ***
private function Init takes nothing returns nothing
set this_ff = Filter(function DummyBool)
call ForceAddPlayer(CODE_FORCE, Player(0xF))
endfunction
endlibrary
// **************************************************
// *** Main math library_once ***
// **************************************************
library_once DDMath
// ----------------------------------------------------------------------
// *** Custom math constants ***
// ----------------------------------------------------------------------
globals
constant real PI = 3.14159
constant real TWO_PI = 6.28319
constant real PI_HALF = 1.57080
constant real E = 2.71828
constant real DEGTORAD = 0.01745
constant real RADTODEG = 57.29578
constant real GRAVITY_ACC = 9.80665
constant real M_TO_C = 209.97375
constant real C_TO_M = 0.00476
constant real SQRT_2 = 1.41421
constant real SQRT_3 = 1.73205
constant real LN2 = 0.69315
constant real LN10 = 2.30258
endglobals
// ---------------------------------------------------------------------
// *** Custom math functions ***
// ---------------------------------------------------------------------
// *** Returns lesser numner ***
constant function imin takes integer a, integer b returns integer
if (a < b) then
return (a)
endif
return (b)
endfunction
// *** Returns greater numner ***
constant function imax takes integer a, integer b returns integer
if (a > b) then
return (a)
endif
return (b)
endfunction
// *** Returns lesser numner ***
constant function rmin takes real x, real y returns real
if (x < y) then
return (x)
endif
return (y)
endfunction
// *** Returns greater numner ***
constant function rmax takes real x, real y returns real
if (x > y) then
return (x)
endif
return (y)
endfunction
// *** Returns remainder of div operator ***
constant function imod takes integer a, integer b returns integer
set a = a - (a/b) * b
if (a < 0) then
set a = a + b
endif
return (a)
endfunction
// *** Returns remainder of div operator ***
function rmod takes real x, real y returns real
set x = x - R2I(x/y) * y
if (x < 0) then
set x = x + y
endif
return (x)
endfunction
// *** Calculates x^y ***
constant function ipow takes integer x, integer y returns integer
if (y == 0) then
return 1
elseif (y < 0) then
return 0
endif
return ipow(x, y-1)*x
endfunction
// *** Calculates x^i ***
constant function rpow takes real x, integer y returns real
if (y == 0) then
return 1.
elseif (y < 0) then
return 1./rpow(x, -y)
endif
return rpow(x, y-1)*x
endfunction
// *** Returns log from given values ***
constant function log takes integer base, integer result returns integer
if (result == 1) then
return 0
endif
return log(base, result/base)+1
endfunction
// *** credits go to "BlinkBoy" for Ln ***
function Ln takes real a returns real
local real s = 1.0
local real sum = 0.0
local real g
local real e
local integer i = 10
loop
exitwhen a < bj_E
set a = a/bj_E
set sum = sum + 1.
endloop
set e = (a-1.0)/10. //quite accurate for numbers < e
set g = s + e
loop
exitwhen i <= 0 or a <= 1.0
set i = i-1
set sum = sum + (g-s)*(1./s + 8./(s+g) + 1./g)/6.
set s = g
set g = s + e
endloop
return sum
endfunction
// *** using Ln for calculations with base ***
function RLog takes real base, real result returns real
if (base == E) then
return Ln(result)
elseif (base == 10.) then
return Ln(result)/LN10
elseif (base == 2.) then
return Ln(result)/LN2
endif
return Ln(result)/Ln(base)
endfunction
// *** Returns square width and height ***
constant function hypot takes real w, real h returns real
return w*w + h*h
endfunction
// *** Gives limits to integer ***
constant function limits takes integer i, integer min, integer max returns integer
if (i < min) then
set i = min
elseif (i > max) then
set i = max
endif
return i
endfunction
// *** Gives limits to real ***
constant function rlimits takes real x, real min, real max returns real
if (x < min) then
set x = min
elseif (x > max) then
set x = max
endif
return x
endfunction
// *** Returns absolute value of x ***
constant function iabs takes integer a returns integer
if (a < 0) then
return -a
endif
return a
endfunction
// *** Returns absolute value of x ***
constant function rabs takes real x returns real
if (x < 0) then
return -x
endif
return x
endfunction
// *** Returns sign of x ***
constant function isign takes integer a returns integer
if (a < 0) then
return -1
endif
return 1
endfunction
// *** Returns sign of x ***
constant function rsign takes real x returns real
if (x < 0) then
return -1.
endif
return 1.
endfunction
// Returns rounded integer ***
// *** optimized thanks to "azlier"
function round takes real rl returns integer
//! runtextmacro IFX("rl > 0.", "return R2I(rl+.5)")
return R2I(rl-.5)
endfunction
// Returns rounded real
function rround takes real rl returns real
return Real(round(rl)) // inlined
endfunction
// *** Returns the lessest number ***
constant function Min4x takes real a, real b, real c, real d returns real
if (a < b and a < c and a < d) then
return (a)
elseif (b < c and b < d) then
return (b)
elseif (c < d) then
return (c)
endif
return (d)
endfunction
// *** Returns the largest number ***
constant function Max4x takes real a, real b, real c, real d returns real
if (a > b and a > c and a > d) then
return (a)
elseif (b > c and b > d) then
return (b)
elseif (c > d) then
return (c)
endif
return (d)
endfunction
// *** Cotangent ***
function Ctan takes real radians returns real
return 1. / Tan(radians)
endfunction
// *** Checks is given angle in circle angle with x,y offsets ***
function IsAngleInAngle takes real radians, real base_radians, real x_offset, real y_offset returns boolean
local boolean flag
set radians = rmod(radians, TWO_PI)
set base_radians = rmod(base_radians, TWO_PI)
if (base_radians+x_offset > TWO_PI) then
set flag = radians <= x_offset-TWO_PI+base_radians or radians >= base_radians
else
set flag = radians <= base_radians+x_offset
endif
if (base_radians-y_offset < 0.) then
set flag = flag or radians >= TWO_PI-y_offset+base_radians or radians <= base_radians
else
set flag = flag or radians >= base_radians-y_offset
endif
return (flag)
endfunction
// --- Type Double ---
globals
private integer array DoubleSize[8191]
endglobals
// ------------------------------------------------------
// *** Custom type double ***
type double extends real array [32]
// *** Creates new double ***
function CreateDouble takes integer size returns double
local double db = double.create()
set DoubleSize[integer(db)] = limits(size, 1, double.size)
return (db)
endfunction
// *** Destroys the double ***
function DestroyDouble takes double db returns nothing
//! runtextmacro FORX("DoubleSize[0]", "0", "DoubleSize[db]", "set db[DoubleSize[0]] = 0.")
set DoubleSize[integer(db)] = 0
call db.destroy()
endfunction
// *** Resets double values ***
function ResetDouble takes double db returns nothing
//! runtextmacro FORX("DoubleSize[0]", "0", "DoubleSize[db]", "set db[DoubleSize[0]] = 0.")
endfunction
// *** Double min get ***
function DMin takes double db returns real
local integer size = DoubleSize[integer(db)]
local real min = db[size-1]
set size = size - 2
loop
exitwhen (size <= 0)
if (min > db[size]) then
set min = db[size]
endif
set size = size - 1
endloop
return min
endfunction
// *** Double max get ***
function DMax takes double db returns real
local integer size = DoubleSize[integer(db)]
local real max = db[size-1]
set size = size - 2
loop
exitwhen (size <= 0)
if (max < db[size]) then
set max = db[size]
endif
set size = size - 1
endloop
return max
endfunction
// *** Sorts double ***
function DSort takes double db, boolean min returns nothing
// *** Locals ***
local integer i
local integer j
// *** Loop through double ***
//! runtextmacro FOR("i", "0", "DoubleSize[db]-1")
//! runtextmacro FOR("j", "i+1", "DoubleSize[db]")
if (min) then
if (db[i] > db[j]) then
set db[i] = db[i] + db[j]
set db[j] = db[i] - db[j]
set db[i] = db[i] - db[j]
endif
else
if (db[i] < db[j]) then
set db[i] = db[i] + db[j]
set db[j] = db[i] - db[j]
set db[i] = db[i] - db[j]
endif
endif
//! runtextmacro ENDFOR()
//! runtextmacro ENDFOR()
endfunction
// *** Displays double ***
function DDisplay takes double db returns nothing
// *** Locals ***
local integer i
local string str = "|cff32cd32"
//! runtextmacro FOR("i", "0", "DoubleSize[db]")
set str = str + R2S(db[i]) + ", "
if (QMod(i+1, 5) == 0) then
set str = str + "\n"
endif
//! runtextmacro ENDFOR()
set str = SubString(str, 0, StringLength(str)-2) + "|r"
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 60., str)
endfunction
// *** Returns double size ***
constant function GetDoubleSize takes double db returns integer
return DoubleSize[integer(db)]
endfunction
// *** Main math type ***
function interface math takes real x returns real
private function ex_pow takes real x, real y returns real
return Pow(x, y)
endfunction
// *** Math float type ***
struct float
private real f
private real min
private real max
private boolean lim
public static float Zero = 0
//! runtextmacro constr("new", "nothing")
set this.f = 0.
//! runtextmacro endconstr()
//! runtextmacro constr("create", "real f")
set this.f = f
//! runtextmacro endconstr()
//! runtextmacro constr("copy", "float fl")
set this.f = fl.f
set this.min = fl.min
set this.max = fl.max
set this.lim = fl.lim
//! runtextmacro endconstr()
//! runtextmacro destr()
set this.f = 0
set this.min = 0
set this.max = 0
set this.lim = false
//! runtextmacro enddestr()
public method Limits takes real min, real max returns nothing
set this.min = min
set this.max = max
set this.lim = true
set this.f = rlimits(this.f, min, max)
endmethod
public method NoLimits takes nothing returns nothing
set this.lim = false
endmethod
public method operator val= takes real x returns nothing
if (this.lim) then
if (x < this.min) then
set x = this.min
elseif (x > this.max) then
set x = this.max
endif
endif
set this.f = x
endmethod
public method operator val takes nothing returns real
return this.f
endmethod
public method Add takes real x returns nothing
set this.val = this.f + x
endmethod
public method Sub takes real x returns nothing
set this.val = this.f - x
endmethod
public method Mul takes real x returns nothing
set this.val = this.f * x
endmethod
public method Div takes real x returns nothing
set this.val = this.f / x
endmethod
public method Pow takes real x returns nothing
set this.val = ex_pow(this.f, x)
endmethod
public method Sqrt takes nothing returns nothing
set this.val = SquareRoot(this.f)
endmethod
public method Mod takes real x returns nothing
set this.val = rmod(this.f, x)
endmethod
public method operator < takes float fl returns boolean
return this.f < fl.f
endmethod
static method onInit takes nothing returns nothing
set float.Zero = float.allocate()
endmethod
endstruct
function Float takes real rl returns float
return float.create(rl)
endfunction
function CopyFloat takes float fl returns float
return float.copy(fl)
endfunction
function FloatLimits takes float fl, real min, real max, boolean add_limits returns nothing
if (add_limits) then
call fl.Limits(min, max)
else
call fl.NoLimits()
endif
endfunction
function SetFloat takes float fl, real rl returns nothing
set fl.val = rl
endfunction
function GetFloat takes float fl returns real
return fl.val
endfunction
function FloatCompare takes float f1, float f2 returns boolean
return f1.val == f2.val
endfunction
function DestroyFloat takes float fl returns nothing
call fl.delete()
endfunction
// *** vector struct ***
struct dd_vector
readonly real x
readonly real y
readonly real z
readonly real dx
readonly real dy
readonly real dz
readonly real alpha
readonly real beta
static dd_vector Zero = 0
//! runtextmacro constr("new", "nothing")
//! runtextmacro endconstr()
//! runtextmacro constr("create", "real x, real y, real z, real alpha, real beta")
set this.x = x
set this.y = y
set this.z = z
set this.dx = Cos(alpha) * Cos(beta)
set this.dy = Sin(alpha) * Cos(beta)
set this.dz = Sin(beta)
set this.alpha = alpha
set this.beta = beta
//! runtextmacro endconstr()
//! runtextmacro constr("copy", "dd_vector v")
set this.x = v.x
set this.y = v.y
set this.z = v.z
set this.dx = v.dx
set this.dy = v.dy
set this.dz = v.dz
set this.alpha = v.alpha
set this.beta = v.beta
//! runtextmacro endconstr()
//! runtextmacro destr()
set this.x = 0.
set this.y = 0.
set this.z = 0.
set this.dx = 0.
set this.dy = 0.
set this.dz = 0.
set this.alpha = 0.
set this.beta = 0.
//! runtextmacro enddestr()
// *** Moves vector at new point ***
public method Move takes real x, real y, real z returns nothing
set this.x = x
set this.y = y
set this.z = z
endmethod
// *** Change facing (direction) of vector
public method Direction takes real alpha, real beta returns nothing
set this.dx = Cos(alpha) * Cos(beta)
set this.dy = Sin(alpha) * Cos(beta)
set this.dz = Sin(beta)
set this.alpha = alpha
set this.beta = beta
endmethod
// *** Move current offset of vector ***
public method OffsetMove takes real dist returns nothing
set this.x = this.x + dist * this.dx
set this.y = this.y + dist * this.dy
set this.z = this.z + dist * this.dz
endmethod
// *** Vectors length ***
public method operator length takes nothing returns real
return SquareRoot(this.x*this.x+this.y*this.y+this.z*this.z)
endmethod
// *** Initialization method ***
static method onInit takes nothing returns nothing
set dd_vector.Zero = dd_vector.allocate()
endmethod
endstruct
// *** Creates new vector ***
function CreateVectorDD takes real x, real y, real z, real a, real b returns dd_vector
return dd_vector.create(x, y, z, a, b)
endfunction
// *** Destroys given vector ***
function DestroyVectorDD takes dd_vector v returns nothing
call v.delete()
endfunction
// *** Moves vector to new position ***
function MoveVector takes dd_vector v, real x, real y, real z returns nothing
call v.Move(x, y, z)
endfunction
// *** Changes vector direction / facing angle ***
function SetVDirection takes dd_vector v, real a, real b returns nothing
call v.Direction(a, b)
endfunction
// *** Returns unit position ***
function GetUnitVPos takes unit u returns dd_vector
return dd_vector.create(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u), GetUnitFacing(u)*DEGTORAD, 0.)
endfunction
//! textmacro dd_parab_ADD_LENGTH
set d = d + clen
set h = this.GetHeight(d)
set len = len + SquareRoot(clen*clen+(h-lh)*(h-lh))
set lh = h
set d = d + clen
set h = this.GetHeight(d)
set len = len + SquareRoot(clen*clen+(h-lh)*(h-lh))
set lh = h
set d = d + clen
set h = this.GetHeight(d)
set len = len + SquareRoot(clen*clen+(h-lh)*(h-lh))
set lh = h
set d = d + clen
set h = this.GetHeight(d)
set len = len + SquareRoot(clen*clen+(h-lh)*(h-lh))
set lh = h
//! endtextmacro
struct dd_parab
private real factor
private real length
private real height
private real perimeter
// *** Returns height at given distance of parabola ***
public method GetHeight takes real dist returns real
set dist = dist - this.length*0.5
return -(this.factor*this.factor*dist*dist) + this.height
endmethod
// *** Returns perimeter of this parabola ***
private method GetPerimeter takes nothing returns real
local real clen = this.length/32.
local real h = 0.
local real lh = 0.
local real len = 0.
local real d = 0.
//! runtextmacro dd_parab_ADD_LENGTH()
//! runtextmacro dd_parab_ADD_LENGTH()
//! runtextmacro dd_parab_ADD_LENGTH()
//! runtextmacro dd_parab_ADD_LENGTH()
return len*2.
endmethod
// ------------------------
// *** Constructors ***
// ------------------------
//! runtextmacro constr("new", "nothing")
//! runtextmacro endconstr()
//! runtextmacro constr("create", "real max_dist, real max_height")
set this.factor = SquareRoot(4.*max_height)/max_dist
set this.length = max_dist
set this.height = max_height
set this.perimeter = this.GetPerimeter()
//! runtextmacro endconstr()
//! runtextmacro constr("copy", "dd_parab p")
set this.factor = p.factor
set this.length = p.length
set this.height = p.height
set this.perimeter = p.perimeter
//! runtextmacro endconstr()
//! runtextmacro destr()
set this.factor = 0.
set this.length = 0.
set this.height = 0.
//! runtextmacro enddestr()
// *** Changes parabola scale ***
public method SetScale takes real max_dist, real max_height returns nothing
set this.factor = SquareRoot(4.*max_height)/max_dist
set this.length = max_dist
set this.height = max_height
set this.perimeter = this.GetPerimeter()
endmethod
// *** Returns width / length of parabola at given height ***
public method GetWidth takes real height returns real
return SquareRoot(this.height - height) / this.factor
endmethod
// *** Returns distance from start / end parabla point to given height / distance ***
public method GetDistance takes real dist, real height returns real
if (dist != 0 and height == 0) then
set height = this.GetHeight(dist)
set dist = dist - this.length*0.5
elseif (dist == 0 and height != 0) then
set dist = this.GetWidth(height)
endif
return SquareRoot(dist*dist + height*height)
endmethod
public method operator max_len takes nothing returns real
return this.perimeter
endmethod
public method operator max_dist takes nothing returns real
return this.length
endmethod
public method operator max_height takes nothing returns real
return this.height
endmethod
endstruct
endlibrary
// *** Fade object library_once ***
library_once DDFadeObject requires DD, DDThreading
private keyword fade
globals
private constant integer DELAY_MS = 25
private integer ObjCount = 0
private fade array FadeGroup
endglobals
// *** Fade function ***
function interface fade_f takes hashtable table, real trans returns nothing
struct fade
public hashtable table
public float trans
public real e_trans
public real fac
public fade_f ff
//! runtextmacro destr()
call FlushParentHashtable(this.table)
set this.table = null
call this.trans.delete()
//! runtextmacro enddestr()
endstruct
private function FadeObjectPeriod takes integer i returns boolean
local fade f
loop
//! textmacro fo__QUICK_LOOP
set f = i:FadeGroup
call f.trans.Add(f.fac)
call f.ff.evaluate(f.table, f.trans.val)
if (f.trans.val == f.e_trans) then
set i:FadeGroup = ObjCount:FadeGroup
set ObjCount = ObjCount - 1
call f.delete()
set i = i - 1
endif
set i = i + 1
if (i > ObjCount) then
exitwhen (true)
endif
//! endtextmacro
// *** we will use 4x speed ***
//! runtextmacro fo__QUICK_LOOP()
//! runtextmacro fo__QUICK_LOOP()
//! runtextmacro fo__QUICK_LOOP()
//! runtextmacro fo__QUICK_LOOP()
endloop
return (ObjCount == 0)
endfunction
function FadeObject takes hashtable ht, real s_trans, real e_trans, real timeout, fade_f ff returns nothing
local fade f = fade.create()
if (ObjCount == 0) then
call NewThread(DELAY_MS, 1, callback.FadeObjectPeriod, false)
endif
set ObjCount = ObjCount + 1
set f.table = ht
set f.trans = Float(s_trans)
set f.e_trans = e_trans
set f.fac = ((e_trans-s_trans)/timeout) * (DELAY_MS*0.001)
set f.ff = ff
set ObjCount:FadeGroup = f
call f.trans.Limits(rmin(s_trans, e_trans), rmax(s_trans, e_trans))
call ff.evaluate(ht, s_trans)
endfunction
endlibrary
// *** Lightning Extra library_once ***
library_once DDLightning requires NativeDDUniversalPack
// *** Main struct ***
struct lightningex
// *** Members ***
private lightning lig
private string ltype
private real array x[2]
private real array y[2]
private real array z[2]
private dd_color clr
// *** Constructor ***
static method create takes string codeName, real x1, real y1, real z1, real x2, real y2, real z2 returns lightningex
local lightningex this = lightningex.allocate()
// *** Members assign (create object) ***
set this.lig = AddLightningEx(codeName, true, x1, y1, DD_GetFloorZ(x1, y1)+z1, x2, y2, DD_GetFloorZ(x2, y2)+z2)
set this.ltype = codeName
set this.x[0] = x1
set this.y[0] = y1
set this.z[0] = z1
set this.x[1] = x2
set this.y[1] = y2
set this.z[1] = z2
set this.clr = CreateColor(255, 255, 255, 255)
// *** Return new object ***
return (this)
endmethod
// *** Move lightningex ***
public method Move takes real x1, real y1, real z1, real x2, real y2, real z2 returns nothing
call MoveLightningEx(this.lig, true, x1, y1, DD_GetFloorZ(x1, y1)+z1, x2, y2, DD_GetFloorZ(x2, y2)+z2)
set this.x[0] = x1
set this.y[0] = y1
set this.z[0] = z1
set this.x[1] = x2
set this.y[1] = y2
set this.z[1] = z2
endmethod
// *** Moves lightnings part ***
public method MovePart takes real x, real y, real z, integer part returns nothing
set part = limits(part, 0, 1)
set this.x[part] = x
set this.y[part] = y
set this.z[part] = z
call MoveLightningEx(this.lig, true, this.x[0], this.y[0], DD_GetFloorZ(this.x[0], this.y[0])+this.z[0], this.x[1], this.y[1], DD_GetFloorZ(this.x[1], this.y[1])+this.z[1])
endmethod
// *** Changes color of lightningex ***
public method SetColor takes integer r, integer g, integer b, integer a returns nothing
call ChangeColor(this.clr, r, g, b, a)
call SetLightningColor(this.lig, this.clr.r/255., this.clr.g/255., this.clr.b/255., this.clr.a/255.)
endmethod
// *** Changes only alpha ***
public method SetColorAlpha takes integer alpha returns nothing
call this.SetColor(this.clr.r, this.clr.g, this.clr.b, alpha)
endmethod
// *** Returns lightningex x part ***
public method GetX takes integer part returns real
return this.x[limits(part, 0, 1)]
endmethod
// *** Returns lightningex y part ***
public method GetY takes integer part returns real
return this.y[limits(part, 0, 1)]
endmethod
// *** Returns lightningex z part ***
public method GetZ takes integer part returns real
return this.z[limits(part, 0, 1)]
endmethod
// *** Returns red of lightning ***
public method GetRed takes nothing returns integer
return this.clr.r
endmethod
// *** Returns green of lightning ***
public method GetGreen takes nothing returns integer
return this.clr.g
endmethod
// *** Returns red of lightning ***
public method GetBlue takes nothing returns integer
return this.clr.b
endmethod
// *** Returns alpha of lightning ***
public method GetAlpha takes nothing returns integer
return this.clr.a
endmethod
// *** Returns lightning type ***
public method GetType takes nothing returns string
return this.ltype
endmethod
// *** Destructor ***
public method delete takes nothing returns nothing
call DestroyLightning(this.lig)
call DestroyColor(this.clr)
set this.lig = null
set this.clr = Null
set this.x[0] = 0.
set this.y[0] = 0.
set this.z[0] = 0.
set this.x[1] = 0.
set this.y[1] = 0.
set this.z[1] = 0.
endmethod
endstruct
// ------------------------------------------------
// *** Lightningex functions ***
// ------------------------------------------------
// *** Creates lightningex basic ***
function CreateLightning takes string ligType, real x1, real y1, real x2, real y2 returns lightningex
return lightningex.create(ligType, x1, y1, 0., x2, y2, 0.)
endfunction
// *** Creates lightningex ***
function CreateLightningEx takes string ligType, real x1, real y1, real z1, real x2, real y2, real z2 returns lightningex
return lightningex.create(ligType, x1, y1, z1, x2, y2, z2)
endfunction
// *** Will Destroy lightningex ***
function DestroyLightningEx takes lightningex whichLightning returns nothing
call whichLightning.delete()
endfunction
// *** Will move lightningex basic ***
function SetLightningPos takes lightningex lig, real x1, real y1, real x2, real y2 returns nothing
call lig.Move(x1, y1, 0., x2, y2, 0.)
endfunction
// *** Will move lightningex ***
function SetLightningPosEx takes lightningex lig, real x1, real y1, real z1, real x2, real y2, real z2 returns nothing
call lig.Move(x1, y1, z1, x2, y2, z2)
endfunction
// *** Will move lightningex part ***
function SetLightningPartPos takes lightningex lig, real x, real y, real z, integer part returns nothing
call lig.MovePart(x, y, z, part-1)
endfunction
// *** Will change lightning color ***
function ChangeLightningColor takes lightningex lig, integer r, integer g, integer b, integer a returns nothing
call lig.SetColor(r, g, b, a)
endfunction
// *** Returns lightningex x1 ***
function GetLightningX1 takes lightningex lig returns real
return lig.GetX(0)
endfunction
// *** Returns lightningex y1 ***
function GetLightningY1 takes lightningex lig returns real
return lig.GetY(0)
endfunction
// *** Returns lightningex z1 ***
function GetLightningZ1 takes lightningex lig returns real
return lig.GetZ(0)
endfunction
// *** Returns lightningex x2 ***
function GetLightningX2 takes lightningex lig returns real
return lig.GetX(1)
endfunction
// *** Returns lightningex y2 ***
function GetLightningY2 takes lightningex lig returns real
return lig.GetY(1)
endfunction
// *** Returns lightningex z2 ***
function GetLightningZ2 takes lightningex lig returns real
return lig.GetZ(1)
endfunction
// *** Returns lightningex red ***
function GetLightningRed takes lightningex lig returns integer
return lig.GetRed()
endfunction
// *** Returns lightningex green ***
function GetLightningGreen takes lightningex lig returns integer
return lig.GetGreen()
endfunction
// *** Returns lightningex blue ***
function GetLightningBlue takes lightningex lig returns integer
return lig.GetBlue()
endfunction
// *** Returns lightningex alpha ***
function GetLightningAlpha takes lightningex lig returns integer
return lig.GetAlpha()
endfunction
// *** Returns lightningex type ***
function GetLightningType takes lightningex lig returns string
return lig.GetType()
endfunction
// *** Creates lightning under angle ***
function CreateLightningAngle takes string ligType, real x, real y, real z, real distance, real alpha, real beta returns lightningex
local real x2 = x + distance * Cos(alpha)*Cos(beta)
local real y2 = y + distance * Sin(alpha)*Cos(beta)
local real z2 = z + distance * Sin(beta)
return lightningex.create(ligType, x, y, z, x2, y2, z2)
endfunction
// *** Fade timed function ***
private keyword FadeLightningPeriodic
function FadeLightningPeriodic takes hashtable ht, real trans returns nothing
call lightningex(LoadInteger(ht, 0, 0)).SetColorAlpha(R2I(trans*2.55))
endfunction
// *** Will fade lightning ***
function FadeLightning takes lightningex lig, integer s_alpha, integer e_alpha, real overtime returns nothing
local hashtable ht = InitHashtable()
call SaveInteger(ht, 0, 0, integer(lig))
call FadeObject(ht, s_alpha/2.55, e_alpha/2.55, overtime, FadeLightningPeriodic)
set ht = null
endfunction
// *** Destroys lightningex timed ***
private function DestroyLigTimed takes nothing returns nothing
call DestroyLightningEx(GetSleepData())
endfunction
function DestroyLightningTimedEx takes lightningex lig, real timeout returns nothing
call SleepEx(timeout, function DestroyLigTimed, integer(lig))
endfunction
endlibrary
// *** Knockback library_once ***
library_once DDKnockback requires DDMath, DD, DDGroup, DDThreading
// *** Globals ***
globals
private integer DELAY_MS = 25
private groupex PGroup = Null
endglobals
// --------------------------------------
// *** Function types ***
// --------------------------------------
function interface vector_f takes vector_d vd, real x returns nothing
// *** Vector data ***
// * List: vector_f = function which is executed takes (vector_d and real x)
// * dd_vector[0] = start pos vector
// * dd_vector[1] = an vector at which unit is now going to be moved at
// * unit[0] = main unit which is pushed
// * unit[1] = other unit which is taken in action
// * float[0-4] = values which are used for many different movement types
// *** End Vector data ***
struct vector_d
// *** Members ***
public vector_f vf
public dd_vector array v[2]
public unit array u[2]
public float array f[4]
public boolean b
// *** Destructor ***
//! runtextmacro destr()
set this.b = false
set this.u[0] = null
set this.u[1] = null
call this.v[0].delete()
call this.v[1].delete()
set this.v[0] = 0
set this.v[1] = 0
if (this.f[0] != 0) then
call this.f[0].delete()
set this.f[0] = 0
endif
if (this.f[1] != 0) then
call this.f[1].delete()
set this.f[1] = 0
endif
if (this.f[2] != 0) then
call this.f[2].delete()
set this.f[2] = 0
endif
if (this.f[3] != 0) then
call this.f[3].delete()
set this.f[3] = 0
endif
//! runtextmacro enddestr()
endstruct
// *** Vector functions ***
// *** Linear movement ***
// * Arguments: dd_vector[0] = start vector
// * dd_vector[1] = vector at which unit will be moved
private keyword Math_Line
private function Math_Line takes vector_d vd, real x returns nothing
call vd.v[1].Move(vd.v[0].x+x*vd.v[0].dx, vd.v[0].y+x*vd.v[0].dy, vd.v[0].z+x*vd.v[0].dz)
endfunction
// *** Circle movement ***
// * Arguments: dd_vector[0] = center vector
// * dd_vector[1] = vector at which unit will be moved
// * float[0] = radius
// * float[1] = offset perimeter
// * float[2] = beta angle
private keyword Math_Circle
private function Math_Circle takes vector_d vd, real x returns nothing
call vd.v[1].Direction((x+vd.f[1].val)/vd.f[0].val, vd.f[2].val)
call vd.v[1].Move(vd.v[0].x+vd.f[0].val*vd.v[1].dx, vd.v[0].y+vd.f[0].val*vd.v[1].dy, vd.v[0].z+vd.f[0].val*vd.v[1].dz)
endfunction
// *** Spiral movement ***
// * Arguments: dd_vector[0] = center vector
// * dd_vector[1] = current vector
// * float[0] = radius
// * float[1] = acceleration
// * float[2] = offset
private keyword Math_Spiral
private function Math_Spiral takes vector_d vd, real x returns nothing
call vd.f[0].Add(vd.f[1].val)
set vd.f[2].val = vd.v[0].alpha*vd.f[0].val
call vd.v[1].Direction((x+vd.f[2].val)/vd.f[0].val, 0)
call vd.v[1].Move(vd.v[0].x+vd.f[0].val*vd.v[1].dx, vd.v[0].y+vd.f[0].val*vd.v[1].dy, vd.v[0].z+rabs(x)*vd.v[0].dz)
endfunction
// *** Parabolic movement ***
// * Arguments: dd_vector[0] = first (start) position
// * dd_vector[1] = new point on parabola
// * float[0] = parabola
// * float[1] = factor (from parabola perimiter and length)
// * float[2] = max_distance
// * float[3] = offset
private keyword Math_Parabola
private function Math_Parabola takes vector_d vd, real x returns nothing
local dd_parab p = dd_parab(vd.f[0])
local real h = p.GetHeight((x+vd.f[3].val)*vd.f[1].val)
local real d = SquareRoot(hypot((x+vd.f[3].val)*vd.f[1].val, h))
local real a = Atan((h*Cos(vd.v[0].beta))/((x+vd.f[3].val)*vd.f[1].val))
call vd.v[1].Direction(vd.v[0].alpha+a, vd.v[0].beta)
call vd.v[1].Move(vd.v[0].x+d*vd.v[1].dx, vd.v[0].y+d*vd.v[1].dy, vd.v[0].z+h*vd.v[1].dz)
if (vd.f[2].val >= 0) then
if (x >= vd.f[2].val) then
set vd.b = true
set vd.f[0] = 0
call p.delete()
endif
else
if (x <= vd.f[2].val) then
set vd.b = true
set vd.f[0] = 0
call p.delete()
endif
endif
endfunction
// *** Follow target movement ***
// * Arguments: dd_vector[0] = nothing
// * dd_vector[1] = current position
// * unit[0] = main unit
// * unit[1] = target unit
// * float[0] = last distance
// * boolean = over when comes near target
private keyword Follow_Line
private function Follow_Line takes vector_d vd, real x returns nothing
local real array r
set r[0] = GetUnitX(vd.u[1])-GetUnitX(vd.u[0])
set r[1] = GetUnitY(vd.u[1])-GetUnitY(vd.u[0])
set r[2] = GetUnitFlyHeight(vd.u[1])-GetUnitFlyHeight(vd.u[0])
set r[3] = r[0]*r[0] + r[1]*r[1]
call vd.v[1].Direction(Atan2(r[1], r[0]), Atan(((r[2]*r[2])*rsign(r[2]))/r[3]))
call vd.v[1].OffsetMove(x-vd.f[0].val)
set vd.f[0].val = x
set vd.b = r[3] <= 2500. or GetWidgetLife(vd.u[0]) <= 0.405
endfunction
// *** Circle target movement ***
// * Arguments: dd_vector[0] = center vector of unit
// * dd_vector[1] = vector at which unit will be moved
// * unit[1] = target
// * float[0] = radius
// * float[1] = offset perimeter
// * float[2] = beta angle
private keyword Target_Circle
private function Target_Circle takes vector_d vd, real x returns nothing
call vd.v[0].Move(GetUnitX(vd.u[1]), GetUnitY(vd.u[1]), GetUnitFlyHeight(vd.u[1]))
call vd.v[1].Direction((x+vd.f[1].val)/vd.f[0].val, vd.f[2].val)
call vd.v[1].Move(vd.v[0].x+vd.f[0].val*vd.v[1].dx, vd.v[0].y+vd.f[0].val*vd.v[1].dy, vd.v[0].z+vd.f[0].val*vd.v[1].dz)
endfunction
// *** Spiral target movement ***
// * Arguments: dd_vector[0] = center vector of target
// * dd_vector[1] = current vector
// * unit[1] = target
// * float[0] = radius
// * float[1] = acceleration
// * float[2] = offset
private keyword Target_Spiral
private function Target_Spiral takes vector_d vd, real x returns nothing
call vd.v[0].Move(GetUnitX(vd.u[1]), GetUnitY(vd.u[1]), GetUnitFlyHeight(vd.u[1]))
call vd.f[0].Add(vd.f[1].val)
set vd.f[2].val = vd.v[0].alpha*vd.f[0].val
call vd.v[1].Direction((x+vd.f[2].val)/vd.f[0].val, 0)
call vd.v[1].Move(vd.v[0].x+vd.f[0].val*vd.v[1].dx, vd.v[0].y+vd.f[0].val*vd.v[1].dy, vd.v[0].z+rabs(x)*vd.v[0].dz)
endfunction
// ==================================================
// *** Outside calls ***
// ==================================================
// *** Vector data creators ***
// *** Lind data load ***
function LineData takes nothing returns vector_d
local vector_d vd = vector_d.create()
set vd.vf = Math_Line
set vd.v[1] = dd_vector.new()
set vd.b = true
return vd
endfunction
// *** Circle data load ***
function CircleData takes real radius returns vector_d
local vector_d vd = vector_d.create()
set vd.vf = Math_Circle
set vd.v[1] = dd_vector.new()
set vd.f[0] = float.create(radius)
set vd.f[1] = float.new()
set vd.f[2] = float.new()
set vd.b = true
return vd
endfunction
// *** Spiral data load ***
function SpiralData takes real radius, real acc, real max_radius returns vector_d
local vector_d vd = vector_d.create()
set vd.vf = Math_Spiral
set vd.v[1] = dd_vector.new()
set vd.f[0] = float.create(radius)
set vd.f[1] = float.create(acc*(DELAY_MS*0.001)*rsign(max_radius-radius))
set vd.f[2] = float.new()
call vd.f[0].Limits(rmin(radius, max_radius), rmax(radius, max_radius))
set vd.b = true
return vd
endfunction
// *** Parabola data load ***
function ParabolaData takes dd_parab p, real offset returns vector_d
local vector_d vd = vector_d.create()
set vd.vf = Math_Parabola
set vd.v[1] = dd_vector.new()
set vd.f[0] = dd_parab.copy(p)
set vd.f[1] = float.create(p.max_dist/p.max_len)
set vd.f[3] = float.create(offset)
set vd.b = false
return vd
endfunction
// *** Follow line data load ***
function FollowLineData takes unit target returns vector_d
local vector_d vd = vector_d.create()
set vd.vf = Follow_Line
set vd.u[1] = target
set vd.f[0] = float.new()
set vd.b = false
// set vd.v[1] = dd_vector.new() --- vector is copied from v[0]
return vd
endfunction
// *** Circle movement with center unit ***
function TargetCircleData takes unit target, real radius returns vector_d
local vector_d vd = vector_d.create()
set vd.vf = Target_Circle
set vd.v[1] = dd_vector.new()
set vd.u[1] = target
set vd.f[0] = float.create(radius)
set vd.f[1] = float.new()
set vd.f[2] = float.new()
set vd.b = true
return vd
endfunction
// *** Spiral target data load ***
function TargetSpiralData takes unit target, real radius, real acc, real max_radius returns vector_d
local vector_d vd = vector_d.create()
set vd.vf = Target_Spiral
set vd.v[1] = dd_vector.new()
set vd.u[1] = target
set vd.f[0] = float.create(radius)
set vd.f[1] = float.create(acc*(DELAY_MS*0.001)*rsign(max_radius-radius))
set vd.f[2] = float.new()
call vd.f[0].Limits(rmin(radius, max_radius), rmax(radius, max_radius))
set vd.b = true
return vd
endfunction
// -------------------
// *** Structs ***
// -------------------
private struct push
unit u
float vel
real acc
float dist
real max_dist
vector_d vd
static thistype array ptrAt
// *** Constructor ***
//! runtextmacro constr("create", "unit u, real vel, real acc, real max_vel, real dist, real alpha, real beta, vector_d vd")
set this.u = u
set this.vel = float.create(vel)
set this.acc = acc*rsign(max_vel-vel)
set this.dist = float.new()
set this.max_dist = dist
set this.vd = vd
set this.vd.v[0] = dd_vector.create(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u), alpha, beta)
call this.dist.Limits(rmin(0, dist), rmax(0, dist))
call this.vel.Limits(rmin(max_vel, vel), rmax(max_vel, vel))
set thistype.ptrAt[ArrayId(u)] = this
//! runtextmacro endconstr()
// *** Destructor ***
//! runtextmacro destr()
call this.vd.delete()
call this.dist.delete()
set this.u = null
call this.vel.delete()
set this.acc = 0
set this.dist = 0
set this.max_dist = 0
//! runtextmacro enddestr()
endstruct
function SetUnitVPos takes unit u, dd_vector v returns nothing
call SetUnitX(u, rlimits(v.x, DDMinX, DDMaxX))
call SetUnitY(u, rlimits(v.y, DDMinY, DDMaxY))
call SetUnitFlyHeight(u, v.z, 0.)
endfunction
function SetUnitVPosOffset takes unit u, dd_vector v, real offset returns nothing
call SetUnitX(u, rlimits(v.x+offset*v.dx, DDMinX, DDMaxX))
call SetUnitY(u, rlimits(v.y+offset*v.dy, DDMinY, DDMaxY))
call SetUnitFlyHeight(u, v.z+offset*v.dz, 0.)
endfunction
private function PushUnitEnum takes nothing returns nothing
local push p = push.ptrAt[ArrayId(GetEnumUnitEx())]
call p.vel.Add(p.acc*(DELAY_MS*0.001))
call p.dist.Add(p.vel.val*(DELAY_MS*0.001))
call p.vd.vf.evaluate(p.vd, p.dist.val)
call SetUnitVPos(p.u, p.vd.v[1])
if ((p.dist.val == p.max_dist or p.vd.vf == Follow_Line) and p.vd.b) then
call PGroup.RemoveUnit(p.u)
call p.delete()
endif
endfunction
private function PushUnitPeriodic takes integer i returns boolean
call PGroup.For(function PushUnitEnum)
if (PGroup.first == null) then
call DestroyGroupEx(PGroup)
set PGroup = Null
return (true)
endif
return (false)
endfunction
// *** Main push function ***
function PushUnit takes unit u, real vel, real acc, real max_vel, real dist, real alpha, real beta, vector_d vd returns nothing
local push p
if (PGroup == Null) then
set PGroup = CreateGroupEx()
call NewThread(DELAY_MS, 0, callback.PushUnitPeriodic, false)
endif
// *** Make flyable ***
call UnitAddAbility(u, 'Amrf')
call UnitRemoveAbility(u, 'Amrf')
if (not PGroup.Contains(u)) then
set p = push.create(u, vel, acc, max_vel, dist, alpha, beta, vd)
call PGroup.AddUnit(u)
if (vd.vf == Follow_Line) then
set vd.u[0] = u
set p.vd.v[1] = dd_vector.copy(p.vd.v[0])
call p.dist.NoLimits()
elseif (vd.vf == Math_Circle) then
set vd.f[1].val = alpha*vd.f[0].val
set vd.f[2].val = beta
elseif (vd.vf == Math_Spiral) then
set vd.f[2].val = alpha*vd.f[0].val
elseif (vd.vf == Math_Parabola) then
set vd.f[2] = float.create(dist)
elseif (vd.vf == Target_Circle) then
set vd.f[1].val = alpha*vd.f[0].val
set vd.f[2].val = beta
elseif (vd.vf == Target_Spiral) then
set vd.f[2].val = alpha*vd.f[0].val
endif
else
set p = push.ptrAt[ArrayId(u)]
call p.vd.delete()
set p.vel.val = vel
set p.acc = acc*rsign(max_vel-vel)
set p.dist.val = 0
set p.max_dist = dist
set p.vd = vd
call p.vd.v[0].Direction(alpha, beta)
call p.vd.v[0].Move(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u))
call p.dist.Limits(rmin(0, dist), rmax(0, dist))
call p.vel.Limits(rmin(vel, max_vel), rmax(vel, max_vel))
if (vd.vf == Follow_Line) then
set vd.u[0] = u
set p.vd.v[1] = dd_vector.copy(p.vd.v[0])
call p.dist.NoLimits()
elseif (vd.vf == Math_Circle) then
set vd.f[1].val = alpha*vd.f[0].val
set vd.f[2].val = beta
elseif (vd.vf == Math_Spiral) then
set vd.f[2].val = alpha*vd.f[0].val
elseif (vd.vf == Math_Parabola) then
set vd.f[2] = float.create(dist)
elseif (vd.vf == Target_Circle) then
set vd.f[1].val = alpha*vd.f[0].val
set vd.f[2].val = beta
elseif (vd.vf == Target_Spiral) then
set vd.f[2].val = alpha*vd.f[0].val
endif
endif
endfunction
// ---------------------------------------------------------------------------------------
// *** Math knockback ***
// ---------------------------------------------------------------------------------------
globals
private groupex FGrp = Null
private timer Tim = null
endglobals
private struct fmove
private unit u
private float vel
private float dist
private real acc
private real mdist
private dd_vector v
private real dxx
private real dxy
private real dy
private real dz
private math m
private boolean fl_detect
private real fz
// *** Static vars ***
static constant location LOC_Z = Location(0., 0.)
// *** Pointer works add ***
implement optional dd_pointer
static method create takes unit u, real vel, real acc, real mvel, real mdist, real alpha, real beta, math m, boolean ignore_floor returns fmove
local fmove this = fmove.allocate()
set this.u = u
set this.vel = Float(vel)
set this.dist = Float(0.)
set this.acc = acc * rsign(mvel-vel)
set this.mdist = mdist
set this.v = CreateVectorDD(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u), alpha, 0.)
set this.dxx = Cos(alpha+PI_HALF)
set this.dxy = Sin(alpha+PI_HALF)
set this.dy = Cos(beta)
set this.dz = Sin(beta)
set this.m = m
set this.fl_detect = not ignore_floor
// *** Make sure unit can fly ***
call UnitAddAbility(this.u, 'Amrf')
call UnitRemoveAbility(this.u, 'Amrf')
call FloatLimits(this.dist, rmin(0., this.mdist), rmax(0., this.mdist), true)
if (mvel != 0.) then
call FloatLimits(this.vel, rmin(vel, mvel), rmax(vel, mvel), true)
endif
if (this.fl_detect) then
call MoveLocation(fmove.LOC_Z, this.v.x, this.v.y)
set this.fz = GetLocationZ(fmove.LOC_Z)
endif
// *** Ptr set ***
call this.SetPtrObj(this.u)
return (this)
endmethod
public method swap takes real vel, real acc, real mvel, real mdist, real alpha, real beta, math m, boolean ignore_floor returns nothing
call SetFloat(this.vel, vel)
call SetFloat(this.dist, 0.)
set this.acc = acc * rsign(mvel-vel)
set this.mdist = mdist
call MoveVector(this.v, GetUnitX(this.u), GetUnitY(this.u), GetUnitFlyHeight(this.u))
call SetVDirection(this.v, alpha, 0.)
set this.dxx = Cos(alpha+PI_HALF)
set this.dxy = Sin(alpha+PI_HALF)
set this.dy = Cos(beta)
set this.dz = Sin(beta)
set this.m = m
set this.fl_detect = not ignore_floor
call FloatLimits(this.dist, rmin(0., this.mdist), rmax(0., this.mdist), true)
if (mvel != 0.) then
call FloatLimits(this.vel, rmin(vel, mvel), rmax(vel, mvel), true)
else
call FloatLimits(this.vel, 0., 0., false)
endif
if (this.fl_detect) then
call MoveLocation(fmove.LOC_Z, this.v.x, this.v.y)
set this.fz = GetLocationZ(fmove.LOC_Z)
endif
endmethod
public method Move takes nothing returns boolean
// *** Locals ***
local real r
local real x
local real y
local real z
// *** Main set values ***
call this.vel.Add(this.acc*(DELAY_MS*0.001))
call this.dist.Add(GetFloat(this.vel)*(DELAY_MS*0.001))
// *** Get Position (x, y, z) ***
set x = GetFloat(this.dist)
set y = this.m.evaluate(x)
set z = y
// *** Temp Store ***
set r = x
// *** Now direction apply ***
set y = y * this.dy
set x = (r * this.v.dx) + (y * this.dxx)
set y = (r * this.v.dy) + (y * this.dxy)
set z = z * this.dz
// *** Floor Z apply if wanted ***
if (this.fl_detect) then
call MoveLocation(fmove.LOC_Z, this.v.x+x, this.v.y+y)
set z = z + (this.fz-GetLocationZ(fmove.LOC_Z))
endif
// *** Now move our unit offset ***
call SetUnitX(this.u, this.v.x + x)
call SetUnitY(this.u, this.v.y + y)
call SetUnitFlyHeight(this.u, this.v.z + z, 0.)
// Done?
return (GetFloat(this.dist) == this.mdist)
endmethod
public method delete takes nothing returns nothing
call fmove.ClrPtrObj(this.u)
call DestroyFloat(this.vel)
call DestroyFloat(this.dist)
call DestroyVectorDD(this.v)
set this.u = null
set this.vel = Null
set this.dist = Null
set this.v = Null
set this.m = Null
call this.destroy()
endmethod
endstruct
private function EnumUnitsMovement takes nothing returns nothing
// *** Locals ***
local fmove fm = fmove.GetPtrObj(GetEnumUnitEx())
// *** Done movement? ***
if (fm.Move()) then
call FGrp.RemoveUnit(GetEnumUnitEx())
call fm.delete()
endif
endfunction
private function ApplyEnumMovement takes integer i returns boolean
// *** Do for action ***
call FGrp.For(function EnumUnitsMovement)
// *** All units finished movement? ***
if (FGrp.size == 0) then
call DestroyGroupEx(FGrp)
set FGrp = Null
return (true)
endif
return (false)
endfunction
// *** Used when need no math ***
constant function NoMath takes real x returns real
return 0. //0.*x = 0.
endfunction
// *** Main math based movement function ***
function ForceUnitMovement takes unit whichUnit, real vel, real acc, real mvel, real mdist, real alpha, real beta, math mathFunc, boolean ignore_floor_z returns nothing
// *** Ptr ***
local fmove pfm = fmove.GetPtrObj(whichUnit)
// *** No group? Load then ***
if (FGrp == Null) then
set FGrp = CreateGroupEx()
call NewThread(DELAY_MS, 0, callback.ApplyEnumMovement, false)
endif
// *** New unit added (force movement now) ***
if (pfm == Null) then
call FGrp.AddUnit(whichUnit)
call fmove.create(whichUnit, vel, acc, mvel, mdist, alpha, beta, mathFunc, ignore_floor_z)
else
// *** Unit is currently pushed so just swap data ***
call pfm.swap(vel, acc, mvel, mdist, alpha, beta, mathFunc, ignore_floor_z)
endif
endfunction
endlibrary
// ----------------------------------------
// *** Handles timed func actions ***
// ----------------------------------------
library_once DDThreading
// *** callback code ***
// *** keywords ***
private keyword tdata
private keyword arr
globals
private timer array TTim[8191] // Timers
private integer array TInst[8191] // Timers instances
private integer array TMS[8191] // Timers Mili Seconds
private arr array TArr[8191] // Threads
endglobals
// *** callback declare ***
function interface callback takes integer value returns boolean
type arr extends tdata array [255]
// *** struct ***
private struct tdata
integer value
callback func
//! runtextmacro constr("create", "integer v, callback f")
set this.value = v
set this.func = f
//! runtextmacro endconstr()
endstruct
// *** Main timed exec threads ***
private function ExecThreads takes nothing returns nothing
// *** Exec locals ***
// local integer ms = TMS[HtoI(GetExpiredTimer())]
local integer ms = R2I(TimerGetTimeout(GetExpiredTimer())*1000.)
local arr a = TArr[ms]
local integer i = 0
loop
//! textmacro dd___thread__SPEED_EXEC
if (a[i].func.evaluate(a[i].value)) then
call a[i].destroy()
set TInst[ms] = TInst[ms] - 1
set a[i] = a[TInst[ms]]
else
set i = i + 1
endif
exitwhen (i == TInst[ms])
//! endtextmacro
// *** 8x speed exec ***
//! runtextmacro dd___thread__SPEED_EXEC()
//! runtextmacro dd___thread__SPEED_EXEC()
//! runtextmacro dd___thread__SPEED_EXEC()
//! runtextmacro dd___thread__SPEED_EXEC()
//! runtextmacro dd___thread__SPEED_EXEC()
//! runtextmacro dd___thread__SPEED_EXEC()
//! runtextmacro dd___thread__SPEED_EXEC()
//! runtextmacro dd___thread__SPEED_EXEC()
endloop
if (TInst[ms] == 0) then
call a.destroy()
call PauseTimer(TTim[ms])
call DestroyTimer(TTim[ms])
set TTim[ms] = null
endif
endfunction
// *** Main new thread generate function ***
function NewThread takes integer miliseconds, integer value, callback func, boolean exec_now returns nothing
if (miliseconds <= 0) then
loop
if (func.evaluate(value)) then
return
endif
endloop
endif
if (exec_now) then
if (func.evaluate(value)) then
return
endif
endif
if (TTim[miliseconds] == null) then
set TTim[miliseconds] = CreateTimer()
call TimerStart(TTim[miliseconds], miliseconds/1000., true, function ExecThreads)
set TArr[miliseconds] = arr.create()
// set TMS[HtoI(TTim[miliseconds])] = miliseconds
/*
debug if (HtoI(TTim[miliseconds]) >= JASS_MAX_ARRAY_SIZE) then
debug call Msg.execute("|cffff0000Error: NewThread handle id to big...|r")
debug endif
*/
endif
set TArr[miliseconds][TInst[miliseconds]] = tdata.create(value, func)
set TInst[miliseconds] = TInst[miliseconds] + 1
endfunction
endlibrary
// ---------------------------
// *** Non-leaking group ***
// ---------------------------
library_once DDGroup requires DDMath, DD
// *** GroupEx globals ***
globals
boolean wantDestroyGroupEx = false
endglobals
// *** Main group type ***
struct groupex
// *** Group ex members ***
private integer count
private integer i
private integer n
public integer value
// *** Static members ***
private static constant group TEMP_GROUP = CreateGroup()
private static constant hashtable HTABLE = InitHashtable()
private static constant integer SIZE = 1024
private static groupex Temp = Null
private static filterfunc FilterAddEnum = null
readonly static unit EnumUnit = null
readonly static groupex ExecutedGroupEx = Null
// *** Constructor ***
static method create takes nothing returns groupex
local groupex this = groupex.allocate()
set this.count = 0
set this.value = 0
set this.i = -1
set this.n = groupex.SIZE
return (this)
endmethod
// *** Copy constructor ***
static method copy takes groupex gx returns groupex
local groupex this = groupex.allocate()
local unit u
// *** Copy values ***
set this.count = gx.count
set this.value = gx.value
set this.n = gx.n
// *** Loop through units and add them with double speed ***
//! runtextmacro FOR("this.i", "0", "this.count")
set u = LoadUnitHandle(groupex.HTABLE, 2*gx-1, this.i)
call SaveInteger(groupex.HTABLE, 2*(this-1), GetHandleId(u), this.i+1)
call SaveUnitHandle(groupex.HTABLE, 2*(this)-1, this.i, u)
set this.i = this.i + 1
exitwhen (this.i == this.count)
set u = LoadUnitHandle(groupex.HTABLE, 2*gx-1, this.i)
call SaveInteger(groupex.HTABLE, 2*(this-1), GetHandleId(u), this.i+1)
call SaveUnitHandle(groupex.HTABLE, 2*(this)-1, this.i, u)
//! runtextmacro ENDFOR()
// *** Reset loop integer i ***
set this.i = -1
// *** Clear leaks ***
set u = null
// *** Return this new group ***
return (this)
endmethod
// *** Add new unit to group ***
public method AddUnit takes unit whichUnit returns boolean
// *** First check is unit already in group ***
// *** This is done only in debug mode, use .Contains to detect ***
debug if (HaveSavedInteger(groupex.HTABLE, 2*(this-1), GetHandleId(whichUnit))) then
debug call Msg.execute("|cffff0000WARNING: Adding unit ("+GetUnitName(whichUnit)+") twice in a same groupex!|r")
debug return false
debug endif
// *** GroupEx is full? ***
if (this.count == this.n) then
debug call Msg.execute("|cff0032cdNOTE: Max size of group reached, cant add more units|r")
return false
endif
// *** Store / add unit to our group ***
call SaveUnitHandle(groupex.HTABLE, 2*(this)-1, this.count, whichUnit)
call SaveInteger(groupex.HTABLE, 2*(this-1), GetHandleId(whichUnit), this.count+1)
set this.count = this.count + 1
return true // *** Use .Contains to detect is unit in groupex, no need to slow this down ***
endmethod
// *** Removes unit from the group ***
public method RemoveUnit takes unit whichUnit returns boolean
// *** Locals ***
local integer unitid = GetHandleId(whichUnit)
local integer id
local unit u
// *** Get units id and check is he in this group ***
//! runtextmacro IF("HaveSavedInteger(groupex.HTABLE, 2*(this-1), unitid)", "set id = LoadInteger(groupex.HTABLE, 2*(this-1), unitid)-1", "return false")
// *** If we are in loop we must decrease counter since we will now swap units ***
//! runtextmacro IFX("this.i != -1", "set this.i = this.i - 1")
// *** Unit was found ***
// *** Now flush its pointer ***
call RemoveSavedInteger(groupex.HTABLE, 2*(this-1), unitid)
// old: call SaveInteger(groupex.HTABLE, 2*(this-1), unitid, 0)
// *** Decrease counter ***
set this.count = this.count - 1
// *** This is not the last unit? ***
if (this.count != id) then
// *** Swap units ***
set u = LoadUnitHandle(groupex.HTABLE, 2*(this)-1, this.count)
call SaveUnitHandle(groupex.HTABLE, 2*(this)-1, id, u)
// *** Change swaped units pointer ***
call SaveInteger(groupex.HTABLE, 2*(this-1), GetHandleId(u), id+1)
// *** Clear unit ***
set u = null
endif
// *** Clear last unit ***
call RemoveSavedHandle(groupex.HTABLE, 2*(this)-1, this.count)
// *** Unit was found ***
return true
endmethod
// *** Removes unit from this group at given index ***
public method RemoveUnitByIndex takes integer index returns boolean
// *** Locals ***
local unit u
// *** Removing unit by index in for loop? ***
debug if (this.i != -1) then
debug call Msg.execute("|cffff0000WARNING: Attempting to remove unit by index in for groupex loop.|r")
debug return false
debug endif
// *** Check is valid index given ***
if (index > 0 and index <= this.count) then
// *** Flush units pointer ***
// old: call SaveInteger(groupex.HTABLE, 2*(this-1), HtoI(LoadUnitHandle(groupex.HTABLE, 2*(this)-1, index-1)), 0)
call RemoveSavedInteger(groupex.HTABLE, 2*(this-1), GetHandleId(LoadUnitHandle(groupex.HTABLE, 2*(this)-1, index-1)))
// *** Reduce size ***
set this.count = this.count - 1
// *** If unit is not last then swap it ***
if (index-1 != this.count) then
set u = LoadUnitHandle(groupex.HTABLE, 2*(this)-1, this.count)
call SaveUnitHandle(groupex.HTABLE, 2*(this)-1, index-1, u)
call SaveInteger(groupex.HTABLE, 2*(this-1), GetHandleId(u), index)
set u = null
endif
// *** Flush unit out ***
call RemoveSavedHandle(groupex.HTABLE, 2*(this)-1, this.count)
// *** Unit Deleted ***
return true
endif
// *** Unknown index ***
return false
endmethod
// *** Returns random unit from this group ***
public method Random takes nothing returns unit
if (this.count > 0) then
return LoadUnitHandle(groupex.HTABLE, 2*(this)-1, GetRandomInt(0, this.count-1))
endif
return null
endmethod
// *** Does group contain unit? ***
public method Contains takes unit whichUnit returns boolean
// *** Was unit found? ***
return HaveSavedInteger(groupex.HTABLE, 2*(this-1), GetHandleId(whichUnit))
endmethod
// *** Clears group ***
public method Clear takes nothing returns nothing
// *** Flush out pointers ***
call FlushChildHashtable(groupex.HTABLE, 2*(this-1))
call FlushChildHashtable(groupex.HTABLE, 2*(this)-1)
// *** Reset counter ***
set this.count = 0
endmethod
// *** Loops through all units ***
public method For takes code c returns nothing
// *** Locals ***
local groupex pgx = thistype.ExecutedGroupEx
// *** For inside for? ***
debug if (this.i != -1) then
debug call Msg.execute("|cffff0000WARNING: Attempting to start groupex \"for\" loop inside already started one.|r")
debug return
debug endif
// *** Store this current loop group ***
set thistype.ExecutedGroupEx = (this)
// *** Loop through units with 4x speed ***
loop
// *** Loop macro ***
//! textmacro groupex__EXEC
set this.i = this.i + 1
exitwhen (this.i == this.count)
set thistype.EnumUnit = LoadUnitHandle(groupex.HTABLE, 2*(this)-1, this.i)
call ExecuteCode(c)
//! endtextmacro
//! runtextmacro groupex__EXEC()
//! runtextmacro groupex__EXEC()
//! runtextmacro groupex__EXEC()
//! runtextmacro groupex__EXEC()
endloop
// *** Clear leaks ***
set this.i = -1
set thistype.ExecutedGroupEx = (pgx)
endmethod
// *** Returns first unit of group ***
public method operator first takes nothing returns unit
if (this.count > 0) then
return LoadUnitHandle(groupex.HTABLE, 2*(this)-1, 0)
endif
return null
endmethod
// *** Returns last unit of group ***
public method operator last takes nothing returns unit
if (this.count > 0) then
return LoadUnitHandle(groupex.HTABLE, 2*(this)-1, this.count-1)
endif
return null
endmethod
// *** Returns size of this group ***
public method operator size takes nothing returns integer
return this.count
endmethod
// *** Sets max size of groupex ***
public method operator size= takes integer n returns nothing
// *** Change size in for loop? ***
debug if (this.i != -1) then
debug call Msg.execute("|cffff00000WARNING: Cant change size of groupex inside for loop!|r")
debug return
debug endif
// *** Check is correct size given ***
//! runtextmacro IFX("n < 0", "return")
// *** Store new size ***
set this.n = n
// *** There are already more units so clear them ***
if (n < this.count) then
// *** To many units in a group (clear them) ***
//! runtextmacro FOR("n", "0", "this.count-this.n")
// *** Get enum unit ***
call RemoveSavedInteger(groupex.HTABLE, 2*(this-1), GetHandleId(LoadUnitHandle(groupex.HTABLE, 2*(this)-1, this.count-n)))
call RemoveSavedHandle(groupex.HTABLE, 2*(this)-1, this.count-n)
//! runtextmacro ENDFOR()
// *** Save counter (size) ***
set this.count = this.n
endif
endmethod
// *** Returns is group empty ***
public method operator empty takes nothing returns boolean
return this.count == 0
endmethod
// *** Returns is group dead ***
public method operator dead takes nothing returns boolean
// *** Locals ***
local integer i
// *** Loop through group with double speed ***
//! runtextmacro FOR("i", "0", "this.count")
//! runtextmacro IFX("GetWidgetLife( LoadUnitHandle(groupex.HTABLE, 2*(this)-1, i) ) > 0.405", "return false")
set i = i + 1
exitwhen (i == this.count)
//! runtextmacro IFX("GetWidgetLife( LoadUnitHandle(groupex.HTABLE, 2*(this)-1, i) ) > 0.405", "return false")
//! runtextmacro ENDFOR()
// *** All units are dead ***
return true
endmethod
// *** Which group has more units? ***
public method operator < takes groupex g returns boolean
return this.count < g.count
endmethod
// *** Returns unit by id ***
public method operator [] takes integer index returns unit
// *** Make sure that we have units in group and that index is in its range ***
if (index > 0 and index <= this.count) then
return LoadUnitHandle(groupex.HTABLE, 2*(this)-1, index-1)
endif
return null
endmethod
// *** Swaps current unit at given id ***
public method operator []= takes integer index, unit whichUnit returns nothing
// *** Locals ***
local unit u
// *** Make sure that we have units in group and that index is in its range ***
if (index > 0 and index <= this.count) then
// *** Load current unit ***
set u = LoadUnitHandle(groupex.HTABLE, 2*(this)-1, index-1)
// *** Check is new unit already in groupex ***
debug if (HaveSavedInteger(groupex.HTABLE, 2*(this-1), GetHandleId(whichUnit))) then
debug call Msg.execute("|cffff0000WARNING: Unable to swap unit inside groupex with already existing unit inside groupex!|r")
debug return
debug endif
// *** Swap units ***
call RemoveSavedInteger(groupex.HTABLE, 2*(this-1), GetHandleId(u))
call SaveUnitHandle(groupex.HTABLE, 2*(this)-1, index-1, whichUnit)
call SaveInteger(groupex.HTABLE, 2*(this-1), GetHandleId(whichUnit), index)
// *** Null locals ***
set u = null
endif
endmethod
// *** Filter units add ***
private static method EnumUnitsAdd takes nothing returns boolean
// *** Locals ***
local groupex this = groupex.Temp
local unit f
// *** Check can we add units ***
if (this.count < this.n) then
// *** Store / add unit to our group ***
set f = GetFilterUnit()
call SaveUnitHandle(groupex.HTABLE, 2*(this)-1, this.count, f)
call SaveInteger(groupex.HTABLE, 2*(this-1), GetHandleId(f), this.count+1)
set this.count = this.count + 1
set f = null
endif
// *** Dummy return ***
return (false)
endmethod
// *** Picks all units matching in radius ***
public method EnumRadius takes real x, real y, real radius, boolexpr filter returns nothing
// *** Locals ***
local boolexpr bx
// *** Picking units inside for loop... ***
debug if (this.i != -1) then
debug call Msg.execute("|cffff0000WARNING: Trying to pick units for groupex which is currently in loop!|r")
debug return
debug endif
// *** Clear if needed ***
if (this.count > 0) then
call FlushChildHashtable(groupex.HTABLE, 2*(this-1))
call FlushChildHashtable(groupex.HTABLE, 2*(this)-1)
set this.count = 0
endif
// *** Detect filter ***
//! runtextmacro IF("filter == null", "set bx = groupex.FilterAddEnum", "set bx = And(filter, groupex.FilterAddEnum)")
// *** Now pick and add enum units ***
set thistype.Temp = this
call GroupEnumUnitsInRange(thistype.TEMP_GROUP, x, y, radius, bx)
// *** Blow bx if needed ***
if (bx != groupex.FilterAddEnum) then
call DestroyBoolExpr(bx)
set bx = null
endif
endmethod
// *** Picks all units matching in rect ***
public method EnumRect takes rect r, boolexpr filter returns nothing
// *** Locals ***
local boolexpr bx
// *** Picking units inside for loop... ***
debug if (this.i != -1) then
debug call Msg.execute("|cffff0000WARNING: Trying to pick units for groupex which is currently in loop!|r")
debug return
debug endif
// *** Clear if needed ***
if (this.count > 0) then
call FlushChildHashtable(groupex.HTABLE, 2*(this-1))
call FlushChildHashtable(groupex.HTABLE, 2*(this)-1)
set this.count = 0
endif
// *** Detect filter ***
//! runtextmacro IF("filter == null", "set bx = groupex.FilterAddEnum", "set bx = And(filter, groupex.FilterAddEnum)")
// *** Now pick and add enum units ***
set thistype.Temp = this
call GroupEnumUnitsInRect(thistype.TEMP_GROUP, r, bx)
// *** Release bx if custom was used ***
if (bx != groupex.FilterAddEnum) then
call DestroyBoolExpr(bx)
set bx = null
endif
endmethod
// *** This only filters units in range ***
public static method FilterRadius takes real x, real y, real r, code func returns nothing
local filterfunc ff = Filter(func)
call GroupEnumUnitsInRange(thistype.TEMP_GROUP, x, y, r, ff)
call DestroyFilter(ff)
set ff = null
endmethod
// *** This only filters units in rect ***
public static method FilterRect takes rect r, code c returns nothing
local filterfunc ff = Filter(c)
call GroupEnumUnitsInRect(thistype.TEMP_GROUP, r, ff)
call DestroyFilter(ff)
set ff = null
endmethod
// *** Destructor ***
public method delete takes nothing returns nothing
// *** Do clear if not already ***
if (this.count > 0) then
call FlushChildHashtable(groupex.HTABLE, 2*(this-1))
call FlushChildHashtable(groupex.HTABLE, 2*(this)-1)
endif
set this.n = 0
set this.value = 0
set this.count = 0
call this.destroy()
endmethod
// *** Init method ***
static method onInit takes nothing returns nothing
set groupex.FilterAddEnum = Filter(function groupex.EnumUnitsAdd)
endmethod
endstruct
// ==================================================================================
// *** GroupEx Functions ***
// ==================================================================================
// *** Will create new group ***
function CreateGroupEx takes nothing returns groupex
return groupex.create()
endfunction
// *** Will copy given group ***
function CopyGroupEx takes groupex whichGroupEx returns groupex
return groupex.copy(whichGroupEx)
endfunction
// *** Will destroy group ***
function DestroyGroupEx takes groupex whichGroupEx returns nothing
call whichGroupEx.delete()
endfunction
// *** Will clear group ***
function ClearGroupEx takes groupex whichGroupEx returns nothing
call whichGroupEx.Clear()
endfunction
// *** Adds new unit to group ***
function GroupExAddUnit takes groupex whichGroupEx, unit u returns nothing
call whichGroupEx.AddUnit(u)
endfunction
// *** Removes old unit from group ***
function GroupExRemoveUnit takes groupex whichGroupEx, unit u returns nothing
call whichGroupEx.RemoveUnit(u)
endfunction
// *** Removes unit by index ***
function GroupExRemoveUnitByIndex takes groupex whichGroupEx, integer index returns nothing
call whichGroupEx.RemoveUnitByIndex(index)
endfunction
// *** Returns random unit from group ***
function GroupExGetRandom takes groupex gx returns unit
return gx.Random()
endfunction
// *** Returns does group contain given unit ***
function GroupExContains takes groupex gx, unit u returns boolean
return gx.Contains(u)
endfunction
// *** Will destroy next group ***
function DestroyNextGroupEx takes nothing returns nothing
set wantDestroyGroupEx = true
endfunction
// *** Function for group loop ***
function ForGroupEx takes groupex gx, code c returns nothing
call gx.For(c)
if (wantDestroyGroupEx) then
call gx.delete()
set wantDestroyGroupEx = false
endif
endfunction
// *** Returns first unit of a group ***
function FirstOfGroupEx takes groupex gx returns unit
return gx.first
endfunction
// *** Returns last unit of a group ***
function LastOfGroupEx takes groupex gx returns unit
return gx.last
endfunction
// *** Returns is group empty ***
function IsGroupExEmpty takes groupex gx returns boolean
return gx.empty
endfunction
// *** Counts units in group ***
function CountUnitsInGroupEx takes groupex gx returns integer
return gx.size
endfunction
// *** Sets a new size of group ***
function SetGroupExSize takes groupex gx, integer size returns nothing
set gx.size = size
endfunction
// *** Retuns is group dead ***
function IsGroupExDead takes groupex gx returns boolean
return gx.dead
endfunction
// *** Picks enum units in range ***
function GroupExEnumUnitsInRange takes groupex gx, real x, real y, real radius, boolexpr bx returns nothing
call gx.EnumRadius(x, y, radius, bx)
endfunction
// *** Picks enum units in rect ***
function GroupExEnumUnitsInRect takes groupex gx, rect r, boolexpr bx returns nothing
call gx.EnumRect(r, bx)
endfunction
// *** Returns unit by index ***
function GroupExReadIndex takes groupex gx, integer index returns unit
return gx[index]
endfunction
// *** Swaps the unit at given index with new one ***
function GroupExSwapUnit takes groupex gx, integer index, unit whichUnit returns nothing
set gx[index] = whichUnit
endfunction
// *** Returns enum unit ***
constant function GetEnumUnitEx takes nothing returns unit
return groupex.EnumUnit
endfunction
// *** Returns filter unit (in case someone uses Ex) ***
constant function GetFilterUnitEx takes nothing returns unit
return GetFilterUnit()
endfunction
// *** Retuirns for group execution ***
constant function GetExecutedGroupEx takes nothing returns groupex
return groupex.ExecutedGroupEx
endfunction
// *** Will filter units in range ***
function GroupExFilterRange takes real x, real y, real radius, code c returns nothing
call groupex.FilterRadius(x, y, radius, c)
endfunction
// *** Will filter units in rect ***
function GroupExFilterRect takes rect r, code c returns nothing
call groupex.FilterRect(r, c)
endfunction
// *** Set group custom value ***
function SetGroupExValue takes groupex gx, integer value returns nothing
set gx.value = value
endfunction
// *** Get group custom value ***
constant function GetGroupExValue takes groupex gx returns integer
return gx.value
endfunction
// --------------------------------------------------------------------------
// *** Extra groupex functions ***
// --------------------------------------------------------------------------
globals
private real NearX = 0.
private real NearY = 0.
private real NearD = 0.
endglobals
// *** Returns nearest unit ***
private function GetEnumDistance takes nothing returns nothing
local unit u = GetEnumUnitEx()
local real d = hypot(NearX-GetUnitX(u), NearY-GetUnitY(u))
if (NearD == -1.) then
set NearD = d
set bj_ghoul[0] = u
else
if (NearD > d) then
set NearD = d
set bj_ghoul[0] = u
endif
endif
set u = null
endfunction
function GetNearestUnitEx takes real x, real y, real radius, boolexpr bx returns unit
// *** Locals ***
local groupex gx = groupex.create()
// *** Pick every unit ***
call gx.EnumRadius(x, y, radius, bx)
// *** Now do for loop ***
set NearX = x
set NearY = y
set NearD = -1.
set bj_ghoul[0] = null
call gx.For(function GetEnumDistance)
call gx.delete()
// *** Return closest unit ***
return (bj_ghoul[0])
endfunction
// *** Returns random unit in range ***
function GetRandomUnitInRangeEx takes real x, real y, real radius, boolexpr bx returns unit
// *** Locals ***
local groupex gx = groupex.create()
// *** Pick units and get random one ***
call gx.EnumRadius(x, y, radius, bx)
set bj_ghoul[0] = gx.Random()
// *** Clear temp group ***
call gx.delete()
// *** Return our unit ***
return (bj_ghoul[0])
endfunction
// *** Returns subgroup ***
function GetRandomSubGroupEx takes groupex gx, integer count returns groupex
// *** Locals ***
local groupex temp = groupex.copy(gx)
local integer size = gx.size
local integer index
local groupex g
// *** Copy it ***
if (count >= size) then
// *** wantDestroy? ***
if (wantDestroyGroupEx) then
call gx.delete()
set wantDestroyGroupEx = false
endif
return (temp)
endif
// *** Do loop here ***
set g = groupex.create()
loop
exitwhen (count == 0)
set index = GetRandomInt(1, size)
call g.AddUnit(temp[index])
call temp.RemoveUnitByIndex(index)
set size = size - 1
set count = count - 1
endloop
// *** wantDestroy? ***
if (wantDestroyGroupEx) then
call gx.delete()
set wantDestroyGroupEx = false
endif
// *** Clear memory ***
call temp.delete()
return (g)
endfunction
// *** Adds units from given group to new one ***
function GroupExAddGroupEx takes groupex source, groupex from returns nothing
// *** Locals ***
local integer i
// *** Loop through source group and add units to main ***
//! runtextmacro FOR("i", "1", "source.size+1")
set bj_ghoul[0] = from[i]
if (not source.Contains(bj_ghoul[0])) then
call source.AddUnit(bj_ghoul[0])
endif
//! runtextmacro ENDFOR()
// *** wantDestroy? ***
if (wantDestroyGroupEx) then
call from.delete()
set wantDestroyGroupEx = false
endif
// *** Null vars ***
set bj_ghoul[0] = null
endfunction
// *** Removes group from given group ***
function GroupExRemoveGroupEx takes groupex source, groupex from returns nothing
// *** Locals ***
local integer i
// *** Loop through source group and remove units from main ***
//! runtextmacro FOR("i", "1", "from.size+1")
set bj_ghoul[0] = from[i]
if (source.Contains(bj_ghoul[0])) then
call source.RemoveUnit(bj_ghoul[0])
endif
//! runtextmacro ENDFOR()
// *** wantDestroy? ***
if (wantDestroyGroupEx) then
call from.delete()
set wantDestroyGroupEx = false
endif
// *** Clear leaks ***
set bj_ghoul[0] = null
endfunction
// *** Checks is given group in rect ***
function IsGroupExInRect takes groupex gx, rect r returns boolean
// *** Locals ***
local integer i
local real x
local real y
local real minx = GetRectMinX(r)
local real miny = GetRectMinY(r)
local real maxx = GetRectMaxX(r)
local real maxy = GetRectMaxY(r)
// *** Check is enum in rect ***
//! runtextmacro FOR("i", "1", "gx.size+1")
set bj_ghoul[0] = gx[i]
set x = GetUnitX(bj_ghoul[0])
set y = GetUnitY(bj_ghoul[0])
if (x < minx or x > maxx or y < miny or y > maxy) then
// *** Clear ghoul ***
set bj_ghoul[0] = null
// *** Groupex is not in rect ***
return false
endif
//! runtextmacro ENDFOR()
// *** wantDestroy? ***
if (wantDestroyGroupEx) then
call gx.delete()
set wantDestroyGroupEx = false
endif
// *** Clear ghoul ***
set bj_ghoul[0] = null
// *** Groupex is in rect ***
return true
endfunction
endlibrary
// ===================================
// *** Damage detection library_once ***
// ===================================
library_once DDDamageDetect initializer Init requires DD, DDGroup
// *** Globals ***
globals
// *** Constants ***
private constant eventid EVENT_ENTER_REGION = ConvertUnitEvent(0x5)
// *** Registerd triggers ***
private trigger array RegTrig[8191]
private integer array pRegTrig[8191]
private groupex Grp = 0
private integer Count = 0
private trigger Trig = null
private trigger MainTrig = null
// *** Unit ptr values ***
private integer array NCasts[8191]
private integer array NAttacks[8191]
// *** Other vars ***
private region Reg = null
private integer FullAttacks = 0
endglobals
// *** Main damage detection functions ***
function TriggerRegisterAnyUnitDamaged takes trigger trig returns boolean
// *** id get ***
local integer id = HtoI(trig)
// *** already registerd? ***
if (pRegTrig[id] == 0) then
set RegTrig[Count] = trig
set Count = Count + 1
set pRegTrig[id] = Count
return true
endif
return false
endfunction
// *** Unregisters damage detection ***
function TriggerUnregisterAnyUnitDamaged takes trigger trig returns boolean
// locals
local integer id = HtoI(trig)
// *** valid trigger ***
if (pRegTrig[id] != 0) then
set Count = Count - 1
set RegTrig[pRegTrig[id]-1] = RegTrig[Count]
set pRegTrig[HtoI(RegTrig[Count])] = pRegTrig[id]
set pRegTrig[id] = 0
set RegTrig[Count] = null
return true
endif
// *** invalid request ***
return false
endfunction
// *** Returns is taken damage of type melee ***
function IsDamageTypeMelee takes nothing returns boolean
return not ( NCasts[ArrayId(GetEventDamageSource())] > 0 or NAttacks[ArrayId(GetTriggerUnit())] == 0 )
endfunction
// *** Clear dynamic allocated units ***
private function EnumUnitsFree takes nothing returns nothing
set NAttacks[ArrayId(GetEnumUnitEx())] = 0
call Grp.RemoveUnit(GetEnumUnitEx())
endfunction
// *** Executes all triggers ***
private function ExecuteTriggers takes nothing returns boolean
local integer i = 0
local integer j = -1
// *** If it was some bug damage then do nothing ***
if (GetEventDamage() == 0.) then
// *** It is impossible to deal 0 damage ***
return (false)
endif
// *** Loop through all triggers with 4x speed ***
//! runtextmacro FOR("i", "0", "((Count-1)/4)+1")
//! textmacro dddd__TRIG_EXEC_ATTEMPT
// *** Exec Attempt ***
set j = j + 1
if (IsTriggerEnabled(RegTrig[j])) then
if (TriggerEvaluate(RegTrig[j])) then
call TriggerExecute(RegTrig[j])
endif
endif
//! endtextmacro
//! runtextmacro dddd__TRIG_EXEC_ATTEMPT()
//! runtextmacro dddd__TRIG_EXEC_ATTEMPT()
//! runtextmacro dddd__TRIG_EXEC_ATTEMPT()
//! runtextmacro dddd__TRIG_EXEC_ATTEMPT()
//! runtextmacro ENDFOR()
// *** Unit took damage now ***
set j = ArrayId(GetTriggerUnit())
if (NAttacks[j] > 0) then
set NAttacks[j] = NAttacks[j] - 1
endif
return (false)
endfunction
// *** Registers all current units in a map ***
private function RegisterAllUnits takes nothing returns boolean
if (not IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) and GetWidgetLife(GetFilterUnit()) > 0.405) then
call TriggerRegisterUnitEvent(Trig, GetFilterUnit(), EVENT_UNIT_DAMAGED)
endif
return (false)
endfunction
// *** Main actions ***
private function Actions takes nothing returns nothing
local unit u
local integer id
// *** No instant exec ***
call DisableTrigger(MainTrig)
// *** Enable this instance ***
call EnableTrigger(MainTrig)
// *** Triggerd by cast ***
if (GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_CAST) then
// *** Assign locals ***
set u = GetTriggerUnit()
set id = ArrayId(u)
// *** Add this cast ***
set NCasts[id] = NCasts[id] + 1
// *** Do delay (wait for end spell cast) ***
call TriggerSleepAction(2.5)
// *** Reduce Cast ***
if (NCasts[id] > 0) then
set NCasts[id] = NCasts[id] - 1
endif
// *** Null locals ***
set u = null
elseif (GetTriggerEventId() == EVENT_PLAYER_UNIT_ATTACKED) then
// *** Make sure that he deals damage now ***
set NCasts[ArrayId(GetAttacker())] = 0
// *** Assign locals ***
set u = GetTriggerUnit()
set id = ArrayId(u)
// *** Do we need to register damage take? ***
if (not Grp.Contains(u)) then
call Grp.AddUnit(u)
endif
// *** Increase number of attacks ***
set NAttacks[id] = NAttacks[id] + 1
// *** Full attacks are on ***
set FullAttacks = FullAttacks + 1
// *** Do delay here ***
// *(if there is 5 seconds of no attack then reset counter)*
if (FullAttacks <= 010) then
call TriggerSleepAction(5.)
endif
// *** Reduce full attacks ***
set FullAttacks = FullAttacks - 1
// *** Do reset units counter? ***
if (FullAttacks == 0) then
// *** Any unit who took damage now gets flushed ***
call Grp.For(function EnumUnitsFree)
endif
// *** Null locals ***
set u = null
elseif (GetTriggerEventId() == EVENT_ENTER_REGION) then
// *** Register this unit ***
call TriggerRegisterUnitEvent(Trig, GetTriggerUnit(), EVENT_UNIT_DAMAGED)
else
// *** Do periodic reset here ***
call DestroyTrigger(Trig)
set Trig = CreateTrigger()
call TriggerAddConditionEx(Trig, function ExecuteTriggers)
// *** Now do refresh enum register ***
call Grp.FilterRect(bj_mapInitialPlayableArea, function RegisterAllUnits)
endif
endfunction
// *** Main init function ***
private function Init takes nothing returns nothing
// *** Locals ***
local trigger t = CreateTrigger()
local integer i = 0
// *** Load main objects ***
set Grp = CreateGroupEx()
set Trig = CreateTrigger()
set Reg = CreateRegion()
call RegionAddRect(Reg, bj_mapInitialPlayableArea)
// *** All units now are geting registerd ***
call Grp.FilterRect(bj_mapInitialPlayableArea, function RegisterAllUnits)
call TriggerAddConditionEx(Trig, function ExecuteTriggers)
// *** Register any unit attacked ***
//! runtextmacro FOR("i", "0", "bj_MAX_PLAYER_SLOTS")
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ATTACKED, GetDummyFilter())
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_CAST, GetDummyFilter())
//! runtextmacro ENDFOR()
// *** Rect enter event ***
call TriggerRegisterEnterRegion(t, Reg, GetDummyFilter())
// *** Register period event ***
call TriggerRegisterTimerEvent(t, 60., true)
// *** Add action ***
call TriggerAddAction(t, function Actions)
set MainTrig = t
endfunction
// *** Registers any unit enters map area ***
function TriggerRegisterEnterMapArea takes trigger whichTrigger returns event
return TriggerRegisterEnterRegion(whichTrigger, Reg, GetDummyFilter())
endfunction
// *** Registers any unit leaves map area ***
function TriggerRegisterLeaveMapArea takes trigger whichTrigger returns event
return TriggerRegisterLeaveRegion(whichTrigger, Reg, GetDummyFilter())
endfunction
endlibrary
// ==========================================
// *** This is damage over time system ***
// ==========================================
library_once DDDamageOverTime requires DD, DDStrings
// *** Main struct ***
private struct damagedata
// *** Members ***
public unit u
public unit v
public real d
public real per
public real rem
public attacktype at
public damagetype dt
public weapontype wt
public string fl
public string ap
public timer t
// *** Constructor ***
static method create takes unit source, unit victim, real damage, real interval, real duration, attacktype at, damagetype dt, weapontype wt returns thistype
local thistype this = thistype.allocate()
set this.u = source
set this.v = victim
set this.d = damage
set this.per = interval
set this.rem = duration
set this.at = at
set this.dt = dt
set this.wt = wt
set this.t = CreateTimer()
return (this)
endmethod
// *** Destructor ***
//! runtextmacro destr()
call PauseTimer(this.t)
call DestroyTimer(this.t)
set this.fl = null
set this.ap = null
set this.t = null
set this.u = null
set this.v = null
//! runtextmacro enddestr()
endstruct
// *** Globals ***
globals
private damagedata array DData[8191]
endglobals
// *** Will parse string for damage data ***
private function ParseStringx takes string file, damagedata dd returns nothing
local strings strs
// *** Is valid string ***
if (file != null and file != "") then
// *** Parse string now ***
set strs = ParseString(file)
// *** File has custom attach point? ***
if (strs[1] != null) then
set dd.fl = strs[0]
set dd.ap = strs[1]
// *** Check which one is attach point and which one is file ***
if (StringContainsStringEx(strs[0], ".", false)) then
set dd.fl = strs[0]
set dd.ap = strs[1]
else
set dd.fl = strs[1]
set dd.ap = strs[0]
endif
else
// *** Do default origin point ***
set dd.fl = file
set dd.ap = "origin"
endif
// *** Clear strs ***
set strs[0] = null
set strs[1] = null
call strs.destroy()
// *** Check out are strings correct ***
debug call Msg.execute(dd.ap)
debug call Msg.execute(dd.fl)
endif
endfunction
// *** Periodic action ***
private function DamagePeriodic takes nothing returns nothing
// *** Locals ***
local damagedata dd = DData[ArrayId(GetExpiredTimer())]
// *** Deal damage now ***
call UnitDamageTarget(dd.u, dd.v, dd.d, true, false, dd.at, dd.dt, dd.wt)
// *** Add effect ***
if (dd.fl != null and dd.fl != "") then
call DestroyEffect(AddSpecialEffectTarget(dd.fl, dd.v, dd.ap))
endif
// *** Decrease remaining time ***
set dd.rem = dd.rem - dd.per
// *** Done? ***
if (dd.rem <= 0.) then
set DData[ArrayId(dd.t)] = 0
call dd.delete()
endif
endfunction
// *** Main function call ***
function DamageTargetTimed takes unit source, unit victim, real damage, real interval, real duration, attacktype at, damagetype dt, weapontype wt, string file returns nothing
// *** Locals ***
local damagedata dd = damagedata.create(source, victim, damage, interval, duration, at, dt, wt)
// *** Store damage data ***
set DData[ArrayId(dd.t)] = dd
// *** Parse attach point and file ***
call ParseStringx(file, dd)
// *** Do damage now ***
call UnitDamageTarget(source, victim, damage, true, false, at, dt, wt)
// *** Damage effect apply now ***
if (dd.fl != null and dd.fl != "") then
call DestroyEffect(AddSpecialEffectTarget(dd.fl, dd.v, dd.ap))
endif
// *** Start timer ***
call TimerStart(dd.t, interval, true, function DamagePeriodic)
endfunction
endlibrary
// ==========================
// *** Effect group ***
// ==========================
library_once DDCluster
// *** main cluster type ***
struct cluster
// *** Members ***
private integer cnt = 0
// *** Static members ***
private static constant hashtable HTABLE = InitHashtable()
// *** Add new effect to nexus ***
public method AddEffect takes effect whichEffect returns nothing
call SaveEffectHandle(cluster.HTABLE, integer(this), this.cnt, whichEffect)
set this.cnt = this.cnt + 1
endmethod
// *** Destructor ***
public method delete takes nothing returns nothing
loop
//! textmacro cluster___DESTROY_EFFECT
set this.cnt = this.cnt - 1
exitwhen (this.cnt < 0)
call DestroyEffect(LoadEffectHandle(cluster.HTABLE, integer(this), this.cnt))
//! endtextmacro
// *** 8x speed ***
//! runtextmacro cluster___DESTROY_EFFECT()
//! runtextmacro cluster___DESTROY_EFFECT()
//! runtextmacro cluster___DESTROY_EFFECT()
//! runtextmacro cluster___DESTROY_EFFECT()
//! runtextmacro cluster___DESTROY_EFFECT()
//! runtextmacro cluster___DESTROY_EFFECT()
//! runtextmacro cluster___DESTROY_EFFECT()
//! runtextmacro cluster___DESTROY_EFFECT()
endloop
call FlushChildHashtable(cluster.HTABLE, integer(this))
call this.destroy()
endmethod
endstruct
// =========================================================
// *** Cluster functions ***
// =========================================================
// *** Creates new empty cluster ***
function CreateCluster takes nothing returns cluster
return cluster.create()
endfunction
// *** Add new effect to cluster ***
function ClusterAddEffect takes cluster whichCluster, effect whichEffect returns nothing
call whichCluster.AddEffect(whichEffect)
endfunction
// *** Destroys cluster and all effects inside ***
function DestroyCluster takes cluster whichCluster returns nothing
call whichCluster.delete()
endfunction
// *** Timed executed function ***
private function DestroyClusterAfter takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
call DestroyCluster(cluster( LoadInteger(DDTable, id, 0) ))
call RemoveSavedInteger(DDTable, id, 0)
call PauseTimer(t)
call DestroyTimer(t)
set t = null
endfunction
// *** Destroys cluster after time ***
function DestroyClusterTimed takes cluster whichCluster, real duration returns nothing
// *** Locals ***
local timer t
// *** Check duration ***
if (duration <= 0) then
call whichCluster.destroy()
return
endif
// *** Load timer ***
set t = CreateTimer()
call SaveInteger(DDTable, GetHandleId(t), 0, integer(whichCluster))
call TimerStart(t, duration, false, function DestroyClusterAfter)
// *** Leaks gone ***
set t = null
endfunction
endlibrary
// ====================================================================
// *** This is the algorithm for detecting destructable death ***
// ====================================================================
library_once DDDestructableDeathDetection initializer Init requires DD
// *** Globals used for detection ***
globals
// *** Main trigger ***
private trigger Trig = null
// *** Triggers group ***
private trigger array Trg[8191]
private integer array pTrig[8191]
private integer TCount = 0
private dd_vector Vec = Null
private rect EnumRect = null
endglobals
// ==================================================
// *** Main event functions ***
// ==================================================
// *** This registers any destructable dies event ***
function TriggerRegisterAnyDestructableDeath takes trigger trig returns boolean
// *** id get ***
local integer id = HtoI(trig)
// *** already registerd? ***
if (pTrig[id] == 0) then
set Trg[TCount] = trig
set TCount = TCount + 1
set pTrig[id] = TCount
return true
endif
return false
endfunction
// *** This will unregister any destructable death event ***
function TriggerUnregisterAnyDestructableDeath takes trigger trig returns boolean
// locals
local integer id = HtoI(trig)
// *** valid trigger ***
if (pTrig[id] != 0) then
set TCount = TCount - 1
set Trg[pTrig[id]-1] = Trg[TCount]
set pTrig[HtoI(Trg[TCount])] = pTrig[id]
set pTrig[id] = 0
return true
endif
// *** invalid request ***
return false
endfunction
// *** Execute all triggers here ***
private function ExecuteTriggers takes nothing returns boolean
// *** Locals ***
local integer i
// *** Now loop through registerd triggers ***
//! runtextmacro FOR("i", "0", "((TCount-1)/4)+1")
//! textmacro dd_destdeath___TRIGGER_FIRE takes OFFSET
// *** Is enabled? ***
if (IsTriggerEnabled(Trg[(i*4)+$OFFSET$])) then
if (TriggerEvaluate(Trg[(i*4)+$OFFSET$])) then
call TriggerExecute(Trg[(i*4)+$OFFSET$])
endif
endif
//! endtextmacro
//! runtextmacro dd_destdeath___TRIGGER_FIRE("0")
//! runtextmacro dd_destdeath___TRIGGER_FIRE("1")
//! runtextmacro dd_destdeath___TRIGGER_FIRE("2")
//! runtextmacro dd_destdeath___TRIGGER_FIRE("3")
//! runtextmacro ENDFOR()
return (false)
endfunction
// *** Is destructable tree? ***
function IsDestructableTree takes destructable d returns boolean
local unit u = CreateUnit(DD_p[0], 'ewsp', GetDestructableX(d), GetDestructableY(d), 0.)
local boolean b = IssueTargetOrder(u, "harvest", d)
call RemoveUnit(u)
set u = null
return (b)
endfunction
// --------------------------------------------------------------------------------------------
// *** Filter for enum dests in range ***
private function EnumDestsInRangeFilter takes nothing returns boolean
local destructable d = GetFilterDestructable()
local real x = GetDestructableX(d)-Vec.x
local real y = GetDestructableY(d)-Vec.y
set d = null
return ((x*x) + (y*y) <= Vec.z)
endfunction
// *** Picks enum destructables in range ***
function EnumDestructablesInRange takes real x, real y, real radius, boolexpr filter, code func returns nothing
// *** Locals ***
local filterfunc ff = Filter(function EnumDestsInRangeFilter)
// *** Variables set ***
call SetRect(EnumRect, x-radius, y-radius, x+radius, y+radius)
call Vec.Move(x, y, radius*radius)
// *** Filter setup ***
if (filter != null) then
set filter = And(ff, filter)
else
set filter = ff
endif
// *** Enum actions ***
call EnumDestructablesInRect(EnumRect, filter, func)
// *** Clear leaks ***
if (filter != ff) then
call DestroyBoolExpr(filter)
endif
endfunction
// *** Stores all dests in dest group ***
private function GetEnumDests takes nothing returns nothing
call TriggerRegisterDeathEvent(Trig, GetEnumDestructable())
endfunction
// *** Main init function ***
private function Init takes nothing returns nothing
// *** Load trigger ***
set Trig = CreateTrigger()
// *** Now register enum event ***
call EnumDestructablesInRect(bj_mapInitialPlayableArea, GetDummyFilter(), function GetEnumDests)
// *** Add action ***
call TriggerAddConditionEx(Trig, function ExecuteTriggers)
// *** Rect load ***
set EnumRect = Rect(0., 0., 0., 0.)
set Vec = dd_vector.new()
endfunction
endlibrary
// -----------------------------------------
// *** Main item functions library_once ***
// -----------------------------------------
library_once DDxEGUIxItems initializer Items_Core requires EGUI
// *** Macros ***
//! textmacro dd_items__TRIGGER_RUN takes TRIG
if (IsTriggerEnabled($TRIG$)) then
if (TriggerEvaluate($TRIG$)) then
call TriggerExecute($TRIG$)
endif
endif
//! endtextmacro
// *** words ***
private keyword box
// *** Items globals ***
globals
private string ItemCombineEffect = null
private string ItemCombineAPoint = null
private sound ItemCombineSound = null
private boolean UseItemStack = false
private integer I = 0
// *** Triggers exec ***
private trigger array Trig[8191]
private integer array pTrig[8191]
private integer TCount = 0
// *** EGUI globals ***
private box e_lastCreatedItemConfig = Null
private item e_fusedItem = null
endglobals
// *** Define items group here ***
private struct box
// *** Members ***
public integer array it[6] // Item Type which hero needs
public integer array need[6] // How much of this item type hero needs to have to fuse
public integer fuse // Type which will be fused in to
public integer n_types // N items of different type
public integer count // Number of items needed
// *** Static vars ***
public static integer NBoxes = 0
// *** Constructor ***
static method create takes nothing returns box
set thistype.NBoxes = thistype.NBoxes + 1
return box.allocate()
endmethod
endstruct
// ===============================================================
// * Main item actions -> pick up item (event)
// * This actions handle both item fuse and stacking
// ===============================================================
private function Actions takes nothing returns boolean
local unit u = GetTriggerUnit() // Unit who picked up item
local integer array hero_item_type // Only differet item types
local integer array hero_item_type_count // How much of this type items hero has
local item array hero_item // All items of hero (sorted)
local integer n_hero_items = 0 // Number of hero items
local integer n_hero_diff_items = 0 // Number of different type items hero has
local integer i // Loop index i
local integer j // Loop index j
local box b // Box b
local boolean has_all // Checks does hero has all items from given box
// *** Get hero items (sorted) ***
//! runtextmacro FOR("I", "0", "bj_MAX_INVENTORY")
// *** Get hero item in slot I ***
set hero_item[n_hero_items] = UnitItemInSlot(u, I)
if (hero_item[n_hero_items] != null) then
// *** New item on hero found ***
set n_hero_items = n_hero_items + 1
// *** Already contains this type? ***
set hero_item_type[6] = GetItemTypeId(hero_item[n_hero_items-1])
//! runtextmacro FOR("i", "0", "n_hero_diff_items")
if (hero_item_type[i] == hero_item_type[6]) then
// *** Same type detected increase counter ***
set hero_item_type_count[i] = hero_item_type_count[i] + 1
set i = 010
exitwhen (true)
endif
//! runtextmacro ENDFOR()
// *** It was not null then store it and get type + increase index counter ***
if (i != 010) then
set hero_item_type[n_hero_diff_items] = hero_item_type[6]
set hero_item_type_count[n_hero_diff_items] = 1
set n_hero_diff_items = n_hero_diff_items + 1
endif
endif
//! runtextmacro ENDFOR()
// *** Now loop through all item configs ***
//! runtextmacro FOR("I", "1", "box.NBoxes+1")
// *** Get enum box ***
set b = box(I)
// *** Here we do loop through box ***
//! runtextmacro FOR("i", "0", "b.n_types")
// *** Now loop through items user has and check do they match the in box one ***
set has_all = false // we say hero does not have this needed items
//! runtextmacro FOR("j", "0", "n_hero_diff_items")
if (b.it[i] == hero_item_type[j] and b.need[i] == hero_item_type_count[j]) then
set has_all = true // we found he has them so set it to true
exitwhen (true)
endif
//! runtextmacro ENDFOR()
// if hero does not have needed item in this box then simple go to next box now (do not continue loop through this box)
exitwhen (not has_all)
//! runtextmacro ENDFOR()
// *** We finsihed looping through one box and now we do check does hero has all needed items in this box ***
// *** Do not loop through more boxes since we now found the right one ***
exitwhen (has_all)
//! runtextmacro ENDFOR()
// *** Check does hero has all items in a box ***
if (has_all) then
// *** Yap hero has all needed items so delete old ones and give new one ***
//! runtextmacro FOR("I", "0", "b.n_types")
set i = b.need[I]
//! runtextmacro FOR("j", "0", "n_hero_items")
if (GetItemTypeId(hero_item[j]) == b.it[I]) then
call RemoveItem(hero_item[j])
set n_hero_items = n_hero_items - 1
set hero_item[j] = hero_item[n_hero_items]
set i = i - 1
set j = j - 1
exitwhen (i == 0)
endif
//! runtextmacro ENDFOR()
//! runtextmacro ENDFOR()
// *** Now add new item ***
set e_fusedItem = UnitAddItemById(u, b.fuse)
// *** Apply effect if set ***
//! runtextmacro IFX("ItemCombineEffect != null", "call DestroyEffect(AddSpecialEffectTarget(ItemCombineEffect, u, ItemCombineAPoint))")
// *** Load sound if set ***
if (ItemCombineSound != null) then
// *** Check is playing and the start sound ***
//! runtextmacro IFX("GetSoundIsPlaying(ItemCombineSound)", "call StopSound(ItemCombineSound, false, false)")
call SetSoundPosition(ItemCombineSound, GetUnitX(u), GetUnitY(u), 0.)
call StartSound(ItemCombineSound)
endif
// -----------------------------------------------------------------
// *** Now execute all registerd triggers ***
// -----------------------------------------------------------------
//! runtextmacro FOR("i", "0", "((TCount-1)/2)+1")
//! runtextmacro dd_items__TRIGGER_RUN("Trig[(i*2)+0]")
//! runtextmacro dd_items__TRIGGER_RUN("Trig[(i*2)+1]")
//! runtextmacro ENDFOR()
// *** Null fused item ***
set e_fusedItem = null
else
// *** No matching boxes where found ***
// *** If no box was valid and ItemStack is enabled then try to combine the item ***
set hero_item[bj_MAX_INVENTORY] = GetManipulatedItem()
if (UseItemStack and GetItemCharges(hero_item[6]) > 0 and not IsItemPowerup(hero_item[6])) then
set hero_item_type[6] = GetItemTypeId(hero_item[6])
// *** Try search for same item type ***
//! runtextmacro FOR("i", "0", "n_hero_diff_items")
// *** Found it ***
if (GetItemTypeId(hero_item[i]) == hero_item_type[6] and hero_item[i] != hero_item[6]) then
// *** Found it! now increase charges and remove picked one ***
call SetItemCharges(hero_item[i], GetItemCharges(hero_item[i]) + GetItemCharges(hero_item[6]))
call RemoveItem(hero_item[6])
endif
//! runtextmacro ENDFOR()
endif
set hero_item[bj_MAX_INVENTORY] = null
endif
// *** Null vars ***
//! runtextmacro FORX("i", "0", "bj_MAX_INVENTORY", "set hero_item[i] = null")
set u = null
return (false)
endfunction
// ===========================================================================
// *** Main Init function ***
// ===========================================================================
private function Items_Core takes nothing returns nothing
// *** Load main trigger ***
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEvent( t, EVENT_PLAYER_UNIT_PICKUP_ITEM )
call TriggerAddConditionEx( t, function Actions )
endfunction
// ========================================================
// *** Global functions ***
// ========================================================
// -----------------------------------------------------
// *** Config ***
// -----------------------------------------------------
// *** File which is created when item is fused ***
function E_SetItemConfigModelFile takes string file returns boolean
// *** Locals ***
local strings strs
// *** Already set? or game started or invalid string ***
//! runtextmacro IFX("bj_gameStarted or ItemCombineEffect != null or file == null or file == DD_NO_FILE", "return false")
// *** Do parse string ***
set strs = ParseString(file)
// *** Contains double str? ***
if (strs[1] != null) then
if (StringCase(strs[0], false) != "default" and not StringContainsStringEx(strs[0], ".", false)) then
set strs[2] = strs[0]
set strs[0] = strs[1]
set strs[1] = strs[2]
set strs[2] = null
endif
else
// *** Contains single string ***
set strs[1] = "origin"
endif
// *** Do needed swap ***
if (StringCase(strs[0], false) == "default") then
set strs[0] = "Abilities\\Spells\\Items\\AIlm\\AIlmTarget.mdl"
endif
// *** Do preload model ***
call Preload(strs[0])
// *** Store current setup ***
set ItemCombineEffect = strs[0]
set ItemCombineAPoint = strs[1]
// *** Clear memory ***
set strs[0] = null
set strs[1] = null
call strs.destroy()
// *** Was set correctly ***
return true
endfunction
// *** Enables sound playing for item combine ***
function E_SetItemConfigSoundEnable takes real volume returns boolean
// *** Sound was already set? ***
//! runtextmacro IFX("ItemCombineSound != null", "return false")
// *** Load sound ***
set ItemCombineSound = CreateSound( "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImage.wav", false, true, true, 10, 10, "" )
call SetSoundParamsFromLabel( ItemCombineSound, "MirrorImage" )
call SetSoundDuration( ItemCombineSound, 1756 )
call SetSoundPitch( ItemCombineSound, 1.2 )
call SetSoundVolume( ItemCombineSound, R2I(volume*1.27) )
call Preload( "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImage.wav" )
// *** Was load successfully ***
return true
endfunction
// *** Enables item stack ***
function E_EnableItemStacking takes nothing returns boolean
// *** Item stack already used or game started ***
//! runtextmacro IFX("UseItemStack or bj_gameStarted", "return false")
// *** Enable item stack ***
set UseItemStack = true
return true
endfunction
// *********************************************************************************
// *** Define custom functions for item works ***
// *********************************************************************************
// *** Will create new item box ***
function E_CreateItemConfig takes nothing returns nothing
//! runtextmacro IFX("not bj_gameStarted", "set e_lastCreatedItemConfig = box.create()")
endfunction
// *** Will return last created itemconfig ***
constant function E_GetLastCreatedItemConfig takes nothing returns box
// *** Game started? ***
//! runtextmacro IFX("bj_gameStarted", "return 0")
return e_lastCreatedItemConfig
endfunction
// *** Sets item config fused type ***
function E_SetItemFuseTypeInItemConfig takes box whichItemConfig, integer e_fuseitemtype returns boolean
// *** Fuse type already set? or game started ***
//! runtextmacro IFX("whichItemConfig.fuse != null or bj_gameStarted", "return false")
// *** Was not set, so set it now ***
set whichItemConfig.fuse = e_fuseitemtype
return true
endfunction
// *** Will add new item type to item config ***
function E_AddItemTypeToItemConfig takes box whichItemConfig, integer e_itemtype returns boolean
// *** Hero max inv slot is 6 or game started ***
//! runtextmacro IFX("whichItemConfig.count >= bj_MAX_INVENTORY or bj_gameStarted", "return false")
// *** Do we already have this type? ***
//! runtextmacro FOR("I", "0", "whichItemConfig.count")
if (whichItemConfig.it[I] == e_itemtype) then
set whichItemConfig.need[I] = whichItemConfig.need[I] + 1
set whichItemConfig.count = whichItemConfig.count + 1
return true
endif
//! runtextmacro ENDFOR()
// *** Not same type so add new one here ***
set whichItemConfig.it[whichItemConfig.n_types] = e_itemtype
set whichItemConfig.need[whichItemConfig.n_types] = 1
set whichItemConfig.count = whichItemConfig.count + 1
set whichItemConfig.n_types = whichItemConfig.n_types + 1
return true
endfunction
// *** Will register given trigger when item is fused ***
function E_TriggerRegisterAnyItemFused takes trigger trig returns boolean
local integer id = HtoI(trig)
if (pTrig[id] == 0) then
set Trig[TCount] = trig
set TCount = TCount + 1
set pTrig[id] = TCount
return true
endif
return false
endfunction
// *** Will unregister trigger event when item is fused ***
function E_TriggerUnregisterAnyItemFused takes trigger trig returns boolean
local integer id = HtoI(trig)
if (pTrig[id] != 0) then
set TCount = TCount - 1
set Trig[pTrig[id]-1] = Trig[TCount]
set pTrig[HtoI(Trig[TCount])] = pTrig[id]
set Trig[TCount] = null
set pTrig[id] = 0
return true
endif
return false
endfunction
// *** Returns fused item ***
constant function E_GetFusedItem takes nothing returns item
return e_fusedItem
endfunction
endlibrary
// *** Functions for EGUI ***
library_once EGUI requires NativeDDUniversalPack
// *** Globals ***
globals
// *** GroupEx Globals ***
private groupex e_lastCreatedGroupEx = Null
endglobals
// *** Returns distance between widgets ***
function E_DistWidget takes widget w1, widget w2 returns real
return SquareRoot(hypot( GetWidgetX(w1)-GetWidgetX(w2), GetWidgetY(w1)-GetWidgetY(w2) ))
endfunction
// *** Returns angle between widgets ***
function E_AngWidget takes widget w1, widget w2 returns real
return RADTODEG * Atan2(GetWidgetY(w2)-GetWidgetY(w1), GetWidgetX(w2)-GetWidgetX(w1))
endfunction
// *** Returns parabolic height ***
constant function E_GetParabolicHeight takes real md, real mh, real d returns real
return ((-d*d/md) + d + 1. - (.25*md)) * mh
endfunction
// =========================
// *** Effect Actions ***
// =========================
// *** Loads effect at given point ***
// *** Credits to KaTTaNa for discovering that invs platform can make effect stay in air ***
function E_AddSpecialEffectZ takes string file, location where, real z returns nothing
local destructable d = CreateDestructableZ('OTip', GetLocationX(where), GetLocationY(where), z, 0., 1, 0)
set bj_lastCreatedEffect = AddSpecialEffectLoc(file, where)
call RemoveDestructable(d)
set d = null
endfunction
// =========================
// *** Lightning actions ***
// =========================
private struct clig
lightningex lig
unit array u[2]
real dur
real app
boolean dis
static constant real Z_OFFSET = 30.
static constant real INTERVAL = THREAD_DELAY*.001
static method create takes string l_typ, unit u1, unit u2, real dur, real app, real r, real g, real b returns clig
local clig this = .allocate()
set this.lig = CreateLightningEx(l_typ, GetUnitX(u1), GetUnitY(u1), GetUnitFlyHeight(u1)+clig.Z_OFFSET, GetUnitX(u2), GetUnitY(u2), GetUnitFlyHeight(u2)+clig.Z_OFFSET)
set this.u[0] = u1
set this.u[1] = u2
set this.dis = true
if (app <= 0.) then
set app = 0.
call ChangeLightningColor(this.lig, R2I(r*2.55), R2I(g*2.55), R2I(b*2.55), 255)
else
call ChangeLightningColor(this.lig, R2I(r*2.55), R2I(g*2.55), R2I(b*2.55), 0)
call FadeLightning(this.lig, 0, 255, app)
endif
set this.app = app
set this.dur = dur+2.*app
return (this)
endmethod
method delete takes nothing returns nothing
call DestroyLightningEx(this.lig)
set this.u[0] = null
set this.u[1] = null
endmethod
endstruct
// *** Connects units with lightning ***
private function LockLightningOnUnits takes clig cl returns boolean
set cl.dur = cl.dur - clig.INTERVAL
call SetLightningPosEx(cl.lig, GetUnitX(cl.u[0]), GetUnitY(cl.u[0]), GetUnitFlyHeight(cl.u[0])+clig.Z_OFFSET, GetUnitX(cl.u[1]), GetUnitY(cl.u[1]), GetUnitFlyHeight(cl.u[1])+clig.Z_OFFSET)
if (cl.dur-cl.app <= 0. and cl.dis) then
set cl.dis = false
call FadeLightning(cl.lig, 255, 0, cl.app)
endif
return cl.dur <= 0.
endfunction
function E_ConnectUnitsWithLightning takes unit u1, unit u2, string l_typ, real r, real g, real b, real app, real dur returns lightningex
local clig cl = clig.create(l_typ, u1, u2, dur, app, r, g, b)
if (cl != Null and cl.dur > 0.) then
call NewThread(THREAD_DELAY, cl, callback.LockLightningOnUnits, false)
elseif (cl != Null) then
call cl.delete()
return (Null)
endif
return (cl.lig)
endfunction
// *** Adds unit to groupex ***
// *** This checks is unit already in group and adds it only if its not in groupex ***
// *** We must check this coz EGUI-ers might be uncareful ***
function E_GroupExAddUnit takes groupex gx, unit u returns nothing
if (not gx.Contains(u)) then
call gx.AddUnit(u)
endif
endfunction
// *** Returns enum units in region ***
function E_PickUnitsInRectEx takes rect r, boolexpr bx returns groupex
local groupex gx = CreateGroupEx()
call gx.EnumRect(r, bx)
call DestroyBoolExpr(bx)
return gx
endfunction
// *** Picks enum units in range ***
function E_PickUnitsInRangeEx takes location loc, real radius, boolexpr bx returns groupex
local groupex gx = CreateGroupEx()
call gx.EnumRadius(GetLocationX(loc), GetLocationY(loc), radius, bx)
call DestroyBoolExpr(bx)
return gx
endfunction
// *** Destroys group timed ***
private function E_TimedGroupExBlow takes nothing returns nothing
local timer t = GetExpiredTimer()
local groupex gx = DDGetValue(t)
local integer i
//! runtextmacro FORX("i", "1", "gx.size+1", "call RemoveUnit(gx[i])")
call DestroyGroupEx(gx)
call DDFlushValue(t)
call PauseTimer(t)
call DestroyTimer(t)
set t = null
endfunction
function E_DestroyGroupExTimed takes groupex gx, real timeout returns nothing
local timer t = CreateTimer()
call DDStoreValue(t, integer(gx))
call TimerStart(t, timeout, false, function E_TimedGroupExBlow)
set t = null
endfunction
// =========================
// *** Unit Actions ***
// =========================
// *** Fade unit ***
private keyword E_UnitFadeFunc
private function E_UnitFadeFunc takes hashtable table, real trans returns nothing
call SetUnitVertexColor(LoadUnitHandle(table, 0, 0), 0xFF, 0xFF, 0xFF, R2I(trans*2.55))
endfunction
function E_FadeUnit takes unit u, real period, real s_trans, real e_trans returns nothing
local hashtable ht = InitHashtable()
call SaveUnitHandle(ht, 0, 0, u)
call FadeObject(ht, 100.-rlimits(s_trans, 0., 100.), 100.-rlimits(e_trans, 0., 100.), period, E_UnitFadeFunc)
set ht = null
endfunction
// *** Registe any player arrow key event ***
function E_TriggerRegisterAnyPlayerKeyEvent takes trigger trig returns nothing
local integer j
//! runtextmacro FOR("j", "0", "bj_MAX_PLAYER_SLOTS")
call TriggerRegisterPlayerEvent(trig, DD_p[j], EVENT_PLAYER_ARROW_UP_UP)
call TriggerRegisterPlayerEvent(trig, DD_p[j], EVENT_PLAYER_ARROW_DOWN_UP)
call TriggerRegisterPlayerEvent(trig, DD_p[j], EVENT_PLAYER_ARROW_RIGHT_UP)
call TriggerRegisterPlayerEvent(trig, DD_p[j], EVENT_PLAYER_ARROW_LEFT_UP)
call TriggerRegisterPlayerEvent(trig, DD_p[j], EVENT_PLAYER_ARROW_UP_DOWN)
call TriggerRegisterPlayerEvent(trig, DD_p[j], EVENT_PLAYER_ARROW_DOWN_DOWN)
call TriggerRegisterPlayerEvent(trig, DD_p[j], EVENT_PLAYER_ARROW_RIGHT_DOWN)
call TriggerRegisterPlayerEvent(trig, DD_p[j], EVENT_PLAYER_ARROW_LEFT_DOWN)
//! runtextmacro ENDFOR()
endfunction
// *** Get triggerd key type ***
constant function E_GetTriggerdKeyType takes nothing returns integer
local eventid id = GetTriggerEventId()
if (not (id == EVENT_PLAYER_ARROW_UP_DOWN or id == EVENT_PLAYER_ARROW_RIGHT_DOWN or id == EVENT_PLAYER_ARROW_LEFT_DOWN or id == EVENT_PLAYER_ARROW_DOWN_DOWN)) then
return 1
endif
return 0
endfunction
// *** Get triggerd key ***
constant function E_GetTriggerdKey takes nothing returns integer
local eventid id = GetTriggerEventId()
if (id == EVENT_PLAYER_ARROW_UP_UP or id == EVENT_PLAYER_ARROW_UP_DOWN) then
return bj_KEYEVENTKEY_UP
elseif (id == EVENT_PLAYER_ARROW_DOWN_UP or id == EVENT_PLAYER_ARROW_DOWN_DOWN) then
return bj_KEYEVENTKEY_DOWN
elseif (id == EVENT_PLAYER_ARROW_LEFT_UP or id == EVENT_PLAYER_ARROW_LEFT_DOWN) then
return bj_KEYEVENTKEY_LEFT
elseif (id == EVENT_PLAYER_ARROW_RIGHT_UP or id == EVENT_PLAYER_ARROW_RIGHT_DOWN) then
return bj_KEYEVENTKEY_RIGHT
endif
return -1
endfunction
// *** Move location to widget ***
function E_MoveLocationToWidget takes location loc, widget w returns nothing
call MoveLocation(loc, GetWidgetX(w), GetWidgetY(w))
endfunction
// ==============================
// *** Widget drop item ***
// ==============================
private struct items
// *** Members ***
public integer array ic[8]
public boolean array inv[8]
public integer i
public trigger t
public static items array ptr[8191]
// *** Actions ***
private static method Actions takes nothing returns boolean
// *** Load locals ***
local widget w = GetTriggerWidget()
local real x = GetWidgetX(w)
local real y = GetWidgetY(w)
local items it = items.ptr[ArrayId(w)]
// *** Load items ***
loop
set it.i = it.i - 1
call SetItemInvulnerable(CreateItem(it.ic[it.i], x, y), it.inv[it.i])
exitwhen (it.i == 0)
endloop
// *** Clear memory ***
call DestroyTrigger(it.t)
set it.t = null
call it.destroy()
set w = null
return true
endmethod
// *** Constructor ***
static method create takes nothing returns items
local items this = items.allocate()
set this.t = CreateTrigger()
call TriggerAddConditionEx(this.t, function items.Actions)
return this
endmethod
endstruct
function E_WidgetDropItem takes widget w, integer ic, boolean inv returns nothing
local items it = items.ptr[ArrayId(w)]
// *** Load? ***
if (GetWidgetLife(w) > 0.405 and it.i == 0) then
set it = items.create()
set items.ptr[ArrayId(w)] = it
call TriggerRegisterDeathEvent(it.t, w)
endif
// *** Store here ***
set it.ic[it.i] = ic
set it.inv[it.i] = inv
set it.i = it.i + 1
endfunction
// =========================
// *** Unit revive ***
// =========================
// *** Revive globals ***
globals
private player Pl = null
private real Ud = 0.
private real Hd = 0.
endglobals
// *** Main struct ***
private struct creep
// *** Creep members ***
unit u
integer id
player owner
dd_vector v
real aq
boolean hero
real delay
// *** Static members ***
static trigger Trig = null
// *** Add ptr module ***
implement optional dd_pointer
// *** Constructor ***
//! runtextmacro constr("create", "unit u, real delay")
set this.u = u
set this.id = GetUnitTypeId(u)
set this.owner = GetOwningPlayer(u)
set this.v = dd_vector.create(GetUnitX(u), GetUnitY(u), 0., GetUnitFacing(u)*DEGTORAD, 0.)
set this.aq = GetUnitAcquireRange(u)
set this.hero = IsUnitType(u, UNIT_TYPE_HERO)
set this.delay = delay
call this.SetPtrObj(u)
//! runtextmacro endconstr()
// *** Main revive method ***
public method Revive takes nothing returns nothing
if (this.hero) then
call ReviveHero(this.u, this.v.x, this.v.y, false)
call SetUnitFacing(this.u, this.v.alpha*RADTODEG)
call SetUnitAcquireRange(this.u, this.aq)
else
call this.ClrPtrObj(this.u)
call RemoveUnit(this.u)
set this.u = CreateUnit(this.owner, this.id, this.v.x, this.v.y, this.v.alpha*RADTODEG)
call SetUnitAcquireRange(this.u, this.aq)
call this.SetPtrObj(this.u)
endif
endmethod
endstruct
// *** Main revive function action ***
private function ReviveCreepDelayed takes nothing returns nothing
// *** Get creep ***
local creep c = creep.GetPtrObj(GetTriggerUnit())
//! runtextmacro IFX("c == null", "return")
call Sleep(c.delay)
call c.Revive()
endfunction
// *** Seek for units ***
private function RegisterEnum takes nothing returns boolean
local unit f = GetFilterUnit()
if (GetOwningPlayer(f) == Pl) then
if (IsUnitType(f, UNIT_TYPE_HERO)) then
//! runtextmacro IFX("Hd != -1", "call creep.create(f, Hd)")
else
//! runtextmacro IFX("Ud != -1", "call creep.create(f, Ud)")
endif
endif
set f = null
return false
endfunction
// *** Enables unit revive for a player (duration of -1 means no revive) ***
function E_EnableUnitReviveForPlayer takes player p, real unitDelay, real heroDelay returns nothing
set Pl = p
set Ud = unitDelay
set Hd = heroDelay
call groupex.FilterRect(bj_mapInitialPlayableArea, function RegisterEnum)
if (creep.Trig == null) then
set creep.Trig = CreateTrigger()
call TriggerAddAction(creep.Trig, function ReviveCreepDelayed)
endif
call TriggerRegisterPlayerUnitEvent(creep.Trig, p, EVENT_PLAYER_UNIT_DEATH, GetDummyFilter())
endfunction
endlibrary
// -----------------------------------------------------------------------------
// *** Require Me ***
// -----------------------------------------------------------------------------
library_once DDUniversalPack requires EGUI, NativeDDUniversalPack, DDxEGUIxItems, DDLightning, DDMissileEngine
function DD_DestroyTrigger_hooked takes nothing returns nothing
call TriggerUnregisterAnyMissileEvent(get_dd_trig())
call E_TriggerUnregisterAnyItemFused(get_dd_trig())
call onUnitCollideUnregister(get_dd_trig())
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
// *** Units ***
//! external ObjectMerger w3u ushd wmis unam "Water Missile" unsf " (DD Universal Pack)" uabi _ ucbs 0. ucpt 0. udtm 0.1 uico "ReplaceableTextures\CommandButtons\BTNWaterExhausting.BLP" umxp 0. umxr -90. umdl "Abilities\Weapons\WaterElementalMissile\WaterElementalMissile.mdx" ussc 0.1 ushu _ umvh 0. umvf -1000. umvs 1 umas 1 umis 1 umvt _ ucol 0. ufoo 0 uhom 1 usid 1 usin 1
//! external ObjectMerger w3u ushd drks unam "Sword" unsf " (DD Universal Pack)" uabi _ ucbs 0. ucpt 0. udtm 0.1 uico "ReplaceableTextures\CommandButtons\BTNDarkTorture.blp" umxp 0. umxr -90. umdl "war3mapImported\Frostmourne.mdx" ushu _ umvh 0. umvf -1000. umvs 1 umas 1 umis 1 umvt _ ucol 0. ufoo 0 uhom 1 usid 1 usin 1
//! external ObjectMerger w3u hpea ssib unam "Blademaster Illusion" unsf " (DD Universal Pack)" uabi _ udtm 0.1 uico "ReplaceableTextures\CommandButtons\BTNHeroBlademaster.blp" umdl "units\orc\HeroBladeMaster\HeroBladeMaster.mdx" ussc 0.1 ushu _ uclr 125 uclg 125 uclb 240 uaen _ udea 0 umvs 1 umas 1 umis 1 ucol 0. uhom 1 urac "undead" utyp _
// *** Imports ***
// external FileImporter .\ddup\Imports
// *** Abilities ***
//! external ObjectMerger m .\ddup\Objects\war3map.w3a
// *** Buffs ***
//! external ObjectMerger m .\ddup\Objects\war3map.w3h
// *** Exporter ***
// external FileExporter C:\temp_mem (by_externals)
//TESH.scrollpos=102
//TESH.alwaysfold=0
/*
**********************************************************************************************************
Installation:
1) Make sure that you use Jass NewGen Pack editor and that you installed DD Library in 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' sound from this map to yours
5) Copy this trigger to your map and modify it to your liking below
**********************************************************************************************************
*/
//! 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";
// * Secondary lightning bolts created during initial spell cast
// Default: "Doodads\\Cinematic\\Lightningbolt\\Lightningbolt.mdl"
constant string EFFECT_LIGHTING_BOLT = "Doodads\\Cinematic\\Lightningbolt\\Lightningbolt.mdx";
// Default: 15
constant integer NUMBER_OF_LIGHTINING_BOLTS = 15;
// Default: 0.12 seconds
constant real LIGHTNING_BOLT_STRIKE_INTERVAL = 0.12;
// * 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';
// ==============================================================================================================================================
// * 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) &&
!DDIsUnitInvulnerable(f);
}
// *******************************************************************************
// * Spell other variables (non-modifiable)
// *******************************************************************************
effect LastLigBolt = null;
real ELECTROLIX_ZAP_AOE[];
real ELECTROLIX_ZAP_DAMAGE[];
real ELECTROLIX_ZAP_MAX_DAMAGE[];
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;
}
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;
ezdata ezd;
if (GetSpellAbilityId() != ABILITY_ELECTROLIX_ZAP)
return false;
// 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 < 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
DestroyEffect(AddSpecialEffect(EFFECT_GREAT_LIGHTNING, x, y));
// * Load extra lightning effects
if (NUMBER_OF_LIGHTINING_BOLTS > 00) {
pr = p_real.create();
pr[00] = ELECTROLIX_ZAP_AOE[lvl]*ELECTROLIX_ZAP_AOE[lvl];
pr[01] = x; pr[02] = y;
DDStartTim(LIGHTNING_BOLT_STRIKE_INTERVAL, true, pr, function() {
p_real pr = DDTimData();
real r = SquareRoot( GetRandomReal(0., pr[00]) );
real rad = GetRandomReal(0., bj_PI*2.);
DestroyEffect(LastLigBolt);
if (DDTimTick() == NUMBER_OF_LIGHTINING_BOLTS) {
LastLigBolt = null;
pr.destroy();
DDQuitTim();
return;
}
LastLigBolt = AddSpecialEffect(EFFECT_LIGHTING_BOLT, pr[01] + r * Cos(rad), pr[02] + r * Sin(rad));
});
}
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;
SetUnitFlyHeight(d, DUMMY_LIGHTNING_HEIGHT, 0.);
GroupAddUnit(ezd.dg, d);
IssueTargetOrder(d, "chainlightning", u);
DestroyEffect(AddSpecialEffectTarget(EFFECT_DUMMY_ELECTRIC_SHOCK, u, "origin"));
// * if unit dies, then dummy cant cast spell on that unit
dmg = RawDamage2Damage(u, ezd.dmg);
if (dmg < GetWidgetLife(u))
UnitDamageTarget(ezd.u, u, RawDamage2Damage(u, ezd.dmg), true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_UNKNOWN, null);
else {
dd = dmgdelay.create();
dd.u = ezd.u; dd.f = u;
dd.dmg = dmg;
DDStartTim(.2, false, dd, function() {
dmgdelay dd = DDTimData();
UnitDamageTarget(dd.u, dd.f, dd.dmg*2., true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_UNKNOWN, null);
dd.destroy();
DDQuitTim();
});
}
d = null;
u = null;
});
DDStartTim(.3, true, ezd, function() {
ezdata.Temp = DDTimData();
if (DDTimTick() == 00) {
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;
}
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
//TESH.scrollpos=708
//TESH.alwaysfold=0
//============================================================================
// Dark Dragon Library Code v1.1
//
// 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_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;
}
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// * 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 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;
}
}
// -----------------------------------------------------------------------
// -> ***** private globals *****
// -----------------------------------------------------------------------
private {
location TempLoc = Location(0., 0.);
timer TimerStack[];
integer TimN = 0;
group GroupStack[];
integer GrpN = 0;
unit DummyStack[];
integer DumN = 0;
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 DDTerrZ(real x, real y) -> real {
MoveLocation(TempLoc, x, y);
return GetLocationZ(TempLoc);
}
function DDWidgetTerrZ(widget w) -> real {
return DDTerrZ(GetWidgetX(w), GetWidgetY(w));
}
// =================================================================
// *** 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 ****
// =================================================================
// -> 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 {
TimerStack[TimN] = t;
TimN += 1;
}
}
// ** Get data stored on expired timer
function DDTimData() -> integer {
return LoadInteger(DDHT, GetHandleId(GetExpiredTimer()), 0);
}
// ** start timer with data storage
function DDStartTim(real secs, boolean looping, integer pdata, code func) -> timer {
timer t = DDLoadTim();
SaveInteger(DDHT, GetHandleId(t), -1, 0); // ticks
SaveInteger(DDHT, GetHandleId(t), 0, pdata); // data
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 t = DDLoadTim(), t2 = DDLoadTim(), t3 = DDLoadTim();
SaveInteger(DDHT, GetHandleId(t), -1, 0); // ticks
SaveInteger(DDHT, GetHandleId(t), 0, pdata); // data
TimerStart(t, 0., false, func);
t2 = DDLoadTim();
SaveInteger(DDHT, GetHandleId(t2), -1, 1); // ticks
SaveInteger(DDHT, GetHandleId(t2), 0, pdata); // data
TimerStart(t2, secs, looping, func);
t3 = DDLoadTim();
SaveTimerHandle(DDHT, GetHandleId(t3), 1, t);
SaveTimerHandle(DDHT, GetHandleId(t3), 2, t2);
TimerStart(t3, .001, false, function() {
timer t = GetExpiredTimer();
timer t2 = LoadTimerHandle(DDHT, GetHandleId(t), 1);
timer t3 = LoadTimerHandle(DDHT, GetHandleId(t), 2);
// *** was t2 destroyed?
if (LoadInteger(DDHT, GetHandleId(t2), -1) == -1) {
// Stop timer 2
SaveInteger(DDHT, GetHandleId(t3), -1, -1); // ticks
PauseTimer(t3);
DDRecycleTim(t3);
} else {
// was not destoyed, then clean first one
SaveInteger(DDHT, GetHandleId(t2), -1, -1); // ticks
PauseTimer(t2);
DDRecycleTim(t2);
}
PauseTimer(t);
DDRecycleTim(t);
});
return t;
}
// *** Gets current tick and adds next one ***
function DDTimTick() -> integer {
timer t = GetExpiredTimer();
integer tick = LoadInteger(DDHT, GetHandleId(t), -1);
SaveInteger(DDHT, GetHandleId(t), -1, tick + 1); // next tick
return tick;
}
// *** Custom timer tick
function DDCTimTick(timer t) -> integer {
return LoadInteger(DDHT, GetHandleId(t), -1);
}
// *** Quit expired timer ***
function DDQuitTim() {
SaveInteger(DDHT, GetHandleId(GetExpiredTimer()), -1, -1); // ticks
PauseTimer(GetExpiredTimer());
DDRecycleTim(GetExpiredTimer());
}
// =================================================================
// *** 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
function DDGroupFilterArea(real x, real y, real radius, code func) {
group g = DDLoadGroup();
GroupEnumUnitsInRange(g, x, y, radius, Filter(func));
DDRecycleGroup(g);
}
// --------------------------------------------------------
// -- Quick filter rect
function DDGroupFilterRect(rect r, code func) {
group g = DDLoadGroup();
GroupEnumUnitsInRect(g, r, Filter(func));
DDRecycleGroup(g);
}
// --------------------------------------------------------
// *** 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); }
// -------------------------------------------------------------------------------------
// *** Chills target unit
private struct chill {
unit u, dmy;
real dur;
static key CHILL_KEY;
}
function DDUnitChill(player p, unit u, real dur) -> boolean {
chill c = DDGet(u, chill.CHILL_KEY);
unit d;
real rad;
boolean b;
if (c == p_null) {
c = chill.create();
c.u = u; c.dur = dur;
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));
b = IssueTargetOrder(d, "frostnova", u);
DDRemoveAbilityTimed(d, DD_CHILL, 2.);
DDRecycleDummyTimed(d, 2.);
if (b)
DDStartTim(.1, true, c, function() {
chill c = DDTimData();
c.dur -= .1;
if (c.dur <= 0.) {
UnitRemoveAbility(c.u, DD_CHILL_BUFF);
DDSet(c.u, chill.CHILL_KEY, p_null);
c.u = null;
c.destroy();
DDQuitTim();
} else if (GetUnitAbilityLevel(c.u, DD_CHILL_BUFF) == 00) {
IssueTargetOrder(c.dmy, "frostnova", c.u);
}
});
return b;
}
if (c.dur < dur)
c.dur = dur;
return true;
}
// 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 xeffect {
private {
unit u;
effect e;
real fac; // facing angle in radians
real pitch; // pitch in radians
real decay;
static constant real EFFECT_DECAY = 5.;
}
// =========================================================================================
// =========================================================================================
static method create(string mdl, real x, real y, real fac, real scale) -> xeffect {
xeffect this = allocate();
u = DDLoadSpellDummy(Player(0xF), x, y, 0, 0);
//UnitAddAbility(u, 'Amrf');
//UnitRemoveAbility(u, 'Amrf');
e = AddSpecialEffectTarget(mdl, u, "origin");
//SetUnitX(u, x); SetUnitY(u, y);
SetUnitLookAt(u, "Bone_Head", u, Cos(fac*bj_DEGTORAD)*1000., Sin(fac*bj_DEGTORAD)*1000., 0.);
SetUnitFlyHeight(u, 0., 0.);
SetUnitScale(u, scale, scale, scale);
SetUnitAnimationByIndex(u, 90);
PauseUnit(u, true);
this.pitch = bj_PI/2.;
this.decay = EFFECT_DECAY;
this.fac = fac;
return this;
}
// -----------------
method destroy() {
DestroyEffect(e);
e = null;
DDStartTim(decay, false, this, function() {
xeffect this = DDTimData();
SetUnitX(u, DDMaxX);
SetUnitY(u, DDMaxY);
DDRecycleDummyTimed(u, EFFECT_DECAY-decay);
DDQuitTim();
deallocate();
/*
PauseTimer(GetExpiredTimer());
TimerStart(GetExpiredTimer(), EFFECT_DECAY-decay, false, function() {
xeffect this = DDTimData();
DDRecycleDummy(u);
DDQuitTim();
deallocate();
});
*/
});
}
// *** destroys effect timed
method destroyx(real delay) {
DDStartTim(delay, false, this, function() {
xeffect(DDTimData()).destroy();
DDQuitTim();
});
}
// decay says that after x seconds dummy will disappera from target location,
// this is usefull if effects do not disappear as quickly as you want
method Decay(real secs) {
if (secs > EFFECT_DECAY) decay = EFFECT_DECAY;
else if (secs < 0.) decay = 0.;
else decay = secs;
}
// =========================================================================================
// =========================================================================================
method operator X=(real x) { SetUnitX(u, x); }
method operator Y=(real y) { SetUnitY(u, y); }
method operator Z=(real z) { SetUnitFlyHeight(u, z, 0.); }
method operator X() -> real { return GetUnitX(u); }
method operator Y() -> real { return GetUnitY(u); }
method operator Z() -> real { return GetUnitFlyHeight(u); }
method operator WZ() -> real { return DDWidgetTerrZ(u); }
method operator Facing=(real fac) { SetUnitLookAt(u, "Bone_Head", u, Cos(fac*bj_DEGTORAD)*1000., Sin(fac*bj_DEGTORAD)*1000., 0.); this.fac = fac; }
method operator Facing() -> real { return fac; }
method Face(widget w) { Facing = Atan2(GetWidgetY(w)-Y, GetWidgetX(w)-X)*bj_RADTODEG; }
}
}
// ----------------------------------------------------------------------------
// *** 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, 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 );
}
// --------------------------------------------------------------------------------------
public function DDGenericSound(string file, real vol, real x, real y, real mxDist, real pitch) {
sound s;
real d;
integer i;
// 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) {
s = CreateSound(file, false, false, false, 10, 10, "");
SetSoundVolume(s, R2I(vol-d*(vol/mxDist)));
StartSound(s);
KillSoundWhenDone(s);
s = null;
}
}
}
}
// --------------------------------------------------------------------------------------
public function DisplayErrorMsgPlayer(player p, real dur, string msg) {
if (GetLocalPlayer() == p) {
StartSound(ErrorSound);
DisplayTimedTextFromPlayer(p, 0., 0., dur, "|cffffcc00"+ msg +"|r");
}
}
// ====================================================================
function onInit() {
InitDamageDetection();
InitDestTreeCheck();
InitEnumDests();
DDMinX = GetRectMinX(bj_mapInitialPlayableArea);
DDMinY = GetRectMinY(bj_mapInitialPlayableArea);
DDMaxX = GetRectMaxX(bj_mapInitialPlayableArea);
DDMaxY = GetRectMaxY(bj_mapInitialPlayableArea);
ErrorSound = CreateSound( "Sound\\Interface\\Error.wav", false, false, false, 10, 10, "" );
SetSoundParamsFromLabel( ErrorSound, "InterfaceError" );
SetSoundDuration( ErrorSound, 614 );
SetSoundVolume(ErrorSound, 127);
}
}
//! 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) {
integer sh = StringHash(file),
snd_n = LoadInteger(GenSndTable, sh, 04);
sound s = LoadSoundHandle(GenSndTable, sh, snd_n);
real d;
integer i;
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);
snd_n += 01;
if (snd_n == 04)
snd_n = 00;
SaveInteger(GenSndTable, sh, 04, snd_n);
// 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( DDHypot(GetCameraTargetPositionX()-x, GetCameraTargetPositionY()-y) );
if (d < mxDist) {
SetSoundVolume(s, R2I((vol-d*(vol/mxDist))*1.27));
StartSound(s);
}
}
}
}
public function DDGetGameElapsedTime() -> real {
return TimerGetElapsed(GameElapsedTimer);
}
public function DDGetRndReal(real min, real max) -> real {
real rnd = ((max-min)/1000000.)*I2R(RndInt[RndIntReadN]);
debug if (max > 1000000.)
DDMsg("ERROR: \"DDGetRndNumber\" - 'max' variable is greater than 1000000!");
RndIntReadN += 01; if (RndIntReadN == RND_INT_MAX_ARRAY_N) RndIntReadN = 00;
return min + rnd;
}
public function DDGetRndInt(integer min, integer max) -> integer {
return R2I( DDGetRndReal(I2R(min), I2R(max)) );
}
// ====================================================================
function onInit() {
InitDamageDetection();
InitDestTreeCheck();
InitEnumDests();
DDMinX = GetRectMinX(bj_mapInitialPlayableArea);
DDMinY = GetRectMinY(bj_mapInitialPlayableArea);
DDMaxX = GetRectMaxX(bj_mapInitialPlayableArea);
DDMaxY = GetRectMaxY(bj_mapInitialPlayableArea);
GenSndTable = InitHashtable();
ErrorSound = CreateSound( "Sound\\Interface\\Error.wav", false, false, false, 10, 10, "" );
SetSoundParamsFromLabel( ErrorSound, "InterfaceError" );
SetSoundDuration( ErrorSound, 614 );
SetSoundVolume(ErrorSound, 127);
GameElapsedTimer = CreateTimer();
TimerStart(GameElapsedTimer, 10800., false, null);
for(RndIntWriteN=00; RndIntWriteN < RND_INT_MAX_ARRAY_N; RndIntWriteN+=01)
RndInt[RndIntWriteN] = GetRandomInt(00, 1000000);
RndIntWriteN = 00;
RndGenForce = CreateForce();
TrigMouseEvent = CreateTrigger();
ForForce(bj_FORCE_ALL_PLAYERS, function() {
if (GetPlayerController(GetEnumPlayer()) == MAP_CONTROL_USER)
TriggerRegisterPlayerEvent(TrigMouseEvent, GetEnumPlayer(), EVENT_PLAYER_MOUSE_MOVE);
});
TriggerAddCondition(TrigMouseEvent, Condition(function() -> boolean {
real mouseN;
boolean xFirst = GetRandomInt(00, 01) == 01;
if (!IsPlayerInForce(GetTriggerPlayer(), RndGenForce)) {
// example: input x = 578.4571496
// output rnd n = 4571498
if (xFirst)
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
else
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
if (mouseN == 0.)
return false;
//mouseN *= 100.;
RndInt[RndIntWriteN] = R2I((mouseN - I2R(R2I(mouseN))) * 1000.);
//DDMsg(I2S(RndInt[RndIntWriteN]));
//RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
if (xFirst)
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
else
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
RndInt[RndIntWriteN] += R2I((mouseN - I2R(R2I(mouseN))) * 1000.)*1000;
//DDMsg(I2S(RndInt[RndIntWriteN]));
RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
ForceAddPlayer(RndGenForce, GetTriggerPlayer());
}
if (DDGetGameElapsedTime()-RndElapsedTime > .125) {
ForceClear(RndGenForce);
RndElapsedTime = DDGetGameElapsedTime();
}
return false;
}));
}
}
//! endzinc
//TESH.scrollpos=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
//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;
}));
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
}
//! endzinc
//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
//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
//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
//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
//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=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 SummonSkeletonsChanneling requires DDLib
{
// *****************************************************************************************
// === Main Data ===
// *****************************************************************************************
// This is the raw code of the spell 'Summon Skeletons'
// Default: 'A000'
constant integer ABILITY_SUMMON_SKELETONS = 'A001';
//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_CASTER_EFFECT_2 = "Abilities\\Spells\\Undead\\Darksummoning\\DarkSummonMissile.mdl";
constant real SUMMON_SKELETON_CASTER_EFFECT_2_SIZE = 1.2;
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";
constant string SUMMON_SKELETONS_LIGHTNING_TYPE = "MBUR"; //"CHIM"; // Use "" (empty string) to disable lightning creation
constant string SUMMON_SKELETONS_LIGHTNING_SOUND_FILE = "Abilities\\Spells\\NightElf\\ManaBurn\\ManaDrainTarget1.wav";
constant real SUMMON_SKELETONS_LIGHTNING_VOLUME = 100.;
constant real SUMMON_SKELETONS_LIGHTNING_PITCH = 1.;
constant real SUMMON_SKELETONS_LIGHTNING_DURATION = 1.;
constant real SUMMON_SKELETONS_LIGHTNING_COLOR_RED = 100.; // color intensity in percentage
constant real SUMMON_SKELETONS_LIGHTNING_COLOR_GREEN = 100.;
constant real SUMMON_SKELETONS_LIGHTNING_COLOR_BLUE = 100.;
constant real SUMMON_SKELETONS_LIGHTNING_CASTER_OFFSET_X = 0.; // (positive numbers) right / left (negative numbers)
constant real SUMMON_SKELETONS_LIGHTNING_CASTER_OFFSET_Y = 0.; // (positive numbers) front / back (negative numbers)
constant real SUMMON_SKELETONS_LIGHTNING_CASTER_OFFSET_Z = 50.; // (positive numbers) up / down (negative numbers)
// *****************************************************************************************
// === 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[];
real SKELETONS_INTERVAL[];
integer SKELETONS_TYPE_ID[];
//real SKELETONS_CHANNEL_DURATION[];
real SKELETONS_LIFESPAN[];
real SKELETONS_MAX_RISE_DISTANCE[];
real SUMMON_SKELETONS_RISE_GRAVE_SIZE[];
real SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_X[];
real SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Y[];
real SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Z[];
// --------------------------------------------------------------
// This part of code is used to define spell constants per level
function SummonSkeletons_Setup() {
// Default: Level 1 = 'ndr1'
// Default: Level 2 = 'ndr1'
// Default: Level 3 = 'ndr3'
// Default: Level 4 = 'ndr3'
SKELETONS_TYPE_ID[01] = 'ndr1';
SKELETONS_TYPE_ID[02] = 'ndr1';
SKELETONS_TYPE_ID[03] = 'ndr3';
SKELETONS_TYPE_ID[04] = 'ndr3';
// Default: Level 1 = 50.
// Default: Level 2 = 50.
// Default: Level 3 = 50.
// Default: Level 4 = 50.
SKELETONS_LIFESPAN[01] = 50.;
SKELETONS_LIFESPAN[02] = 50.;
SKELETONS_LIFESPAN[03] = 50.;
SKELETONS_LIFESPAN[04] = 50.;
// Default: Level 1 = 230 RGB
// Default: Level 2 = 230 RGB
// Default: Level 3 = 150 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] = 230;
SKELETONS_TYPE_COLOR_GREEN [02] = 230;
SKELETONS_TYPE_COLOR_BLUE [02] = 230;
SKELETONS_TYPE_COLOR_RED [03] = 150;
SKELETONS_TYPE_COLOR_GREEN [03] = 150;
SKELETONS_TYPE_COLOR_BLUE [03] = 150;
SKELETONS_TYPE_COLOR_RED [04] = 150;
SKELETONS_TYPE_COLOR_GREEN [04] = 150;
SKELETONS_TYPE_COLOR_BLUE [04] = 150;
// Default: Level 1 = 1.5
// Default: Level 2 = 0.5
// Default: Level 3 = 1.5
// Default: Level 4 = 0.5
SKELETONS_INTERVAL[01] = 1.5;
SKELETONS_INTERVAL[02] = .5;
SKELETONS_INTERVAL[03] = 1.5;
SKELETONS_INTERVAL[04] = .5;
// * Max skeleton rise distance from caster
// Default: Level 1 = 400.
// Default: Level 2 = 400.
// Default: Level 3 = 400.
// Default: Level 4 = 400.
SKELETONS_MAX_RISE_DISTANCE[01] = 400.;
SKELETONS_MAX_RISE_DISTANCE[02] = 400.;
SKELETONS_MAX_RISE_DISTANCE[03] = 400.;
SKELETONS_MAX_RISE_DISTANCE[04] = 400.;
// Default: Level 1 = 0.9
// Default: Level 2 = 0.9
// Default: Level 3 = 1.3
// Default: Level 4 = 1.3
SUMMON_SKELETONS_RISE_GRAVE_SIZE[01] = 0.9;
SUMMON_SKELETONS_RISE_GRAVE_SIZE[02] = 0.9;
SUMMON_SKELETONS_RISE_GRAVE_SIZE[03] = 1.3;
SUMMON_SKELETONS_RISE_GRAVE_SIZE[04] = 1.3;
// -------------------------------------------------------------------------------------
// Values meaning are same as 'SUMMON_SKELETONS_LIGHTNING_CASTER_OFFSET_XYZ' variable constants
SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_X[01] = 0.; SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Y[01] = 0.; SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Z[01] = 25.;
SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_X[02] = 0.; SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Y[02] = 0.; SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Z[02] = 25.;
SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_X[03] = 0.; SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Y[03] = 0.; SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Z[03] = 35.;
SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_X[04] = 0.; SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Y[04] = 0.; SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Z[04] = 35.;
// -------------------------------------------------------------------------------------
}
// *****************************************************************************************
// === 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, tz;
real x, y;
integer lvl, ns;
integer nskels;
boolean channeling;
unit sk[33];
p_real vec;
timer t;
effect eff;
static boolean DestsFlag = false;
static data AttachVar[];
static lightning Ligs[];
}
/*
struct blightdata {
integer tt, tv;
real x, y;
}*/
function onInit() {
trigger t = CreateTrigger();
SummonSkeletons_Setup();
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST);
TriggerAddCondition(t, Condition(function() -> boolean {
data d;
integer i;
unit u;
if (GetSpellAbilityId() != ABILITY_SUMMON_SKELETONS)
return false;
if (GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_ENDCAST) {
d = data.AttachVar[H2ID( GetTriggerUnit() )];
while(d.nskels > 00) {
d.nskels -= 01;
d.sk[d.nskels] = null;
}
DestroyEffect(d.eff); d.eff = null;
d.vec.destroy();
DDQuitTimEx(d.t); d.t = null;
d.destroy();
return false;
}
d = data.create();
u = GetTriggerUnit();
d.p = GetOwningPlayer(u);
d.fac = GetUnitFacing(u);
d.x = GetWidgetX(u); d.y = GetWidgetY(u);
d.tz = DDTerrZ(d.x, d.y);
d.lvl = GetUnitAbilityLevel(u, ABILITY_SUMMON_SKELETONS);
d.vec = p_real.create();
d.nskels = 00;
d.channeling = true;
data.AttachVar[H2ID(u)] = d;
DestroyEffect(AddSpecialEffectTarget(SUMMON_SKELETON_CASTER_EFFECT, u, "origin"));
d.eff = AddSpecialEffect(SUMMON_SKELETON_CASTER_EFFECT_2, d.x, d.y);
BlzSetSpecialEffectScale(d.eff, SUMMON_SKELETON_CASTER_EFFECT_2_SIZE);
BlzPlaySpecialEffect(d.eff, ANIM_TYPE_STAND);
d.t = DDStartTimInst(SKELETONS_INTERVAL[d.lvl], true, d, function() {
data d = DDTimData();
real dist, cdist, rad;
boolean skeleton_pathing_invalid;
real x, y, tz;
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.channeling) {
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);
tz = DDTerrZ(x, y);
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, tz + 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;
// ---------------------------------------------------------------------------------------------------------------
vx = Cos(d.fac*bj_DEGTORAD);
vy = Sin(d.fac*bj_DEGTORAD);
if (SUMMON_SKELETONS_LIGHTNING_TYPE != "") {
bj_lastCreatedLightning = AddLightningEx(SUMMON_SKELETONS_LIGHTNING_TYPE, false,
d.x + (SUMMON_SKELETONS_LIGHTNING_CASTER_OFFSET_X * vy) + (SUMMON_SKELETONS_LIGHTNING_CASTER_OFFSET_Y * vx),
d.y - (SUMMON_SKELETONS_LIGHTNING_CASTER_OFFSET_X * vx) + (SUMMON_SKELETONS_LIGHTNING_CASTER_OFFSET_Y * vy),
d.tz + SUMMON_SKELETONS_LIGHTNING_CASTER_OFFSET_Z,
x + (SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_X[d.lvl] * vy) + (SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Y[d.lvl] * vx),
y - (SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_X[d.lvl] * vx) + (SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Y[d.lvl] * vy),
tz + SUMMON_SKELETONS_LIGHTNING_SKELETON_OFFSET_Z[d.lvl]);
SetLightningColor(bj_lastCreatedLightning, SUMMON_SKELETONS_LIGHTNING_COLOR_RED*.01, SUMMON_SKELETONS_LIGHTNING_COLOR_GREEN*.01, SUMMON_SKELETONS_LIGHTNING_COLOR_BLUE*.01, 1.);
DDGenericSound(SUMMON_SKELETONS_LIGHTNING_SOUND_FILE, SUMMON_SKELETONS_LIGHTNING_VOLUME, x, y, 3500., SUMMON_SKELETONS_LIGHTNING_PITCH);
data.Ligs[GetHandleId( bj_lastCreatedLightning )] = bj_lastCreatedLightning;
DDStartTim(SUMMON_SKELETONS_LIGHTNING_DURATION, false, GetHandleId(bj_lastCreatedLightning), function() {
integer id = DDTimData();
DestroyLightning(data.Ligs[id]); data.Ligs[id] = null;
DDQuitTim();
});
}
// ---------------------------------------------------------------------------------------------------------------
/*
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.channeling) {
while(d.nskels > 00) {
d.nskels -= 01;
d.sk[d.nskels] = null;
}
d.vec.destroy();
d.destroy();
DDQuitTim();
}*/
});
u = null;
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
//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