• Уважаемые гости и новички, приветствуем Вас на нашем форуме
    Здесь вы можете найти ответы практически на все свои вопросы о серии игр «Готика» (в том числе различных модах на нее), «Ведьмак», «Ризен», «Древние свитки», «Эра дракона» и о многих других играх. Можете также узнать свежие новости о разработке новых проектов, восхититься творчеством наших форумчан, либо самим показать, что вы умеете. Ну и наконец, можете обсудить общие увлечения или просто весело пообщаться с посетителями «Таверны».

    Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
    Удачи!
  • Друзья, доброго времени суток!
    Стартовал новый литературный конкурс от "Ордена Хранителей" - "Пираты Миртанского моря".
    Каждый может принять в нём участие и снискать славу и уважение, а в случае занятия призового места ещё и получить награду. Дерзайте
  • Дорогие друзья, год подходит к концу, и пришло время подвести его итоги и наградить достойных

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

Вопросы по union

MW 7


Модостроитель
Регистрация
26 Мар 2004
Сообщения
2.011
Благодарности
981
Баллы
295
там отдельно dll собирается что бы получить эту информацию. но я попробовал использовать относительные пути и всё заработало.

***

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

MW 7


Модостроитель
Регистрация
26 Мар 2004
Сообщения
2.011
Благодарности
981
Баллы
295
Slavemaster а есть ли вызов какой то функции перед сменой мира?
 

Jr13San


Модостроитель
Регистрация
1 Апр 2010
Сообщения
462
Благодарности
285
Баллы
230
Я хочу попробовать добавить серебро в экономику, возникшие проблемы: 1) жёсткая привязка обменной валюты к C_ITEM, не могу понять какую функцию хукать. 2) нужно добавить в инвентарь строку кол-ва серебра, проблема заключается в том, что отрисовка текста надписи при рендере происходит раньше наложения рамки и в итоге текст выходит тёмный. 3) не понимаю какие функции соотносятся к отображению инвентаря игрока, а какие торговые и контейнера. P.S. для меня инвентарь это какая-то дремучая штуковина и если есть какие-нибудь исходники с его работой, или более полная инфа по работе с ним, было бы просто шикарно.

Получилось ли что-то сделать по этой теме? Если нет, то:
Есть ли концепция/наброски твоей задумки? Интересует описание и желательно со схемами или картинками.
Если не хочешь афишировать наброски, можешь написать в личку.

Ну, например, такие идеи по разработке:
1) Нужна торговля за разные типы монет: золото, серебро, стар. монеты.

2) Нужно как-то запомнить, какие предметы за какие монеты продаются.
И поскольку в классе 'C_ITEM' уже нет свободных полей, то нужно создать массив для хранения структур данных, внутри плагина.
Допустим нам нужна функция регистрации, типа:
Daedalus:
// какой предмет, за какую валюту продаётся
RegTradeItemInfo(ItRw_Arrow,    ITMI_GOLD);

Но чтобы не описывать всю кучу предметов, сократим работу, введя новую функцию назначения основной валюты, например:
Daedalus:
// устанавливаем серебро в качестве основной валюты 
SetMainCoin(ITMI_SILVER);
Получается, что нам нужно будет обозначить лишь предметы, торгуемые за золото и за старинные монеты.
Если предмета нет в нашей базе данных о предметах, значит он торгуется за основную валюту, т.е. за серебро.

3) Торговля активируется через диалог, например:
ГГ: Покажи мне свои товары.
НПС: А что конкретно тебя интересует?

Выбор 1: Редкие вещи(за золото - 5 шт)
Выбор 2: Обычные вещи(за серебро - 23 шт)

Выбор 1 -> Покажи редкие вещи. -> Выбирай.
Выбор 2 -> Мне нужны обычные вещи. -> Выбирай.
В обоих случаях открывается режим торговли, но с разными типами торговых валют.
Например, при выборе 1, так:
Daedalus:
SetCurrentTradeCoin(ITMI_GOLD);
А при выборе 2, так:
Daedalus:
SetCurrentTradeCoin(ITMI_SILVER);
SetCurrentTradeCoin - это новая функция назначения текущей валюты для торговли.
Также перед началом торговли нужно временно перенести в некую базу данных, лишь те предметы, что не подходят под выбранную валюту (причём у обоих НПС).
Ну, т.е. если выбрали 'Редкие вещи', то у торговца и у ГГ тоже, остаются только те предметы, что торгуются за золото, всё остальное - отправляется на склад хранения.
Затем после окончания торговли, нужно будет вернуть предметы обратно.
Таким образом мы получаем сортированную торговлю.

4) Если же торговля осуществляется без предварительной сортировки, в так называемом 'киш-миш' режиме.
Это когда все предметы торговца и ГГ имеют разную ценность в разных типах валют.
Тогда придётся выводить информацию о кошельке ГГ, например так:
TestTitle3.jpg

И постоянно подменять текущую валюту.
Как видим, на низком разрешении информация о монетах смотрится "не в тему". Две ещё как-то можно уместить, но три уже лишку.
Или же нужно вместо заголовка контейнера ГГ, написать например: "Мой кошелёк", и дать возможность наводить на него фокус, и в этом случае в окне информации о предмете, выводить именно информацию о денежных запасах ГГ:
TradeMulti.jpg

Но это может быть неудобным решением.

5) Также неизвестно, как лучше обозначить тип монет и цену предмета (через иконки или текстом).
Нужно понимать, что иконка золотой монеты и старой - похожи. А размером в 10х10 пикселей, они сливаются почти в одну цветную точку.
Мелкая серебряная также может напрягать глаза.

6) Также нужно учесть то, что будет отображаться в заголовке контейнера ГГ в свободном режиме, т.е. вне режима торговли.

7) Ещё нужна будет функция регистрации названия монет, при выводе их заголовке контейнера, что-то типа:
Daedalus:
RegInvTitleItem("Золото: ", ITMI_GOLD);
RegInvTitleItem("Серебро: ", ITMI_SILVER);
RegInvTitleItem("Стар. монеты: ", ITMI_OLDCOIN);

И так далее...
Т.е. нужен полный обзор работы системы.
 

D36


Модостроитель
Регистрация
3 Дек 2014
Сообщения
2.222
Благодарности
3.449
Баллы
485
Если подразумевается простая замена золота на серебро, то это делается несколькими строчками в файле .d в папке system/autorun (или в скриптах).
Пример замены золота на руду:

Daedalus:
//замена предмета-валюты с ITMI_GOLD
const string TRADE_CURRENCY_INSTANCE = "ITMI_NUGGET";

//замена текста (для кириллицы файл сохранять в кодировке ANSI)
const string NAME_Currency = "Руда: ";
const string PRINT_Trade_Not_Enough_Gold = "У вас недостаточно руды для покупки этого предмета.";

Untitled.jpg
 
Последнее редактирование:

Maksh

Участник форума
Регистрация
11 Дек 2024
Сообщения
5
Благодарности
0
Баллы
10
Добрый день! Подскажите, пожалуйста, есть ли возможность добавлять свой плагин поверх установленного Нового баланса (на стим версии готики)?
С чистой готикой плагины работают, с новым балансом - нет. Для совместимости можно использовать zParserExtender?
У меня есть цель создать мод с квестами по отрисовке картин (с созданием скриншота в игре и дальнейшим появлением его на холсте). Важно, чтобы этот мод работал вместе с установленным новым балансом.
Если кто-то сможет пнуть в нужном направлении, буду очень благодарна.
 
Последнее редактирование:

Jr13San


Модостроитель
Регистрация
1 Апр 2010
Сообщения
462
Благодарности
285
Баллы
230
Для совместимости можно использовать zParserExtender?
Ну, он скорей не для совместимости, а для внедрения/изменения скриптов. Про его возможности можешь почитать в соотв. теме:
https://worldofplayers.ru/threads/41999/#post-1086550
А так, просто пишешь отдельный d-скрипт с НПС, его диалогами и прочей логикой вставки на локацию. Затем сохраняешь этот скрипт в папке "Gothic2\System\autorun\". Потом, после успешных испытаний можешь его упаковать в отдельный мод-том и распространять как угодно.

У меня есть цель создать мод с квестами по отрисовке картин (с созданием скриншота в игре и дальнейшим появлением его на холсте). Важно, чтобы этот мод работал вместе с установленным новым балансом.
По плагину скажу так. На стандартном рендере DX7 возможно сделать скриншот экрана, преобразовать его в текстуру, изменить пиксели, альфа-канал и прочее. Затем естественно сохранить в папку с текстурами. Принцип работы такой же, как во время сохранения превью картинки для файла сохранения.
Ну и в последующем уже - можно загружать эту текстуру и накладывать на любые поверхности. Или просто поиском найти материал, той же самой картины, и заменить его текстуру на текстуру скриншота. Но этот вопрос скорей для тех, кто очень знаком с движком и его функционалом.
MakeScreen2.png
MakeScreen3.png
MakeScreen1.jpg
MakeScreen4.jpg

С включённым враппером DX11 (для Готики) будут проблемы при создании скриншотов. Максимально возможный скриншот 256х256.
Судя по откликам, проблема актуальна и по сей день:
https://github.com/kirides/GD3D11/issues/125
 
Последнее редактирование:

ang1

Участник форума
Регистрация
6 Июн 2010
Сообщения
608
Благодарности
61
Баллы
200
Добрый день! Подскажите, пожалуйста, есть ли возможность добавлять свой плагин поверх установленного Нового баланса (на стим версии готики)?
С чистой готикой плагины работают, с новым балансом - нет. Для совместимости можно использовать zParserExtender?
У меня есть цель создать мод с квестами по отрисовке картин (с созданием скриншота в игре и дальнейшим появлением его на холсте). Важно, чтобы этот мод работал вместе с установленным новым балансом.
Если кто-то сможет пнуть в нужном направлении, буду очень благодарна.
Ты имеешь в виду блокировка в НБ?
 

Maksh

Участник форума
Регистрация
11 Дек 2024
Сообщения
5
Благодарности
0
Баллы
10
Спасибо за ответ!
А так, просто пишешь отдельный d-скрипт с НПС, его диалогами и прочей логикой вставки на локацию
Да, про это знаю, тут проблем нет.
Я правильно понимаю, что если восстановлю совместимость моей игры (с установленным новым балансом) с DX7, то возникнут проблемы с производительностью, всякие баги и прочие неприятные вещи?
Скрипты НБ зашифрованы, как я понимаю. Могу ли я внедрить свой плагин в игру с установленным новым балансом при помощи zParserExtender? Или это можно сделать каким-то другим способом?
Пост автоматически объединён:

Ты имеешь в виду блокировка в НБ?
Да, она не позволяет привычным способом внедрять в игру плагины.
 

ang1

Участник форума
Регистрация
6 Июн 2010
Сообщения
608
Благодарности
61
Баллы
200
Спасибо за ответ!

Да, про это знаю, тут проблем нет.
Я правильно понимаю, что если восстановлю совместимость моей игры (с установленным новым балансом) с DX7, то возникнут проблемы с производительностью, всякие баги и прочие неприятные вещи?
Скрипты НБ зашифрованы, как я понимаю. Могу ли я внедрить свой плагин в игру с установленным новым балансом при помощи zParserExtender? Или это можно сделать каким-то другим способом?
Пост автоматически объединён:


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

DAMROCK

Участник форума
Регистрация
23 Янв 2017
Сообщения
24
Благодарности
6
Баллы
160
Получилось ли что-то сделать по этой теме? Если нет, то:
Есть ли концепция/наброски твоей задумки? Интересует описание и желательно со схемами или картинками.
Если не хочешь афишировать наброски, можешь написать в личку.

Ну, например, такие идеи по разработке:
1) Нужна торговля за разные типы монет: золото, серебро, стар. монеты.

2) Нужно как-то запомнить, какие предметы за какие монеты продаются.
И поскольку в классе 'C_ITEM' уже нет свободных полей, то нужно создать массив для хранения структур данных, внутри плагина.
Допустим нам нужна функция регистрации, типа:
Daedalus:
// какой предмет, за какую валюту продаётся
RegTradeItemInfo(ItRw_Arrow,    ITMI_GOLD);

Но чтобы не описывать всю кучу предметов, сократим работу, введя новую функцию назначения основной валюты, например:
Daedalus:
// устанавливаем серебро в качестве основной валюты
SetMainCoin(ITMI_SILVER);
Получается, что нам нужно будет обозначить лишь предметы, торгуемые за золото и за старинные монеты.
Если предмета нет в нашей базе данных о предметах, значит он торгуется за основную валюту, т.е. за серебро.

3) Торговля активируется через диалог, например:
ГГ: Покажи мне свои товары.
НПС: А что конкретно тебя интересует?

Выбор 1: Редкие вещи(за золото - 5 шт)
Выбор 2: Обычные вещи(за серебро - 23 шт)

Выбор 1 -> Покажи редкие вещи. -> Выбирай.
Выбор 2 -> Мне нужны обычные вещи. -> Выбирай.
В обоих случаях открывается режим торговли, но с разными типами торговых валют.
Например, при выборе 1, так:
Daedalus:
SetCurrentTradeCoin(ITMI_GOLD);
А при выборе 2, так:
Daedalus:
SetCurrentTradeCoin(ITMI_SILVER);
SetCurrentTradeCoin - это новая функция назначения текущей валюты для торговли.
Также перед началом торговли нужно временно перенести в некую базу данных, лишь те предметы, что не подходят под выбранную валюту (причём у обоих НПС).
Ну, т.е. если выбрали 'Редкие вещи', то у торговца и у ГГ тоже, остаются только те предметы, что торгуются за золото, всё остальное - отправляется на склад хранения.
Затем после окончания торговли, нужно будет вернуть предметы обратно.
Таким образом мы получаем сортированную торговлю.

4) Если же торговля осуществляется без предварительной сортировки, в так называемом 'киш-миш' режиме.
Это когда все предметы торговца и ГГ имеют разную ценность в разных типах валют.
Тогда придётся выводить информацию о кошельке ГГ, например так:
Посмотреть вложение 127741
И постоянно подменять текущую валюту.
Как видим, на низком разрешении информация о монетах смотрится "не в тему". Две ещё как-то можно уместить, но три уже лишку.
Или же нужно вместо заголовка контейнера ГГ, написать например: "Мой кошелёк", и дать возможность наводить на него фокус, и в этом случае в окне информации о предмете, выводить именно информацию о денежных запасах ГГ:
Посмотреть вложение 127761
Но это может быть неудобным решением.

5) Также неизвестно, как лучше обозначить тип монет и цену предмета (через иконки или текстом).
Нужно понимать, что иконка золотой монеты и старой - похожи. А размером в 10х10 пикселей, они сливаются почти в одну цветную точку.
Мелкая серебряная также может напрягать глаза.

6) Также нужно учесть то, что будет отображаться в заголовке контейнера ГГ в свободном режиме, т.е. вне режима торговли.

7) Ещё нужна будет функция регистрации названия монет, при выводе их заголовке контейнера, что-то типа:
Daedalus:
RegInvTitleItem("Золото: ", ITMI_GOLD);
RegInvTitleItem("Серебро: ", ITMI_SILVER);
RegInvTitleItem("Стар. монеты: ", ITMI_OLDCOIN);

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

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


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


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


В целом, я разобрался как всё это вывести на экран, но есть некоторые нюансы с отрисовкой предметов в инвентаре, например подсчёт выделяемой памяти.
 

Jr13San


Модостроитель
Регистрация
1 Апр 2010
Сообщения
462
Благодарности
285
Баллы
230
Вроде помогает плагин от поляков, называется zBlockerFucker качать на миртане
Можно ещё прикинуться одним из разрешённых плагинов, например, "zTest.dll". Хотя их там целый список...
И когда новый плагин получит разрешение на загрузку, тогда он уже сможет загружать и остальные целевые плагины из списка, через функцию LoadLibrary().
Как вариант.

Благодарю за отличный пример! я задумывался над реализацией системы, но ещё не так глубоко, по части экономики)
Да не за что... Просто у меня были некоторые мысли на этот счёт и немного наработок, решил поделиться.
Мой выбор пал на сортированный режим торговли. Первые тесты прошли успешно. Последней проблемой было найти наиболее удобную систему отображения монет.
В принципе, мысленно, я пришёл к нескольким техническим решениям:
C++:
// примеры режимов отображения монет в заголовке контейнера:
enum eShowCoinsInTitleState
{
    SHOW_ONLY_MAIN_CURRENCY,  // показывать только основную валюту
    SHOW_ONLY_MANY_CURRENCY,  // показывать только те монеты, которых больше всех
    SHOW_EVERY_TIMED,         // показывать каждую зарегистрированную, по очереди, временно (на 2 сек)
    SHOW_TABLE_CURRENCY,      // показывать таблицу монет, с названиями и иконками (до 4х максимум)
    SHOW_CUSTOM               // кастомный выбор (ручное управление по флагам)
};
Первые 3 режима - почти тоже самое, что и оригинал, только с небольшими изменениями. Например, можно ещё добавить иконку типа монет.
Есть набросок режима "SHOW_TABLE_CURRENCY" (это рисунок):
TradeMulti_ListCoins.jpg

Последний режим - это чисто ручное управление содержимым заголовка. Например, можно вывести только один/два определённых вида монет (в строчку или в таблицу). Этот режим можно вообще не использовать.

Нужно сначала сделать всё в общем виде, а потом уже управлять всеми этими настройками по желанию:
1) в скриптах, через функции
2) или в меню, через специальный блок настроек плагина.


Иконку монеты в описании предмета можно использовать, а можно - не использовать.
Как вариант, использовать текст, как в Г3: "Цена в золоте/серебре/старинных монетах" и т.д. С текстом было бы проще, не нужно ничего выравнивать или подгонять. Да и изначальный стиль не сильно пострадает.
В общем, если сильно не заморачиваться и не углубляться в перфекционизм, опустив некоторые решения, то вполне можно сделать примитивную торговлю в нескольких денежных единицах. Это реально может работать.

Так я начал работать над инвентарём с нуля, однако, пока что не могу понять как выключить старый совсем.
Как вариант, попробуй перехватить нажатие логической клавиши, отвечающей за откр/закр. инвентаря:
C++:
//0x006FC170 private: virtual int __thiscall oCGame::HandleEvent(int)
static int __fastcall Game_HandleEvent(oCGame* _this, void* vt, int key);
static CInvoke <int(__thiscall*)(oCGame*, int)> pGame_HandleEvent(0x006FC170, Game_HandleEvent, IVK_AUTO);
static int __fastcall Game_HandleEvent(oCGame* _this, void* vt, int key)
{
    if (!player)
        return FALSE;

    if (_this && _this->world && _this->world->csPlayer && _this->world->csPlayer->GetPlayingGlobalCutscene())
        return FALSE;
 
    zWORD LogicalKey = zinput->GetFirstBindedLogicalKey(key);

    if (LogicalKey == GAME_INVENTORY)
    {
        cmd << "Произошла попытка открыть инвентарь." << endl;
        return TRUE;
    }
    // исправил, добавив return
    return pGame_HandleEvent(_this, key);
}

я склоняюсь к тому, чтобы сделать гибридную систему как в Готике 3.
А как там было?

Основной валютой сделать серебро, так наличие золота может стать, своего рода, требованием финансового уровня, для более серьёзного крафта и обучения, учитывая моё желание- попробовать сделать динамическую экономику.
Можно сделать ещё такой обмен. Золотую можно обменять, например, условно, на 100 серебряных монет. Т.е. от золота ГГ избавят легко. А вот наоборот поменять серебро на золото - это проблема. Никто не хочет этого делать, либо цена обмена: 1 зол. за 1000 серебра. Правда, нужно взвесить за и против. Потому что может быть, что один из обменов не будет иметь смысла.

но есть некоторые нюансы с отрисовкой предметов в инвентаре, например подсчёт выделяемой памяти.
что ты имеешь в виду под "подсчётом выделяемой памяти"?
 
Последнее редактирование:

DAMROCK

Участник форума
Регистрация
23 Янв 2017
Сообщения
24
Благодарности
6
Баллы
160
Как вариант, попробуй перехватить нажатие логической клавиши
Вот спасибо, я в этих функциях движка самостоятельно не могу ориентироваться без исходного кода, что в какой момент вызывается -остаётся только гадать.
А как там было?
Система торговли по типу бартера+ усреднение золотом, мне очень нравится такой подход. Но думаю, что в игре не все торговцы могут захотеть меняться или покупать что-либо, или надо заслужить это право перед торговцем, ведь он может предложить более интересные расценки по ходу раскрутки торговца.
Можно сделать ещё такой обмен. Золотую можно обменять, например, условно, на 100 серебряных монет. Т.е. от золота ГГ избавят легко. А вот наоборот поменять серебро на золото - это проблема. Никто не хочет этого делать, либо цена обмена: 1 зол. за 1000 серебра. Правда, нужно взвесить за и против. Потому что может быть, что один из обменов не будет иметь смысла.
Кстати говоря я размышлял над этим, надумал над тем чтобы ввести банк или что-то вроде "теневого" банка. Над этим надо хорошенько подумать, увы я не так силён в экономике *around the head*

что ты имеешь в виду под "подсчётом выделяемой памяти"?
Ну при отрисовке предмета в инвентаре используется zCWorld, если предмет один, то вопросов нет, просто выделяем память и освобождаем при выходе. Но предметов там может быть до~ 80шт. Когда я пробовал рисовать все предметы через один объект zCWorld, игра вылетает. Чистить память в цикле до закрытия инвентаря- вылет, массив zCWorld двиг вообще переваривать отказывается. Если каждый раз выделять память, но не освобождать, вроде работает, но это вызывает вопросы что происходит с этой памятью. Прочитал где-то здесь, на форуме, что можно считать выделенную память, пока-что не пробовал и не совсем понимаю как именно это делать.
Последней проблемой было найти наиболее удобную систему отображения монет.
Я конечно не думал над тем, чтобы делать более 2х валют, но первым вариантом отображения, именно хотел сделать как на рисунке (TradeMulti_ListCoins.jpg) Но там столкнулся с неуправляемым масштабированием, то бишь, новая текстура заголовка и его текст при изменении разрешения, постоянно норовят уйти в сторону, это был ещё один плюс к тому, чтобы делать с нуля.

 
Последнее редактирование:

Jr13San


Модостроитель
Регистрация
1 Апр 2010
Сообщения
462
Благодарности
285
Баллы
230
я в этих функциях движка самостоятельно не могу ориентироваться без исходного кода, что в какой момент вызывается -остаётся только гадать.
Понимаю тебя. Там если что, не забудь исправить код (из предыдущего ответа), добавив в конец блока функции return, потому что эта функция обязательно должна возвращать значение, сорян, я только сейчас беглым взглядом это заметил.

Система торговли по типу бартера+ усреднение золотом, мне очень нравится такой подход. Но думаю, что в игре не все торговцы могут захотеть меняться или покупать что-либо, или надо заслужить это право перед торговцем, ведь он может предложить более интересные расценки по ходу раскрутки торговца.
А, ну примерно как в Г1. Думаю, что здесь как раз подойдёт сортированная торговля. Т.е. оружейник принимает только металлолом, алхимик - различные травы/зелья, повар - некоторые ингредиенты (пищу), столяр - принимает луки/арбалеты, торговец маг. артефактами - принимает свитки, руны. Ну и остальной относительный хлам - принимают все вышеперечисленные торговцы, но всего по не многу.

Кстати говоря я размышлял над этим, надумал над тем чтобы ввести банк или что-то вроде "теневого" банка. Над этим надо хорошенько подумать, увы я не так силён в экономике
Ну да, нужно вводить какой-то пункт обмена.
Ещё можно сделать обмен с некоторыми НПС - вне пункта, но по более выгодным ценам. А в какой-то момент и менее выгодным, чтобы попытаться обмануть игрока.
И возможно даже делать некоторые предложения временными. Грубо говоря, день-два и строчка выбора обмена пропадает или строчка остаётся, но НПС говорит, что валюта уже ушла.

Ну при отрисовке предмета в инвентаре используется zCWorld, если предмет один, то вопросов нет, просто выделяем память и освобождаем при выходе. Но предметов там может быть до~ 80шт. Когда я пробовал рисовать все предметы через один объект zCWorld, игра вылетает. Чистить память в цикле до закрытия инвентаря- вылет, массив zCWorld двиг вообще переваривать отказывается. Если каждый раз выделять память, но не освобождать, вроде работает, но это вызывает вопросы что происходит с этой памятью. Прочитал где-то здесь, на форуме, что можно считать выделенную память, пока-что не пробовал и не совсем понимаю как именно это делать.
Понятно, в общем тебе нужен какой-то пример, чтобы разобраться. Там, вроде бы, с обычными предметами ничего сложного, помню лишь, что были проблемы с рендером факелов, т.к. они состоят не только из меша, но ещё и из источника света и партикль частиц.
Вот пример (тест рендера ячеек с предметами):
C++:
// текстура заднего плана ячейки
const zSTRING InvSlot_Back = "InvSlot_Back.tga";

// текстура рамки ячейки
const zSTRING InvSlot_Border = "InvSlot_Border.tga";

// шрифт для вывода текста в ячейках
const zSTRING Font_Old_10_White = "Font_Old_10_White.tga";

// Функция рендера предметов НПС, где
// pNpc - указатель на НПС, чей инвентарь будет отрисован,
// cell_psize - размер стороны квадратной ячейки (в пикселях).
void RenderInvItems(oCNpc* pNpc, int cell_psize)
{
    // если указателя на НПС нет
    if (!pNpc)
        // выходим
        return;

    // Иначе, преобразуем размеры ячеек из пикселей в виртуальные координаты
    int view_VSizeX = zPixelX(cell_psize);    // ширина ячейки
    int view_VSizeY = zPixelY(cell_psize);    // высота ячейки


    /**********************************/
    //  Подготовка слоёв для рисования
    /**********************************/
    // создаём слои для рисования (по размеру ячейки):
    // (рендер предмета)
    zCView* pViewItem = new zCView(0, 0, view_VSizeX, view_VSizeY);

    // если слой не создан
    if (!pViewItem)
        // выходим
        return;

    // (задний план ячейки)
    zCView* pViewBack = new zCView(0, 0, view_VSizeX, view_VSizeY);
  
    // если слой не создан
    if (!pViewBack)
    {
        // удаляем слой для рендера
        delete pViewItem;

        // и выходим
        return;
    }

    // (счётчик предметов и номер горячей клавиши)
    zCView* pViewNumItems = new zCView(0, 0, view_VSizeX, view_VSizeY);
  
    // если слой не создан
    if (!pViewNumItems)
    {
        // удаляем два предыдущих слоя
        delete pViewItem;
        delete pViewBack;

        // и выходим
        return;
    }

    // ---

    // создаём новый мир для редера предметов
    zCWorld* pWorld = new zCWorld();

    // если мир не создан
    if (!pWorld)
    {
        // удаляем все слои для рисования
        delete pViewItem;
        delete pViewBack;
        delete pViewNumItems;

        // и выходим
        return;
    }

    // Иначе, почва для рендера предметов успешно создана.
    // Выполняем настройки слоёв.

    // (Задний план)
    // устанавливаем текстуру слоя
    pViewBack->InsertBack(InvSlot_Back);

    // включаем поддержку прозрачности
    pViewBack->alphafunc = zRND_ALPHA_FUNC_BLEND;

    // уменьшаем прозрачность текстуры (255->200)
    pViewBack->alpha = 200;

    // ---

    // (Счётчик предметов и номер горячей клавиши)
    // устанавливаем текстуру слоя
    pViewNumItems->InsertBack(InvSlot_Border);
  
    // включаем поддержку прозрачности
    pViewNumItems->alphafunc = zRND_ALPHA_FUNC_BLEND;

    // корректируем прозрачность слоя
    pViewNumItems->alpha = 100;

    // устанавливаем шрифт
    pViewNumItems->SetFont(Font_Old_10_White);

    // цвет шрифта
    pViewNumItems->SetFontColor(zCOLOR(255, 255, 255));
  
    // цвет слоя (или цвет рамки ячейки)
    pViewNumItems->color = zCOLOR(100, 100, 100);

    // ---

    // вставляем слои во вьюпорт
    screen->InsertItem(pViewBack);
    screen->InsertItem(pViewItem);
    screen->InsertItem(pViewNumItems);

    // ставим флаг, что этот мир предназначен для рендера предметов
    pWorld->m_bIsInventoryWorld = TRUE;
  
    // если у НПС есть вещи, упакованные в текстовую строку (их нет в инвентаре)
    if (!pNpc->inventory2.packString.IsEmpty())
        // распаковываем вещи и вставляем в инвентарь НПС
        pNpc->inventory2.UnpackAllItems();
  
    // временная позиция слоя в вирт. координатах (используется в цикле, см. ниже)
    int vposx, vposy;

    // временный текст
    zSTRING txt = "";

    // включаем подсветку во время рендера предметов
    oCItem::SetLightingSwell(TRUE);

    // пробегаемся по списку предметов в инвентаре НПС
    zCListSort<oCItem>* node = pNpc->inventory2.contents->GetNextInList();
    while (node)
    {
        // берём указатель на предмет
        oCItem* item = node->GetData();

        // заранее переходим к следующему "узлу с предметом"
        node = node->GetNextInList();

        // если указателя нет (маловероятно)
        if (!item)
            // переходим к следующему узлу
            continue;

        // ---

        // иначе, проверяем, если предмет имеет активный флаг
        if (item->HasFlag(ITM_FLAG_ACTIVE))
            // окрашиваем задний план ячейки в красный цвет
            pViewBack->color = zCOLOR(255, 0, 0);
        else // иначе, это обычная ячейка
             // устанавливаем чёрный цвет ячейки
            pViewBack->color = zCOLOR(0, 0, 0);

        // сначала рендерим задний план ячейки
        pViewBack->Blit();

        // ---

        // рендерим сам предмет в место расположения слоя "pViewItem"
        item->RenderItem(pWorld, pViewItem, 0);

        // ---
      
        // если нужно указывать кол-во предметов
        if (item->amount > 1)
        {
            // преобразуем число предметов в текстовый вид
            txt = Z item->amount;

            // вычисляем координаты для вывода текста (в вирт. координатах)
            // (в правом нижнем углу, с отступом в 10 пикселей по сторонам)
            int textVPosX = 8192 - pViewNumItems->FontSize(txt) - pViewNumItems->anx(10);
            int textVPosY = 8192 - pViewNumItems->FontY() - pViewNumItems->any(10);

            // очищаем слой от текста
            pViewNumItems->ClrPrintwin();

            // устанавливаем цвет текста
            pViewNumItems->SetFontColor(zCOLOR(255, 255, 255));

            // выводим текст на слой
            pViewNumItems->Print(textVPosX, textVPosY, txt);

            // рендерим слой на экране
            // (и текст и рамку)
            pViewNumItems->Blit();
        }
        else // если предмет всего один
        {
            // очищаем слой от текста
            pViewNumItems->ClrPrintwin();

            // рендерим слой на экране
            // иначе, не будет рамки ячейки
            pViewNumItems->Blit();
        }

        // ---


        // горячая клавиша для оружия (1/2)
        // если предмет экипирован
        if (item->HasFlag(ITM_FLAG_ACTIVE))
        {
            txt = "";

            // если это оружие ближнего радиуса поражения
            if (item->mainflag == ITM_CAT_NF)
                txt = "1";
            // если это оружие дальнего радиуса поражения
            else if (item->mainflag == ITM_CAT_FF)
                txt = "2";
            // если это руна или свиток
            else if (item->mainflag == ITM_CAT_RUNE)
            {
                // если НПС имеет книгу с заклинаниями
                if(pNpc->HasMagic())
                {
                    // получаем индекс горячей клавиши
                    int key = pNpc->GetSpellBook()->GetKeyByItem(item);

                    // если индекс подходящий
                    if (key < 10)
                        // выполянем смещение номера кнопки на 3
                        txt = Z (key + 3);
                }
            }

            // если есть какой-то номер кнопки, для вывода
            if (!txt.IsEmpty())
            {
                // вычисляем координаты для текста
                // (с правого краю, по середине ячейки)
                int textVPosX = 8192 - pViewNumItems->FontSize(txt) - pViewNumItems->anx(10);
                int textVPosY = 4096 - pViewNumItems->FontY() / 2;

                // устанавливаем красный цвет шрифта
                pViewNumItems->SetFontColor(zCOLOR(255, 0, 0));

                // печатаем текст на слое
                pViewNumItems->Print(textVPosX, textVPosY, txt);

                // рендерим слой на экране
                pViewNumItems->Blit();
            }
        } /* end of hotkey text */


        // получаем текущие координаты позиции слоя ячейки (в вирт. координатах)
        pViewItem->GetPos(vposx, vposy);

        // вычисляем кординаты для следующей ячейки:
        // перемещаемся на следующую ячейку (по оси Х)
        vposx += view_VSizeX;

        // и смещаемся на 1 пиксель назад (по оси Х)
        vposx -= zPixelX(1);

        // если координаты следующей ячейки выходят за пределы вьюпорта (по оси Х)
        if ((vposx + view_VSizeX) > 8192)
        {
            // обнуляем координату ячейки (по оси Х)
            vposx = 0;

            // переходим на следующую строку (по оси Y)
            vposy += view_VSizeY;

            // смещаемся на 1 пиксель вверх (по оси Y)
            vposy -= zPixelY(1);
        }

        // если координаты следующей ячейки выходят за пределы вьюпорта (по оси Y)
        if (vposy > 8192)
        {
            // выводим отладочное сообщение
            cmd << "Не хватает места на экране -> выходим.";

            // удаляем все используемые слои
            delete pViewItem;
            delete pViewBack;
            delete pViewNumItems;

            // освобождаем одну ссылку на исползуемый мир
            // (т.о. избавляемся от этого объекта)
            pWorld->Release();

            // и выходим
            return;
        }

        // если рендер предметов продолжается, то
        // устанавливаем новую позицию для следующей ячейки
        // (на всех слоях)
        pViewItem->SetPos(vposx, vposy);
        pViewBack->SetPos(vposx, vposy);
        pViewNumItems->SetPos(vposx, vposy);

    } /* end of loop by item list */


    // в конце цикла рендера предметов
    // избавляемся от всех слоёв
    // и от мира, для рендера предметов
    delete pViewItem;
    delete pViewBack;
    delete pViewNumItems;
    pWorld->Release();
}


void Game_Loop()
{
    // при нажатии на клавишу "Z"
    if (zKeyPressed(KEY_Z))
    {
        // рендерим предметы игрока на экране,
        // с размером ячейки в 100 пикселей
        RenderInvItems(player, 100);
    }
}
Текстуры из папки "TEX" скопировать в "Gothic II\_work\Data\Textures\_Compiled\".
Textures.zip

Тесты:
TestInv1_800x600.jpg
TestInv1_1920x1080.jpg
TestInv2_1920x1080.jpg
TestInv3_1024x768.jpg


Понятно, что здесь приведена не вся логика инвентаря, а лишь некоторые её моменты.
Предназначение кода - чисто для изучения и проведения дальнейших экспериментов (как подсказка).
Утечек памяти вроде как не обнаружено (выделенная память, после 10к циклов рендера, вновь высвобождается).

Я конечно не думал над тем, чтобы делать более 2х валют, но первым вариантом отображения, именно хотел сделать как на рисунке (TradeMulti_ListCoins.jpg) Но там столкнулся с неуправляемым масштабированием, то бишь, новая текстура заголовка и его текст при изменении разрешения, постоянно норовят уйти в сторону, это был ещё один плюс к тому, чтобы делать с нуля.
Тогда надо просто брать в свои руки перерисовку этого заголовка. Ну а старый алгоритм выключить, если он не исправим.
Изменение разрешения экрана отследить легко, достаточно примерно такой проверки в цикле меню:
C++:
void Game_MenuLoop()
{
    // разрешение экрана (по умолчанию: не заданы)
    static int screenX_old = -1;
    static int screenY_old = -1;

    // если разрешение экрана поменялось
    if ((screen->psizex != screenX_old) && (screen->psizey != screenY_old))
    {
        // выполняем свой код
        // или ставим флажок и выполняем код потом

        // запоминаем новое разрешение экрана
        screenX_old = screen->psizex;
        screenY_old = screen->psizey;
    }
}

Или с помощью перехвата функции:
C++:
//0x007ABDB0 public: static void __cdecl zCView::SetMode(int,int,int,struct HWND__ * *)
// Перед изменением размеров окна приложения
void __cdecl zCView_SetMode(zCView*, int, int, int, HWND*);
CInvoke <void (__cdecl*) (zCView*, int, int, int, HWND*)> pView_SetMode(0x007ABDB0, zCView_SetMode, IVK_AUTO);
void __cdecl zCView_SetMode(zCView* _this, int x, int y, int bpp, HWND* initContextHandle)
{
    // запоминаем в какой-нибудь переменной, что произошло изменение разрешения экрана,
    // затем в функции перерисовки используем эту информацию.

    // вызываем оригинал
    pView_SetMode(_this, x, y, bpp, initContextHandle);
}
 

Вложения

  • Textures.zip
    9 KB · Просмотры: 1
Последнее редактирование:
Сверху Снизу