Murarius 10 569 Опубликовано 1 Января 2015 Поделиться Опубликовано 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 Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-904579
Dennis_Chikin 3 665 Опубликовано 8 Ноября 2014 Поделиться Опубликовано 8 Ноября 2014 (изменено) Это, разумеется, читать и использовать все равно ни кто не будет, но поскольку однотипные вопросы, задаваемые неизвестным телепатам, слегка поддостали, хоть душу отведу. Исторически сложилось, что разные события, случающиеся с актором, обрабатываются в 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 часов не взирая на погоду выводили, и расстреливали. Почему здесь везде actor а не db.actor ? По тому что код пал жертвой рефакторинга, и актор опрделяется раз и навсегда в глобальном пространстве: function actor_binder:__init( npc ) super( npc ) _G.actor = self.object db.add_actor( actor ) self.is_saved = false end Изменено 1 Января 2015 пользователем Murarius 2 1 Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/
Desertir 202 Опубликовано 8 Ноября 2014 Поделиться Опубликовано 8 Ноября 2014 А вообще за код вида "== nil" и "~= nil" надо давать сразу 35 лет строгого расстрела. Это еще почему? ИМХО Тема не совсем верно названа, по смыслу скорее "Использование событий актора" и дополнить не только потерей\получением предмета, но и всеми остальными событиями, на которые можно подписаться. Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-889052
Dennis_Chikin 3 665 Опубликовано 8 Ноября 2014 Автор Поделиться Опубликовано 8 Ноября 2014 (изменено) По тому что требуют явно определять переменные и явно возвращать из вызываемых функций это самый nil. Да, а рассматривать в этой теме on_hit(), on_death() и прочие апдейты я не буду. Только операции с предметами. Изменено 8 Ноября 2014 пользователем Dennis_Chikin Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-889058
Desertir 202 Опубликовано 8 Ноября 2014 Поделиться Опубликовано 8 Ноября 2014 При чем тут сравнение с nil? И кто требует? Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-889061
Dennis_Chikin 3 665 Опубликовано 8 Ноября 2014 Автор Поделиться Опубликовано 8 Ноября 2014 (изменено) По тому что не срабатывает. Неделю назад в ШМ обсуждалось. Впрочем, сейчас воспроизвести не удалось. Но движок другой - для забугра, а не для одноэса. Продолжаем разговор. © 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() будет отдельная телега. Изменено 8 Ноября 2014 пользователем Dennis_Chikin 1 Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-889101
Карлан 1 050 Опубликовано 8 Ноября 2014 Поделиться Опубликовано 8 Ноября 2014 эта функция нам пригодится. Интересно посмотреть как ее нормально использовать, если бы в нее непись передавался - другое дело, а так я ее только через жопу использовать смог, но тем не менее хоть что-то. Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-889108
Dennis_Chikin 3 665 Опубликовано 8 Ноября 2014 Автор Поделиться Опубликовано 8 Ноября 2014 Для неписей, внезапно, нужна function actor_binder:on_item_drop( item ). Как ни странно. Вот там все будет. А с этой, например, удобно считать предметы, или те же деньги, которые перешли к кому-либо после открытия окна торговли. То есть, мы можем, например, переоткрывать лавочку для обмена предметов, притащенных по спецзаказу, на товары "из под прилавка". Вместо того, чтобы плодить 100500 строк "бартера" в диалогах, или забивать нетпакет мусором из task_manager.ltx. И, кстати, все не доходят руки посмотреть, кто у перемествшегося предмета - parent, и когда именно он меняется. Собственно, по тому и не доходят, что on_drop хватает. Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-889120
RayTwitty 567 Опубликовано 8 Ноября 2014 Поделиться Опубликовано 8 Ноября 2014 И, кстати, все не доходят руки посмотреть, кто у перемествшегося предмета - parent, и когда именно он меняется.На дропе? Для какого объекта вызывали колбек, тот и парент. Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-889267
Dennis_Chikin 3 665 Опубликовано 8 Ноября 2014 Автор Поделиться Опубликовано 8 Ноября 2014 На трэйде. А вот на дропе, внезапно, как раз таки нет, из чего и проистекает весьма много вкусностей. Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-889271
RayTwitty 567 Опубликовано 8 Ноября 2014 Поделиться Опубликовано 8 Ноября 2014 А вот на дропе, внезапно, как раз таки нет, из чего и проистекает весьма много вкусностей.Ссылка на инвентарь, в котором лежит предмет обнуляется аккурат перед вызовом колбека для владельца. На трэйде.Скорее всего парент уже актор, вызов колбека происходит в самом конце функции трансфера. Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-889281
Dennis_Chikin 3 665 Опубликовано 9 Ноября 2014 Автор Поделиться Опубликовано 9 Ноября 2014 (изменено) Продолжаем разговор... 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, кстати, тоже надо отсюда убрать. Изменено 9 Ноября 2014 пользователем Dennis_Chikin Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-889643
RayTwitty 567 Опубликовано 9 Ноября 2014 Поделиться Опубликовано 9 Ноября 2014 повесил на этот вызов 100500 if ... then ...Стоит лишь отсечь стартовый спавн предметов в актора, например через проверку device().precache_frame и проблема будет решена. Переписывавем традиционно:А толку, если такая таблица актуальна только при старте игры по сути? Тогда уж на дропе убирай из таблицы выкинутую вещь. Впрочем, чем не нравится получение предмета через actor:object(section) (или через ту же итерацию по инвентарю) - я хз. Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-889663
Dennis_Chikin 3 665 Опубликовано 10 Ноября 2014 Автор Поделиться Опубликовано 10 Ноября 2014 (изменено) А теперь самое вкусное. Я так до сих пор и не понял, зачем люди вешают все-все на 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 На этом, в общем-то, все. И немедленно выпил. © Изменено 10 Ноября 2014 пользователем Dennis_Chikin Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-889942
Murarius 10 569 Опубликовано 11 Ноября 2014 Поделиться Опубликовано 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 На этом, в общем-то, все. И немедленно выпил. © Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-890212
Dennis_Chikin 3 665 Опубликовано 11 Ноября 2014 Автор Поделиться Опубликовано 11 Ноября 2014 Мне одному кажется, что в отредактированном варианте что-то пропало ? Ну и удаление разбивки читабельность отнюдь не улучшает. Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-890216
abramcumner 1 229 Опубликовано 11 Ноября 2014 Поделиться Опубликовано 11 Ноября 2014 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Код все же лучше давать в псевдо-коде. Тогда предыдущий кусок будет гораздо понятней if item_used(item) then return end if item_released(item) then signals.on_item_release(item) elseif item_has_parent(item) then signals.on_transfer_item(item) else signals.on_drop_item_to_ground(item) end Еще в примере описка походу: on_drop_t.any( item ) endИ в одном случае: for i, f in ipairs( on_drop_t[sect] ) do f( item ) end а в другом: local t = on_take_t[item:section()] if t then for i = 1, #t do t[i]( item ) end Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-890230
Dennis_Chikin 3 665 Опубликовано 11 Ноября 2014 Автор Поделиться Опубликовано 11 Ноября 2014 (изменено) local on_drop_t = { ["any"] = {} } -- вполне честная таблица из inv_manager.scriptкусок кода с for i = 1, on_drop_n do on_drop_t.any( item ) end -- оттуда же.Вызываются все функции, добавленные на выпадание любого предмета на землю.по for i, f in ipairs( on_drop_t[sect] ) do f( item ) end -- это вызов всех функций, которые добавлены для конкретного предмета. В принципе кусок не из этой серии, добавлен для иллюстрации возможностей. Из рабочего скрипта. Да, наверное, лучше переписать в псевдокоде. Если кому заняться нечем. Но не signals все-же. Изменено 11 Ноября 2014 пользователем Dennis_Chikin Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-890244
Murarius 10 569 Опубликовано 11 Ноября 2014 Поделиться Опубликовано 11 Ноября 2014 Мне одному кажется, что в отредактированном варианте что-то пропало ? По-моему, ничего. Ну и удаление разбивки читабельность отнюдь не улучшает. Это о чем? Я вроде не удалял ничего. Вот редактор при копипасте иногда чудеса вытворяет с лишними строками и пробелами - это может быть... Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-890245
Карлан 1 050 Опубликовано 11 Ноября 2014 Поделиться Опубликовано 11 Ноября 2014 Можно вопрос, а в самих скриптах где мы интить будем свои функции разве не нужно будет писать if then 100500 раз? Посыл то какой, автономность? Так xr_s (xStream), m_main (Artos), ogse_signals (malandrinus)... Я конечно не программист, но у тебя вроде быстрее работа, это основная цель написания своей системы что-ли? Или я не понял чего? Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-890268
Dennis_Chikin 3 665 Опубликовано 11 Ноября 2014 Автор Поделиться Опубликовано 11 Ноября 2014 (изменено) Да, bind_stalker.script, как и все остальное, мы изменяем один раз в жизни, и никогда больше не трогаем. Есть апи, которое из любого добавляемого скрипта дергается при ините, и вот там добавляется все, что надо.При этом отработав, скажем, на локаци X квест Y больше никогда ничего не добавляется, и естественно, не проверяется.Ну а выборка из таблицы выигрывает уже при 5-7 if даже без сравнения строк. Вот, например, инициализация escape_dialog.script из NLC/солянки, квест с ночной звездой: t_take = { { have_a_art, "af_night_star", "kvest_art_started", "test_quest_art_vziat" } } function init() for i, v in ipairs( t_take ) do if ( ( not v[3] ) or actor:has_info( v[3] ) ) and ( ( not v[4] ) or actor:dont_has_info( v[4] ) ) then bind_stalker.add_on_take( v[1], v[2] ) end end return true end - это оно сюда вообще из amk.script переехало.А вот по-злобнее будет: t_take = { -- f, item, level, has_info, dont_has_info { pda1, "kostya_pda", "l01_escape", "kostya_taynik1_start", "kostya_pda_have" }, { esc_art_have, "af_gold_fish", "l01_escape", "kostya_art_start", "kostya_art_have" }, { pda2, "kostya_pda", "l02_garbage", "kostya_svalka_taynik_start", "kostya_svalka_taynik_have" }, { pda3, "kostya_pda", "l04u_labx18", "kostya_x18_taynik_start", "kostya_x18_taynik_have" }, { pda5, "kostya_pda", "l03_agroprom", "kostya_agroprom_taynik_start", "kostya_agroprom_taynik_have" }, { pda4, "kostya_pda", "l08_yantar", "kostya_yantar_taynik_start", "kostya_yantar_taynik_have" }, { pda6, "kostya_pda", "l07_military", "kostya_as_taynik_start", "kostya_as_taynik_have" }, { pda7, "kostya_pda", "l10_radar", "kostya_radar_taynik_start", "kostya_radar_taynik_have" }, { pda9, "kostya_pda9", "l10u_bunker", "kostya_x10_taynik_start", "kostya_x10_taynik_have" }, { kostya_doc, "kostya_documents","l10u_bunker","kostya_x10_taynik_start", "kostya_documents_have" }, { pda8, "kostya_pda", "l11_pripyat", "kostya_sacharov_start", "kostya_pripyat_taynik_have" }, { pda_vasilyev, "pda_vasilyev", "l08u_brainlab","kostya_sacharov_pda_start", "kostya_sacharov_pda_have" }, } function init() local lname = level.name() for i, v in ipairs( t_take ) do if lname == v[3] and actor:has_info( v[4] ) and actor:dont_has_info( v[5] ) then bind_stalker.add_on_take( v[1], v[2] ) end end return true end Раньше все это висело всю игру, причем на любой чих, да еще сначала вызовом через 3 скрипта, а потом уже получение секции и проверка. P.S. На самом деле это примерно то самое, что ПЫС имели в виду под схемами. Только предельно упрощенное, и по тому - работающее как задумано, а не как получилось. Вообще, про динамическое подключение чего угодно куда попало будет отдельная тема. Изменено 11 Ноября 2014 пользователем Dennis_Chikin 1 Ссылка на комментарий https://www.amk-team.ru/forum/topic/13054-ispolzovanie-predmetov-aktorom/#findComment-890277
Рекомендуемые сообщения
Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий
Комментарии могут оставлять только зарегистрированные пользователи
Создать аккаунт
Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!
Зарегистрировать новый аккаунтВойти
Есть аккаунт? Войти.
Войти