Урок Шаблон "Hero defense"

vulkantsk

Администратор
Команда форума
21 Июн 2017
1,247
205
www.dotabuff.com
Проект
Roshan defense
1663432731935.pngСегодня я решил поделиться с вами одной из самых крутых своих наработок, которая объеденяет большую часть всех гайдов на форуме.
Данная наработка будет полезна абсолютно всем новичкам и даже модерам с опытом.
С помошью данного шаблона вы сможете создать очень крутой херо дефенс (Moo Moo я жду тебя ;))
Вот собственно сама карта:
hero_defense_map.png


Особенности шаблона:
  • настройки мода: начальный уровень героя ,длительность возрождения и многое другое
  • система выпадения предметов
  • система лайновых крипов
  • система порталов
  • локализации русская/английская
  • возрождение нейтральных крипов
  • поведение юнитов AI

Система выпадения предметов:

  • файл: scripts/vscripts/item_drop
  • items - список предметов, которые могут выпасть
  • chance - шанс выпадения (если не определен, то равен 100%)
  • список юнитов из которых могут выпасть предметы ( если не определен, то выпадает с любого юнита)
  • duration - длительность жизни предмета на полу( если не определен, то предмет лежит бесконечно)
  • limit - лимит предметов - количество предметов, которые могут выпасть( если не определен, то количество не ограничено)

Система лайновых крипов
  • файл: scripts/vscripts/gamemode
  • reward gold - награда золота за прохождение волны
  • reward exp - награда опыта за прохождение волны
  • units - список юнитов, которые будут идти по линии
Возрождение нейтральных крипов
  • файл: scripts/vscripts/abilities/respawn
  • weak - возрождается на месте, где его убили через 5 секунд
  • strong - возрождается на месте, где появился
  • boss - возрождается на месте, где появился. После возрождения улучшается
Поведение юнитов
  • файл: scripts/vscripts/ai/...
  • поведение нейтрального босса 1/2/3 уровня
  • поведение лайнового босса
Надеюсь данный "урок" будет вам полезен, так как я вложил много времени и сил на его создание.
Может быть скоро мы увидим новых энтузиастов с горящими глазами, которые смогут создать годный Hero Defense (например MooMoo)
Ах да у меня там много мусора в файлах, было оченб сильно лень так что не обессудьте :cool:
Всем пока, всем удачи !

Ссылка на стим
Ссылка на гитхаб
 
Последнее редактирование:
вау,супер, просто класс! Ставьте все лайки! В топ! Ваааау, это просто крутейбл
 
Возьму вашу базу AI,спасибо,помогите пожалуйста в личных сообщениях с выбором героев.
 
Всем привет !
Сделал по вашему примеру, все работает. Но нужно немного другое и тут загвоздка...
В целом затея такая после спавна в рандомной точке генериться энтити в луа и в неё идут мобы, после того как доходят нужно перенаправить их в новую рандомную точку
но они категорически отказываются следовать туда , игорируя unit:SetInitialGoalEntity( waypoint )
Код:
require( 'timer' )

if GameMode == nil then
    _G.GameMode = class({})
end


GameMode.current_units = {}
GameMode.line_interval = {}
GameMode.wave_number = 0

GameMode.wave_list = {
    [1]={reward_gold=1000,reward_exp=500,
            units={["npc_dota_creep_goodguys_melee_upgraded_mega"]=6}},
    [2]={reward_gold=5000,reward_exp=1000,
            units={["npc_dota_hero_slardar"]=2}},
    -- [3]={reward_gold=1000,reward_exp=2000,
    --         units={"npc_line_boss_1",["npc_line_creep_4"]=2}},
}

function Precache( context )
    --[[
        Precache things we know we'll use.  Possible file types include (but not limited to):
            PrecacheResource( "model", "*.vmdl", context )
            PrecacheResource( "soundfile", "*.vsndevts", context )
            PrecacheResource( "particle", "*.vpcf", context )
            PrecacheResource( "particle_folder", "particles/folder", context )
    ]]
end

-- Create the game mode when we activate
function Activate()
    GameRules.AddonTemplate = GameMode()
    GameRules.AddonTemplate:InitGameMode()
end

function GameMode:InitGameMode()
    print( "Template addon is loaded. GameMode:InitGameMode()" )
    ListenToGameEvent('game_rules_state_change', Dynamic_Wrap(self, 'OnGameRulesStateChange'), self)
    GameRules:GetGameModeEntity():SetThink( "OnThink", self, "GlobalThink", 2 )
    ListenToGameEvent('entity_killed', Dynamic_Wrap(self, 'OnEntityKilled'), self)
end

function GameMode:OnGameRulesStateChange()
    local newState = GameRules:State_Get()
    print("State "..newState)
    GameRules:SendCustomMessage("#State "..newState,0,0)
if newState == 7 then
    print("SPAAAAAAAAAAAAAAAAAAWWN ")
        GameMode:LineBossSpawner()
    end
    -- if newState == DOTA_GAMERULES_STATE_GAME_IN_PROGRESS then
    --     GameMode:LineBossSpawner()
    -- end
end

function GameMode:LineBossSpawner()
     self.wave_number = self.wave_number + 1   
    local current_boss = self.wave_list[self.wave_number]

    --if current_boss == nil then
    --    GameRules:SetGameWinner(DOTA_TEAM_GOODGUYS)
    --    return
    --end

    GameMode:SpawnLineUnits(self.wave_number)
end

function GameMode:SpawnLineUnits(index)
    local current_wave = self.wave_list[index]
    local flag = 1;
    if current_wave == nil then
    print( "Array of wawes index out of range")
        return
    end

    local point = Entities:FindByName( nil, "z1"):GetAbsOrigin()
    -- local POINTS = class({})
    -- for i=0,10 do
    --     -- POINTS.vectors[i]= Vector(RandomInt(-2000,2000),RandomInt(-2000,2000),RandomInt(0,0))
    --     -- POINTS[i] = Entities:CreateByClassname ("path_corner"):SetAbsOrigin(Vector(RandomInt(-2000,2000),RandomInt(-2000,2000),RandomInt(0,0)))
 --        -- print (POINTS[i]:GetAbsOrigin())
 --        print ("i = "..i)
    -- end
    
    local v = Vector(RandomInt(-2000,2000),RandomInt(-2000,2000),RandomInt(0,0))
    local v2 = Entities:CreateByClassname ("path_corner")
    v2:SetAbsOrigin(v)
    print (v2:GetAbsOrigin())


    local waypoint = v2 --Entities:FindByName( nil, "p1")
    local units = current_wave.units

    for key, value in pairs (units) do
        if type(key) == "string" then
            unit_count = value
            unit_name = key
            else
            unit_count =1
            unit_name = value
        end
                        -- print ("self.current_units[ent_index]")
        for i=1, unit_count do
            local unit = CreateUnitByName( unit_name , point + RandomVector( RandomFloat( 0, 200 ) ), true, nil, nil, DOTA_TEAM_BADGUYS )
            unit:SetInitialGoalEntity( waypoint )
            unit:MoveToPosition( v )
            unit.reward = true
            local ent_index = unit:entindex()
            -- table.insert(self.current_units, ent_index, unit)
            self.current_units[ent_index]= unit
-- local q1  = self.current_units[ent_index]:GetUnitName();
--                         print ("index "..ent_index)
--                         print (self.current_units[ent_index])
--                         print ( q1)
        end
    end

    -- тута будет какоенить  умное  зацикливание
    Timers:CreateTimer(15, function()
        GameRules:SendCustomMessage("#kanec ",0,0)
        local r = Vector(RandomInt(-2000,2000),RandomInt(-2000,2000),RandomInt(0,0))
        local r2 = Entities:CreateByClassname ("path_corner")
        r2:SetAbsOrigin(r)
        print (r2:GetAbsOrigin())
        local XX = FindUnitsInRadius(DOTA_TEAM_BADGUYS,v2:GetAbsOrigin(), nil, 350, DOTA_UNIT_TARGET_TEAM_FRIENDLY, DOTA_UNIT_TARGET_BASIC, 0, 0, false )
        if XX ~= nil then
            v2:Destroy()
            for k, unit in pairs(XX) do
                unit:SetInitialGoalEntity( r2 )               
                -- unit:Stop()
                -- unit:MoveToPosition( r )
                print ("unit name =  "..unit:GetUnitName())
            end
        end   
        GameRules:SendCustomMessage("#kanec2 ",0,0)
    end) 
end

-- Evaluate the state of the game
function GameMode:OnThink()
    if GameRules:State_Get() == DOTA_GAMERULES_STATE_GAME_IN_PROGRESS then
        --print( "Template addon script is running." )
    elseif GameRules:State_Get() >= DOTA_GAMERULES_STATE_POST_GAME then
        return nil
    end
    return 1
end

function GameMode:OnEntityKilled(keys)

    local unit = EntIndexToHScript(keys.entindex_killed)
    local unit_name = unit:GetUnitName()
    
    if unit_name == "npc_goodguys_fort" then
        GameRules:SetGameWinner(DOTA_TEAM_BADGUYS)       
    end

    if unit.reward then
        local ent_index = unit:entindex()
        
        self.current_units[ent_index] = nil
        local units = 0
        for key,value in pairs(self.current_units) do
            units = units + 1
        end

        if units == 0 then
            local current_wave = self.wave_list[self.wave_number]
            local reward_gold = current_wave.reward_gold
            local reward_exp = current_wave.reward_exp

            GiveGoldPlayers( reward_gold )
            GiveExperiencePlayers( reward_exp )
            GameMode:LineBossSpawner()
        end
    end
end

function GiveGoldPlayers( gold )
    for index=0 ,4 do
        if PlayerResource:HasSelectedHero(index) then
            local player = PlayerResource:GetPlayer(index)
            local hero = PlayerResource:GetSelectedHeroEntity(index)
            hero:ModifyGold(gold, false, 0)
            SendOverheadEventMessage( player, OVERHEAD_ALERT_GOLD, hero, gold, nil )
        end
    end
end


function GiveExperiencePlayers( experience )
    for index=0 ,4 do
        if PlayerResource:HasSelectedHero(index) then
            local player = PlayerResource:GetPlayer(index)
            local hero = PlayerResource:GetSelectedHeroEntity(index)
            hero:AddExperience(experience, 0, false, true )
        end
    end
end

Если есть возможность устроить это же через приказы , было бы весьма не плохо!)
Но почемуто нифига не работает )
 
Ну так это свою логику писать надо, возьми задачу поменьше или корректней.
 
Если есть возможность устроить это же через приказы , было бы весьма не плохо!)
Код:
        ExecuteOrderFromTable({
            UnitIndex = hUnit:entindex(),
            OrderType = DOTA_UNIT_ORDER_MOVE_TO_POSITION,
            Position = vPos,
            Queue = 0
        })
Если в Queue поставить 1, то можно несколько раз подряд это через цикл заюзать, и юнит будет перемещаться между точками в указанном порядке. Но размер очереди ограничен.
Можешь попробовать вместо DOTA_UNIT_ORDER_MOVE_TO_POSITION использовать DOTA_UNIT_ORDER_PATROL, тогда по идее юнит вообще не остановится никогда.
 
А может можно как то в lua next point задать ?
 
Скорее всего можно с помощью SpawnEntityFromTableSynchronous(string string_1, handle handle_2) и подобных (посмотри примеры на гитхабе). По идее в таблицу можно указать любой параметр, который можно задать в хаммере, надеясь, что у него такой же ключ, что и в хаммере. Но я не уверен в этом, нужно эксперементировать. Да и не понятно, зачем это вообще нужно, если приказы.
 
А как сделать таким образом союзные волны крипов?
 
Сегодня я решил поделиться с вами одной из самых крутых своих наработок, которая объеденяет большую часть всех гайдов на форуме.
Данная наработка будет полезна абсолютно всем новичкам и даже модерам с опытом.
С помошью данного шаблона вы сможете создать очень крутой херо дефенс (Moo Moo я жду тебя ;))
Вот собственно сама карта:


Особенности шаблона:
  • настройки мода: начальный уровень героя ,длительность возрождения и многое другое
  • система выпадения предметов
  • система лайновых крипов
  • локализации русская/английская
  • возрождение нейтральных крипов
  • поведение юнитов AI

Система выпадения предметов:

  • файл: scripts/vscripts/item_drop
  • items - список предметов, которые могут выпасть
  • chance - шанс выпадения (если не определен, то равен 100%)
  • список юнитов из которых могут выпасть предметы ( если не определен, то выпадает с любого юнита)
  • duration - длительность жизни предмета на полу( если не определен, то предмет лежит бесконечно)
  • limit - лимит предметов - количество предметов, которые могут выпасть( если не определен, то количество не ограничено)

Система лайновых крипов
  • файл: scripts/vscripts/gamemode
  • reward gold - награда золота за прохождение волны
  • reward exp - награда опыта за прохождение волны
  • units - список юнитов, которые будут идти по линии
Возрождение нейтральных крипов
  • файл: scripts/vscripts/abilities/respawn
  • weak - возрождается на месте, где его убили через 5 секунд
  • strong - возрождается на месте, где появился
  • boss - возрождается на месте, где появился. После возрождения улучшается
Поведение юнитов
  • файл: scripts/vscripts/ai/...
  • поведение нейтрального босса 1/2/3 уровня
  • поведение лайнового босса
Надеюсь данный "урок" будет вам полезен, так как я вложил много времени и сил на его создание.
Может быть скоро мы увидим новых энтузиастов с горящими глазами, которые смогут создать годный Hero Defense (например MooMoo)
Ах да у меня там много мусора в файлах, было оченб сильно лень так что не обессудьте :cool:
Всем пока, всем удачи !

Ссылка на стим
Ссылка на гитхаб
Что с гитхабом? почини ссылку плиз))
 
Запускаю тест карты и постоянно одни и те же ошибки, в чём причина?
error.png
500-525 строки
Lua:
function GameSettings:OnPlayerLevelUp(keys)
    print ('[BAREBONES] OnPlayerLevelUp')
    DeepPrintTable(keys)

    local hero = PlayerResource:GetSelectedHeroEntity(keys.player_id)
    local level = keys.level
    local ability_point = hero:GetAbilityPoints()
    print(level)
    if hero and level then
        local no_points_levels = {
        [17] = 1,
        [19] = 1,
        [21] = 1,
        [22] = 1,
        [23] = 1,
        [24] = 1,
        }

        if no_points_levels[level] or level >= 30 then
            hero:SetAbilityPoints(ability_point + 1)
        end
    end
end
588-625 строка

Lua:
function GameSettings:OnNPCSpawned(keys)
--    print("[BAREBONES] NPC Spawned")
--    DeepPrintTable(keys)
    local npc = EntIndexToHScript(keys.entindex)
    local name = npc:GetUnitName()
   
    if npc:IsRealHero() and npc.bFirstSpawned == nil then
--        GameSettings:OnHeroInGame(npc)          
        npc.bFirstSpawned = true
        local playerID = npc:GetPlayerID()
        local steamID = PlayerResource:GetSteamAccountID(playerID)

        if FirstSpawned == nil then
            FirstSpawned = {}
        end
       
        if not FirstSpawned[playerID] then

            FirstSpawned[playerID] = true
            while npc:GetLevel() < HERO_START_LEVEL do
                npc:AddExperience(50, 0, true, true)
            end
        end
    end  
    if npc:IsTempestDouble() or npc:IsIllusion() then
        local owner = npc:GetPlayerOwner():GetAssignedHero()
        npc:SetTeam(owner:GetTeam())
        for _, modifier in pairs( owner:FindAllModifiers() ) do
            local stacks = modifier:GetStackCount()
            local modifier_name = modifier:GetName()

            if stacks > 0 then
                local modifier = npc:AddNewModifier(owner, nil, modifier_name, {})
                modifier:SetStackCount(stacks)
            end
        end
    end
end
 
Последнее редактирование модератором:
Запускаю тест карты и постоянно одни и те же ошибки, в чём причина?
500-525 строки
Lua:
function GameSettings:OnPlayerLevelUp(keys)
    print ('[BAREBONES] OnPlayerLevelUp')
    DeepPrintTable(keys)

    local hero = PlayerResource:GetSelectedHeroEntity(keys.player_id)
    local level = keys.level
    local ability_point = hero:GetAbilityPoints()
    print(level)
    if hero and level then
        local no_points_levels = {
        [17] = 1,
        [19] = 1,
        [21] = 1,
        [22] = 1,
        [23] = 1,
        [24] = 1,
        }

        if no_points_levels[level] or level >= 30 then
            hero:SetAbilityPoints(ability_point + 1)
        end
    end
end
588-625 строка

Lua:
function GameSettings:OnNPCSpawned(keys)
--    print("[BAREBONES] NPC Spawned")
--    DeepPrintTable(keys)
    local npc = EntIndexToHScript(keys.entindex)
    local name = npc:GetUnitName()
 
    if npc:IsRealHero() and npc.bFirstSpawned == nil then
--        GameSettings:OnHeroInGame(npc)        
        npc.bFirstSpawned = true
        local playerID = npc:GetPlayerID()
        local steamID = PlayerResource:GetSteamAccountID(playerID)

        if FirstSpawned == nil then
            FirstSpawned = {}
        end
     
        if not FirstSpawned[playerID] then

            FirstSpawned[playerID] = true
            while npc:GetLevel() < HERO_START_LEVEL do
                npc:AddExperience(50, 0, true, true)
            end
        end
    end
    if npc:IsTempestDouble() or npc:IsIllusion() then
        local owner = npc:GetPlayerOwner():GetAssignedHero()
        npc:SetTeam(owner:GetTeam())
        for _, modifier in pairs( owner:FindAllModifiers() ) do
            local stacks = modifier:GetStackCount()
            local modifier_name = modifier:GetName()

            if stacks > 0 then
                local modifier = npc:AddNewModifier(owner, nil, modifier_name, {})
                modifier:SetStackCount(stacks)
            end
        end
    end
end
Чтобы не засорять место, под большие блоки кода используй спойлер.

А еще лучше отмечай каким-то цветом или шрифтом, в каком месте начинается ошибка )
Да, эти ошибки есть. Насколько я знаю они не мешают работе, в любом случае на данный момент я не знаю как их исправить.
 
Спасибо, ещё такой вопрос, как принудительно запустить новую волну спустя N-время?
Вижу есть код GameMode.line_interval, а как его использовать ещё не понял
 
  • Вооу
Реакции: vulkantsk
Так и не понял половину функционала кода
Начал писать с 0

И тем не менее, спасибо за шаблон, в котором можно посмотреть принцип работы тех или иных функций
 
  • Нравится
Реакции: vulkantsk
как зделать раунды и что бы в конце раунда тпхело на спавны
 
Реклама: