Урок [lua, datadriven] Урок 1. Создаем первый кастомный предмет со скриптом

Se7eN

Друзья CG
22 Ноя 2014
334
18
bfury.png
Добрый день всем читателям! ;)

Сегодня мы создадим предмет из доты2, который называется battle fury. Этот топор распространяет урон по всем юнитам в небольшом радиусе. Начнём!​

Note: На днях выпущу общий гайд по datadriven способностям, а этот урок должен был идти как дополнение к нему.

Note: Обязательно прочитайте и вникните в код.

Итак. Battle fury как предмет.

План урока:
1. Рассмотреть datadriven-составляющую предмета. Посмотреть как устроены события, как устроены действия.
2. Рассмотреть lua-скрипт с комментариями, чтобы вам было понятно ЧТО и ЗАЧЕМ делается.


Код:
"item_bfury_datadriven"
	{
"BaseClass"						"item_datadriven" //указываем класс предмета
"ID"							"1145" //У вольво предмет 145 ID, мы ставим 1145, для себя
"AbilityBehavior"				"DOTA_ABILITY_BEHAVIOR_PASSIVE" //пассивка (сплэш)
"Model"							"models/props_gameplay/red_box.vmdl" //красный ящик, модель 
"Effect"						"particles/generic_gameplay/dropped_item.vpcf" 
"AbilityTextureName"			"item_bfury" //иконка предмета
		
		// Item Info
		//-------------------------------------------------------------------------------------------------------------
		"ItemCost"						"4350" //сколько стоит предмет
		"ItemShopTags"					"damage;health_regen;mana_regen;hard_to_tag" //тэги для 
		"ItemQuality"					"epic" //качество предмета стоит epic, значит предмет редкий
		"ItemAliases"					"bf;battle fury" //алиасы для предмета, позже скажу для чего
		"ItemDeclarations"				"DECLARE_PURCHASES_TO_TEAMMATES | DECLARE_PURCHASES_IN_SPEECH | DECLARE_PURCHASES_TO_SPECTATORS" //показывать союзникам что вы его купили, озвучивать героем предмет, показывать броадкастерам что он был куплен

//С подготовкой закончено, теперь сам datadriven-код.
			
		// Special	
		//-------------------------------------------------------------------------------------------------------------
		"AbilitySpecial" //Специальные значения предмета, чтобы мы могли их брать отсюда
		{
			"01"
			{
				"var_type"						"FIELD_INTEGER"
				"bonus_damage"					"65" //[b]Бонусный урон, пока что просто задается как параметр, если оставите его так, то он не будет давать урона[/b]
			}
			"02"
			{
				"var_type"						"FIELD_INTEGER"
				"bonus_health_regen"			"6" //бонусная регенерация хп
			}
			"03"
			{
				"var_type"						"FIELD_INTEGER"
				"bonus_mana_regen_percentage"	"150" //бонусная регенерация маны в процентах
			}
			"04"
			{
				"var_type"						"FIELD_INTEGER"
				"cleave_damage_percent"			"35" //процент сплэша
			}
			"05"
			{
				"var_type"						"FIELD_INTEGER"
				"cleave_radius"					"250" //радиус сплэша
			}
		}
		


		"Modifiers" //Секция модифиеры
		{
			"modifier_item_bfury_datadriven" //Модификатор баттлы
			{
				"Passive"			"1" //пассивный
				"IsHidden"			"1" //скрытый, не виден на панели статусов
				"Attributes" 		"MODIFIER_ATTRIBUTE_MULTIPLE" //Мультипл тут означает что предмет усиливает сразу несколько параметров
				
				"Properties"
				{
					"MODIFIER_PROPERTY_BASEATTACK_BONUSDAMAGE"		"%bonus_damage" // Как вы видите, мы берем это значение из переменной которой мы указывали выше, в AbilitySpecial, там мы указали 65 урона, значит даваться будет 65). Но это всего лишь модификатор! Урон всё ещё не дается при поднятии предмета, мы обязательно должны "Дать" этот модификатор какому-либо юниту, в данном случае тому, кто поднимет предмет
					"MODIFIER_PROPERTY_HEALTH_REGEN_CONSTANT"		"%bonus_health_regen"
					"MODIFIER_PROPERTY_MANA_REGEN_PERCENTAGE"		"%bonus_mana_regen_percentage" //То же самое с регеном маны и хп.
				}



				
				"OnCreated" //СОБЫТИЕ: когда юнит поднимает предмет, мы запускаем скрипт
				{
					"RunScript" //вот тут секция запуска скрипта
					{
						"ScriptFile"		"items/item_bfury.lua" //название скрипта
						"Function"			"modifier_item_bfury_datadriven_on_created" //функция внутри скрипта
					}
				}
				
				"OnDestroy" //СОБЫТИЕ: когда предмет выкидывается
				{
					"RunScript" //запускаем скрипт)
					{
						"ScriptFile"		"items/item_bfury.lua" //путь к скрипту)
						"Function"			"modifier_item_bfury_datadriven_on_destroy" //функция
					}
				}
				
				//Итак теперь мы каждые .03 секунды будем проверять
				//вдруг герой ближнего боя поменял свой тип атаки на дальний бой, тогда мы заберем сплэш
				"ThinkInterval" ".03" //таймер, который будет выполняться каждые 0.03 сек.
				"OnIntervalThink" //СОБЫТИЕ: на каждое срабатывание таймера
				{
					"RunScript" //запустить скрипт
					{
						"ScriptFile"		"items/item_bfury.lua"
						"Function"			"modifier_item_bfury_datadriven_on_interval_think" //функция проверки
					}
				}
			}
			"modifier_item_bfury_datadriven_cleave" //Вот теперь тот самый сплэш.
			{
				"Passive"			"0" //активная
				"IsHidden"			"1" //скрытый модификатор
				"Attributes" 		"MODIFIER_ATTRIBUTE_MULTIPLE" //даёт аттрибуты
				
				"OnAttackLanded" //СОБЫТИЕ: когда юнит обладающий предметом атаковал
				{
					"CleaveAttack" //сделать сплэш атаку.
					{
						"CleavePercent"		"%cleave_damage_percent" //процент сплэша, указан выше
						"CleaveRadius"		"%cleave_radius" //радиус
						"CleaveEffect"		"particles/items_fx/battlefury_cleave.vpcf" //эффект
					}
					
					"FireSound" //Так же воспроизвести звук
					{
						"EffectName"				"DOTA_Item.BattleFury" //путь к звуку
						"Target"					"TARGET" //на цель, обладателя предмета
					}
				}
				
				//Continually check to see if the hero this modifier is on is still melee 
				//(since some units, like Troll Warlord, can switch between ranged/melee forms).
				"ThinkInterval" ".03" //ещё одна доп. проверка на ближника/дальника
				"OnIntervalThink"
				{
					"RunScript"
					{
						"ScriptFile"		"items/item_bfury.lua"
						"Function"			"modifier_item_bfury_datadriven_cleave_on_interval_think"
					}
				}
			}
		}
	}


Так, в datadriven разобрались. Как вы знаете, вся datadriven-инфа по предметам находится в npc_items_custom.txt


Теперь скрипт.

Код:
--[[ ============================================================================================================
	Author: Rook
	Date: February 2, 2015
	Called when a Battle Fury is acquired. Grants the cleave modifier if the caster is a melee hero.
================================================================================================================= ]]
function modifier_item_bfury_datadriven_on_created(keys)
	if not keys.caster:IsRangedAttacker() then
		keys.ability:ApplyDataDrivenModifier(keys.caster, keys.caster, "modifier_item_bfury_datadriven_cleave", {duration = -1})
	end
end


--[[ ============================================================================================================
	Author: Rook
	Date: February 2, 2015
	Called when a Battle Fury is removed from the caster's inventory. Removes a cleave modifier if they are a melee hero.
================================================================================================================= ]]
function modifier_item_bfury_datadriven_on_destroy(keys)
	if not keys.caster:IsRangedAttacker() then
		keys.caster:RemoveModifierByName("modifier_item_bfury_datadriven_cleave")
	end
end


--[[ ============================================================================================================
	Author: Rook
	Date: February 2, 2015
	Called regularly while the caster has a Battle Fury in their inventory. If the caster has switched from ranged
	to melee, give them cleave modifier(s).
================================================================================================================= ]]
function modifier_item_bfury_datadriven_on_interval_think(keys)
	if not keys.caster:IsRangedAttacker() and not keys.caster:HasModifier("modifier_item_bfury_datadriven_cleave") then
		for i=0, 5, 1 do
			local current_item = keys.caster:GetItemInSlot(i)
			if current_item ~= nil then
				if current_item:GetName() == "item_bfury_datadriven" then
					keys.ability:ApplyDataDrivenModifier(keys.caster, keys.caster, "modifier_item_bfury_datadriven_cleave", {duration = -1})
				end
			end
		end
	end
end


--[[ ============================================================================================================
	Author: Rook
	Date: February 2, 2015
	Called regularly while the caster has at least one cleave modifier from Battle Fury. If the caster is no longer
	melee (which would be the case on, for example, Troll Warlord), remove the cleave modifiers from the caster.
================================================================================================================= ]]
function modifier_item_bfury_datadriven_cleave_on_interval_think(keys)
	if keys.caster:IsRangedAttacker() then
		while keys.caster:HasModifier("modifier_item_bfury_datadriven_cleave") do
			keys.caster:RemoveModifierByName("modifier_item_bfury_datadriven_cleave")
		end
	end
end

d8b23e9a96b81b693f46a49f4564922e.jpg

Когда предмет был подобран, вызывается эта функция. Если герой БЛИЖНЕГО БОЯ, то даёт ему модификатор сплэша. Как вы поняли, если нет модификатора - нет сплэша.
IsRangedAttacker() - проверка на дальний бой. Однако если взять обратное значение типа:
If not IsRangedAttacker(), то всё работает на ура!)

0794707fe62938b3bd61f52cb6171a7f.jpg

Когда предмет был выброшен или продан, то удаляет модификатор, если герой БЛИЖНЕГО БОЯ.

d148d5875cd7b6fcafde78879bd419ae.jpg

Функция вызывается каждые 0.03 секунды и проверяет не переключился ли герой с ближнего на дальний бой.
Если да, то забирает модификатор.

6bd57fb7c88cc22ac56609a686ed8dc7.jpg

Вызывается, если у героя есть хоть один модификатор сплэша, и затем проверяется не поменял ли герой свой тип атаки с ближнего на дальний. Если поменял - модификатор удаляется.


Путь: scripts/vscripts/items/item_bfury.lua
Название скрипта: item_bfury.lua

Поздравляем, теперь вы создали свой первый предмет!
Пока, что достаточно было просто скопировать код, но это уже много!
Старайтесь и у вас всегда будет получаться лучше и лучше! :D
 
Последнее редактирование модератором:
  • Нравится
Реакции: Sannin

danilkoo832832

Пользователь
5 Фев 2016
40
3
Я захотел сделать предмет на ману.
Код:
  //=================================================================================================================
  // Mana Booster
  //=================================================================================================================
  "item_mana_booster"
  {
   // General
   //-------------------------------------------------------------------------------------------------------------
   "ID"           "3003" 
   "AbilityBehavior"      "DOTA_ABILITY_BEHAVIOR_PASSIVE"
   "Model"        "models/props_gameplay/red_box.vmdl"
   "ItemSellable"         "1"
   "ItemKillable"        "0" 
   "ItemDroppable"        "1"
   "ItemShareability"      "ITEM_NOT_SHAREABLE" 
   "ItemDeclarations"      "DECLARE_PURCHASES_TO_TEAMMATES | DECLARE_PURCHASES_TO_SPECTATORS | DECLARE_PURCHASES_IN_SPEECH"
   "AbilityTextureName"   "item_energy_booster"
   "BaseClass"      "item_datadriven"
   // Item Info
   //-------------------------------------------------------------------------------------------------------------
   "ItemCost"         "2700"
   "ItemShopTags"        "mana_pool"
   "ItemQuality"        "secret_shop"
   "ItemAliases"        "mana booster"
   "SecretShop"        "1"   
   "SideShop"         "1"
   // Special  
   //-------------------------------------------------------------------------------------------------------------
   "AbilitySpecial"
   {
     "01"
     {
      "var_type"      "FIELD_INTEGER"
      "bonus_mana"     "1000"
     }
   }
   "Modifiers"
   {
     "modifier_item_mana_booster" 
     {
      "Passive"     "1"
      "IsHidden"     "1" 
        "Properties"
        {
        "MODIFIER_PROPERTY_MANA_BONUS"   "%bonus_mana"
        }
     }   
   }
  }
Но маны данный предмет не даёт. что сделано не так?
 
Последнее редактирование модератором:

Илья

Друзья CG
25 Сен 2015
2,348
41
В модификаторах вещей я не так силен, чтобы сходу сказать, как надо правильно сделать. Скорее всего, ты не указал атрибуты. Можешь глянуть тут. item_precept_of_mana практически та же самая вещь, что и у тебя.

Вообще вот список всех возможных атрибутов:

MODIFIER_ATTRIBUTE_IGNORE_INVULNERABLE
MODIFIER_ATTRIBUTE_MULTIPLE
MODIFIER_ATTRIBUTE_NONE
MODIFIER_ATTRIBUTE_PERMANENT
 

danilkoo832832

Пользователь
5 Фев 2016
40
3
[quote author=Илья link=topic=165.msg2862#msg2862 date=1454846677]
Можешь глянуть тут.
[/quote]
А есть такой же список ток с вещами и скиллами из об. доты?
 

danilkoo832832

Пользователь
5 Фев 2016
40
3
Нет. Страницы с модификаторами у меня есть. Мне надо дефолтные дота абилки и предметы с модификаторами.
 

Илья

Друзья CG
25 Сен 2015
2,348
41
Последнее редактирование модератором:

somsim

Пользователь
14 Мар 2017
23
0
Дело за малым! Осталось: добавить имя, сделать в окне магазина. Допустим эту бф2 сделать улучшением дотерской бф"ы то есть находя дот. бф"у показывая ее в магазине была видна наша навая item'a (не заменяя)...
Наверно запутал - собирая бф можно кликнуть на топорик/другие составляющие и увидеть сам артефакт. Тоже самое сделать для созданной: клацая на бф видеть нашу.
Да проблемы с пояснениями... признаки гениальности:|
[size=8pt]З.Ы знаки препинания для героев![/size]
 
Последнее редактирование модератором:

Илья

Друзья CG
25 Сен 2015
2,348
41
Все это реализуется простым добавлением вещи в магазин. Если сделаешь рецепт, в котором будет участвовать новая вещь - отображение её составляющих или же итога из составляющих автоматически проявляется.
 

somsim

Пользователь
14 Мар 2017
23
0
Все это реализуется простым добавлением вещи в магазин. Если сделаешь рецепт, в котором будет участвовать новая вещь - отображение её составляющих или же итога из составляющих автоматически проявляется.
Этого лично я в гайдах не смог найти. Хотя по этому и пишу.
 
Последнее редактирование модератором:

Илья

Друзья CG
25 Сен 2015
2,348
41
Ты бы это нашел, еслиб реализовал вещь и рецепт для неё, используя стандартные, с чего по сути и начинают : )
 

somsim

Пользователь
14 Мар 2017
23
0
А кстати интересно у вас тут получается. У каждого свое творчество. И у каждого свой мод. У вас же написаны всякие-разные гайды и значится вижу что по идее мне этот гайд ну вовсе не нужен и считаю что там нет никакой для меня (моего мода) никакой пользы! Однако именно здесяhttps://customgames.ru/forum/index.php?topic=155.0 нашел немало нового.
реализовал вещь и рецепт для неё, используя стандартные
Мне почему то кажется с этим будут проблемы. Но в принципе смысл тот же? Добавить рецепт в итем кастом с его хар-ми, написать скрипт. А слот делать отдельный под него (папку)? (я конечно щас еще поищу как с стандартных делать........)
 

Илья

Друзья CG
25 Сен 2015
2,348
41
[quote author=somsim link=topic=165.msg9033#msg9033 date=1490799242]
А кстати интересно у вас тут получается. У каждого свое творчество. И у каждого свой мод. У вас же написаны всякие-разные гайды и значится вижу что по идее мне этот гайд ну вовсе не нужен и считаю что там нет никакой для меня (моего мода) никакой пользы! Однако именно здесяhttps://customgames.ru/forum/index.php?topic=155.0 нашел немало нового.
[/quote]

Суть гайдов то от этого не меняется - каждый из них учит конкретным вещам, которые уже нужны тому или иному модеру в зависимости от того, что он хочет сделать в своем моде.

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

Son1cPr00wer

Пользователь
23 Сен 2017
43
2
vk.com
Проект
Whack-A-Meepo
Плюсую, именно этот гайд мало чему меня научил, так как пока до него дошел, уже много перерыл стандартных вещей\абилок. Но все равно появляются затыки в самых тривиальных местах, а тут смотришь в этот гайд, и все сразу становится ясно)
Не хочется плодить темы, поэтому задам свой вопрос тут (ибо создаю предмет).
Я создал предмет (молот), но у меня проблемы именно с анимацией. С одной стороны, нужно создавать новый партикл (или втыкать имеющийся) - это к ассетам в тему. С другой - в одних примерах (рэнжевые топоры тролля и первый скилл квопы) я видел, что TrackingProjectile задаётся в datadriven'e, а например траектория для ракет тинкера и гиро - в lua. В моём случае, как я понимаю, более подходит метод через datadriven.
Для начала тупо воткнул анимацию квопы - модифаер накладывается, значит всё куда надо долетает, но как долетает - не видно, почему - я так и не понял. Если посмотреть в ассет браузере сам партикл, там моделька клинка квопы есть, на практике - пусто. К слову, анимация шипов и раптуры на цели работает нормально и кончается когда надо. Посоветуйте пожалуйста, куда двигаться, и на что обратить внимание.
P.S. На звуки внимание не обращайте, еще не разобрался где что задавать.
Код:
"item_hammer"
{
	// General
	//-------------------------------------------------------------------------------------------------------------
	"BaseClass"						"item_datadriven"
	"AbilityBehavior"				"DOTA_ABILITY_BEHAVIOR_UNIT_TARGET"
	"AbilityUnitTargetTeam"			"DOTA_UNIT_TARGET_TEAM_ENEMY"
	"AbilityUnitTargetType"			"DOTA_UNIT_TARGET_HERO | DOTA_UNIT_TARGET_BASIC"
	"AbilityUnitDamageType"			"DAMAGE_TYPE_PHYSICAL"	
	"SpellImmunityType"				"SPELL_IMMUNITY_ENEMIES_NO"
	"FightRecapLevel"				"1"
	"AbilityTextureName"			"item_hammer"
	"Model"							"models/props_gameplay/red_box.vmdl" 
	"Effect"						"particles/generic_gameplay/dropped_item.vpcf"
	
	// Precache
	//-------------------------------------------------------------------------------------------------------------
	"precache"
	{
		"soundfile"			"sounds/weapons/hero/troll_warlord/whirling_ranged_cast.vsnd"
		"particle"			"particles/units/heroes/hero_queenofpain/queen_shadow_strike.vpcf"
		"particle"			"particles/econ/items/bristleback/bristle_spikey_spray/bristle_spikey_quill_spray_sparks.vpcf"
		"particle"			"particles/units/heroes/hero_bloodseeker/bloodseeker_rupture.vpcf"
	}
	
	// Stats
	//-------------------------------------------------------------------------------------------------------------
	"AbilityCastRange"				"500"
	"AbilityCastPoint"				"0.450"
	"AbilityCooldown"				"7.0"
	
	// Cost
	//-------------------------------------------------------------------------------------------------------------
	"AbilityManaCost"				"55"

	
	// Item Info
	//-------------------------------------------------------------------------------------------------------------
	"ItemCost"						"875"
	"ItemShopTags"					"str"
	"ItemQuality"					"epic"
	"ItemAliases"					"hammer"
	"ItemDeclarations"				"DECLARE_PURCHASES_TO_TEAMMATES | DECLARE_PURCHASES_TO_SPECTATORS"
	"ItemRequiresCharges"			"0"
	"ItemDisplayCharges"			"0"
	"ItemStackable"					"0"
	"ItemPermanent"					"1"
	"ItemInitialCharges"			"0"	
		
	// Special	
	//-------------------------------------------------------------------------------------------------------------
	"AbilitySpecial"
	{
		"01"
		{
			"var_type"				"FIELD_INTEGER"
			"strike_damage"			"75"
		}
		"02"
		{
			"var_type"				"FIELD_INTEGER"
			"duration_damage"		"40"
		}
		"03"
		{
			"var_type"				"FIELD_INTEGER"
			"movement_slow"			"-30"
		}
		"04"
		{
			"var_type"				"FIELD_INTEGER"
			"projectile_speed"		"900"
		}
		"05"
		{
			"var_type"				"FIELD_INTEGER"
			"cast_range_tooltip"	"800"
		}
		"06"
		{
			"var_type"				"FIELD_INTEGER"
			"damage_interval"		"3"
		}
		"07"
		{
			"var_type"				"FIELD_FLOAT"
			"duration"				"15.0"
		}
		
	}

	"OnSpellStart"
	{
		"TrackingProjectile"
		{
			"Target"      "TARGET"
		  "EffectName"    "particles/units/heroes/hero_queenofpain/queen_shadow_strike.vpcf"
		  "Dodgeable"    "1"
		  "ProvidesVision"  "0"
		  "MoveSpeed"    "%projectile_speed"
		  "SourceAttachment" "DOTA_PROJECTILE_ATTACHMENT_ATTACK_1"
		}
		
		"RunScript"
		{
			"ScriptFile"	"items/hammer.lua"
			"Function"		"hammer_on_spell_start"
		}

		
		"FireSound"
		{
			"Sound"				"sounds/weapons/hero/troll_warlord/whirling_ranged_cast.vsnd"
			"Target"			"CASTER"
		}

		"FireEffect"
		{
			"Target"			"CASTER"
			"EffectAttachType"	"follow_origin"
			"EffectName"		"particles/econ/items/bristleback/bristle_spikey_spray/bristle_spikey_quill_spray_sparks.vpcf"
		}
	}

	"OnProjectileHitUnit"
	{
		"RunScript"
		{
			"ScriptFile"	"items/hammer.lua"
			"Function"		"hammer"
			"modifier"		"modifier_hammer"
		}

		"Damage"
		{
			"Target"		"TARGET"
			"Type"			"DAMAGE_TYPE_PHYSICAL"
			"Damage"		"%strike_damage"
		}		
	}

	"Modifiers"
	{
		"modifier_hammer"
		{
			"IsDebuff"	"1"

			"OnCreated"
			{
				"AttachEffect"
				{
					"EffectName"		"particles/units/heroes/hero_bloodseeker/bloodseeker_rupture.vpcf"
					"EffectAttachType"	"attach_origin"
					"Target"      "TARGET"
				}
			}

			"ThinkInterval"	"%damage_interval"
			"OnIntervalThink"
			{
				"Damage"
				{
					"Target"		"TARGET"
					"Type"			"DAMAGE_TYPE_MAGICAL"
					"Damage"		"%duration_damage"
				}
			}

			"Properties"
			{
				"MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE"		"-1"
			}
		}
	}
}
 
Последнее редактирование модератором:

I_GRIN_I

Друзья CG
15 Мар 2016
1,335
105
Son1cPr00wer, партикл кинжала квопы вместо кинжала берет оружие кастера
 

Son1cPr00wer

Пользователь
23 Сен 2017
43
2
vk.com
Проект
Whack-A-Meepo
[quote author=I_GRIN_I link=topic=165.msg11674#msg11674 date=1506406097]
Son1cPr00wer, партикл кинжала квопы вместо кинжала берет оружие кастера
[/quote]
Стало быть, у меня что-то неправильно где-то указано, хотя вроде по гайдам делал и примеры разбирал.
 
Реклама: