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

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


Svoboда

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

13 часов назад, mole venomous сказал:

почему так

Потому что db.actor:object(dropped) ищет не конкретный предмет, а первый попавшийся предмет с секцией, равной секции dropped.

Ты заметил, что после первого выкидывания остаются в инвентаре только те предметы, количество которых больше 1? Можно предположить, например, что функция drop_item удаляет предмет не одномоментно, а с некоторым запаздыванием, и когда ты ищешь следующий предмет с такой же секцией, всё время находится первый из них, который уже должен был удалиться к тому времени.

Чтобы этого избежать, можно попробовать перейти на работу с идентификаторами вместо секций:

table.insert(tActorItems,item:id()) -- запоминаем идентификатор вместо секции
db.actor:drop_item(level.object_by_id(dropped)) -- ищем объект по идентификатору

В остальном код будет идентичным.

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

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


Ссылка на сообщение
5 часов назад, Norman Eisenherz сказал:

Есть ли разница, в каком именно скрипте указан код с подменой функций?

Есть разница в том, в каком порядке загружаются и инициализируются твой скрипт и целевой.

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

Об этом. кстати, уже написано в постах по приведённым ссылкам.

  • Согласен 1

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


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

Правильный формат описан в тех постах.

Используй вызов prefetch("модуль_с_подменяемой_функцией") в _g.start_game_callback() для принудительной заблаговременной загрузки.

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

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


Ссылка на сообщение
1 час назад, Norman Eisenherz сказал:

какие дополнительные возможности дает подмена на ходу по сравнению с прямым редактированием исходных скриптов?

Редактирование исходного скрипта - постоянно, до конца игры изменённый функционал, подмена на лету - изменённый функционал автоматически откатывается к стандартному при ближайшей загрузке.

Нужно это или нет в конкретном моде, можно решить самостоятельно.

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


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

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

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

Изменено пользователем Kirgudu
  • Полезно 1

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


Ссылка на сообщение
55 минут назад, Norman Eisenherz сказал:

Откуда эта проверка вызывается и куда возвращает true/false?

См. элементы function_fail в game_tasks_by_vendor.xml
Вызывается движком.

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


Ссылка на сообщение
20.11.2022 в 00:47, Norman Eisenherz сказал:

В чем может быть проблема?

В невнимательности.

Аргумент, передаваемый в метод alife():spawn_id(), должен прописываться в all.spawn как spawn_story_id = xxx.

А story_id может использоваться в методе alife():story_object() для получения объекта, но не его spawn_id.

  • Нравится 1

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


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

Внимательно стоит читать не только документацию по игре в случае её наличия, но и посты, на которые сам же ссылаешься, например, тут: https://www.amk-team.ru/forum/topic/6138-cs-kovyryaemsya-v-faylah/?do=findComment&comment=1443031
Всё в ЧН работает, проверено личным опытом модостроя, а Malandrinus в своём посте разжевал вопрос так, что дальше некуда.

Не нужно путать spawn_id, задаваемый отдельным параметром в секции объекта в all.spawn и тот виртуальный spawn_id, который является порядковым номером этой секции объекта и который при распаковке, например, средствами acdc (с SDK не работал, но, возможно, там тоже) можно увидеть в квадратных скобках в начале секции. Поскольку при перепаковке all.spawn этот номер может измениться, единственно правильным способом получить его при исполнении будет вызов метода alife():spawn_id(spawn_story_id), где spawn_story_id, в свою очередь, также прописан отдельным параметром в секции объекта в all.spawn. Никакие spawn_id, прописанные там же отдельным параметром, не должны использоваться в данном механизме ни в ТЧ, ни в ЧН, ни в ЗП, а story_id можно использовать только для поиска уже заспавненного объенкта перед его удалением, но не для повторного спавна.

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


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

@dsh было, было и в основной ветке, видимо, дополнение признали полезным и затянули. :)
https://github.com/OGSR/OGSR-Engine/commit/544daa9962efb142b6e2b058e045846f96dc5608
Легко найти по истории, уже второй коммит с конца:
https://github.com/OGSR/OGSR-Engine/commits/main/ogsr_engine/xrGame/alife_spawn_registry.cpp

 

Офф: я впервые в ваши правки движка заглянул так-то; даже при взгляде наискосок попадаются любопытные вещи. :)
Если б ещё было время заниматься модостроем, да по-прежнему сохранялся высокий интерес... но нет.

Хватит того, что периодически могу дать ответ по легаси скриптам.

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

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


Ссылка на сообщение
1 час назад, Jekyll сказал:

local table = {1,2,3,4,5} (условно)

math.max(table) - не работает (вылет - number expected, got table)

 

local t = {1,2,3,4,5}
local m = math.max(unpack(t))
 

  • Полезно 4

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


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

Оператор #, кстати, вполне успешно вернёт длину строки, но это ничему не поможет.

Предположим, что загадочная функция gts - это game.translate_string. Предположим даже, что функция show_news действительно вызывается, но тогда непонятно, как в неё передаётся переменная item, ведь show_news объявлена за пределами on_trade, а область видимости переменной item - внутри on_trade. Скорее всего, как писали выше, передавать item в функцию таки необходимо.
Но и это ещё не всё.

local name = system_ini():r_string(item:section(), "inv_name") - здесь переменной name присвоится строка. Одна строка.

#name отработает и даст длину, math.random(#name) - случайный индекс в пределах длины строки.
Однако name[math.random(#name)] вернёт nil, поскольку получить один символ из строки посредством обращения к индексу в Lua нельзя - у символов в строках просто нет индекса. Для этого необходимо использовать функцию string.sub.

Но даже если бы удалось получить символ, то после применения загадочной local ww = gts(n) в переменную ww попадёт ровно тот же символ без всякого перевода. Ибо вряд ли в xml с текстами найдутся записи, идентификаторами которых служат одиночные символы латинского алфавита...

  • Нравится 2
  • Согласен 1

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


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

@Colder  Вот поэтому и стоит для начала сделать то, о чём писали выше:

19 часов назад, mole venomous сказал:

Вообще не понял...

Может, стоит для начала рандомно  определить оружие?

Нужно получить список имён оружия и вывести его в новости. Какого оружия, где? В слоте? В инвентаре? На уровне? Вообще любого в игре? Передаваемого покупателю/продавцу (если отталкиваться от on_trade? Если берёшь в работу один единственный предмет, который передаётся в параметр item функции on_trade, то о каких множественных строках, перебор которых ты хочешь сделать, может идти речь? И т. д.
Цель непонятна, условия непонятны, а гадать, что именно требуется сделать, - неблагодарное занятие.

Изменено пользователем Kirgudu
  • Согласен 2

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


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

Перебирает все предметы в игре (на всех локациях), помещает уникальные записи оружия в таблицу, затем выводит в новость случайное название из этой таблицы:

local weapon_names, weapon_sections, cnt, sini, al = {}, {}, 0, system_ini(), alife()
for i=1, 65535 do
	local obj = al:object(i)
	if obj and isWeapon(obj) then
		local section = obj.section_name and obj:section_name()
		if section and (not weapon_names[section]) and sini:section_exist(section) and sini:line_exist(section, "inv_name") then
			local name = sini:r_string(section, "inv_name")
			weapon_names[section] = name
			table.insert(weapon_sections, section)
			cnt = cnt + 1
		end
	end
end
if cnt > 0 then
	local news_text = "%c[ui_gray_1]"..game.translate_string(weapon_names[weapon_sections[math.random(cnt)]])
	db.actor:give_game_news(news_text, "ui\\cop\\ui_cop_iconstotal", Frect():set(415,0,83,47), 0, 5000)
end

Вот только затратная это штука благодаря перебору.

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

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

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


Ссылка на сообщение
10 часов назад, AndrewMor сказал:

Мне вот не подошел

Аналогично, но с советом целиком и полностью согласен. :)
Но я на работе настолько наедаюсь программированием (full stack от БД до фронта), что лезть и в движок Сталкера тупо не хотелось, хотя при нужде могу просто посмотреть, что там в нужном месте написано.

Для меня чистые Lua скрипты на ванильном движке - это редкий отдых. :)


@Zander_driver почему бы просто не выложить куда-нибудь в инструментарий мододела? Кому надо - найдёт и возьмёт не сейчас, так позже. Я вот тоже обязательно полюбопытствую, а может и воспользуюсь однажды.

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

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


Ссылка на сообщение
3 часа назад, phalcor сказал:

Для серверного объекта способ нашёл

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

3 часа назад, phalcor сказал:

А как тоже самое сделать для клиентского объекта

Воспользоваться аналогичным кодом, но поменять свойство character_name.

Это, однако, не поможет, поскольку имя персонажа в журнале, диалогах и при наведении курсора берётся из профиля.

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

Имхо, без редактирования движка на все 100 задачу не выполнить.

  • Спасибо 1
  • Согласен 1

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


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

@phalcor можно попробовать воспользоваться встроенной в модуль функцией автоматического перевода объекта онлайн-оффлайн-онлайн:

m_netpk.do_switchings(id)

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

  • Спасибо 1
  • Нравится 1
  • Полезно 1

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


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

@Norman Eisenherz не подтверждаю.

https://disk.yandex.ru/d/TfcsZSLsZNf9zQ
Здесь скрипт (дополнил логом в функциях init, update, load и save), лог и сейвы - до использования уже мёртвой псевдособаки, после, и после загрузки.

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

Сидоровича использовал во время первого диалога, собаку при обыске, плоть - нет. И до и после загрузки видно 2 положительных флага и один отрицательный, как ожидалось.

Возможно, разница в движках; пользуюсь купленной 15 лет назад в Стиме версией игры.

 

@imcrazyhoudini не значит, поскольку для того, чтобы отработало inherited::Load(section), необходимо, чтобы класс CScriptBinder наследовал другой, родительский класс, который в актуальной версии не указан.

Возможно, там и было что-то ранее, но вопрос возврата всего явно лежит не в плоскости данной темы.

  • Спасибо 1

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


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

@Norman Eisenherz Имхо, никак, потому что здесь как раз наследование, инициализация базового класса со всеми его инкапсулированными свойствами и т. д.

В цитируемом посте речь, к. м. к., шла о более простом варианте класса, типа такого:

class "WeatherManager"
function WeatherManager:__init()
	self.weather_change_day = 0
	self.update_time = 0
	self.update_level = ""
end
function WeatherManager:reset()
	-- ...
end
function WeatherManager:select_weather(now)
	-- ...
end

Который можно было бы переписать как:

self = {}
function reset()
	-- содержимое не меняется
end
function select_weather(now)
	-- содержимое не меняется
end
self.weather_change_day = 0
self.update_time = 0
self.update_level = ""

Но зачем вообще вопрос и желание - непонятно. Академический интерес?

  • Согласен 3

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


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

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