Moderator
M
Moderator
17:04, 13th Mar 2010
TriggerHappy:
Updated your code to fit my last review.
TriggerHappy:
Updated your code to fit my last review.
scope Haunt initializer Init
//REQUIRES JASS NEWGEN PACK AND JASSHELPER
//http://www.wc3c.net/showthread.php?t=90999 *JassNewGen Pack Download
//http://www.wc3c.net/showthread.php?t=88142 *JassHelper
//************************************H***********************************HAUNT
//*************************************A**********************************HAUNT
//**************************************U*********************************HAUNT
//***************************************N********************************HAUNT
//****************************************T*******************************HAUNT
//* SPELL - HAUNT v1.6.0
//* By Purgeandfire - Give credits if you can.
//* Description: Sends Sends ghostly souls to travel around the caster which hit any target that comes within
//* range for 10*AbilityLevl+5 damage. It will then return to heal the caster for the total damage dealt.
//* ****Systems Used: Timer32 -- Jesus4Lyf
//* ****Models Used: Dummy.mdx
//***** Documentation *****
//* Required Object Editor Data: "Haunt" Spell, "Dummy" unit
//* Implementation: Copy the Haunt spell, the dummy unit, T32 trigger and the haunt trigger.
//* Required Alterations:
//* - RAWCODE: Change this to the RAWCODE of your haunt spell.
//* - DUMCODE: Change this to the RAWCODE of your dummy unit.
//* Cautions:
//* - RADIUS: Don't set this too high. It *might* cause a crash if it is too large.
//* - SOULRAD: This will look ugly if set too high. It has an emergency execute which will
//* execute the timer drain functions if the soul takes too long to get to the unit.
//* This was made to prevent possible infinite timer loops. (Which would cause extreme lag and crash)
//* - DSPAWNS: This can make your circle look a lot more circular. However, more units results in
//* more attached effects, so it can cause a bit more frame rate drop for the duration of the spell.
//* - waitBeforeEnum: When the spell is cast, black souls spawn and revolve around the caster. This enumeration wait
//* was made so that you can actually see the black souls spawn before they go and steal life from a unit.
//* You can shorten/lengthen this.
//* - angINCFACTOR: This modifies how fast the circles revolve around the caster. Note that this
//* takes RADIANS not degrees. This can go extremely fast if you set it to anything
//* higher than 1. About 0.08 or so is a moderate pace. 0.3 is a fast pace, but makes
//* a good circular effect.
//* - TRANSPARENCY: Modifies the transparency of the ghost that returns to the caster. This is from
//* 0-255, not 0-100. 0 is completely transparent, and 255 is visible.
//* - SOULVELOCITY: This modifies the movement speed of the souls. The only thing to beware of is that
//* a HIGHER input results in a lower speed and vice versa.
//* - GHOSTVELOCITY: Modifies the movement speed of the ghosts that return. Same thing as above, beware
//* that a HIGHER input results in a lower speed and vice versa. Note that setting this
//* too high can result the ghost actually being slower than the ranger. There is now an
//* emergency destroy and an automatic accelerator to have the ghost eventually reach the target.
//* Don't set this too high or else you'll hardly see it, or it will have to rely each time on the
//* emergency destroy to prevent the spell from crashing the game. Be sure that if this is increased,
//* that you set the exittimer_distance global mentioned next.
//* - EXITTIMER_DISTANCE: This modifies the distance the soul/ghost needs to be before it is destroyed.
//* Be careful though, this is distance*distance, not just normal distance. So 35*35 = 1225
//* which will mean that they will be destroyed once they get within a distance of 35.
//* - emergencyExits: These are new which allow you to modify when the soul/ghost timers will perform an emergency exit.
//* This is for those rare occasions where the spell might bug out. This ensures that the timer will
//* be paused eventually, preventing the chances of infinite loops which would most likely lag spike or
//* eventually crash the game.
//***** End Documentation *****
//* Default Values:
//* globals
//* private constant integer RAWCODE = 'A000'
//* private constant integer DUMCODE = 'h003'
//* private constant string DUMMYSFX = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
//* private constant string ONHITSFX = "Abilities\\Weapons\\DemonHunterMissile\\DemonHunterMissile.mdl"
//* private constant string DRAINSFX = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"
//* private constant real RADIUS = 300
//* private constant real SOULRAD = 175
//* private constant integer DSPAWNS = 13
//* private constant real waitBeforeEnum = 1.05
//* private constant real angINCFACTOR = 0.3
//* private constant integer SOULRED = 0
//* private constant integer SOULGREEN = 255
//* private constant integer SOULBLUE = 225
//* private constant integer TRANSPARENCY = 100
//* private constant real SOULVELOCITY = 10
//* private constant real GHOSTVELOCITY = 12
//* private group HauntG = CreateGroup()
//* private timer t = CreateTimer()
//* endglobals
globals
private constant integer RAWCODE = 'A000'
//RAWCODE of the Haunt spell.
private constant integer DUMCODE = 'h003'
//RAWCODE of the dummy unit.
private constant real DURATION = 5.005
//Modifies the duration before the effects automatically destroy themselves. (Seconds)
//For most accuracy, be sure that 0.035 is a factor of the value. (It works fine if it isn't though)
private constant string DUMMYSFX = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
//String path of the effects that revolve around the caster.
private constant string ONHITSFX = "Abilities\\Weapons\\DemonHunterMissile\\DemonHunterMissile.mdl"
//String path of the effects that occur when a target unit is hit.
private constant string DRAINSFX = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"
//String path of the effect that occurs when the soul returns to the caster.
private constant real RADIUS = 300
//Modifies the radius of the circle that the effects follow.
private constant real SOULRAD = 175
//Modifies the radius in which the soul will jump.
private constant integer DSPAWNS = 13
//Number of souls to create. More units equals more circular motion. However, more units also equals more frame drop.
private constant real waitBeforeEnum = 1.05
//Modifies the time to wait before the souls will start snatching nearby units. (Seconds)
private constant real angINCFACTOR = 0.3
//This is the angle increase factor every 0.035 seconds. It takes radians, not degrees. This can go pretty fast if too high.
private constant integer SOULRED = 0
private constant integer SOULGREEN = 255
private constant integer SOULBLUE = 225
private constant integer TRANSPARENCY = 100
//Modifies the vertex coloring of the soul that returns to the caster. Values are from 0-255 for all.
private constant real SOULVELOCITY = 10
//Modifies the velocity of the souls that rush toward the enemies.
//This isn't very noticeable though, unless the enemy is moving fast.
//LOWER VELOCITY SPECIFIED RESULTS IN A HIGHER SPEED, higher velocity results in a slower speed.
private constant real GHOSTVELOCITY = 12
//Modifies the speed of the ghosts that return to the caster.
//LOWER VELOCITY SPECIFIED RESULTS IN A HIGHER SPEED, higher velocity results in a slower speed.
private constant real EXITTIMER_DISTANCE = 1225
//Modifies the distance the soul/ghost has to be to the target before stopping the timer.
//This distance is actually 35. Use your input distance^2 (your input to the power of 2) and input that value for it to work.
private constant integer SOULHEIGHT = 75
//Modifies the elevation of the black souls.
private constant integer GHOSTHEIGHT = 15
//Modifies the elevation of the ghosts.
private constant attacktype ATTACKTYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGETYPE = DAMAGE_TYPE_ACID
private constant weapontype WEAPONTYPE = WEAPON_TYPE_WHOKNOWS
//Modifies the attacktype, damagetype, and weapontype of the damaging souls. Why did I use acid? No clue.
private constant real emergencyExitSoul = 1.25
//Modifies the duration (seconds) before the souls will be forced to reach they're destination. (To prevent infinite loops)
private constant real emergencyExitGhost = 4
//Modifies the duration (seconds) before the ghosts will be forced to reach they're destination. (To prevent infinite loops)
//Do not change these below.
private group HauntG = CreateGroup()
private timer t = CreateTimer()
endglobals
private constant function HAUNTDAMAGE takes integer LVL returns real
return 10.*LVL+5
//Modify this to modify the damage dealt by the spell per soul to unit.
//10 * Ability_Level + 5
//Rank 1: 15 damage, Rank 2: 25 damage, Rank 3: 35 damage.
endfunction
native UnitAlive takes unit u returns boolean
private function HSCond takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_ETHEREAL))
endfunction
//*SPELL CODE*
private struct HauntSReturn
unit a
unit b
unit c
real damage
real mD
effect e
real speedMultiplier
method onDestroy takes nothing returns nothing
set .a = null
set .b = null
set .c = null
set .damage = 0
set .mD = 0
set .speedMultiplier = 0
endmethod
private method periodic takes nothing returns nothing
local real x = GetUnitX(.c)
local real y = GetUnitY(.c)
local real dx = GetUnitX(.a)
local real dy = GetUnitY(.a)
local real distX = dx-x
local real distY = dy-y
local real ang = Atan2(distY,distX)
local real dist = distX*distX+distY*distY
local real newX = x+(.mD/GHOSTVELOCITY)*Cos(ang)*.speedMultiplier
local real newY = y+(.mD/GHOSTVELOCITY)*Sin(ang)*.speedMultiplier
call SetUnitX(.c,newX)
call SetUnitY(.c,newY)
if dist<EXITTIMER_DISTANCE or .speedMultiplier >= ((emergencyExitGhost/0.03125)*.1) then
call SetUnitX(.c,dx)
call SetUnitY(.c,dy)
call DestroyEffect(AddSpecialEffect(ONHITSFX,dx,dy))
call DestroyEffect(.e)
call RemoveUnit(.c)
call SetWidgetLife(.a,GetWidgetLife(.a)+.damage)
call this.stopPeriodic()
call this.destroy()
endif
set .speedMultiplier = .speedMultiplier + .1
endmethod
implement T32x
static method create takes unit caster, unit target, real damageTar returns HauntSReturn
local thistype this = thistype.allocate()
local real x = GetUnitX(target)-GetUnitX(caster)
local real y = GetUnitY(target)-GetUnitY(caster)
local real f = (bj_RADTODEG*Atan2(y,x))+180
set .a = caster
set .b = target
call SetUnitPathing(target,false)
set .c = CreateUnit(Player(15),GetUnitTypeId(.b),GetUnitX(.b),GetUnitY(.b),f)
set .damage = damageTar
set .mD = SquareRoot(x*x+y*y)
set .e = AddSpecialEffectTarget(DRAINSFX,.c,"origin")
set .speedMultiplier = 1
call SetUnitColor(.c,GetPlayerColor(GetOwningPlayer(.b)))
call UnitAddAbility(.c,'Amrf')
call SetUnitPathing(.c,false)
call UnitAddAbility(.c,'Aloc')
call UnitAddAbility(.c,'Avul')
call SetUnitFlyHeight(.c,GHOSTHEIGHT,0)
call PauseUnit(.c,true)
call SetUnitVertexColor(.c,SOULRED,SOULGREEN,SOULBLUE,TRANSPARENCY)
call SetUnitPathing(target,true)
return this
endmethod
endstruct
private struct HauntSGmove
unit a
unit b
effect c
unit d
real mD
integer SCount
method onDestroy takes nothing returns nothing
set .a = null
set .b = null
set .c = null
set .d = null
set .mD = 0
endmethod
private method periodic takes nothing returns nothing
local real x = GetUnitX(.a)
local real y = GetUnitY(.a)
local real tx = GetUnitX(.b)
local real ty = GetUnitY(.b)
local real distX = tx-x
local real distY = ty-y
local real ang = Atan2(distY,distX)
local real dist = distX*distX+distY*distY
local real newX = x+(.mD/SOULVELOCITY)*Cos(ang)
local real newY = y+(.mD/SOULVELOCITY)*Sin(ang)
call SetUnitX(.a,newX)
call SetUnitY(.a,newY)
if dist<EXITTIMER_DISTANCE or .SCount >= (emergencyExitSoul/0.03125) then
call SetUnitX(.a,tx)
call SetUnitY(.a,ty)
call DestroyEffect(AddSpecialEffect(ONHITSFX,tx,ty))
call UnitDamageTarget(.a,.b,HAUNTDAMAGE(GetUnitAbilityLevel(.d,RAWCODE)),true,false,ATTACKTYPE,DAMAGETYPE,WEAPONTYPE)
call DestroyEffect(.c)
call UnitRemoveAbility(.a,'Aloc')
call ShowUnit(.a,false)
call HauntSReturn.create(.d,.b,HAUNTDAMAGE(GetUnitAbilityLevel(.d,RAWCODE))).startPeriodic()
call this.stopPeriodic()
call this.destroy()
endif
set .SCount = .SCount + 1
endmethod
implement T32x
static method create takes unit u, unit tar, effect E, unit caster returns HauntSGmove
local thistype this = thistype.allocate()
local real x = GetUnitX(tar)-GetUnitX(u)
local real y = GetUnitY(tar)-GetUnitY(u)
set .a = u
set .b = tar
set .c = E
set .d = caster
set .mD = SquareRoot(x*x+y*y)
return this
endmethod
endstruct
private struct HauntS
unit u
real x
real y
unit array dum[DSPAWNS]
effect array sfx[DSPAWNS]
real DURATIONx
integer ENUMERATE
boolean array b[DSPAWNS]
method onDestroy takes nothing returns nothing
local integer i = 0
loop
exitwhen i == (DSPAWNS)
if (not IsUnitHidden(.dum[i])) then
call DestroyEffect(AddSpecialEffect(DUMMYSFX,GetUnitX(.dum[i]),GetUnitY(.dum[i])))
endif
call DestroyEffect(.sfx[i])
call UnitRemoveAbility(.dum[i],'Aloc')
call ShowUnit(.dum[i],false)
set .sfx[i] = null
set i = i + 1
endloop
set .x = 0
set .y = 0
set .DURATIONx = 0
set .ENUMERATE = 0
endmethod
private method periodic takes nothing returns nothing
local integer i = 0
local real dumX
local real dumY
local real x = GetUnitX(.u)
local real y = GetUnitY(.u)
local real ang = 0
local unit FoG
set .ENUMERATE = .ENUMERATE + 1
loop
exitwhen i == (DSPAWNS)
if .b[i] == true then
set ang = Atan2(GetUnitY(.dum[i])-y,GetUnitX(.dum[i])-x)+angINCFACTOR
set dumX = x+RADIUS*Cos(ang)
set dumY = y+RADIUS*Sin(ang)
call SetUnitX(.dum[i],dumX)
call SetUnitY(.dum[i],dumY)
if .ENUMERATE >= (waitBeforeEnum/0.035) then
call GroupEnumUnitsInRange(HauntG,dumX,dumY,SOULRAD,Condition(function HSCond))
set FoG = FirstOfGroup(HauntG)
if FoG != null and (not IsUnitAlly(FoG,GetOwningPlayer(.u))) and (not IsUnitHidden(.dum[i])) then
call HauntSGmove.create(.dum[i],FoG,.sfx[i],.u).startPeriodic()
set .b[i] = false
endif
endif
endif
set i = i + 1
endloop
set .DURATIONx = .DURATIONx - 0.035
if .DURATIONx <= 0 then
call this.stopPeriodic()
call this.destroy()
endif
set FoG = null
endmethod
implement T32x
static method create takes unit a returns HauntS
local thistype this = thistype.allocate()
local integer i = 0
local real angle = 360/DSPAWNS
local real dumX
local real dumY
set .u = a
set .x = GetUnitX(a)
set .y = GetUnitY(a)
set .DURATIONx = DURATION
set .ENUMERATE = 0
loop
exitwhen i >= (DSPAWNS)
set dumX = .x+RADIUS*Cos((i*angle)*bj_DEGTORAD)
set dumY = .y+RADIUS*Sin((i*angle)*bj_DEGTORAD)
set .b[i] = true
if (not IsUnitHidden(.dum[i])) or .dum[i] == null then
set .dum[i] = CreateUnit(Player(15),DUMCODE,dumX,dumY,270)
else
call ShowUnit(.dum[i],true)
call SetUnitX(.dum[i],dumX)
call SetUnitY(.dum[i],dumY)
endif
set .sfx[i] = AddSpecialEffectTarget(DUMMYSFX,.dum[i],"origin")
call UnitAddAbility(.dum[i],'Aloc')
call UnitAddAbility(.dum[i],'Amrf')
call SetUnitFlyHeight(.dum[i],SOULHEIGHT,0)
set i = i + 1
endloop
return this
endmethod
endstruct
private function Cond takes nothing returns boolean
if GetSpellAbilityId() == RAWCODE then
call HauntS.create(GetTriggerUnit()).startPeriodic()
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function Cond))
set t = null
endfunction
endscope