- Joined
- Feb 11, 2011
- Messages
- 1,860
I am working on some hero spells for someone and one of them is Multi-Shot. I have coded it and it works, except sometimes some of the arrows just freeze. What is the cause of this? Also, any optimizations are welcome.
JASS:
scope MultiShot
globals
// ~~~ Configurables ~~~
private constant integer SPELL_ID = 'A002'
private constant integer DUMMY_ID = 'h005' // The ID of the "Multi-Shot [arrow]" unit, not the invisible dummy unit.
private constant integer ARROW_COUNT = 10
private constant real ARROW_DISTANCE = 1500
private constant real ARROW_SPEED = 1000
private constant string ARROW_EFFECT = "Abilities\\Weapons\\GlaiveMissile\\GlaiveMissileTarget.mdl" // The effect when the arrows are destroyed.
private constant real ARROW_AREA = 150 // How close a unit has to be to an arrow to get damaged.
private constant boolean SHOW_DAMAGE = true
// ~~~ End of Configurables ~~~
private constant real ARROW_DEGREES = 60 / ARROW_COUNT
private constant real ARROW_SPEED_TRUE = ARROW_SPEED * 0.03
endglobals
private struct MultiShot_Data
group units = CreateGroup()
integer dead = 0
unit array arrow [ARROW_COUNT]
unit caster
real array distance [ARROW_COUNT]
real damage
private method destroy takes nothing returns nothing
local integer i = 1
call DestroyGroup(.units)
set .units = null
set .caster = null
call .deallocate()
endmethod
private static method Timer_Actions takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer i = 1
local integer index = 0
local real x
local real y
local unit u
local texttag tt
loop
if .arrow[i] != null then
set x = GetUnitX(.arrow[i]) + ARROW_SPEED_TRUE * Cos(bj_DEGTORAD * GetUnitFacing(.arrow[i]))
set y = GetUnitY(.arrow[i]) + ARROW_SPEED_TRUE * Sin(bj_DEGTORAD * GetUnitFacing(.arrow[i]))
if .distance[i] > 0 and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) then
call SetUnitX(.arrow[i], x)
call SetUnitY(.arrow[i], y)
set .distance[i] = .distance[i] - ARROW_SPEED_TRUE
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, ARROW_AREA, null)
loop
set u = FirstOfGroup(bj_lastCreatedGroup)
exitwhen u == null
if not IsUnitInGroup(u, .units) and IsUnitEnemy(u, GetOwningPlayer(.caster)) and not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) then
call GroupAddUnit(.units, u)
call UnitDamageTarget(.caster, u, .damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
if SHOW_DAMAGE then
set tt = CreateTextTag()
call SetTextTagText(tt, "|cffff0000" + I2S(R2I(.damage)) + "!|r", 0.023)
call SetTextTagPosUnit(tt, u, 25)
call SetTextTagVelocity(tt, 0, 64 * 0.071 / 128)
call SetTextTagPermanent(tt, false)
call SetTextTagLifespan(tt, 2)
call SetTextTagFadepoint(tt, 1)
call SetTextTagVisibility(tt, true)
endif
call DestroyEffect(AddSpecialEffect(ARROW_EFFECT, GetUnitX(.arrow[i]), GetUnitY(.arrow[i])))
call RemoveUnit(.arrow[i])
set .arrow[i] = null
set .dead = .dead + 1
endif
call GroupRemoveUnit(bj_lastCreatedGroup, u)
endloop
else
call DestroyEffect(AddSpecialEffect(ARROW_EFFECT, GetUnitX(.arrow[i]), GetUnitY(.arrow[i])))
call RemoveUnit(.arrow[i])
set .arrow[i] = null
set .dead = .dead + 1
endif
endif
exitwhen i == ARROW_COUNT
set i = i + 1
endloop
if .dead == ARROW_COUNT then
call ReleaseTimer(GetExpiredTimer())
call .destroy()
endif
endmethod
private static method Actions takes nothing returns thistype
local thistype this = thistype.allocate()
local timer t = NewTimer()
local integer i = 1
local real min
local real x
local real y
local unit caster = GetTriggerUnit()
local integer agility = GetHeroAgi(caster, true)
local integer intelligence = GetHeroInt(caster, true)
local integer strength = GetHeroStr(caster, true)
local integer hero_level = GetHeroLevel(caster)
set .caster = caster
set .damage = (5 + (3 * hero_level)) + (1.5 * agility)
set .caster = GetTriggerUnit()
set min = bj_RADTODEG * Atan2(GetSpellTargetY() - GetUnitY(.caster), GetSpellTargetX() - GetUnitX(.caster)) - 30 - ARROW_DEGREES
set x = GetUnitX(.caster)
set y = GetUnitY(.caster)
loop
set .arrow[i] = CreateUnit(GetOwningPlayer(.caster), DUMMY_ID, x, y, min + (ARROW_DEGREES * i))
set .distance[i] = ARROW_DISTANCE
exitwhen i == ARROW_COUNT
set i = i + 1
endloop
call SetTimerData(t, this)
call TimerStart(t, 0.03, true, function thistype.Timer_Actions)
set t = null
set caster = null
return this
endmethod
private static method Conditions takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call thistype.Actions()
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.Conditions))
set t = null
endmethod
endstruct
endscope