Урок Вот вам Spawner

METABYTE

Пользователь
26 Фев 2023
25
0
Lua:
if Spawner == nil then
    Spawner = {}
    Spawner.__index = Spawner
    print('[Spawner] Spawner Created')
end

function Spawner:Init()
    print('[Spawner] Spawner Init')
    self:ReadKeyValue()
    self:Setting()
    ListenToGameEvent('entity_killed', Dynamic_Wrap(Spawner, 'OnUnitKilled'), self)
    LinkLuaModifier("modifier_creep_antilag", "modifiers/modifier_creep_antilag.lua", LUA_MODIFIER_MOTION_NONE)
    LinkLuaModifier("modifier_no_health", "modifiers/modifier_no_health.lua", LUA_MODIFIER_MOTION_NONE)
end

function Spawner:ReadKeyValue()
    print('[Spawner] Reading KeyValues from spawn_info.kv')
    self.kvTable = LoadKeyValues("scripts/kv/spawn_info.kv")
end

function Spawner:Setting()
    print('[Spawner] Setting Up Spawner')
    local kv = self.kvTable

    self.natureList = kv.nature_list
    self.natureSpawnInterval = tonumber(kv.spawn_interval)
    self.natureRemaining = {}
    self.natureName = {}
    self.natureBossName = {}
    self.natureMaxCount = {}
    self.natureBossSpawnRate = {}
    self.natureCampLocation = {}

    for k, v in pairs(self.natureList) do
        self.natureRemaining[k] = {}
        self.natureName[k] = v.unit.name
        self.natureBossName[k] = v.boss.name
        self.natureMaxCount[k] = tonumber(v.maxCount)
        self.natureBossSpawnRate[k] = tonumber(v.bossRate)
        self.natureCampLocation[k] = Entities:FindByName(nil, v.campName):GetAbsOrigin()
    end
end

function Spawner:UnitProperty(unit, unitData, isBoss)
    local experienceMultiplier = tonumber(unitData.experienceMultiplier)
    local goldBountyMultiplier = tonumber(unitData.goldBountyMultiplier)
    local damageMultiplier = tonumber(unitData.damageMultiplier)
    local armorMultiplier = tonumber(unitData.armorMultiplier)
    local healthMultiplier = tonumber(unitData.healthMultiplier)

    unit:SetDeathXP(unit:GetDeathXP() * experienceMultiplier)
    unit:SetMinimumGoldBounty(unit:GetMinimumGoldBounty() * goldBountyMultiplier)
    unit:SetMaximumGoldBounty(unit:GetMaximumGoldBounty() * goldBountyMultiplier)
    unit:SetBaseDamageMin(unit:GetBaseDamageMin() * damageMultiplier)
    unit:SetBaseDamageMax(unit:GetBaseDamageMax() * damageMultiplier)
    unit:SetBaseMaxHealth(unit:GetBaseMaxHealth() * healthMultiplier)
    unit:SetPhysicalArmorBaseValue(unit:GetPhysicalArmorBaseValue() * armorMultiplier)

    for i = 1, 16 do
        local ability = unit:GetAbilityByIndex(i - 1)
        if ability then
            ability:SetLevel(5)
        end
    end

    -- Применяем модификаторы, если они указаны в .kv файле
    if unitData.modifiers then
        for modifierName, _ in pairs(unitData.modifiers) do
            unit:AddNewModifier(unit, nil, modifierName, nil)
            print('[Spawner] Applied modifier ' .. modifierName .. ' to ' .. unit:GetUnitName())
        end
    end
end

function Spawner:NatureStart()
    print('[Spawner] Nature Spawner Started')
    self:NatureThink()
end

function Spawner:NatureThink()
    print('[Spawner] Nature Think')
    Timers:CreateTimer(function()
        for k, v in pairs(self.natureList) do
            if #self.natureRemaining[k] == 0 then
                self:DoNatureSpawn(k)
            end
        end
        return self.natureSpawnInterval
    end)
end

function Spawner:DoNatureSpawn(k)
    print('[Spawner] Spawning Units for Location: ' .. k)
    local locationData = self.natureList[k]
    local unitData = locationData.unit
    local bossData = locationData.boss

    for i = 1, self.natureMaxCount[k] do
        local point = self.natureCampLocation[k]
        local random_point = Vector(point.x + RandomFloat(-200, 200), point.y + RandomFloat(-200, 200), point.z)

        local spawnData = RollPercentage(locationData.bossRate) and bossData or unitData
        PrecacheUnitByNameAsync(spawnData.name, function()
            local unit = CreateUnitByName(spawnData.name, random_point, true, nil, nil, DOTA_TEAM_BADGUYS)

            self:UnitProperty(unit, spawnData, spawnData == bossData)
            unit.campIndex = k
            table.insert(self.natureRemaining[k], unit)
            --print('[Spawner] Spawned Unit: ' .. spawnData.name .. ' at Location: ' .. k)
        end)
    end
end

function Spawner:OnUnitKilled(t)
    local caster = EntIndexToHScript(t.entindex_killed)

    if caster.campIndex then
        for i, unit in pairs(self.natureRemaining[caster.campIndex]) do
            if caster == unit then
                table.remove(self.natureRemaining[caster.campIndex], i)
                break
            end
        end
    end
    print('[Spawner] Unit Killed: ' .. caster:GetUnitName())
end
А это .kv с которым он работает

JSON:
"Spawns"
{
    "spawn_interval" "120"
    "nature_list"
    {
        "forest"
        {
            "unit"
            {
                "name" "npc_dota_forest_1_creep"
                "experienceMultiplier" "1.6"
                "goldBountyMultiplier" "0.6"
                "damageMultiplier" "1.0"
                "armorMultiplier" "1.0"
                "healthMultiplier" "1.0"
                "modifiers"
                {
                    "modifier_creep_antilag" "modifier_creep_antilag"
                    "modifier_no_health" "modifier_no_health"
                }
            }
            "boss"
            {
                "name" "npc_dota_forest_1_boss"
                "experienceMultiplier" "2.0"
                "goldBountyMultiplier" "1.0"
                "damageMultiplier" "1.5"
                "armorMultiplier" "2.0"
                "healthMultiplier" "2.5"
                "modifiers"
                {
                    "modifier_creep_antilag" "modifier_creep_antilag"
                    "modifier_no_health" "modifier_no_health"
                }
            }
            "maxCount" "4"
            "bossRate" "20"
            "campName" "forest_loc"
        }
        "forest_1"
        {
            "unit"
            {
                "name" "npc_dota_forest_1_creep"
                "experienceMultiplier" "1.6"
                "goldBountyMultiplier" "0.6"
                "damageMultiplier" "1.0"
                "armorMultiplier" "1.0"
                "healthMultiplier" "1.0"
                "modifiers"
                {
                    "modifier_creep_antilag" "modifier_creep_antilag"
                    "modifier_no_health" "modifier_no_health"
                }
            }
            "boss"
            {
                "name" "npc_dota_forest_1_boss"
                "experienceMultiplier" "2.0"
                "goldBountyMultiplier" "1.0"
                "damageMultiplier" "1.5"
                "armorMultiplier" "2.0"
                "healthMultiplier" "2.5"
                "modifiers"
                {
                    "modifier_creep_antilag" "modifier_creep_antilag"
                    "modifier_no_health" "modifier_no_health"
                }
            }
            "maxCount" "4"
            "bossRate" "20"
            "campName" "forest_loc1"
        }
        "forest_2"
        {
            "unit"
            {
                "name" "npc_dota_forest_1_creep"
                "experienceMultiplier" "1.6"
                "goldBountyMultiplier" "0.6"
                "damageMultiplier" "1.0"
                "armorMultiplier" "1.0"
                "healthMultiplier" "1.0"
                "modifiers"
                {
                    "modifier_creep_antilag" "modifier_creep_antilag"
                    "modifier_no_health" "modifier_no_health"
                }
            }
            "boss"
            {
                "name" "npc_dota_forest_1_boss"
                "experienceMultiplier" "2.0"
                "goldBountyMultiplier" "1.0"
                "damageMultiplier" "1.5"
                "armorMultiplier" "2.0"
                "healthMultiplier" "2.5"
                "modifiers"
                {
                    "modifier_creep_antilag" "modifier_creep_antilag"
                    "modifier_no_health" "modifier_no_health"
                }
            }
            "maxCount" "4"
            "bossRate" "20"
            "campName" "forest_loc2"
        }
        "forest_3"
        {
            "unit"
            {
                "name" "npc_dota_forest_1_creep"
                "experienceMultiplier" "1.6"
                "goldBountyMultiplier" "0.6"
                "damageMultiplier" "1.0"
                "armorMultiplier" "1.0"
                "healthMultiplier" "1.0"
                "modifiers"
                {
                    "modifier_creep_antilag" "modifier_creep_antilag"
                    "modifier_no_health" "modifier_no_health"
                }
            }
            "boss"
            {
                "name" "npc_dota_forest_1_boss"
                "experienceMultiplier" "2.0"
                "goldBountyMultiplier" "1.0"
                "damageMultiplier" "1.5"
                "armorMultiplier" "2.0"
                "healthMultiplier" "2.5"
                "modifiers"
                {
                    "modifier_creep_antilag" "modifier_creep_antilag"
                    "modifier_no_health" "modifier_no_health"
                }
            }
            "maxCount" "4"
            "bossRate" "20"
            "campName" "forest_loc3"
        }
    }
}
Сам код я изначально подрезал у китайцев. Немного прокачал его.
Теперь он позволяет редактировать параметры юнитов на каждой локации
 
Забыл для юных падаванов сказать, что вам нужно на карте создавать триггеры с именем из "campName".
 
Ну нихуя се, машинное обучение в деле, ни одного комментария и объяснения

Spawner.__index = Spawner написанный в типичном GPT формате, с __index - двумя "_"
 
Ты поставил тэг - урок , хотя это исходник
Урок в понятии программирования это наличие объяснений к тому , что ты пишешь
Ладно люди которые тут понимают, хотя бы часть кода или весь код, вот новичок зайдет такой, ООО урок ебать, а там в тупую исходный код китайцев , спасибо папаша что написал комментарии

Хотя если это делали и китайцы , то там должны были быть комментарии к коду ибо они любят все описывать
 
Ты поставил тэг - урок , хотя это исходник
Урок в понятии программирования это наличие объяснений к тому , что ты пишешь
Ладно люди которые тут понимают, хотя бы часть кода или весь код, вот новичок зайдет такой, ООО урок ебать, а там в тупую исходный код китайцев , спасибо папаша что написал комментарии

Хотя если это делали и китайцы , то там должны были быть комментарии к коду ибо они любят все описывать
Всё верно, если это урок то оформлен должен быть соответствующе.
А еще лучше с примером github кастомки, где реализована эта конкретная фича
 
Нам нужна либа timers для создания таймеров, ее найдёте в исходниках каких нибудь абилок от меня где я прикреплял либу например тут

Открываем Хаммер вашу карту и на ней размещаем info_target указываем ее наименование , как указали локацию в примере "forest" локация спавна готова, так же босс насколько понял требует отдельной точки "forest_loc"

Создаем юнитов в npc_units_custom.txt
Обязательные параметры:
"BountyXP""57"// кол-во exp за юнита
"BountyGoldMin""34"// мин. голды за юнита
"BountyGoldMax""39"// макс. голды за юнита
"StatusHealth""550"// базовое значение хп
"AttackDamageMin""19"// мин. урон юнита
"AttackDamageMax""23"// макс. урон юнита
"ArmorPhysical""2"// базовая броня юнита

Lua:
 -- Создаем класс Spawner
if Spawner == nil then
    Spawner = {}
    Spawner.__index = Spawner
    print('[Spawner] Spawner Created')
end

-- Инициализация Spawner
function Spawner:Init()
    print('[Spawner] Spawner Init')
    self:ReadKeyValue() -- Читаем данные из файла spawn_info.kv
    self:Setting() -- Настраиваем спавнер
    ListenToGameEvent('entity_killed', Dynamic_Wrap(Spawner, 'OnUnitKilled'), self) -- Слушаем событие смерти юнита
    LinkLuaModifier("modifier_creep_antilag", "modifiers/modifier_creep_antilag.lua", LUA_MODIFIER_MOTION_NONE) -- Подключаем модификаторы
    LinkLuaModifier("modifier_no_health", "modifiers/modifier_no_health.lua", LUA_MODIFIER_MOTION_NONE)
end

-- Чтение данных из файла spawn_info.kv
function Spawner:ReadKeyValue()
    print('[Spawner] Reading KeyValues from spawn_info.kv')
    self.kvTable = LoadKeyValues("scripts/kv/spawn_info.kv")
end

-- Настройка спавнера
function Spawner:Setting()
    print('[Spawner] Setting Up Spawner')
    local kv = self.kvTable

    self.natureList = kv.nature_list -- Список локаций спавна
    self.natureSpawnInterval = tonumber(kv.spawn_interval) -- Интервал спавна
    self.natureRemaining = {} -- Оставшиеся юниты на локации
    self.natureName = {} -- Название юнитов
    self.natureBossName = {} -- Название боссов
    self.natureMaxCount = {} -- Максимальное количество юнитов
    self.natureBossSpawnRate = {} -- Шанс спавна босса
    self.natureCampLocation = {} -- Местоположение лагеря

    for k, v in pairs(self.natureList) do
        self.natureRemaining[k] = {}
        self.natureName[k] = v.unit.name
        self.natureBossName[k] = v.boss.name
        self.natureMaxCount[k] = tonumber(v.maxCount)
        self.natureBossSpawnRate[k] = tonumber(v.bossRate)
        self.natureCampLocation[k] = Entities:FindByName(nil, v.campName):GetAbsOrigin()
    end
end

-- Настройка свойств юнита
function Spawner:UnitProperty(unit, unitData, isBoss)
    local experienceMultiplier = tonumber(unitData.experienceMultiplier) -- Множитель exp за юнита
    local goldBountyMultiplier = tonumber(unitData.goldBountyMultiplier) -- Множитель голды за юнита
    local damageMultiplier = tonumber(unitData.damageMultiplier) -- Множитель урона юнита
    local armorMultiplier = tonumber(unitData.armorMultiplier) -- Множитель брони юнита
    local healthMultiplier = tonumber(unitData.healthMultiplier) -- Множитель хп юнита

    unit:SetDeathXP(unit:GetDeathXP() * experienceMultiplier)  -- xp за смерть юнита * множитель итог exp за юнита
    unit:SetMinimumGoldBounty(unit:GetMinimumGoldBounty() * goldBountyMultiplier)  -- минимальная голда за юнита * множитель юнита итоговая голда за юнита
    unit:SetMaximumGoldBounty(unit:GetMaximumGoldBounty() * goldBountyMultiplier) -- максимальная голда за юнита * множитель юнита итоговая голда за юнита
    unit:SetBaseDamageMin(unit:GetBaseDamageMin() * damageMultiplier) -- минимальный урон юнита * множитель итоговый мин. урон юнита
    unit:SetBaseDamageMax(unit:GetBaseDamageMax() * damageMultiplier)  -- максимальный урон юнита * множитель итоговый мах. урон юнита
    unit:SetBaseMaxHealth(unit:GetBaseMaxHealth() * healthMultiplier)  -- хп юнита * множитель итоговое хп юнита
    unit:SetPhysicalArmorBaseValue(unit:GetPhysicalArmorBaseValue() * armorMultiplier)  -- броня юнита * множитель итоговая броня юнита

    for i = 1, 16 do
        local ability = unit:GetAbilityByIndex(i - 1)
        if ability then
            ability:SetLevel(5)
        end
    end

    -- Применяем модификаторы, если они указаны в .kv файле
    if unitData.modifiers then
        for modifierName, _ in pairs(unitData.modifiers) do
            unit:AddNewModifier(unit, nil, modifierName, nil)
            print('[Spawner] Applied modifier ' .. modifierName .. ' to ' .. unit:GetUnitName())
        end
    end
end

-- Запуск спавна юнитов
function Spawner:NatureStart()
    print('[Spawner] Nature Spawner Started')
    self:NatureThink()
end

-- Цикл спавна юнитов
function Spawner:NatureThink()
    print('[Spawner] Nature Think')
    Timers:CreateTimer(function()
        for k, v in pairs(self.natureList) do
            if #self.natureRemaining[k] == 0 then
                self:DoNatureSpawn(k)
            end
        end
        return self.natureSpawnInterval
end)
end

-- Спавн юнитов на локации
function Spawner:DoNatureSpawn(k)
    print('[Spawner] Spawning Units for Location: ' .. k)
    local locationData = self.natureList[k]
    local unitData = locationData.unit
    local bossData = locationData.boss

    for i = 1, self.natureMaxCount[k] do
        local point = self.natureCampLocation[k]
        local random_point = Vector(point.x + RandomFloat(-200, 200), point.y + RandomFloat(-200, 200), point.z)
        local spawnData = RollPercentage(locationData.bossRate) and bossData or unitData -- Определяем, будет ли спавниться босс или обычный юнит

        PrecacheUnitByNameAsync(spawnData.name, function()
            local unit = CreateUnitByName(spawnData.name, random_point, true, nil, nil, DOTA_TEAM_BADGUYS)

            self:UnitProperty(unit, spawnData, spawnData == bossData) -- Настраиваем свойства юнита
            unit.campIndex = k
            table.insert(self.natureRemaining[k], unit)
            --print('[Spawner] Spawned Unit: ' .. spawnData.name .. ' at Location: ' .. k)
        end)
    end
end

-- Обработка события смерти юнита
function Spawner:OnUnitKilled(t)
    local caster = EntIndexToHScript(t.entindex_killed)

    if caster.campIndex then
        for i, unit in pairs(self.natureRemaining[caster.campIndex]) do
            if caster == unit then
                table.remove(self.natureRemaining[caster.campIndex], i)
                break
            end
        end
    end
    print('[Spawner] Unit Killed: ' .. caster:GetUnitName())
end
Код:
"Spawns"
{
    "spawn_interval" "120" // интервал спавнеров
    "nature_list" // массив
    {
        "forest" // локация
        {
            "unit" // юниты
            {
                "name" "npc_dota_forest_1_creep" // юнит из npc_units_custom.txt
                "experienceMultiplier" "1.6" // множитель exp
                "goldBountyMultiplier" "0.6" // множитель Голды
                "damageMultiplier" "1.0" // множитель урона
                "armorMultiplier" "1.0" // множитель брони
                "healthMultiplier" "1.0" // множитель хп
                "modifiers" // модификаторы
                {
                    "modifier_creep_antilag" "modifier_creep_antilag"
                    "modifier_no_health" "modifier_no_health"
                }
            }
            "boss" // боссы локации
            {
                "name" "npc_dota_forest_1_boss" // название босса из txt
                "experienceMultiplier" "2.0" // множитель exp
                "goldBountyMultiplier" "1.0" // множитель голды
                "damageMultiplier" "1.5" // множитель дамага
                "armorMultiplier" "2.0" // множитель армора
                "healthMultiplier" "2.5" // множитель хп
                "modifiers" // модификаторы
                {
                    "modifier_creep_antilag" "modifier_creep_antilag"
                    "modifier_no_health" "modifier_no_health"
                }
            }
            "maxCount" "4" // максимальное кол-во
            "bossRate" "20" // шанс спавна
            "campName" "forest_loc" // название Кемпа спавна
        }
}
}

}
 
Последнее редактирование:
При этом ты не дал 2 модификатора:
modifier_creep_antilag.lua
modifier_no_health.lua

Скорее всего их цель:
Антилаг убивает крипов если их кол-во превысит какое то значение на локации в связи с тем, что кол-во их не ограничено в спавне раз в 120 сек

Нет хп скорее всего убивает крипа которого не добили в процессе и он имеет менее 75% здоровья и его Реген хп менее 3+
 
Последнее редактирование:
Вообще скорее всего должно быть так:
Lua:
setmetatable(Spawner, { __index = Spawner})
Но это тоже бессмысленно
 
А для чего эта строка вообще нужна?
В данном коде в языке программирования LUA создается класс Spawner и устанавливается его метатаблица (__index) на самого себя (Spawner).
Метатаблица в LUA используется для определения поведения таблицы при выполнении определенных операций, таких как доступ к несуществующим ключам или вызов функций на таблице.
В данном случае, установка метатаблицы на класс Spawner позволяет использовать его методы и свойства при создании объектов этого класса, даже если они не определены явно в объекте.

Это уже чистый LUA, без обращения грубо говоря к API доты
 
Ну это не верное описание, нет там никаких метатаблиц
Я выше показал как это должно выглядеть, чтобы было хотя бы синтаксически верно
 
Реклама: