scope FrostFireBolt
globals
private constant real PERIOD = 0.03125
private constant string SPELL_SOUND = "Units\\Creeps\\LavaSpawn\\LavaSpawnMorphBirth.wav"
private constant string EFFECT_ONE = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl"
private constant string EFFECT_TWO = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
private constant string IMPACTEFFECT_ONE = "Abilities\\Spells\\Other\\Volcano\\VolcanoDeath.mdl"//"Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl"
private constant string IMPACTEFFECT_TWO = "Abilities\\Spells\\Undead\\FreezingBreath\\FreezingBreathTargetArt.mdl"//"Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
private constant real BOLT_SCALE_ONE = 0.70 // 70% scale of effect one
private constant real BOLT_SCALE_TWO = 0.70 // 70% scale of effect two
private constant real BOLT_SPEED = 900. // 800/sec
private constant real AMPLITUDE_VERTICAL = 40.
private constant real AMPLITUDE_HORISONTAL = 35.
private constant real BOLT_SPIN_SPEED = 500.
private constant real BOLT_OFFSET = 50. // the overall height of the spinning bolts
private constant integer SPELL_ID = 'ffbs' // id of the main spell
private constant integer DUMMYUNIT_ID = 'ffbd' //dummy units id
private constant integer CASTERDUMMY_ID = 'ffbc'
private constant integer SLOWSPELL_ORDER = 852075 //slowing spells order (slow)
private constant integer BURNSPELL_ORDER = 852209 //burning spells order (unholyfrenzy)
private constant integer SLOWSPELL_ID = 'cosd' //slowing spells id
private constant integer BURNSPELL_ID = 'fisd' //burning spells id
private constant real DAMAGEPERLEVEL_FIRE = 60. // base damage of the spell per level of ability
private constant real DAMAGEPERLEVEL_FROST = 60.
private constant attacktype ATTACKTYPE_ONE = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGETYPE_ONE = DAMAGE_TYPE_FIRE
private constant weapontype WEAPONTYPE_ONE = null
private constant attacktype ATTACKTYPE_TWO = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGETYPE_TWO = DAMAGE_TYPE_COLD
private constant weapontype WEAPONTYPE_TWO = null
// for raw code's (ability id, unit id) press ctrl+d in object editor
endglobals
private struct FrostFireBolt
effect effOne = null
effect effTwo = null
unit utarg = null
widget target = null
unit caster = null
boolean active = true
integer sign = 1
integer lvl = 0
real angle = 0.
static unit spellDummy = null
unit frostDummy = null
unit fireDummy = null
real targetX = 0.
real targetY = 0.
real newX = 0.
real newY = 0.
real ampX = 0.
real ampY = 0.
real ampZ = 0.
thistype next = 0
thistype prev = 0
static integer count = 0
static timer t = CreateTimer()
private method destroy takes nothing returns nothing
set .next.prev = .prev
set .prev.next = .next
set thistype.count = thistype.count -1
if thistype.count <= 0 then
set thistype.count = 0
call PauseTimer(thistype.t)
endif
call .deallocate()
endmethod
//--------------------------------------------------------------------
//--------- POSITIONING ----------------------------------------------
//--------------------------------------------------------------------
private static method positioning takes nothing returns nothing
local real velocity
local real amppower
local thistype this = thistype(0)
loop
set this = .next
exitwhen this == thistype(0)
if .active then
if not IsUnitType(.utarg,UNIT_TYPE_DEAD) and GetUnitTypeId(.utarg) != 0 then
set .targetX = GetWidgetX(.target)
set .targetY = GetWidgetY(.target)
set .angle = Atan2(.targetY-.newY,.targetX-.newX)
call SetUnitFacing(.frostDummy,.angle*57.29577951)
call SetUnitFacing(.fireDummy,.angle*57.29577951)
endif
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----Placing the unit to the next point---------//
set velocity = BOLT_SPEED * PERIOD
set .newX = .newX+(velocity * Cos(.angle))
set .newY = .newY+(velocity * Sin(.angle))
set amppower = 0.
if AMPLITUDE_VERTICAL != 0 then
set velocity = BOLT_SPIN_SPEED * PERIOD
set .ampX = .ampX + velocity * Cos(.angle+1.57079632 )*.sign
set .ampY = .ampY + velocity * Sin(.angle+1.57079632 )*.sign
set amppower = SquareRoot(.ampX*.ampX+.ampY*.ampY)
endif
if AMPLITUDE_HORISONTAL != 0. and AMPLITUDE_VERTICAL != 0. then
if .sign == 1 then
set .ampZ = (BOLT_OFFSET-AMPLITUDE_HORISONTAL)+(amppower*amppower)/(AMPLITUDE_VERTICAL*AMPLITUDE_VERTICAL/AMPLITUDE_HORISONTAL)
else
set .ampZ = (BOLT_OFFSET+AMPLITUDE_HORISONTAL)-(amppower*amppower)/(AMPLITUDE_VERTICAL*AMPLITUDE_VERTICAL/AMPLITUDE_HORISONTAL)
endif
call SetUnitFlyHeight(.frostDummy,.ampZ,0.)
if .sign == (-1) then
set .ampZ = (BOLT_OFFSET-AMPLITUDE_HORISONTAL)+(amppower*amppower)/(AMPLITUDE_VERTICAL*AMPLITUDE_VERTICAL/AMPLITUDE_HORISONTAL)
else
set .ampZ = (BOLT_OFFSET+AMPLITUDE_HORISONTAL)-(amppower*amppower)/(AMPLITUDE_VERTICAL*AMPLITUDE_VERTICAL/AMPLITUDE_HORISONTAL)
endif
call SetUnitFlyHeight(.fireDummy,.ampZ,0.)
elseif AMPLITUDE_HORISONTAL != 0. then
set velocity = BOLT_SPIN_SPEED * PERIOD
set .ampZ = .ampZ + velocity * .sign
call SetUnitFlyHeight(.frostDummy,BOLT_OFFSET+.ampZ,0.)
call SetUnitFlyHeight(.fireDummy,BOLT_OFFSET-.ampZ,0.)
endif
call SetUnitPosition(.frostDummy,.newX+.ampX,.newY+.ampY)
call SetUnitPosition(.fireDummy,.newX-.ampX,.newY-.ampY)
if AMPLITUDE_HORISONTAL != 0 or AMPLITUDE_VERTICAL != 0 then
if AMPLITUDE_HORISONTAL != 0 and AMPLITUDE_VERTICAL == 0 then
if .ampZ*.ampZ>=AMPLITUDE_HORISONTAL*AMPLITUDE_HORISONTAL then
set .sign = .sign*(-1)
endif
else
if amppower>=AMPLITUDE_VERTICAL then
set .sign = .sign*(-1)
endif
endif
endif
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----Checks if the unit reached its destination-//
if (.targetX-.newX)*(.targetX-.newX)+(.targetY-.newY)*(.targetY-.newY) <= 400. then
set .active = false
call DestroyEffect(.effOne)
call DestroyEffect(.effTwo)
call RemoveUnit(.frostDummy)
call RemoveUnit(.fireDummy)
set .frostDummy = null
set .fireDummy = null
if not IsUnitType(.utarg,UNIT_TYPE_DEAD) and GetUnitTypeId(.utarg) != 0 then
call DestroyEffect(AddSpecialEffect(IMPACTEFFECT_ONE,GetWidgetX(.target),GetWidgetY(.target)))
call DestroyEffect(AddSpecialEffect(IMPACTEFFECT_TWO,GetWidgetX(.target),GetWidgetY(.target)))
call UnitDamageTarget(.caster,.target,.lvl*DAMAGEPERLEVEL_FIRE,false,false,ATTACKTYPE_ONE,DAMAGETYPE_ONE,WEAPONTYPE_ONE)
call UnitDamageTarget(.caster,.target,.lvl*DAMAGEPERLEVEL_FROST,false,false,ATTACKTYPE_TWO,DAMAGETYPE_TWO,WEAPONTYPE_TWO)
call SetUnitPosition(thistype.spellDummy,GetWidgetX(.target),GetWidgetY(.target))
call SetUnitAbilityLevel(thistype.spellDummy,SLOWSPELL_ID,.lvl)
call SetUnitAbilityLevel(thistype.spellDummy,BURNSPELL_ID,.lvl)
call IssueTargetOrderById( thistype.spellDummy,SLOWSPELL_ORDER, .target)
call IssueTargetOrderById( thistype.spellDummy,BURNSPELL_ORDER, .target)
endif
set .target = null
set .caster = null
endif
endif
if not .active then
call .destroy()
set this = .prev
endif
endloop
endmethod
//--------------------------------------------------------------------
//STRUCT CREATION , REGISTRATION , INSTANCE HANDLING FOR MULTI STRUCTS
//--------------------------------------------------------------------
private static method create takes nothing returns thistype
local sound snd = CreateSound(SPELL_SOUND,false,true,false,10000,10000,"")
local thistype this = thistype.allocate()
local player pler
set .target = GetSpellTargetUnit()
set .utarg = GetSpellTargetUnit()
set .caster = GetSpellAbilityUnit()
set pler = GetOwningPlayer(.caster)
call SetUnitOwner(thistype.spellDummy,pler,false)
set .lvl = GetUnitAbilityLevel(.caster,SPELL_ID)
set .newX = GetUnitX(.caster)
set .newY = GetUnitY(.caster)
set .targetX = GetWidgetX(.target)
set .targetY = GetWidgetY(.target)
set .angle = Atan2(.targetY-.newY,.targetX-.newX)*57.29577951
call AttachSoundToUnit(snd,.caster)
call SetSoundVolume(snd,75)
call StartSound(snd)
call KillSoundWhenDone(snd)
set .frostDummy = CreateUnit(pler,DUMMYUNIT_ID,.newX,.newY,.angle)
set .fireDummy = CreateUnit(pler,DUMMYUNIT_ID,.newX,.newY,.angle)
call SetUnitScale(.frostDummy,BOLT_SCALE_TWO,BOLT_SCALE_TWO,BOLT_SCALE_TWO)
call SetUnitScale(.fireDummy,BOLT_SCALE_ONE,BOLT_SCALE_ONE,BOLT_SCALE_ONE)
call SetUnitPathing(.frostDummy,false)
call SetUnitPathing(.fireDummy,false)
call PauseUnit(.frostDummy,true)
call PauseUnit(.fireDummy,true)
set .effOne = AddSpecialEffectTarget(EFFECT_ONE,.fireDummy,"chest")
set .effTwo = AddSpecialEffectTarget(EFFECT_TWO,.frostDummy,"chest")
call UnitAddAbility(.frostDummy,'Amrf')
call UnitAddAbility(.fireDummy,'Amrf')
call UnitRemoveAbility(.frostDummy,'Amrf')
call UnitRemoveAbility(.fireDummy,'Amrf')
set .active = true
//this goes here and there and that goes there and here...
set .prev = thistype(0).prev
set .next = thistype(0)
set .prev.next = this
set thistype(0).prev = this
set thistype.count = thistype.count + 1
if thistype.count == 1 then
call TimerStart(thistype.t, PERIOD, true, function thistype.positioning)
endif
set snd = null
return this
endmethod
private static method filt takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call thistype.create()
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local integer i = 0
local trigger t = CreateTrigger()
set thistype.spellDummy = CreateUnit(Player(0),CASTERDUMMY_ID,0.,0.,0.)
call UnitAddAbility(thistype.spellDummy,'Amrf')
call UnitRemoveAbility(thistype.spellDummy,'Amrf')
call UnitAddAbility(thistype.spellDummy,SLOWSPELL_ID)
call UnitAddAbility(thistype.spellDummy,BURNSPELL_ID)
call TriggerAddCondition(t, Condition(function thistype.filt))
loop
exitwhen i >= bj_MAX_PLAYER_SLOTS
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
set i = i +1
endloop
endmethod
endstruct
endscope