• Страница 1 из 1
  • 1
Архив - только для чтения
Модератор форума: andycrowz  
Форум » Корзина форума » Корзина » Уроки по созданию .LUA скриптов для WoW №1 Часть 1 (Обучающий учебник .LUA языка.)
Уроки по созданию .LUA скриптов для WoW №1 Часть 1
citynov
Капрал
Долго думал с чего начать и наткнулся на этот учебник и решил его опубликовать тут как начальный урок по созданию lua скриптов.

Обучающий учебник .LUA языка.

Это руководство можно найти на wow-v и на ac-web. На английском языке.
(Автор идеи учебника не я. Я наткнулся на него на одном англ форуме и решил сделать подобный учебник здесь).
Обратите внимание, что этот учебник, устарел и не будет работать с LuaBridge, хотя синтаксис задан правильно, но функции не будут работать.

От автора:

Привет всем и добро пожаловать на мой конечный учебник Lua. Этот учебник направлен на тех людей, которые хотели бы узнать Lua, но не могут найти способ сделать это легко и надежно. Этот учебник будет также охватывать некоторые расширенные возможности языка программирования.

Введение в Lua.

Вероятно вы пришли узнать о Lua, и таким образом у вас уже есть ясное понимание того, на что способен язык Lua. Если вы сможете скриптовать в Lua, вы, вероятно, будет в состоянии применить высокопоставленные должности в частных серверах— это очень мощный, легкий язык. Lua можно сделать почти как C++ и его гораздо, гораздо легче понять и писать.

Плюсы и минусы обучения Lua.

Плюсы:
Легкий.
Мощный.
Простой.
Поддержка всех ArcEmu серверов по умолчанию.
Его власть, безгранична.

Минусы

Ограничивает вас только в ArcEmu серверов (если Mangos и Trinitycore решили бы поддержать Lua, не дай Бог этому.)

Основы.

Эта тема является самым низким уровнем lua,что вы можете получить внутри этой темы. Этот раздел состоит из:

Code

       Low-level functions
       Arguments
       Gossip NPCs
       If statements
       Else & Elseif
       Commenting
       Variables


В этом учебнике предполагается что вы ищете ArcEmu обучения, и поэтому мы будет фокусироваться на пути ArcEmu сценариев. В этом учебнике я научу вас, как сделать сплетни NPC. Прежде чем начать, я считаю, что вы должны иметь эти вещи, прежде чем начать изучать Lua:

Сборка ArcEmu с поддержкой LuaHypArc.
ArcEmu Default Lua Command List.
Notepad++ Installer

Если вы не имеете что то из этого списка, не волнуйтесь! Это не обязательно что бы научиться Lua, но я уверен, что будет проще делать скрипты с этим. Вы можете найти Notepad++. и список команд ArcEmu, нажав ссылки ниже.
Notepad++.
ArcEmu команды.
Все это бесплатно.

Для более удобного управления скриптами можно использовать коментарии.

Комментарии являются строки кода, которые не читают Lua язык. Он просто игнорирует их. Существует два типа комментариев; Однострочный комментарий и блок комментарий.

Однострочный комментарий

Однострочный комментарий; он блокирует остальные линии (где вы использовали идентификатор комментария). После так называемого комментария идентификатора является полностью игнорируемым обработчиком Lua и впоследствии ArcEmu-миром. Однострочный комментарий обозначается двумя дефисами, размещены в непосредственной близости друг от друга. Пример можно увидеть ниже.

Code
Stuff() --Ваш коментарий!


Это хороший метод для обеспечения того, что ваши мысли помещены на виртуальной бумаге. Но что делать, если этот комментарий занимает более одной строки? Конечно было бы запутанно, если сделать это так;

Code
-- This
-- Needs
-- To    
-- Be
-- Neater
-- ...!


Блок комментарий.

Блок комментарий: является комментарий, который занимает несколько строк, пока вы не добавите закрывающий символ. Что написано между открывающим и закрывающим идентификатором полностью игнорируются. Это отлично подходит для вступительных пунктов в верхней части сценария или вообще указанием каких либо мыслей.
Блок комментарий выглядит так:

Code
--[[
Это ,блок комментарий    
, он может занимать несколько строк!    
]]


Открытие блок комментария

Code
--[[


Закрытие блок комментария

Code
]]


Пожалуйста, обратите внимание, что это не имеет никакого эффекта
на скрипт.

Добавлено (01.10.2012, 01:59)
---------------------------------------------
Функции.(function)

Функции являются обязательным (в ArcEmu), чтобы сделать этот сценарий рабочим. Функция начинается с function и закрывается end значением. После слова «function» есть пространство и там вы можете поместить ваше название функции, далее идут -(). Скобки, известны как аргументы. Они в основном решают, какой бит данных передается на мир WOW от ArcEmu-мира. Вот пример функции;

Code
function FunctionName(Unit, Event)
    -- Это функция!
end


Обратите внимание, что ключевое слово «function» пишется с маленькой буквы. Использование большой буквы «F» (или любой другой буквы) приведет к тому что система, не признает ее за функцию. То же самое для имен функций, аргументов и т.д.

Ну как упоминалось ранее, они являются аргументами. Почти в каждой функции, которые вы используете как Lua Scripter вы будете использовать значения внутри этих скобок. Существуют различные аргументы для различных событий. Вот наиболее распространенные:

Code
GeneralCreatureEvent(Unit, Event[, pMisc])
GossipOnTalkEvent(Unit, Event, player)
GossipOnSelectEvent(Unit, Event, player, id, intid, code, pMisc)


Квадратные скобки указывают факультативный выбор. (Unit, Event)может быть использован вместо (Unit, Event, pMisc) например.

Создание вашей первой функции.

В соответствии с традицией, ваша первая функция/сценарий будет «Привет Мир!».

Code

function HelloWorld()
    print("Привет Мир!")
    print("Это мой первый сценарий LUA!")
end

HelloWorld()


Вас интересует что я сделал? Ну, во-первых, я создал функцию HelloWorld без аргументов. Обратите внимание, что я еще добавил пару круглых скобках. Это является обязательным для каждой функции которую вы создаете. В первой строке, функция HelloWorld () ничего не делает, кроме как рассказать Lua, что мы начинаем новую функцию, которую мы обозначили под названием "HelloWorld", и что он не имеет аргументов.

Далее идет print скобки кавычки. Внутри этих кавычек вы пишете тот текст который вы хотите видеть.

Если вы сохраните этот код в HelloWorld.lua и поместите его в вашу папку / scripts , а затем открыть ArcEmu-World, она будет печатать в консоль:

Code
Привет Мир!")
Это мой первый сценарий LUA!


Скучно да? Это не полезно, так что мы будем двигаться немного дальше...

Ваш первый gossip скрипт.

В WoW, когда вы говорите с NPC, он дает вам разные варианты. Такие, как: "Да, я готов», и так далее. Это называется gossip. И это то, что мы собираемся создать сейчас. Прежде всего, необходимо создать NPC ...

После создания NPC установите ему флаг 1. Это можно сделать через базу данных либо через команду в игре ( .npc flag 1 ).

Теперь создайте новый файл lua и напишите в нем

Code
--[[
    Мой первый сценарий!

-- Variables
local NPC_ID = YourEntryID

-- On Triggers

-- RegisterUnitEvents
]]


Замените YourEntryID на ид вашего NPC.

Variables.

Переменная данных, которое хранит определенную часть данных. В отличие от C + +, нет типами переменных в Lua. Кроме того, нет необходимости беспокоиться о неподписанных / Подпись переменных.

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

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

Code
a b c d e f g h i j k l m n o p q r s t u v w x y z    
    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
    1 2 3 4 5 6 7 8 9 0
    _


Обратите внимание, что переменные чувствительны к регистру. MY_VAR не то же самое, как my_var или My_VaR.

Глобальная переменная может переходить от одного сценария к другому, так что это может вызвать проблемы. Лучше всего использовать локальные переменные, которые остаются внутри текущего сценария, для уменьшения помех.

Хорошие имена переменных:

Code
My_Var = 1
    myVar = 2


Неверное имя переменных:

Code
%MyVar = 1
    My.Var = 2
    My&Var = 3


Это всегда хорошая идея, чтобы назвать ваши переменные функции.

Code
local Var983


Это не так просто, чтобы знать, что он делает, как это:

Code
local Npc_Id


Во всяком случае, вернемся к Gossip NPC!

Code
--[[
    Мой первый сценарий!

-- Variables
local NPC_ID = YourEntryID

-- On Triggers

-- RegisterUnitEvents
]]


Прежде всего, давайте создадим новую функцию под названием "exampleGossipOnTalk (Unit, Event, player). Эти аргументы являются стандартными для OnTalk события сплетни. Мероприятие OnTalk сплетни первого меню в диалоге который вы видите, когда вы говорите с NPC.

Code
--[[
    Мой первый сценарий!
]]
-- Variables
local NPC_ID = YourEntryID

-- On Triggers
function exampleGossipOnTalk(Unit, Event, player)
end

-- RegisterUnitEvents


Обратите внимание, что я добавил end ниже функции. Мы должны закончить каждую функцию, которая будет создана, или Lua будет бросать ошибку. Теперь, когда мы создали функцию, давайте добавим RegisterUnitEvent. Обычно, если вы создаете босса, вы будете использовать RegisterUnitEvent(), но так как мы делаем Gossip NPC, мы используем RegisterUnitGossipEvent. Отметим, что это учитывается. Добавляйте это под соответствующий раздел;

Code

RegisterUnitGossipEvent(NPC_ID, 1, exampleGossipOnTalk)


RegisterUnitGossipEvent () имеет четыре аргумента: ID, Event ID и имя функции. Вы можете использовать переменные для ID Event ID и событий, поэтому я добавил NPC_ID переменной вместо ID.

Code

RegisterUnitGossipEvent(ID, EVENT_ID, FUNCTION_NAME)


Сейчас этот сценарий будет регистрироваться как пустая функция, когда вы говорите с NPC. Это что-то, но это бесполезно. Итак, давайте начнем его конкретизацию.

Code

function exampleGossipOnTalk(Unit, Event, player)
end


Как мы видели выше, заявления идут между функцией и концом (function и end), например, так.

Code

function exampleGossipOnTalk(Unit, Event, player)
    -- Заявления суда
end


Добавлено (01.10.2012, 02:01)
---------------------------------------------
Чтобы начать создание gossip меню, Lua должен знать, что мы создаем меню, мы создаем оболочку, используя: GossipCreateMenu() заявление.

Code

:GossipCreateMenu(TEXT_ID, player, INTID)


Обратите внимание, что :GossipCreateMenu() имеет двоеточие перед ним; это означает, что требуется Unit. По умолчанию, Группа 'Unit'. Это не нужно менять, если мы имеем дело с несколькими НПС в один сценарий; Мы вернемся к этому позже. Давайте добавим наше заявление в функцию.

Code

function exampleGossipOnTalk(Unit, Event, player)
               Unit:GossipCreateMenu(100, player, 0)
end


Вы видите, что я использовал '100 'для текста ID. Это значение по умолчанию: "Привет, <name>. Чем я могу вам помочь?». Он может быть изменен, глядя в соответствующей таблице в вашей базе данных. Я установил Intid '0 ', потому что это то, что все первые меню устанавливаются на; 0. Это делает логический смысл.

Так что теперь мы создали меню, которое отображает "Привет, <name>. Чем я могу вам помочь? '. Это все еще довольно непрактичный сценарий и не поможет никому. Чтобы сделать этого NPC полезным, давайте добавим несколько вариантов, :GossipMenuAddItem ()

Code

:GossipMenuAddItem(ICON_ID, MENU_CONTENT, INTID[, CODE])


Это утверждение может сбить вас с толку; INTID? CODE? ICON_ID? Что это такое? Ну, ICON_IDs как вы видите рядом с текстом, когда вы открываете меню. Есть несколько различных значков, идентификаторов перечисленных ниже;

Code

           0 = Chat bubble
    1 = Bag
    2 = Wings
    3 = Book
    4 = Cog/Gear
    5 = Cog/Gear
    6 = Bag with coin
    7 = Chat bubble with "..."


Когда вы пишете в MENU_CONTENT, вы можете покрасить его. Вы можете использовать его, положив |ср######, где #S ваш шестнадцатеричный цвет (Web Color Chart - Hexadecimal - by VisiBone Дополнительная информация) и добавление |г в конце.

Code

:GossipMenuAddItem(0, "|cfFFFFFF White! |r", 1, 0)


INTID? Ну, когда вы используете функцию OnSelect, INTID используется, чтобы определить, какое меню было открыто. Таким образом, вы должны использовать уникальные IntIDs для каждого выбора меню или сценария иначе они не будут работать. Кроме того, IntID используется в числовой форме.

CODE является дополнительным ... вариантом .. , которые могут быть пропущены. Они используется только, когда вы хотите, загрузить CodeBox. Это не будет объяснено в этом учебнике, и вам придется поэкспериментировать, чтобы получить его рабочим. Само собой разумеется, оставить его на 0.
Я добавлю несколько вариантов Gossip Menu..

Code

function exampleGossipOnTalk(Unit, Event, player)
               Unit:GossipCreateMenu(100, player, 0)
               Unit:GossipMenuAddItem(0, "Телепорт меня в столицу.", 1, 0)
               Unit:GossipMenuAddItem(0, "Удалить болезнь после воскрешения.", 2, 0)
               Unit:GossipMenuAddItem(0, "Выход.", 3, 0)
end


Это добавляет три варианта нашего меню, Телепорт меня в столицу, Удалить болезнь после воскрешения, и вариант, чтобы закрыть меню. Тем не менее, это не будет работать. Мы создали меню, но, мы не сможем послать его к игроку. Для этого мы добавим: GossipSendMenu() команду.

Code

Unit:GossipSendMenu(player)


Что делает нашу функцию такой:

Code

function exampleGossipOnTalk(Unit, Event, player)
               Unit:GossipCreateMenu(100, player, 0)
               Unit:GossipMenuAddItem(0, "Телепорт меня в столицу.", 1, 0)
               Unit:GossipMenuAddItem(0, "Удалить болезнь после воскрешения.", 2, 0)
               Unit:GossipMenuAddItem(0, "Выход.", 3, 0)
               Unit:GossipSendMenu(player)
end


Давайте добавим это к остальной части нашего кода:

Code

--[[
    Мой первый сценарий!
]]
-- Variables
local NPC_ID = ид нпс

-- On Triggers
function exampleGossipOnTalk(Unit, Event, player)
               Unit:GossipCreateMenu(100, player, 0)
               Unit:GossipMenuAddItem(0, "Телепорт меня в столицу.", 1, 0)
               Unit:GossipMenuAddItem(0, "Удалить болезнь после воскрешения.", 2, 0)
               Unit:GossipMenuAddItem(0, "Выход.", 3, 0)
               Unit:GossipSendMenu(player)
end

-- RegisterUnitEvents
RegisterUnitGossipEvent(NPC_ID, 1, "exampleGossipOnTalk")


У нас получилось! Однако, это только показывает меню. Варианты не будут работать, вы будете иметь возможность видеть их, но если вы нажмете на них, ничего не произойдет. Вы должны прописать ему, что делать. И мы делаем это с нашей второй функцией; OnSelect

Code

exampleGossipOnSelect(Unit, Event, player, id, intid, code, pMisc)


Это ничего не значит для вас на данный момент, не так ли? Давайте создадим еще одну функцию.
Code

--[[
    Мой первый сценарий!
]]
-- Variables
local NPC_ID = ид нпс

-- On Triggers
function exampleGossipOnTalk(Unit, Event, player)
                Unit:GossipCreateMenu(100, player, 0)
                Unit:GossipMenuAddItem(0, "Телепорт меня в столицу.", 1, 0)
                Unit:GossipMenuAddItem(0, "Удалить болезнь после воскрешения.", 2, 0)
                Unit:GossipMenuAddItem(0, "Выход.", 3, 0)
                Unit:GossipSendMenu(player)
End
function exampleGossipOnSelect(Unit, Event, player, id, intid, code, pMisc)
end

-- RegisterUnitEvents
RegisterUnitGossipEvent(NPC_ID, 1, "exampleGossipOnTalk")


И добавить RegisterUnitGossipEvent под OnTalk

Code

--[[
    Мой первый сценарий!
]]
-- Variables
local NPC_ID = ид нпс

-- On Triggers
function exampleGossipOnTalk(Unit, Event, player)
               Unit:GossipCreateMenu(100, player, 0)
               Unit:GossipMenuAddItem(0, "Телепорт меня в столицу.", 1, 0)
               Unit:GossipMenuAddItem(0, "Удалить болезнь после воскрешения.", 2, 0)
               Unit:GossipMenuAddItem(0, "Выход.", 3, 0)
               Unit:GossipSendMenu(player)
End
function exampleGossipOnSelect(Unit, Event, player, id, intid, code, pMisc)
end

-- RegisterUnitEvents
RegisterUnitGossipEvent(NPC_ID, 1, "exampleGossipOnTalk")
RegisterUnitGossipEvent(NPC_ID, 2, exampleGossipOnSelect)


Итак, давайте добавим первый вариант (из 3) наших новых функций.
Code

function exampleGossipOnSelect(Unit, Event, player, id, intid, code, pMisc)
    if (intid == 1) then
     player:Teleport(MapID, x, y, z)
     player:GossipComplete()
    end
end


Что мы видим.

Code

if (intid == 1) then


Он используется, чтобы проверить, если определенные условия. Помните, когда мы добавили варианты, и мы должны были определить уникальный номер? Это было intid. Таким образом, если intid, я выбрал 1 ("Телепорт меня в столицу!”), То мы запустим эту часть сценария.

Code

player:Teleport(MapID, x, y, z)


Эти значения говорят о том что они телепортируют игрока по введенным координатам.
Для примера я возьму Shattrat. Его координаты.

Code

MapID: 1    
X: -9101.980469    
Y: 1612.902832    
Z: 21


Так что это будет выглядеть так:

Code

player:Teleport(1, -9101.980469, 1612.902832, 21)  -- Это не точные координаты шаттрат.


Теперь мы знаем, что это делает, и мы будем двигаться дальше;
Player:GossipComplete(). Это довольно просто и не требует весь абзац, чтобы объяснить это. Он просто закрывает меню.

Code

end


Подождите, у нас есть два end? Да, мы сделали все правильно. «if» заявление необходимо положить конец, так как Lua не может определить, когда он закончился.
Давайте посмотрим что у нас получилось.

Code

function exampleGossipOnSelect(Unit, Event, player, id, intid, code, pMisc)
    if (intid == 1) then
     player:Teleport(1, -9101.980469, 1612.902832, 21)
     player:GossipComplete()
    end
end


Таким образом, мы понимаем, что это все делает? Хорошо. Давайте перейдем на следующий intid; Удалить болезнь после воскрешения.

Code

function exampleGossipOnSelect(Unit, Event, player, id, intid, code, pMisc)
    if (intid == 1) then
     player:Teleport(1, -9101.980469, 1612.902832, 21)
     player:GossipComplete()
    end
    if (intid == 2) then
    end
end


Как вы можете видеть, мы создали другую, петлю. На этот раз, она работает, только если intid равен 2. Таким образом, в этой функции мы хотим удалить ауру болезнь после воскрешения. Теперь идем на wowhead.com и находим там ид ауры 15007.
Смотрим что у нас получается.

Code

function exampleGossipOnSelect(Unit, Event, player, id, intid, code, pMisc)
    if (intid == 1) then
     player:Teleport(1, -9101.980469, 1612.902832, 21)
     player:GossipComplete()
    end
    if (intid == 2) then
     if (player:HasAura(15007) == true) then
      player:SendBroadcastMessage("Болезнь после воскрешения была удалена. Будьте осторожны в следующий раз!")
      player:RemoveAura(15007)
      player:GossipComplete()
     else
      player:SendBroadcastMessage("У вас нет болезни после воскрешения!")
      player:GossipComplete()
     end
    end
end


Что мы тут видим. Я добавил

Code
    
HasAura()


и

Code

RemoveAura().


HasAura() проверяет есть ли на вас эта аура.
RemoveAura() снимает эту ауру.

Так же я добавил player:SendBroadcastMessage объяснять что это делает я не буду. Так как это понятно наглядно.

Конечно, вы все равно должны добавить end для "if" и "function".

Итак, теперь у нас есть два intids, давайте посмотрим на нашу функцию сейчас, и добавить последнюю.

Code
function exampleGossipOnSelect(Unit, Event, player, id, intid, code, pMisc)
    if (intid == 1) then
     player:Teleport(1, -9101.980469, 1612.902832, 21)
     player:GossipComplete()
    end
    if (intid == 2) then
     if (player:HasAura(15007) == true) then
      player:SendBroadcastMessage("Болезнь после воскрешения была удалена. Будьте осторожны в следующий раз!")
      player:RemoveAura(15007)
      player:GossipComplete()
     else
      player:SendBroadcastMessage("У вас нет болезни после воскрешения!")
      player:GossipComplete()
     end
    end
    if (intid == 3) then
     player:GossipComplete()
    end
end
    


Мы просто добавили в intid 3: GossipComplete(), потому что опция стоит, чтобы закрыть окно.

Теперь посмотрим что у нас получилось.

Code
--[[
    Мой первый сценарий!
]]
-- Variables
local NPC_ID = ид нпс

-- On Triggers
function exampleGossipOnTalk(Unit, Event, player)
                Unit:GossipCreateMenu(100, player, 0)
                Unit:GossipMenuAddItem(0, "Телепорт меня в столицу.", 1, 0)
                Unit:GossipMenuAddItem(0, "Удалить болезнь после воскрешения.", 2, 0)
                Unit:GossipMenuAddItem(0, "Выход.", 3, 0)
                Unit:GossipSendMenu(player)
end
function exampleGossipOnSelect(Unit, Event, player, id, intid, code, pMisc)
    if (intid == 1) then
     player:Teleport(1, -9101.980469, 1612.902832, 21)
     player:GossipComplete()
    end
    if (intid == 2) then
     if (player:HasAura(15007) == true) then
      player:SendBroadcastMessage("Болезнь после воскрешения была удалена. Будьте осторожны в следующий раз!")
      player:RemoveAura(15007)
      player:GossipComplete()
     else
      player:SendBroadcastMessage("У вас нет болезни после воскрешения!")
      player:GossipComplete()
     end
    end
    if (intid == 3) then
     player:GossipComplete()
    end
end
RegisterUnitGossipEvent(NPC_ID, 1, "exampleGossipOnTalk")
RegisterUnitGossipEvent(NPC_ID, 2, exampleGossipOnSelect)


Молодцы! Вы создали свой первый сценарий функционирования. Или, по крайней мере, с помощью этого учебника. Сохраните его как TeleporterNPC.lua и поместить его в папку scripts. Поставьте нпс и перезагрузите сервер, и наслаждайтесь вашим первым сценарием!

If и elseif.

Таким образом, вы знаете, как использовать If и elseif но, используя следующее довольно сложно читать ну и как то однообразно;

Code
if (var == true) then
    Weee()
end
if (var == false) then
    Awww()
end
if (var == nil) then
    WTFITSHOULDNTBENIL()
end


Вместо этого, вы можете заменить 2й и 3й if на elseIf, в зависимости от ситуации. elseIf в основном удаляет последующие if и заканчивается так, что они легко читаются. Вот наглядным пример.

Code
if (var == true) then
    Wee()
elseif (var == false) then
    Awww()
elseif (var == nil) then
    WTFITSHOULDNTBENIL()
end


Как вы можете видеть, это гораздо легче читать, чем предыдущие.

Ну что же. Вот и конец этого учебника. Вторая часть будет более позже.

Так же если вы будете использовать lua скрипты с русскими символами не забываем ставить кодировку UTF-8

Всем удачного изучения этого языка. smile

Если у вас есть какие то либо вопросы то не стесняйтесь задавайте их.

There is nothing better than .LUA
Сообщение # 1 отредактировано citynov - Понедельник, 01.10.2012, 09:33
TyI
Скаут
Bump +

Прошу закрепить тему smile
Сообщение # 2 написано 01.10.2012 в 02:23
citynov
Капрал
Ty. Это было бы очень даже не плохо smile

There is nothing better than .LUA
Сообщение # 3 написано 01.10.2012 в 02:37
INFERNOS
Закрепил
Присяжный - краб.
Сообщение # 4 написано 01.10.2012 в 18:09
keonji
I ♥ S-PB
если и браться переводить, то не с помощью гугл транслейта
Сообщение # 5 написано 05.03.2013 в 20:17
Форум » Корзина форума » Корзина » Уроки по созданию .LUA скриптов для WoW №1 Часть 1 (Обучающий учебник .LUA языка.)
  • Страница 1 из 1
  • 1
Поиск: