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

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

@Romz, зависит от того, что происходит раньше (вот тут не помню), первый апдейт или исполнение логики (UI при заходе на Янов отключается в ней). Если первое - вполне можно не заморачиваться, а однократно сбрасывать инфопорцию при каждой загрузке. Можно даже не на апдейт сброс повесить, а, например, на спавн актора.

  • Согласен 1
Ссылка на комментарий

@Kirgudu, В принципе, да. В скрипте, где этот инфопоршень проверяется, помимо него ещё проверки есть, так что, думаю, не критично будет.


 

 

Рекомендую, кстати, глянуть доработанный se_stor, который я выложил вчера в «сборочном цехе».
Да, я видел уже, и себе уволок. :)
  • Нравится 1

Шаман - СисАдмин

Всяко-разно: для ЧН

Ссылка на комментарий

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

Вот что я думаю по этому поводу

Про «

сделаны для более удобного вызова».

Вам придётся написать ещё пару страниц, чтобы убедить меня, что настучать на клавиатуре «get_substr_since_space_to_end (line)» мне удобнее, чем набрать знакомое «line:match('(%S+)%s?')», которое и короче самого имени и заодно сразу решает проблему. Это может бы быть не единственным примером.

 

Про «так как в них сразу проверки на пустоту».

Да нет там таких проверок. Все «If», которые там присутствуют, в лучшем случае просто дублируют последующие системные вызовы, а в большинстве просто бессмысленны. Но уж «пустоту» они точно не проверяют и не контролируют.

 

Про «и тд.».

Вот это «и тд.» есть самое опасное и тревожное. Посмотрите сами, некоторые Ваши функции/методы/оболочки (назовите, как хотите) порой вызывают простой системный метод через цепочку 3-4 вызовов промежуточных функций. А ведь ничего в этом мире не даётся просто так. Каждый вызов функции это ресурсные траты на очень многие скрытые от прямого взгляда вещи. Оправданы эти траты в данном конкретном случае? Однозначно нет. И никакое сомнительное удобство их не компенсирует.

 

Ну и теперь, чтобы не быть голословным покажу, как бы я решал те же проблемы. Это не истина в последней инстанции, «я ведь не волшебник, а только учусь» и спасибо Charsi, который в шутейной беседе здорово подтянул меня в этой области «кодоблудия». Весьма благодарен ему за это.

FonSwong, привожу только Ваше описание функции и мой вариант решения (везде line – строка, substring – подстрока, как они определении в оригинале текста)

 

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

local var = line:match('(%S+)%s?')
Примечание: вот здесь проверка не нужна, т.к. любой конец строки (по определению, но не всегда по реализации) считается пробельным символом, и поэтому получите то, что заказали.

 

Функция возвращает подстроку с пробела (первого) и до конца

local var = (line:find(' ')) and line:gsub('%w*%s?','',1) or ''
Примечание: здесь проверка на наличие хоть одного пробела требуется по ТЗ

 

А это мой бонус. Как получить любое слово из строки с пробелами в качестве разделителя? Да вот так. Кстати, можно задать любой разделитель, но речь, сейчас, не об этом.

local word = 0 -- индекс слова, которое надо получить. Начинается с 0, а не с 1
local var = line: gsub('%w*%s?*','',word):match('(%S*)')
Вычисление длины строки

local var = line and line:len()
Примечание: здесь просто проверка на «дурака», чтобы избежать вылета.

 

Выделение подстроки по индексам

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

 

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

Примечание: здесь простое дублирование системного метода. Эквивалентно следующему:

local var1, var2 = line:find(substring)
Содержит ли строка данную подстроку.

Примечание: простое дублирование системного метода. Все проверки просто бессмысленны, т.к. реализуются системой. Эквивалентно следующему:

local var = (line:find(substr)) and true or false
Заменяет первое вхождение old в строке line на new

local var = line:gsub(old,new,1) or line
Заменяет все вхождения old в строке line на new

local var = line:gsub(old,new) or line
Проверяет, начинается ли строка с данной подстроки

local var = line:sub(1,substring:len()) == substring
Проверяет, заканчивается ли срока line последовательностью substring

local var = line:sub(-substring:len()) == substring
Проверяет, является ли строка пустой. true, если пустая, false иначе.

local var = line:len() == 0
Всё остальное не требует ни цитирования, ни комментариев, ни даже внимания, т.к. является простым дублированием системных методов, которые просто втихую съедают ресурсы.

 

Вот такое моё мнение про этот пакет «Служебные функции для удобной работы со строками»

 

Изменено пользователем Kirgudu
Ссылка на комментарий

 

 

Да я, собственно, переделываю скрипт Vergas-a, который должен будить ГГ перед выбросом.
Вернусь к этой теме. Сразу добавлю, что у меня ещё AtmosFear 3 прикручен. В процессе тестирования получившейся функции выяснилось, что промежуток между выбросами может динамически меняться, почему-то. Этот самый промежуток вычисляется и хранится в surge_manager в переменной self._delta.

А вычисляется он в функцях CSurgeManager:initialize, CSurgeManager:new_surge_time, CSurgeManager:update плюс ещё сохраняется и считывается из сохранения. В CSurgeManager:initialize и CSurgeManager:new_surge_time Всё понятно и прозрачно. А вот в CSurgeManager:update есть интересный кусок, назначение которого я не очень хорошо понимаю.

if(self.time_forwarded) then
	local diff = math.abs(self._delta - g_time:diffSec(self.last_surge_time))
	if(diff<3600) then
		self._delta = 3*3600+g_time:diffSec(self.last_surge_time)
	end
	self.time_forwarded = false
end

Этот код, в точно таком же виде, есть и в оригинале ЗП, только там ещё и вывод в лог кой какой инфы идёт.

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

Зачем???


В общем, обошёл этот момент, путём закомменчивания в ui_sleep_dialog в функции dream_callback строчки surge_manager.get_surge_manager().time_forwarded = true. Да, теперь выброс вполне может случиться сразу после того, как ГГ проснётся.


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


Вот такая конструкция получилась

 

function dream_calculation()
	local delta_surge = surge_manager.get_delta()
	local past_seconds_su = surge_manager.get_last_surge_time()
	local delta_su = delta_surge - past_seconds_su

	local delta_psi_storm = psi_storm_manager.get_delta()
	local past_seconds_ps = psi_storm_manager.get_last_psi_storm_time()
	local delta_ps = delta_psi_storm - past_seconds_ps

	if delta_ps < delta_su then
		local delta = delta_ps
	else
		local delta = delta_su
	end

	local delta_h = math.floor(delta/3600)
	local delta_m = math.floor(math.fmod(delta,3600)/60)
	return delta_h, delta_m
end

 

Добавил в psi_storm_manager по аналогии с surge_manager переменную и функции.

А эта функция должна выдать оставшееся до выброса или пси-выброса, если он ближе, время в часах и минутах.

  • Нравится 1

Шаман - СисАдмин

Всяко-разно: для ЧН

Ссылка на комментарий

@Romz, я правильно понимаю, что здесь ты вычисляешь время, оставшееся до ближайшего из двух типов выброса?

В целом нормально, но оптимизировать есть куда. Например:

function dream_calculation()
	local delta_su = surge_manager.get_delta() - surge_manager.get_last_surge_time()
	local delta_ps = psi_storm_manager.get_delta() - psi_storm_manager.get_last_psi_storm_time()

	local delta_h, delta_m = math.modf(math.min(delta_ps, delta_su)/3600)
	delta_m = math.floor(delta_m*60)

	return delta_h, delta_m
end
Ну и отсюда очевидно, что лучше не дёргать 2 раза подряд один и тот же менеджер, а вычислять соотв. дельту прямо в нём, в дополнительной функции, передавая наружу уже результат.
  • Нравится 1
Ссылка на комментарий

 

 

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

 

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

А вот math.modf, он разве не целую и десятичную часть возвращает? Потому что десятичная часть - это же миллисекунды, вроде. И тогда их на 3600 умножать надо, чтоб минуты получились. Мож ошибаюсь, конечно. Математика - не мой конёк совершенно... :)

Шаман - СисАдмин

Всяко-разно: для ЧН

Ссылка на комментарий

@Romz, странная функция dream_calculation. По идее же так должно быть:

function dream_calculation()
  local now = level.get_game_time()
  local next_surge = surge_manager.get_last_surge_time() + surge_manager.get_delta()
  local next_psi = psi_storm_manager.get_last_psi_storm_time() + psi_storm_manager.get_delta()
  local delta = min(next_surge:diffSec(now), next_psi:diffSec(now))
  return delta...
end
  • Нравится 1
  • Полезно 1
Ссылка на комментарий

@abramcumner, вот кстати да. Но и в твоём примере не всё гладко. Если get_last_surge_time возвращает объект CTime, а get_delta - секунды, то...

Ссылка на комментарий
get_last_surge_time

Не, я переписал функцию, она время в секундах возвращает.

странная функция dream_calculation.

Ну это функция от @Vergas-а. Я её и переписываю, в меру своих познаний.

Возвращает целую и дробную части, но уже после деления на 3600

А, точно. Торможу :)

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

Шаман - СисАдмин

Всяко-разно: для ЧН

Ссылка на комментарий

 

 

А вот math.modf, он разве не целую и десятичную часть возвращает? Потому что десятичная часть - это же миллисекунды, вроде. И тогда их на 3600 умножать надо, чтоб минуты получились.

Возвращает целую и дробную части, но уже после деления на 3600, то есть и то и другое - в часах. Поэтому для получения минут дробную часть надо умножать на 60.


 

 

Не, я переписал функцию, она время в секундах возвращает.

Тогда бери мой пример. Вариант @abramcumner относится к CTime.

  • Спасибо 1
Ссылка на комментарий

@abramcumner, А почему складываем время, прошедшее с последнего выброса, с временем между выбросами? И как из этого получилось время до следующего выброса?


 

 

Вариант abramcumner относится к CTime.
Ааа, понятно тогда Изменено пользователем Romz

Шаман - СисАдмин

Всяко-разно: для ЧН

Ссылка на комментарий

@abramcumner, можно я отвечу? :)
@Romz, где ты такое увидел? Сначала сложением вычисляется время следующего выброса (двух), потом разница между ними и текущим временем (next_surge:diffSec(now)), потом берётся минимальная разница. Она и возвращается, как оставшееся время до ближайшего выброса.

Ссылка на комментарий

@Kirgudu, Не, ну из приведённых мной кусков кода, может быть, конечно, не очень понятно, что и как, но у меня get_last_*_time возвращает, в секундах, время, прошедшее со времени окончания предыдущего выброса. А get_delta - возвращает текущий промежуток между выбросами. Тоже в секундах.

@Kirgudu, С секундами проще. Промежуток между выбросами - максимум четверо суток (игровых). CTime для моих задач, ИМХО, черезмерно.

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

Шаман - СисАдмин

Всяко-разно: для ЧН

Ссылка на комментарий

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

Либо, если делать через CTime, у тебя уже есть время последнего выброса (self.last_surge_time)), а время до следующего можно получить так (при учёте того, что self._delta - в минутах):

local delta_ctime = game.CTime()
delta_ctime:setHMS(0,self._delta,0)

И тогда, вернув оба объекта, можно воспользоваться функцией, которую привёл @abramcumner. Только потом не забудь перевести его результат delta (который тоже CTime) в часы и минуты:

return delta:timeToString(game.CTime.TimeToMinutes)

И получишь сразу строку вида "hh:mm".

Изменено пользователем Kirgudu
  • Полезно 1
Ссылка на комментарий

Не обращайте внимания на мой пост - только запутал.
В ЗП CSurgeManager self.last_surge_time - это CTime, вот я и подумал, что get_last_surge_time() возвращает его же. Однако такого метода в ЗП нет. И он похоже возвращает совсем не CTime :blush:

Изменено пользователем abramcumner
Ссылка на комментарий

 

 

Не обращайте внимания на мой пост - только запутал.
Не, почему же. В свете того, что работает с CTime, может кому-то ещё пригодится.

Шаман - СисАдмин

Всяко-разно: для ЧН

Ссылка на комментарий

Добрый вечер.

 

Я пытаюсь добавить в Зов Припяти мины, которые мог бы устанавливать игрок. Мины должны реагировать на монстров и сталкеров.  Базовый элемент мины - объект класса inventory_box. Спавнится объект под ногами игрока при юзании бустера (мины) в инвентаре ГГ.

 

Логика мины

 

 

 

[logic]
active = ph_idle

[ph_idle]
nonscript_usable = false
on_info = {=dist_to_actor_ge(45)}
ph_idle@1

[ph_idle@1]
nonscript_usable = false
on_game_timer = 30 | ph_idle@2

[ph_idle@2]
nonscript_usable = false
on_info = ph_idle@1
%=mine_control%

 

 

 

Функция подрыва

 

 

function mine_control(obj1,obj)
for i = 1,65534 do
  local obj1 =
alife():object(i)
  if obj1 then
   if (obj1==IsStalker or
obj1==IsMonster) then
    if distance_between(obj1,obj)<15
then
    
alife():create("remote_explosive_bomb_1",vector():set(obj:position()),obj:level_vertex_id(),obj:game_vertex_id())
    
level.add_call(
    
function()
      if
get_story_object("remote_explosive_bomb_1") ~= nil
then
       return
true
      end
    
end,
     function()
     
expl_obj =
get_story_object("remote_explosive_bomb_1")
     
expl_obj:explode(0)
     end
    
)
     local sobj =
alife():object(obj:id())
     if sobj
then
     
alife():release(sobj,true)
     end
   
end
   end
  end
end
end

 

 

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

 

Где я допустил ошибку?

Ссылка на комментарий

Еще в ТЧ разрабы "зачем-то" делали мины на классе аномалий. а не ящиков...

for i = 1,65534 do в функции, которая должна вызываться на апдейте чтоб давать нормальный результат - отбивает всякое желание разбираться, почему она не вызывается. Не вызывается, и слава богу :)

 

К знатокам созрел вопрос...

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

И периодически при вот этой операции, а именно - при записи измененного нетпакета в объект, происходил краш игры. т.е. так называемый "безлоговый" вылет.

Замечу что происходило это не всегда. А достаточно редко. Путем раскопок выяснил следующее: если в подтаблице upd нетпакета, есть подтаблица quaternion - то любая попытка записи этого нетпакета тому же объекту, с которого он получен, приводит к вылету, даже если в пакете вообще ничего не меняли. Если в прочитанном пакете upd.quaternion отсутствует, то все работает нормально.

 

Я соорудил такую конструкцию:

local spawn_ex_settings = {}

function spawn_ex_transfer(sect, keys_packet)
    local f_name = "spawn_ex_transfer"
    local args = {sect, keys_packet}
    log_tool(f_name, args)
        spawn_ex_settings["active"] = true
        spawn_ex_settings["attempt"] = 0
        spawn_ex_settings["keys_packet"] = keys_packet
    local coords = mag_ref_support.get_access_position()
    local sobj = alife():create(sect, coords[1], coords[2], coords[3])
    bind_slot_item.add_to_wait(sobj.id)
    --local pc = m_net_utils.Get_NetPacket(sobj, 2)
        spawn_ex_settings["id"] = sobj.id
    --    spawn_ex_settings.pc = pc
    alife():set_switch_online(sobj.id, false)
    alife():set_switch_offline(sobj.id, true)    
    log_tool(f_name, "running")
        mag_ref_support.ExSpawning_process()
    
end

function ExSpawning_process()
    if spawn_ex_settings["active"] ~= true then return true end
    spawn_ex_settings["attempt"] = spawn_ex_settings["attempt"] + 1
    log_tool("ExSpawning in progress:", spawn_ex_settings)
    local sobj = alife():object(spawn_ex_settings["id"])
    if not sobj then abort("mag_ref_support.script: ExSpawning_process ~~~ Error") end
    local pc = m_net_utils.Get_NetPacket(sobj, 2)
    if not pc then return true end --- повторим после
    if pc.upd and pc.upd.quaternion then return true end --- повторим после
        for k, v in pairs(spawn_ex_settings["keys_packet"]) do
            pc[k] = v
            if pc.upd ~= nil and pc.upd[k] ~= nil then
                if k == 'condition' then
                    pc.upd[k] = round(v * 255)
                else
                    pc.upd[k] = v
                end
            end
        end
        m_net_utils.Set_NetPacket(sobj, pc)
        bind_slot_item.add_to_wait(sobj.id)
                alife():set_switch_online(sobj.id, true)
                alife():set_switch_offline(sobj.id, false)
        spawn_ex_settings["active"] = false
        spawn_ex_settings["keys_packet"] = nil
        spawn_ex_settings["id"] = nil
        spawn_ex_settings["attempt"] = nil
        log_tool("ExSpawning_process", "complete!")
end

 

 

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

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

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

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

Поиск дал лишь пару упоминаний на этом форуме - Скриптование, страница 147, и справочник по функциям и классам, страницы 16/17. Из находящихся там постов явно видно, что есть люди разбирающиеся в данном вопросе, но совершенно не ясно как решить поставленную проблему.

  • Нравится 1

Мод, где не бывает одинаковых путей - Судьба Зоны. (Лучшее, что у меня получилось на X-Ray) На базе модифицированного движка OGSR Engine.

Бывший мододел на X-Ray / Начинающий игродел на Unreal Engine. Программист.

AMD Ryzen 9 7950X (16 ядер, 5.7ГГц); RTX 3080; 128 ГБ DDR5; Arctic Liquid Freezer II-420; 3 ТБ SSD PCIe 4.0; 4ТБ HDD.

Ссылка на комментарий

@Zander_driver, судя по всему, ты пользуешься старым m_net_utils.

upd.quaternion в m_net_utils --> upd.ph_rotation в m_netpk.

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

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

Изменено пользователем Kirgudu
  • Нравится 1
Ссылка на комментарий

Вот что я думаю по этому поводу

 

Составлял не я, не имея опыта в луа, опирался на уже написанное, используя и изменяя в своих целях.

Про "проверки на пустоту"- имел ввиду, что-то вроде "здесь просто проверка на «дурака», чтобы избежать вылета"..

Спасибо за развёрнутый коммент:)

Изменено пользователем FonSwong
Ссылка на комментарий

Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

Создать аккаунт

Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!

Зарегистрировать новый аккаунт

Войти

Есть аккаунт? Войти.

Войти
  • Недавно просматривали   0 пользователей

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

AMK-Team.ru

×
×
  • Создать...