/*
Rain of Shards v1.1 MADE BY 'KhaosMachine'
IF YOU WANT TO IMPORT THIS SPELL INTO YOUR MAP YOU HAVE TO COPY THE NEXT THINGS:
*********************************************************************************
TRIGGER EDITOR:
FOLDERS: NONE
TRIGGERS: Rain of Shards
OBJECT EDITOR:
UNITS: Dummy ('u000') (Set the model of this unit to the dummy imported model)
ABILITIES: Rain Of Shards ('A000')
Rain Of Shards ('A001')
IMPORT MANAGER:
dummy.mdx | Model | 34 | war3mapImported\dummy.mdx
THEN YOU HAVE TO CONFIGURE THE SPELL:
OBVIOUSLY THINGS:
ABILITY_ID = RAWCODE OF THE SPELL
DUMMY_ID = RAWCODE OF THE DUMMY
********************************************************************************
UPDATES:
©Version 1.2:
-Some fixes
-Now the spell cast changing the angle
-New functions added for setting the angles
©Version 1.1:
-Added a semi-parabolas system for the arrow
-Changed the models of the arrow and the explosion
-Now the arrow damages all touched units in his way
©Version 1.0:
-First release
********************************************************************************
*/
scope rainOfShards initializer init
/*************************************************************
|*** ***|
|** **|
|* *|
|====> ¡¡¡START OF THE CONFIGURATION!!! <====|
|* *|
|** **|
|*** ***|
|************************************************************/
globals
private constant integer ABILITY_ID = 'A000'//ABILITY RAWCODE
private constant integer SLOW_ABILITY_ID = 'A001'//SLOW ABILITY RAWCODE
private constant string SLOW_ABILITY_ORDER = "slow"//SLOW ABILITY ORDER STRING
private constant integer DUMMY_ID = 'u000'//DUMMY RAWCODE
private constant string MISSILE_MODEL = "Abilities\\Weapons\\LichMissile\\LichMissile.mdl"//MISSILE MODEL STRING
private constant string DEAL_MODEL = "Abilities\\Spells\\Undead\\FreezingBreath\\FreezingBreathMissile.mdl"//DAMAGE POINT MODEL STRING
private constant real MISSILE_SCALE = 1.3//MISSILE SCALE
private constant real MISSILE_HEIGHT = 120//MISSILE HEIGHT
private constant real DM_SCALE = 1.4//DAMAGE POINT SPECIAL EFFECT SCALE
private constant integer ATTRIBUTE = 2//1 = STRENGHT 2 = AGILITY 3 = INTELLIGENCE
private constant boolean ADD_BONUSES = true//ADD BONUSES IF TRUE, OR NOT IF FALSE
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL//ATTACK TYPE
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL//DAMAGE TYPE
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS//WEAPON TYPE
private constant string ANIMATION_NAME = "attack"//ANIMATION OF THE UNIT WHEN MAKE THE ARROW
private constant real ANIMATION_SPEED = 4//ANIMATION SPEED OF THE CASTER
private constant boolean DAMAGE_WHEN_IMPACT = true//TRUE = DAMAGE WHEN THE MISSILE IMPACTS (DAMAGE ON MOVEMENT + THIS)
private real SPEED = 2200//SPEED OF THE MISSILE
private real FPS = 60//FRAMES PER SECOND
endglobals
private function MISSILES takes integer level returns integer
return 20//COUNT OF ARROWS
endfunction
private function INTERVAL_PER_MISSILE takes integer level returns real
return .08//TIME WAIT PER MISSILE
endfunction
private function ATTRIBUTE_MULTIPLIER takes integer level returns real
return .05 + .04 * level//ATTRIBUTE MULTIPLIER
endfunction
private function AREA_OF_EFFECT takes integer level returns real
return 75. + 25. * level//AREA OF EFFECT OF THE ABILITY
endfunction
private function EXPLOSION_AREA_OF_EFFECT takes integer level returns real
return 150. + 30. * level//AREA OF EFFECT WHEN THE MISSILE EXPLODES
endfunction
private function MIN_DISTANCE takes integer level returns real
return 200.
endfunction
private function MAX_ANGLE takes nothing returns real
return 15.
endfunction
private function ANGLE_FACTOR takes nothing returns real
return 10.
endfunction
/*************************************************************
|*** ***|
|** **|
|* *|
|====> ¡¡¡END OF THE CONFIGURATION!!! <====|
|* *|
|** **|
|*** ***|
|************************************************************/
private struct rOSRun1
unit caster
integer level
integer missiles
real interval
real x
real y
real angle
boolean changeAngle
endstruct
private struct rOSRun2
unit caster
integer level
unit missile
real angle
real maxDis
real currentDis
effect fx
group g
endstruct
globals
private constant timer TIMER = CreateTimer()
private constant timer TIMER2 = CreateTimer()
private rOSRun1 array ARRAY
private integer TOTAL = 0
private rOSRun2 array ARRAY2
private integer TOTAL2 = 0
private constant player PLAYER = Player(PLAYER_NEUTRAL_AGGRESSIVE)
private constant group UNIT_GROUP = CreateGroup()
endglobals
private function missileAction takes nothing returns nothing
local rOSRun2 D
local boolean DESTROY_DATA
local unit j
local unit d
local integer i = 0
local real DAMAGE
local real x
local real y
loop
exitwhen i >= TOTAL2
set D = ARRAY2[i]
set x = GetUnitX(D.missile)
set y = GetUnitY(D.missile)
call GroupEnumUnitsInRange(UNIT_GROUP, x, y, EXPLOSION_AREA_OF_EFFECT(D.level), null)
loop
set j = FirstOfGroup(UNIT_GROUP)
exitwhen j == null
call GroupRemoveUnit(UNIT_GROUP, j)
if not IsUnitType(j, UNIT_TYPE_DEAD) and not IsUnitType(j, UNIT_TYPE_STRUCTURE) and not IsUnitType(j, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(j, GetOwningPlayer(D.caster)) and not IsUnitInGroup(j, D.g) then
if ATTRIBUTE == 1 then
set DAMAGE = GetHeroStr(D.caster, ADD_BONUSES)
elseif ATTRIBUTE == 2 then
set DAMAGE = GetHeroAgi(D.caster, ADD_BONUSES)
else
set DAMAGE = GetHeroInt(D.caster, ADD_BONUSES)
endif
call UnitDamageTarget(D.caster, j, DAMAGE, true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
call GroupAddUnit(D.g, j)
set d = CreateUnit(GetOwningPlayer(D.caster), DUMMY_ID, GetUnitX(j), GetUnitY(j), 0)
call UnitAddAbility(d, SLOW_ABILITY_ID)
call SetUnitAbilityLevel(d, SLOW_ABILITY_ID, D.level)
call IssueTargetOrder(d, SLOW_ABILITY_ORDER, j)
call UnitApplyTimedLife(d, 'BTLF', .5)
endif
endloop
call SetUnitPosition(D.missile, x + SPEED * Cos(D.angle * bj_DEGTORAD), y + SPEED * Sin(D.angle * bj_DEGTORAD))
call SetUnitFlyHeight(D.missile, (((D.currentDis + D.maxDis) * (D.currentDis - D.maxDis)) * ((-1.00 * MISSILE_HEIGHT) / (D.maxDis * D.maxDis))), 0)
set DESTROY_DATA = D.currentDis >= D.maxDis
set D.currentDis = D.currentDis + SPEED
if DESTROY_DATA then
set j = CreateUnit(PLAYER, DUMMY_ID, x, y, 0)
call SetUnitScale(j, DM_SCALE, DM_SCALE, DM_SCALE)
call DestroyEffect(AddSpecialEffectTarget(DEAL_MODEL, j, "origin"))
call UnitApplyTimedLife(j, 'BTLF', 1)
call GroupEnumUnitsInRange(UNIT_GROUP, x, y, AREA_OF_EFFECT(D.level), null)
loop
set j = FirstOfGroup(UNIT_GROUP)
exitwhen j == null
call GroupRemoveUnit(UNIT_GROUP, j)
if DAMAGE_WHEN_IMPACT and not IsUnitType(j, UNIT_TYPE_DEAD) and not IsUnitType(j, UNIT_TYPE_STRUCTURE) and not IsUnitType(j, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(j, GetOwningPlayer(D.caster)) then
if ATTRIBUTE == 1 then
set DAMAGE = GetHeroStr(D.caster, ADD_BONUSES)
elseif ATTRIBUTE == 2 then
set DAMAGE = GetHeroAgi(D.caster, ADD_BONUSES)
else
set DAMAGE = GetHeroInt(D.caster, ADD_BONUSES)
endif
call UnitDamageTarget(D.caster, j, DAMAGE, true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
set d = CreateUnit(GetOwningPlayer(D.caster), DUMMY_ID, GetUnitX(j), GetUnitY(j), 0)
call UnitAddAbility(d, SLOW_ABILITY_ID)
call SetUnitAbilityLevel(d, SLOW_ABILITY_ID, D.level)
call IssueTargetOrder(d, SLOW_ABILITY_ORDER, j)
call UnitApplyTimedLife(d, 'BTLF', .5)
endif
endloop
call DestroyEffect(D.fx)
call UnitApplyTimedLife(D.missile, 'BTLF', 1)
set ARRAY2[i] = ARRAY2[TOTAL2 - 1]
set TOTAL2 = TOTAL2 - 1
call D.destroy()
endif
set i = i + 1
endloop
if TOTAL2 == 0 then
call PauseTimer(TIMER2)
endif
endfunction
private function action takes nothing returns nothing
local rOSRun1 D
local rOSRun2 A
local boolean DESTROY_DATA
local integer i = 0
local real x
local real y
local real dx
local real dy
local real angle
loop
exitwhen i >= TOTAL
set D = ARRAY[i]
if D.interval <= 0 then
set A = rOSRun2.create()
set x = GetUnitX(D.caster)
set y = GetUnitY(D.caster)
set dx = D.x - x
set dy = D.y - y
call SetUnitAnimation(D.caster, "stand")
call SetUnitAnimation(D.caster, ANIMATION_NAME)
set A.caster = D.caster
set A.level = D.level
set angle = Atan2(D.y - y, D.x - x) + bj_DEGTORAD * D.angle
set A.angle = angle * bj_RADTODEG
set A.missile = CreateUnit(PLAYER, DUMMY_ID, x, y, A.angle)
call SetUnitFlyHeight(A.missile, MISSILE_HEIGHT, 0)
set A.currentDis = 0
set A.maxDis = SquareRoot(dx * dx + dy * dy)
set A.g = CreateGroup()
call SetUnitScale(A.missile, MISSILE_SCALE, MISSILE_SCALE, MISSILE_SCALE)
set A.fx = AddSpecialEffectTarget(MISSILE_MODEL, A.missile, "origin")
call SetUnitFacing(D.caster, D.angle)
if D.changeAngle then
if D.angle < MAX_ANGLE() then
set D.angle = D.angle + ANGLE_FACTOR()
else
set D.changeAngle = FALSE
endif
else
if D.angle > - MAX_ANGLE() then
set D.angle = D.angle - ANGLE_FACTOR()
else
set D.changeAngle = TRUE
endif
endif
if TOTAL2 == 0 then
call TimerStart(TIMER2, FPS, true, function missileAction)
endif
set TOTAL2 = TOTAL2 + 1
set ARRAY2[TOTAL2 - 1] = A
set D.missiles = D.missiles - 1
set D.interval = INTERVAL_PER_MISSILE(D.level)
else
set D.interval = D.interval - FPS
endif
set DESTROY_DATA = IsUnitType(D.caster, UNIT_TYPE_DEAD) or D.missiles <= 0
if DESTROY_DATA then
call PauseUnit(D.caster, false)
call SetUnitTimeScale(D.caster, 1)
call IssueImmediateOrder(D.caster, "stop")
call SetUnitAnimation(D.caster, "stand")
set ARRAY[i] = ARRAY[TOTAL - 1]
set TOTAL = TOTAL - 1
call D.destroy()
endif
set i = i + 1
endloop
if TOTAL == 0 then
call PauseTimer(TIMER)
endif
endfunction
private function run takes nothing returns boolean
local rOSRun1 D
local unit caster
local real x
local real y
local real x1
local real y1
if GetSpellAbilityId() == ABILITY_ID then
set D = rOSRun1.create()
set caster = GetTriggerUnit()
set D.caster = caster
set D.level = GetUnitAbilityLevel(caster, ABILITY_ID)
set D.missiles = MISSILES(D.level)
set D.interval = 0
set x = GetUnitX(D.caster)
set y = GetUnitY(D.caster)
set D.x = GetSpellTargetX()
set D.y = GetSpellTargetY()
set D.changeAngle = FALSE
set D.angle = 0
if x == D.x and y == D.y then
set D.x = x + MIN_DISTANCE(D.level) * Cos(GetUnitFacing(D.caster) * bj_DEGTORAD)
set D.y = y + MIN_DISTANCE(D.level) * Sin(GetUnitFacing(D.caster) * bj_DEGTORAD)
endif
call PauseUnit(D.caster, true)
call SetUnitTimeScale(caster, ANIMATION_SPEED)
if TOTAL == 0 then
call TimerStart(TIMER, FPS, true, function action)
endif
set TOTAL = TOTAL + 1
set ARRAY[TOTAL - 1] = D
set caster = null
endif
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local unit d = CreateUnit(Player(0), DUMMY_ID, 0, 0, 0)
loop
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(t, Condition(function run))
call UnitAddAbility(d, ABILITY_ID)
call UnitAddAbility(d, SLOW_ABILITY_ID)
call Preload(MISSILE_MODEL)
call Preload(DEAL_MODEL)
call RemoveUnit(d)
set FPS = 1 / FPS
set SPEED = SPEED * FPS
set t = null
set d = null
endfunction
endscope