Функция обработки движения
Теперь, когда главная функция, которая наносит урон и запускает таймер,
сделана, мы приступаем к следующему этапу: созданию циклической функции
движения.
А начнем мы с извлечения из кеша данных, сохраненных другой
функцией:
function Stomp_Move takes nothing returns nothing
local string s = I2S(H2I(GetExpiredTimer()))
local gamecache gc = udg_AbilityCache
local real x = GetStoredReal(gc, s, "x")
local real y = GetStoredReal(gc, s, "y")
local integer i = GetStoredInteger(gc, s, "level")
local group g = Stomp_CopyGroup(I2G(GetStoredInteger(gc, s, "group")))
...
Вам следует обратить особое внимание на некоторые участки этого фрагмента
кода.
Во-первых, мы не записываем истекший таймер в отдельную переменную, мы
напрямую используем значение, сохраненное в кэше (за исключением единственного
случая, когда нам надо будет уничтожить таймер), так как он будет нужен нам
только один раз при каждом выполнении функции. Мы не записываем группу в
отдельную переменную по тем же причинам. Мы берем значение из кэша и копируем
его в переменную g.
Загрузка из кэша оригинала группы и его модифицирование, является очень
распространенной ошибкой JASS спеллов, вроде этого. Некоторые думают, что при
следующем срабатывании таймера, загрузится та же группа, содержащая тех же
юнитов, даже если они удаляют юнитов из группы и уничтожаю ее. Но это далеко не
так.
Например, если вы убьете юнита, прикрепленного к таймеру, юнит будет
мертв и при следующем срабатывании этого таймера. То же самое происходит и с
группами и всеми другими объектами.
Спелл отталкивает юнита назад, в течении определенного промежутка времени,
таким образом, чтобы отследить сколько времени спелл уже отталкивает юнита, нам
потребуется еще одна переменная.
function Stomp_Move takes nothing returns nothing
local string s = I2S(H2I(GetExpiredTimer()))
local gamecache gc = udg_AbilityCache
local real x = GetStoredReal(gc, s, "x")
local real y = GetStoredReal(gc, s, "y")
local integer i = GetStoredInteger(gc, s, "level")
local group g = Stomp_CopyGroup(I2G(GetStoredInteger(gc, s, "group")))
local real dur = GetStoredReal(gc, s, "dur")+0.05
if dur < 1+0.5*i then
else
endif
...
Я добавил переменную dur типа real. В нее загружается значение,
прикрепленное к таймеру с меткой 'dur' и затем ее значению увеличивается на
+0.05 (временной интервал таймера).
Если вы загружаете НЕ сохраненное значение из кэша, то загруженное значение
всегда будет равно 0/0.0/"" или null, в зависимости от типа.
Этот спелл толкает юнитов на протяжении 1+0.5*i (i – уровень спелла)
секунд, так что нам требуется добавить блок if/then/else.
Ну что ж, давайте, добавим часть кода, которая непосредственно двигает
юнитов, на которых действует спелл. Для этого добавим следующие переменные: real
ux, real uy, real a, unit f.
function Stomp_Move takes nothing returns nothing
local string s = I2S(H2I(GetExpiredTimer()))
local gamecache gc = udg_AbilityCache
local real x = GetStoredReal(gc, s, "x")
local real y = GetStoredReal(gc, s, "y")
local integer i = GetStoredInteger(gc, s, "level")
local group g = Stomp_CopyGroup(I2G(GetStoredInteger(gc, s, "group")))
local real dur = GetStoredReal(gc, s, "dur")+0.05
local real ux
local real uy
local real a
local unit f
if dur < 1+0.5*i then
loop
set f = FirstOfGroup(g)
exitwhen f == null
set ux = GetUnitX(f)
set uy = GetUnitY(f)
set a = Atan2(uy-y, ux-x)
call SetUnitPosition(f, ux+40*Cos(a), uy+40*Sin(a))
call GroupRemoveUnit(g, f)
endloop
call StoreReal(gc, s, "dur", dur)
else
endif
...
Как и в главной функции, мы циклически перебираем всех юнитов группы,
используя функцию FirstOfGroup().
Для начала, нам нужно сохранить координаты
юнита.
Затем мы вычисляем угол (в радианах) между эпицентром спелла и
позицией юнита, используя функцию Atan2.
Я не буду рассказывать, как эта функция устроена, я лучше расскажу, как нам
ее правильно использовать.
Попросту используйте конструкцию вида
Atan2(otherPointY-centerPointY, otherPointX-centerPointX), чтобы получить угол
(в радианах) между точками centerPoint и otherPoint.
Спелл должен передвигать юнита. Существует два различных (лучших) способа
передвигать юнита:
SetUnitPosition – Эта native функция передвигает юнита в точку с
координатами X и Y. Пока юнит передвигается, он не может двигаться и кастовать
канальные (channeling) спеллы и вообще ведет себя как будто остановлен. Эта
функция не требует дополнительных проверок, и она полностью безопасна.
SetUnitX/Y – Native функции SetUnitX и SetUnitY также меняют X и Y
координаты юнита. Однако, юнит не прекращает двигаться, кастовать канальные
спеллы и так далее, во время движения. Эти функции работают быстрее, чем
SetUnitPosition, но если вы используете координаты за пределами карты, игра
вылетит.
Здесь я использую функцию SetUnitPosition для передвижения юнита на 40
единиц, всякий раз, когда таймер срабатывает. Это значит, что скорость движения
будет 40*100*0.05 = 800. Применение именно этой функции, в данном случае, проще,
так как не требует дополнительных проверок, но главной причиной, по которой я
использую эту функцию, является то, что юниты не могут двигаться, пока таймер
толкает их, и все возможные касты канальных спеллов, будут остановлены.
Следовательно, эта функция идеальна для этого спелла.
Я также сохраняю переменную dur, значение которой увеличивается и
сохраняется в кэш всякий раз, когда таймер срабатывает. Иначе действие спелла
длилось бы бесконечно долго.
Когда время действия спелла истекает, он должен остановится. Так что нам
требуется добавить код, который очистит значения в кэше, уничтожит группу и
остановит таймер.
function Stomp_Move takes nothing returns nothing local string s = I2S(H2I(GetExpiredTimer())) local gamecache gc = udg_AbilityCache local real x = GetStoredReal(gc, s, "x") local real y = GetStoredReal(gc, s, "y") local integer i = GetStoredInteger(gc, s, "level") local group g = Stomp_CopyGroup(I2G(GetStoredInteger(gc, s, "group"))) local real dur = GetStoredReal(gc, s, "dur")+0.05 local real ux local real uy local real a local unit f if dur < 1+0.5*i then loop set f = FirstOfGroup(g) exitwhen f == null set ux = GetUnitX(f) set uy = GetUnitY(f) set a = Atan2(uy-y, ux-x) call SetUnitPosition(f, ux+40*Cos(a), uy+40*Sin(a)) call GroupRemoveUnit(g, f) endloop call StoreReal(gc, s, "dur", dur) else call DestroyGroup(I2G(GetStoredInteger(gc, s, "group"))) call FlushStoredMission(gc, s) call DestroyTimer(GetExpiredTimer()) endif ...
|
Сначала, мы уничтожаем группу юнитов, сохраненную в кэше.
Затем полностью очищаем категорию s в кэше. Это действие очищает все
данные, которые мы 'прикрепили' к таймеру, таким образом, мы исключаем повторное
использование этих данных другими спеллами в дальнейшем.
И в завершении, мы уничтожаем истекший таймер.
Теперь все что нам осталось сделать в этой функции – устранить утечки
памяти. Давайте сделаем это:
function Stomp_Move takes nothing returns nothing local string s = I2S(H2I(GetExpiredTimer())) local gamecache gc = udg_AbilityCache local real x = GetStoredReal(gc, s, "x") local real y = GetStoredReal(gc, s, "y") local integer i = GetStoredInteger(gc, s, "level") local group g = Stomp_CopyGroup(I2G(GetStoredInteger(gc, s, "group"))) local real dur = GetStoredReal(gc, s, "dur")+0.05 local real ux local real uy local real a local unit f if dur < 1+0.5*i then loop set f = FirstOfGroup(g) exitwhen f == null set ux = GetUnitX(f) set uy = GetUnitY(f) set a = Atan2(uy-y, ux-x) call SetUnitPosition(f, ux+40*Cos(a), uy+40*Sin(a)) call GroupRemoveUnit(g, f) endloop call StoreReal(gc, s, "dur", dur) else call DestroyGroup(I2G(GetStoredInteger(gc, s, "group"))) call FlushStoredMission(gc, s) call DestroyTimer(GetExpiredTimer()) endif set gc = null call DestroyGroup(g) set g = null set f = null endfunction
|
Вот и все! Вы успешно завершили создании своего собственного stomp
спелла.
Вот полный код нашего триггера:
function Trig_Stomp_Conditions takes nothing returns boolean return GetSpellAbilityId() == 'A000' endfunction
function Stomp_Filter takes nothing returns boolean return IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetFilterUnit()))
and GetWidgetLife(GetFilterUnit()) > 0.405 and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) endfunction
function Stomp_CopyGroup takes group g returns group set bj_groupAddGroupDest = CreateGroup() call ForGroup(g, function GroupAddGroupEnum) return bj_groupAddGroupDest endfunction
function Stomp_Move takes nothing returns nothing local string s = I2S(H2I(GetExpiredTimer())) local gamecache gc = udg_AbilityCache local real x = GetStoredReal(gc, s, "x") local real y = GetStoredReal(gc, s, "y") local integer i = GetStoredInteger(gc, s, "level") local group g = Stomp_CopyGroup(I2G(GetStoredInteger(gc, s, "group"))) local real dur = GetStoredReal(gc, s, "dur")+0.05 local real ux local real uy local real a local unit f if dur < 1+0.5*i then loop set f = FirstOfGroup(g) exitwhen f == null set ux = GetUnitX(f) set uy = GetUnitY(f) set a = Atan2(uy-y, ux-x) call SetUnitPosition(f, ux+40*Cos(a), uy+40*Sin(a)) call GroupRemoveUnit(g, f) endloop call StoreReal(gc, s, "dur", dur) else call DestroyGroup(I2G(GetStoredInteger(gc, s, "group"))) call FlushStoredMission(gc, s) call DestroyTimer(GetExpiredTimer()) endif set gc = null call DestroyGroup(g) set g = null set f = null endfunction
function Trig_Stomp_Actions takes nothing returns nothing local unit c = GetTriggerUnit() local real x = GetUnitX(c) local real y = GetUnitY(c) local integer i = GetUnitAbilityLevel(c, 'A000') local boolexpr b = Condition(function Stomp_Filter) local group g = CreateGroup() local group n local unit f local gamecache gc = udg_AbilityCache local timer t = CreateTimer() local string s = I2S(H2I(t)) call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl", x, y)) call GroupEnumUnitsInRange(g, x, y, 100+50*i, b) set n = Stomp_CopyGroup(g) loop set f = FirstOfGroup(n) exitwhen f == null call UnitDamageTarget(c, f, 25*i, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null) call GroupRemoveUnit(n, f) endloop call StoreInteger(gc, s, "level", i) call StoreInteger(gc, s, "group", H2I(g)) call StoreReal(gc, s, "x", x) call StoreReal(gc, s, "y", y) call TimerStart(t, 0.05, true, function Stomp_Move) set c = null call DestroyBoolExpr(b) set b = null set g = null call DestroyGroup(n) set n = null set f = null set gc = null set t = null endfunction
//=========================================================================== function InitTrig_Stomp takes nothing returns nothing set gg_trg_Stomp = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ( gg_trg_Stomp, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition( gg_trg_Stomp, Condition( function Trig_Stomp_Conditions ) ) call TriggerAddAction( gg_trg_Stomp, function Trig_Stomp_Actions ) call Preload("Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl") endfunction
|