e4s-sdk/gamedata/scripts/xr_motivator.script
2026-06-17 23:06:51 +03:00

585 lines
No EOL
21 KiB
Text

--[[------------------------------------------------------------------------------------------------------------------
àâòîð: Äèäåíêî Ðóñëàí (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