Продолжение xgm.guru/p/100/208276
Экспериментальным путём удалось выяснить, что фатал из-за переполнения памяти после выхода из игры вызывает заклинание Priest of Titania.
В крайнем случае заменю его на другое, но, может, всё-таки кто отыщет возможную причину?
function PriestOfTitaniaCrystals takes nothing returns nothing
ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t2, u5, u6, u7, p, p2, elfs, crystals, heroes
local timer t2 = GetExpiredTimer()
local unit u5 = LoadUnitHandle(udg_Hash,GetHandleId(t2),2)
local unit u6
local unit u7
local location p
local location p2
local group elfs = GetUnitsInRectAll(GetPlayableMapRect())
local group crystals = GetUnitsInRectAll(GetPlayableMapRect())
local group heroes = GetUnitsInRectAll(GetPlayableMapRect())
local integer i = LoadInteger(udg_Hash, GetHandleId(t2),3)
local integer count = 0
local integer count2 = 0
local real elf_creaturetype = 0
local real crystal_might = 0
ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО
set i = i+1
call SaveInteger(udg_Hash,GetHandleId(t2),3,i)
if IsDead(u5) == true or u5 == null then
call PauseTimer(t2)
call DestroyTimer(t2)
call FlushChildHashtable(udg_Hash, GetHandleId(t2))
else
ГЕРОИ БЕРУТ КРИСТАЛЛЫ...
loop
set u6=FirstOfGroup(heroes)
exitwhen u6==null
call GroupRemoveUnit(heroes,u6)
if ( IsUnitType((u6), UNIT_TYPE_HERO) == true ) and IsDead (u6) == false then
set p = GetUnitLoc(u6)
set count2 = CountUnitsInGroup(crystals)
set count = 0
ТУТ ПОД ПЕРВЫМ УСЛОВИЕМ УЖЕ ЦИКЛ ДЛЯ КРИСТАЛЛОВ, СВЕРХУ НАМ НУЖНО ТОЛЬКО U6 И P...
loop
set u7=FirstOfGroup(crystals)
exitwhen u7==null
call GroupRemoveUnit(crystals,u7)
if ( GetUnitTypeId(u7) == 'n416' ) and IsDead(u7) == false then
set p2 = GetUnitLoc(u7)
if DistanceBetweenPoints (p, p2) < 120 then
set crystal_might = LoadReal(udg_Hash,GetHandleId(u7),StringHash("crystal_might"))
loop
exitwhen crystal_might == 0
set crystal_might = crystal_might - 1
ЭФФЕКТ И ДОБАВКА МАКС. МАНЫ ТРИЖДЫ - ЗАКРЫТО.
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Human\\Polymorph\\PolyMorphTarget.mdl.&#8203;mdx", u6, "chest"))
call UnitAddAbilityBJ('A25P', u6)
call SetUnitAbilityLevelSwapped('A25P', u6, 2)
call UnitRemoveAbilityBJ('A25P', u6)
call UnitAddAbilityBJ('A25P', u6)
call SetUnitAbilityLevelSwapped('A25P', u6, 2)
call UnitRemoveAbilityBJ('A25P', u6)
call UnitAddAbilityBJ('A25P', u6)
call SetUnitAbilityLevelSwapped('A25P', u6, 2)
call UnitRemoveAbilityBJ('A25P', u6)
ЭФФЕКТ И ДОБАВКА МАКС. МАНЫ ТРИЖДЫ - ЗАКРЫТО.
endloop
call RemoveUnit(u7)
call RemoveLocation(p2)
endif
else
endif
endloop
ТУТ ПJД ПЕРВЫМ УСЛОВИЕМ УЖЕ ЦИКЛ ДЛЯ КРИСТАЛЛОВ - ЗАКРЫТО.
ГРУППА КРИСТАЛЛОВ ПОЛНОСТЬЮ ОЧИЩЕНА, НУЖНО ДОБАВИТЬ В НЕЕ ВСЕХ
call GroupAddGroup( elfs, crystals )
ГРУППА КРИСТАЛЛОВ ПОЛНОСТЬЮ ОЧИЩЕНА, НУЖНО ДОБАВИТЬ В НЕЕ ВСЕХ
call RemoveLocation(p)
else
endif
endloop
ГЕРОИ БЕРУТ КРИСТАЛЛЫ - ЗАКРЫТО.
ЖРЕЦ СПАВНИТ КРИСТАЛЛ МОЩЬЮ ПО КОЛ-ВУ ЭЛЬФОВ - ПРОВЕРКА ПО ХЭШУ С ЗАПИСЬЮ В ПЕРЕМЕННУЮ! - СО ВРЕМЕНЕМ ЖИЗНИ 4 СЕК КАЖДЫЙ (РАЗ В 5 СЕКУНД)
if i >=5 then
set i = 0
call SaveInteger(udg_Hash,GetHandleId(t2),3,i)
loop 
set u6=FirstOfGroup(elfs)
exitwhen u6==null
set elf_creaturetype = LoadReal(udg_Hash,GetHandleId(u6),StringHash("elf_creaturetype"))
call GroupRemoveUnit(elfs,u6)
if elf_creaturetype == 1 and IsDead (u6) == false then
set count = count + 1
else
endif
endloop
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Human\\Polymorph\\PolyMorphTarget.mdl.&#8203;mdx",u5,"chest"))
set u6=CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE), 'n416', GetUnitX(u5), GetUnitY(u5), bj_RADTODEG * Atan2(GetUnitY(u5) - GetUnitY(u5), GetUnitX(u5) - GetUnitX(u5)))
call UnitApplyTimedLifeBJ( 4.00, 'BTLF', u6 )
set crystal_might = 0
loop
exitwhen count==0
set crystal_might = crystal_might + 1
set count = count-1
endloop
call SaveReal(udg_Hash,GetHandleId(u6),StringHash("crystal_might"),crystal_might)
endif
ЖРЕЦ СПАВНИТ КРИСТАЛЛ МОЩЬЮ ПО КОЛ-ВУ ЭЛЬФОВ СО ВРЕМЕНЕМ ЖИЗНИ 4 СЕК КАЖДЫЙ - ЗАКРЫТО.
endif
call DestroyGroup (elfs)
call DestroyGroup (crystals)
call DestroyGroup (heroes)
ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t2, u5, u6, u7, p, p2, elfs, crystals, heroes
set t2 = null
set u5 = null
set u6 = null
set u7 = null
set p = null
set p2 = null
set elfs = null
set crystals = null
set heroes = null
ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО
endfunction
function PriestOfTitania2 takes nothing returns nothing
ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t, u5
local timer t = GetExpiredTimer()
local unit u5 = LoadUnitHandle(udg_Hash,GetHandleId(t),2)
ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО
ТУТ СТОП, Т. К. ЭТО ПОСЛЕ АВТОКАСТА НА ОСНОВЕ ХИЛКИ СТАТУИ, НО И ТАЙМЕР ЧУТЬ ПОДОЛЬШЕ
call IssueImmediateOrderBJ( u5, "stop" )
ТУТ СТОП, Т. К. ЭТО ПОСЛЕ АВТОКАСТА НА ОСНОВЕ ХИЛКИ СТАТУИ - ЗАКРЫТО.
call SaveReal(udg_Hash,GetHandleId(u5),StringHash("priestoftitania"),0)
call PauseTimer(t)
call DestroyTimer(t)
call FlushChildHashtable(udg_Hash,GetHandleId(t))
ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t, u5
set t = null
set u5 = null
ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО
endfunction
function PriestOfTitania takes nothing returns nothing
ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t, t2, u5, u6
local timer t = CreateTimer()
local timer t2 = CreateTimer()
local unit u5=GetTriggerUnit()
local unit u6
ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО
local integer i = 0
local real priestoftitania = 0
set priestoftitania = LoadReal(udg_Hash,GetHandleId(u5),StringHash("priestoftitania"))
if ( GetSpellAbilityId() == 'A33A' ) and priestoftitania == 0 then
call SaveReal(udg_Hash,GetHandleId(u5),StringHash("priestoftitania"),1)
call PlaySoundOnUnitBJ( gg_snd_MaroSorcererPriestOfTitania, 100, u5 )
set u6=CreateUnit(GetOwningPlayer(u5), 'n413', GetUnitX(u5), GetUnitY(u5), bj_RADTODEG * Atan2(GetUnitY(u5) - GetUnitY(u5), GetUnitX(u5) - GetUnitX(u5)))
call UnitApplyTimedLifeBJ( 120.00, 'BTLF', u6 )
ХЭШ ЭЛЬФА
call SaveReal(udg_Hash,GetHandleId(u6),StringHash("elf_creaturetype"),1)
ХЭШ ЭЛЬФА - ЗАКРЫТО.
call DestroyEffect(AddSpecialEffectTarget("MaroSorcerer2.mdx",u6,"origin"))
call DestroyEffect(AddSpecialEffectTarget("MaroSorcerer2.mdx",u6,"chest"))
ХП РЕГ ВНОВЬ СОЗДАННОМУ ПО ЛВЛУ СКАСТОВАННОЙ АБИЛЫ
call AddUnitHPRegen (u6,10.00*GetUnitAbilityLevelSwapped(GetSpellAbilityId(), GetTriggerUnit()))
ХП РЕГ ВНОВЬ СОЗДАННОМУ ПО ЛВЛУ СКАСТОВАННОЙ АБИЛЫ - ЗАКРЫТО.
call SaveUnitHandle(udg_Hash, GetHandleId(t),2,u5)
call TimerStart(t,0.19,false,function PriestOfTitania2)
call SaveUnitHandle(udg_Hash, GetHandleId(t2),2,u6)
call SaveInteger(udg_Hash, GetHandleId(t2),3,i)
call TimerStart(t2,1,true,function PriestOfTitaniaCrystals)
else
call PauseTimer(t)
call DestroyTimer(t)
call FlushChildHashtable(udg_Hash, GetHandleId(t))
call PauseTimer(t2)
call DestroyTimer(t2)
call FlushChildHashtable(udg_Hash, GetHandleId(t2))
endif
ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ: t, t2, u5, u6
set t = null
set t2 = null
set u5 = null
set u6 = null
ВНИМАНИЕ!!! ОБНУЛЯЕМЫЕ ПЕРЕМЕННЫЕ ЗДЕСЬ СПИСКОМ - НОВЫЙ ШАБЛОН СОВЕРШЕНСТВА, ВСЕ ТЕСТКАРТЫ БЕЗ ОНОГО БУДУТ УДАЛЕНЫ - ЗАКРЫТО
endfunction

Все.
Проблема была НЕ в коде.
Код абилы вообще выключен, но после игры с ней фаталит. Просто за активацию дефолтной абилы.
Проблема в РО. ФАТАЛ ПОСЛЕ ИГРЫ ВЫЗЫВАЕТ НАСТРОЙКА АБИЛЫ В РО.
Для меня это звучит как "девочка с бородой" - в принципе возможно, но довольно удивительно, поэтому сразу в голову и не пришло.
Че-то нашаманено, ща перераспакую архив с "чистыми" картами и буду разбираться, что. Но т. к. эта абила пастилась на основе другой, сделанной на основе replenishlife, а там фатала не наблюдалось - разберусь.
///////////////////
Ну или другой вариант еще остался - РО в принципе настолько раздут до опупения, что любая дефолтная абила, созданная после момента X, начинает фаталить, если часто юзать ее в игре.
Но это крайне сомнительно, ибо:
  1. Довольно бредово;
  2. Первая из двух фаталящих абил на основе replenishlife была создана ДО трех последующих за ней абил у своего героя.
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
28
IsDead(u5) == true можно просто писать IsDead(u5).
Ровно как и not IsDead(u7) вместо IsDead(u7) == false.
Сделай скриншот ошибки и кинь сюда, избавься от локаций и сломанных моделек, после этого протестируй на фатал. Если он снова будет, вновь сделай скрин ошибки.
Bergi_Bear:
ну да может быть, а если триггер такой всего 1 и он крутит с десяток функций, которые не нужно будет уничтожать или очищать, может в этом случае и да таймеры лучше, коли дело разовое
Крутить десяток функций можно и таймером)
21
PT153,
  1. у себя и так делаю, это форматирование побилось тут
  2. см. выше
  3. мне тяжелее пока использовать координаты, многого не понимаю в их вычислении, даже не понимаю, как, например, юнит в направлении X и Y другого юнита подвинуть
Скриншот ошибки вроде "классический", да и драколич по ссылке перед кодом уже объяснил насчет ее причины
По таймеру: тут на самом деле в идеале надо как-то юзать один, чтобы он обсчитывал всех пристов, а не один для каждого приста. Но глобальные периодические таймеры - в любом случае бОльшее зло.
Спасибо за комментарии, ну скорее всего я пока просто заблокирую это заклинание и сделаю другое
Мне все равно в пристах многое не нравится, портрета у них нет (и из-за особенностей модельки чет тяжело поставить), сам по себе спелл не так удобно использовать, как в Battlegrounds
Понятно, что все это поодиночке можно исправить, но я пока лучше заменю спелл на спелл попроще, герой и вообще без Q збс играется, проверено
Пока "условно-закрыто".
28
Но глобальные периодические таймеры - в любом случае бОльшее зло.
Почему?
21
PT153, потому что если их много, снижается производительность, очевидно же.
28
мне тяжелее пока использовать координаты, многого не понимаю в их вычислении, даже не понимаю, как, например, юнит в направлении X и Y другого юнита подвинуть
Математика Вам в помощь)
у себя и так делаю, это форматирование побилось тут
Я так и думал, потому лучше прикрепляйте ещё и .txt файл с кодом. А ещё TESH позволяет Tab переводить в пробелы и наоборот.
21
Просто вот в случае с пристами код неоптимален, но если пристов вообще не кастуется (а может, этого героя и в игре-то не будет конкретной), то и нагрузки нет никакой, и когда все присты умирают, то и нагрузка падает.
А глобальный периодический таймер с кучей условий проверки - это нагрузка всегда.
///
Когда-то очень давно были попытки использования именно такой системы - все ключевое обрабатывалось глобальными периодическими таймерами.
Кодить было намного проще, просто пихаешь там юнит в нужную группу или даешь ему нужную метку и все. Но производительность стремительно снижалась с появлением новых заклинаний.
Это не так должно быть, надо функции писать в кастом код и уже через них юнитов обрабатывать на толчок или еще какое действие....
И в функциях таймеры должны быть ЛОКАЛЬНЫЕ...
//
вообще предполагаю глобальные периодические таймеры способом работы для кодеров-халявщиков не только в варкрафте
потому что так реально кодить гораздо проще, но сильно повышается сжирание ресурсов
вот потом и некоторые игры с графикой как в 2000, а требования к железу как в 2020
28
Прочитайте вот тут.
А глобальный периодический таймер с кучей условий проверки - это нагрузка всегда.
А триггер нет?
Но производительность стремительно снижалась с появлением новых заклинаний.
Делаю много таймеров, так как у меня своя система баффов. Таймеры хранятся в глобальном массиве, но постоянно удаляются и создаются снова, и ничего не глючит. Может, всё-таки дело в локациях?
И в функциях таймеры должны быть ЛОКАЛЬНЫЕ...
Простите, ЧТО? Откуда вы это вообще взяли?
21
PT153,
  1. относительно глобального периодического таймера - нет, конечно, одно дело что-то каждую секунду вертится и проверяет кучу условий вне зависимости от того, есть там юниты вообще удовлетворяющие этим условиям или нет; другое дело - что-то только при конкретных действиях создается и проверяется.
  2. даже без них снижаться должна хз
  3. что не так? upd: а, так если создаются и удаляются через глобальный массив - то понятно. Главное - чтобы УДАЛЯЛИСЬ.
Что-то мы разбредаться начинаем, ладно, в общем, проехали. Помощь получена, спасибо.
28
относительно глобального периодического таймера - нет, конечно, одно дело что-то каждую секунду вертится и проверяет кучу условий вне зависимости от того, есть там юниты вообще удовлетворяющие этим условиям или нет; другое дело - что-то только при конкретных действиях создается и проверяется.
Кажется, Вы плохо понимаете, как работает периодический триггер.
Периодически триггер вызывает условие, если оно есть, (TriggerEvaluate), а после действие (TriggerExecute). Потому если есть N периодических триггеров с разными условиями, то будет вызываться N условий каждый тик, то есть N новых потоков (если не 2N, я вот не знаю, наследуют ли действия поток условий или создают новый).
А если заменить это всё 1 таймеров с if - elseif, то условия будут вычисляться только до того момента, пока одно из них не даст TRUE. В худшем случае, будет посчитано N условий. Но даже в этом случае это будет лучше триггеров, так как те каждый тик создают как минимум N новых потоков, а 1 таймер каждый тик создают только 1 новый поток.
Другое дело, разница между одним период. триггером и 1 период таймером. Таймер регулировать проще, чем триггер, а в случае ненадобности, таймер можно просто удалить.
что не так?
Глобальные таймеры - это ни хорошо, ни плохо. Глобальный таймер можно создать 1 раз и его использовать.
globals
   constant timer TIMER = CreateTimer()
endglobals
Этот таймер нельзя перезаписать, но можно удалить.
А можно создавать и удалять, если это нужно. Я так делаю, чтобы быть убеждённым, что отсроченные действия не будут выполнены после удаления структуры и уже созданный таймер не будет потерян в памяти из-за его пересоздания при инициализации структуры.
struct CustomTimer
   timer t
   // some parameters

   static method create takes nothing returns thistype
      local thistype this = allocate()
      set t = CreateTimer()
      return this
   endmethod

   method onDestroy takes nothing returns nothing
      call DestroyTimer(t)
   endmethod
endstruct
В целом можно таймер просто паузить при удалении структуры, а при создании написать условие для проверки наличия таймера, но тогда это будет 4 строки, вместо 2-х, а таймеры будут всегда висеть в памяти.
21
PT153, триггеры могут просто вообще даже не созданы быть
а таймер глобальный если не удален то всегда крутится и проверяет
ну если ты удаляешь то ок
все я говорю тема не об этом уже
28
а таймер глобальный если не удален то всегда крутится и проверяет
Есть такая функция, называется PauseTimer().
К тому же, таймер можно и не стартовать (Timer Start()), тогда и крутиться нечему.
триггеры могут просто вообще даже не созданы быть
Может быть, выключены, а не созданы?
все я говорю тема не об этом уже
Да я понял, просто совет даю.
Можете не отвечать.
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.