Добавлен , опубликован
Статья
Раздел:
Триггеры и объекты
Так как игра не позволяет нам применять произвольную способность от произвольного юнита, была придумана хитрая система: сделать скрытого юнита и кастовать от его лица. Поэтому мы сразу его и создадим, для чего скопируем "(nsno) Полярная сова" с равкодом "dumy" и изменим следующие поля:
ucbs (castbsw) Графика - Анимация: обратный ход броска 0
udtm (death) Графика - Время смерти (сек.) 0.10
ushu (unitShadow) Графика - Отображение тени (боевая единица) Нет
umdl (file) Графика - Файл модели .mdl
ucol (collision) Пути - Физический размер 0.00
uine (inEditor) Редактор - Размещаемо в Редакторе Нет
uabi (abilList) Способности - Возможные способности (Aloc) Москиты
umpr (regenMana) Характеристики - Восстановление маны 1000.00
umpi (mana0) Характеристики - Изначальное количество маны 1000
umpm (manaN) Характеристики - Максимум маны 1000
upoi (points) Характеристики - Опыт, начисляемый за уничтожение 0
usid (sight) Характеристики - Радиус обзора (днём) 0
usin (nsight) Характеристики - Радиус обзора (ночью) 0

Применение

Для начала создадим тестовую способность с минимальным количеством кода и проверим её работоспособность.
local SpellGroup   = CreateGroup()
local SpellTrigger = CreateTrigger()
local SpellId      = FourCC('spel')
for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
	TriggerRegisterPlayerUnitEvent(SpellTrigger, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT)
end
TriggerAddAction(SpellTrigger, function()
	if GetSpellAbilityId() ~= SpellId then return end
	local caster      = GetTriggerUnit()
	local casterOwner = GetOwningPlayer(caster)
	local x, y        = GetSpellTargetX(), GetSpellTargetY()
	
	local radius = BlzGetAbilityRealLevelField(GetSpellAbility(), ABILITY_RLF_AREA_OF_EFFECT, 0)
	GroupEnumUnitsInRange(SpellGroup, x, y, radius)
	for index = BlzGroupGetSize(SpellGroup) - 1, 0, -1 do
		local target = BlzGroupUnitAt(SpellGroup, index)
		if UnitAlive(target) and IsUnitEnemy(target, casterOwner) then
			KillUnit(target) -- впоследствии мы заменим эту функцию на даммикаст
		end
	end
	GroupClear(SpellGroup)
end)
Теперь можно кастовать самую популярную способность - оглушение. Скопируем "(ACcb) Ледяная стрела" с равкодом "stun" и изменим следующие поля:
amat (Missileart) Графика - Анимация дистанционной атаки удалить всё
Htb1 (DataA1) ABILITY_RLF_DAMAGE_HTB1 Данные - Урон 0.00
amcs (Cost1) ABILITY_ILF_MANA_COST Характеристики - Затрачиваемая мана 0
aсdn (Cool1) ABILITY_RLF_COOLDOWN Характеристики - Перезарядка 0.00
atar (targs) Характеристики - Разрешённые цели снять все галки
Чтобы каждый раз не создавать даммиков, мы поступим хитро - создадим одного даммика за "Нейтрально-пассивного" и будем двигать его к цели. Таки способом мы можем кастовать в цикле и не испытывать проблем с видимостью. "Нейтрально-пассивный" видит всю карту.
local dummy  = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), FourCC('dumy'), 0, 0, 0)
local stunId = FourCC('stun')
UnitAddAbility(dummy, stunId)
local stunAbility = BlzGetUnitAbility(dummy, stunId)

---@param target unit
---@param durationNormal real
---@param durationHero real
function DummyCastStun(target, durationNormal, durationHero)
	SetUnitX(dummy, GetUnitX(target))
	SetUnitY(dummy, GetUnitY(target))
	BlzSetAbilityRealLevelField(stunAbility, ABILITY_RLF_DURATION_NORMAL, 0, durationNormal)
	BlzSetAbilityRealLevelField(stunAbility, ABILITY_RLF_DURATION_HERO, 0, durationHero)
	IssueTargetOrderById(dummy, 852095, target) -- thunderbolt
end
Таким способом используя всего одного даммика можно накладывать баффы, которые не наносят урон. С уроном всё немного сложнее ибо ели юнит умрёт, то убийцей игра посчитает "Нейтрально-пассивного" и не даст награду за убийство в купе с опытом.
Если всёже вам понадобится урон, то рассмотрим следующий пример. Скопируем "(Aenr) Гнев деревьев (враг 1)" с равкодом "root" и настроим следующие поля:
amcs (Cost1) ABILITY_ILF_MANA_COST Характеристики - Затрачиваемая мана 0
aсdn (Cool1) ABILITY_RLF_COOLDOWN Характеристики - Перезарядка 0.00
atar (targs) Характеристики - Разрешённые цели снять все галки
Пот тому же принципу, что и в прошлый раз, создадим даммика каждому игроку и выдадим им способность:
local dummys      = {}
local rootAbility = {}
local rootId      = FourCC('root')
for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
	dummys[i] = CreateUnit(Player(i), FourCC('dumy'), 0, 0, 0)
	UnitAddAbility(dummys[i], rootId)
	rootAbility[i] = BlzGetUnitAbility(dummys[i], rootId)
end

---@param player player
---@param target unit
---@param durationNormal real
---@param durationHero real
function DummyCastRoot(player, target, durationNormal, durationHero, damage)
	local id      = GetPlayerId(player)
	local dummy   = dummys[id]
	local ability = rootAbility[id]
	
	SetUnitX(dummy, GetUnitX(target))
	SetUnitY(dummy, GetUnitY(target))
	BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DURATION_NORMAL, 0, durationNormal)
	BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DURATION_HERO, 0, durationHero)
	BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DAMAGE_PER_SECOND_EER1, 0, damage)
	UnitShareVision(target, player, true)
	IssueTargetOrderById(dummy, 852171, target) -- entanglingroots
	UnitShareVision(target, player, false)
end
Обратите внимание на UnitShareVision, всё дело в том, что юниты не могут кастовать в цель, которую не видят. Поэтому и приходится проделать трюк с предоставлением видимости. Так что будте осторожны, если в другом заклинании вы открываете видимость над юнитом, то UnitShareVision(target, player, false) заберёт её обратно.
Осталось разобрать те случаи, когда невозможно использовать одного юнита для всех кастов. Например заклинания требующие поддержки (channel) или с задержкой перед кастом. Тогда нам придётся уничтожать даммиков после использования. К счастью это несложно:
local dummy       = CreateUnit(casterOwner, FourCC('dumy'), x, y, 0) -- создаём дамми
UnitAddAbility(dummy, FourCC('ACfs')) -- даём юниту способность "Огненный столб"
IssuePointOrderById(dummy, 852488, x, y) -- отдаём приказ flamestrike
UnitApplyTimedLife(dummy, FourCC('BTLF'), 10) -- устанавливаем юниту время жизни

Заключение

Система дамми-каста довольно проста, но если всёже у вас останутся вопросы, то можете смело задавать их в комментариях. Также можете скачать карту и поэксперементировать.
Код
do
	local InitGlobalsOrigin = InitGlobals
	function InitGlobals()
		InitGlobalsOrigin()
		
		do
			local dummy  = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), FourCC('dumy'), 0, 0, 0)
			local stunId = FourCC('stun')
			UnitAddAbility(dummy, stunId)
			local stunAbility = BlzGetUnitAbility(dummy, stunId)
			
			---@param target unit
			---@param durationNormal real
			---@param durationHero real
			function DummyCastStun(target, durationNormal, durationHero)
				SetUnitX(dummy, GetUnitX(target))
				SetUnitY(dummy, GetUnitY(target))
				BlzSetAbilityRealLevelField(stunAbility, ABILITY_RLF_DURATION_NORMAL, 0, durationNormal)
				BlzSetAbilityRealLevelField(stunAbility, ABILITY_RLF_DURATION_HERO, 0, durationHero)
				IssueTargetOrderById(dummy, 852095, target) -- thunderbolt
			end
		end
		
		do
			local dummys      = {}
			local rootAbility = {}
			local rootId      = FourCC('root')
			for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
				dummys[i] = CreateUnit(Player(i), FourCC('dumy'), 0, 0, 0)
				UnitAddAbility(dummys[i], rootId)
				rootAbility[i] = BlzGetUnitAbility(dummys[i], rootId)
			end
			
			---@param player player
			---@param target unit
			---@param durationNormal real
			---@param durationHero real
			function DummyCastRoot(player, target, durationNormal, durationHero, damage)
				local id      = GetPlayerId(player)
				local dummy   = dummys[id]
				local ability = rootAbility[id]
				
				SetUnitX(dummy, GetUnitX(target))
				SetUnitY(dummy, GetUnitY(target))
				BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DURATION_NORMAL, 0, durationNormal)
				BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DURATION_HERO, 0, durationHero)
				BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DAMAGE_PER_SECOND_EER1, 0, damage)
				UnitShareVision(target, player, true)
				IssueTargetOrderById(dummy, 852171, target) -- entanglingroots
				UnitShareVision(target, player, false)
			end
		end
		
		-- Spell
		local SpellGroup   = CreateGroup()
		local SpellTrigger = CreateTrigger()
		local SpellId      = FourCC('spel')
		for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
			TriggerRegisterPlayerUnitEvent(SpellTrigger, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT)
		end
		TriggerAddAction(SpellTrigger, function()
			if GetSpellAbilityId() ~= SpellId then return end
			local caster      = GetTriggerUnit()
			local casterOwner = GetOwningPlayer(caster)
			local x, y        = GetSpellTargetX(), GetSpellTargetY()
			
			local dummy       = CreateUnit(casterOwner, FourCC('dumy'), x, y, 0) -- создаём дамми
			UnitAddAbility(dummy, FourCC('ACfs')) -- даём юниту способность "Огненный столб"
			IssuePointOrderById(dummy, 852488, x, y) -- отдаём приказ flamestrike
			UnitApplyTimedLife(dummy, FourCC('BTLF'), 10) -- устанавливаем юниту время жизни
			
			local radius = BlzGetAbilityRealLevelField(GetSpellAbility(), ABILITY_RLF_AREA_OF_EFFECT, 0)
			GroupEnumUnitsInRange(SpellGroup, x, y, radius)
			for index = BlzGroupGetSize(SpellGroup) - 1, 0, -1 do
				local target = BlzGroupUnitAt(SpellGroup, index)
				if UnitAlive(target) and IsUnitEnemy(target, casterOwner) then
					DummyCastStun(target, 5, 3)
					DummyCastRoot(casterOwner, target, 5, 3, 10)
				end
			end
			GroupClear(SpellGroup)
		end)
	
	end
end
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
28
Ману у дамми можно занулить, а требования способности менять в коде. Опыт не нужно трогать, юнит же неуязвим.
Вместо .mdl можно поставить просто _.
Укажи поля CastPoint, Fly Height, Movement Type, Collision Size. Они тоже важны.
33
Отличная статья, всё кратко и по полочкам, а пехотинец с последней гифки побежал изучать Луа
31
Ману у дамми можно занулить, а требования способности менять в коде.
Если уже можно настроить в редакторе, почему это не сделать.
Опыт не нужно трогать, юнит же неуязвим.
Это же очки статистики в послеигровом меню, зачем лишний раз их накапливать.
Вместо .mdl можно поставить просто _.
Сколько не видел даммиков, везде .mdl, решил не ломать традицию
Укажи поля CastPoint, Fly Height, Movement Type, Collision Size. Они тоже важны.
Они у исходного юнита уже правильно выставлены.
28
Это же очки статистики в послеигровом меню, зачем лишний раз их накапливать.
Это же опыт за убийство, не? А тут даммик, которого нельзя убить.
Если уже можно настроить в редакторе, почему это не сделать.
Просто всё равно у дамми спелов манакост и кд на 0.
31
Это же опыт за убийство, не?
Нет, это значение, которое можно получить через GetUnitPointValue() и нужное для послеигровой статистики.
Просто всё равно у дамми спелов манакост и кд на 0.
Я даже в статье кастовал "Огненный столб" не сбивая ему манакост.
28
GetUnitPointValue()
Это же вообще другое поле в РО. Или это опять кривой перевод?
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.