Раздел:
Триггеры и объекты

Идея

Основная идея заключается в том, что lua позволяет хранить функции в таблицах и при срабатывании триггера нам всего лишь нужно вызвать определённую функцию. Основная проблема в том, как это красиво сделать.

Шаблон

Воспользовавшись тем, что редактор собирает код в том порядке, в котором он там размещён, создадим папку для способностей:
В блоке Ability определим глобальную таблицу, для хранения всех способностей:
ABILITY = {}
В самом простом случае, хранить способности можно таким образом:
ABILITY[FourCC('A000')] = function()
	print('spell')
end
Но как мы знаем у способности есть пять событий, вызываемых в таком порядке:
  • EVENT_PLAYER_UNIT_SPELL_CHANNEL
  • EVENT_PLAYER_UNIT_SPELL_CAST
  • EVENT_PLAYER_UNIT_SPELL_EFFECT
  • EVENT_PLAYER_UNIT_SPELL_ENDCAST
  • EVENT_PLAYER_UNIT_SPELL_FINISH
Поэтому немного изменим шаблон:
ABILITY[FourCC('A000')] = {
	CHANNEL = function()
		print('EVENT_PLAYER_UNIT_SPELL_CHANNEL')
	end,
	CAST    = function()
		print('EVENT_PLAYER_UNIT_SPELL_CAST')
	end,
	EFFECT  = function()
		print('EVENT_PLAYER_UNIT_SPELL_EFFECT')
	end,
	ENDCAST = function()
		print('EVENT_PLAYER_UNIT_SPELL_ENDCAST')
	end,
	FINISH  = function()
		print('EVENT_PLAYER_UNIT_SPELL_FINISH')
	end
}

Триггер

Осталось только сделать триггер, который будет работать с этим шаблоном. Для этого вернёмся в блок Ability, откроем do ... end и начнём.
Для начала сохраним id всех событий, чтоб каждый раз не вызывать функцию:
local EventChannelId = GetHandleId(EVENT_PLAYER_UNIT_SPELL_CHANNEL)
local EventCastId    = GetHandleId(EVENT_PLAYER_UNIT_SPELL_CAST)
local EventEffectId  = GetHandleId(EVENT_PLAYER_UNIT_SPELL_EFFECT)
local EventEndCastId = GetHandleId(EVENT_PLAYER_UNIT_SPELL_ENDCAST)
local EventFinishId  = GetHandleId(EVENT_PLAYER_UNIT_SPELL_FINISH)
Далее создадим триггер и добавим ему все события.
local AbilityTrigger = CreateTrigger()
for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
	TriggerRegisterPlayerUnitEvent(AbilityTrigger, Player(i), EVENT_PLAYER_UNIT_SPELL_CHANNEL)
	TriggerRegisterPlayerUnitEvent(AbilityTrigger, Player(i), EVENT_PLAYER_UNIT_SPELL_CAST)
	TriggerRegisterPlayerUnitEvent(AbilityTrigger, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT)
	TriggerRegisterPlayerUnitEvent(AbilityTrigger, Player(i), EVENT_PLAYER_UNIT_SPELL_ENDCAST)
	TriggerRegisterPlayerUnitEvent(AbilityTrigger, Player(i), EVENT_PLAYER_UNIT_SPELL_FINISH)
end
Если вам не нужны все события, то ненужную строчку можете просто удалить.
Так как доступ к локальным переменным быстрее чем к глобальным, то включим дух оптимизаторства на спичках и определим переменную поближе к месту использования:
local ABILITYS = ABILITY ---@type table
И в завершении добавим действия триггеру:
TriggerAddAction(AbilityTrigger, function()
	local eventId = GetHandleId(GetTriggerEventId())
	local ability = ABILITYS[GetSpellAbilityId()]
	
	if ability ~= nil then
		if eventId == EventChannelId and ability.CHANNEL ~= nil then ability.CHANNEL()
		elseif eventId == EventCastId and ability.CAST ~= nil then ability.CAST()
		elseif eventId == EventEffectId and ability.EFFECT ~= nil then ability.EFFECT()
		elseif eventId == EventEndCastId and ability.ENDCAST ~= nil then ability.ENDCAST()
		elseif eventId == EventFinishId and ability.FINISH ~= nil then ability.FINISH()
		end
	end
end)
Как видите, код довольно таки прост, что зная шаблон комментировать его не вижу смысла
Весь код
ABILITY = {}
do
	local EventChannelId = GetHandleId(EVENT_PLAYER_UNIT_SPELL_CHANNEL)
	local EventCastId    = GetHandleId(EVENT_PLAYER_UNIT_SPELL_CAST)
	local EventEffectId  = GetHandleId(EVENT_PLAYER_UNIT_SPELL_EFFECT)
	local EventEndCastId = GetHandleId(EVENT_PLAYER_UNIT_SPELL_ENDCAST)
	local EventFinishId  = GetHandleId(EVENT_PLAYER_UNIT_SPELL_FINISH)
	
	local AbilityTrigger = CreateTrigger()
	for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
		TriggerRegisterPlayerUnitEvent(AbilityTrigger, Player(i), EVENT_PLAYER_UNIT_SPELL_CHANNEL)
		TriggerRegisterPlayerUnitEvent(AbilityTrigger, Player(i), EVENT_PLAYER_UNIT_SPELL_CAST)
		TriggerRegisterPlayerUnitEvent(AbilityTrigger, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT)
		TriggerRegisterPlayerUnitEvent(AbilityTrigger, Player(i), EVENT_PLAYER_UNIT_SPELL_ENDCAST)
		TriggerRegisterPlayerUnitEvent(AbilityTrigger, Player(i), EVENT_PLAYER_UNIT_SPELL_FINISH)
	end
	
	local ABILITYS = ABILITY ---@type table
	TriggerAddAction(AbilityTrigger, function()
		local eventId = GetHandleId(GetTriggerEventId())
		local ability = ABILITYS[GetSpellAbilityId()]
		
		if ability ~= nil then
			if eventId == EventChannelId and ability.CHANNEL ~= nil then ability.CHANNEL()
			elseif eventId == EventCastId and ability.CAST ~= nil then ability.CAST()
			elseif eventId == EventEffectId and ability.EFFECT ~= nil then ability.EFFECT()
			elseif eventId == EventEndCastId and ability.ENDCAST ~= nil then ability.ENDCAST()
			elseif eventId == EventFinishId and ability.FINISH ~= nil then ability.FINISH()
			end
		end
	end)
end

Добавление способностей

Основное правило добавления способностей - соблюдать шаблон и размещать их ниже блока Ability. Для примера создадим тестовую способность Ability_test:
ABILITY[FourCC('A000')] = {
	CHANNEL = function()
		print('EVENT_PLAYER_UNIT_SPELL_CHANNEL')
	end,
	CAST    = function()
		print('EVENT_PLAYER_UNIT_SPELL_CAST')
	end,
	EFFECT  = function()
		print('EVENT_PLAYER_UNIT_SPELL_EFFECT')
	end,
	ENDCAST = function()
		print('EVENT_PLAYER_UNIT_SPELL_ENDCAST')
	end,
	FINISH  = function()
		print('EVENT_PLAYER_UNIT_SPELL_FINISH')
	end
}
Если принцип работы понятен, то добавим ещё одну способность, более приближённую к настоящим реалиям:
do
	local DELAY             = 1
	local DAMAGE            = 100
	local EFFECT_CAST       = 'Abilities/Spells/Human/MassTeleport/MassTeleportCaster.mdl'
	local EFFECT_LIGHTNING  = 'Effect/Spell/Lightning.mdx'
	local GROUP             = CreateGroup()
	
	ABILITY[FourCC('ALig')] = {
		EFFECT = function()
			local caster  = GetTriggerUnit()
			local ability = GetSpellAbility()
			local level   = GetUnitAbilityLevel(caster, GetSpellAbilityId())
			local x, y    = GetSpellTargetX(), GetSpellTargetY()
			local radius  = BlzGetAbilityRealLevelField(ability, ABILITY_RLF_AREA_OF_EFFECT, level - 1)
			
			DestroyEffect(AddSpecialEffect(EFFECT_CAST, x, y))
			
			TimerStart(CreateTimer(), DELAY, false, function()
				DestroyEffect(AddSpecialEffect(EFFECT_LIGHTNING, x, y))
				GroupEnumUnitsInRange(GROUP, x, y, radius + 128)
				
				for index = BlzGroupGetSize(GROUP) - 1, 0, -1 do
					local target = BlzGroupUnitAt(GROUP, index)
					if UnitAlive(target) and IsUnitInRangeXY(target, x, y, radius) and not IsUnitType(target, UNIT_TYPE_FLYING) then
						UnitDamageTarget(caster, target, DAMAGE * level, false, true, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
					end
				end
				GroupClear(GROUP)
				
				DestroyTimer(GetExpiredTimer())
			end)
		end
	}
end

Заключение

Как видите, в оптимизации нет ничего сложного, но если всётаки у вас остались вопросы или появились предложения, то можее смело оставлять их в комментариях.
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
0
12
5 лет назад
0
А можно отдельно файл импорта Lightning.mdx , для тех кто на 1.26
0
27
5 лет назад
Отредактирован MpW
0
хм.. думал там обычная молния
2
29
5 лет назад
2
А можно отдельно файл импорта Lightning.mdx
Держите, у автора там ещё много хороших эффектов.
1
27
5 лет назад
Отредактирован nazarpunk
1
Daro, на хайве заметил много молнии-эффектов в разделе SFX => effect
0
1
4 года назад
0
Хорошая статья и всё понятно. На основе данного метода, я решил проверить свои силы и запились систему читов на карте. Простенькую - добавить хп, маны, статов и прочее для персонажа. И наткнулся сразу на проблему TriggerRegisterPlayerChatEvent почему-то не работает на lua. Это событие просто не срабатывает. Может кто-нибудь из опытных скинуть ссылку на api-методы lua? Ну и конкретно метод для отлавливания события в чате.
0
1
4 года назад
Отредактирован BARZZ
0
Быть может, не совсем по теме, но все же.
При создании Спец Эффекта (как через встроенные функции игрового редактора так и посредством lua-кода) заметил следующую беду: звук спецэффекта проигрывается несколько раз (в зависимости от его продолжительности). Визуальное отображение исчезает после одного проигрывания (как и задумано). Триггерное удаление эффекта или установка минимальной продолжительности существования не помогают.
Один из используемых эффектов: 'Abilities/Spells/Human/ManaFlare/ManaFlareBoltImpact.mdl' (звук проигрывается 7 раз).
Подобные проблемы возникают, как в картах с массой триггеров, так и в пустых тестовых.
Используется клиент Reforge.
0
10
4 года назад
0
BARZZ:
тандерклэп тоже проигрывается несколько раз. Мне кажется, что это просто такой звук у эффекта.
0
32
4 года назад
0
BARZZ, на рефордже и на старых картах багаются звуки эффектов, это норма, луа тут не причем
0
24
4 года назад
0
звук спецэффекта проигрывается несколько раз
Это известный баг в рефорже.
0
1
4 года назад
Отредактирован BARZZ
0
Bergi_Bear:
BARZZ, на рефордже и на старых картах багаются звуки эффектов, это норма, луа тут не причем
Да и на новых звук повторяется частенько.
У стандартных заклинаний такого счастья не наблюдается. Я так понимаю, эта проблема решаема.
Поделится кто мудростью Рефорджа?
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.