![]() |
Здравствуйте, гость ( Вход | Регистрация )
![]() ![]() |
![]() |
![]()
Сообщение
#1
|
|
Level 9 ![]() Класс: Волшебник Характер: Chaotic Good Раса: Дракон NWN: Скриптинг [PW] ![]() |
Вопрос для обсуждения - как сделать крафтовый движок, достаточно гибкий, удобный в обновлении и, самое главное, не приводящий к лагам.
Самый распространенный вариант - делать все рецепты для крафта через .2da файлы. И обновлять удобно (достаточно на сервер выложить новую версию файлов и )желательно) перегрузить модуль). Но есть и проблемы. 2да таблицы хороши, пока число строк в ней невелико. С ростом файла задержки становяться просто недопустимыми и нужно придумывать что-то другое. Хороший вариант - использовать внешнюю БД (mySQL например) и делать через нее. Будет быстрее и довольно гибко (обновлять рецепты можно даже через веб-интерфейс, не перегружая модуль). Только есть свои "если". Если хостер прикрутит поддержку mySQL и поставит саму mySQL. Хочу предложить другой вариант, в чем-то менее удобный, но заметно более быстрый. Идея простая. Пусть все рецепты хранятся в тех-же 2да файлах (не важно в чем соб-но). Пишется простой скрипт (программа) (я использовал perl), который преобразует таблицу в .nss скрипт, готовый для компиляции. Его можно вставить в модуль, а для обновления можно просто класть новую версию в папку override на сервере, не меняя сам модуль. Что должен делать такой скрипт, на примере. Вот кусок реальной 2da-таблицы (список ресурсов-спавнов, с Мидгарда) Код 2DA V2.0 Name FullName FullName2 ResRef Price Spawn TTL Radius Search Spot MaxValue MaxCount DC Desc 0 min020 "Торф" "торфа" "_res_min020" 1 2 6000 5 6 **** 5 48 8 "Торф._Применяется_для_выплавки_слитков_и_сплавов_из_руды." 1 min022 "Уголь" "угля" "_res_min022" 15 1 6000 4 12 **** 7 25 12 "Каменный_уголь._Применяется_для_выплавки_слитков_и_сплавов_из_руды." 2 min013 "Болотное_железо" "железа" "_res_min013" 1 2 6000 2 10 **** 8 30 10 "Железная_руда_невысокого_качества" [...skip...] Описывается имя резурса, русское имя, имя в падеже, ресреф, шансы на спавн, место спаван (поверхность\подземелье\везде), время жизни спавна, радиус обнаружения ресурса, DC на search, spot, на добычу, максимальное качество и количество ресурса в спавне и описание. После работы скрипта получаем вот такой скрипт Код string _res_i() { object oI = GetWaypointByTag("mg_wp_i"); if (GetIsObjectValid(oI)) return GetName(oI); else return "Я"; } //return count of resources int _cra_GetMaxRes(); struct stRes { string Name,FullName,FullName2,ResRef, Desc; int Price, Spawn, TTL, Radius, Search, Spot, MaxValue, MaxCount, DC, Result; }; struct stRes _cra_GetRes(int nNumber) { struct stRes stResult; if (!nNumber || nNumber > _cra_GetMaxRes()) { stResult.Result = 0; return stResult; } switch (nNumber) { case 1: stResult.Name = "min020"; stResult.FullName = "Торф"; stResult.FullName2 = "торфа"; stResult.ResRef = "_res_min020"; stResult.Price = 1; stResult.Spawn = 2; stResult.TTL = 6000; stResult.Radius = 5; stResult.Search = 6; stResult.Spot = 0; stResult.MaxValue = 5; stResult.MaxCount = 48; stResult.DC = 8; stResult.Desc = "Торф. Примен"+_res_i()+"етс"+_res_i()+" дл"+_res_i()+" выплавки слитков и сплавов из руды."; stResult.Result = 1; break; case 2: stResult.Name = "min022"; stResult.FullName = "Уголь"; stResult.FullName2 = "угл"+_res_i()+""; stResult.ResRef = "_res_min022"; stResult.Price = 15; stResult.Spawn = 1; stResult.TTL = 6000; stResult.Radius = 4; stResult.Search = 12; stResult.Spot = 0; stResult.MaxValue = 7; stResult.MaxCount = 25; stResult.DC = 12; stResult.Desc = "Каменный уголь. Примен"+_res_i()+"етс"+_res_i()+" дл"+_res_i()+" выплавки слитков и сплавов из руды."; stResult.Result = 1; break; case 3: stResult.Name = "min013"; stResult.FullName = "Болотное железо"; stResult.FullName2 = "железа"; stResult.ResRef = "_res_min013"; stResult.Price = 1; stResult.Spawn = 2; stResult.TTL = 6000; stResult.Radius = 2; stResult.Search = 10; stResult.Spot = 0; stResult.MaxValue = 8; stResult.MaxCount = 30; stResult.DC = 10; stResult.Desc = "Железна"+_res_i()+" руда невысокого качества"; stResult.Result = 1; break; [...skip...] return stResult; } int _cra_GetMaxRes() { return 3; } Данный вариант - инклуд (можно делать и вызываемый скрипт), возвращает структуру по номеру и количество ресурсов. Вставляем этот файл в папку с открытым модулем, компилим. Скорость работы - выше сложнее придумать, ведь мегабайтные файлы с АИ мобов выполняются шустро, а тут объемы гораздо меньше и все данные статичны. Аналогично можно поступить с любой 2да-таблицей. Как уже говорил - не обязаетельно делать вариант с инклудом полученного скрипта в другой, можно создавать полноценный скрипт с main() функцией и передавать в него параметры через SetLocalInt(GetModule()...), ExecuteScript("..",) и потом так-же получать результат - GetLocalInt(GetModule()...) В моем случае все скрипты писались под определенные 2да-таблицы, но легко можно сделать унифицированный скрипт, который будет работать очень похоже на функцию чтения данных из 2да-файла. Что в итоге: минусы: * дополнительный этап при сборке модуля, если изменились таблицы - компиляция 2да-таблиц в скрипты. * меньшая гибкость по сравнению с mySQL -вариантом в плане обновления плюсы: * скорость. выше - сложнее придумать, а для шарда это очень важно * не нужны спец. условия для хостинга * простота и универсальность (хоть и проигрывает в этом простому варианту с 2да-файлами) Предлагаю обсудить другие варианты и отвечу на вопросы - если будут (IMG:style_emoticons/kolobok_light/smile.gif) Сообщение отредактировал _kaa_ - Jun 17 2004, 11:07 |
![]()
Сообщение
#2
|
|
4-х Кубовый ![]() Класс: Некромант Характер: Lawful Evil Раса: Человек NWN: Скриптинг [Sn] Проклятие Левора ![]() |
Каа, а ты не видел как сделан крафт у нас? Сейчас уже вообще без лагов. С 2да-таблицей. Думаю и в мульте прокатит такой вариант.
|
![]()
Сообщение
#3
|
|
Level 9 ![]() Класс: Волшебник Характер: Chaotic Good Раса: Дракон NWN: Скриптинг [PW] ![]() |
Цитата аа, а ты не видел как сделан крафт у нас? Сейчас уже вообще без лагов. С 2да-таблицей. Думаю и в мульте прокатит такой вариант. Нет еще, жду пока из бета-теста выйдет. Вариант с 2да для мультика подойдет в случае, если как компонент используется "рецепт" или что-то похожее, однозначно определяющее строку в 2да-таблице. Я же говорил про "честный" вариант, без рецептов в виде компонента крафта. |
![]()
Сообщение
#4
|
|
Level 9 ![]() Класс: Волшебник Характер: Chaotic Good Раса: Дракон NWN: Скриптинг [PW] ![]() |
Вот тут скриптик, помогающий создавать крафтовые рецепты для Мидгарда. Для работы требует всего лишь тектовый файлик с названием и тегом кастомных предметов (делается NWNExplorer'ом например, из файла item*.itp)
Работает в обе стороны, т.е. можно из компонентов получить строку для 2да-таблицы или же из строки получить список компонентов. |
![]()
Сообщение
#5
|
|
Level 4 ![]() ![]() Класс: Друид Характер: Neutral Evil Раса: Эльф NWN: Скриптинг [PW] ![]() |
Вопрос таков: если обновить 2da на сервере (т.е. на сервере в папку override сунуть) - обновятся ли рецепты для ВСЕХ игроков или же им нужно будет обновлять сам файл 2da самим?
Если самим - уж лучше использовать скриптовой вариант. ЗЫ: научился работать со структурами... и написал крафт на все виды эначанта (а точнее переписал старый), который использует очень много интерестных вещей и находится в 4х скриптах, 2 из которых - вспомогательные... Работает, и есть поддержка 2da т.е. я встроил ее. Вот так значит если первое работает с обновлением на сервере - буду в 2da рецепты писать... а если нет - придется извращаться через скрипты... |
![]()
Сообщение
#6
|
|
Level 10 ![]() ![]() ![]() Класс: Воин Характер: Lawful Neutral Раса: Человек NWN: Скриптинг [PW] ![]() |
Нет, если только на серваке 2da обновишь то у игроков рецепты не изменятся.
|
![]()
Сообщение
#7
|
|
Level 9 ![]() Класс: Волшебник Характер: Chaotic Good Раса: Дракон NWN: Скриптинг [PW] ![]() |
Цитата(Аваддон @ Jul 31 2004, 13:10) Нет, если только на серваке 2da обновишь то у игроков рецепты не изменятся. Не совсем верно. Если 2да твоя, не используемая клиентом - а только сервером - то игрокам ее вообще противопоказанно иметь. Достаточно будет обновлять ее на сервере. Если же речь идет о _замене_ стандартных 2да, _используемых_ движком самого клиента - то без обновления на стороне клиента не обойтись |
![]()
Сообщение
#8
|
|
Level 4 ![]() ![]() Класс: Друид Характер: Neutral Evil Раса: Эльф NWN: Скриптинг [PW] ![]() |
2 kaa - о пасибки.
Я и не собирался игрокам 2da давать =) Добавлено в [mergetime]1091326763[/mergetime] Ах да чтобы облегчить использование 2da надо просто разделять их. Например в 1 2da - 50 рецептов, во втором - тоже 50. И лагов не будет. Скрипт будет сам смотреть по индентификатору крафта в каком файле что лежит. Если например индентификатор 161, то скрипт отнимет один раз 50 и передйет к след. файлу, еще раз 50, еще раз 50 и получится у нас 11 - он уже из четвертого файла и вытащит 11 рецепт. Легко и лагов нет. =) |
![]()
Сообщение
#9
|
|
Level 9 ![]() Класс: Волшебник Характер: Chaotic Good Раса: Дракон NWN: Скриптинг [PW] ![]() |
Есть много способов поделить рецепты в кучу разных 2да. Но из скрипта - все равно намного быстрее, пусть и сложнее их туда затолкать.
Кстати, есть такая фича - может пригодится. Если делать безрецептных крафт - то есть определять что получится исходя из того, что затолкали - можно использовать вот что. При переборе по GetFirstItemInInventory() - GetNext..() первым возвращается последняя затолканая вещь. Т.е. если ты будешь требовать правильный порядок рецепта - то получается совсем легко, простая проверка. |
![]()
Сообщение
#10
|
|
Level 4 ![]() ![]() Класс: Друид Характер: Neutral Evil Раса: Эльф NWN: Скриптинг [PW] ![]() |
Да у меня крафт такой, что нельзя сделать из там металла+дерева топор.
У меня можно из этого топора забабахать +1 энчант, +2 урон кислотой и т.п. =) Смотря какие рецепты... И кончно же этот энчант +1 можно сделать +2 и +3 и т.п.... |
![]() ![]() |
Текстовая версия | Сейчас: 26th April 2025 - 23:48 |