Типы целей в Warcraft 3

Добавлен , опубликован
Раздел:
Основы
Не видел на форуме анализа целей Warcraft-а. Для меня всегда логика этой большой таблички в РО
(WRONG IMAGE URL - imageshack)
была неинтуитивной.
Я решил разобраться и выкладываю результат, возможно кто-то найдёт что-то новое для себя.
За разрешённые цели/типы целей отвечают такие поля в РО, как:
Характеристики - Разрешённые цели (способности) - разреш. цели данного спела
Бой - относится к типу целей (войска) - тип цели, к которому отнсоится данный юнит
Характеристики - Классификация войск (войска) - тип цели, к которому относится данный юнит (доп.)
Бой - тип цели (разрушаемые объекты) - тип цели, к которому относится данный разрушаемый объект
Бой - АтакаN - допустимые цели (войска) - разреш. цели АтакиN юнита
Бой - АтакаN - радиус поражения (цели) (войска) - разреш. цели сплэша юнита
И такие функции в common.j, как IsUnitType; UnitAddType; UnitRemoveType
В первую очередь все цели делятся на 3 группы: Предметы, *Разрушаемые объекты*, Юниты.
Все юниты делятся на 3 группы: Духи/Не духи; Воздушные/Наземные; Здания/Не здания. Они сочетаются по принципу "или". Т.е. если установить разрешённые цели (для заклинания или для атаки юнита, далее - Р.Ц.) Духи, Здания, Наземные, то это покроет все основные б.ед.

Типы целей боевых единиц

Если установить юниту "Бой - относится к типу целей" ("Бой - относится к типу целей" или "Бой - тип цели (разрушаемые объекты)" далее - Т.Ц.) Духи, Здания, Наземные, это НЕ будет означать, что он является наземным-духовным-зданием, т.е. это НЕ отдельный Т.Ц. Это будет означать, что он относится и к Духам и к Зданиям и к Наземным.
Из чего следует, что Р.Ц. только Воздушные Здания - не существует и указать её нельзя!
Воздушные Здания - это все Воздушные + все Здания. Также не существует Воздушных Духов, Духов Зданий и т.п.
Ещё раз Т.Ц. - это каким типом цели явл. сам юнит/разрушаемый объект; Р.Ц. - это разрешённые цели атаки/заклинания
Также нужно сказать, что если у юнита в Т.Ц. не стоит ни Дух, ни Наземные, ни Здание - это означает, что он НЕ является целью типа "юнит". Ни один спелл, ни один юнит не смогут его атаковать его или кинуть на него спелл.
Установка галки Герой, по моим изысканиям, ни к чему не приводит. Героем являются те, что созданы в РО из основы любого героя. Эту классификацию нельзя ни убрать, ни добавить с помощью UnitAdd/RemoveType.
Также установка Т.Ц. у юнитов: Живые, Мёртвые, Сам воин, Не сам воин, Уязвимые, Неуязвимые, Союзники, Нейтральные, Друзья, Враги, Войска Игрока не даёт ничего (НЕ путать с Р.Ц.)
Друзья, Враги, Нейтральный, Союзник, Войска Игрока определяются только во время игры динамически, если юнит вражеский, значит и Т.Ц. у него Враги. В Союзников входят только союзники игрока, войска игрока не входят в союзников. На сколько я понял, Друзья = Союзники + Войска Игрока.
Для того, чтобы являться Зданием нужно: а) установленные в РО галки: "Является зданием" и Т.Ц. "Здание". Триггерный вызов call UnitAddType(u, UNIT_TYPE_STRUCTURE) НЕ даёт никакого эффекта.
Для того, чтобы являться Духом: установленный Т.Ц. Духи. Установка "Характеристики - Классификация войск" : "Страж" - не даёт ничего.
Галки в Р.Ц. Неуязвимый и Уязвимый относятся и к Предметам, и к Разрушаемым объектам, и к Юнитам.
Установка Т.Ц. "Нет" сделает невозможным указание данного юнита как цели любого заклинания или любой атаки. Насколько я понял, "Нет" равносильно тому, чтобы просто убрать все галки в Т.Ц.

Самоубийцы, Древа, Механика

Для того, чтобы являться Самоубийцей: установленный в Классификации: Самоубийца. Установка в Т.Ц. Самоубийца не даёт ничего.
С Древом и Механикой тоже самое как и с Самоубийцей.
Эти три Р.Ц. являются единственными действующими "по принципу И". Т.е. они сужают область допустимых целей. Если установить галки Древо, Самоубийца, это будет значить, что заклинание или атака действует только на тех, кто является и Древом и Самоубийцей. Это Можно отлично использовать и об этом позже.

Р.Ц. заклинаний по умолчанию и их изменение.

У большинства (у всех не проверял) заклинаний насылаемых на юнита, если не устанавливать никаких флажков, это будет равносильно
следующему:
(WRONG IMAGE URL - imageshack)
Изменение Р.Ц. происходит по группам. К примеру, если установить только галку Древа и никаких других, то это будет равносильно
следующему:
(WRONG IMAGE URL - imageshack)
Устанавливая 1 флажок в группе как бы убираются все остальные.
Группы:
  1. Духи
  2. Герои / Не Герои
  3. Сам воин / Не сам воин
  4. Наземные / Воздушные
  5. Живые / Мёртвые
  6. Неуязвимые / Уязвимые
  7. Здания
  8. Друзья / Враги / Союзники / Войска игрока / Нейтральные
  9. Самоубийцы / Не самоубийцы
  10. Древа / Не Древа
  11. Механизмы / Организмы

Т.Ц. разрушаемых объектов

К Т.Ц. разрушаемых объектов относятся: Мосты, Декорации, Прочее, Деревья, Стены. Они НЕ сочетаются! Т.е. декорация с Т.Ц.: Мосты, Стены - не будет ни мостом, ни стеной, ни мостостеной. В свою очередь Р.Ц. атак, заклинаний, направленных на разрушаемые объекты, сочетаются по принципу или. Р.Ц. атаки Деревья, Мосты - значит, что юнит может атаковать мосты и деревья. По умолчанию атаковать Т.Ц. "Декорации" не может никто, поэтому им никто урон и не наносит. Разрушаемые объекты с Т.Ц. "Декорации" - это Вулкан, всякие троны и т.п. Если поставить эту галку в Р.Ц. у юнита, Вулкан можно будет снести.
Если у разрушаемого объекта не устанавливать в Т.Ц. ничего, то он не будет являться "разрушаемым объектом" как целью, также как и юнит, т.е. его нельзя ни атаковать, ни кинуть на него спелл.
При определённых условиях разрушаемые объекты даже с установленным Т.Ц. не могут стать целью заклинаний. Обычно это лечится установкой галки "Графика - Можно выбрать в игре".

Собственная система целей

Важно отметить: при попытке установить разрушаемым объектам Т.Ц. юнита, например "самоубийца", никакого эффекта не будет. Также как и юнитам Т.Ц. разрушаемого объекта н.р. "прочее". То же самое с Т.Ц. "предмет". Видимо в Варе внутри стоит проверка на то, является ли данная цель разрушаемым объектом, боевой единицей или предметом на самом деле.
Тем не менее, часто требуется запретить или разрешить применение заклинания только на определённых юнитов. Обычно для этого используется самая "бесполезная" классификация: Самоубийцы (в Доте - Древа).
Тем не менее, для большой карты или проекта, одной группы войск может быть не достаточно. Как быть?
Классификация Древа, на сколько я понял, отличается только тем, что строитель здания с такой классификацией исчезает. Это легко сделать триггерно, освободив все Древа от такой классификации. Т.о. Древа становится доступна, как доп. классификация для войск. Выходит 2 типа искусственных целей.
Но и этого может быть не достаточно. В этом случае можно создать 3юю, а именно чётко указывая у каждой атаки/заклинания цели для Древ - Древа, Не Самоубийцы, для Самоубийц - Самоубийцы, Не Древа. И 3ий тип целей будет: Древа, Самоубийцы. (Как вы помните они сочетаются по принципу "и")
Но и этого может быть не достаточно. Можно задействовать 3ий, сочетающийся по принципу "и", тип целей: Механика. Но тут придётся потрудится и у всех дефолтовых способностей, юнитов и т.п. убрать чистые Р.Ц. Механика и заменить их на Механика, Не Древа, Не Самоубийцы. Т.о. становятся доступны следующие искусственные типы целей/разрешённые цели:
Самоубийца?Древо?Механика?
+ + +
+ + -
+ - +
+ - -
- + +
- + -
- - +
- - -
Т.о. число доступных типов выходит: 2 в степени 3 = 8; один из которых: Не Древа, Не Самоубийцы, Организмы - тип по умолчанию боевых единиц.
При этом оговорка, ещё раз, все цели должны быть прописаны чётко, если где-то встретится НЕ чёткий тип: Древа, Механика, то под него подпадут 2 искусственных типа: Древа, Механика, Не самоубийцы и Древа, Механика, Самоубийцы (хотя возможно будут случаи когда и такое полезно). Цели должны быть чётко прописаны везде.
Также большим плюсом Самоубийц, Древ, Механики является то, что эти классификации можно добавить триггерно.

Звуки и сообщения об ошибках при неверном выборе цели

При неверном выборе цели может быть проигран звук и выдано сообщение об ошибке с подсказкой: какова верная цель. На это я сильное внимание не обращал, могу только сказать точно, что для юнитов, не являющихся целью типа "юнит", т.е. у тех, у которых отсутствуют данные в поле Т.Ц. (см. пункт "Типы целей боевых единиц" данной статьи) НЕ будет выдано никакого сообщения об ошибке и НЕ будет проигран никакой звук, как будто бы вы вообще не нажали мышью.
В общем и целом сообщения об ошибочном выборе цели неадекватны. Особенно при сочетании таких Р.Ц. как Древо, Самоубийца, Механика. Тем не менее это можно условно вылечить в Игровом интерфейсе, указав наиболее абстрактные сообщения для некоторых сообщений.

Заключение

Спасибо, если прочли столько много букоф. Буду благодарен за любые уточнения, исправления.
Тестировано на способности "Молот бурь" и некоторых других.
На что влияет галка Рельеф - я так и не понял.
`
ОЖИДАНИЕ РЕКЛАМЫ...
0
27
2 года назад
Отредактирован MpW
0
вот код проверки атаки, можно навести мышью на любого юнита, и вам покажет может ли герой атаковать. по правде говоря, можно было проверить и приказом. Но тот сбивает приказ. Работал только с полями таргета атаки, а вот доступ к таргетам абилки 'atar' недоступно, возвращает ноль.
код
есть один нюанс. обозначение цели как герой/не герой, древо/не древо, техника/не техника, самоубийцы/не самоубийцы лучше чекать классификацией. Не работает. На этом все.
do
    local InitGlobalsOrigin = InitGlobals -- хукаем функцию InitGlobals

	function InitGlobals()
		InitGlobalsOrigin()
        
	function IsFlagSet(flags, pos)
		if pos == nil then
			return false
		else
			return flags >> pos & 1 == 1
		end
	end
		
	pos_unit = {}
	pos_destructible = {}
	classification = {}
	string_class = {}
	pos_destructible[1]=7 --tree
	pos_destructible[2]=8 --wall
	pos_destructible[3]=9 --debris
	pos_destructible[4]=10 --decoration
	pos_destructible[5]=11 --bridge
	
	pos_unit[1]=1 --ground
	pos_unit[2]=2 --air
	pos_unit[3]=3 --structure
	pos_unit[4]=4 --ward
	pos_unit[5]=23 --non-hero 
	pos_unit[6]=22 --hero
	pos_unit[7]=26 --Organic
	pos_unit[8]=27 --Mechanical 
	pos_unit[9]=28 --non-suicidal
	pos_unit[10]=29 --suicidal
	pos_unit[11]=30 --non-ancient
	pos_unit[12]=31 --ancient
	pos_unit[13]=24 --alive
	pos_unit[14]=25 --dead
	pos_unit[15]=20 --Vulnerable 
	pos_unit[16]=21 --Invulnerable
	pos_unit[17]=17 --not self
	pos_unit[18]=12 --self
	pos_unit[19]=14 --friend
	pos_unit[20]=16 --enemy
	
	pos_unit[21]=13 --Player Units
	pos_unit[22]=15 --Neutral => указывают в UNIT CLASSIFICATION

	classification[5] = UNIT_TYPE_HERO
	classification[7] = UNIT_TYPE_MECHANICAL
	classification[9] = UNIT_TYPE_SAPPER
	classification[11] = UNIT_TYPE_ANCIENT
	classification[13] = UNIT_TYPE_DEAD

	string_class[5] = " non-hero: "
	string_class[6] = " hero: "
	string_class[7] = " mechanical: "
	string_class[8] = " organic: "
	string_class[9] = " suicidal: "
	string_class[10] = " non-suicidal: "
	string_class[11] = " non-ancient: "
	string_class[12] = " ancient: "
	string_class[13] = " alive: "
	string_class[14] = " dead: "
	string_class[15] = " vulnerable: "
	string_class[16] = " invulnerable: "
	string_class[17] = " not self: "
	string_class[18] = " self: "
	string_class[19] = " friend: "
	string_class[20] = " non-friend: "
	string_class[21] = " player unit: "
	string_class[22] = " neutral: "
	
	local string_bug = ""
	
	function comp_flags(flags1,flags2,pos)
		local p1 = IsFlagSet(flags1, pos)
		local p2 = IsFlagSet(flags2, pos)		
		
		--условие истино: 2 параметра положительно, или если 2 параметра не указано
		--условие ложно: если 2 параметра не соотвествуют друг другу
		if p1==p2 then
			--если два параметра положительны
			if p1 then
				string_bug = string_bug.."1"
			else
				string_bug = string_bug.."0"
			end
			return true
		else
			string_bug = string_bug.."0"
			return false
		end

	end	
	function comp_bool(flags1,bool,pos)
			
		local p1 = IsFlagSet(flags1, pos_unit[pos])

		if p1 ~= bool and p1 then
			string_bug = string_bug.."|cffff0000"..string_class[pos].."|r".."0"
			return false
		elseif p1 == bool and p1 then
			string_bug = string_bug.."|cff00ff00"..string_class[pos].."|r".."1"
		end
		return true
	end
	function IsUnitFriend(unit,tg)
		return IsUnitAlly(tg, GetOwningPlayer(unit)) and unit~=tg
	end
	function IsItUnitPlayer(unit,tg)
		return GetOwningPlayer(unit)==GetOwningPlayer(tg) and unit~=tg
	end
	function IsItUnitNeutral(unit)
		local flags = BlzGetUnitIntegerField(unit, UNIT_IF_UNIT_CLASSIFICATION)
		return IsFlagSet(flags,8)
	end
	function CheckFieldAbility(unit,field,rawcode,lv)
		local ability = BlzGetUnitAbility(unit, rawcode)
		local param = ConvertAbilityIntegerLevelField(field)
    
		return BlzGetAbilityIntegerLevelField(ability, param, lv)
	end
	
	function flages_target(flags1,flags2,unit,tg,IsItAttack)

		local max = 22 --макс параметров типов цели
		local b = true
		local c = 0
		
		if IsItAttack then
			max = 12
		end
		
		--проверка по типу "или"
		--если один из параметров цели (ground,air,structure,ward) соотвествует атаке.
		--если юнит не может атаковать ни одной из 4 целей
		string_bug = ""
		for a=1,4 do
			if not comp_flags(flags1,flags2,pos_unit[a]) then
				c = c + 1
			end
		end
		
		if c == 4 then
			b = false
			string_bug = "|cffff0000ground,air,structure,ward:|r "..string_bug
		else
			string_bug = "|cff00ff00ground,air,structure,ward:|r "..string_bug			
		end
		
		--далее идет проверка по типу "и"
		for i=5,max,2 do 
			if i < max then
				if not comp_bool(flags1,IsUnitType(tg, classification[i]),i+1) then
					b = false
				end
				if not comp_bool(flags1,not IsUnitType(tg, classification[i]),i) then
					b = false
				end
			end
		end
		
		if IsItAttack then
			if BlzIsUnitInvulnerable(tg) then
				string_bug=string_bug.." |cffff0000invulnerable:|r 0"
				b = false
			end
		else
			--vulnerable/invulnerable
			if not comp_bool(flags1,not BlzIsUnitInvulnerable(tg),15) then
				b = false
			end
			if not comp_bool(flags1,BlzIsUnitInvulnerable(tg),16) then
				b = false
			end
			
			--vulnerable/invulnerable
			if not comp_bool(flags1,not BlzIsUnitInvulnerable(tg),15) then
				b = false
			end
			if not comp_bool(flags1,BlzIsUnitInvulnerable(tg),16) then
				b = false
			end
			--not self/self
			if not comp_bool(flags1,unit~=tg,17) then
				b = false
			end
			if not comp_bool(flags1,unit==tg,18) then
				b = false
			end
			--friend/enemy
			if not comp_bool(flags1,IsUnitFriend(unit,tg),19) then
				b = false
			end
			if not comp_bool(flags1,IsUnitEnemy(tg, GetOwningPlayer(unit)),20) then
				b = false
			end
			--player units
			if not comp_bool(flags1,IsItUnitPlayer(unit,tg),21) then
				b = false
			end
			--neutral
			if not comp_bool(flags1,IsItUnitNeutral(unit),22) then
				b = false
			end
		end
		
		print(string_bug)
				
		return b
	end
	
		
		function IsItPossibleToAttackTheTarget(unit, target)
		
			--если может атаковать
			if GetUnitAbilityLevel(unit,FourCC('Aatk'))>0 and target~=nil then

				local b1 = BlzGetUnitWeaponBooleanField(unit, UNIT_WEAPON_BF_ATTACKS_ENABLED, 0)
				local b2 = BlzGetUnitWeaponBooleanField(unit, UNIT_WEAPON_BF_ATTACKS_ENABLED, 1)
				if b1 then
					local flags_attacker_1 = BlzGetUnitWeaponIntegerField(unit, UNIT_WEAPON_IF_ATTACK_TARGETS_ALLOWED, 0)
					local flags_target = BlzGetUnitIntegerField(target, UNIT_IF_TARGETED_AS)
					--local flags_spelltargetscaster = CheckFieldAbility(unit,FourCC('atar'),FourCC('AHad'),0) --поле таргета абилок не работает, вернет ноль

                    if not flages_target(flags_attacker_1,flags_target,unit,target,true) then
						print("|cffff0000"..GetUnitName(unit).." не может атаковать цель|r")
					else
						print("|cff00ff00"..GetUnitName(unit).." может атаковать цель|r")
                    end
				end
			end
		end
    
		local Hpal = CreateUnit(Player(0),FourCC('Hpal'),0,0,0)
    
		TimerStart(CreateTimer(),1.00,true,function()
			IsItPossibleToAttackTheTarget(Hpal, BlzGetMouseFocusUnit())
		end)
    end
end
Чтобы оставить комментарий, пожалуйста, войдите на сайт.