Murarius 10 579 Опубликовано 11 Ноября 2014 Денис, смотри. Условно разделил на три части. Правильно ли, что на три? Правильно ли вообще разделил? К остальным академикам вопрос: не хотите ли что-то добавить в рамках темы? Напомню, Денис рассматривает коллбэки получения и потери предметов, take_item_from_box(), on_trade(), on_item_take(), как узнать куда положили или кому передали предмет. Если по теме имеется что добавить - пишите, пожалуйста. Исторически сложилось, что разные события, случающиеся с актором, обрабатываются в bind_stalker скрипт. Опять же, исторически, в нем есть несколько коллбэков, отвечающих за получение и потерю предметов: function actor_binder:take_item_from_box( box, item ) function actor_binder:on_trade( item, sell_bye, money ) function actor_binder:on_item_take( item ) function actor_binder:on_item_drop( item ) function actor_binder:on_use_object( item ) По идее, на этом статью можно было бы и закончить, поскольку все вполне прозрачно из названий функций, но для тех, кому лень смотреть скрипты, добавлю, что ставятся они вот как-то так: function actor_binder:reinit() log( "info", "reinit" ) object_binder.reinit( self ) pstor = {} st = { ["pstor"] = pstor } self.st = st db.storage[0] = st self.next_restrictors_update_time = -10000 actor:set_callback( callback.inventory_info, self.info_callback, self ) actor:set_callback( callback.article_info, self.article_callback, self ) actor:set_callback( callback.on_item_take, self.on_item_take, self ) actor:set_callback( callback.on_item_drop, self.on_item_drop, self ) actor:set_callback( callback.trade_sell_buy_item,self.on_trade, self ) actor:set_callback( callback.task_state, self.task_callback, self ) actor:set_callback( callback.level_border_enter,self.level_border_enter,self ) actor:set_callback( callback.level_border_exit, self.level_border_exit, self ) actor:set_callback( callback.take_item_from_box,self.take_item_from_box,self ) actor:set_callback( callback.use_object, self.on_use_object, self ) actor:set_callback( callback.death, self.death_callback, self ) log( "info", "reinit, done" ) end Здесь и далее приводимые куски кода пали жертвой рефакторинга, и у вас это может быть иначе. Снимаются колбэки так: function actor_binder:net_destroy() local log_level = smart_terrain.get_log_level() or 100 if log_level < 20 and log_level >= 2 then smart_terrain.disable_log() if se_stalker.disable_log then se_stalker.disable_log() end if se_monster.disable_log then se_monster.disable_log() end xr_gulag.disable_log() end local remove_from_ranking = actor_stats.remove_from_ranking if remove_from_ranking then remove_from_ranking( 0 ) end sr_light.clean_up() actor:set_callback( callback.inventory_info ) -- чистим колл-бэки ( устанавливаются в nil ) actor:set_callback( callback.article_info ) actor:set_callback( callback.on_item_take ) actor:set_callback( callback.on_item_drop ) actor:set_callback( callback.trade_sell_buy_item ) actor:set_callback( callback.task_state ) actor:set_callback( callback.level_border_enter ) actor:set_callback( callback.level_border_exit ) actor:set_callback( callback.take_item_from_box ) actor:set_callback( callback.use_object ) actor:set_callback( callback.death ) if psy_antenna then psy_antenna:destroy() sr_psy_antenna.psy_antenna = false end xr_sound.stop_all_sound_object() db.del_actor( actor ) _G.actor = nil object_binder.net_destroy( self ) end - надо это, чтобы игра не вылетала при уничтожении объекта (в данном случае - при завершении/перезагрузке). Далее будет в порядке графомании расписано, что с этими коллбэками вообще можно делать, а заодно - как их стоило бы переписать для вящего удобства и чтобы перестать уже насиловать процессор. Да, в случае очистки коллбэков магическое nil, типа actor:set_callback( callback.article_info, nil ) вроде не требуется. По крайней мере, я никаких негативных эффектов пока не увидел. Но, возможно, ошибаюсь, и эффекты просто пока не вылезли. А вообще за код вида "== nil" и "~= nil" надо давать сразу 35 лет строгого расстрела. Вот чтоб каждое утро в 6 часов, невзирая на погоду, выводили и расстреливали, поскольку требуется явно определять переменные и явно возвращать из вызываемых функций это самый nil Почему здесь везде actor а не db.actor? Потому что код пал жертвой рефакторинга, и актор определяется раз и навсегда в глобальном пространстве: function actor_binder:__init( npc ) super( npc ) _G.actor = self.object db.add_actor( actor ) self.is_saved = false end *** function actor_binder:take_item_from_box( box, item ) function actor_binder:on_trade( item, sell_bye, money ) function actor_binder:on_item_take( item ) С первым все понятно. Срабатывает при взятии предметов из ящиков. На входе - объект ящика и объект собственно предмета. Кстати, если сюда прицепить телепортацию актора на другой конец локации - ящик уйдет в офлайн, и объект станет невалидным. Со всеми вытекающими. Впрочем, знаменитый вылет с буквами e случается, по-моему, еще раньше. Да, если при этом уничтожать, скажем, взятую из ящика гранату, будет примерно то же самое. И вообще, прежде чем делать что-либо дальше с ящиком или предметом - закройте ящик через level.hide_indicators() или подождите, пока игрок его сам закроет. Что же с ней можно сделать еще - это переписать простыни о 100500 строк всяких странных if ... then ... и вызовы 100500 скриптов, которые в нее обычно суют, следующим образом: local t_takebox, t_takebox_sid, t_takebox_any = {}, {}, {} function add_on_take_box( f, sect ) -- ( функция, true - проверка на sid ящика ) if sect then if sect == true then table_insert( t_takebox_sid, f ) elseif t_takebox[sect] then table_insert( t_takebox[sect], f ) else t_takebox[sect] = { f } end else table_insert( t_takebox_any, f ) end end function actor_binder:take_item_from_box( box, item ) local sid = box:story_id() if sid then -- dc: а что, nil бывает ? for i, v in ipairs( t_takebox_sid ) do v( box, item ) end end if item and t_takebox[item:section()] then for i, v in ipairs( t_takebox[item:section()] ) do v( box, item ) end end for i, v in ipairs( t_takebox_any ) do v( box, item ) end if level.map_has_object_spot( box:id(), "crlc_big" ) ~= 0 then -- amk.on_item_take_from_box level.map_remove_object_spot( box:id(), "crlc_big" ) end end - и в табличку помещать ваши сиды для ящиков, секции шмоток и функции, которые нужно дергать. Можно делать руками, а можно - из самих подключаемых скриптов. Впрочем, про это будет совсем другая тема. on_trade() - обычно здесь бывает что-то типа function actor_binder:on_trade( item, sell_bye, money ) if sell_bye then game_stats.money_trade_update( money ) else game_stats.money_trade_update( -money ) end end, и в общем-то больше ничего не нужно. Но если, например, переписывать "квесты" типа "принеси мне 100500 AK-100, а я дам тебе за это фантик от конфеты - можешь его облизать" - эта функция нам пригодится. А вот про on_item_take() - отдельная телега. on_item_take() вызывается, когда какой-либо предмет появляется в инвентаре актора. "Появляется" - здесь - ключевое слово. То есть, неважно как. То есть, первый вызов случается тогда, когда у загрузившегося актора (а он грузится всегда первым) предметы инвентаря начинают входить в онлайн. Извращение, конечно, зато мы можем составить свою табличку предметов, вместо медленного и печального перебора инвентаря. Хотя по разным причинам на практике так никто не делает. Зато, имеем медленную и печальную загрузку, когда игрок привык таскать на себе по полтонны хлама, а аффтар мода, традиционно, повесил на этот вызов 100500 if ... then ... Переписывавем традиционно: local on_take_t, on_take_any, on_take_n = {}, {}, 0 function add_on_take( f, sect ) -- ( функция, nil ) - для любых секций, либо ( f, секция ) if sect then local t = on_take_t[sect] if t then table_insert( t, f ) else on_take_t[sect] = { f } end else on_take_n = on_take_n + 1; on_take_any[on_take_n] = f end end function actor_binder:on_item_take( item ) -- log( "info", "on_item_take" ) ltasks_proceed() for i = 1, on_take_n do on_take_any[i]( item ) end local t = on_take_t[item:section()] if t then for i = 1, #t do t[i]( item ) end end if level.map_has_object_spot( item:id(), "red_location" ) ~= 0 then level.map_remove_object_spot( item:id(), "red_location" ) end -- log( "info", "on_item_take, done" ) end вызов level_tasks, кстати, тоже надо отсюда убрать. *** А теперь самое вкусное. Я так до сих пор и не понял, зачем люди вешают все-все на function actor_binder:on_item_drop( item ), потом пишут секцию, или, что еще хуже, id этого item в pstor актора, а потом запускают какие-то аццкие таймеры с аццкими проверками на неизвестно что для неизвестных объектов, у которых id случайно совпало с сохраненным, когда есть function actor_binder:on_use_object( item ), срабатывающий именно на использование. Но, кстати, да, on_item_drop() при этом тоже срабатывает. А еще он срабатывает, например, на уход предмета в оффлайн, так что если коллбэк для уничтожаемого актора не обнулить при net_destroy(), результат будет немного предсказуем. С использованием все понятно. А вот как узнать, куда положили или кому передали предмет? Или просто выбросили? Или он по какой-то мистической причине просто исчез? А элементарно (если мы отслеживаем еще и использование через on_use_object(), и ведем табличку, скажем inv_used[]): local sect = item:section() -- log( "info", "on_item_lost: %s", sect ) local id = item:id() if inv_used[id] then inv_used[id] = nil -- использован -- log( "info", "on_item_lost: %s, used", sect ) else local obj = sim:object( id ) if obj then if ( obj.parent_id or 65535 ) == 65535 then -- выброшен -- log( "info", "on_item_lost: %s, drop", sect ) for i = 1, on_drop_n do on_drop_t.any[i]( item ) end if on_drop_t[sect] then for i, f in ipairs( on_drop_t[sect] ) do f( item ) end end -- else log( "info", "on_item_lost: %s, new parent: %s", sect, obj.parent_id ) end -- else log( "info", "on_item_lost: %s, deleted", sect ) end end - это кусочек менеджера инвентаря, который отслеживает выбрасывание предметов, и, в свою очередь, запускает всякие варки артефактов и прочие нанопульты. В принципе, сюда же можно запихать и всякое переодевание и перевооружение неписей, но не нужно, поскольку если про парламент, как не место для дискуссий - еще спорно, то Зона - совершенно точно - не для стриптиза. Впрочем, это совсем другая тема. А пока просто следует запомнить, что таймеры - не нужны. Кстати, вот полный скрипт менеджера, с учетом пояса, перепаковки чего попало во что угодно, и еще всяким странным: https://dl.dropboxusercontent.com/u/27871782/inv_manager.script Да, сами функции использования и потери мы традиционно переписываем так: local on_drop_t, on_drop_any, on_drop_n = {}, {}, 0 function add_on_drop( f, sect ) -- ( функция, nil ) - для любых секций, либо ( f, секция ) if sect then local t = on_drop_t[sect] if t then table_insert( t, f ) else on_drop_t[sect] = { f } end else on_drop_n = on_drop_n + 1; on_drop_any[on_drop_n] = f end end function actor_binder:on_item_drop( item ) -- log( "info", "on_item_drop" ) ltasks_proceed() for i = 1, on_drop_n do on_drop_any[i]( item ) end local t = on_drop_t[item:section()] if t then for i = 1, #t do t[i]( item ) end end -- log( "info", "on_item_drop, done" ) end local on_use_t, on_use_n = { ["any"] = {} }, 0 function add_on_use( f, sect ) -- ( функция, nil ) - для любых секций, либо ( f, секция ) if sect then local t = on_use_t[sect] if t then t[#t + 1] = f else on_use_t[sect] = { f } end else on_use_n = on_use_n + 1; on_use_t.any[on_use_n] = f end end function actor_binder:on_use_object( item ) local sect = item:section() log( "info", "on_use, item: %s", sect ) for i = 1, on_use_n do on_use_t.any[i]( item ) end if on_use_t[sect] then for i, f in ipairs( on_use_t[sect] ) do f( item ) end end end На этом, в общем-то, все. И немедленно выпил. © Литературка (избранное): "Мяу навсегда" (2025) --- "Координаты избушки" (2023) --- "Колобок времени" (2019) --- "Пиво и жлоб" (2018) --- "Лекарство против морщин" (2013) --- "Когда все пройдет" (2013) Креатив (бесперспективное): Dominanta --- Сон на земле Досвиданьице (слезное): Смена администратора (2024) Поделиться этим сообщением Ссылка на сообщение
Murarius 10 579 Опубликовано 11 Ноября 2014 Мне одному кажется, что в отредактированном варианте что-то пропало ? По-моему, ничего. Ну и удаление разбивки читабельность отнюдь не улучшает. Это о чем? Я вроде не удалял ничего. Вот редактор при копипасте иногда чудеса вытворяет с лишними строками и пробелами - это может быть... Литературка (избранное): "Мяу навсегда" (2025) --- "Координаты избушки" (2023) --- "Колобок времени" (2019) --- "Пиво и жлоб" (2018) --- "Лекарство против морщин" (2013) --- "Когда все пройдет" (2013) Креатив (бесперспективное): Dominanta --- Сон на земле Досвиданьице (слезное): Смена администратора (2024) Поделиться этим сообщением Ссылка на сообщение
Murarius 10 579 Опубликовано 11 Ноября 2014 @Desertir, не отклоняемся ли от темы? Может, в отдельную перенести? Или это здесь очень даже к месту? Литературка (избранное): "Мяу навсегда" (2025) --- "Координаты избушки" (2023) --- "Колобок времени" (2019) --- "Пиво и жлоб" (2018) --- "Лекарство против морщин" (2013) --- "Когда все пройдет" (2013) Креатив (бесперспективное): Dominanta --- Сон на земле Досвиданьице (слезное): Смена администратора (2024) Поделиться этим сообщением Ссылка на сообщение
Murarius 10 579 Опубликовано 11 Ноября 2014 Денис рассматривает коллбэки получения и потери предметов, take_item_from_box(), on_trade(), on_item_take(), как узнать куда положили или кому передали предмет. Я правильно понимаю круг вопросов? Если вписывается, то нужно добавить это в текст, если нет - то создать отдельную статейку. либо расширить круг вопросов в рамках этой статьи. Просто помните, мы говорили, что, конечно, одно цепляется за другое, но так мы никогда не остановимся? Вот этого и стоит опасаться. Литературка (избранное): "Мяу навсегда" (2025) --- "Координаты избушки" (2023) --- "Колобок времени" (2019) --- "Пиво и жлоб" (2018) --- "Лекарство против морщин" (2013) --- "Когда все пройдет" (2013) Креатив (бесперспективное): Dominanta --- Сон на земле Досвиданьице (слезное): Смена администратора (2024) Поделиться этим сообщением Ссылка на сообщение
Murarius 10 579 Опубликовано 1 Января 2015 @Dennis_Chikin ИСПОЛЬЗОВАНИЕ ПРЕДМЕТОВ АКТОРОМ Какие коллбэки отвечают за получение и потерю предметов? Как эти коллбэки ставятся и снимаются? Для чего нужны и как работают take_item_from_box(), on_trade(), on_item_take()? Как узнать, куда положили или кому передали предмет? Ответы на эти вопросы - в статье ниже. Исторически сложилось, что разные события, случающиеся с актором, обрабатываются в bind_stalker скрипт. Опять же, исторически, в нем есть несколько коллбэков, отвечающих за получение и потерю предметов: function actor_binder:take_item_from_box( box, item ) function actor_binder:on_trade( item, sell_bye, money ) function actor_binder:on_item_take( item ) function actor_binder:on_item_drop( item ) function actor_binder:on_use_object( item ) По идее, на этом статью можно было бы и закончить, поскольку все вполне прозрачно из названий функций, но для тех, кому лень смотреть скрипты, добавлю, что ставятся они вот как-то так: function actor_binder:reinit() log( "info", "reinit" ) object_binder.reinit( self ) pstor = {} st = { ["pstor"] = pstor } self.st = st db.storage[0] = st self.next_restrictors_update_time = -10000 actor:set_callback( callback.inventory_info, self.info_callback, self ) actor:set_callback( callback.article_info, self.article_callback, self ) actor:set_callback( callback.on_item_take, self.on_item_take, self ) actor:set_callback( callback.on_item_drop, self.on_item_drop, self ) actor:set_callback( callback.trade_sell_buy_item,self.on_trade, self ) actor:set_callback( callback.task_state, self.task_callback, self ) actor:set_callback( callback.level_border_enter,self.level_border_enter,self ) actor:set_callback( callback.level_border_exit, self.level_border_exit, self ) actor:set_callback( callback.take_item_from_box,self.take_item_from_box,self ) actor:set_callback( callback.use_object, self.on_use_object, self ) actor:set_callback( callback.death, self.death_callback, self ) log( "info", "reinit, done" ) end Здесь и далее приводимые куски кода пали жертвой рефакторинга, и у вас это может быть иначе. Снимаются колбэки так: function actor_binder:net_destroy() local log_level = smart_terrain.get_log_level() or 100 if log_level < 20 and log_level >= 2 then smart_terrain.disable_log() if se_stalker.disable_log then se_stalker.disable_log() end if se_monster.disable_log then se_monster.disable_log() end xr_gulag.disable_log() end local remove_from_ranking = actor_stats.remove_from_ranking if remove_from_ranking then remove_from_ranking( 0 ) end sr_light.clean_up() actor:set_callback( callback.inventory_info ) -- чистим колл-бэки ( устанавливаются в nil ) actor:set_callback( callback.article_info ) actor:set_callback( callback.on_item_take ) actor:set_callback( callback.on_item_drop ) actor:set_callback( callback.trade_sell_buy_item ) actor:set_callback( callback.task_state ) actor:set_callback( callback.level_border_enter ) actor:set_callback( callback.level_border_exit ) actor:set_callback( callback.take_item_from_box ) actor:set_callback( callback.use_object ) actor:set_callback( callback.death ) if psy_antenna then psy_antenna:destroy() sr_psy_antenna.psy_antenna = false end xr_sound.stop_all_sound_object() db.del_actor( actor ) _G.actor = nil object_binder.net_destroy( self ) end - надо это, чтобы игра не вылетала при уничтожении объекта (в данном случае - при завершении/перезагрузке). Далее будет в порядке графомании расписано, что с этими коллбэками вообще можно делать, а заодно - как их стоило бы переписать для вящего удобства и чтобы перестать уже насиловать процессор. Да, в случае очистки коллбэков магическое nil, типа actor:set_callback( callback.article_info, nil ) вроде не требуется. По крайней мере, я никаких негативных эффектов пока не увидел. Но, возможно, ошибаюсь, и эффекты просто пока не вылезли. А вообще за код вида "== nil" и "~= nil" надо давать сразу 35 лет строгого расстрела. Вот чтоб каждое утро в 6 часов, невзирая на погоду, выводили и расстреливали, поскольку требуется явно определять переменные и явно возвращать из вызываемых функций это самый nil Почему здесь везде actor а не db.actor? Потому что код пал жертвой рефакторинга, и актор определяется раз и навсегда в глобальном пространстве: function actor_binder:__init( npc ) super( npc ) _G.actor = self.object db.add_actor( actor ) self.is_saved = false end *** function actor_binder:take_item_from_box( box, item ) function actor_binder:on_trade( item, sell_bye, money ) function actor_binder:on_item_take( item ) С первым все понятно. Срабатывает при взятии предметов из ящиков. На входе - объект ящика и объект собственно предмета. Кстати, если сюда прицепить телепортацию актора на другой конец локации - ящик уйдет в офлайн, и объект станет невалидным. Со всеми вытекающими. Впрочем, знаменитый вылет с буквами e случается, по-моему, еще раньше. Да, если при этом уничтожать, скажем, взятую из ящика гранату, будет примерно то же самое. И вообще, прежде чем делать что-либо дальше с ящиком или предметом - закройте ящик через level.hide_indicators() или подождите, пока игрок его сам закроет. Что же с ней можно сделать еще - это переписать простыни о 100500 строк всяких странных if ... then ... и вызовы 100500 скриптов, которые в нее обычно суют, следующим образом: local t_takebox, t_takebox_sid, t_takebox_any = {}, {}, {} function add_on_take_box( f, sect ) -- ( функция, true - проверка на sid ящика ) if sect then if sect == true then table_insert( t_takebox_sid, f ) elseif t_takebox[sect] then table_insert( t_takebox[sect], f ) else t_takebox[sect] = { f } end else table_insert( t_takebox_any, f ) end end function actor_binder:take_item_from_box( box, item ) local sid = box:story_id() if sid then -- dc: а что, nil бывает ? for i, v in ipairs( t_takebox_sid ) do v( box, item ) end end if item and t_takebox[item:section()] then for i, v in ipairs( t_takebox[item:section()] ) do v( box, item ) end end for i, v in ipairs( t_takebox_any ) do v( box, item ) end if level.map_has_object_spot( box:id(), "crlc_big" ) ~= 0 then -- amk.on_item_take_from_box level.map_remove_object_spot( box:id(), "crlc_big" ) end end - и в табличку помещать ваши сиды для ящиков, секции шмоток и функции, которые нужно дергать. Можно делать руками, а можно - из самих подключаемых скриптов. Впрочем, про это будет совсем другая тема. on_trade() - обычно здесь бывает что-то типа function actor_binder:on_trade( item, sell_bye, money ) if sell_bye then game_stats.money_trade_update( money ) else game_stats.money_trade_update( -money ) end end, и в общем-то больше ничего не нужно. Но если, например, переписывать "квесты" типа "принеси мне 100500 AK-100, а я дам тебе за это фантик от конфеты - можешь его облизать" - эта функция нам пригодится. А вот про on_item_take() - отдельная телега. on_item_take() вызывается, когда какой-либо предмет появляется в инвентаре актора. "Появляется" - здесь - ключевое слово. То есть, неважно как. То есть, первый вызов случается тогда, когда у загрузившегося актора (а он грузится всегда первым) предметы инвентаря начинают входить в онлайн. Извращение, конечно, зато мы можем составить свою табличку предметов, вместо медленного и печального перебора инвентаря. Хотя по разным причинам на практике так никто не делает. Зато, имеем медленную и печальную загрузку, когда игрок привык таскать на себе по полтонны хлама, а аффтар мода, традиционно, повесил на этот вызов 100500 if ... then ... Переписывавем традиционно: local on_take_t, on_take_any, on_take_n = {}, {}, 0 function add_on_take( f, sect ) -- ( функция, nil ) - для любых секций, либо ( f, секция ) if sect then local t = on_take_t[sect] if t then table_insert( t, f ) else on_take_t[sect] = { f } end else on_take_n = on_take_n + 1; on_take_any[on_take_n] = f end end function actor_binder:on_item_take( item ) -- log( "info", "on_item_take" ) ltasks_proceed() for i = 1, on_take_n do on_take_any[i]( item ) end local t = on_take_t[item:section()] if t then for i = 1, #t do t[i]( item ) end end if level.map_has_object_spot( item:id(), "red_location" ) ~= 0 then level.map_remove_object_spot( item:id(), "red_location" ) end -- log( "info", "on_item_take, done" ) end вызов level_tasks, кстати, тоже надо отсюда убрать. *** А теперь самое вкусное. Я так до сих пор и не понял, зачем люди вешают все-все на function actor_binder:on_item_drop( item ), потом пишут секцию, или, что еще хуже, id этого item в pstor актора, а потом запускают какие-то аццкие таймеры с аццкими проверками на неизвестно что для неизвестных объектов, у которых id случайно совпало с сохраненным, когда есть function actor_binder:on_use_object( item ), срабатывающий именно на использование. Но, кстати, да, on_item_drop() при этом тоже срабатывает. А еще он срабатывает, например, на уход предмета в оффлайн, так что если коллбэк для уничтожаемого актора не обнулить при net_destroy(), результат будет немного предсказуем. С использованием все понятно. А вот как узнать, куда положили или кому передали предмет? Или просто выбросили? Или он по какой-то мистической причине просто исчез? А элементарно (если мы отслеживаем еще и использование через on_use_object(), и ведем табличку, скажем inv_used[]): local sect = item:section() -- log( "info", "on_item_lost: %s", sect ) local id = item:id() if inv_used[id] then inv_used[id] = nil -- использован -- log( "info", "on_item_lost: %s, used", sect ) else local obj = sim:object( id ) if obj then if ( obj.parent_id or 65535 ) == 65535 then -- выброшен -- log( "info", "on_item_lost: %s, drop", sect ) for i = 1, on_drop_n do on_drop_t.any[i]( item ) end if on_drop_t[sect] then for i, f in ipairs( on_drop_t[sect] ) do f( item ) end end -- else log( "info", "on_item_lost: %s, new parent: %s", sect, obj.parent_id ) end -- else log( "info", "on_item_lost: %s, deleted", sect ) end end - это кусочек менеджера инвентаря, который отслеживает выбрасывание предметов, и, в свою очередь, запускает всякие варки артефактов и прочие нанопульты. В принципе, сюда же можно запихать и всякое переодевание и перевооружение неписей, но не нужно, поскольку если про парламент, как не место для дискуссий - еще спорно, то Зона - совершенно точно - не для стриптиза. Впрочем, это совсем другая тема. А пока просто следует запомнить, что таймеры - не нужны. Кстати, вот полный скрипт менеджера, с учетом пояса, перепаковки чего попало во что угодно, и еще всяким странным: https://dl.dropboxusercontent.com/u/27871782/inv_manager.script Да, сами функции использования и потери мы традиционно переписываем так: local on_drop_t, on_drop_any, on_drop_n = {}, {}, 0 function add_on_drop( f, sect ) -- ( функция, nil ) - для любых секций, либо ( f, секция ) if sect then local t = on_drop_t[sect] if t then table_insert( t, f ) else on_drop_t[sect] = { f } end else on_drop_n = on_drop_n + 1; on_drop_any[on_drop_n] = f end end function actor_binder:on_item_drop( item ) -- log( "info", "on_item_drop" ) ltasks_proceed() for i = 1, on_drop_n do on_drop_any[i]( item ) end local t = on_drop_t[item:section()] if t then for i = 1, #t do t[i]( item ) end end -- log( "info", "on_item_drop, done" ) end local on_use_t, on_use_n = { ["any"] = {} }, 0 function add_on_use( f, sect ) -- ( функция, nil ) - для любых секций, либо ( f, секция ) if sect then local t = on_use_t[sect] if t then t[#t + 1] = f else on_use_t[sect] = { f } end else on_use_n = on_use_n + 1; on_use_t.any[on_use_n] = f end end function actor_binder:on_use_object( item ) local sect = item:section() log( "info", "on_use, item: %s", sect ) for i = 1, on_use_n do on_use_t.any[i]( item ) end if on_use_t[sect] then for i, f in ipairs( on_use_t[sect] ) do f( item ) end end end На этом, в общем-то, все. И немедленно выпил. © Рецензия: @lsclon В статье автор описывает весьма оригинальное использование динамических таблиц для проверок. Я бы сказала, что эта статья - хорошая с большой буквы. 1 2 Литературка (избранное): "Мяу навсегда" (2025) --- "Координаты избушки" (2023) --- "Колобок времени" (2019) --- "Пиво и жлоб" (2018) --- "Лекарство против морщин" (2013) --- "Когда все пройдет" (2013) Креатив (бесперспективное): Dominanta --- Сон на земле Досвиданьице (слезное): Смена администратора (2024) Поделиться этим сообщением Ссылка на сообщение