--[[------------------------------------------------------------------------------------------------------------------ автор: Диденко Руслан (Stohe) порядок вызова методов биндера: reload reinit load net_spawn --------------------------------------------------------------------------------------------------------------------]] ---------------------------------------------------------------------------------------------------------------------- class "motivator_binder" (object_binder) function motivator_binder:__init (obj) super(obj) self.loaded = false self.last_update = 0 ----memusage.collect_info(self.object, "__init") self.first_update = false -- self.need_relation_update = false self.opened_doors = {} --Хранит таблицу дверей, открытых данным персонажем (чтобы потом закрыть) end function motivator_binder:extrapolate_callback(cur_pt) if self.st.active_section then xr_logic.issue_event(self.object, self.st[self.st.active_scheme], "extrapolate_callback") self.st.move_mgr:extrapolate_callback(self.object) end if patrol(self.object:patrol()):flags(cur_pt):get() == 0 then return true end return false end function motivator_binder:reinit() object_binder.reinit(self) --memusage.collect_info(self.object, "reinit_start") --printf("motivator_binder:reinit(): self.object:name()='%s'", self.object:name()) -- КОЛЛБЕКИ ЗДЕСЬ СТАВИТЬ НЕЛЬЗЯ! Здесь еще неизвестна активная схема. -- Ставьте коллбеки в методе update в отмеченном комментарием месте. local char_ini = self.object:spawn_ini() or ini_file("scripts\\dummy.ltx") ---------------------------------------------------------------------------------------------------------------------- -- Общие скрипты ---------------------------------------------------------------------------------------------------------------------- db.storage[self.object:id()] = { followers = {} } self.st = db.storage[self.object:id()] --' Создание менеджера контроля положений тела --memusage.collect_info(self.object, "before_statemgr") self.st.state_mgr = state_mgr.bind_manager(self.object) self.st.move_mgr = move_mgr.move_mgr(self.object) self.st.move_mgr:initialize() --memusage.collect_info(self.object, "after_statemgr") --memusage.collect_info(self.object, "reinit_end") end ---------------------------------------------------------------------------------------------------------------------- function motivator_binder:net_spawn(sobject) printf("motivator_binder:net_spawn(): self.object:name()='%s'", self.object:name()) local visual = utils.cfg_get_string(system_ini(), self.object:section(), "set_visual", obj, false, "") if visual ~= nil and visual ~= "" then if visual == "actor_visual" then self.object:set_visual_name(db.actor:get_visual_name()) else self.object:set_visual_name(visual) end end xrs_dyn_music.npc_table[self.object:id()] = self.object:id() --memusage.collect_info(self.object, "netspawn_start") if not object_binder.net_spawn(self, sobject) then return false end db.add_obj(self.object) -- Все CALLBACK-и ставить здесь: self.object:set_patrol_extrapolate_callback(motivator_binder.extrapolate_callback, self) self.object:set_callback(callback.hit, motivator_binder.hit_callback, self) self.object:set_callback(callback.death, motivator_binder.death_callback, self) self.object:set_callback(callback.use_object, motivator_binder.use_callback, self) self.object:set_callback(callback.sound, motivator_binder.hear_callback, self) --memusage.collect_info(self.object, "after__callback") -- ставим дефолтное расстояние входа в смарткаверы self.object:apply_loophole_direction_distance(1.0) --' Загрузка историй для лагеря. if self.loaded == false then local char_ini = "" local spawn_ini = self.object:spawn_ini() local filename = nil if spawn_ini then filename = utils.cfg_get_string(spawn_ini, "logic", "cfg", self.object, false, "") printf("filename is [%s]", tostring(filename)) end if filename ~= nil then char_ini = ini_file(filename) else char_ini = self.object:spawn_ini() or ini_file("scripts\\dummy.ltx") end printf("xr_info loaded!!!") xr_info.loadInfo(self.object, char_ini) end if not self.object:alive() then self.object:death_sound_enabled(false) release_body_manager.get_release_body_manager():moving_dead_body(self.object) ---НЕ ЗАБЫТЬ УБРАТЬ return true end -- setting npc relation and sympathy from storage local relation = db.goodwill.relations and db.goodwill.relations[self.object:id()] if relation ~= nil and db.actor then game_relations.set_npcs_relation(self.object, db.actor, relation) end local sympathy = db.goodwill.sympathy and db.goodwill.sympathy[self.object:id()] if sympathy ~= nil then game_relations.set_npc_sympathy(self.object, sympathy) end -- game_relations.set_level_faction_community(self.object) db.add_enemy( self.object ) self.e_index = db.heli_enemy_count - 1 --' загрузка озвучки --memusage.collect_info(self.object, "before_soundmgr") sound_theme.init_npc_sound(self.object) --memusage.collect_info(self.object, "after_soundmgr_netspawn_end") --' Для зомбированных чуваков говорим что аномалий не существует if get_object_story_id(self.object:id()) == "zat_b53_artefact_hunter_1" then local manager = self.object:motivation_action_manager() manager:remove_evaluator (stalker_ids.property_anomaly) manager:add_evaluator (stalker_ids.property_anomaly, property_evaluator_const(false)) end xr_reach_task.add_reach_task_action(self.object) --******************************* Телепорт на первую точку пути работы смарттеррейна...***************************** local se_obj = alife():object(self.object:id()) if se_obj ~= nil then if db.spawned_vertex_by_id[se_obj.id] ~= nil then self.object:set_npc_position(level.vertex_position(db.spawned_vertex_by_id[se_obj.id])) db.spawned_vertex_by_id[se_obj.id] = nil elseif db.offline_objects[se_obj.id] ~= nil and db.offline_objects[se_obj.id].level_vertex_id ~= nil then printf("changing position for object[%s] from %s to %s : level vertex [%s] to [%s]", se_obj:name(), vec_to_str(se_obj.position), vec_to_str(level.vertex_position(db.offline_objects[se_obj.id].level_vertex_id)), tostring(se_obj.m_level_vertex_id), tostring(db.offline_objects[se_obj.id].level_vertex_id)) self.object:set_npc_position(level.vertex_position(db.offline_objects[se_obj.id].level_vertex_id)) elseif se_obj.m_smart_terrain_id ~= 65535 then local smart_terrain = alife():object(se_obj.m_smart_terrain_id) if smart_terrain.arriving_npc[se_obj.id] == nil then local smart_task = smart_terrain.job_data[smart_terrain.npc_info[se_obj.id].job_id].alife_task self.object:set_npc_position(smart_task:position()) end end end --****************************************************************************************************************** smart_terrain.setup_gulag_and_logic_on_spawn(self.object, self.st, sobject, modules.stype_stalker, self.loaded) --' Универсальный тормоз на выход из комбата if character_community(self.object) ~= "zombied" then post_combat_idle.add_post_combat_idle(self.object) end self.object:group_throw_time_interval(2000) return true end function motivator_binder:net_destroy() --printf("motivator_binder:net_destroy(): self.object:name()='%s'", self.object:name()) xrs_dyn_music.npc_table[self.object:id()] = nil xr_combat_ignore.fighting_with_actor_npcs[self.object:id()] = nil -- xrs_dyn_music.stop_combat(self.object:id()) xr_sound.stop_sounds_by_id(self.object:id()) local st = db.storage[self.object:id()] if st.active_scheme then xr_logic.issue_event(self.object, st[st.active_scheme], "net_destroy", self.object) end if self.st.reach_task then xr_logic.issue_event(self.object, self.st.reach_task, "net_destroy", self.object) end local on_offline_condlist = db.storage[self.object:id()] and db.storage[self.object:id()].overrides and db.storage[self.object:id()].overrides.on_offline_condlist if on_offline_condlist ~= nil then xr_logic.pick_section_from_condlist(db.actor, self.object, on_offline_condlist) end -- Запоминаем позицию и активную секцию -------- if db.offline_objects[self.object:id()] then db.offline_objects[self.object:id()].level_vertex_id = self.object:level_vertex_id() db.offline_objects[self.object:id()].active_section = db.storage[self.object:id()].active_section end ------------------------------------------------n db.del_obj(self.object) db.storage[self.object:id()] = nil self:clear_callbacks() if self.e_index ~= nil then db.delete_enemy( self.e_index ) end object_binder.net_destroy(self) end function motivator_binder:clear_callbacks() self.object:set_patrol_extrapolate_callback(nil) self.object:set_callback(callback.hit, nil) self.object:set_callback(callback.death, nil) self.object:set_callback(callback.sound, nil) end function motivator_binder:hit_callback(obj, amount, local_direction, who, bone_index) -- FIXME: коллбеки неплохо было бы регистрировать в общем storage, а не посхемно... -- просто всегда ставить их при включении схемы и снимать при отключении. if(who:id()==db.actor:id()) then xr_statistic.set_best_weapon(amount) --[[ local se_obj = alife():object(obj:id()) if se_obj and se_obj.m_smart_terrain_id ~= 65535 and amount > 0 then local smart_obj = alife():object(se_obj.m_smart_terrain_id) smart_obj:set_alarm() if smart_obj.base_on_actor_control ~= nil then smart_obj.base_on_actor_control:actor_attack() end end ]] -- Ищем смарты по расстоянию if amount > 0 then for k,v in pairs(sim_board.get_sim_board().smarts) do local smart = v.smrt if smart.base_on_actor_control ~= nil then local level_id = game_graph():vertex(smart.m_game_vertex_id):level_id() local actor_level_id = game_graph():vertex(alife():actor().m_game_vertex_id):level_id() if level_id == actor_level_id and db.actor:position():distance_to_sqr(smart.position) <= 6400 then if self.object:relation(db.actor) ~= game_object.enemy then smart.base_on_actor_control:actor_attack() end end end end end end if self.st.active_section then xr_logic.issue_event(self.object, self.st[self.st.active_scheme], "hit_callback", obj, amount, local_direction, who, bone_index) end if self.st.combat_ignore then xr_logic.issue_event(self.object, self.st.combat_ignore, "hit_callback", obj, amount, local_direction, who, bone_index) end if self.st.combat then xr_logic.issue_event(self.object, self.st.combat, "hit_callback", obj, amount, local_direction, who, bone_index) end if self.st.hit then xr_logic.issue_event(self.object, self.st.hit, "hit_callback", obj, amount, local_direction, who, bone_index) end -- Если пришел смертельный хит, с некоторой вероятностью делаем чувака раненым. if bone_index ~= 15 and amount > self.object.health*100 then --printf("DEADLY HIT %s %s", amount, self.object.health*100) self.object.health = 0.15 end if amount > 0 then printf("HIT_CALLBACK: %s amount=%s bone=%s", obj:name(), amount, tostring(bone_index)) -- self.need_relation_update = who:id() == db.actor:id() xr_wounded.hit_callback(obj:id()) end end function motivator_binder:death_callback(victim, who) self:hit_callback(victim, 1, vector():set(0,0,0), who, "from_death_callback") xrs_dyn_music.npc_table[self.object:id()] = nil xr_combat_ignore.fighting_with_actor_npcs[self.object:id()] = nil -- xrs_dyn_music.stop_combat(self.object:id()) --' Если игрок был с квестом от механика - фейлить его local st = db.storage[self.object:id()] local npc = self.object stalker_generic.remove_level_spot(npc, st) --' end -- if who:id() == db.actor:id() then xr_statistic.inc_killed_stalkers_counter() xr_statistic.set_best_monster(npc) end --' Загрузка историй для лагеря. local known_info = utils.cfg_get_string(st.ini,st.section_logic , "known_info", self.object, false, "", nil) printf("xr_info from death loaded!!!") xr_info.loadInfo(self.object, st.ini, known_info) --' Принудительно обнуляем анимейшн менеджер чтобы отдетачились приатаченные объекты if self.st.state_mgr ~= nil then self.st.state_mgr.animation:set_state(nil, true) end if self.st.reach_task then xr_logic.issue_event(self.object, self.st.reach_task, "death_callback", victim, who) end if self.st.death then xr_logic.issue_event(self.object, self.st.death, "death_callback", victim, who) end if self.st.active_section then xr_logic.issue_event(self.object, self.st[self.st.active_scheme], "death_callback", victim, who) end sr_light.check_light(self.object) death_manager.drop_manager(self.object):create_release_item() db.delete_enemy( self.e_index ) self:clear_callbacks() --' Наносим небольшой импульс вперед. if(actor_stats.remove_from_ranking~=nil)then local community = character_community(self.object) if community == "zombied" or community == "monolith" then else actor_stats.remove_from_ranking(self.object:id()) end end release_body_manager.get_release_body_manager():moving_dead_body(self.object) end function motivator_binder:use_callback(obj, who) printf("motivator_binder:use_callback(obj, who)") if self.object:alive() then inventory_upgrades.need_victim(obj) xr_meet.notify_on_use(obj, who) dialog_manager.disabled_phrases[obj:id()] = nil if self.st.active_section then xr_logic.issue_event(self.object, self.st[self.st.active_scheme], "use_callback", obj, who) end end end function motivator_binder:update(delta) object_binder.update(self, delta) if xr_combat_ignore.fighting_with_actor_npcs[self.object:id()] and self.object:best_enemy() == nil then xr_combat_ignore.fighting_with_actor_npcs[self.object:id()] = nil end local object = self.object local object_alive = object:alive() local actor = db.actor -- Переключение логики. update_logic(object) if self.first_update == false then if object_alive == false then death_manager.drop_manager(object):create_release_item() end self.first_update = true end if time_global() - self.last_update > 1000 then sr_light.check_light(object) self.last_update = time_global() end -- Открываем двери if object_alive then local opened_doors = ph_door.try_to_open_door(self.object) utils.copy_table(self.opened_doors, opened_doors) -- Закрываем двери local closed_doors = ph_door.try_to_close_door(self.object, self.opened_doors) for id,v in pairs(closed_doors) do self.opened_doors[id] = nil end end --' Апдейт менеджера состояний тела if self.st.state_mgr then if object_alive then self.st.state_mgr:update() -- Апдейт торговли if self.st.state_mgr.combat == false and self.st.state_mgr.alife == false --and self.st.state_mgr.planner:current_action_id() == self.st.state_mgr.operators["end"] then trade_manager.update(object) end else self.st.state_mgr = nil end end if object_alive then --' Апдейт саундменеджера xr_sound.update(object:id()) --' Апдейт юзабилити персонажа xr_meet.process_npc_usability(object) -- Апдейт безсмертия stalker_generic.update_invulnerability(self.object) end -- Апдейт отряда local squad = get_object_squad(self.object) if squad ~= nil then if squad:commander_id() == self.object:id() then squad:update() end end object:info_clear() if object_alive then local active_section = db.storage[object:id()].active_section if active_section then object:info_add("section: " .. active_section) end local best_enemy = object:best_enemy() if best_enemy then object:info_add("enemy: " .. best_enemy:name()) end local best_danger = object:best_danger() if best_danger then object:info_add("danger: " .. xr_danger.get_danger_name(best_danger)) end object:info_add(object:name().." ["..object:team().."]["..object:squad().."]["..object:group().."]") if alife():object(object:id()) == nil then return end if squad ~= nil then object:info_add("squad_id: " .. squad:section_name()) if squad.current_action ~= nil then local target = squad.assigned_target_id and alife():object(squad.assigned_target_id) and alife():object(squad.assigned_target_id):name() self.object:info_add("current_action: " .. squad.current_action.name .."["..tostring(target).."]") end end else object:set_tip_text_default() end --[[ -- Полечилось в двигле... if self.need_relation_update then local squad = get_object_squad(self.object) local obj_relation_to_actor = game_relations.game_relations_by_num[self.object:relation(db.actor)] if squad ~= nil and (game_relations.get_squad_goodwill_to_actor_by_id(squad.id) ~= obj_relation_to_actor) then game_relations.set_squad_goodwill(squad.id, obj_relation_to_actor) end self.need_relation_update = false end]]-- end function motivator_binder:reload(section) --memusage.collect_info(self.object, "reload_start") object_binder.reload(self, section) --memusage.collect_info(self.object, "reload_end") end function motivator_binder:net_save_relevant() --printf("motivator_binder:net_save_relevant(): self.object:name()='%s'", self.object:name()) return true end function motivator_binder:save(packet) set_save_marker(packet, "save", false, "motivator_binder") object_binder.save(self, packet) xr_logic.save_obj(self.object, packet) trade_manager.save(self.object, packet) -- treasure_manager.save(packet) xr_sound.save_npc(packet, self.object:id()) dialog_manager.save_npc(packet, self.object:id()) local i = 0 for k,v in pairs(self.opened_doors) do i = i + 1 end packet:w_u8(i) for k,v in pairs(self.opened_doors) do packet:w_u16(k) end set_save_marker(packet, "save", true, "motivator_binder") end function motivator_binder:load(reader) self.loaded = true set_save_marker(reader, "load", false, "motivator_binder") object_binder.load(self, reader) xr_logic.load_obj(self.object, reader) trade_manager.load(self.object, reader) -- treasure_manager.load(reader) xr_sound.load_npc(reader, self.object:id()) dialog_manager.load_npc(reader, self.object:id()) local i = reader:r_u8() for k = 1,i do local key = reader:r_u16() self.opened_doors[key] = true end set_save_marker(reader, "load", true, "motivator_binder") end function motivator_binder:hear_callback(self, who_id, sound_type, sound_position, sound_power) if who_id == self:id() then return end xr_hear.hear_callback(self, who_id, sound_type, sound_position, sound_power) end function AddToMotivator(npc) if alife() then npc:bind_object(this.motivator_binder(npc)) end end -- Эвалюаторы, которые имеют высший приоритет, и, соответственно, перебивают остальные скрипты собой function addCommonPrecondition(action) action:add_precondition (world_property(xr_evaluators_id.stohe_meet_base + 1,false)) action:add_precondition (world_property(xr_evaluators_id.sidor_wounded_base + 0, false)) action:add_precondition (world_property(xr_evaluators_id.abuse_base, false)) action:add_precondition (world_property(xr_evaluators_id.wounded_exist, false)) action:add_precondition (world_property(xr_evaluators_id.corpse_exist, false)) action:add_precondition (world_property(stalker_ids.property_items, false)) end function update_logic(object) local object_alive = object:alive() local st = db.storage[object:id()] local actor = db.actor local st_combat = st.combat if st ~= nil and st.active_scheme ~= nil and object_alive then -- Обрабатываем Override on_combat local switched = false local manager = object:motivation_action_manager() if manager:initialized() and manager:current_action_id() == stalker_ids.action_combat_planner then local overrides = xr_logic.generic_scheme_overrides(object) if overrides then if overrides.on_combat then xr_logic.pick_section_from_condlist(actor, object, overrides.on_combat.condlist) end if st_combat and st_combat.logic then if not xr_logic.try_switch_to_another_section(object, st_combat, actor) then if overrides.combat_type then set_combat_type(object, actor, overrides) end else switched = true end end else set_combat_type(object, actor, st_combat) end end if not switched then xr_logic.try_switch_to_another_section(object, st[st.active_scheme], actor) end else -- если нет активной секции, то тип боя взять из секции on_combat xr_combat.set_combat_type(object, actor, st_combat) end end