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

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

Здравствуйте! Сегодня решил попробовать свои силы в создании модов для Сталкера, так как это моя любимая игра. Когда-то раньше пробовал, но почему-то забросил...

Я в основном занимаюсь Web-программированием, а сегодня попробовал скрипты, минут за 20 сделал  такую штуку, как механизм событий и их обработчиков, это что-то вроде биндинга, но по-другому работает.

Принцип сейчас покажу в коде:
 


function handler()
    -- Тут всякий код
end
function handler2()
    -- Тут всякий код
end

setHander("test_action", handler)
setHander("test_action", handler2)

setAction("test_acton")
-- Будет выполнен код из функций handler() и hander2()

Такая штука будет очень полезна, когда нужно обработать какое-то событие в моде и вся эта система должна содержаться в порядке.
Вызывать события можно вообще любые:
setAction("bla-bla-bla"). Если нет обработчиков, то ничего не произойдёт, но ошибок не будет

Регистрировать обработчики можно в любом месте любого скрипта, будет работать.

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

В общем система очень простая и полезная, можно много применений найти. Её можно использовать в любой версии Сталкера, так как она использует только возможности языка Lua

Также в приведённом коде есть ещё некоторые функции, они может и не самые полезные, но их тоже можно использовать.

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

 

Мог объяснить не очень понятно, просто спать уже хочу... Критикуйте) буду только рад

Как вам моя идея? Есть ли у неё перспективы?
Если есть предложения, чего бы ещё сделать, предлагайте)
 

 

 

--[[
MOD TOOLS 0.0.1
Инструменты для облегчения модификаций

Разработчик: Николай1
]]

-- Массив с обработчиками
hooks = {}

--[[
Вызов события. Вызов происходит корректно и не вызывает ошибок, даже если не было зарегистрировано
обработчиков для этого события. В таком случае функция возвращает false

action_name [String] - название события

Пример:
setAction("test_action")
]]
function setAction(action_name)
    if hooks[action_name] == nil then
        return false
    end
    
    for action,hook in pairs(hooks[action_name]) do
        hook()
    end
end

--[[
Добавление обработчика к событию.
Можно добавлять много обработчиков для одного события

action_name [String] - название события
handler [Callback] - функция-обаботчик

Пример:
function handler()
    -- Тут всякий код
end
function handler2()
    -- Тут всякий код
end

setHander("test_action", handler)
setHander("test_action", handler2)
]]
function setHandler(action_name, handler)
    if hooks[action_name] == nil then
        hooks[action_name] = {}
    end
    
    table.insert(hooks[action_name], handler)
end

--[[
Эта функция лишняя, её использовал для теста механизма событий
]]
function route()
    setAction("test")
end

-- Создать предмет под ногами игрока
-- [String] item - код предмета, например medkit
-- [Int] count - количиство предметов
function spawnNearActor(item, count)
    if count == nil then
        alife():create(item, db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id())
    else
        for i = 1, count do
            alife():create(item, db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id())
        end
    end
end

-- Создать предмет в инвентаре игрока
-- [String] item - код предмета, например medkit
function addToActorInventory(item, count)
    addToInventory(item, db.actor:id(), db.actor:position(), db.actor:game_vertex_id(), count);
end

-- Создать предмет в инвентаре
-- [String] item - код предмета, например medkit
-- [Int] character_id - ID персонажа, которому нужно создать предмет
-- [Vector] position - координаты добавления объекта
-- [Int] game_vertex_id - game_vertex_id для создания предмета
-- [Int] count - количиство предметов
function addToInventory(item, character_id, position, game_vertex_id, count)
    if count == nil or count == 1 then
        alife():create(item, position, 1, game_vertex_id, character_id)
    else
        for i = 1, count do
            alife():create(item, position, 1, game_vertex_id, character_id)
        end
    end
end

-- Сохранить значение переменной
-- [String] variable_name - название переменной
-- [Mixed] value - значение переменной
function setVar(variable_name, value)
    xr_logic.pstor_store(db.actor, variable_name, value)
end

-- Получить значение переменной
-- [String] variable_name - название переменной
-- [Mixed] default_value - значение по-умолчанию, значение переменной, которое будет возвращено,
-- если переменной не существует
function getVar(variable_name, default_value)
    return xr_logic.pstor_retrieve(db.actor, variable_name, default_value)
end

--[[
Действия с переменной с помощью одной функции

variable_name [String] - название переменной
value [Mixed] - значение переменной (или значение по-умолчанию)
is_set [Bool] - флаг создания переменной. Если стоит 1 - будет использовано действие
"Сохранить значение переменной"

Примеры использования:

Получение значения переменной:
var("test_var", false) -- Второй аргумент - значение по-умолчанию
var("test_var", false, false) -- Третив аргумент - отключение флага установки переменной

Установка значения переменной
var("test_var", "test_value", true)
]]
function var(variable_name, value, is_set)
    -- Если is_set не задан, ставим false
    if (is_set == nil) then
        is_set = false
    end

    if (is_set == false) then
        -- Если is_set равен 0 или false, то получаем значение переменной
        return _getVar(variable_name, value)
    else
        -- Если стоит флаг создания, то устанавливаем значение переменной
        return _setVar(variable_name, value)
    end
end

--[[
Выполнить какую-либо функцию заданное число раз
]]
--[[
function runFunction(func, count, ...)
    if count == nil or count = 1 then
        func(arg)
    else
        for i = 1, count do
            func(arg)
        end
    end
end
]]

 

 

Добавлено Dennis_Chikin,

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

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

Как вам моя идея? Есть ли у неё перспективы?

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

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

 

1. Убило название функции для срабатывания события - setAction. Установить действие? Иногда называют такие методы fire или fireEvent, но не setAction.

2. Нет передачи аргументов в хендлеры.

3. Нет прерывания срабатывания событий (в цикле).

4. Симпатичное комментирование кода, конкретно описание аргументов + тип.

ТЧ 1.0004. SAP и Trans mod

github

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

@Николай1, начинание благое, жаль только припозднилось года этак на 2-3.
Но если удастся предложить что-то действительно новое и полезное - всем будет только хорошо.

Для начала рекомендую изучить аналогичные решения, уже предложенные другими авторами, коих даже больше, чем указал @Desertir.

Можно сделать общий поиск по форуму слова «event». Или поискать отдельные варианты: "Песочница" от @xStream, слот-сигнальная система от @Malandrinus, были ещё частные реализации от @Artos и, кажется, @Monnoroch и @Viнt@rь.

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

Кто объяснит, как в цикле for отловить последнюю итерацию?

 

function take_money(pid)


local cond = 5000
local col = 0
local count = db.actor:object_count() - 1
if has_alife_info("ui_car_body") then
for i=0, count do
local item = db.actor:object(i)
if item:section() == "pack_money" then
local sobj = alife():object(item:id())
if sobj then
col = col + 1
db.actor:give_money(cond)
alife():release(sobj,true)
end
end
end
end
end

 

 

Нужно вывести итоговое значение переменной col. Пробовал проверку сделать

 

if i=count then

.

.

.

end

 

так вылетает.

Сталкер - наше всё!

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

@AndrewMor, как минимум "if i==count then", чтобы не вылетало.

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

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

 

 

как минимум "if i==count then", чтобы не вылетало.

Блин, ну конечно. Привычка, что "=" - это сравнение. Спасибо.


Kirgudu, переписал функцию так:

function take_money(pid)
local cond = 5000
local col = 0
local count = db.actor:object_count() - 1
if has_alife_info("ui_car_body") then
for i=0, count do
local item = db.actor:object(i)
if item:section() == "pack_money" then
local sobj = alife():object(item:id())
if sobj then
col = col + 1
alife():release(sobj,true)
end
end
if i == count and col ~= 0 then
cond = cond * col
db.actor:give_money(cond)
local text = "Найдено денег - "..cond
news_manager.send_tip(db.actor, cond, nil, nil, 30000)
end
end
end
end

 

но сообщение выводится не один раз, как задумывал, а столько раз, сколько нужного предмета попало в рюкзак. А если перед закрывающим функцию end вывод вписать, то вообще ничего не выводится. Почему?

Сталкер - наше всё!

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

@Kirgudu, А всё равно выводится не одно сообщение, а несколько. Я так понимаю, что биндер отслеживает получение первого предмета и идет подсчет в цикле. Выводится сообщение. Далее отслеживается получение предмета номер 2, опять цикл и вывод сообщения. Ну и так далее. А как бы мне сделать, чтобы выводилось только одно, итоговое сообщение?

Сталкер - наше всё!

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

@Kirgudu, при взятии денег из схрона прибавлять их к общим деньгам ГГ, взятые деньги из рюкзака долой, а потом выводить сообщение о полученной сумме целиком. Эта моя функция вызывается из биндера on_take_item.

Сталкер - наше всё!

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

@AndrewMor, как-то так лучше сделать:

function take_money(obj)
if obj:section() == 'money_500' then
alife():release(alife:object(obj.fov and obj:id() or obj.id))
db.actor:give_money(500)
news_manager.send_tip(db.actor, "Получено 500 рублей.", nil, nil, 10000)
end
end

При желании рандомизировать, циклы тут точно не нужны, лишняя нагрузка.

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

"при взятии денег из схрона прибавлять их к общим деньгам ГГ"

 

Если деньги - это предмет, то вешаем вызов на on_take_item_from_box().

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

@Dennis_Chikin, лучше на обретение. Какая разница откуда ты их получил если алгоритм для всего один и тот же? Будь-то труп, тайник или любой другой способ.

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

Ребята, помощь нужна.

Мне нужно заспавнить physic_destroyable_object. Через all.spawn всё красиво, но есть одно "НО" - не работает функция [spawner]. Может быть через скрипт как-то можно его спавнить, а то я что-то не догоняю... 

PS Всем мир!

Пишу мод с неповторимым сюжетом.

Нужны "модельеры"

Заинтересованным писать в ЛС

J.A.A..gif

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

 

 

Мне нужно заспавнить physic_destroyable_object. Через all.spawn всё красиво, но есть одно "НО" - не работает функция [spawner].

Насколько я помню, секция spawner работает только для НПС и монстров.

Сталкер - наше всё!

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

Всем привет, опять я со своей проблемой по деньгам.

В bind.stalker.script в биндере take_item_from_box пишу: money.take_money(item)

 

Мой скрипт:

function take_money(pid)


db.actor:iterate_inventory(is_item, db.actor)
end

function is_item(npc, item)
local cond = math.ceil(math.random(28,70)*100)
local section = item:section()
if item:section() == "pack_money" then
local sobj = alife():object(item:id())
if sobj then
alife():release(sobj,true)
db.actor:give_money(cond)
local text = "Найдено денег - "..cond.."\\nВсего денег - "..db.actor:money()
news_manager.send_tip(db.actor, text, nil, nil, 30000)
end
end
end

 

 

Или так:

function take_money(pid)
local cond = math.ceil(math.random(28,70)*100)
local count = db.actor:object_count() - 1
for i=0, count do
local item = db.actor:object(i)
if item:section() == "pack_money" then
local sobj = alife():object(item:id())
if sobj then
db.actor:give_money(cond)
local text = "Найдено денег - "..cond.."\\nВсего денег - "..db.actor:money()
news_manager.send_tip(db.actor, text, nil, nil, 30000)
alife():release(sobj,true)
end
end
end
end

 

 

Результат один и тот же: первый предмет, берущийся из ящика, не обрабатывается. Остается в рюкзаке. Соответственно, если в ящике 8 предметов, то обрабатываются только 7, один остается в инвентаре. Почему?

Сталкер - наше всё!

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

@AndrewMor, зачем ты спрашиваешь? Я тебе сказал как нужно сделать, но ты упорно продолжаешь делать по своему. Ты понимаешь как работает коллбек? В общем, как говорится, "если не можешь - значит не нужно"(с).

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

@Карлан, Я делал, как ты посоветовал, и все равно не работает.

 

PS: ты бы лучше объяснил, что где не так. В общем, спасибо за "помощь".


Карлан, по твоему совету при обыске трупа вылет:

Expression : fatal error
Function : CScriptEngine::lua_error
File : E:\stalker\sources\trunk\xr_3da\xrGame\script_engine.cpp
Line : 73
Description :
Arguments : LUA error: ...blishing\s.t.a.l.k.e.r\gamedata\scripts\money.script:39: attempt to index global 'alife' (a function value)

Сталкер - наше всё!

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

function take_money(pid)

db.actor:iterate_inventory(is_item, db.actor)

end

 

Жуть какая...

Собственно, то, что получаем - это и есть искомый предмет. Все. Получаем id, получаем объект, удаляем.

Больше ничего не надо. Ни каких итераций.

 

Единственный момент - после того, как удалили, ни каких действий в :on_что-попало() быть не должно. Собственно, я в статье про использование именно по-этому и сделал выбор нужной функции по таблице. Ну и продолжение трэда можно в курилке посмотреть.

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

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

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

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

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

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

Войти

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

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

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