Урок Рецепт руны с анчоусами

  • Автор темы Автор темы Илья
  • Дата начала Дата начала

Илья

Друзья CG
25 Сен 2015
2,348
41
Мне известно три способа создания своей руны.

Первый - это через создание вещицы-руны.
Однако у такого способа есть огромный недостаток: необходимо иметь пустой слот в инвентаре, чтобы поднять руну, да и к тому же в этом пустом слоте на миг будет мелькать иконка руны. В следствие этого недостатка я не буду приводить пример реализации этого метода, так как есть более красивые.


Второй - в момент активации руны заменять ее модификатор на свой. Весьма элегантный метод, но обладает своими минусами:не можем изменить модельку руны; руны бывают разные и работать надо будет с каждой; на миг вы будете видеть модификатор оригинальной руны (я не знаю, можно ли его как-то перехватить). Этот способ я так же не буду рассматривать, ибо я хочу "чистый" продукт.

И вот тут появляется третий способ - создание монстра-руны.
Единственный его минус заключается в том, что мы будем атаковать руну, что весьма непривычно.


Итак, для начала подготовим свой модификатор:
Создаем в папочке "vscripts" папочку "modifiers", а в ней создаем файл с расширением "lua" и иминем вашего будущего модификатора. Я придумал такое: modifier_one_punch_speed.

Далее прописываем в него все необходимое.
Код:
--объявляем наш модификатор как объект-класс
modifier_one_punch_man_speed = class({})

--возвращаем false (лож) на вопросы о скрывании модификатора с глаз
--мы ведь хотим видеть красивую иконочку над показателями здоровья
function modifier_one_punch_man_speed:IsHidden()
	return false
end

--эта штука будет на любые распросы о типе нашего модификатора
--говорить, что он на скорость атаки
--все типы можете в офф документации глянуть
function modifier_one_punch_man_speed:DeclareFunctions()
	return { MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT }
end

--тут возвращаем имя иконочки
--не буду рассказывать, как делать иконочку
--можете использовать тут имена стандартных абилок
--например вместо моего "one_punch_man" взять "brewmaster_drunken_haze"
function modifier_one_punch_man_speed:GetTexture()
  return "one_punch_man"
end

--когда нас спросят, а сколько скорости то плюсануть
--мы ответим 99999
function modifier_one_punch_man_speed:GetModifierAttackSpeedBonus_Constant()
		return 99999
end

--возвращаем true на любые вопросы о том
--удалять ли модификатор, если носитель умрет
--можете и false поставить, чтобы он не пропадал после смерти
--как хотите
function modifier_one_punch_man_speed:RemoveOnDeath()
	return true
end

Советую удалить все мои комментарии из него, дабы глаза не мозолили и снизить вероятность возможных ошибок (комментарии начинаются с --).

Далее нам надо подумать о респавне руны и коде, функции, что будет активировать наш модификатор на убийце будущего юнита-руны.

Поэтому идем в папочку "scripts", там создаем файлик lua. В нем будет логика респавна да и активации модификатора. Я такой обычно называю ai.
--во первых подключаем наш модификатор сюда
LinkLuaModifier("modifier_one_punch_man_speed", "modifiers/modifier_one_punch_man_speed.lua", LUA_MODIFIER_MOTION_NONE )

--дале делаем функцию респавна руны
function RuneRespawn(data)

--копируем ссылку на юнита
local moob = data.caster
--копируем его имя
local name = moob:GetUnitName()
--копируем точку его спавна на карте в виде координат вектора
local SpawnLoc = moob.vSpawnLoc


--в случае, если точка спавна пустая была
--бацаем ту, в которой умер юнит
if (SpawnLoc == nil) then
SpawnLoc = moob:GetOrigin()
end

--запусаем таймер, про таймеры можете найти видос неплохой на ютюбе и форуме
--я поставил таймер на 15 сек
--то есть через 15 сек после смерти руна возрадится снова
GameRules:GetGameModeEntity():SetContextThink(string.format("CreatureThink_%d",moob:GetEntityIndex()),
function()
local unit = CreateUnitByName(name, SpawnLoc, true, nil, nil, DOTA_TEAM_NEUTRALS )
unit.vSpawnLoc = SpawnLoc
end,
15)

end

--функция активации модификатора
function AddModifier(data)
--копируем ссылку на атакующего
local Attacker = data.attacker
--вешаем на него наш модификатор на 15 секунд
Attacker:AddNewModifier( data.caster, nil, "modifier_one_punch_man_speed", {duration = 15} )
end

Теперь идем в папочку "scripts", там в папочку "npc", а там в npc_abilities_custom.txt
Создаем две абилки:

Код:
	"ability_rune_respawn"
	{
		"BaseClass" 					"ability_datadriven"
		"AbilityBehavior" 				"DOTA_ABILITY_BEHAVIOR_HIDDEN"		
		
		"OnOwnerDied"
		{
			"RunScript"
			{
				"ScriptFile"		"ai.lua"
				"Function"			"RuneRespawn"
			}
		}
	}

Код:
	"ability_rune"
	{
		"BaseClass" 					"ability_datadriven"
		"AbilityBehavior" 				"DOTA_ABILITY_BEHAVIOR_HIDDEN"			
		

		"OnCreated"
		{
			"ApplyModifier"
			{
				"Target"		"CASTER"
				"ModifierName"	"modifier_rune"
			}
			
			"ApplyModifier"
			{
				"Target"	"CASTER"
				"ModifierName"	"modifier_no_health_bar"
			}
		
		}
		
		"Modifiers"
		{
			"modifier_rune"
			{
				"IsHidden"   		"1"
				"OnAttacked"
				{
					"RunScript"
					{
						"ScriptFile"		"ai.lua"
						"Function"			"AddModifier"
					}
				}
			}
			
			"modifier_no_health_bar"
			{
				"Passive"	"1"
				"IsHidden"	"1"
				"States"
				{
					"MODIFIER_STATE_NO_HEALTH_BAR"	"MODIFIER_STATE_VALUE_ENABLED"
				}

			}		
		}		
			
	}

В абилке активации модификатора у нас что есть: есть два модификатора, что висят на самом юните-руне, это
"modifier_rune"
Который запустит скрипт "ai.lua", а в нем функцию "AddModifier" тогда, когда юнит умрет.
и "modifier_no_health_bar"
Который скрывает хп бар юнита, дабы было красиво и он больше походил на рунку.

Теперь мы создаем саму руну-юнита.
Идем в папочку "scripts", там в папочку "npc", а там в файлик npc_units_custom.txt
В нем создаем нашего крипа-руну, который не должен уметь ходить или бить и иметь мизерное хп.
Код:
	"npc_rune"
	{
		// General
		//----------------------------------------------------------------
		"BaseClass"					"npc_dota_creature"
		"Model"						"models/props_gameplay/rune_regeneration01.vmdl"
		"ModelScale"				"0.9"
		"Level"						"1"
		"HealthBarOffset"			"140"
		"HasInventory"				"0"
		"ConsideredHero"			"0"
		"IsNeutralUnitType"     "1"



		"UnitLabel"					"rune"
		
		// Abilities
		//----------------------------------------------------------------
		"Ability1"					"ability_rune"
		"Ability2"					"ability_rune_respawn"

		
		// Armor
		//----------------------------------------------------------------
		"ArmorPhysical"				"0"
		"MagicalResistance"			"0"
		
		// Attack
		//----------------------------------------------------------------
		"AttackCapabilities"		"DOTA_UNIT_CAP_NO_ATTACK"
		"AttackDamageType"			"DAMAGE_TYPE_ArmorPhysical"
		"AttackDamageMin"			"0"
		"AttackDamageMax"			"0"
		
		// Bounty
		//----------------------------------------------------------------
		"BountyGoldMin"				"0.0"
		"BountyGoldMax"				"0.0"
		
		// Bounds
		//----------------------------------------------------------------
		"BoundsHullName"			"DOTA_HULL_SIZE_HERO"
		"RingRadius"				"70"
		"CollisionSize"				"1"
		
		
		// Movement
		//----------------------------------------------------------------
		"MovementCapabilities"		"DOTA_UNIT_CAP_MOVE_NONE"
		"MovementSpeed"				"0"
		
		// Status
		//----------------------------------------------------------------
		"StatusHealth"				"1"
		"StatusHealthRegen"			"0"
		"StatusMana"				"0"
		"StatusManaRegen"			"0"
		
		// Vision
		//----------------------------------------------------------------
		"VisionDaytimeRange"		"900"
		"VisionNighttimeRange"		"600"
		
		// Team
		//----------------------------------------------------------------
		"TeamName"					"DOTA_TEAM_NEUTRALS"
		"CombatClassAttack"			"DOTA_COMBAT_CLASS_ATTACK_BASIC"
		"CombatClassDefend"			"DOTA_COMBAT_CLASS_DEFEND_STRUCTURE"
		"UnitRelationShipClass"		"DOTA_NPC_UNIT_RELATIONSHIP_TYPE_BUILDING"
		
		
	}

Заметьте:
"Model" "models/props_gameplay/rune_regeneration01.vmdl"
Я использовал модель руны регенерации, но можно и любую другую задать. Это плюс этого метода.

Кроме того не забываем указать ему:

// Abilities
//----------------------------------------------------------------
"Ability1" "ability_rune"
"Ability2" "ability_rune_respawn"

Наши, созданные выше, абилки.


Итак, теперь шагаем в тот файл, в котором вы спавните юнита. У меня это main.lua, у вас может быть addon_game_mode.lua.

Код:
function main:SpawnRune()

--не забываем в хамере, на карте, создать объект с именем, к примеру, spawn_rune
--то есть ту точку, куда будем спавнить руну
--это может быть как и блок типа path_corner
--так и объект spawner_info, смотрите сами

--вот здесь я ищу и копирую координаты созданного мною spawn_rune в хаммере
local point = Entities:FindByName( nil, "spawn_rune"):GetAbsOrigin()
--создаю юнита-руну, делаю его нейтралом
local unit = CreateUnitByName("npc_rune", point, true, nil, nil, DOTA_TEAM_NEUTRALS )	
--указываю ему точку спавна для будущего респавна
unit.vSpawnLoc = unit:GetOrigin()

end

Далее не забываем в папке "resource" в файле "addon_english.txt" указать описание монстра и модификатора.
Код:
"lang"
{
	"Language"		"English"
	"Tokens"
	{	
		"addon_game_name"			"my addon"

		// ******************** moobs ********************						
		"npc_rune"                        "Rune of attack speed"


		// ******************** MODIFIERS ********************		
		

		"DOTA_Tooltip_modifier_one_punch_man_speed"		"Speed of One Punch Man"
		"DOTA_Tooltip_modifier_one_punch_man_speed_Description"		"Three-year training bearing fruit: attack speed increase by 99999"	
	}
}
 
Последнее редактирование модератором:
CastOnPickup при айтеме.
И как денаить руну? А так норм, неплохо вышло.
 
CastOnPickup при айтеме.
И как денаить руну? А так норм, неплохо вышло.

Разве при castOnPickUp она не будет мигать в инвентаре? Она же на миг все же в рюкзаке появляется, а затем уже кастуется...

Минусы этого метода как раз в том, что мы атакуем руну. Тут не заденаить и у дальников преимущество.
Но по сравнению с минусами других методов, этот наиболее красивый.

Вышло говяно, в плане оформления, ибо чет нет расцветки кода. Хз почему.
 
Последнее редактирование модератором:
Тут не заденаить и у дальников преимущество.
Но по сравнению с минусами других методов, этот наиболее красивый.
хм, что то я не в восторге от наиболее красивого метода =(
Это получается я руну могу подобрать с раcстояния 700 к примеру. Это сильно портит ситуацию. Тут бы ограничение какое нибудь добавить, чтобы нельзя было атаковать с расстояния дальше чем 128 AOE
 
Последнее редактирование модератором:
Разве при castOnPickUp она не будет мигать в инвентаре? Она же на миг все же в рюкзаке появляется, а затем уже кастуется...

Минусы этого метода как раз в том, что мы атакуем руну. Тут не заденаить и у дальников преимущество.
Но по сравнению с минусами других методов, этот наиболее красивый.

Вышло говяно, в плане оформления, ибо чет нет расцветки кода. Хз почему.
Нужно форсировано вести игрока к руне если он хочет взять, и если расстояние между ними меньше 150 тогда брать руну.
И она не появляется в инвентаре вроде.
 
Последнее редактирование модератором:
Щас еще пришла идея, что если использовать фильтры подбора предмета. Если пытается подобрать предмет, то айтем удалять, давать эффект руны. И в инвентаре мешаться не будет, и косяков сильных не должно быть.
 
в варике третьем руны были отдельным типом предметов. может тут тоже есть такой параметр... типа "Использовать сразу"
 
Последнее редактирование модератором:
Щас еще пришла идея, что если использовать фильтры подбора предмета. Если пытается подобрать предмет, то айтем удалять, давать эффект руны. И в инвентаре мешаться не будет, и косяков сильных не должно быть.

Фильтр подбора - эт ты про слушателя или что?
С помощью слушателей нельзя перехватить действие, можно лишь сказать, что делать после действия.
Я в этом убедился как на рунах, так и еще давным давно, когда счетчики убийств реализовывал.
 
Последнее редактирование модератором:
Фильтр подбора - эт ты про слушателя или что?
С помощью слушателей нельзя перехватить действие, можно лишь сказать, что делать после действия.
Я в этом убедился как на рунах, так и еще давным давно, когда счетчики убийств реализовывал.
В том и прикол, есть фильтры а есть слушатели. Фильтры происходят ДО действия, слушатели ПОСЛЕ действия.
Чутка объясняющий гайд на моддоте:
https://moddota.com/forums/discussion/1285/the-order-filter-and-other-filters
Что я сделал:
Код:
function AngelArena:OrderFilter(event)
	local orged_type = event.order_type
  if orged_type == DOTA_UNIT_ORDER_CAST_TARGET then
  	local caster
  	for i,x in pairs(event.units) do 
  		if x then
  			caster = EntIndexToHScript(x)
  		end
  	end
  	
  	local ability = EntIndexToHScript( event.entindex_ability)
  	local target = EntIndexToHScript( event.entindex_target)

  	if forbidden_ability_boss[ability:GetName()] and IsUnitBossGlobal(target) then
  		ForceStopUnit(caster) 		
  	end
  end

  return true
end
Это я запрещаю юзать ульт гули на босса и другие подобные скиллы, если же пытаются то я форсировано останавливаю юнита(даю ему стан на 0.1 сек).
Ну, думаю ты понял что я имею в виду и как можно это использовать :D
 
Последнее редактирование модератором:
Слушай, да, можно поэкспериментировать и сделать персик))
 
Смотря как руну реализуешь. Если как крипа, то попросту начисляешь за его смерть голду. А если иначе, то в lua через SetGold() к примеру.
 
Смотря как руну реализуешь. Если как крипа, то попросту начисляешь за его смерть голду. А если иначе, то в lua через SetGold() к примеру.
Точно, он же npc, за него деньги давать буду. Спасибо, кажется очевидным но до меня не сразу дошло.
 
Последнее редактирование модератором:
Не забудь поставить
Код:
		"CanBeDominated"      "0"
а то смогут доминатором подчинить.

А вообще лучше не как крипа её реализовывать, а как тут в комментариях обсуждали.
Иначе дальникам проще будет подбирать её и денай не сделать. Вариант через крипа - бюджетный, когда по иному не умеешь.
 
Последнее редактирование модератором:
хм, что то я не в восторге от наиболее красивого метода =(
Это получается я руну могу подобрать с раcстояния 700 к примеру. Это сильно портит ситуацию. Тут бы ограничение какое нибудь добавить, чтобы нельзя было атаковать с расстояния дальше чем 128 AOE
Привет,
Вы не знаете как уменьшить время возрождения героя?
 
Последнее редактирование модератором:
А есть такой модефикатор как команда -refresh? Что бы руна и здоровье восстановила и скиллы отКДшила.
 
Реклама: