Artos 99 Опубликовано 6 Сентября 2011 (изменено) Из категории: "Маленткие хитрости" 1. Нередко в модах можно встретить сохранение некоторых переменных в pstor актора (иль иного объекта) для хранения значений типа 'вкл/откл' в виде: 'xr_logic.pstor_store(db.actor, "var_name", 1)' или 'xr_logic.pstor_store(db.actor, "var_name", 0)'. Можно подсчитать сколько байт займет данная переменная в сэйве: Имя переменной займет string.len(var_name) + 1 = 9 байт Значение переменной (число) займет float() = 4 байта. Итого имеем: 9+4 = 13 байт Если же использовать булевы значения (true/false), т.е.: 'xr_logic.pstor_store(db.actor, "var_name", true)' или 'xr_logic.pstor_store(db.actor, "var_name", false)' то запись значения переменной будет занимать всего 1 байт ( bool() == u8() ) и на каждой подобной переменной экотомится целых 3 байта(!) Напомню, что максимальный объем для psor'а ограничен 8192 байтами. 2. Нередко в модах используют сохранение таблиц в pstor, с использованием упаковки таблицы в строку функциями упаковки из АМК мода. В случае, упаковки таблицы типа простого списка {val1,val2,...valN} этот список упаковывается с порядковыми индексами всех значений в списке. Учитывая изложенное в п.1 и, принимая во внимание, что на каждый разряд индекса тратится 1 байт, выходит, что списки, имеющие более чем 10 индексов, экономнее хранить используя упакованным как массив с булевыми значениями. Если перед записью переводить подобный список в массив типа: {val1=true,val2=true,...valN=true} - то экономия байт в сэйве будет достигать (N - 9) байт для списка менее 100 индексов! Для списков с более 100 индексов экономия еще более, т.к. на каждом более 100-ом индексе уже экономится по 2 байта. Естественно при извлечении из сэйва (возможно) потребуется обратная конвертация в простой список. Резюме: Храните переменные в pstor'ах по-возможности в булевых значениях и большие таблицы (списки) в массивах с булевыми значениями, а не списками. Изменено 7 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 7 Сентября 2011 (изменено) Gun12, singapur22 А почему бы не привязаться к реальным кодам игры и не точечно, а хотя бы точками из дапазона? Что-то типа:: function test_concat() --/ предустановка local size = {1,25,50,75,100,125,150} --/ кол-ва символов for _,v in ipairs(size) do --/ циклы проверки с различной длиной --/ предустановки для тест-циклов local a = string.rep("a",v) local b = string.rep("b",v) local c = string.rep("c",v) local d = string.rep("d",v) local e = string.rep("e",v) printf("Size=%s:%s", v, string.rep("~",50)) --/ test table.concat local t1 = profile_timer() t1:start() local tb = {a, b, c, d, e} --/ таблица local s for i=1, 1000000 do s = table.concat(tb) end t1:stop() printf("table.concat:=%s", t1:time()) --/ test strings concat local t2 = profile_timer() t2:start() local s for i=1, 1000000 do s = a .. b .. c .. d .. e end t2:stop() printf("strings_concat:=%s", t2:time()) --/ test string.format local t3 = profile_timer() t3:start() local s for i=1, 1000000 do s = string.format("%s%s%s%s%s", a, b, c, d, e) end t3:stop() printf("string.format:=%s", t3:time()) end end Специально создание таблицы стрингов для 'table.concat' внес внутрь цикла, т.к. все же исходными являются именно строки, а шаг по формированию 'удобств' - это уже издержки самого выбираемого метода слияния. Имеем (для реального x-Rey Stalker:SHoC): Size=1:~~~~~~~~~~~~~~~~~~~~~~ table.concat : 382377.4375 strings_concat: 126710.90625 string.format : 849551.875 Size=25:~~~~~~~~~~~~~~~~~~~~~ table.concat : 560190.5625 strings_concat: 225513.09375 string.format : 2258268.75 Size=50:~~~~~~~~~~~~~~~~~~~~~ table.concat : 612529.625 strings_concat: 288320.59375 string.format : 3637614 Size=75:~~~~~~~~~~~~~~~~~~~~~ table.concat : 686650.125 strings_concat: 334355.90625 string.format : 5190542 Size=100:~~~~~~~~~~~~~~~~~~~~ table.concat : 687361.375 strings_concat: 373632.84375 string.format : 759546.75 Size=125:~~~~~~~~~~~~~~~~~~~~ table.concat : 1232452.5 strings_concat: 448679.375 string.format : 1306033.125 Size=150:~~~~~~~~~~~~~~~~~~~~ table.concat : 1321812.875 strings_concat: 502137.53125 string.format : 1403406.5 Варьируя различными значениями можно подтвердить для LUA в применении к Сталкеру, что; 1. Самым оптимальным является 'чистый' concat, т.е. применение оператора '..'; 2. Для коротких строк оптимальнее применять слияние строк при помощи 'table.concat', а не 'string.format'; 3. Самым медленным вариантом является "string.format', хотя для длинных строк (>=100) его проигрыш уже не очевиден. Изменено 7 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 7 Сентября 2011 (изменено) 7.9 Из лога ошибки ('bind_stalker.script:397'): get_console():execute("g_game_difficulty "..game_difficulty_by_num[game_difficulty]) - т.е. ошибка чтения уже первого байта из пакета актора ('game_difficulty'), а значит природа обоих вылетов едина - 'битый' сэйв. Просто неудачный вывод в лог (от разрабов) чуть раньше, чем прерывание по 'SAVE FILE IS CORRUPT' СтОит немного подправить в любом случае для любых модов, типа: if game_difficulty_by_num[game_difficulty] then get_console():execute("g_game_difficulty "..game_difficulty_by_num[game_difficulty]) else abort("SAVE FILE IS CORRUPT: Error read game_difficulty") end - тогда не будет ругаться на конкатенацию и выдаст причину ошибки. ... остальное позже, требуется анализ кодов. Изменено 7 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 7 Сентября 2011 (изменено) 7.9 Суть твоей ошибки: 1. Строкою: st = get_hud():AddCustomStatic("d2hud", true) - ты создаешь объект кастомстатика 2. Приаттачиваешь свою страничку (page_static) строкою: st:wnd():AttachChild(page_static) - Внимание! st:wnd() - это новый объект 'окно', т.е. ты приаттачиваешь не к 'st', а к его дочернему объекту ( 'wnd()' ). 3. Отключаешь свою страничку (page_static) строкою: st:wnd():DetachChild(page_static) - Внимание! Тут уже от родительского 'st' создается НОВЫЙ объект 'окна' st:wnd(), который совершенно иной, чем тот, к которому ты приаттачивал ранее. В результате возникает фатальная ошибка попытки отключить то, что не было приаттачено -> зависает биндер актора (поток, откуда был вызов) -> рушатся сэйвы (пакет актора некому сохранять). Решение: Работать только с одним и тем же объектом окна. При создании 'st', запоминать не его, а сразу объект его 'окна', типа: st_wnd = get_hud():AddCustomStatic("d2hud", true):wnd() - и уже далее работать именно с ним (st_wnd). И удалять именно его. Примечание: Удалять созданное 'st_wnd' нужно в обязательном порядке при любом выходе из игры. Если не удалять принудительно - перезагрузка сэйва без выхода в ОС может приводить к аналогичным фатальным ошибкам, т.к. объект 'st_wnd' не уничтожается чистильщиком LUA. Добавлено через 32 мин.: Gun12 Полностью согласен с тем, что всегда требуется учитывать контекст (конкретику), почему редко стараюсь давать полные варианты ответов, т.к. всего не учесть ... Но в данном конкретном случае, даже вынесение из цикла подсчета времени формирования таблицы для табличного слиятия (table.concat) и принудительная конкатенация операторами '..' выборкой из из сформированной таблицы (s = tb[1]..tb[2]..tb[3]..tb[4]..tb[5]) - все одно приводит к одному и тому же результату: конкатенация операторами более чем в 2 раза быстрее любого другого метода. Т.е. даже заведомое использование 'родной' заранее подготовленной таблицы со строками для "table.concat' и принудительное ее использование для 'обычной' (что добавляется к времени обычной конкатенации операторами '..') не приводит к преимуществам 'табличной конкатенации'. Изменено 7 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 7 Сентября 2011 (изменено) Gun12 Если результаты разнятся - значит есть различия в условиях тестирования ... их нужно или устранять или учитывать. Я убираю различные субъективные причины: версия интерпретатора LUA на компе, его надстройка в SciTE (и пр.), оставив только то, что имеет отношение к собственно игре (к встроенному интерпретатору LUA). Воспроизвести эту функция (аналог твоей) может каждый, даже не запуская игру (подключив в '_g'), а только выйдя в основное меню: function test_concat() --/ предустановки local size = {1,25,50,75,100,125,150,175} --/ кол-ва символов local a,b,c,d,e,s,tb,t1,t2,t3 --/ объявление переменных for k,v in ipairs(size) do --/ циклы проверки с различной длиной строк --/ (пере)учтановки a = string.rep("a",v) b = string.rep("b",v) c = string.rep("c",v) d = string.rep("d",v) e = string.rep("e",v) tb = {a, b, c, d, e} --/ таблица для 'table.concat' printf("Size=" .. tostring(v) .. ":" .. string.rep("~",22)) --/ table.concat t1 = profile_timer() t1:start() for i=1, 1000000 do s = table.concat(tb) end t1:stop() printf("table.concat:=" .. tostring(t1:time())) --/ strings concat t2 = profile_timer() t2:start() for i=1, 1000000 do -- s = a .. b .. c .. d .. e s = tb[1] .. tb[2] .. tb[3] .. tb[4] .. tb[5] end t2:stop() printf("strings_concat:=" .. tostring(t2:time())) --/ string.format t3 = profile_timer() t3:start() for i=1, 1000000 do s = string.format("%s%s%s%s%s", a, b, c, d, e) end t3:stop() printf("string.format:=" .. tostring(t3:time())) end end и Size=1:~~~~~~~~~~~~~~~~~~~~~~ table.concat : 378829.9375 strings_concat: 115101.3671875 string.format : 885728.75 Size=25:~~~~~~~~~~~~~~~~~~~~~~ table.concat : 556410.875 strings_concat: 239722.640625 string.format : 2280955.75 Size=50:~~~~~~~~~~~~~~~~~~~~~~ table.concat : 614913.9375 strings_concat: 290451.71875 string.format : 3658724.5 Size=75:~~~~~~~~~~~~~~~~~~~~~~ table.concat : 685291.0625 strings_concat: 352517.84375 string.format : 5199886 Size=100:~~~~~~~~~~~~~~~~~~~~~~ table.concat : 692421.5 strings_concat: 379044.0625 string.format : 778628.3125 Size=125:~~~~~~~~~~~~~~~~~~~~~~ table.concat : 1130261.5 strings_concat: 460861.65625 string.format : 1249460.5 Size=150:~~~~~~~~~~~~~~~~~~~~~~ table.concat : 1326386.75 strings_concat: 493378.875 string.format : 1415757 Size=175:~~~~~~~~~~~~~~~~~~~~~~ table.concat : 1426552.75 strings_concat: 569790.5625 string.format : 1558606.125 - как видно есть различия с твоими результатами ... Остается - набирать статистики в одинаковых условиях. Изменено 7 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 7 Сентября 2011 *Shoker* Воздержись от ответов на вопросы в которых сам 'плаваешь'. Разница между 'InitTexture' и 'InitStatic' достаточно огромна. Тебе следует поискать ответ в "Справочник по классам и функциям". Кратко: 'InitStatic' - метод только для класса 'ScriptXmlInit', подразумевающего использование xml-файлы в качестве основы/параметров. Инициализирует/создает (для CUIWindow) дочерний объект класса 'CUIStatic'. 'InitTexture' - метод, доступный для многих CUI-классов, которым инициализилуется/подключается заданная текстура (иль ее аналог). "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 7 Сентября 2011 (изменено) _Призрак_ То, что машина спавнится точно по координатам - никогда не скажу. Т.к. если спавнится в all.spawn'е - то да, 'точно', если скриптом - о точности только помечтать можно ... Что на нее не действуют законы физики? Хм, и да и нет, но точно, что именно после первого хита, она начинает подчиняется неким игровым 'законам физики'. Будет ли она скатываться от первого хита? У горе-ковырялкина возможно ... Все зависит от собственно силы и импульса хита. Можно и в небо запулить. ;-) По сути: Если хочешь точно застопорить машину - то посмотри ранее посты, где давал уже варианты ступоров, т.е. или 'collide' или 'fixed_bones'. Если же машина для 'езды' - то придется с нет-пакетами (иль еще как) поупражняться, например, отключая ступор(ы) при посадке и включая их при высадке ГГ. Изменено 7 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 8 Сентября 2011 (изменено) *Shoker* Молодец, что и мне напомнил, а то как-то начал делать и ... не закончил. Однако более правильным будет несколько иначе: 1. Функция/метод остановки и постановки на 'ручник' машины уже полностью реализована в схеме машин ( ph_car.script -> action_car:stop_car() ) и если объект под этой схемой - то и использовать встроенный функционал. Если же не под схемой (чего правда не встречал) - то и работать не будет. 2. В твоей строке задается некий аргумент =100? А это задание условной скорости - т.о. этот аргумент при остановке логично ставить в 0. 3. Дополнительно к 'move.handbrake' есть резон задавать константу остановки 'move.off', т.е. 'move.off + move.handbrake'. Взятие же машины под контроль удобнее делать под апдейтом самой 'машинной схемы' ( action_car:update(delta) ), что-то типа: --/ Управление 'ручником' автотранспорта --/ oCar: game_object машины --/ bAction: true=>'стоп', false=>'разрешено движение' function Car_Handbrake(oCar,bAction) local st = db.storage[oCar:id()] --/ сторадж объекта if st and st.car_mgr then --/ менеджер схемы 'ph_car' st.car_mgr .on_handbrake = bAction --/ ставим флаг: вкл/откл ручника end end function action_car:update(delta) --/ из ph_car.script -- ... --/ внешнее управление 'ручником' (остановкой) if self.on_handbrake and (self.time_handbrake or 0) < time_global() then --/ включить ручник? self.time_handbrake = time_global() + 3000 --/ тайм-аут self:stop_car() self.state_moving = iState_moving_end end end Примечание: Машина должна быть 'забиндена' (иметь сторадж в db.storage). Постановка на ручник работает независимо от (от)включенного двигателя или отсутсвия актора внутри. При движении машины - естественно ручник (on_handbrake) должен быть 'снят'. --------------------------------------- Переместить объекты из инвентаря ГГ в иной серверный объект (хотя бы и ящик) - невозможно. Методы трансфера доступны только для гейм-объектов. Если же под 'перемещением' подразумевается возможность удаление предмета и воссоздание его копии в нужном месте - это иной разговор ... Думаю мороки будет поболе, чем дождаться перехода ящика в онлайн, если озаботиться точными копиями ... Изменено 8 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 8 Сентября 2011 (изменено) Jurok А в чем заминка самому написать простенький скрипт удаления? Ведь все просто и очевидно: if phantom_manager:phantom_count() > 1 then --/ Удаление фантомов local sim = alife() for id=1,65534 do local soObj = sim:object(id) if soObj and soObj:clsid() == clsid.phantom then alife():release(soObj, true) --/ удаляем end end end или для текущего уровня: if phantom_manager:phantom_count() > 1 then --/ Удаление фантомов на текущем уровне local sim = alife() for id=1,65534 do local oObj = level.object_by_id(id) --/ на текущем уровне? if oObj and oObj:clsid() == clsid.phantom then local soObj = sim:object(id) if soObj then alife():release(soObj, true) --/ удаляем end end end end Добавлено через 11 мин.: *Shoker*: А где нибудь есть разъяснение, какие аргументы ещё можно передавать в другие move-действия? Ну и в догонку тогда уже интересует 'cond(cond.time_end, 5000)' Кроме информации в 'lua_help.script' и кодах оригинальных игр - не встречал толковалок. Немного сам поразбирался ... Вот что получилось: move.none, --/ 1 move.on + move.fwd, --/ 256 +2 вперед move.on + move.fwd + move.left, --/ 256 +2 +8 вперед-налево move.on + move.fwd + move.right, --/ 256 +2 +16 вперед-направо move.on + move.back, --/ 256 +4 назад move.on + move.back + move.left, --/ 256 +4 +8 назад-налево move.on + move.back + move.right, --/ 256 +4 +16 назад-направо move.off, --/ 512 стоп move.off + move.handbrake --/ 512 +128 стоп+постановка на ручник Да и по 'cond.time_end' также только догадки. Судя по тестам - ты прав в догадке, что это таймер периода окончания действия, которое устанавливается строкою: action( object, move(const, speed), cond(const, time) ), после которого (судя по тестам) вызывается новая (пере)установка 'action'. Изменено 8 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 8 Сентября 2011 (изменено) KASIMKA Твоя ошибка: в непонимании структуры и функционирования 'схемы'. Условно: схемы состоят из трех частей: - функции 'set/disable' (пресетов) - установка, сброс, биндер (активация), ... - методы 'evaluators' - проверки условий схемы - методы 'actions' - выполнение действий схемы Если схема активна для объекта - то эвалуаторы схемы вызываются постоянно(!) и в зависимост от проверок различных условий - на выход передают булево значение (истина/лож) о результате проверки. По этим результатам от эвалуаторов - могут запускаться/останавливаться экшены (выполнение действий схемы) и запускаться/блокироваться другие схемы. Ты вставил свой кусок кодов 'Проверка на огневую мощь' в эвалуатор схемы. По сути ты вставил кусок экшена (действия), которые должен выполнять объект при определенном тобою условии. При выпонении условия: 'self.fire_power < 3', которое постоянно(!) проверяется в активном эвалуаторе - ты вынуждаешь объект обязательно выполнять действие (бежать в укрытие). Что же удивляться, что твой НПС подчиняется твоим же указаниям? В эвалуаторе требуется только проверять условие! И уже по результату этой проверки - вызывать выполнение нужного действия. При чем(!) выполнение действия должно иметь окончание! Т.е. по достижении цели действия (в твоем случае - добежал до укрытия) - его выполнение должно быть прекращено. Примечание: По отсутстующему в оригинальной игре (heli_target) невозможно сказать сбрасывается ли у тебя 'self.fire_power', иль вообще принимает какие-то значения для отмены условия. Резюме: Перед правкой/созданием схем - требуется знать и понимать общую структуру схем и их взаимодействий и написать алгоритм работы схемы, который и воплощать в кодах (а не наоборот). Изменено 8 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 9 Сентября 2011 (изменено) Убедительная просьба к спрашивающим: Не пытаться продолжать обсуждение заданного в топике вопроса в ЛС без вестких на то причин. Топик на то и создан, чтобы задавать вопросы и разбираться с ними. Избегайте адресовать (без причины) вопросы(ы) конкретно кому-то. Вам могут дать на тот же вопрос и другие форумчане, а не только выбранный Вами адресат. Не адресуйте вопрос к 'толпе', типа: "Народ! ..." или "Люди! ...". Мы не в пустыне, вы не на трибуне и Нелюди вам все одно не ответят. А вот те, кто себя не ассоциирует с 'толпой' - могут промолчать ... Так же, задавая вопрос, не нужно (тем более кажды раз) 'здороваться'. Это все же форум, а не очная встреча. Ваше "Доброе утро", странно кому-то читать позней ночью, через пару дней, и может оно и не очень у кого-то 'доброе' ... Избегайне неинформативной 'дипломатии' и излишних 'расшаркиваний' ... Изменено 9 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 9 Сентября 2011 (изменено) KASIMKA 1. Применяя конструкции типа: 'self.var_name = ...' следует учитывать, к чему относится 'self'. Устанавливая в эвалуаторе 'self.hide = true' - бессмыслено проверять значение этой переменной в экшене, т.е. она всегда будет равна 'nil'. В данном случае 'self.hide' - это переменная, область которой ограничена классом эвалуактора 'evaluator_shoot' и вне его она недоступна. Для того, чтобы использовать нужную переменную и в экшене ('action_shoot') можно или вынести эту переменную на уровень локальной/глобальной переменной корня скрипт-файла или же использовать типовой прием: 'self.a.hide = true', помещая ее в единый сторадж ('self.a') данной схемы и читать из экшена этой же схемы аналогично: 'if self.a.hide == true then' 2. Из контекста вопроса(ов), можно сделать вывод, что тебе хочется, чтобы при отсутствии необходимого оружия для стрельбы по вертолетем - НПС убегал в укрытие, если же 'имеет' - неясно ... (вероятно не должен убегать, а стрелять по вертушке/сражаться). Данная схема имеет только один эвалуатор и один экшен: - проверка - "можно пострелять по вертолёту" - действие - "стрелять по вертолёту" Т.о. полностью отсутствует проверка - "нужно ли прятаться" и действие - "бежать и прятаться", без реализации которых невозможно осуществить тобою задуманное. Если, как в 1-ый раз, вставлять проверку и действие в общий эвалуатор - ресурсоемкая проверка будет съедать немало ресурсов и будет приводить к тому, что НПС при отсутствии подходящего оружия и при наличии 'живого' вертолета на локации - всегда будут бежать прятаться. Иными словами: требуется второй дополнительый эвалуатор, который по флагу от 1-го (есть вертушка) давал бы команду второму экшену - 'бежать прятаться', запрещая первый экшн (стрелять). Тогда НПС будут вести себя аналогично схеме 'выброса': нет опасности - схема не активна, опасно и есть оружие - стрелять, опасно и безоружен - прятаться. 3. Функция 'get_target_priority(obj)' очень неоптимальна. Использовать постоянные прямые перечтения файлов конфигурации - значительная нагрузка на русурсы. Тем более для эвалуаторов (по сути как апдейт НПС) эта функция мягко говоря 'тяжела'. а) Можно заранее один раз прочитать все конфиги оружия и патронов к нему и занести в таблицу, из которой и пользоваься; б) Постепенное чтение (по мере смены активного оружия) с запоминанием в таблицу нужных значений; в) Можно один раз проверить и запомнить активное оружие и, если оно не менялось - не перепроверять его каждым вызовом функции; г) Комбинация а), б), в). P.S. В дополнение к п.2 Можно попробовать обойтись и имеющейся парой в схеме. В проверке (эвалуаторе) определять необходимость действия (стрелять или прятаться => true). А в действии (экшене) перепроверять условие 'стрелять иль прятаться' (т.е. наличие нужного оружия) и выбирать соответствующую ветвь действия, что конечно потребует экшен разделить на две ветки (вторую ветку 'прятаться' самому написать на базе уже написанных кодов). Изменено 9 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Сентября 2011 (изменено) =VENOM= Возражу и поправлю. Файл 'smart_terrain_presets.ltx' является конфигом, который предназначен только для универсальных гулагов типа 'general_lager'. Гулаг, о котором и в вопросе и ты говоришь - 'esc_fabrika_bandit' - именнОй. 1. Именно для этого гулага в all.spawn'е организован респавнер 'esc2_respawn_bandits_fabrika', в котором и прописаны секции, которые будут спавниться (esc_bandit_respawn_1, esc_bandit_respawn_2). Т.е. если в респавнер добавить секции бандитов с рангом мастеров - то они и заспавнятся (если не будет иных ограничений). Изменять в исходных секциях я очень бы не рекомендовал, т.к. изменять секции, предназначенные для всей Зоны, ради одного гулага - ну очень недальновидно. Ни что не мешает просто добавить секции 'esc_bandit_respawn_3' и 'esc_bandit_respawn_4' с рангами ветерана и мастера, если не устраивают готовые (cit_bandit_respawn_1, cit_bandit_respawn_2 - ветеран и мастер). 2. Условия приема для этого гулага прописаны в 'gulag_escape.script' и ограничение имеется только одно - по группировке. Т.е. любой бандит будет взят в гулаг при условии наличия свободной работы. Т.о. для 'заматерелости' АТП достаточно только правильно настроить респавнер этого гулага, внеся желаемые секции спавна (при недостатке - досоздать). Изменено 12 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Сентября 2011 (изменено) Вопрос-1: Как оптимально определить тип таблицы 'список' (list), т.е. типа: {"a","b","c","d"} или {1,5,2,4,3}? Интересует не просто что-то вроде 'if next(Tbl) == 1 then', что явно недостаточно, и не полный перебор списка на поиск 'дыр' в индексах, а что-то пооптимальнее. Т.е. 100%-ое, нересурсоемкое и быстрое определение именно списка, а не иного типа массива. Совместимость с LUA игры обязательна. Вопрос-2: Как перевести целочисленное число в хекс-стринг, т.е. типа: 65535 => "FFFF" и обратно? Интересуют быстрые оптимальные варианты совместимые с LUA игры. Изменено 12 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Сентября 2011 (изменено) malandrinus, (уже позже, перечитав вопрос, понял о двузначности фразы ...) Не разница между этими таблицами интересует, а это два основных варианта таблиц именно интересующего типа 'список'. Т.е. интересуют все таблицы, которые ключем имеют индексную последовательность, а не собственное значение ключа. Изменено 12 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Сентября 2011 (изменено) Цель заданных вопросов по таблице-списку и переводам Hex->String->Hex: Уже немало модов, где требуется сохранять 'свои' параметры и в достаточно большом кол-ве и в виде таблиц. Вариантов сохранения по сути три: 1. Записывать в 'pstor' объектам; 2. Записывать в пакет объекта напрямуую; 3. Записывать во внешние файлы. Вариант 3 - для ТЧ практически отпадает, т.к. класс работы с файлами достаточно рудиментарен, тем более на запись. Да и 'мусорить' на компе игрока - не самый удачный вариант. Вариант 2 для разрабатываемых модов или опционально подключаемых мини-модов/модулей - неприемлем и-за несовместимости сэйвов. Остается вариант 1. Запись в 'pstor' (вариант 1) имеет два подварианта: 1.1. Штатный метод из 'xr_logic.script' (pstor_store/pstor_retrieve); 1.2. Расширеный метод, дополненный сохранением таблиц. Ярким (и наиболее часто используемым до сих пор) примером являются методы АМК-мода (amk.script -> save_variable/load_variable). Особенности использования варианта 1.1 'штатным' методом: В 'pstor' записываются только три типа значений "boolean", "string", "number". Запись таблиц невозможна. Если по булевым и строковым значениям нет вопросов, то для чисел уже появляются. Все(!) численные значения записываются в сэйвы единым методом "w_float", т.е. всем им в обязательном порядке выделяется 4 байта (float == w_u32). Т.о. даже числу '1' (единичке) выделяются те же 4 байта из потребного одного бита. В игре из числовых значений как правило сохраняются время, игровые идентификаторы объектов, некие кол-ва и флаги (0/1/2). Т.о. основная часть сохраняемых числовых значений вполне укладывается в два байта (u_16 -> 65535). Внеся в 'штатный' метод сохранения/чтения сэйвов доп.признак 'целое число не более FFFF' - можно сэкономить немало байт в сэйвах. Особенности использования варианта 1.2 'расширеным' методом: Расширеный вариант помимо некоторого удобства применения (не требуется указывать объект и можно записывать 'nil'), имеет возможность записи таблиц. Для записи таблиц используется перевод всех полей (кеу и value) в таблицах в строковые значения и конкатенация (слияние) их в единую строку, которая и сохраняется. При чтении - обратная операция по восстановлению таблицы. 1. При переводе все типы таблиц упаковываются едино, т.е. и ключ и значение его поля (key & value) переводятся в строки. Несложно заметить, что для таблиц типа 'список', в качестве ключей будут упаковываться индексы (1,2,3... и т.д.). Внимание(!). Но ведь для таких таблиц по сути и НЕ требуется запоминать ключи-индексы. Достаточно только запомнить, что это именно 'список' и сохранить значеня в порядке их следования. Минусом/плюсом является эффект 'чистки', если в списке есть 'дырки' (нилевые значения). Для сэйвов это плюс. 2. При переводе таблицы в строку, опять же используется единый метод 'tostring(number)' для всех численых значений. Т.к. переводится в строку десятичное значение(!), то кол-во знаков (а каждый знак - это байт при записи!) для чисел более 9 начинается 'растранжиривание' байтов. Если конвертировать в стринг не десятичное, а шестнадцатеричное значение - экономия на лицо. Практически реализовал вышеописанную оптимизицию для записей в 'pstor', которая совместима с нынешними играми/сэйвами (при отсутствии обратной совместимости). Осталось гарантированно (все же сэйв) определять признак (НЕ)списков и найти оптимальный вариант методов конвертеров Hex -> String -> Hex. Думаю к 'завтра' выложу для публичной критики и/или общего применения. Изменено 12 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Сентября 2011 (изменено) _Призрак_ Хотя ты и немного ошибся с переводом в 'hex_num' (удалив и пост), но спасибо за подсказку! Это как раз перевод в 'dec_num'. Похоже это: local iDec = 12345 --/ исходное десятичное целочисленное local sHex = string.format('%X', iDec) --/ переводим в хекс-строку iDec = tonumber('0x'..sHex, 16) --/ переводим в десятичное число хекс-строку - то, что нужно! (Подзабыл про параметры для этого метода) Одна голова - маловато будет. Вот несколько - в самый раз. Изменено 12 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Сентября 2011 (изменено) panzyuza Функции из 'xr_conditions.script' - это функции общего применения и не привязаны к каким-то конкретным гулагам (если внутри функции нет конкретных указаний на имена/секции) и могут применяться в любых схемах логики. Да и простыми скриптами, при правильном указании входных аргументов. Параметры 'switch_0' и 'switch_1' - аналоги функции 'load_states()' из скриптов типа 'gulag_XXX.script' для 'именных' гулагов, возвращающей состояние гулага в зависимости от неких условий. Твое предположение почти верно - гулагам типа 'genelar_lager' именно этими параметрами задаются условия и проверяются их состояния. Изменено 12 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Сентября 2011 (изменено) panzyuza Все (почти), о чем ты спрашиваешь - лежит в кодах 'gulag_general.script' и конечно в 'xr_logic.script'. Условия же читаются из 'xr_conditions.script'. Что конкретно проверяет каждая функция из условий - зависит от нее. Строка: 'switch_0 = {=gulag_empty(esc_stone_lager)}' - приведет к тому, что при пустом гулаге 'esc_stone_lager' будет возвращено состояние '0' для гулага 'esc2_dogs_zamost' с этой стокою. Никакой тут ночной/дневной работой и не пахнет. Вероятно просто собаки вернутся в свой гулаг (не смотрел логику). Вторая строка с 'switch_1' - вернет состояние гулага '1' при упомянутых в строке (и растолкованных тобою) условиях. Вероятно собаки побегут нападать ... Не вешай себе шоры. Нет в этих строках конфига логики никакой привязки (условий) ночная иль дневная работа. Более правильно: Условие истино, если - '=' функция вернет true, Условие истино, если - '!=' функция НЕ вернет true (т.е. если вернет false). Изменено 12 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Сентября 2011 (изменено) Не стал, чтобы не навешивать шоры, ранее давать свой двух-недельной давности набросок: local iIdx = next(tTbl) if iIdx then --/ проверка: таблица не пуста? --/#+# проверка: таблица типа 'список'? local bList = iIdx == 1 and #tTbl > 0 --/ флаг списка if bList then --/ предпроверка: может быть списком? --/ основная проверка: это 'список'? for i=1,#tTbl-1 do bList = next(tTbl,i) ~= nil if not bList then break end --/ прерываем - НЕ список! end end --/ тело основного кода end - набросок пока имеет изъяны ... Перебор как видно присутствует, но прерываемый. Остается внутри перебора максимально добиться 100% идентификации списков. malandrinus Тут два момента. 1. Елиная функция, которая обрабатывает (рас)паковывает двумя методами. Для надежности (и скорости) - предусматривается ключ-фргумент на входе, который диктует выбор метода. 2. Делаю все же универсальный вариант и для тех кто порой не понимает разницы в типах таблиц (т.е. не готов определять директивный аргумент) и ... порой таблицы могут иметь различный тип ... упаковка 't должна быть всегда и по возможности максимально экономная. Примечание: Один раз написанная, достаточно универсальная функция упаковки/распаковки (AMK-мод), используется до сих пор многими модами по сути 'в слепую'. Есть намерение и заменить ее на более актуальный и экономичный вариант и заодно избавить многие моды от ее ошибки (не дающей упаковывать некоторые типы таблиц): function pack_new(tbl) --/ ... elseif type(v)=="boolean" then ret=ret..string.char(pack_type_bool)..v --/<!?! конкатенация строки и булева значения!?! --/ ... end Исправленный вариант: ret=ret..string.char(pack_type_bool)..((v and "1") or "0") Изменено 12 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение