XGM Forum
Сайт - Статьи - Проекты - Ресурсы - Блоги

Форуме в режиме ТОЛЬКО ЧТЕНИЕ. Вы можете задать вопросы в Q/A на сайте, либо создать свой проект или ресурс.
Вернуться   XGM Forum > Warcraft> Академия: форум для вопросов> Желтая пресса: обучающие статьи
Ник
Пароль
Войти через VK в один клик
Сайт использует только имя.

 
J
expert
offline
Опыт: 48,447
Активность:
fsgui - создаем инвентарь
Короче эту статью я сделал еще давно, еще до лета, я думал - как же написать статью об инвентаре, но нипридумал ниче лучше как написать его, инвентарь, прямо в статье с коментарием всех действий. Но чем больше я писал тем больше это было похоже на какойто бред понятный только мне одному, потому я както скомкал рабту и бросил в угол, ну теперь я ее достал, мне стало ее жалко...
Несмотря на такой большой рамер статья еще далеко далеко не полностью написана, ну я ее выкладываю, может да и пригодится кому, если будет поддержка то может быть даже допишу...


Как писалась статья?



Перед тем как написать статью, я почти полностью сделал инвентарь (я его прикрепил к посту под названием MyInventory v0.9 (end)), а потом в статье я пыталя вспомнить как я его делал постояно копирую коды из моего уже сделаного инва, получислось не очень удобно но всеже хоть как то...
так что раз все промежуточные коды между null и готовым инвентарем я писал по памяти, то имеется вероятность ошибок, если вы решили делать инвентарь по инструкции то пишите обо всех нестыковках или о всех местах где у вас чтолибо невыходит в статье...

|



fsgui переводится как "полно-экранный графический пользовательский интерфейс".
Конечно, вам уже приходилось видеть нечто подобное от Zibad'ы, Netrat'а или т.п. людей
В данной статье мы создадим свой fsgui, если сказать точнее - Инвентарь.
Статья полезна и не только по практическому руководству по fsgui, но даже просто как обучающий материал о возможностях джаза.

Сначала будет много рутинной работу с обьектами в РО, разьясненю концепции и импарта, и как будем готовы приступем непосредствено к кодингу...
Когда я выкладываю функции что выклдывал раньше но с некоторыми дополнениями то помечаю их плюсиком

Первая часть - Начало



Теперь по обсуждаем - как наш инвентарь будет выглядеть...

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

Так как же сделать инвентарь универсальным? чтобы можно было изменить положение какой-либо вещи только одним взмахом руки?

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

Вот как выглядит наш конечный инвентарь в виде массива строк:


» Массив строк
Код:
// "X" - слот рюкзака
    // "O" - рамка слота
    // "H" - Положение иконки активного героя (Должно быть только 1)
    
    // "I" - рамка (левая  )
    // "J" - рамка (правая )
    // "-" - рамка (верхния)
    // "_" - рамка (нижния )
    
    // "P" - Угол рамки (Верхне-Левый )
    // "M" - Угол рамки (Верхне-Правый)
    // "N" - Угол рамки (Нижне -Правый)
    // "L" - Угол рамки (Нижне -Левый )
    
    // "U" - Кнобка (Использовать предмет   ) (Должно быть только 1)
    // "Y" - Кнобка (Выбросить предмет      ) (Должно быть только 1)
    // "G" - Кнобка (Отсортировать предметы ) (Должно быть только 1)
    // ">" - Кнобка (Ложить предметы в сумку) (Должно быть только 1)
    // "E" - Кнобка (Выход из инвентаря     ) (Должно быть только 1)
    
    // "/" - Обозначает левый верхний и правых нижний угол описания.                                                (Долнжо быть только 2)
    // "*" - Определяет верхние левое положение окошка нормального инвентаря героя, он будет занимать 2*3 клеток.   (Долнжо быть только 1)
    // "{" - Определяет начало строки, где будет писатся описание кнопок-менюшек.                                   (Долнжо быть только 1)
    // "C" - Определяет начало строки, где будет писатся имя активного героя                                        (Долнжо быть только 1)
    // "R" - Место где будет писацо что предмет нельзя использовать                                                 (Долнжо быть только 1)
    //                        0       1       2       3       4       5       6       7       8       9       10      11      12      13      14      15      16      17    18
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[0] = ".......|.......|.......|.......|.......|.......|.......|.......|POX....|-OX....|-OX....|-OX....|MOX....|.......|P/.....|-......|-......|-......|M......" //9
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[1] = ".......|.......|.......|.......|LPMNOH.|.......|.......|.......|IOX....|.OX....|  .OX....|.OX....|JOX....|.......|I......|.......|.......|.......|J......" //8
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[2] = ".......|.......|.......|.......|.......|.......|.......|.......|IOX....|.OX....|  .OX....|.OX....|JOX....|.......|I......|.......|.......|.......|J......" //7
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[3] = ".......|.......|.......|CR.....|.......|.......|.......|.......|IOX....|.OX....|  .OX....|.OX....|JOX....|.......|I......|.......|.......|.......|J......" //6
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[4] = ".......|.......|.......|LPU....|-_Y....|-_G....|MN>....|.......|IOX....|.OX....|.OX....|.OX....|JOX....|.......|I......|.......|...  ....|.......|J......" //5
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[5] = ".......|.......|.......|.......|.......|.......|.......|.......|IOX....|.OX....|  .OX....|.OX....|JOX....|.......|I......|.......|.......|.......|J......" //4
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[6] = ".......|.......|.......|.......|.......|PO*....|MO.....|.......|IOX....|.OX....|  .OX....|.OX....|JOX....|.......|I......|.......|.......|.......|J......" //3
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |YOU    |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[7] = ".......|.......|.......|LPMNE..|.......|IO.....|JO.....|.......|LOX....|_OX....|  _OX....|_OX....|NOX....|.......|I......|.......|.......|.......|J......" //2
    //                    |       |       |       |       |       |       |       |       |^_^    |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[8] = ".......|.......|.......|.......|.......|LO.....|NO.....|.......|{......|.......|  .......|.......|.......|.......|L......|_......|_......|_......|N/....." //1
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[9] = ".......|.......|.......|.......|.......|.......|.......|.......|.......|.......|  .......|.......|.......|.......|.......|.......|.......|.......|......." //0
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                        0       1       2       3       4       5       6       7       8       9       10      11      12      13      14      15      16      17    18


Комментарии между элементами массива служат чисто для наглядного удобства пропорций ячеек.

Что вообще в реале представляет будующий наш и все другие инвентори?

Джаз не дает возможность рисовать какие-то картинки на экране и услеживать положение и щелканье мыши по любому из этих кортинках как вам взумается.
Потому fsgui это просто рисунок на земле. Т.е. вы создаете камеру перпендикулярную земле и смотрящую на нее. И на облости видемости камеры создаете декорации, и на некоторых декорациях еще и трекаблы.

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


Т.е. красивая рассовая рамочка, все слоты, и все остальное, каждое в отдельности это отдельная декорация.

Всего таких тиов декораций будет очень много, нам прийдется изрядно помучится что создать в редакторе обьектов типов декораций по количеству всех возможных текстур предметов в игре, и + еще не относящиеся к игре модели границы и ячеек и т.п.
Мне лень считать и назвать вам точную цыфру, но типов декорация будет 250 и более.

Именно поэтому переносить или начинать делать инвентарь проще на совершено пусто карте, а только после заниматся всем остальным...

Ощутимо большая часть этих декорация будет выполнять роль иконок предметов в нашем инвентаре.

Т.е. первое что нам нужно сделать в инвенторе, это создать декорации для иконок предметов. (Однако я их уже подготовил, она находятся в файле StandObject.w3o прикрепленому к этому посту)

|



Далее когда у нас предметы будут готовы, нам надо както связать эти предметы с декорациями которые их представляют.

Также у нас возникает проблема - несуществует способа узнать описание предмета, потому нам прийдется атачить к предмету также информацию о его описании.

Также в нашем инвентаре будет отображатся количество зарядов у предмета, но нельзя сразу 100%-тно определить что предмет имеет заряды или он постоянный, потому нам надо также атачить к предмету Булевую переменую true(имеет)/flase(неимеет) заряд.

Чтобы сделать все это нам понадобится RB и SCV

Примечание: предпологается что читателю известо что такое RB и SCV, если же вам это не известно то сначало советую пойти почитать статьи по этому.


Давайте еще раз обратим внимание на то как нам приатачить тип декорации к предмету...

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

НО, этот способ неуниверсален, т.е. прийдется создавать кучи декораций с одной и тойже текстурой, потому что предметы иногда имеют одинаковые текстуры, а если нестандартных предметов очень много? то это может вызвать путаницу.
Потому мы будем атачить к предмету "путь к его текстуре".

А к текстуре будем атачить "Тип декорации".

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

Вторая часть - Подготовка



Наш инвентарь будет использовать Jass New Gen Pack

о нем можно узнать по этой ссылке.

Из его способностей нам понадобится только упрощеное обьявление глобалок, потому если вы не хотите использовать JNGP, то создавайте глобалки в редакторе переменных и в коде добавляйте им прификс "udg_".

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

Я люблю делать системы по отдельным триггерам, т.е. система в одном тригере другая система в другом, мне такой подход кажется удобнее, но вы смотрите сами...

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


|



Эту часть статьи мне лень писать, сдесь должно описыватся как создавать объекты в РО, а также описисывается весь импорт, принцепи читатель и сам после вставки в карту может посмотреть что они из себя представляют и как все реализованно

|



Создадим новую карту, импортируем туда нашы обьекты StandObject.w3o

Потом импортируем в карту все обьеты из папки StandImports
У всех импортируемых файлов уберите из строчки пути "war3mapImported\"
Кроме BTNInventory.tga и DISBTNInventory.tga, для них нужно заменить "war3mapImported\" на "ReplaceableTextures\CommandButtons\"
Потому что это единственные текстуры использующиеся в панеле упровления игрока...

добавте одному или нескольким героям абилку инвенторя, и поставте их на карту

|



Создадим триггер под названием "MyInventory", в нем будет находитя вся система инвентаря, теперь создадим триггер "MapInitialization", в нем просто в Gui-шной форме создайте событие загрузки карты, и добавте действие на открытие поля видемости всей карты.

Теперь зайдем в триггер "MyInventory", Он у нас покашто пустой, сделаем его еще более пустее - конвектируем его в текст, и удаляем весь его код, оставив только функцию
Код:
function InitTrig_MyInventory takes nothing returns nothing
endfunction


Т.к. без нее триггер просто не может существовать...
Для справки: Функция InitTrig_MyInventory() выполняется в самую первую очередь до всех событий Map Initializtion

Все глобалки будем добовлять только в триггере инвентаря в самом верху кода. Добавим Кеш.
Код:
globals
    gamecache gcinv = null
endglobals


Теперь нам нужно создать этот кеш, для этого нам пригодится функцию которая выполняется всегда при загрузке карты.

Код:
function InitTrig_MyInventory takes nothing returns nothing
    set gcinv = InitGameCache("MyInventory.w3v")
endfunction


|



Теперь зделаем функцию в которой в массив строк будет заносится условное оформление инвенторя:

Создадим ее в Costum Script'е карты, потому что мы больше к этой функции не вернемся, но чтобы она немешалась создадим именно там.


» функция InvStructureConfig()
Код:
function InvStructureConfig takes nothing returns nothing
    // "X" - слот рюкзака
    // "O" - рамка слота
    // "H" - Положение иконки активного героя (Должо быть только 1)
    
    // "I" - рамка (левая  )
    // "J" - рамка (правая )
    // "-" - рамка (верхния)
    // "_" - рамка (нижния )
    
    // "P" - Угол рамки (Верхне-Левый )
    // "M" - Угол рамки (Верхне-Правый)
    // "N" - Угол рамки (Нижне -Правый)
    // "L" - Угол рамки (Нижне -Левый )
    
    // "U" - Кнобка (Использовать предмет   ) (Должно быть только 1)
    // "Y" - Кнобка (Выбросить предмет      ) (Должно быть только 1)
    // "G" - Кнобка (Отсортировать предметы ) (Должно быть только 1)
    // ">" - Кнобка (Ложить предметы в сумку) (Должно быть только 1)
    // "E" - Кнобка (Выход из инвентаря     ) (Должно быть только 1)
    
    // "/" - Обозначает левый верхний и правых нижний угол описания.                                                (Должно быть только 2)
    // "*" - Определяет верхние левое положение окошка нормального инвентаря героя, он будет занимать 2*3 клеток.   (Должно быть только 1)
    // "{" - Определяет начало строки, где будет писатся описание кнопок-менюшек.                                   (Должно быть только 1)
    // "C" - Определяет начало строки, где будет писатся имя активного героя                                        (Должно быть только 1)
    // "R" - Место где будет писацо что предмет нельзя использовать                                                 (Должно быть только 1)
    //                        0       1       2       3       4       5       6       7       8       9       10      11      12      13      14      15      16      17    18
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[0] = ".......|.......|.......|.......|.......|.......|.......|.......|POX....|-OX....|-OX....|-OX....|MOX....|.......|P/.....|-......|-......|-......|M......" //9
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[1] = ".......|.......|.......|.......|LPMNOH.|.......|.......|.......|IOX....|.OX....|  .OX....|.OX....|JOX....|.......|I......|.......|.......|.......|J......" //8
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[2] = ".......|.......|.......|.......|.......|.......|.......|.......|IOX....|.OX....|  .OX....|.OX....|JOX....|.......|I......|.......|.......|.......|J......" //7
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[3] = ".......|.......|.......|CR.....|.......|.......|.......|.......|IOX....|.OX....|  .OX....|.OX....|JOX....|.......|I......|.......|.......|.......|J......" //6
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[4] = ".......|.......|.......|LPU....|-_Y....|-_G....|MN>....|.......|IOX....|.OX....|.OX....|.OX....|JOX....|.......|I......|.......|...  ....|.......|J......" //5
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[5] = ".......|.......|.......|.......|.......|.......|.......|.......|IOX....|.OX....|  .OX....|.OX....|JOX....|.......|I......|.......|.......|.......|J......" //4
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[6] = ".......|.......|.......|.......|.......|PO*....|MO.....|.......|IOX....|.OX....|  .OX....|.OX....|JOX....|.......|I......|.......|.......|.......|J......" //3
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |YOU    |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[7] = ".......|.......|.......|LPMNE..|.......|IO.....|JO.....|.......|LOX....|_OX....|  _OX....|_OX....|NOX....|.......|I......|.......|.......|.......|J......" //2
    //                    |       |       |       |       |       |       |       |       |^_^    |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[8] = ".......|.......|.......|.......|.......|LO.....|NO.....|.......|{......|.......|  .......|.......|.......|.......|L......|_......|_......|_......|N/....." //1
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    set InvStructure[9] = ".......|.......|.......|.......|.......|.......|.......|.......|.......|.......|  .......|.......|.......|.......|.......|.......|.......|.......|......." //0
    //                    |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
    //                    +-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    //                        0       1       2       3       4       5       6       7       8       9       10      11      12      13      14      15      16      17    18
endfunction



InvStructure - глобальная переменая, добавим ее к списку глобальных переменных в триггере инвентаря.

Код:
globals
    gamecache gcinv = null
+   string array InvStructure
endglobals


а также вызовем эту функцию в функции InitTrig_MyInventory() чтобы она выполнялась при инициализации.

Код:
function InitTrig_MyInventory takes nothing returns nothing
    set gcinv = InitGameCache("MyInventory.w3v")
+   call InvStructureConfig()
endfunction


|



Сделаем функции для добавления записей в таблицу :

Код:
function InventoryInitItemDB_Func takes integer ItemType, string Texture, string Description, integer Cost, boolean Charge returns nothing
    local string ItemTypeStr = I2S(ItemType)
    call StoreString (gcinv, ItemTypeStr, "Description", Description)
    call StoreInteger(gcinv, ItemTypeStr, "Cost"       , Cost       )
    call StoreBoolean(gcinv, ItemTypeStr, "Charge"     , Charge     )
    call StoreString (gcinv, ItemTypeStr, "Texture"    , Texture    )
endfunction

function InventoryInitDestDB_Func takes integer TypeDest, string Texture returns nothing
    call StoreInteger(gcinv, "IconDest", Texture, TypeDest)
endfunction

function InventoryInitIconDB_Func takes integer TypeUnit, string Texture returns nothing
    call StoreString(gcinv, "Texture", I2S(TypeUnit), Texture)
endfunction


InventoryInitItemDB_Func(ItemType, Description, Cost, Charge, Texture)
  • ItemType(integer) - Сам тип предмета к которому будем атачить всю информацию.
  • Description(string) - Описание этого премета.
  • Cost(integer) - Его цена.
  • Texture(string) - Путь к его текстуре.

InventoryInitDestDB_Func(TypeDest, Texture)
  • TypeDest(integer) - Декорация которую будем атачить к текстуре.
  • Texture(string) - Сама текстура.


InventoryInitIconDB_Func(TypeUnit, Texture)
  • TypeUnit(integer) - Юнит к которому атачим текстуру.
  • Texture(string) - Сама текстура.

Последняя функция нужна для того чтобы отображалась иконка активного героя, по самому герою мы получим текстуру, а потом по текстуре получим Декорацию иконку при помощи функции выше


Теперь добавим туда же мою базу данных по предметам

Она прикреплена к посту с названием DataBase.j

Вставим ее в Costum Script'е по тойже причине...

теперь опять подкоректируем функцию InitTrig_MyInventory()
Код:
function InitTrig_MyInventory takes nothing returns nothing
    set gcinv = InitGameCache("MyInventory.w3v")
    call InvStructureConfig()
+   call InventoryInitDB()
endfunction


|



Теперь нам нужно создать функцию которая создает инвентарь.

Так как у нас инвентарь будет мультиплеерный, то функция должна принимать не только позицию инвенторя, но и группу игроков для которых будет создан инвентарь.
Также функция будет еще принимать равкод абилки которая активирует инвентарь.
Код:
function CreateInventoryForForce takes force f, real invX, real invY, integer AbilCode returns nothing
endfunction


А теперь в триггере инициализации в конце через костум скрипт, вызываем эту функцию
cs: call CreateInventoryForForce(GetPlayersAll(), 0, 0, 'A000')
Для справки: Коды триггеров выстраиваются по пряду сверху вниз как в редакторе триггеров, нужно чтобы обьявление функции в триггере MyInventory, было выше чем вызов ее в триггере инициализации, т.е. он должен быть ниже, если всеравно выдает ошибку перезапустите карту...

Все, подготовка завершена, теперь можете сохранить карту, если не сохранилась значит сделали чтото не то...

Код:
globals
    gamecache gcinv = null
    string array InvStructure
endglobals

+function CreateInventoryForForce takes force f, real invX, real invY returns nothing
+endfunction

function InitTrig_MyInventory takes nothing returns nothing
    set gcinv = InitGameCache("MyInventory.w3v")
    call InvStructureConfig()
    call InventoryInitDB()
endfunction



Третья часть - "Рисуем" инвентарь



Теперь у нас есть одна проблема - "каким способом нам рисовать инвентарь?"

Напомню что в инвентарях Zibad'ы и Netrat'а создается инвентарей по количеству игроков, т.е. если игроков 12, то инвентарей будет 12...

Так делается чтобы все игроки могли пользоватся инвентарем одновремено, иначе бы если бы 2 игрока запустили инвентари одновремено, то один из играков видел все предметы и действия другово игрока т.к. они распологались на одной территории.

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

Ну а мы сделаем иначе, мы создадим инвентарь на одном месте, НО сделаем так чтобы если игрок пользовался инвентарем, то все его действия были бы невидны другим игрокам, как мы это сделаем? А через функцию GetLocalPlayer()

Примечание: Предпологается что читатель имеет о функции GetLocalPlayer() хотябы базовые знания, знает что это и как работает


Напомню только что использование этой функции чревато Дисинхранизацией, потому использовать ее нужно очень осторожно иначе это вызовет много проблем.

|



Помните мы решили в целях удобства и универсальности сделать так чтобы инвентарь можно было рисовать в коде в виде Строковых переменных.
Теперь же мы напишем функцию которая по этим строкам создает инвентарь.

Для начала создадим пару переменых в которые запишутся входящие параметры функции CreateInventoryForForce(), т.к. мы будем обращатся к ним не однократно

Код:
globals
    gamecache gcinv = null
    string array InvStructure
+   integer InvAbilCode // Рав код абилки активирующую инвентарь
+   real XINV      // /Координаты верхнего
+   real YINV      // \левого угла инвентаря
+   force ForceInv // Группа игроков для которой включен инвентарь
endglobals


Теперь нам нужно заполнить эти переменые значениями, в функции, в которой создается инвентрь

CreateInventoryForForce(Force, invX, invY, AbilCode)

вот в ней и будем инициализировать эти переменые

Код:
function CreateInventoryForForce takes force f, real invX, real invY, integer AbilCode returns nothing
+    set ForceInv = f
+    set XINV = invX
+    set YINV = invY
+    set InvAbilCode = AbilCode
endfunction


Также имейте ввиду что нам не нужно создавать инвентари для компьютеров или неиграющих играков, такчто имеет смысл подправить ту force что мы передаем в функцию

Код:
+function FilterForceInv takes nothing returns boolean
+   return GetPlayerController(GetFilterPlayer()) == MAP_CONTROL_USER and GetPlayerSlotState(GetFilterPlayer()) == PLAYER_SLOT_STATE_PLAYING
+endfunction

function CreateInventoryForForce takes force f, real invX, real invY, integer AbilCode returns nothing
    set ForceInv = f
    set XINV = invX
    set YINV = invY
    set InvAbilCode = AbilCode
+   call ForceEnumPlayers(ForceInv, Filter(function FilterForceInv))
endfunction


Таким образом мы изключаем из группы играков всех неиграющих и всех нелюдей.

И получится что из всех игровов которых мы передали - GetPlayersAll(), во время теста, передастся только один игрок - мы.

|



Помните я расказывал об инвеноре Zibad'ы и Netrat'а? о том что там создается инвентарь для каждого игрока
Предположим нам нужно создать инвентарь для всех 12 играков, и предположим что оформление одного инвенторя состоит из 300 декораций
Напоминание: Имейте ввиду что оформление инвенторя зависит от рассы игрока, т.е. если это эльф, то оформление будет другое в отличии от например нежити.

Т.е. для всех играков получается создается от 300 до 3600(300*12) декораций.

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

Фишка в том что нам не надо будет создавать более одного инвентаря каждой рассы играков
т.е. мы на одном месте создадим инвентарь для Людей, Орков, Нежити и Ельфов. А потом просто скроем все декорации от всех Играков, и сделаем их видимыми только для игроков соответствующих расс.

Получается декораций у нас будет от 300 до 1200(300*4) декораций

Тогда сначало узнаем какие именно расы у нас присутствуют в нашем ForceInv

Код:
globals
    gamecache gcinv = null
    string array InvStructure
    real XINV
    real YINV
    force ForceInv
    integer InvAbilCode
+   boolean array RaceExists // Существуют ли такието рассы в группе ForceInv
endglobals


По умолчанию все элементы массива равны false

Наша задача сделать так чтобы Если в ForceInv существует раса Людей, то RaceExists[0] = true
-||- Орков, то RaceExists[1] = true
-||- Нежити, то RaceExists[2] = true
-||- Ельфов, то RaceExists[3] = true

Сделаем это в функции CreateInventoryForForce(Force, invX ,invY)

Код:
function CreateInventoryForForce takes force f, real invX, real invY, integer AbilCode returns nothing
+   local integer i = 0 // Эта индексая переменная для перебора всех игроков
+   local player p
    set ForceInv = f
    set XINV = invX
    set YINV = invY
    set InvAbilCode = AbilCode
    call ForceEnumPlayers(ForceInv, Filter(function FilterForceInv))
+   loop
+       exitwhen i > 11 // Перебираем всех игроков от 0 до 11
+       set p = Player(i)
+       if IsPlayerInForce(p, ForceInv) then // Если Выбраный игрок присутствует в нашем ForceInv
+           set RaceExists[H2I(GetPlayerRace(p))-1] = true
+       endif
+       set i = i + 1
+   endloop
endfunction



строчка

set RaceExists[H2I(GetPlayerRace(p))-1] = true

равносильна этому:
Код:
if     GetPlayerRace(p) == RACE_HUMAN    then // Если он человек
    set RaceExists[0] = true
elseif GetPlayerRace(p) == RACE_ORC      then // Если он орк
    set RaceExists[1] = true
elseif GetPlayerRace(p) == RACE_UNDEAD   then // Если он нежить
    set RaceExists[2] = true
elseif GetPlayerRace(p) == RACE_NIGHTELF then // Если он ельф
    set RaceExists[3] = true
endif


потомучто RACE_HUMAN, RACE_ORC, RACE_UNDEAD и RACE_NIGHTELF имеют хендлы 1, 2, 3 и 4 соответствено.

Теперь нам извесны все расы для которых нам нужно создать инвентарь

|



Далее нам нужно написать анализатор массива InvStructure

Напишей его в тойже функции CreateInventoryForForce(Force, invX ,invY)

Вот как он будет выглядеть:

Код:
function CreateInventoryForForce takes force f, real invX, real invY, integer AbilCode returns nothing
+   local string simvol // Расматриваемый символ
+   local integer Length
+   local integer y = 0 // Эта индексая переменная для перебора всех ячеек массива InvStructure
    local integer i = 0 // Эта индексая переменная для перебора всех символов в одной ячейке массива InvStructure
+   local integer x // переменная i обозначает текущий номер расматриваемоего индекса, а x уже отображает информацию о взаиморасположении обьекта о котором нам говорит расматриваемый символ
    
+   local real X //  /Полная координата
+   local real Y //  \расматриваемого символа

    local player p
    set ForceInv = f
    set XINV = invX
    set YINV = invY
    set InvAbilCode = AbilCode
    call ForceEnumPlayers(ForceInv, Filter(function FilterForceInv))
    loop
        exitwhen i > 11
        set p = Player(i)
        if IsPlayerInForce(p, ForceInv) then
            set RaceExists[H2I(GetPlayerRace(p))-1] = true
        endif
        set i = i + 1
    endloop
+   loop
+       exitwhen y > 9 // Размер массива InvStructure[0-9]
+       set Length = StringLength(InvStructure[y])-1 // Узнаем сколько символов в данной ячейке массива
+       set i = 0 //  /Т.к. расматриваем новую ячейку, то
+       set x = 0 //  \сбрасываем значения индексов прошлой ячейки
+       loop
+           exitwhen i > Length // Переберать символы от 0 до конца строки
+           set simvol = SubString(InvStructure[y], i, i+1) // Следующий символ
+           set X = invX+20*x //  /Полная координата
+           set Y = invY-20*y //  \расматриваемого сивола
+            
+            
+           if simvol == "." then // Ничего не делать, так сделано потомучто точек больше всего в строках, и чтобы if по 100 раз не проверял их на равенство с дркгими символами то логичнее было бы равенство на точку поставить сначало
+           elseif simvol == "|" then // Увеличить переменую x на 1, т.е. начинаются символы ячейки что правее текущей
+               set x = x + 1
+         //elseif simvol == "Другой Символ" then
+             //Действия
+           endif
+           
+           set i = i + 1
+       endloop
+       set y = y + 1
+   endloop
endfunction


Как видите мы просто перебираем каждый символ из этого массива строк.
Там Где написано

//elseif simvol == "Другой Символ" then
//Действия


Можно вставить обработку следующего символа и потом следующего и т.д.

|



Теперь нам нужно сделать оформление инвентаря, т.е. рамки иконки и черный фон.

Для этого нам потребуется новая функция которая создает все 4 декорации для разных расс и показыывает их нужным игрокам.

Вот эта функция:
Код:
function CreateDestructableForRaces takes integer TypeHuman, integer TypeOrc, integer TypeUndead, integer TypeElf, real X, real Y returns nothing
    local destructable d
    if RaceExists[0] then // если у нас есть люди
        set d = CreateDestructable (TypeHuman , X, Y, 0, 1, 0)
        call ShowDestructable(d, GetPlayerRace(GetLocalPlayer()) == RACE_HUMAN   ) // Показать только для люди
    endif
    if RaceExists[1] then // если у нас есть орки
        set d = CreateDestructable (TypeOrc   , X, Y, 0, 1, 0)
        call ShowDestructable(d, GetPlayerRace(GetLocalPlayer()) == RACE_ORC     ) // Показать только для орков
    endif
    if RaceExists[2] then // если у нас есть нежить
        set d = CreateDestructable (TypeUndead, X, Y, 0, 1, 0)
        call ShowDestructable(d, GetPlayerRace(GetLocalPlayer()) == RACE_UNDEAD  ) // Показать только для нежити
    endif
    if RaceExists[3] then // если у нас есть ельфы
        set d = CreateDestructable (TypeElf   , X, Y, 0, 1, 0)
        call ShowDestructable(d, GetPlayerRace(GetLocalPlayer()) == RACE_NIGHTELF) // Показать только для ельфов
    endif
    set d = null
endfunction


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

и теперь создадим эти декорации

Замените

Код:
if simvol == "." then
elseif simvol == "|" then
   set x = x + 1
//elseif simvol == "Другой Символ" then
    //Действия
endif


на это:

Код:
if simvol == "." then
elseif simvol == "|" then
    set x = x + 1
    
    // Рамки
+elseif simvol == "I" then
+   call CreateDestructableForRaces('C001', 'C002', 'C003', 'C004', X, Y) //Левая
+elseif simvol == "J" then
+   call CreateDestructableForRaces('C005', 'C006', 'C007', 'C008', X, Y) //Правая
+elseif simvol == "-" then
+   call CreateDestructableForRaces('C009', 'C00A', 'C00B', 'C00C', X, Y) //Верхния
+elseif simvol == "_" then
+   call CreateDestructableForRaces('C00D', 'C00E', 'C00F', 'C00G', X, Y) //Нижния
+elseif simvol == "P" then
+   call CreateDestructableForRaces('C00H', 'C00I', 'C00J', 'C00K', X, Y) //Верх-Лево
+elseif simvol == "M" then
+   call CreateDestructableForRaces('C00L', 'C00M', 'C00N', 'C00O', X, Y) //Верх-Право
+elseif simvol == "N" then
+   call CreateDestructableForRaces('C00P', 'C00Q', 'C00R', 'C00S', X, Y) //Низ-Право
+elseif simvol == "L" then
+   call CreateDestructableForRaces('C00T', 'C00U', 'C00V', 'C00W', X, Y) //Низ-Лево
+    
+elseif simvol == "O" then
+   call CreateDestructableForRaces('C00X', 'C00Y', 'C00Z', 'C010', X, Y) //Рамка Иконки
     
    // Кнопки
+elseif simvol == "E" then
+   call CreateDestructable ('C01E', X, Y, 0, 1, 0) // Вернутся в игру
+elseif simvol == "U" then
+   call CreateDestructable ('C01J', X, Y, 0, 1, 0) // Использовать выбранный предмет
+elseif simvol == "Y" then
+   call CreateDestructable ('C01H', X, Y, 0, 1, 0) // Выкинуть предмет (Выключена)
+elseif simvol == "G" then
+   call CreateDestructable ('C01L', X, Y, 0, 1, 0) // Отсартировать
+elseif simvol == ">" then
+   call CreateDestructable ('C01C', X, Y, 0, 1, 0) // Ложить предметы в инвентарь
endif


Думаю код очень нагляден, для каждого символа создаем свои декорации.

И в функции CreateInventoryForForce(Force, invX, invY, AbilCode)

прямо перед вторым циклом ставим еще такую строку

Код:
call CreateDestructable ('C000', invX-70, invY+90, 0, 1, 0) //Черный фон


Декорация 'C000' - просто черная большая пластина.

Теперь запустите в игре, посередине карты вы увидите маленький инвентарь, И вы будете видеть его по разному изходя из того какой вы рассы, можете зайти игроком другой рассы и проверить.


Четверая часть - Делаем "Запуск" и "Выход" из инвенторя



Т.к. у нас появляется 2 состояния игрока "Инвентарь открыт" и "Инвентарь закрыт"

Потому создаем глобальную переименую:
boolean array OpenInv

OpenInv[0-11] - Возвращает значение того "открыт ли инвентарь в данный момент у игрока 0-11?"

Также нам еще потребуется таймер

Код:
globals
    gamecache gcinv = null
    string array InvStructure
    real XINV
    real YINV
    force ForceInv
    integer InvAbilCode
    boolean array RaceExists
+   timer ViewCamera //Таймер обновления камеры

+   boolean array OpenInv
endglobals


Этот таймер создадим и запустим в функции CreateInventoryForForce(Force, invX, invY, AbilCode)

Для начала напишем функцию которую будет выполнять таймер:

Она будет устанавливать камеру на инвентарь для всех играков из ForceInv инвентарь у которых открыт.

Код:
function ViewCamera_Action takes nothing returns nothing
    if IsPlayerInForce(GetLocalPlayer(), ForceInv) and OpenInv[GetPlayerId(GetLocalPlayer())] then
        call PanCameraToTimed(XINV+200, YINV-70, 0) // Устанавливаем камеру примерно в цент инвентаря
        call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, 400, 0) // Дальность от земли - 400
        call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, 270, 0) // Угол Атаки - 270
        call SetCameraField(CAMERA_FIELD_ROTATION       , 90 , 0) // Угол Поворот - 90
    endif
endfunction


В функции нет никакого цикла на перебор всех игроков, но одновремено с этим она обрабатывает каждого игрока в отдельности, это одно из преемуществ GetLocalPlayer()
А теперь создаем и запускаем этот таймер

Код:
function CreateInventoryForForce takes force f, real invX, real invY, integer AbilCode returns nothing
    local string simvol
    local integer Length
    local integer y = 0
    local integer i = 0
    local integer x
    
    local real X
    local real Y

    local player p
    set ForceInv = f
    set XINV = invX
    set YINV = invY
    set InvAbilCode = AbilCode
    call ForceEnumPlayers(ForceInv, Filter(function FilterForceInv))

+   set ViewCamera = CreateTimer()
+   call TimerStart(ViewCamera, 0.05, true, function ViewCamera_Action)

    //......
    //......
    //......
endfunction


Т.е. этот таймер будет активирован на протежении всей игры, и чтобы активировать его для какогото игрока, надо лиш только присвоить значение переменной OpenInv true, для нужного игрока.

|




Теперь нам нужно создать 2 триггера
1) Вход в инвентарь при касте способности 'A000'
2) Выход из инвенторя при нажатии кнобки Esc

Но перед тем как задумыватся о триггерах, давайте просто создадим функции Открытия/Закрытия инвенторя

Для начала создадим еще одну глобалку, которая отображает для какого Юнита у нас сейчас отрыт инвентарь
Обратите внимание:Мы создаем функцию нисвязвную с никакими событияи отдельно, а при событие будем выполнять ее, именно так нужно всегда делать, создавать свой своеобразный api, т.е. поясню на текущем примере, у нас будет функция Открыть() и Закрыть(), при сработывнии события мы используем ту или иную функцию и в этом ничего страного, но алюс в том что эти функции будут еще доступны вне движка инвенторя, тому кто будет этим инвентарем пользоватся

Код:
globals
    gamecache gcinv = null
    string array InvStructure
    real XINV
    real YINV
    force ForceInv
    ineger InvAbilCode
    boolean array RaceExists
    timer ViewCamera

    boolean array OpenInv
+   unit array CurrentInvUnit
endglobals



Открытие:

Код:
function OpenInventory takes unit u returns nothing
    local integer id = GetPlayerId(GetOwningPlayer(u)) // Номер игрока
    call IssueImmediateOrder(u, "stop") // Остановить юнита который использовал способность
    set CurrentInvUnit[id] = u // Юнит для которого открыть инвентарь Для данного игрока
    set OpenInv[id] = true // Инвентарь открыт для даного игрока
    call ViewCamera_Action() // Вызываем функцию которую использует таймер ViewCamera
endfunction


Функция принимает юнита, т.к. именно юнит является основным пораметром для инвенторя, т.к. именно у каждого юнита содержится разный набор предметов.

set OpenInv[id] = true

Этим действием мы считай уже Открыли инвентарь, т.к. таймер ViewCamera постояно каждые 0,05 сек вызывает функцию ViewCamera_Func()
И если эта переменая ровна true, то и устанавливает для этого игрока камуру в нужное положение.

После мы еще раз вызываем функцию ViewCamera_Action() чтобы нам не пришлось ждать от 0 до 0,05 секунд до следующего тика таймера, как камера переместится, а все сработало сразуже.


Закрытие:

Код:
function CloseInventory takes player p returns nothing
    local integer id = GetPlayerId(p)
    set OpenInv[id] = false
    if GetLocalPlayer() == p then
        call ResetToGameCamera(0)
        call PanCameraToTimed(GetUnitX(CurrentInvUnit[id]), GetUnitY(CurrentInvUnit[id]), 0)
    endif
    set CurrentInvUnit[id] = null
endfunction


Все должно быть понятно.
Устанавливаем переменной OpenInv значение false и таймер перестанет выполнять свои действия для этого игрока

Далее используя GetLocalPlayer() устанавливаем стандартное положение камеры в позицию юнита который открывал этот инвентарь.

Ну и обнуляем переменую в которой содержится юнит для которого открыт инвентарь.

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

С этими функциями пожалуй все, теперь перейдем к триггерам...

|



Функция CreateInventoryForForce(Force, invX, invY, AbilCode) у нас и так уже сильно разбухла, и еще больше разбухнет в будущем, потому сделаем маленькое ответвление:

Код:
+function CreateInvTriggers takes nothing returns nothing
+endfunction

function CreateInventoryForForce takes force f, real invX, real invY, integer AbilCode returns nothing
    //...........
        endif
        set i = i + 1
    endloop
    
+   call CreateInvTriggers()
    
    call CreateDestructable ('С000', invX-70, invY+90, 0, 1, 0)
    loop
        exitwhen y > 9
        set Length = StringLength(InvStructure[y])-1
    //...........
endfunction


Мы сделали функцию CreateInvTriggers() в которой будем создавать все подобные прочии триггеры, и вызываем эту функцию в конце функции CreateInventoryForForce(Force, invX ,invY)

|



Давайте на немного отвлечомся, и посмотрим на эти две функции

Код:
function TriggerRegisterAnyUnitEventBJ takes trigger trig, playerunitevent whichEvent returns nothing
    local integer index

    set index = 0
    loop
        call TriggerRegisterPlayerUnitEvent(trig, Player(index), whichEvent, null)

        set index = index + 1
        exitwhen index == bj_MAX_PLAYER_SLOTS
    endloop
endfunction

native TriggerRegisterPlayerEvent takes trigger whichTrigger, player  whichPlayer, playerevent whichPlayerEvent returns event


Первая функция Автомотически создается в триггере при использования события Боевая Еденица - Generic UnitEvent в GUI

Вторая функция Тоже автомотически создается но при использовании события Игрок - Cinematic Skipped

Мы их немножко поправим в тот вид в котором будем использовать

Код:
function TriggerRegisterAnyUnitEventInv takes trigger trig, playerunitevent whichEvent, boolexpr f returns nothing
    local integer i = 0
    local player p
    loop
        exitwhen i > 11
        set p = Player(i)
        if IsPlayerInForce(p, ForceInv) then
            call TriggerRegisterPlayerUnitEvent(trig, p, whichEvent, f)
        endif
        set i = i + 1
    endloop
endfunction

function TriggerRegisterPlayerEventInv takes trigger trig, playerevent whichEvent returns nothing
    local integer i = 0
    local player p
    loop
        exitwhen i > 11
        set p = Player(i)
        if IsPlayerInForce(p, ForceInv) then
            call TriggerRegisterPlayerEvent(trig, p, EVENT_PLAYER_END_CINEMATIC)
        endif
        set i = i + 1
    endloop
endfunction


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

Всуньте эти функции в нестандартный код чтобы не мазолили глаза

|



Ну и теперь непосредствено создаем триггеры

Начнем с открытия инвенторя:

Код:
+function InvStart_Conditions takes nothing returns boolean
+   return GetSpellAbilityId() == InvAbilCode and not OpenInv[GetPlayerId(GetOwningPlayer(GetSpellAbilityUnit()))]
+endfunction

+function InvStart_Actions takes nothing returns nothing
+   call OpenInventory(GetSpellAbilityUnit())
+endfunction

function CreateInvTriggers takes nothing returns nothing
+    local trigger trig = CreateTrigger()
+    call TriggerRegisterAnyUnitEventInv(trig, EVENT_PLAYER_UNIT_SPELL_CAST, null    ) // Юнит кастует Магию
+    call TriggerAddCondition           (trig,Condition(function InvStart_Conditions))
+    call TriggerAddAction              (trig,          function InvStart_Actions    )
+    set trig = null
endfunction


Видите, все довольно просто, создаем тригер, нацепляем ему Функцию-Действие, в которой вызываем только функцию открытия инвентаря которую мы уже написали, принимающую юнита который включил эту способность.

А также подцепляем к этому триггеру Условие:
1) Чтобы Кастуемая способность была InvAbilCode т.е. инвентарь наш.
2) Чтобы инвентарь у Владельца этого юнита еще небыл открыт

И еще один триггер зкрытия инвенторя:

Код:
+function GetOpenInvPl takes nothing returns boolean
+   return OpenInv[GetPlayerId(GetTriggerPlayer())]
+endfunction

+function PressEsc_Action takes nothing returns nothing
+   call CloseInventory(GetTriggerPlayer())
+endfunction

function CreateInvTriggers takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventInv(trig, EVENT_PLAYER_UNIT_SPELL_CAST, null    )
    call TriggerAddCondition           (trig,Condition(function InvStart_Conditions))
    call TriggerAddAction              (trig,          function InvStart_Actions    ) 
+   set trig = CreateTrigger()
+   call TriggerRegisterPlayerEventInv(trig, EVENT_PLAYER_END_CINEMATIC        ) // Игрок Нажимает Esc
+   call TriggerAddCondition          (trig,Condition(function GetOpenInvPl   ))
+   call TriggerAddAction             (trig,          function PressEsc_Action )
    set trig = null       
endfunction


Ну тут до смехотворного все просто, небуду даже коментировать.

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


Пятая часть - Делаем Слоты инвенторя



В этой главе мы начнем работать с трекаблами.

Во первых в глобальные переменные добавим еще три переменые:

Код:
globals
    gamecache gcinv = null
    string array InvStructure
    real XINV
    real YINV
    force ForceInv
    integer InvAbilCode
    boolean array RaceExists
    timer ViewCamera

+   integer AmountSlot // Сюда будет занесено количество слотов в инвенторе

    boolean array OpenInv
    unit array CurrentInvUnit
    
+   real array InvCellX
+   real array InvCellY
endglobals


В InvStructure[] ячейка у нас обозначается символом "X"

Потому в функцию CreateInventoryForForce(Force, invX ,invY)

В тот большой if, добавим еще один elseif:

Код:
+function CteateSlotInventoryForForce takes real X, real Y, integer Index returns nothing
+endfunction

function CreateInventoryForForce takes force f, real invX, real invY returns nothing
    //...
    call CreateInvTriggers()
    call CreateDestructable ('С100', invX-70, invY+90, 0, 1, 0)
+   set AmountSlot = -1 // Переменая начинается с -1, потомучто будет и нулевой слот
    loop
        //...
        loop
            //...
            if simvol == "." then
            elseif simvol == "|" then
                set x = x + 1
+           elseif simvol == "X" then // Создать слот
+               set AmountSlot = AmountSlot + 1 // Повышаем количество слотов на 1
+               call CteateSlotInventoryForForce (X, Y, AmountSlot)
                
                // Рамки
            elseif simvol == "I" then
                call CreateDestructableForRaces('BX05', 'BX08', 'BX06', 'BX07', X, Y)
            //...
            endif
            set i = i + 1
        endloop
        set y = y + 1
    endloop
endfunction


CteateSlotInventoryForForce (X, Y, Index) - Функция создания ячейки, принимает Координаты этой самой ячейки а также уникальный номер, т.е. у каждой ячейки будет свой номер от 0 до (количство ячеек - 1)

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

Напишем тело функции CteateSlotInventoryForForce (X, Y, Index)

Во первых запоминаем координаты Слота:

Код:
function CteateSlotInventoryForForce takes real X, real Y, integer Index returns nothing
+   set InvCellX[Index] = X
+   set InvCellY[Index] = Y
endfunction


На этом мы пока остановимся, и обсудим одну вещь косающуюся трекаблов и без которой сейчас не сможем обойтись

|



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

Нужно сказать что в функции-действии триггера с событием Нажатия/Наведения на трекабл, можно получить только сам трекабл функцией GetTriggeringTrackable()

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

Наша с вами основная проблема это определение игрока который нажал на трекабл

Т.е. допустим у нас играют 12 игроков, и они одновремено включили инвентарь, один из игроков нажмет на трекабл, но как узнать какой игрок нажал? И для какого игрока выполнять действия в результате нажатия?

Если мы будем через GetLocalPlayer() создавать трекаблы для отдельных играков, то это вызовит Десинхронизацию.

т.е. надо в каждой нужной позиции создавать по 12 трекаблей для всех играков, и через кеш дать им значение Номера игрока которому они принадлежат,
и потом "показать" их только каждому игроку в отдельности, т.е. например как мы делали декорации.

Но тут новая проблема, у декорации есть действие ShowDestructable()
А вот аналогичного действия у трекаблов нет.

Однако, можно сделать так:

Код:
local player p = Player(2)
local trackable tk
local string path = ""
if GetLocalPlayer() == p then
    set path = "square.MDX"
endif
set tk = CreateTrackable(path, X, Y, 0)


Да, да, это будет работать...

Т.е. получается Мы создаем Трекабл для всех играков
Но для третьего игрока созданый трекабл будет иметь модель "square.MDX"
А для всех остальных игроков у трекабла не будет модели

Т.е. если я приатачу через кеш к этому трекаблу значение 2, т.е. номер игрока
то потом используя его в событии триггера, я смогу получить трекабл функцией GetTriggeringTrackable(), и по ней из кеша дастать номер игрока которому он принадлежит.

Т.е. надо создавать до 12 трекаблей на одной и тойже позиции, но показывать каждый трекабл только одному из этих 12 играков

|



Теперь продолжим заполнение функции CteateSlotInventoryForForce (X, Y, Index)

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

Потому ВСЕ трекабли-слоты инвенторя будут обробатывать только 2 триггера

Давайте обьявим их в глобалках

Код:
globals
    gamecache gcinv = null
    string array InvStructure
    real XINV
    real YINV
    force ForceInv
    integer InvAbilCode
    boolean array RaceExists
    timer ViewCamera

    integer AmountSlot

+   trigger TkHit // Триггер нажатия
+   trigger TkTrack // Триггер наведения

    boolean array OpenInv
    unit array CurrentInvUnit
    
    real array InvCellX
    real array InvCellY
endglobals


И создадим их в функции где у нас создаются триггеры, т.е. в CreateInvTriggers()

Код:
+function InvHitTrackableRucksack takes nothing returns nothing
+   //Действие если трекабл нажат
+endfunction

+function InvTrackTrackableRucksack takes nothing returns nothing
+   //Действие если на трекабл навели мышкой
+endfunction

+function GetOpenInvTk takes nothing returns boolean
+   return OpenInv[GetStoredInteger(gcinv, I2S(H2I(GetTriggeringTrackable())), "IdPlayer")]
+endfunction

function CreateInvTriggers takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventInv(trig, EVENT_PLAYER_UNIT_SPELL_CAST, null    )
    call TriggerAddCondition           (trig,Condition(function InvStart_Conditions))
    call TriggerAddAction              (trig,          function InvStart_Actions    ) 
    set trig = CreateTrigger()
    call TriggerRegisterPlayerEventInv(trig, EVENT_PLAYER_END_CINEMATIC        )
    call TriggerAddCondition          (trig,Condition(function GetOpenInvPl   ))
    call TriggerAddAction             (trig,          function PressEsc_Action )
    set trig = null
    //       
+   set TkHit = CreateTrigger()
+   call TriggerAddCondition(TkHit  , Condition(function GetOpenInvTk))
+   call TriggerAddAction   (TkHit  , function InvHitTrackableRucksack) 
+   set TkTrack = CreateTrigger()
+   call TriggerAddCondition(TkTrack, Condition(function GetOpenInvTk  ))
+   call TriggerAddAction   (TkTrack, function InvTrackTrackableRucksack)
endfunction


функция GetOpenInvTk() подобна функции GetOpenInvPl() что мы делали раньше, только подработана для данной ситуации.
В ней вы видите пример получения номера игрока из трекабла.

Мы натягиваем на триггер только действие и условие, т.е. они будут в одном экземпляре, а вот событий будет много (КоличествоСлотов*Количество игроков), их мы натяним на триггер в функции CteateSlotInventoryForForce()

Но имейте ввиду что это еще только пол пути, мы еще не создавали ни трекаблов и неатачили им никаких номеров, это мы сделаем в функции CteateSlotInventoryForForce (X, Y, Index)

Код:
function CteateSlotInventoryForForce takes real X, real Y, integer Index returns nothing
    local integer i = 0 // Индексная переменая для перебора всех игроков
    local player p
    local trackable tk // Переменая в которой создадим трекабл
    local string path // Путь к модели трекабла
    call CreateDestructableForRaces('C011', 'C012', 'C013', 'C014', X, Y) // Создаем просто пустой слот, он для каждой рассы свой
    loop
        exitwhen i > 11 // для 0-11 игроков
        set p = Player(i)
        if IsPlayerInForce(p, ForceInv) then // Если выбраный игрок находится в нашем ForceInv
            //Манипуляция с путем к модели о котором шла речь ранее
            set path = ""
            if GetLocalPlayer() == p then
                set path = "TkSquare.MDX"
            endif
            set tk = CreateTrackable(path, X, Y, 0)
            // Добовление в ране расмотренные триггеры событий...
            call TriggerRegisterTrackableHitEvent  (TkHit  , tk) // Клика
            call TriggerRegisterTrackableTrackEvent(TkTrack, tk) // И наведения
            // Атачим к трекаблу...
            call StoreInteger(gcinv, I2S(H2I(tk)), "Index"   , Index) // Индекс его слота
            call StoreInteger(gcinv, I2S(H2I(tk)), "IdPlayer", i    ) // И номер игрока
        endif
        set i = i + 1
    endloop
    set InvCellX[Index] = X
    set InvCellY[Index] = Y
endfunction


|



Теперь чтобы увидеть хоть какойто результат нашей работы сунем какиенибудь действия в функции InvHitTrackableRucksack() и InvTrackTrackableRucksack()

Код:
function InvHitTrackableRucksack takes nothing returns nothing
+   local trackable tk = GetTriggeringTrackable()
+   local integer PId   = GetStoredInteger(gcinv, I2S(H2I(tk)), "IdPlayer")
+   local integer Index = GetStoredInteger(gcinv, I2S(H2I(tk)), "Index"   )
+   call echo("Игрок " + I2S(PId) + " нажал на трекабл № "+ I2S(Index))
endfunction

function InvTrackTrackableRucksack takes nothing returns nothing
+    local trackable tk = GetTriggeringTrackable()
+    local integer PId   = GetStoredInteger(gcinv, I2S(H2I(tk)), "IdPlayer")
+    local integer Index = GetStoredInteger(gcinv, I2S(H2I(tk)), "Index"   )
+    call echo("Игрок " + I2S(PId) + " навел на трекабл № "+ I2S(Index))
endfunction


Это позволить хоть какнить оценить работу функции, можете даже потестить в мультиплеере


Шестая часть - Юнит подбирает предмет



Всегда перед тем как чето делать надо подумать, а в каком виде мы будем хранить предметы?

Долеко не все, но некоторые, делали инвентарь храня только тип предмета, т.е. юнит подбирает предмет -> атачить тип предмета к юниту (Это проще, потому что тип это integer) -> предмет удалить
Когда мы достаем из инвентаря этот предмет то мы создем новый предмет по типу который запомнели.

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

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

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

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

Но мы сделаем инвентарь не на кеше а на массивах, и потому на это нам совершенно пох, но всеже стоило упомянуть.

Но раз мы делаем инвентарь не на кеше а на массивах то тут будут свои проблемы.


как вы помните мы договорились делать инвентарь для каждого юнита, т.е. если выберем юнит и запустим инвентарь, то в нем будут только предметы которые принадлежат чисто ему, выберем другово - предметы будут другие.

И вот возникает пролема, "как атачить все предметы к юниту через _массивы_?"

допустим мы использовали бы кеш, то тогда мы просто первым индексом использовали Хендл юнита, а вторым порядковый номер в инвенторе, и под каждый прядковый намер от 0 до AmountSlot засунем в инвентарь хендл предмета.

Это самый простой способ, но если мы делаем на массивах - забудте о строках и хендалх.

Мы обойдемся одним массивом предметов. максимум мы можем хранить 8190 предметов

Допустим что для одного юнита нам за глаза хватит 150 предметов, это даже будет выглядеть чересчур нагроможденным, но возмем именно 150
тогда R2I(8190/150)=54

Т.е. получается что используя один массив мы можем обеспечить инвентарь для 54 юнитов.

Нам еще прийдется каждому юниту приатачить индекс с которого начинаются его ячейки в массиве, т.е. например 0 или 1 или 2 или ... или 53. Предмет будут хронится в массиве ItemInUnit[] от i*150 до i*150+150 - [0-150], [150-300], ..., [7950-8100].

Но тут все говорит о том что нам надо будет вручную давать юниту этот индекс, т.е. его же нельзя дать сразу всем юнитам на карте, потому что размер ограничен.
Наи прийдется написать свою функцию UnitAddAbilityInventory (по нозванию понятно)

и еще сделаем один массив булеонов, ячейки которого [0-53] будут показывать свободно ли это место для нового юнита или оно занято уже созданым.

Код:
globals
    gamecache gcinv = null
    string array InvStructure
    real XINV
    real YINV
    force ForceInv
    integer InvAbilCode
    boolean array RaceExists
    timer ViewCamera

    integer AmountSlot

    trigger TkHit
    trigger TkTrack

+   boolean array UnitOccupied // Количество юнитов использующих инвентарь ограницено, это просто флаговая переменная обозначающая свободна ли позиция юнита от 0 до 53 или нет

    boolean array OpenInv
    unit array CurrentInvUnit
    
    real array InvCellX
    real array InvCellY
    
+   item array ItemInUnit
endglobals


Теперь напишем функцию UnitAddAbilityInventory(), но перед этим давайте зайдем в РО и удалим у нашего юнита способность инвентаря, т.к. мы всеравно будем добовлять ее вручную.

Код:
function UnitAddAbilityInventory takes unit u returns boolean
    local integer i = 0
    loop
        exitwhen i > 53
        if not UnitOccupied[ i] then
            if UnitAddAbility(u, InvAbilCode) then // Даем абилку
                set UnitOccupied[ i] = true // Ячейка занята
                call StoreInteger(gcinv, I2S(H2I(u)), "Index", i) // Присваеваем индекс юнита юниту
                return true
            endif
            exitwhen true
        endif
        set i = i + 1
    endloop
    return false
endfunction


Все должно быть понятно, перебираем массив UnitOccupied[], и ищем свободные ячейки, как только нашли то дать юниту абилку инвенторя, пометить ячейку занятой, и приатачит через кеш его индес массива ItemInUnit[].

Функция возвращает true если добавить удалось, и false если по каким бы то небыло причинам этого сделать не удалась.

ну раз зошла тема то и напишем функцию для удаления абилки

Код:
function UnitRemoveAbilityInventory takes unit u returns boolean
    local integer i
    if UnitRemoveAbility(u, InvAbilCode) then
        set i = GetStoredInteger(gcinv, I2S(H2I(u)), "Index") // Получаем индекс юнита
        set UnitOccupied[ i] = false // Ячейка свободна
        set i = i * 150 // Индекс с которого начинают идти предметы юнита
        loop
            exitwhen i > i + 150
            set ItemInUnit[ i] = null
            set i = i + 1
        endloop
        call FlushStoredInteger(gcinv, I2S(H2I(u)), "Index") // Очищаем кеш
        return true
    endif
    return false
endfunction


Все должно быть понятно, объясню только что это за цикл - он обнуляет все ячейки массива ItemInUnit[] в которых содержатся предметы данного юнита, чтобы когда следующий юнит занял его место у него небыло такихже предметов.

Функция также возвращает true или false соответствено удалось или неудалось (например абилки этой небыло у юнита) удалить эту абилку.

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

cs: call UnitAddAbilityInventory(<Ваш юнит>)

можно чтобы сэкономить себе время на время теста, сделать таким образом:
Код:
Отряд - Pick every unit in (Units in (Playable map area) matching (((Matching unit) is Герой) Равно Да)) and do (Actions)
    Цикл - Действия
        Custom script:   call UnitAddAbilityInventory(GetEnumUnit())


Все, теперь мы можем при желании хранить предметы в этом массиве, теперь нужно написать обработчик события "юнит подбирает предмет"

|



Но перед тем как создать триггер, напишем саму функцию добавления предмета к юниту.

В таком виде:

function UnitAddItemInv takes unit u, item itm, boolean CustomInv returns boolean

u - собствено юнит которому даем

itm - предмет что даем

CustomInv - Булевая переменая true (Положить предмет в наш нестандартный инвентарь) / false (Положить предмет в стандартный инвентарь)

И сама функция возвращает булевую переменую true (Предмет взян) / false (предмет взять невозможно)

Код:
function UnitAddItemInv takes unit u, item itm, boolean CustomInv returns boolean
    local integer index
    if CustomInv then
        // Действия добовления предмета в нестандартный инвентарь
        set index = GetStoredInteger(gcinv, I2S(H2I(u)), "Index")*150 // Получаем место в котором у нас хранятся предметы этого юнита
        loop
            exitwhen index > index + AmountSlot // Перебираем AmountSlot мест под предметы, максимальное значение AmountSlot достигает 150, и зависит от того скольо слотов в инвентаре вы создадите.
            if ItemInUnit[index] == null then // Если текуще расматриваемая ячейка свободна, то:
                set ItemInUnit[index] = itm // Запомним предмет
                call SetItemPosition (itm, XINV, YINV) // Как я уже говорил раньше, предмет нужно кудато деть, чтобы он немешался но и одновременно - существоал
                call SetItemVisible  (itm, false     ) // И скрываем его чтобы никто не увидел в какую жопу мы засунули этот предмет
                return true // Ну и возвращаем тру т.к. мы предмет всетаки пложили в инвентарь
            endif
            set index = index + 1
        endloop
        return false // И возвращаем фолс если перебирая все ячейки мы не нашли сободного места чтобы положить наш предмет
    else
        // Действия добовления предмета в стандартный инвентарь, тут не очень много действий,
        // почти все делается одной строчкой, однако в последующих главах нам еще прийдется вернутся к этому месту
        return UnitAddItem(u, itm)
    endif
endfunction


В будущем мы эту функцию конешно еще будем доробатывать...

Теперь создадим триггер, тобиш вернемся к функции CreateInvTriggers()

Код:
+function PickUpItem_Filter takes nothing returns boolean
+   return GetUnitAbilityLevel(GetFilterUnit(), InvAbilCode) > 0
+endfunction

+function PickUpItem_Action takes nothing returns nothing
+   call UnitAddItemInv(GetTriggerUnit(), GetManipulatedItem(), true)
+endfunction

function CreateInvTriggers takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventInv(trig, EVENT_PLAYER_UNIT_SPELL_CAST, null    )
    call TriggerAddCondition           (trig,Condition(function InvStart_Conditions))
    call TriggerAddAction              (trig,          function InvStart_Actions    )
+   set trig = CreateTrigger()
+   call TriggerRegisterAnyUnitEventInv(trig,EVENT_PLAYER_UNIT_PICKUP_ITEM, Filter(function PickUpItem_Filter))
+   call TriggerAddAction              (trig, function PickUpItem_Action)
    set trig = CreateTrigger()
    call TriggerRegisterPlayerEventInv(trig, EVENT_PLAYER_END_CINEMATIC        )
    call TriggerAddCondition          (trig,Condition(function GetOpenInvPl   ))
    call TriggerAddAction             (trig,          function PressEsc_Action )
    set trig = null
    //       
    set TkHit = CreateTrigger()
    call TriggerAddCondition(TkHit  , Condition(function GetOpenInvTk))
    call TriggerAddAction   (TkHit  , function InvHitTrackableRucksack) 
    set TkTrack = CreateTrigger()
    call TriggerAddCondition(TkTrack, Condition(function GetOpenInvTk  ))
    call TriggerAddAction   (TkTrack, function InvTrackTrackableRucksack)
endfunction


Посмотрите, в этот раз мы не пользуемся conditionfunc как во всех других триггерах, а пользуемся filterfunc

Отличие между ними в том что в conditionfunc доступно больше функций для проверки увливия, например для данного случая это GetTriggerUnit(), GetManipulatedItem(), и т.д.
а в filterfunc для данного случая только одна GetFilterUnit(), что нам пока будет достаточно

Ну и естествено делать через filterfunc это оптимальнее.

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

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

|



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

Мы напишем функцию "Отображения" предмета в инвентаре

function CreateIconItemInv takes player p, integer position, item itm returns nothing

Функция принимает Игрока, в инвенторе которого будем отображать иконку, точнее она будет создаватся для всех инвентарей, но отображать мы ее будем только для игрока p

Еще функция принимает позию ячейки которую нужно отобразить, т.е. ее порядковый номер, мы его уже не раз использовали но просто напомню что это тот номер что дается ячейки через пораметр этой функции
function CteateSlotInventoryForForce takes real X, real Y, integer Index returns nothing

В функции CreateIconItemInv(p, position, itm)
Мы будем впервые использовать базу данных что делали в самом начале стайтьи.

Код:
function CreateIconItemInv takes player p, integer position, item itm returns nothing
    local string Texture = GetStoredString(gcinv, I2S(GetItemTypeId(itm)), "Texture") // По нашей БД узнаем текстуру что у предмета
    local integer TypeDest = GetStoredInteger(gcinv, "IconDest", Texture) // По этой текстуре находим декорацию с этой текстурой
    local destructable d = CreateDestructable (TypeDest, InvCellX[position], InvCellY[position], 0, 1, 0) // Создаем эту декорацию в позиции Слота position
    // напомню что позиция слота устанавливается в конце функкции CteateSlotInventoryForForce(X, Y, Index)
    call ShowDestructable (d, GetLocalPlayer() == p) // Показываем эту декорацию только для указаного игрока
    set d = null
endfunction


Теперь подумаем, вот у нас будут декорации-иконки, отображать предметы в инвентаре, но они же не могут вечно быть создаными в инвенторе, т.е. если мы открываем инвентарь - они создаются, если закрываем, они удалятся.

Чтобы удалять их после создания нам потребуется еще массив в котором они вcе будут хранится

Т.е. создадим еще один массив

Код:
globals
    gamecache gcinv = null
    string array InvStructure
    real XINV
    real YINV
    force ForceInv
    integer InvAbilCode
    boolean array RaceExists
    timer ViewCamera

    integer AmountSlot

    trigger TkHit
    trigger TkTrack

    boolean array UnitOccupied

    boolean array OpenInv
    unit array CurrentInvUnit
    
    real array InvCellX
    real array InvCellY
    
    item array ItemInUnit
    
+   destructable array InvIconDest
endglobals


Как видно из коментария декорации атачутся не к юнитам (как было в случае с глобалкой ItemInUnit), а к игрокам.

Код:
function CreateIconItemInv takes player p, integer position, item itm returns nothing
    local string Texture = GetStoredString(gcinv, I2S(GetItemTypeId(itm)), "Texture")
    local integer TypeDest = GetStoredInteger(gcinv, "IconDest", Texture)
    local destructable d = CreateDestructable (TypeDest, InvCellX[position], InvCellY[position], 0, 1, 0) 
    call ShowDestructable (d, GetLocalPlayer() == p)
+   set InvIconDest[GetPlayerId(p)*150 + position] = d // Запоминаем эту декорацию для этого игрока
    set d = null
endfunction

Имейте ввиду: При чтении и записи из кеша нельзя использовать ключи "" или null. если же таковые будут использованы (Если пораметр itm будет равен null) произойдет крах игры, и вас выкинет из нее с ошибкой. Т.е. перед тем как создать декорацию-иконку в указаной позиции, нужно проверить а существует ли предмет у юнита в этой позиции.

Ну и сделаем функцию-напарницу - для удаления:

Код:
function RemoveIconItemInv takes player p, integer position returns nothing
    local integer index = GetPlayerId(p)*150 + position
    if InvIconDest[index] != null then
        call RemoveDestructable (InvIconDest[index])
    endif
endfunction


|



Т.к. мы уже решили что все декорации иконки будут создаватся при открытии инвенторя и удалятся при закрытии то нам прийдется дописать функции
OpenInventory(unit)
CloseInventory(player)
Которые мы написали ранее.

в первой функции нам нужно отобазить все предметы, при помощи декораций-иконок, которые присутствуют у юнита.

а во второй удалить эти декорации-иконоки по выходу из инвенторя.


Открытие:

Код:
function OpenInventory takes unit u returns nothing
+   local player p = GetOwningPlayer(u)
+   local integer id = GetPlayerId(p)
+   local integer i = 0
    call IssueImmediateOrder(u, "stop")
    set CurrentInvUnit[id] = u
    set OpenInv[id] = true
    call ViewCamera_Action()
+   loop
+       exitwhen i > AmountSlot
+       if ItemInUnit[id*150+i] != null then // Проверяем существует ли у юнита предмет в данной расматриваемой позиции
+           call CreateIconItemInv(p, i, ItemInUnit[id*150+i])
+       endif
+       set i = i + 1
+   endloop
endfunction


т.е. перебираем все предметы которые принадлежат юниту и показываем их в нужной позиции


Закрытие:

Код:
function CloseInventory takes player p returns nothing
    local integer id = GetPlayerId(p)
+   local integer i = 0
    set OpenInv[id] = false
    if GetLocalPlayer() == p then
        call ResetToGameCamera(0)
        call PanCameraToTimed(GetUnitX(CurrentInvUnit[id]), GetUnitY(CurrentInvUnit[id]), 0)
    endif
    set CurrentInvUnit[id] = null
+   loop
+       exitwhen i > AmountSlot
+       call RemoveIconItemInv(p, id*150+i)
+       set i = i + 1
+   endloop
endfunction


Перебираем все предметы которые могут находится в инвентаре и удаляем их.


Вот и все, теперь можно нагладно посмотреть, взять парочку придметов и вы увидите что они у нас в инвентаре выстраиваются по порядочку...

В следующей главе мы сделаем так чтобы их можно было перемещать из слота в слот.

Седьмая часть - Перемещаем предмет из слота в слот



В этой главе оснавная функция с которой нам прийдется работать, это функция InvHitTrackableRucksack()

Она у нас есть, и срабатывает каждый раз когда мы нажимаем на трекабл, мы также можем узнать номер нажатого трекабла, а также и игрока который нажимал.

Самое главное что сдесь понадобится, это глобальная целочисленная переменая, которая обозначает какой из слотов у нас выделен, если же у нас никакой предмет не выделен то эта переименная будет ровнятся "-1"

Это глобальная переменая естественно будет массивом, от 0 до 11, т.к. у каждого игрока может быть свой выделеный слот.

"Выделенный" это значит вокруг этого слота будет идти такая мешура, похожая на автокаст, это отдельная декорация которая имеет модель ввиде эффекта, но у каждой расы своя цетом
люди - синия 'С115'
орки - красная 'С116'
нежить - фиолетовая 'С117'
ельфы - зеленая 'С118'

Чтобы динамически удалять и создавать эту декорацию нам и для нее прийдется создать глобальную переменную, также от 0 до 11

Код:
globals
    gamecache gcinv = null
    string array InvStructure
    real XINV
    real YINV
    force ForceInv
    integer InvAbilCode
    boolean array RaceExists
    timer ViewCamera

    integer AmountSlot

    trigger TkHit
    trigger TkTrack

    boolean array UnitOccupied

    boolean array OpenInv
    unit array CurrentInvUnit
+   integer array SelectCell
+   destructable array EffectSelect
    
    real array InvCellX
    real array InvCellY
    
    item array ItemInUnit
    
    destructable array InvIconDest
endglobals


Напишем функцию CreateEffectSelect(player p, real X, real Y)

Которая создается в позиции (X,Y) декорацию для игрока p, естествено тип декорации зависит от расы этого игрока, а также после ее нужно будет скрыть отовсех и показать только игроку p.

Код:
function RemoveEffectSelect takes player p returns nothing
    call RemoveDestructable (EffectSelect[GetPlayerId(p)])
endfunction

function CreateEffectSelect takes player p, real X, real Y returns nothing
    local integer id = GetPlayerId(p)
    local race r =  GetPlayerRace(p)
    local integer Type
    if EffectSelect[id] != null then
        call RemoveEffectSelect(p) // Если эта декорация уже существует - удалить
    endif
    if r == RACE_HUMAN then // Если человек
        set Type = 'C015'
    elseif r == RACE_ORC then // Если Орк
        set Type = 'C016'
    elseif r == RACE_UNDEAD then // Если нежить
        set Type = 'C017'
    elseif r == RACE_NIGHTELF then // Если ельф
        set Type = 'C018'
    else
        set Type = 0
    endif
    set EffectSelect[id] = CreateDestructable (Type, X, Y, 0, 1, 0)
    call ShowDestructable(EffectSelect[id], GetLocalPlayer() == p)
endfunction


Заметте что мы удаляем декорацию-эффект если он уже существует, т.е. не может у нас быть выбрано одновременно 2 и более слотов.

По умолчанию переменая выделеног слота ровна 0, что неправельно, она должна быть ровна -1, и должна быть такой всегда при открытии иненторя

самое разусеон это ручками выставлять ей такое значение в функции создания инвенторя

Код:
function CreateInventoryForForce takes force f, real invX, real invY, integer AbilCode returns nothing
    //...
    //...
    loop
        exitwhen i > 11
+       set SelectCell[ i] = -1
        set p = Player(i)
        if IsPlayerInForce(p, ForceInv) then
            set RaceExists[H2I(GetPlayerRace(p))-1] = true
        endif
        set i = i + 1
    endloop
    //...
    //...
endfunction

и в функции закрытия инвенторя

Код:
function CloseInventory takes player p returns nothing
    local integer id = GetPlayerId(p)
    local integer i = 0
    set OpenInv[id] = false
    if GetLocalPlayer() == p then
        call ResetToGameCamera(0)
        call PanCameraToTimed(GetUnitX(CurrentInvUnit[id]), GetUnitY(CurrentInvUnit[id]), 0)
    endif
    set CurrentInvUnit[id] = null
+   set SelectCell[id] = -1
    loop
        exitwhen i > AmountSlot
        call RemoveIconItemInv(p, id*150+i)
        set i = i + 1
    endloop
endfunction


Ну а теперь смотрите что напишем в функции InvHitTrackableRucksack()

Код:
function InvHitTrackableRucksack takes nothing returns nothing
    local trackable tk = GetTriggeringTrackable()
    local integer PId   = GetStoredInteger(gcinv, I2S(H2I(tk)), "IdPlayer") // от 0 до 11
    local integer Index = GetStoredInteger(gcinv, I2S(H2I(tk)), "Index"   ) // от 0 до AmountSlot
    local integer IndexUnit = GetStoredInteger(gcinv, I2S(H2I(CurrentInvUnit[PId])), "Index") // от 0 до 53
    if ItemInUnit[IndexUnit*150+Index] != null then // Если у юнита предмет в слоте на который мы кликнули - существует
        // Действия Если мы нажали на слот где находится предмет
        if SelectCell[PId] == Index then // Если номер выделеного слота совпадает с номером слота на который мы кликнули
            //Если этот выбранный предмет и есть тот самый выделеный
            //
            //Т.е. надо убрать выделение
        else
            //Если мы нажали на новый предмет
            //
            //Т.е. надо его выделеть
        endif
    else
        // Действия Если мы нажали на слот где нету предмета
        if SelectCell[PId] != -1 then // Если у нас есть выделеный слот
            //Действия если мы нажали на пустой слот, но при этом у нас есть выбранный предмет
            //
            //Т.е. сдесь мы возмем выбраный предмет и переместим на тот слот на который кликнули
            //Номер выделеного слота - SelectCell[PId], а номер того на который кликнули - Index
            
        else
            //Действия если мы нажали на пустой слот, и при этом у нас нету выбранного предмета
            //
            //Т.е. в данной областе функции мы ничего писать не будет
        endif
    endif
endfunction


вуаля...

Код:
function InvHitTrackableRucksack takes nothing returns nothing
    local trackable tk = GetTriggeringTrackable()
    local integer PId   = GetStoredInteger(gcinv, I2S(H2I(tk)), "IdPlayer")
    local integer Index = GetStoredInteger(gcinv, I2S(H2I(tk)), "Index"   )
    local integer IndexUnit = GetStoredInteger(gcinv, I2S(H2I(CurrentInvUnit[PId])), "Index")
    if ItemInUnit[IndexUnit*150+Index] != null then
        if SelectCell[PId] == Index then
+           call RemoveEffectSelect(Player(PId))
+           set SelectCell[PId] = -1
        else
+           call CreateEffectSelect (Player(PId), InvCellX[Index], InvCellY[Index])
+           set SelectCell[PId] = Index
        endif
    elseif SelectCell[PId] != -1 then
        //SelectCell[PId] -> Index
        //IndexUnit+SelectCell[PId] -> IndexUnit+Index
+       set ItemInUnit[IndexUnit*150+Index] = ItemInUnit[IndexUnit*150+SelectCell[PId]]
+       set ItemInUnit[IndexUnit*150+SelectCell[PId]] = null
+       call RemoveIconItemInv(Player(PId), SelectCell[PId])
+       call CreateIconItemInv(Player(PId), Index, ItemInUnit[IndexUnit*150+Index])
            
+       call RemoveEffectSelect(Player(PId))
+       set SelectCell[PId] = -1
    endif
endfunction
Прикрепленные файлы
Тип файла: w3x MyInventory v0.9 (end).w3x (227.4 Кбайт, 697 просмотров )
Тип файла: rar StandImports.rar (103.1 Кбайт, 196 просмотров )
Тип файла: rar StandObject.rar (4.4 Кбайт, 176 просмотров )
Тип файла: rar DataBase.rar (9.0 Кбайт, 180 просмотров )

Отредактировано Jon, 18.12.2008 в 13:29.
Старый 01.08.2008, 22:08
J
expert
offline
Опыт: 48,447
Активность:
далеко не полностью, ну четверть мб
ну незнаю, вроде для меня кажется простой вещью, а кратко написать немогу, что даже сам начал путатся, потому мне казалось что статья не удалась совсем
Старый 01.08.2008, 22:20
akkolt

offline
Опыт: 13,826
Активность:
Jon, прочитал только самое начало, дочитал до трекаблей. Но ведб их же нельзя удалять, как быть, если я хочу заюзать это в мультиплеере?

akkolt добавил:
Кстати, я раньше не понимал, как это осуществлено, а щас всё понятно =) Респект! Отличная статья!
Старый 01.08.2008, 22:29
J
expert
offline
Опыт: 48,447
Активность:
Цитата:
Jon, прочитал только самое начало, дочитал до трекаблей. Но ведб их же нельзя удалять, как быть, если я хочу заюзать это в мультиплеере?

все будет работать в мультеплеере, читай дальше
Цитата:
1) Будет ли меняться вид инвентаря, если ставить рассу самому?

если ты меняеш расу сам, вид панели упровления не меняется, потому корректнее былобы оставлять все как есть
Цитата:
2) А нельзя сделать так что бы предметы работали в полноэкранном инвентаре(чтобы их не перекладывать в основной)? если не трудно сделай так и выложи пожалуйста.

нельзя
Старый 01.08.2008, 22:41
akkolt

offline
Опыт: 13,826
Активность:
Jon, нет почему... можно же менять декорации в соответствии с расой игрока, тогда и интерфейс инвентаря будет меняться.
Старый 01.08.2008, 22:49
J
expert
offline
Опыт: 48,447
Активность:
просто еще добавлю что инвентарь не такой лагуий как у зибады или нетрата, т.к. все с оптимизироано как можно лучше
также в статье все это говорится о надо было в самый верх написать что в инвентаре используется много нестандартных идей
если все реализовать правельно, то несмотря на то что инвентарь будет распологатся на земле, то deadZone непонадобится, даже если инв будет прямо на глазах в центре карты
также очень гибкая настройки, и множество фукций обращение к инвентарю из вне

Jon добавил:
akkolt интерфейсо то инва бдет менятся, а интерфейс панели провлни нет, и какой смысл
Старый 01.08.2008, 22:55
akkolt

offline
Опыт: 13,826
Активность:
Jon, не совсем понял. Я хочу сказать, что можно менять интерфейс инвентаря, заменяя декорации. Т.к. из твоей статьи я узнал, что все рамки и украшения - это ни что иное, как эти самые декоры. Тоесть менять интерфейс инвентаря не составит особого труда. ИМХО.
Старый 01.08.2008, 22:59
J
expert
offline
Опыт: 48,447
Активность:
akkolt да млин, ты не втыкаеш, почему по твойму для каждой расы сделано разное оформление?
потомучто сама панель управления, варовская, стандартная, имеет разное оформление для разных рас, для инвентаря я только импортировал модели, все текстуры моделей стандартные, потому инвентарь занимает так мыло места в килобитиках
если ты поменяешь оформление инва, то оформление панели управления останется прежднее, и это будет некрасиво, и это вообще не нужно
Старый 01.08.2008, 23:02
akkolt

offline
Опыт: 13,826
Активность:
Jon, дыг я же сказал менять оформление инвентаря в соответствии с "РАСОЙ ИГРОКА".
Тоесть не интерфейс вара будет подстраиваться под инвентарь, а наоборот - ОФОРМЛЕНИЕ ИНВА БУДЕТ ПОДСТРАИВАТЬСЯ ПОД РАСУ ИГРОКА.

akkolt добавил:
Объясняю на примере:
if race of player 1 - orcs then (подстраиваем инвентарь под стиль орков) else (подстраиваем под например альянс)
Старый 01.08.2008, 23:09
J
expert
offline
Опыт: 48,447
Активность:
akkolt ну дыг, так так и есть.......
ты меня недооцениваеш?)

Jon добавил:
akkolt просто вопрос выше стоял можно ли изменить вид инва на протижении игры
повторю, можно добавить такую способность системе, но не нужно
Старый 01.08.2008, 23:10
akkolt

offline
Опыт: 13,826
Активность:
А воизбежание проблем в мультиплеере можно создать несколько таких областей для инвентарей со своими декорами, тоесть для 12 игроков нам нужно 12 областей.

akkolt добавил:
Цитата:
ты меня недооцениваеш?)

Вовсе нет... скорее даже наоборот
Старый 01.08.2008, 23:11
J
expert
offline
Опыт: 48,447
Активность:
Цитата:
А воизбежание проблем в мультиплеере можно создать несколько таких областей для инвентарей со своими декорами, тоесть для 12 игроков нам нужно 12 областей.

это так как реалиовано в инвентаре димонта или нетрата - ЭТО НЕПРАВЕЛЬНО
и вообще прочитай статью а потом предожения говори
там использует более совершеный метод

инвентарь будет для всех играков на одной и тойже территории, на одной и тойже, но если они все 12 играков одновремено запустят инв то никаких проблем е будет
Старый 01.08.2008, 23:12
J
expert
offline
Опыт: 48,447
Активность:
krimatoriy ну я значит не так понял, ну вообщем все уже сказали, все делается само, единственое что нужно сделать, это дать юнитам абилки с помощью соответствующих функций
а также при инициилизации карты вызывать одну единтвеную функцю, в которую передаеш группу играков для которых создаеш нвентарь (фильтруется только для играков), позицию левого верхнего угла инва, ну и абилку которой инв вызывается, все остальное делается автоматичски
Старый 01.08.2008, 23:15
akkolt

offline
Опыт: 13,826
Активность:
Цитата:
инвентарь будет для всех играков на одной и тойже территории, на одной и тойже, но если они все 12 играков одновремено запустят инв то никаких проблем е буде

Интересно... сейчас почитаю.
Цитата:
Я не имел в виду в игре я сказал чтобы ставить расу в меню выбора карты в WarCraft'е

Ну дыг можно. По сути можно менять и во время игры (инвентарь) но этого лучше не делать. Так как интерфейс альянса с инвентарём нежитей будет выглядеть не тру.
Старый 01.08.2008, 23:18
Модельщик
Во славу JC!
offline
Опыт: 2,686
Активность:
А не проще вместо трекаблей юзать юнитов?
Старый 02.08.2008, 14:39
J
expert
offline
Опыт: 48,447
Активность:
Модельщик ты сам осознаш насколько это тупо и неэффективно или мне подробно обьяснить?
Старый 02.08.2008, 14:44
Модельщик
Во славу JC!
offline
Опыт: 2,686
Активность:
Осазнаю, но это вариант, хоть и тупой))
Старый 02.08.2008, 14:45
J
expert
offline
Опыт: 48,447
Активность:
если бы там были юниты, ну вопервых самое первое что сразу бросается в глаза, ну никак нельзя определить наведение мыши на юнита, вот посмотри инвентарь что я приложил в вложения, и посмотри можно ли сделать так на юнитах, приличная часть функциональности инва лежит на этой способности
щелчок мыши сопровождается выделением юнита но даже если потом выделение сбрасывается происходит это не сразу и долю секунды видно что выделение пропало, это к списку косметических багов
я не проверял но вроде радиус выбора мышкой юнита происходит в радиусе от его центра, т.е. еси я нажму на уголок кнбки то кнопка не нажмется, в случае трекаблов можно сделать ровную нажимающуюся кнопку, также косметический баг

зачем мне описывать такие вариаенты в статье?
также проще создавать каждый инвентарь для отдельного игрока, тобы они могли параллельно работать, но это тоже фигня

можно вообще как некоторые - декорации (100x100) прям в редакторе ставить на землю и юнитов ставить, это вообще бред ламеров

Jon добавил:
К томуже способ что реализован в статье - _легче_, поему? он леге для того кто будет его юзать

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

и в том случае как в статье - чтобы сделать инвентарь - всего _одна строчка кода_ и весь инвентарь готов, всего одна строка добавления к базе донных нового предмета

статья не "инвентари делать легко, может любой дураг", а "делаем универсальный инвентарь"

ну и она еще к томуже и не закончена, но главные идеи и концепции подает
Старый 02.08.2008, 15:05
Модельщик
Во славу JC!
offline
Опыт: 2,686
Активность:
Я понел

Модельщик добавил:
У меня идея: в редакторе есть возможность наносить на экран эффекты - это можно заюзать - заменить эффекты на внешность инва, а всё остальное оставить
Старый 02.08.2008, 15:16
ScorpioT1000
Работаем
online
Опыт: отключен
Цитата:
call echo("Игрок " + I2S(PId) + " нажал на трекабл № "+ I2S(Index))

TT напиши это в JNGP

ScorpioT1000 добавил:
Модельщик, это все брет.

Jon статья мало кому пригодится ибо кто знает тот сам сделает) хотя 2-3 оптимальных идеи тут есть :j
Старый 02.08.2008, 15:27

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы можете скачивать файлы

BB-коды Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход



Часовой пояс GMT +3, время: 19:31.