Перейти к контенту

Скриптование


Svoboда

Рекомендуемые сообщения

Из категории: "Маленткие хитрости"

 

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'ах по-возможности в булевых значениях и большие таблицы (списки) в массивах с булевыми значениями, а не списками.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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) его проигрыш уже не очевиден.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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

- тогда не будет ругаться на конкатенацию и выдаст причину ошибки.

 

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

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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' и принудительное ее использование для 'обычной' (что добавляется к времени обычной конкатенации операторами '..') не приводит к преимуществам 'табличной конкатенации'.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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

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

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

*Shoker*

Воздержись от ответов на вопросы в которых сам 'плаваешь'.

 

Разница между 'InitTexture' и 'InitStatic' достаточно огромна. Тебе следует поискать ответ в "Справочник по классам и функциям".

Кратко:

'InitStatic' - метод только для класса 'ScriptXmlInit', подразумевающего использование xml-файлы в качестве основы/параметров.

Инициализирует/создает (для CUIWindow) дочерний объект класса 'CUIStatic'.

'InitTexture' - метод, доступный для многих CUI-классов, которым инициализилуется/подключается заданная текстура (иль ее аналог).

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

_Призрак_

То, что машина спавнится точно по координатам - никогда не скажу. Т.к. если спавнится в all.spawn'е - то да, 'точно', если скриптом - о точности только помечтать можно ...

Что на нее не действуют законы физики? Хм, и да и нет, но точно, что именно после первого хита, она начинает подчиняется неким игровым 'законам физики'.

Будет ли она скатываться от первого хита? У горе-ковырялкина возможно ... Все зависит от собственно силы и импульса хита. Можно и в небо запулить. ;-)

 

По сути:

Если хочешь точно застопорить машину - то посмотри ранее посты, где давал уже варианты ступоров, т.е. или 'collide' или 'fixed_bones'.

Если же машина для 'езды' - то придется с нет-пакетами (иль еще как) поупражняться, например, отключая ступор(ы) при посадке и включая их при высадке ГГ.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

*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) должен быть 'снят'.

 

---------------------------------------

Переместить объекты из инвентаря ГГ в иной серверный объект (хотя бы и ящик) - невозможно.

Методы трансфера доступны только для гейм-объектов.

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

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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'.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

KASIMKA

Твоя ошибка: в непонимании структуры и функционирования 'схемы'.

Условно: схемы состоят из трех частей:

- функции 'set/disable' (пресетов) - установка, сброс, биндер (активация), ...

- методы 'evaluators' - проверки условий схемы

- методы 'actions' - выполнение действий схемы

 

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

 

Ты вставил свой кусок кодов 'Проверка на огневую мощь' в эвалуатор схемы. По сути ты вставил кусок экшена (действия), которые должен выполнять объект при определенном тобою условии. При выпонении условия: 'self.fire_power < 3', которое постоянно(!) проверяется в активном эвалуаторе - ты вынуждаешь объект обязательно выполнять действие (бежать в укрытие).

Что же удивляться, что твой НПС подчиняется твоим же указаниям?

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

 

Примечание: По отсутстующему в оригинальной игре (heli_target) невозможно сказать сбрасывается ли у тебя 'self.fire_power', иль вообще принимает какие-то значения для отмены условия.

 

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

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

Убедительная просьба к спрашивающим:

Не пытаться продолжать обсуждение заданного в топике вопроса в ЛС без вестких на то причин.

Топик на то и создан, чтобы задавать вопросы и разбираться с ними.

 

Избегайте адресовать (без причины) вопросы(ы) конкретно кому-то.

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

 

Не адресуйте вопрос к 'толпе', типа: "Народ! ..." или "Люди! ...".

Мы не в пустыне, вы не на трибуне и Нелюди вам все одно не ответят. А вот те, кто себя не ассоциирует с 'толпой' - могут промолчать ...

 

Так же, задавая вопрос, не нужно (тем более кажды раз) 'здороваться'.

Это все же форум, а не очная встреча. Ваше "Доброе утро", странно кому-то читать позней ночью, через пару дней, и может оно и не очень у кого-то 'доброе' ...

Избегайне неинформативной 'дипломатии' и излишних 'расшаркиваний' ...

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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).

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

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

=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' и ограничение имеется только одно - по группировке. Т.е. любой бандит будет взят в гулаг при условии наличия свободной работы.

Т.о. для 'заматерелости' АТП достаточно только правильно настроить респавнер этого гулага, внеся желаемые секции спавна (при недостатке - досоздать).

 

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

Вопрос-1: Как оптимально определить тип таблицы 'список' (list), т.е. типа: {"a","b","c","d"} или {1,5,2,4,3}?

 

Интересует не просто что-то вроде 'if next(Tbl) == 1 then', что явно недостаточно, и не полный перебор списка на поиск 'дыр' в индексах, а что-то пооптимальнее. Т.е. 100%-ое, нересурсоемкое и быстрое определение именно списка, а не иного типа массива. Совместимость с LUA игры обязательна.

 

Вопрос-2: Как перевести целочисленное число в хекс-стринг, т.е. типа: 65535 => "FFFF" и обратно?

Интересуют быстрые оптимальные варианты совместимые с LUA игры.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

malandrinus,

(уже позже, перечитав вопрос, понял о двузначности фразы ...)

Не разница между этими таблицами интересует, а это два основных варианта таблиц именно интересующего типа 'список'.

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

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

Цель заданных вопросов по таблице-списку и переводам 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. Думаю к 'завтра' выложу для публичной критики и/или общего применения.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

_Призрак_

Хотя ты и немного ошибся с переводом в 'hex_num' (удалив и пост), но спасибо за подсказку! Это как раз перевод в 'dec_num'.

Похоже это:

local iDec = 12345 --/ исходное десятичное целочисленное
local sHex = string.format('%X', iDec) --/ переводим в хекс-строку
iDec = tonumber('0x'..sHex, 16) --/ переводим в десятичное число хекс-строку

- то, что нужно! (Подзабыл про параметры для этого метода)

 

Одна голова - маловато будет. Вот несколько - в самый раз. :good2:

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

panzyuza

Функции из 'xr_conditions.script' - это функции общего применения и не привязаны к каким-то конкретным гулагам (если внутри функции нет конкретных указаний на имена/секции) и могут применяться в любых схемах логики. Да и простыми скриптами, при правильном указании входных аргументов.

 

Параметры 'switch_0' и 'switch_1' - аналоги функции 'load_states()' из скриптов типа 'gulag_XXX.script' для 'именных' гулагов, возвращающей состояние гулага в зависимости от неких условий.

Твое предположение почти верно - гулагам типа 'genelar_lager' именно этими параметрами задаются условия и проверяются их состояния.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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).

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

Не стал, чтобы не навешивать шоры, ранее давать свой двух-недельной давности набросок:

  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")

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение
  • Недавно просматривали   0 пользователей

    • Ни один зарегистрированный пользователь не просматривает эту страницу.
×
×
  • Создать...