Цель гайда: Создать карту, на которой после начала матча(00:00 на игровых часах) через определенное время запустится система раундов.
В итоге мы получим:
• Определенное кол-во раундов с промежутками между друг другом.
• Нарастающая сложность мобов на разных раундах.
• Мобы движутся по заданной траектории(не прямая линия, а любые повороты и т.д.)
• Прекращение спавна мобов после указанного раунда.
Приступим. Шаг №1. Создаем траекторию пути мобов.
Шаг №2. Создаем точку спавна мобов.
Шаг №3. Создаем юнитов для разных волн.
Заходим в npc_units_custom.txt и создаем там 5 разных юнитов с именами, к примеру, example_unit_1, example_unit_2 - это, как Вы уже наверное догадались, юниты, которые будут идти на первой и второй волне соответственно. Я не углублялся в вопрос создания юнитов и пока просто создал одинаковых крипов, но на 2 и 5 волнах у них увеличенные модели.
И так далее. Фулл npc_units_custom.tхt прикреплю в конце.
Шаг №4. Устанавливаем базовые скрипты BMD.
Для работы того, что мы задумали, требуется отслеживание момента начала матча и таймер. Получить такие возможности можно благодаря написанию мода на предоставленной базе скриптов: https://github.com/MNoya/barebones/tree/source2/game/dota_addons/barebones/scripts/vscripts. Вам следует заменить свой addon_game_mode.lua на предоставленный, а так же закинуть в vscripts скрипты timers.lua и barebones.lua. Timers.lua - здесь понятно, он позволяет использовать таймеры, о них подробнее тут: http://customgames.ru/forum/index.php?topic=43.0. Barebones.lua дает возможность изменять некоторые параметры Вашего мода, отслеживать события и еще много-много другого.
Шаг №5. Пишем скрипт.
Открываем addon_game_mode.lua и вверху, над встроенной в него функцией 'Precache', создаем глобальные переменные:
Переходим в самый низ нашего addon_game_mode.lua и пишем туда следующее:
Шаг №6. Делаем предварительное кеширование юнитов.
Кэшируем только example_unit_1 - этого будет достаточно. В addon_game_mode.lua от ВMD вверху уже есть ф-ция 'Precache', просто вставляем в неё:
Советы:
• Советую для ознакомления два открытых на гитхабе популярных проекта:
Warchasers: https://github.com/MNoya/Warchasers
Жизнь на Арене: https://github.com/ZLOY5/LiA
Скачайте их, распакуйте и установите программу Far 3. И когда становится тяжело понять, как описать ту или иную команду или функцию - заходите через Far в корень скачанных проектов, жмите alt + F7. В первом поле вводите "*.*", во втором - то, что нужно найти, например "OnAttacked".
• На ГитХабе в поиске так же всегда можно поискать интересующие команды и функции. Также для поиска и вообще "почитать" советую https://moddota.com
• На сайте https://moddota.com создатель Warchasers - Noya, оформляет качественные гайды, несколько из них:
http://moddota.com/forums/discussion/14/datadriven-ability-breakdown-documentation
https://moddota.com/forums/discussion/4/datadriven-items
http://moddota.com/forums/discussion/93/point-channeling-aoe-ability-example
https://moddota.com/forums/discussion/69/particle-attachment
• Ну и конечно же - по возможности старайтесь создавать свои или переводить чужие гайды, так как то, что Вам кажется очевидным, кому-то придется понимать на забугорских сайтах 2 а то и больше дней, так как российское сообщество D2WT ушло не очень далеко от нулевого уровня.
В итоге мы получим:
• Определенное кол-во раундов с промежутками между друг другом.
• Нарастающая сложность мобов на разных раундах.
• Мобы движутся по заданной траектории(не прямая линия, а любые повороты и т.д.)
• Прекращение спавна мобов после указанного раунда.
Приступим. Шаг №1. Создаем траекторию пути мобов.
Для создания траектории пути мобов нам понадобится 4 или больше обычных блоков(Ctrl + B):
Далее кликаем на каждый из них и делаем из них Entity горячей клавишей Ctrl + T.
Теперь каждому блоку справа нужно вписать класс 'path_corner' и применить - нажать Enter.
Далее каждому блоку справа мы даем имя. Пусть это будет way1, way1.5, way2, и way3.
Настало время создавать траекторию. Указываем в каждом блоке имя последующего блока в поле 'Next stop target'. В way1 вписываем way1.5 и так далее.
Теперь если мы пошлем юнита на way1, он, дойдя туда, отправится на way.1.5, а оттуда - на way2 и т.д.
И расставим блоки в хаотичном порядке, но есть нюанс: на way1 мобы не побегут - они пойдут сразу к way1.5( возможно такой баг только у меня ):http://i.imgur.com/JoRwNo8.jpg
Далее кликаем на каждый из них и делаем из них Entity горячей клавишей Ctrl + T.
Теперь каждому блоку справа нужно вписать класс 'path_corner' и применить - нажать Enter.
Далее каждому блоку справа мы даем имя. Пусть это будет way1, way1.5, way2, и way3.
Настало время создавать траекторию. Указываем в каждом блоке имя последующего блока в поле 'Next stop target'. В way1 вписываем way1.5 и так далее.
Теперь если мы пошлем юнита на way1, он, дойдя туда, отправится на way.1.5, а оттуда - на way2 и т.д.
И расставим блоки в хаотичном порядке, но есть нюанс: на way1 мобы не побегут - они пойдут сразу к way1.5( возможно такой баг только у меня ):http://i.imgur.com/JoRwNo8.jpg
Шаг №2. Создаем точку спавна мобов.
Создаем любой Entity. Пусть это будет 'info_target':
Размещаем его где душа пожелает и справа присваиваем ему произвольное имя, пусть это будет 'spawnerino':
Размещаем его где душа пожелает и справа присваиваем ему произвольное имя, пусть это будет 'spawnerino':
Шаг №3. Создаем юнитов для разных волн.
Заходим в npc_units_custom.txt и создаем там 5 разных юнитов с именами, к примеру, example_unit_1, example_unit_2 - это, как Вы уже наверное догадались, юниты, которые будут идти на первой и второй волне соответственно. Я не углублялся в вопрос создания юнитов и пока просто создал одинаковых крипов, но на 2 и 5 волнах у них увеличенные модели.
// Units File
"DOTAUnits"
{
"Version" "1"
//=================================================================================
// Creature: Gnoll Assassin
//=================================================================================
"example_unit_1"
{
// General
//----------------------------------------------------------------
"Model" "models/creeps/neutral_creeps/n_creep_gnoll/n_creep_gnoll_frost.vmdl" // Model.
"BaseClass" "npc_dota_creature"
"SoundSet" "n_creep_Ranged"
"GameSoundsFile" "soundevents/game_sounds_creeps.vsndevts"
"Level" "1"
"ModelScale" ".9"
// Abilities
//----------------------------------------------------------------
"Ability1" "" // Ability 1
"Ability2" "" // Ability 2
"Ability3" "" // Ability 3
"Ability4" "" // Ability 4
// Armor
//----------------------------------------------------------------
"ArmorPhysical" "10" // Physical protection.
// Attack
//----------------------------------------------------------------
"AttackCapabilities" "DOTA_UNIT_CAP_RANGED_ATTACK"
"AttackDamageMin" "30" // Damage range min.
"AttackDamageMax" "36" // Damage range max.
"AttackRate" "1.6" // Speed of attack.
"AttackAnimationPoint" "0.4" // Normalized time in animation cycle to attack.
"AttackAcquisitionRange" "800" // Range within a target can be acquired.
"AttackRange" "500" // Range within a target can be attacked.
"ProjectileModel" "particles/neutral_fx/gnoll_base_attack.vpcf" // Particle system model for projectile.
"ProjectileSpeed" "1500" // Speed of projectile.
// Bounds
//----------------------------------------------------------------
"RingRadius" "40"
"HealthBarOffset" "170"
// Bounty
//----------------------------------------------------------------
"BountyXP" "24" // Experience earn.
"BountyGoldMin" "21" // Gold earned min.
"BountyGoldMax" "29" // Gold earned max.
// Movement
//----------------------------------------------------------------
"MovementCapabilities" "DOTA_UNIT_CAP_MOVE_GROUND"
"MovementSpeed" "270" // Speed.
// Status
//----------------------------------------------------------------
"StatusHealth" "75" // Base health.
"StatusHealthRegen" "0.5" // Health regeneration rate.
"StatusMana" "0" // Base mana.
"StatusManaRegen" "0.0" // Mana regeneration rate.
// Vision
//----------------------------------------------------------------
"VisionDaytimeRange" "400" // Range of vision during day light.
"VisionNighttimeRange" "400" // Range of vision at night time.
// Team
//----------------------------------------------------------------
"TeamName" "DOTA_TEAM_GOODGUYS" // Team name.
"CombatClassAttack" "DOTA_COMBAT_CLASS_ATTACK_PIERCE"
"CombatClassDefend" "DOTA_COMBAT_CLASS_DEFEND_BASIC"
"UnitRelationshipClass" "DOTA_NPC_UNIT_RELATIONSHIP_TYPE_DEFAULT"
// Creature Data
//----------------------------------------------------------------
"Creature"
{
//Level Up
"HPGain" "50"
"DamageGain" "2"
"ArmorGain" "0.25"
"MagicResistGain" "0.1"
"MoveSpeedGain" "1"
"BountyGain" "3"
"XPGain" "15"
}
}
"example_unit_2"
{
// General
//----------------------------------------------------------------
"Model" "models/creeps/neutral_creeps/n_creep_gnoll/n_creep_gnoll_frost.vmdl" // Model.
"BaseClass" "npc_dota_creature"
"SoundSet" "n_creep_Ranged"
"GameSoundsFile" "soundevents/game_sounds_creeps.vsndevts"
"Level" "1"
"ModelScale" "1.4"
// Abilities
//----------------------------------------------------------------
"Ability1" "" // Ability 1
"Ability2" "" // Ability 2
"Ability3" "" // Ability 3
"Ability4" "" // Ability 4
// Armor
//----------------------------------------------------------------
"ArmorPhysical" "10" // Physical protection.
// Attack
//----------------------------------------------------------------
"AttackCapabilities" "DOTA_UNIT_CAP_RANGED_ATTACK"
"AttackDamageMin" "30" // Damage range min.
"AttackDamageMax" "36" // Damage range max.
"AttackRate" "1.6" // Speed of attack.
"AttackAnimationPoint" "0.4" // Normalized time in animation cycle to attack.
"AttackAcquisitionRange" "800" // Range within a target can be acquired.
"AttackRange" "500" // Range within a target can be attacked.
"ProjectileModel" "particles/neutral_fx/gnoll_base_attack.vpcf" // Particle system model for projectile.
"ProjectileSpeed" "1500" // Speed of projectile.
// Bounds
//----------------------------------------------------------------
"RingRadius" "40"
"HealthBarOffset" "170"
// Bounty
//----------------------------------------------------------------
"BountyXP" "24" // Experience earn.
"BountyGoldMin" "21" // Gold earned min.
"BountyGoldMax" "29" // Gold earned max.
// Movement
//----------------------------------------------------------------
"MovementCapabilities" "DOTA_UNIT_CAP_MOVE_GROUND"
"MovementSpeed" "270" // Speed.
// Status
//----------------------------------------------------------------
"StatusHealth" "75" // Base health.
"StatusHealthRegen" "0.5" // Health regeneration rate.
"StatusMana" "0" // Base mana.
"StatusManaRegen" "0.0" // Mana regeneration rate.
// Vision
//----------------------------------------------------------------
"VisionDaytimeRange" "400" // Range of vision during day light.
"VisionNighttimeRange" "400" // Range of vision at night time.
// Team
//----------------------------------------------------------------
"TeamName" "DOTA_TEAM_GOODGUYS" // Team name.
"CombatClassAttack" "DOTA_COMBAT_CLASS_ATTACK_PIERCE"
"CombatClassDefend" "DOTA_COMBAT_CLASS_DEFEND_BASIC"
"UnitRelationshipClass" "DOTA_NPC_UNIT_RELATIONSHIP_TYPE_DEFAULT"
// Creature Data
//----------------------------------------------------------------
"Creature"
{
//Level Up
"HPGain" "50"
"DamageGain" "2"
"ArmorGain" "0.25"
"MagicResistGain" "0.1"
"MoveSpeedGain" "1"
"BountyGain" "3"
"XPGain" "15"
}
}
"DOTAUnits"
{
"Version" "1"
//=================================================================================
// Creature: Gnoll Assassin
//=================================================================================
"example_unit_1"
{
// General
//----------------------------------------------------------------
"Model" "models/creeps/neutral_creeps/n_creep_gnoll/n_creep_gnoll_frost.vmdl" // Model.
"BaseClass" "npc_dota_creature"
"SoundSet" "n_creep_Ranged"
"GameSoundsFile" "soundevents/game_sounds_creeps.vsndevts"
"Level" "1"
"ModelScale" ".9"
// Abilities
//----------------------------------------------------------------
"Ability1" "" // Ability 1
"Ability2" "" // Ability 2
"Ability3" "" // Ability 3
"Ability4" "" // Ability 4
// Armor
//----------------------------------------------------------------
"ArmorPhysical" "10" // Physical protection.
// Attack
//----------------------------------------------------------------
"AttackCapabilities" "DOTA_UNIT_CAP_RANGED_ATTACK"
"AttackDamageMin" "30" // Damage range min.
"AttackDamageMax" "36" // Damage range max.
"AttackRate" "1.6" // Speed of attack.
"AttackAnimationPoint" "0.4" // Normalized time in animation cycle to attack.
"AttackAcquisitionRange" "800" // Range within a target can be acquired.
"AttackRange" "500" // Range within a target can be attacked.
"ProjectileModel" "particles/neutral_fx/gnoll_base_attack.vpcf" // Particle system model for projectile.
"ProjectileSpeed" "1500" // Speed of projectile.
// Bounds
//----------------------------------------------------------------
"RingRadius" "40"
"HealthBarOffset" "170"
// Bounty
//----------------------------------------------------------------
"BountyXP" "24" // Experience earn.
"BountyGoldMin" "21" // Gold earned min.
"BountyGoldMax" "29" // Gold earned max.
// Movement
//----------------------------------------------------------------
"MovementCapabilities" "DOTA_UNIT_CAP_MOVE_GROUND"
"MovementSpeed" "270" // Speed.
// Status
//----------------------------------------------------------------
"StatusHealth" "75" // Base health.
"StatusHealthRegen" "0.5" // Health regeneration rate.
"StatusMana" "0" // Base mana.
"StatusManaRegen" "0.0" // Mana regeneration rate.
// Vision
//----------------------------------------------------------------
"VisionDaytimeRange" "400" // Range of vision during day light.
"VisionNighttimeRange" "400" // Range of vision at night time.
// Team
//----------------------------------------------------------------
"TeamName" "DOTA_TEAM_GOODGUYS" // Team name.
"CombatClassAttack" "DOTA_COMBAT_CLASS_ATTACK_PIERCE"
"CombatClassDefend" "DOTA_COMBAT_CLASS_DEFEND_BASIC"
"UnitRelationshipClass" "DOTA_NPC_UNIT_RELATIONSHIP_TYPE_DEFAULT"
// Creature Data
//----------------------------------------------------------------
"Creature"
{
//Level Up
"HPGain" "50"
"DamageGain" "2"
"ArmorGain" "0.25"
"MagicResistGain" "0.1"
"MoveSpeedGain" "1"
"BountyGain" "3"
"XPGain" "15"
}
}
"example_unit_2"
{
// General
//----------------------------------------------------------------
"Model" "models/creeps/neutral_creeps/n_creep_gnoll/n_creep_gnoll_frost.vmdl" // Model.
"BaseClass" "npc_dota_creature"
"SoundSet" "n_creep_Ranged"
"GameSoundsFile" "soundevents/game_sounds_creeps.vsndevts"
"Level" "1"
"ModelScale" "1.4"
// Abilities
//----------------------------------------------------------------
"Ability1" "" // Ability 1
"Ability2" "" // Ability 2
"Ability3" "" // Ability 3
"Ability4" "" // Ability 4
// Armor
//----------------------------------------------------------------
"ArmorPhysical" "10" // Physical protection.
// Attack
//----------------------------------------------------------------
"AttackCapabilities" "DOTA_UNIT_CAP_RANGED_ATTACK"
"AttackDamageMin" "30" // Damage range min.
"AttackDamageMax" "36" // Damage range max.
"AttackRate" "1.6" // Speed of attack.
"AttackAnimationPoint" "0.4" // Normalized time in animation cycle to attack.
"AttackAcquisitionRange" "800" // Range within a target can be acquired.
"AttackRange" "500" // Range within a target can be attacked.
"ProjectileModel" "particles/neutral_fx/gnoll_base_attack.vpcf" // Particle system model for projectile.
"ProjectileSpeed" "1500" // Speed of projectile.
// Bounds
//----------------------------------------------------------------
"RingRadius" "40"
"HealthBarOffset" "170"
// Bounty
//----------------------------------------------------------------
"BountyXP" "24" // Experience earn.
"BountyGoldMin" "21" // Gold earned min.
"BountyGoldMax" "29" // Gold earned max.
// Movement
//----------------------------------------------------------------
"MovementCapabilities" "DOTA_UNIT_CAP_MOVE_GROUND"
"MovementSpeed" "270" // Speed.
// Status
//----------------------------------------------------------------
"StatusHealth" "75" // Base health.
"StatusHealthRegen" "0.5" // Health regeneration rate.
"StatusMana" "0" // Base mana.
"StatusManaRegen" "0.0" // Mana regeneration rate.
// Vision
//----------------------------------------------------------------
"VisionDaytimeRange" "400" // Range of vision during day light.
"VisionNighttimeRange" "400" // Range of vision at night time.
// Team
//----------------------------------------------------------------
"TeamName" "DOTA_TEAM_GOODGUYS" // Team name.
"CombatClassAttack" "DOTA_COMBAT_CLASS_ATTACK_PIERCE"
"CombatClassDefend" "DOTA_COMBAT_CLASS_DEFEND_BASIC"
"UnitRelationshipClass" "DOTA_NPC_UNIT_RELATIONSHIP_TYPE_DEFAULT"
// Creature Data
//----------------------------------------------------------------
"Creature"
{
//Level Up
"HPGain" "50"
"DamageGain" "2"
"ArmorGain" "0.25"
"MagicResistGain" "0.1"
"MoveSpeedGain" "1"
"BountyGain" "3"
"XPGain" "15"
}
}
Шаг №4. Устанавливаем базовые скрипты BMD.
Для работы того, что мы задумали, требуется отслеживание момента начала матча и таймер. Получить такие возможности можно благодаря написанию мода на предоставленной базе скриптов: https://github.com/MNoya/barebones/tree/source2/game/dota_addons/barebones/scripts/vscripts. Вам следует заменить свой addon_game_mode.lua на предоставленный, а так же закинуть в vscripts скрипты timers.lua и barebones.lua. Timers.lua - здесь понятно, он позволяет использовать таймеры, о них подробнее тут: http://customgames.ru/forum/index.php?topic=43.0. Barebones.lua дает возможность изменять некоторые параметры Вашего мода, отслеживать события и еще много-много другого.
Шаг №5. Пишем скрипт.
Открываем addon_game_mode.lua и вверху, над встроенной в него функцией 'Precache', создаем глобальные переменные:
GAME_ROUND = 0 - номер текущего раунда
MAX_ROUNDS = 5 - номер конечного раунда
ROUND_UNITS = 2 - кол-во юнитов на 1 раунде
MAX_ROUNDS = 5 - номер конечного раунда
ROUND_UNITS = 2 - кол-во юнитов на 1 раунде
function GameMode:OnGameInProgress() // Функция начнет выполняться, когда начнется матч( на часах будет 00:00 ).
local point = Entities:FindByName( nil, "spawnerino"):GetAbsOrigin() // Записываем в переменную 'point' координаты нашего спавнера 'spawnerino'
local waypoint = Entities:FindByName( nil, "way1") // Записываем в переменную 'waypoint' координаты первого бокса way1.
local return_time = 10 // Записываем в переменную значение '10'
Timers:CreateTimer(15, function() // Создаем таймер, который запустится через 15 секунд после начала матча и запустит следующую функцию.
GAME_ROUND = GAME_ROUND + 1 // Значение GAME_ROUND увеличивается на 1.
if GAME_ROUND == MAX_ROUNDS // Если GAME_ROUND равно MAX_ROUNDS, переменная return_time получит нулевое значение.
return_time = nil
end
Say(nil,"Wave №" .. GAME_ROUND, false) // Выводим в чат сообщение 'Wave №', в конце к которому добавится значение GAME_ROUND.
for i=1, ROUND_UNITS do // Произведет нижние действия столько раз, сколько указано в ROUND_UNITS. То есть в нашем случае создаст 2 юнита.
local unit = CreateUnitByName( "example_unit_" .. GAME_ROUND, point + RandomVector( RandomFloat( 0, 200 ) ), true, nil, nil, DOTA_TEAM_GOODGUYS ) // Создаем юнита 'example_unit_', в конце к названию добавится 1,2,3,4 или 5, в зависимости от раунда, и в итоге получатся наши example_unit_1, example_unit_2 и т.д.. Юнит появится в векторе point + RandomVector( RandomFloat( 0, 200 ) ) - point - наша переменная, а рандомный вектор добавляется для того, чтобы мобы не появлялись в одной точке и не застревали. Мобы будут за силы света.
unit:SetInitialGoalEntity( waypoint ) // Посылаем мобов на наш way1, координаты которого мы записали в переменную 'waypoint'
end
return return_time // Возвращаем таймеру время, через которое он должен снова сработать. Когда пройдет последний раунд таймер получит значение 'nil' и выключится.
end)
end
local point = Entities:FindByName( nil, "spawnerino"):GetAbsOrigin() // Записываем в переменную 'point' координаты нашего спавнера 'spawnerino'
local waypoint = Entities:FindByName( nil, "way1") // Записываем в переменную 'waypoint' координаты первого бокса way1.
local return_time = 10 // Записываем в переменную значение '10'
Timers:CreateTimer(15, function() // Создаем таймер, который запустится через 15 секунд после начала матча и запустит следующую функцию.
GAME_ROUND = GAME_ROUND + 1 // Значение GAME_ROUND увеличивается на 1.
if GAME_ROUND == MAX_ROUNDS // Если GAME_ROUND равно MAX_ROUNDS, переменная return_time получит нулевое значение.
return_time = nil
end
Say(nil,"Wave №" .. GAME_ROUND, false) // Выводим в чат сообщение 'Wave №', в конце к которому добавится значение GAME_ROUND.
for i=1, ROUND_UNITS do // Произведет нижние действия столько раз, сколько указано в ROUND_UNITS. То есть в нашем случае создаст 2 юнита.
local unit = CreateUnitByName( "example_unit_" .. GAME_ROUND, point + RandomVector( RandomFloat( 0, 200 ) ), true, nil, nil, DOTA_TEAM_GOODGUYS ) // Создаем юнита 'example_unit_', в конце к названию добавится 1,2,3,4 или 5, в зависимости от раунда, и в итоге получатся наши example_unit_1, example_unit_2 и т.д.. Юнит появится в векторе point + RandomVector( RandomFloat( 0, 200 ) ) - point - наша переменная, а рандомный вектор добавляется для того, чтобы мобы не появлялись в одной точке и не застревали. Мобы будут за силы света.
unit:SetInitialGoalEntity( waypoint ) // Посылаем мобов на наш way1, координаты которого мы записали в переменную 'waypoint'
end
return return_time // Возвращаем таймеру время, через которое он должен снова сработать. Когда пройдет последний раунд таймер получит значение 'nil' и выключится.
end)
end
Шаг №6. Делаем предварительное кеширование юнитов.
Кэшируем только example_unit_1 - этого будет достаточно. В addon_game_mode.lua от ВMD вверху уже есть ф-ция 'Precache', просто вставляем в неё:
PrecacheUnitByNameSync("example_unit_1", context)
Советы:
• Советую для ознакомления два открытых на гитхабе популярных проекта:
Warchasers: https://github.com/MNoya/Warchasers
Жизнь на Арене: https://github.com/ZLOY5/LiA
Скачайте их, распакуйте и установите программу Far 3. И когда становится тяжело понять, как описать ту или иную команду или функцию - заходите через Far в корень скачанных проектов, жмите alt + F7. В первом поле вводите "*.*", во втором - то, что нужно найти, например "OnAttacked".
• На ГитХабе в поиске так же всегда можно поискать интересующие команды и функции. Также для поиска и вообще "почитать" советую https://moddota.com
• На сайте https://moddota.com создатель Warchasers - Noya, оформляет качественные гайды, несколько из них:
http://moddota.com/forums/discussion/14/datadriven-ability-breakdown-documentation
https://moddota.com/forums/discussion/4/datadriven-items
http://moddota.com/forums/discussion/93/point-channeling-aoe-ability-example
https://moddota.com/forums/discussion/69/particle-attachment
• Ну и конечно же - по возможности старайтесь создавать свои или переводить чужие гайды, так как то, что Вам кажется очевидным, кому-то придется понимать на забугорских сайтах 2 а то и больше дней, так как российское сообщество D2WT ушло не очень далеко от нулевого уровня.
Автор - Slavko.
На написание гайда натолкнуло: https://moddota.com/forums/discussion/comment/476/#Comment_476[
На написание гайда натолкнуло: https://moddota.com/forums/discussion/comment/476/#Comment_476[
Последнее редактирование модератором: