Добавлен , опубликован
Способ реализации:
Версия Warcraft:
Простое заклинание для новичков. Добавляет к стандартному Бурану дополнительный AOE урон раз в секунду. Код намеренно упрощён из-за обучающего характера материала. Более продвинутую версию можете посмотреть по следущей ссылке.

Видео

Код

//1. Обнуление глобалок не имеет смысла (только если вы уверены что она не будет больше вызыватся, тогда ради перфекционизма можно обнулить)
//2. Обнулять локальные переменные нужно обязательно, можно не обнулять integer, real, string (тут сам не знаю)
//3. Спелл сделан без кастомных функций, попытался более менее разъяснить о функциях для новичков, если где-то какая то ошибка, просьба отписать о ней)
//4. Итерация (повторение) - повторение таймера или цикла (тут таймер повторяется раз в 1 секунду, то есть раз в 1 секунду происходит итерация таймера)

globals //Начало глобальных переменных  
    hashtable HT = InitHashtable() //Создаётся хэш-таблица
    group Group = CreateGroup() //Создаём группу, которую будем использовать для моментальной выборки юнитов

    unit Caster //Создаётся переменная для кастеров
    unit Target //Создаётся переменная для целей
    timer Timer //Создаётся переменная для таймеров
    integer TimerId //Создаётся переменная для хэндл-айди таймеров

	//Константы для удобства изменения параметров способности
    constant integer Blizzard_Id = 'A000' //Равкод способности
	constant real Blizzard_Range = 300 //Область воздействия способности
	constant real Blizzard_Damage = 30 //Урон способности
endglobals //Конец глобальных переменных

native UnitAlive takes unit id returns boolean //Объявляется функция с проверкой на то что юнит жив, если не объявить, то и юзать не получится

function Blizzard_Group takes nothing returns nothing //Функция группы (фильтрация и нанесение урона)
	set Target = GetEnumUnit() //В переменную Target записывается выбираемый из группы юнит
    
	if UnitAlive( Target ) and IsUnitEnemy( Target, GetOwningPlayer( Caster ) ) and not IsUnitType( Target, UNIT_TYPE_STRUCTURE ) then //Условие на то что юнит жив, юнит враг, юнит не структура
		call UnitDamageTarget( Caster, Target, Blizzard_Damage, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_UNKNOWN, WEAPON_TYPE_WHOKNOWS ) //Наносит 300 урона отфильтрованным юнитам (врагам)
	endif //Конец условия
endfunction 

function Blizzard_Timer takes nothing returns nothing //Функция для таймера
    set Timer = GetExpiredTimer() //В Timer записывается истекающий таймер
    set TimerId = GetHandleId( Timer ) //в TimerId записывается хэндл-айди истекающего таймера
    set Caster = LoadUnitHandle( HT, TimerId, 'cstr' ) //В Caster выгружается значение из хэш-таблицы которые мы сохранили под родительским ключем Timer-а (TimerId) и под дочерним ключем 'cstr'
    
    //Ниже в двух функциях LoadReal мы таким же образом выгружаем наши значения из хэш-таблицы но уже под другими дочерними ключами
    call GroupEnumUnitsInRange( Group, LoadReal( HT, TimerId, 'cstX' ), LoadReal( HT, TimerId, 'cstY' ), Blizzard_Range, null ) //Выделяет юнитов в области Blizzard_Range (константа равная 300) и их добавление в группу
    call ForGroup( Group, function Blizzard_Group ) //Тут происходит вызов действия для группы
    call GroupClear( Group ) //Очистка группы от всех юнитов

    if GetUnitCurrentOrder( Caster ) != OrderId( "blizzard" ) then //Условие на то что наш герой перестал применять Буран
    //if GetUnitCurrentOrder( Caster ) != 0xd0079 then - этот вариант работает быстрее, так как использует id приказа без лишней возни со строками (без OrderId)
        call PauseTimer( Timer ) //Остановка таймера (таймер нужно остановить перед удалением так как иногда случается баг что итерация таймера происходит ещё раз)
        call DestroyTimer( Timer ) //Удаление таймера
        call FlushChildHashtable( HT, TimerId ) //Очистка хэш-таблицы по родительскому ключу (хэндл-айди Timer-а - TimerId)
    endif //Конец условия
endfunction //Конец функции

function Blizzard_Actions takes nothing returns nothing //Функция когда герой или юнит применяет способность
    if GetSpellAbilityId() == Blizzard_Id then //Условие если способность равна Blizzard_Id (константа равная равкоду Бурана 'A000')
        //CTRL + D в редакторе объектов что-бы узнать равкод чего либо
        set Timer = CreateTimer() //Создание таймера и его запись в переменную Timer
        set TimerId = GetHandleId( Timer ) //Получение хэндл-айди Timer-а и его запись в переменную TimerId

        //Сохранения в хэш-таблицу по родительскому ключу TimerId
        call SaveUnitHandle( HT, TimerId, 'cstr', GetTriggerUnit() ) //Сохраняется применяющий способность герой или юнит по дочернему ключу 'cstr'
        call SaveReal( HT, TimerId, 'cstX', GetSpellTargetX() ) //Сохраняется точка применения способности X по дочернему ключу 'cstX'
        call SaveReal( HT, TimerId, 'cstY', GetSpellTargetY() ) //Сохраняется точка применения способности Y по дочернему ключу 'cstY'
        call TimerStart( Timer, 1.0, true, function Blizzard_Timer ) //Запуск таймера Timer периодичностью в 1 секунду к которому привязана функция Blizzard_Timer (3 аргумент отвечает за периодичность)
	endif //Конец условия
endfunction //Конец функции

function InitTrig_Blizzard takes nothing returns nothing //Функция инициализации триггера (из функции main вызывается InitCustomTriggers() которая вызывает инициализацию всех триггеров на карте)
	local trigger t = CreateTrigger() //Создание триггера
	local integer i = 0 //Объявление целочисленной переменной

	loop //Начало цикла
		call TriggerRegisterPlayerUnitEvent( t, Player( i ), EVENT_PLAYER_UNIT_SPELL_EFFECT, null ) //Регистрирует событие применения способности для игрока i (изначально 0, 0 = 1 игрок, и т.д.)
		set i = i + 1 //Добавление 1 к i
		exitwhen i == bj_MAX_PLAYER_SLOTS //Условие выхода из цикла, тут вместо bj константы должно быть указано макс. кол-во игроков на карте 
    endloop //Конец цикла
    
	call TriggerAddAction( t, function Blizzard_Actions ) //В триггер добавляется функция с действиями при применения способности
	set t = null //Обнуляется локальная переменная
    
    call FogEnable( false ) //Это для видимости на всю карту (удалить)
    call FogMaskEnable( false ) //Это для видимости на всю карту (удалить)
endfunction //Конец функции

Требования

Скопировать код из шапки карты и триггера со спеллом к себе на карту, если такие глобальные переменные уже имеются, то копировать не надо. Поменять равкод способности в константах если он другой (скорее всего другой).
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Ответы (2)
25
nazarpunk, своя реализация рошной заливки архимага что ли? Так можно так и написать
15
Вышла новая версия! Прокрутить к ресурсу
Переписал весь спелл (с помощью от nazarpunk)
Ответы (3)
30
LastUchiha, годнота. Лайк. Только формулировка сбивает с толку:
это результат конвертации строки "blizzard" в целочисленное значение
Это не конвертация, это настоящий ид приказа. Строки сбоку присраны для гуймуйщмков.
30
LastUchiha, у приказове есть целлочисленный id, который и использует игра. А есть представление в виде строки, которое используется в РО и гуймуй чтоб кожанным мешкам было удобней.
//if GetUnitCurrentOrder( Caster ) != 0xd0079 then - этот вариант работает быстрее, так как использует ид приказа без лишней возни со строками
30
Итерация (повторение) - повторение таймера или цикла (тут таймер повторяется раз в 1 секунду, то есть раз в 1 секунду происходит итерация таймера)
Этот коммент вообще не в том месте находится.

Когда копипастил забыл вкомментариях изменить ключи:
call SaveReal( HT, TimerId, 'cstX', GetSpellTargetX() ) //Сохраняется точка применения способности X по дочернему ключу 'cstr'
call SaveReal( HT, TimerId, 'cstY', GetSpellTargetY() ) //Сохраняется точка применения способности Y по дочернему ключу 'cstr'

group Group = CreateGroup() //Создаёт темповая группа
Это не темповая (слово "временна" видать запретили), а глобальная группа, которая используется для всех одноразовых переборов GroupEnum*.
Ответы (4)
30
group Group = CreateGroup() //Создаём группу, которую будем использовать для моментальной выборки юнитов
15
nazarpunk, так а про итерацию, вынести к комментам в начале триггера?
30
так а про итерацию, вынести к комментам в начале триггера?
Ну да. В блоке удаления это явно не к месту.
30
Как я понял, ты просто взял дефолт буран и добавил к нему урона. А будет версия с рандомными эффектами?
Ответы (45)
30
попробуй напади!
Они падают скучно, одной пачкой. Сделал бы чтоб они вразнобой летели, как например здесь.
15
nazarpunk, ну тут у тебя в спелле моментальный эффект удара, а у меня пока долетит... Мне теперь что-ли 2 таймера запускать (один для эффекта, а другой для всего остального) ?
30
ну дак как сделать это нормально ?
Запускаешь таймер с минимальным периодом, например 0.1 и спавнишь чё надо. Заодно приказ проверяешь. Гемороя не сильно больше чем сейчас, но правда требует аккуратности.
15
nazarpunk, пришлось включить мозги...
На то что не вынес LoadInteger значение в переменную не обращай внимания.
function Blizzard_Timer takes nothing returns nothing //Функция для таймера
    set Timer = GetExpiredTimer() //В Timer записывается истекающий таймер
    set TimerId = GetHandleId( Timer ) //в TimerId записывается хэндл-айди истекающего таймера
    set Caster = LoadUnitHandle( HT, TimerId, 'cstr' ) //В Caster выгружается значение из хэш-таблицы которые мы сохранили под родительским ключем Timer-а (TimerId) и под дочерним ключем 'cstr'
    set CastX = LoadReal( HT, TimerId, 'cstX' )
    set CastY = LoadReal( HT, TimerId, 'cstY' )

    call SaveInteger( HT, TimerId, 'time', LoadInteger( HT, TimerId, 'time' ) + 1 )

    if LoadInteger( HT, TimerId, 'time' ) >= 8 then //Условие: 8 = 0.8 = время полёта эффекта (задержка перед уроном вначале)
        call GroupEnumUnitsInRange( Group, CastX, CastY, Blizzard_Range, null ) //Выделяет юнитов в области Blizzard_Range (константа равная 300)
        call ForGroup( Group, function Blizzard_Group ) //Тут происходит вызов действия для группы
        call GroupClear( Group ) //Очистка группы от всех юнитов 
    endif //Конец условия

    if GetUnitCurrentOrder( Caster ) != OrderId( "blizzard" ) then //Условие на то что наш герой перестал применять Буран
    //if GetUnitCurrentOrder( Caster ) != 0xd0079 then - этот вариант работает быстрее, так как использует id приказа без лишней возни со строками (без OrderId)
        call PauseTimer( Timer ) //Остановка таймера (таймер нужно остановить перед удалением так как иногда случается баг что итерация таймера происходит ещё раз)
        call DestroyTimer( Timer ) //Удаление таймера
        call FlushChildHashtable( HT, TimerId ) //Очитска хэш-таблицы по родительскому ключу (хэндл-айди Timer-а - TimerId)
    else
        //Если выложу спелл - закоменчу.
        if LoadInteger( HT, TimerId, 'time' ) < 23 then
            if GetRandomInt( 0, 1 ) == 0 then
                call DestroyEffect( AddSpecialEffect( "Rain of Fire.mdx", GetRandomReal( CastX - Blizzard_Range / 2, CastX + Blizzard_Range / 2 ), GetRandomReal( CastY - Blizzard_Range / 2, CastY + Blizzard_Range / 2 ) ) )
            else
                call DestroyEffect( AddSpecialEffect( "Rain of Fire Fel.mdx", GetRandomReal( CastX - Blizzard_Range / 2, CastX + Blizzard_Range / 2 ), GetRandomReal( CastY - Blizzard_Range / 2, CastY + Blizzard_Range / 2 ) ) )  
            endif
        endif
    endif //Конец условия
endfunction //Конец функции

function Blizzard_Actions takes nothing returns nothing //Функция когда герой или юнит применяет способность
    if GetSpellAbilityId() == Blizzard_Id then //Условие если способность равна Blizzard_Id (константа равная равкоду Бурана 'A000')
        //CTRL + D в редакторе объектов что-бы узнать равкод чего либо
        set CastX = GetSpellTargetX()
        set CastY = GetSpellTargetY()
       
        set Timer = CreateTimer()
        set TimerId = GetHandleId( Timer )
        
        //Сохранения в хэш-таблицу по родительскому ключу TimerId
        call SaveUnitHandle( HT, TimerId, 'cstr', GetTriggerUnit() ) //Сохраняется применяющий способность герой или юнит по дочернему ключу 'cstr'
        call SaveReal( HT, TimerId, 'cstX', CastX ) //Сохраняется точка применения способности X по дочернему ключу 'cstX'
        call SaveReal( HT, TimerId, 'cstY', CastY ) //Сохраняется точка применения способности Y по дочернему ключу 'cstY'
        call SaveInteger( HT, TimerId, 'time', 0 )
        call TimerStart( Timer, 0.1, true, function Blizzard_Timer ) //Запуск таймера Timer периодичностью в 1 секунду к которому привязана функция Blizzard_Timer (3 аргумент отвечает за периодичность)
    endif //Конец условия
endfunction //Конец функции
Загруженные файлы
30
LastUchiha, не, ну красота же. Можно публиковать следующую версию. Ну и в этом и следующем ресурсе лучше видео вместо скриншота делать.
30
ну тут у тебя в спелле моментальный эффект удара
А маркировки точки удара тоже моментальная?
30
код норм ?
Вполне. Для новичка вообще идеальный. Отдельный лайк за то, что догадался считать тики таймера и работать с целыми числами.
call SaveInteger( HT, TimerId, 'time', LoadInteger( HT, TimerId, 'time' ) + 1 )
15
nazarpunk, это я уже давно умею, я сонный просто и не мог догадаться до столь простого решения
15
nazarpunk, это всё конечно хорошо, но вот это... Надо бы да фиксить!
Загруженные файлы
30
я сонный просто и не мог догадаться до столь простого решения
Я это решение очень редко вижу, вместо него лепят просто страшные городухи из миллиона таймеров, хотя можно было сделать намного проще. Новичкам очень полезно его показать.
30
это всё конечно хорошо, но вот это...
Что именно? Каст после отмены или спавн в одну точку?
15
LastUchiha, круто по таймеру на каждого даммика...
Загруженные файлы
15
nazarpunk, визуально я попал бураном по цели, а до стадии урона не дошло дело.
30
визуально я попал бураном по цели, а до стадии урона не дошло дело.
Ну так нужно дать таймеру ещё потикать. И положить значение в HT которое говорит, что каст уже отменён, но мы ждём урона.
30
ну вот я о том же щас думаю.
Ну или вообще заморочиться с тем, чтоб каждая плямба наносила урон. Там если немного вспомнить математику не сильно сложно доработать сделав из хт многомерный массив.
30
не дружу с математикой на самом деле.
Ну смотри, значения в виде равкодов 'cstr' и прочих очень большие. Значит у тебя свободно начало индексов. Тебе нужно хранить массив точек, куда ты направляешь плямбы. Вот например есть у тебя индексы:
[0,1,2,3,4,5,6,7,8,9]
Но у точки же две координаты - [x,y]
[[0,1],[2,3],[4,5],[6,7],[8,9]]
Ну и изи ищем индексы иксов:
x0 = 0*2 = 0
x1 = 1*2 = 2
x2 = 2*2 = 4
x3 = 3*2= 6
x4 = 4*2 = 8
Ну а игрик будет тупо идти следующим.
Ну а дальше всё вообще просто. В таблицу целых чисел, которая не пересекается с реальными, в ячейку номера тика ложишь индекс плямбы.
Например, на 24 тике должна бахнуть плямба номер 1. Вот и сохраянешь по ключу 24 значение 1. А потом просто проверяешь каждый тик, есть ли плямба.
15
Ну так нужно дать таймеру ещё потикать
А вот если дать ему ещё потикать, то полетит не 1 выстрел, а чуть больше половины от всего спелла.
15
nazarpunk, этот момент пока что отпустим так сказать.
Ну или вообще заморочиться с тем, чтоб каждая плямба наносила урон.
30
то полетит не 1 выстрел, а чуть больше половины от всего спелла.
Читай комментарий выше. Если сделать с плямбами, то у тебя будет индекс последней плямбы и флаг о том, что плямбы больше плодить не нужно. Когда ты ловишь отмену приказа, то просто ставишь флаг горшочек не вари и ждёшь когда текущий тик станет больше последнего.
30
этот момент пока что отпустим так сказать.
А зачем его опускать? Так же будет намного круче. Эволюция так сказать. В этом спеле ты дал новичкам базу, а в спеле с отдельными плямбами уже немного математики за пятый класс.
15
nazarpunk, понятное дело круче, щас попытаюсь вкурить это в себя...
30
знал бы ты что я год назад лепил...
Траст ми, на мой дерьмометр совы уже не помещаются.
30
nazarpunk, что-то понял но 80% не понял :D
Ну смотри, что нам нужно? Через 8 тиков нанести урон по точке. Для этого нужно сохранить точку на номер тика. В хэштаблице нам доступно миллион индексов, поэтому не паримся что индексы закончатся. Так как мы упаковываем двумерные массивы [x,y] в одномерный, то индексов у нас становится в два раза меньше, но полмиллиона нам точно хватит.
Назовём ключи [xk, yk], а номер тика i. Тогда номер ключа будет находиться таким образом:
xk = i * 2
yk = i * 2 + 1
Когда с упаковкой точек разобрались, разберёмся с тиками. Допустим мы хотим плодить плямбы со случайными промежутками. Единственное ограничение, мы не сможем сделать две плямбы в один тик. Разберём тики тупо по порядку:
1
2
3 - Рандом сработал, через 8 тиков на нужно нанести урон. Значит i = 8 + 3 = 11. Сохраняем на 11 тик координаты.
4
5 - Рандом сработал, через 8 тиков на нужно нанести урон. Значит i = 8 + 5 = 13. Сохраняем на 13 тик координаты.
6
7 - Кастер прервал заклинание, но i=13, а 7 < 13, пишем флаг, что плодить плямбы больше не нужно и продолжаем тикать
8
9
10
11 - В этой ячейке есть координаты, наносим урон
12
13 - В этой ячейке есть координаты, наносим урон, заодно прерываем заклинание ибо есть флаг и 13 >= 13
15
LastUchiha, что-бы этого добиться мне пришлось какую то ересь в коде написать...
xk = i * 2
yk = i * 2 + 1
Вот это не захотело работать!
Вместо этого не умножаю на два, а просто вместо единицы прибавляю 100 для уверенности.
Также если перестать кастовать буран после 16 тика, урона не будет. Тут я не могу догадаться как сделать.
15
	set CastX = GetRandomReal( CastX - Blizzard_Range, CastX + Blizzard_Range )
    set CastY = GetRandomReal( CastY - Blizzard_Range, CastY + Blizzard_Range )

    if Tick >= 8 then
        if Tick >= 16 then
            call SaveReal( HT, TimerId, Tick * 2, CastX )
            call SaveReal( HT, TimerId, Tick * 2 + 1, CastY )
        endif
        
        if Tick >= 16 and not Bool then 
            call GroupEnumUnitsInRange( Group, LoadReal( HT, TimerId, Tick * 2 ), LoadReal( HT, TimerId, Tick * 2 + 1 ), Blizzard_Range, null )
        elseif Tick < 16 then
            call GroupEnumUnitsInRange( Group, LoadReal( HT, TimerId, Tick * 2 ), LoadReal( HT, TimerId, Tick * 2 + 1 ), Blizzard_Range, null )
        endif
        
        call ForGroup( Group, function Blizzard_Group )
        call GroupClear( Group )
    else
        call SaveReal( HT, TimerId, Tick * 2 + 8, CastX )
        call SaveReal( HT, TimerId, Tick * 2 + 8 + 1, CastY )
    endif
30
Вместо этого не умножаю на два, а просто вместо единицы прибавляю 100 для уверенности.
Не нужно писать числа наугад. Перечитай коммент. Тут же всё просто:
set i = 14 // Номер тика, integer
set xk = i * 2 // Ключ x, integer
set yk = xk + 1 // Ключ y, integer
set x = GetReal(xk) // Значение x, real
set y = GetReal(yk) // Значение y, real
30
LastUchiha, вот, смотри, набросал на коленку:
function SpellBlizzardCallback1 takes nothing returns nothing
	local integer i // Номер тика
	local integer last // Номер последнего тика
	local integer xk // Ключ для x
	local integer yk // Ключ для y
	local real x // Координата x
	local real y // Координата y
	
	set Timer = GetExpiredTimer()
	set TimerId = GetHandleId(Timer)
    
	set Caster = LoadUnitHandle(HT, TimerId, 'cstr')
	set CasterX = LoadReal(HT, TimerId, 'cstX')
	set CasterY = LoadReal(HT, TimerId, 'cstY')

	set i = LoadInteger(HT, TimerId, 'tick') // Получаем номер тика
	call SaveInteger(HT, TimerId, 'tick', i + 1) // Увеличиваем тик на единицу

	set last = LoadInteger(HT, TimerId, 'last') // Получаем номер последнего тика

	// Проверяем флаг и рандомим плямбу
	if HaveSavedBoolean(HT, TimerId, 'ends') and GetRandomInt(1, 100) <= 30 then
		// Рандомим координаты плямбы.
		set x = CasterX + SpellBlizzardRange * GetRandomReal(-1, 1)
		set y = CasterY + SpellBlizzardRange * GetRandomReal(-1, 1)

		set last = last + 8 // Откладываем на восемь тиков
		call SaveInteger(HT, TimerId, 'last', last) // Сохраняем тик последнего урона

		// Расчитываем ключ для сохранения
		set xk = last * 2
		set yk = xk + 1

		// Сохраняем координаты
		call SaveReal(HT, TimerId, xk, x)
		call SaveReal(HT, TimerId, yk, y)

		// Спавним эффект
		// DestroyEffect ... лень писать
	endif

	// Проверяем плямбу на текущем тике
	set xk = i * 2
	set yk = xk + 1

	// Для проверки тупо проверяем, сохранено ли что либо на ячейке
	if HaveSavedReal(HT, TimerId, xk) then 
		set x = LoadReal(HT, TimerId, xk)
		set y = LoadReal(HT, TimerId, yk)

		// Наносим урон в координанах
	endif

	// Проверяем приказ и пишем флаг
	if GetUnitCurrentOrder(Caster) != 0xd0079 then // blizzard
		call SaveBoolean(HT, TimerId, 'ends', true)
	endif

	// Останавливаем каст если выполнены условия
	if HaveSavedBoolean(HT, TimerId, 'ends') and i >= last then 
		call PauseTimer(Timer)
		call DestroyTimer(Timer)
		call FlushChildHashtable(HT, TimerId)
	endif
endfunction 
30
странно оно всё конечно работает
Что именно то странного?
И я кстати протупил и за был not здесь:
if not HaveSavedBoolean(HT, TimerId, 'ends') and GetRandomInt(1, 100) <= 30 then
15
nazarpunk, это я уже понял, тут и не только это
set last = last + 8
А надо
set last = Tick + 8
15
Во что получилось!
function Blizzard_Timer takes nothing returns nothing
	local integer last
	local integer xk
	local integer yk
    
	set Timer = GetExpiredTimer()
	set TimerId = GetHandleId( Timer )
	set Caster = LoadUnitHandle( HT, TimerId, 'cstr' )
	set CastX = LoadReal( HT, TimerId, 'cstX' )
	set CastY = LoadReal( HT, TimerId, 'cstY' )

	set Tick = LoadInteger( HT, TimerId, 'tick' )
	call SaveInteger( HT, TimerId, 'tick', Tick + 1 )
    set last = LoadInteger( HT, TimerId, 'last' )

	if not LoadBoolean( HT, TimerId, 'ends' ) then
		set CastX = CastX + Blizzard_Range * GetRandomReal( -1, 1 )
		set CastY = CastY + Blizzard_Range * GetRandomReal( -1, 1 )

		set last = Tick + 8
		call SaveInteger( HT, TimerId, 'last', last )

		set xk = last * 2
		set yk = xk + 1

		call SaveReal( HT, TimerId, xk, CastX )
		call SaveReal( HT, TimerId, yk, CastY )

        if GetRandomInt( 0, 1 ) == 0 then
            call DestroyEffect( AddSpecialEffect( "Rain of Fire.mdx", CastX, CastY ) )
        else
            call DestroyEffect( AddSpecialEffect( "Rain of Fire Fel.mdx", CastX, CastY ) )
        endif
	endif

	set xk = Tick * 2
	set yk = xk + 1

	if HaveSavedReal( HT, TimerId, xk ) then 
		set CastX = LoadReal( HT, TimerId, xk )
		set CastY = LoadReal( HT, TimerId, yk )
        
        call GroupEnumUnitsInRange( Group, CastX, CastY, Blizzard_Range, null )
        call ForGroup( Group, function Blizzard_Group )
        call GroupClear( Group )
	endif

	if GetUnitCurrentOrder( Caster ) != 0xd0079 then
		call SaveBoolean( HT, TimerId, 'ends', true )
	endif

	if LoadBoolean( HT, TimerId, 'ends' ) and Tick >= last then 
		call PauseTimer( Timer )
		call DestroyTimer( Timer )
		call FlushChildHashtable( HT, TimerId )
	endif
endfunction
15
ты зачем HaveSaved на Load поменял?
если бы не поменял оно бы и не заработало.
18
Лайк за любую движуху связанную с тригорноми скилами.
Ответы (7)
30
OVOgenez, ну я хз. Тригорные спелы конечно весело, но они сферические в вакууме. А хочется больше синергии и разных клёвых механик. А это в рамках одного спела не реализуешь,
18
nazarpunk, можно ж ещё спелпаки тогда делать, в виде героя для синергии.
за запятую в конце предложения тоже лайк
30
можно ж ещё спелпаки тогда делать, в виде героя для синергии.
Ну да, а к ним итемпаки, чтоб например создать огненный след по пути блинка.
18
nazarpunk, вообще я не думаю что тот кто шарит чё как делается будет себе в мапу импортировать чужие скилы, у каждого свои 100500 систем.
Те кому хочется разных клевых механик может с таких наработок взять разве что идею и мб глянуть как именно сделан спел, а дальше с 0 сделают под себя.
Короче не такая уж это и бессмысленная затея.
30
у каждого свои 100500 систем
Видел я эти системы. Лучше бы импортировали скилы.
18
nazarpunk, конечному игроку до фени чё там внутри. А вот автору удобнее работать со своим кодом. Всяко лучше чем напихать 99+ скилов рандомных людей и думать почему игра крашит. И скорее всего эти все скилы будут тянуть свои системы.
30
А вот автору удобнее работать со своим кодом.
Среднестатистическому автору удобей работать со своим кодом_Копировать_Копировать_Копировать_Копировать_Копировать_Копировать_Копировать_Копировать_Копировать_002
30
Кстати, держи, сгенерил тебе картинку для превью и основу для иконки.
Загруженные файлы
Ответы (3)
15
nazarpunk, это та что слева для превью на ресурсе получается?
30
это та что слева для превью на ресурсе получается?
Ну да, я ж специально с рамками без фона сделал, чтоб красиво смотрелась как у меня на боевом плоту.
Этот комментарий удален
15
Вышла новая версия! Прокрутить к ресурсу
  1. Исправил опечатки
  2. Поменял врагов для теста на пехотинцев (урон также уменьшил под них)
  3. Сделал ресурс красивее :)
Ответы (9)
30
LastUchiha, не нужно делать Франкенштейна из ресурса. Изначальный Буран был прекрасен своей простотой. Оставь его как есть, убери скриншоты и запиши короткое видео с прокастом. Притом желательно для целей юзать всяких футманов и прочую тир1 шушпару.
Картинку для предосмотра лучше вставляй только в предосмотр. Для это в коротком описании ресурса первой строкой сделай так:
<- картинка
А продвинутый буран опубликуй следующим ресурсом. Для примера глянь на количество вариаций AOE Благодати.
30
LastUchiha, чел, не знаю что у тебя за картошка, но это видео у меня мобилка воспроизводит.
Загруженные файлы
30
LastUchiha, ну блин, красота же. Только вот описание на фоне такой красоты смотрится просто ужасно.
Язык исполнения принято ставить перед названием. Да и JASS это аббревиатура, посему лучшим выбором будет [JASS] - Буран.
Ну и само описание тоже сделано ужасно. Можно же было нечто подобное сделать:
Простое заклинание для новичков. Добавляет к стандартному Буран дополнительный AOE урон раз в секунду. Код намеренно упрощён из-за обучающего характера материала. Более продвинутую версию можете посмотреть по следущей >ссылке<.
Ну и абзацем ниже телега про ошибки.
30
я о видео моего бурана.
У него тоже нет проблем с воспроизведением. Может у тебя интернет глючит?
Загруженные файлы
15
nazarpunk, хмм, может потому что я его записал через кодировщик HEVC
30
LastUchiha, ну и Спелл сделан без кастомных функций. режет глаза. Ибо твои функции по определению кастомные. Да и требуемые моды в обязательном порядке указываются. Так что если у тебя ничего не указано, то автоматически подразумевается запуск на ваниле.
30
LastUchiha, но и насколько я помню, кастом код карты вставляется после объявления функций. Соответственно в дефолт эдиторе у тебя не получится объявить глобалки и UnitAlive. Соответственно в требованиях необходимо указать JNGP или альтернативный способ пробросить глобалки и UnitAlive.
Этот комментарий удален
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.