Урок Создание кастомного пика героев

Дикий Пёс

Друзья CG
28 Июн 2017
411
96
Проект
Petri Reborn
Здесь я попытаюсь показать процесс создания пика и рассказать что-нибудь по теме. Пишу это сразу как только проснулся, так что могу где-то затупить, пишите в комментах, исправлю.

20181111142001_1.jpg

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

PANORAMA
Создаём нужные файлы, а именно:
В "content/dota_addons/addon_name/panorama/layout/custom_game" надо создать "pick.xml",
в "content/dota_addons/addon_name/panorama/styles" надо создать "pick.css"
и в "content/dota_addons/addon_name/panorama/scripts" надо создать "pick.js"

Подключаем нужные файлы:
В "content/dota_addons/addon_name/panorama/layout/custom_game/custom_ui_manifest.xml" надо подключить новый элемент худа.
(Да, делать пик мы будем уже после спавна героев, по другому никак(наверное), поэтому это элемент именно худа игры, а не выбора или загрузочного экрана).
XML:
    <CustomUIElement type="Hud" layoutfile="file://{resources}/layout/custom_game/pick.xml" />
В созданном нами "pick.xml" надо подключить файлы стилей и скриптов:
XML:
  <scripts>
    <include src="file://{resources}/scripts/pick.js" />
  </scripts>
  <styles>
    <include src="file://{resources}/styles/pick.css" />
  </styles>

А теперь, когда всё подключено, можно начинать писать штуки.
(Я надеюсь, что с css вы сами разберётесь и поставите что надо, куда надо. Если, что чекайте мой старый(относительно) Гайд по загрузочному экрану.)

Начнём с "pick.xml":
XML:
<root>
  //-это у вас уже должно быть
  <scripts>
    <include src="file://{resources}/scripts/pick.js" />
  </scripts>
  <styles>
    <include src="file://{resources}/styles/pick.css" />
  </styles>
  //-

  <Panel> //первая панель всегда без id, ибо дота ругается по какой-то причине.
    <Panel id="bg"> // это панель заднего фона, в css ставим 1920x1080 и чёрный фон, можно картинку по желанию
    <Panel id="radiant"> // герои света
        <Button id="dragon_knight" onactivate="PickHero('dragon_knight');"> // кнопка выбора героя, которая вызывает функцию js и отправляет имя героя. У вас таких будет несколько, под каждого героя, у меня одня, для примера
            <Image class="icon" src="file://{images}/pick/dragon_knight.png"/> //картинка на кнопке
        </Button> 
    </Panel>
    <Panel id="dire"> // герои тьмы
        <Button id="pudge" onactivate="PickHero('pudge');">
            <Image class="icon" src="file://{images}/pick/pudge.png"/>
        </Button> 
    </Panel> 
    </Panel>
    <Panel id="timer"> // таймер, если у вас ограниченное время на выбор героев
        <Label id="timerlabel" text="60"/>
    </Panel>
  </Panel>
</root>

Вроде всё просто, да? теперь переходим к "pick.js", js для меня это что-то дико непонятное я вообще иногда в шоке как что-то, что я пишу на js работает(впрочем не только на js).
Надо всё же упомянуть кое-что про css, в моём примере используется стиль, который я ещё пару лет назад взял из петров и использую до сих пор:
CSS:
.Hide
{
    transform: translate3d( 75px, 0px, 0px );
    blur: gaussian( 15.0 );
    opacity: 0.0;
}
JavaScript:
//-здесь ставим слушатели на кастомные ивенты из луа
GameEvents.Subscribe( "ban_hero", OnBanHero);
GameEvents.Subscribe( "send_time", OnTime);
GameEvents.Subscribe( "time_over", OnTimeOver);
GameEvents.Subscribe( "prepare", OnPrepare);
//-

function OnPrepare(arg){ // функция в которой мы будем прятать героев противопложной команды для игрока
    $.Msg("#" + arg.key1)
    $("#" + arg.key1).ToggleClass("Hide");
}

function PickHero(hero) { // функция, которую запускает наша кнопка в "pick.xml", отправляет имя выборанного героя в луа
    var data = {     
    pick: hero,
    }
    GameEvents.SendCustomGameEventToServer( "pick_hero_event", data );
    $("#bg").DeleteAsync(0.0);
}

function OnBanHero(arg){ //функция, которая прячет героя для всех игроков после выбора, если у вас одного героя могут выбрать несколько раз, то она вам не нужна
    $("#" + arg.key1).ToggleClass("Hide");
}

function OnTime(arg){ //функция отсчёта времени, ненужна если у вас неогранниченное время на выбор героя
    $("#timerlabel").text = arg.key1;
}

function OnTimeOver(){ // функция, которая удаляет пик с экрана
    $("#bg").DeleteAsync(0.0);
}

Вроде с панорамой всё, теперь луа:

LUA
Lua для меня не так сложен, как js, но код выглядит так, будто я хотел построить дом из дерева и пошёл в шахту.
У меня есть свой отдельный файл для некоторых функций, но вы можете и в одном файле всё написать.
Я использую barebones и файл events.lua соответственно.

Начнём с него
events.lua:
Lua:
function GameMode:OnNPCSpawned(keys)
  DebugPrint("[BAREBONES] NPC Spawned")
  DebugPrintTable(keys)

  local npc = EntIndexToHScript(keys.entindex)
  if npc:IsRealHero() then
    local teamn = GetTeamName(npc:GetTeamNumber())
    local playerID = npc:GetPlayerOwner():GetPlayerID()
    if teamn == '#DOTA_GoodGuys' then --проверка на команду, отправляем команду, которую прячем, можно несклько, для этого надо больше проверок на команды
        -- если что, в js это функция "OnPrepare"
        local event_data = { key1 = "dire" }
        CustomGameEventManager:Send_ServerToPlayer(PlayerResource:GetPlayer(playerID), "prepare", event_data )
    else
        local event_data = { key1 = "radiant" }
        CustomGameEventManager:Send_ServerToPlayer(PlayerResource:GetPlayer(playerID), "prepare", event_data )
    end
    end
  end
end

теперь gamemode.lua:

В фукции GameMode:InitGameMode() регаем слушателя:
Lua:
 CustomGameEventManager:RegisterListener( "pick_hero_event", OnPickHero )
и пишем в этой функции:
Lua:
function OnPickHero( eventSourceIndex, keys ) -- эту функцию запускает кнока выбора героя, в js функция PickHero(hero)
    local pid = nil -- id игрока
    for k,v in pairs(keys) do -- отправляется две строчки, одна с id игрока, другая уже кастомная с выбранным героем
        if k == "pick" then -- если вторая строчка, то...
            print(pid) -- принты для проверки, можно убрать
            print("npc_dota_hero_"..v) -- принты для проверки, можно убрать
            PlayerResource:ReplaceHeroWith(pid, "npc_dota_hero_"..v, 0, 0) -- выдаём героя
            local event_data = { key1 = v }
            CustomGameEventManager:Send_ServerToAllClients( "ban_hero", event_data ) -- отправляем ивент и прячем героя в js функции OnBanHero
        else
            pid = v -- если первая строчка то сохраняем id игрока
        end
    end
end

далее у меня свой файл, но вы можете писать в одном из предыдущих:
Lua:
timetopickleft = 60 -- время на выбор героев

function timetopick() -- функция отсчёта времени, ненужна вам если у вас неогранниченное... ну вы поняли, запустите где-нибудь в InitGameMode(), например
    Timers:CreateTimer(function() -- создаём таймер, но извращённый
        timetopickleft = timetopickleft - 1; -- считаем секундочки
        print(timetopickleft) -- спамим в консоль
        local event_data = { key1 = timetopickleft }
        CustomGameEventManager:Send_ServerToAllClients( "send_time", event_data ) -- отправляем ивент в js, что время менялось на экране, функция OnTime
        if timetopickleft == 0 then -- если время вышло, то
            CustomGameEventManager:Send_ServerToAllClients( "time_over", nil ) -- отправляем ивент, чтобы спрятать пик для всех игроков, функция js OnTimeOver
            return nil -- останавливаем таймер
        else -- если время ещё есть, то
            return 1.0 -- возвращаем секунду для таймера
        end
    end)
end

Вроде всё, как вы поняли я не писал это специально для гайда, я делаю кастомку и взял код, что написал оттуда, и сделал гайд из этого, немного поменяв код под это.
Так как я делаю кастомки в доте и играю в пупк у меня нет друзей и эту штуку и проверял только в соло, а кастомка моя ещё не готова, я был бы благодарен если бы вы проверили это с друзьями или ещё с кем-то и отписались, что работает или не работает.
Я всё ещё не очень проснулся и мог реально где-то ошибица поэтому пишите в комментах я исправлю всё.

уф, больше часа потратил на этот гайд, и это не считая сколько я этот код писал, хотя выглядит он не таким сложным(и гайд и код), если вам понравилось поставьте люкантропа пожалуйста

034uLsYfPmA.jpg

UPD1: Добавил пример
UPD2: исправил ошибку, которую ниже описали, спасибо кста
этот гайд очень устарел лично для меня, всё можно гораздо лучше сделать, но мне лень
 
Последнее редактирование:
  • Нравится
Реакции: ZerH, Chernobl и -ExotiC-

GOD

Пользователь
7 Янв 2019
51
2
Проект
Reborn in progress
Один из предыдущих, это какой?
 

Hulimontana

Новичок
19 Апр 2019
6
1
Кто-то еще пробовал? У меня не работает. Просто идет выбор команды, затем дефолтный пик героев, единственная разница - после полной прогрузки героя и карты, слева-сверху появилась цифра "60".
f8d5a-clip-26kb.jpg
в "content/dota_addons/addon_name/panorama/scripts/custom_game" надо создать "pick.js"
<include src="file://{resources}/scripts/pick.js" />
pick.js создаешь в scripts/custom_game/
а путь к нему прописываешь в file://{resources}/scripts/
сори если что, может такое допускается, не проверял, сразу исправил и указал верный путь у себя.
 
Последнее редактирование:
  • Нравится
Реакции: Дикий Пёс
Реклама: