--[[ scheme_type: generic author: Alundaio modified_by: --]] -------------------- evaid = 188113 actid = 188113 -------------------- -------------------- local alife, math_random, tonumber = alife, math.random, tonumber -------------------- local xr_weight_torch_detection = false -- DONT ENABLE local ini = ini_file("ai_tweaks\\xr_danger.ltx") DangerInertion = utils_data.collect_section(ini,"danger_inertion",true) DangerInertionActor = utils_data.collect_section(ini,"danger_inertion_actor",true) DangerIgnore = utils_data.collect_section(ini,"danger_object",true) DangerIgnoreActor = utils_data.collect_section(ini,"danger_object_actor",true) ini = nil --[[ danger:perceive_type() [danger_object.hit] = "hit", [danger_object.sound] = "sound", [danger_object.visual] = "visual" --]] local script_danger = {} local bd_types = { [danger_object.grenade] = "grenade", [danger_object.entity_corpse] = "entity_corpse", [danger_object.entity_attacked] = "entity_attacked", [danger_object.attacked] = "attacked", [danger_object.bullet_ricochet] = "bullet_ricochet", [danger_object.enemy_sound] = "enemy_sound", [danger_object.attack_sound] = "attack_sound", [danger_object.entity_death] = "entity_death", } ----------------------------------------------------------------------------- -- PUBLIC ----------------------------------------------------------------------------- function set_script_danger(npc,time,who_id,pos) local id = npc:id() if not (script_danger[id]) then script_danger[id] = {danger_time = time_global(), danger_inertion = time, id = who_id, position = pos} else script_danger[id].danger_time = time_global() script_danger[id].danger_inertion = time script_danger[id].id = who_id script_danger[id].position = pos end end function has_danger(npc) local id = npc:id() local st = db.storage[id] return st and st.danger_flag == true end ----------------------------------------------------------------------------- -- PRIVATE ----------------------------------------------------------------------------- local function get_danger_name(best_danger) local best_danger_object = best_danger:object() local bd_type = best_danger:type() if bd_type ~= danger_object.grenade and best_danger:dependent_object() ~= nil then best_danger_object = best_danger:dependent_object() end if best_danger_object == nil then return "none" end return best_danger_object:name() end function get_danger_time(danger,npc) if (danger:type() == danger_object.entity_corpse) then local corpse_object = danger:object() return corpse_object and IsStalker(corpse_object) and corpse_object:death_time() or 0 end return danger:time() end local npc_update_time = 0 local function npc_on_update(npc) if not xr_weight_torch_detection then return end if (npc:see(db.actor)) then return end if (npc_update_time > time_global()) then return end npc_update_time = time_global()+100 if not (IsStalker(npc)) then return end if (has_danger(npc)) then return end if (npc:best_enemy()) then return end if (IsWounded(npc) or npc:character_community() == "zombied" or xr_combat_ignore.npc_in_safe_zone(npc)) then return end local dist = npc:position():distance_to_sqr(db.actor:position()) local actor_weight = (db.actor:get_total_weight() - 25)*6 local diff = actor_weight/dist if (diff > 1) and (dist < 200) then npc:make_object_visible_somewhen(db.actor) --printf("DETECT ACTOR WEIGHT:"..diff) end if (itms_manager.Torch2) and (dist < 80) and (npc:get_luminocity() < 0.3) then npc:make_object_visible_somewhen(db.actor) --printf("DETECT ACTOR TORCH") end end local function npc_on_hear_callback(npc,who_id,s_type,dist,sound_power,sound_position) if not (IsStalker(npc)) then return end if (has_danger(npc)) then return end if (npc:best_enemy()) then return end --utils_data.debug_write("xr_danger:hear_callback",true) if (IsWounded(npc) or character_community(npc) == "zombied" or xr_combat_ignore.npc_in_safe_zone(npc)) then return end if (s_type == "WPN_hit" or s_type == "MST_attack" or s_type == "MST_damage") and dist < 900 then local who = who_id and (db.storage[who_id] and db.storage[who_id].object) if not (who) then return end if not (IsStalker(who) or IsMonster(who)) then return end if (xr_combat_ignore.ignore_enemy_by_overrides(npc,who)) then return end if (who_id == AC_ID or npc:relation(who) >= 2) then set_script_danger(npc,7000,who_id,vec_set(sound_position)) end end end local function npc_on_death_callback(npc,who) if (IsStalker(npc) and character_community(npc) ~= "zombied") then --save_var(npc,"death_by_id",who:id()) local st = db.storage[npc:id()] if (st) then st.killer_last_known_position = vector():set(who:position()) end end end -- xcvb this one looks weird, need testing because npcs should hear the body sound anyway --[[ local function npc_on_hit_callback(npc,amount,local_direction,who,bone_index) if (bone_index == "from_death_callback") then return end set_script_danger(npc,7000,who_id,who and vector():set(who:position())) end --]] local function get_rand_vertex(npc,vo) local dir = npc:position():sub(vo:position()) dir = vector_rotate_y(dir,math_random(MinVectorRotateY,MaxVectorRotateY)) return vo:vertex_in_direction(vo:level_vertex_id(),dir,math_random(MinDistance,MaxDistance)) end function danger_in_radius(npc,best_danger,bd_type) local from_actor = (best_danger:object() and (best_danger:object():id() == db.actor:id())) local src = (from_actor and DangerIgnoreActor[bd_types[bd_type]]) or DangerIgnore[bd_types[bd_type]] local condlist = xr_logic.parse_condlist(npc,bd_types[bd_type],"danger_object",src) local ignore_distance = condlist and tonumber(xr_logic.pick_section_from_condlist(db.actor,npc,condlist)) or 0 if (ignore_distance == 0) then return false end if (best_danger:position():distance_to_sqr(npc:position()) < ignore_distance) then return true end return false end local function is_danger(npc,best_danger,script) if xr_wounded.is_heavy_wounded_by_id(npc:id()) then return false end if (xrs_kill_wounded and axr_task_manager.hostages_by_id[npc:id()]) then return false end local best_danger_object = best_danger:object() local bd_type = best_danger:type() --if (bd_type == danger_object.entity_corpse) then --return false --end if (bd_type == danger_object.grenade) then if not (best_danger:dependent_object() and character_community(npc) ~= "zombied") then return false end return danger_in_radius(npc,best_danger,bd_type) end if (xr_combat_ignore.npc_in_safe_zone(npc)) then return false end if (bd_type == danger_object.entity_corpse) then if (best_danger_object and IsStalker(best_danger_object) and character_community(best_danger_object) == character_community(npc)) then local corpse_st = db.storage[best_danger_object:id()] if not (corpse_st and corpse_st.death_time and corpse_st.death_by_id) then return false end local killer = db.storage[corpse_st.death_by_id] and db.storage[corpse_st.death_by_id].object if not (killer and (IsMonster(killer) or IsStalker(killer)) and killer:alive() and npc:relation(killer) > 0 and character_community(killer) ~= character_community(npc)) then return false end if (xr_combat_ignore.ignore_enemy_by_overrides(npc,killer)) then return false end if (game.get_game_time():diffSec(corpse_st.death_time) > 120000) then return false end return danger_in_radius(npc,best_danger,bd_type) end return false end --if (db.storage[npc:id()].active_scheme == "camper") then --return false --end if (best_danger:dependent_object()) then best_danger_object = best_danger:dependent_object() end if (best_danger_object and (best_danger:perceive_type() == danger_object.hit)) then -- bypass combat ignore radius for attacked, entity_attacked, ricochet return danger_in_radius(npc,best_danger,bd_type) end --if not (best_danger_object and (IsMonster(best_danger_object) or IsStalker(best_danger_object)) and npc:relation(best_danger_object) >= 2) then if not (best_danger_object and (IsMonster(best_danger_object) or IsStalker(best_danger_object)) and ((best_danger:object():id() == db.actor:id()) or (npc:relation(best_danger_object) >= 2))) then return false end if (xr_corpse_detection.is_under_corpse_detection(npc) or xr_help_wounded.is_under_help_wounded(npc)) then return false elseif (xrs_kill_wounded and xrs_kill_wounded.is_under_kill_wounded(npc)) then return false end if (best_danger_object and (IsMonster(best_danger_object) or IsStalker(best_danger_object))) then if not (best_danger_object:alive() and xr_combat_ignore.is_enemy(npc, best_danger_object,true)) then return false end end return danger_in_radius(npc,best_danger,bd_type) end local function check_script_danger(id) if (script_danger[id]) then if (time_global() < script_danger[id].danger_time + script_danger[id].danger_inertion) then return true else script_danger[id] = nil end end return false end ----------------------------------------------------------------------------- -- ON GAME START ----------------------------------------------------------------------------- function on_game_start() RegisterScriptCallback("npc_on_update",npc_on_update) -- RegisterScriptCallback("npc_on_hit_callback",npc_on_hit_callback) RegisterScriptCallback("npc_on_hear_callback",npc_on_hear_callback) RegisterScriptCallback("npc_on_death_callback",npc_on_death_callback) end ---------------------------------------------------------------------------------------------------------------------- -- EVALUATORS ---------------------------------------------------------------------------------------------------------------------- class "evaluator_danger" (property_evaluator) function evaluator_danger:__init(name, storage, npc) super (nil, name) self.a = storage end local function eval_danger(self) local npc = self.object local danger = npc:best_danger() local flags = {ret_value = true} SendScriptCallback("npc_on_eval_danger", self.object, flags) if flags.ret_value == false then return false end if not (danger) then return false end local from_actor = (danger:object() and (danger:object():id() == db.actor:id())) local tg = time_global() local bd_type = danger:type() if (self.a.inertion) then if (self.a.type == bd_type) then if (tg < self.a.danger_time + self.a.inertion) then return true end end end self.a.inertion = nil --[[ if (bd_types[bd_type] == "entity_corpse") then printf("%s %s %s",npc:name(),danger:object():name(),bd_types[bd_type]) end --]] local src = (from_actor and DangerInertionActor[bd_types[bd_type]]) or DangerInertion[bd_types[bd_type]] local inertion = tonumber(xr_logic.pick_section_from_condlist(db.actor,npc,xr_logic.parse_condlist(npc,bd_types[bd_type],"danger_inertion_time",src))) or 0 local new_danger_time = get_danger_time(danger,npc) if (tg >= new_danger_time + inertion) then return false end if not(is_danger(npc,danger)) then return false end --[[ local sim = alife() local se_obj = sim:object(id) if (se_obj and se_obj.m_smart_terrain_id ~= 65535) then sim:object(se_obj.m_smart_terrain_id):set_alarm() end --]] self.a.danger_time = new_danger_time self.a.inertion = inertion self.a.type = bd_type return true end function evaluator_danger:evaluate() --utils_data.debug_write("eva_danger") local danger = eval_danger(self) or false db.storage[self.object:id()].danger_flag = danger return danger end ----------------------------------------------------------------------- -- ACTION ----------------------------------------------------------------------- class "action_danger" ( action_base ) function action_danger:__init( name, storage ) super ( nil, name ) self.a = storage end function action_danger:initialize() action_base.initialize( self ) self.a.stage = 1 self.object:set_path_type(game_object.level_path) self.object:set_detail_path_type(move.line) self.object:set_desired_direction() self.a.last_state = state_mgr.get_state(self.object) end local function script_action_danger_scripted(npc,st) --utils_data.debug_write("script_action_danger_scripted") local id = npc:id() local bdo = script_danger[id].id and db.storage[script_danger[id].id] and db.storage[script_danger[id].id].object local bdo_pos = script_danger[id].position local dist = bdo_pos and npc:position():distance_to_sqr(bdo_pos) local tg = time_global() local who_dist = bdo and bdo:position():distance_to_sqr(bdo_pos) --[[ if (dist and dist >= 70) then if (bdo and npc:relation(bdo) > 0 and npc:see(bdo)) then state_mgr.set_state(npc,"hide_na",nil,nil,{look_object = bdo}) if (not utils_obj.squad_in_los(npc,bdo)) then local wpn = npc:best_weapon() if (wpn) then local relation = npc:relation(bdo) local look_pos = vector():set(bdo:position()) look_pos.y = relation >= 2 and look_pos.y + 1 or look_pos.y - (math_random(25,35)/100) npc:set_sight(look.fire_point,look_pos) if (npc:target_body_state() == move.crouch) then st.__warning_shot_tmr = not st.__warning_shot_tmr and tg+500 or st.__warning_shot_tmr if (tg < st.__warning_shot_tmr) then npc:set_item(object.fire1,wpn,1,1) xr_sound.set_sound_play(npc:id(),"tolls") end end end end st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,npc:level_vertex_id()) end end --]] if (st.stage == 1) then local new_vid = random_choice(utils_obj.try_to_strafe(npc),utils_obj.try_go_backward(npc,6),utils_obj.try_go_cover(npc,bdo and bdo:position() or bdo_pos or npc:position())) st.lvid = nil if (new_vid and (not st.lvid or (new_vid ~= st.lvid and new_vid ~= npc:level_vertex_id())))then st.lvid = new_vid end if not (new_vid) then local dir = bdo_pos and vector():set(npc:position()):sub(bdo_pos) or npc:direction() dir = vector_rotate_y(dir,math_random(-20,20)) local node = position_node(5) node = node:select_best_vertex_id(npc,dir,npc:level_vertex_id(),20,35) if (node and node.vertex_id and node.vertex_id ~= npc:level_vertex_id()) then st.lvid = node.vertex_id end end if (st.lvid and st.lvid ~= npc:level_vertex_id()) then st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,st.lvid) st.stage = 2 local sm = db.storage[npc:id()].state_mgr if (sm and sm.target_state ~= "assault") then npc:clear_animations() sm:set_state("assault",nil,nil,{look_position = who_dist and who_dist < 22500 and bdo and bdo:position() or bdo_pos}, {fast_set = true}) sm.animation:set_state(nil, true) sm.animation:set_control() sm.animstate:set_state(nil,true) sm.animstate:set_control() end return end state_mgr.set_state(npc,"hide_na",nil,nil,{look_position = who_dist and who_dist < 22500 and bdo and bdo:position() or bdo_pos}) elseif (st.stage == 2) then if (npc:level_vertex_id() == st.lvid) then local tg = time_global() st.__dtimer = not st.__dtimer and tg + math_random(5000,15000) or st.__dtimer if (tg > st.__dtimer) then st.stage = 1 return end state_mgr.set_state(npc,"threat_danger",nil,nil,{look_position = who_dist and who_dist < 22500 and bdo and bdo:position() or bdo_pos}) return end state_mgr.set_state(npc,"assault",nil,nil,{look_position = who_dist and who_dist < 22500 and bdo and bdo:position() or bdo_pos}) utils_obj.send_to_nearest_accessible_vertex(npc,st.lvid) end end local function script_action_danger_grenade(npc,st,bd,best_danger_object,dependent_object,bd_type) --utils_data.debug_write("script_action_danger_grenade") -- In this case dependent_object should be actual grenade object; best_danger_object should be nade thrower local bdo = dependent_object or best_danger_object local bdo_pos = bdo and bdo:position() local dist = bdo_pos and npc:position():distance_to_sqr(bdo_pos) local be = npc:best_enemy() or best_danger_object and npc:relation(best_danger_object) > 0 and best_danger_object local tg = time_global() if (dist and dist >= 150) then --[[ if (be and npc:see(be)) then state_mgr.set_state(npc,"hide_na",nil,nil,{look_object = be}) if (not utils_obj.squad_in_los(npc,be)) then local wpn = npc:best_weapon() if (wpn) then local relation = npc:relation(be) local look_pos = vector():set(be:position()) look_pos.y = relation >= 2 and look_pos.y + 1 or look_pos.y - (math_random(25,35)/100) npc:set_sight(look.fire_point,look_pos) if (npc:target_body_state() == move.crouch) then st.__warning_shot_tmr = not st.__warning_shot_tmr and tg+500 or st.__warning_shot_tmr if (tg < st.__warning_shot_tmr) then npc:set_item(object.fire1,wpn,1,1) xr_sound.set_sound_play(npc:id(),"tolls") end end npc:set_body_state(move.crouch) end end else state_mgr.set_state(npc,"threat_danger",nil,nil,{look_position = bdo_pos}) end --]] local sm = db.storage[npc:id()].state_mgr if (sm and sm.target_state ~= "threat_danger") then npc:clear_animations() sm:set_state("threat_danger",nil,nil,{look_position = bdo_pos}, {fast_set = true}) sm.animation:set_state(nil, true) sm.animation:set_control() sm.animstate:set_state(nil,true) sm.animstate:set_control() end st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,npc:level_vertex_id()) return end if (st.stage == 1) then local dir = bdo_pos and vector():set(npc:position()):sub(bdo_pos) or npc:direction() dir = vector_rotate_y(dir,math_random(-20,20)) local new_vid = npc:vertex_in_direction(npc:level_vertex_id(),dir,math_random(20,35)) st.lvid = nil if (new_vid and (not st.lvid or (new_vid ~= st.lvid and new_vid ~= npc:level_vertex_id())))then st.lvid = new_vid end if not (new_vid) then local node = position_node(5) node = node:select_best_vertex_id(npc,dir,npc:level_vertex_id(),20,35) if (node and node.vertex_id and node.vertex_id ~= npc:level_vertex_id()) then st.lvid = node.vertex_id end end if (st.lvid and st.lvid ~= npc:level_vertex_id()) then st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,st.lvid) st.stage = 2 local sm = db.storage[npc:id()].state_mgr if (sm) then npc:clear_animations() sm:set_state("assault",nil,nil,{look_object = dist <= 100 and nil or bdo}, {fast_set = true}) sm.animation:set_state(nil, true) sm.animation:set_control() sm.animstate:set_state(nil,true) sm.animstate:set_control() end return end state_mgr.set_state(npc,"hide_na",nil,nil,{look_object = be and npc:see(be) and be or bdo},{fast_set = true}) elseif (st.stage == 2) then if (npc:level_vertex_id() == st.lvid) then st.stage = 1 return end state_mgr.set_state(npc,"assault",nil,nil,{look_object = dist <= 100 and nil or bdo}) utils_obj.send_to_nearest_accessible_vertex(npc,st.lvid) end end local function script_action_danger_corpse(npc,st,bd,best_danger_object,dependent_object,bd_type) --utils_data.debug_write("script_action_danger_corpse") -- In this case corpse is best_danger_object; dependent_object should be killer or it's probably nil local bdo = best_danger_object or dependent_object local dst = db.storage[bdo:id()] local killer = dependent_object or dst and dst.death_by_id and (db.storage[dst.death_by_id] and db.storage[dst.death_by_id].object) -- force hostile to killer if (st.stage >= 2 and killer and killer:id() == 0 and killer:alive() and npc:relation(killer) >= game_object.neutral and npc:see(killer)) then local squad = get_object_squad(npc) if (squad) then for k in squad:squad_members() do npc = db.storage[k.id] and db.storage[k.id].object or level.object_by_id(k.id) if(npc) then npc:force_set_goodwill( -1000, killer) end end else npc:force_set_goodwill( -1000, killer) end --printf("xr_danger.script_action_danger_corpse: forced enemies %s and %s",npc:name(), killer:name()) elseif (killer and killer:id() == 0 and killer:alive() and npc:relation(killer) < game_object.neutral) then npc:make_object_visible_somewhen(killer) end --printf("stage = %s",st.stage) if (st.stage == 1) then if (math.random(1,100) < 50) then st.stage = 3 end end if (st.stage == 1) then st.lvid = bdo:level_vertex_id() st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,st.lvid) state_mgr.set_state(npc,"assault",nil,nil,{look_object = bdo}, {fast_set = true}) st.rnd = math_random(30) st.stage = 2 elseif (st.stage == 2) then if not (st.rnd and st.lvid) then st.stage = 1 return end local dist = bdo and bdo:position():distance_to_sqr(npc:position()) or 0 if (st.lvid == npc:level_vertex_id() or dist <= st.rnd) then local rnd = math_random(100) if (rnd <= 33) then xr_sound.set_sound_play(npc:id(),"search") state_mgr.set_state(npc,"threat_danger",nil,nil,{look_position = dst and dst.killer_last_known_position}) st.dtimer = time_global() + math_random(3000,5000) elseif (dist <= 5 and not st.searched) then st.searched = true state_mgr.set_state(npc,"search") st.dtimer = time_global() + math_random(3000,5000) else state_mgr.set_state(npc,"raid",nil,nil,{look_position = dst and dst.killer_last_known_position}, {fast_set = true}) end st.stage = 3 return end st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,st.lvid) state_mgr.set_state(npc,"assault",nil,nil,{look_position = dst and dst.killer_last_known_position}, {fast_set = true}) elseif (st.stage == 3) then if (st.dtimer and time_global() < st.dtimer) then return end local dist_to_killer = dst and dst.killer_last_known_position and npc:position():distance_to_sqr(dst.killer_last_known_position) or 30 local new_vid = random_choice(utils_obj.try_to_strafe(npc),utils_obj.try_go_backward(npc,6),utils_obj.try_go_cover(npc,dst and dst.killer_last_known_position or bdo and bdo:position() or npc:position())) if (new_vid and (not st.lvid or new_vid ~= st.lvid))then st.lvid = new_vid end if not (new_vid) then local dir = dst and dst.killer_last_known_position and vector():set(dst.killer_last_known_position):sub(npc:position()) or killer and vector():set(killer:position()):sub(npc:position()) or npc:direction() local node = position_node(3) node = node:select_best_vertex_id(npc,dir,npc:level_vertex_id(),10,dist_to_killer/2) if (node and node.vertex_id and node.vertex_id ~= npc:level_vertex_id()) then st.lvid = node.vertex_id end end if (st.lvid) then st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,st.lvid) state_mgr.set_state(npc,"assault",nil,nil,{look_position = dst and dst.killer_last_known_position}, {fast_set = true}) st.stage = 4 return end st.dtimer = time_global() + math_random(5000,7000) st.stage = 5 state_mgr.set_state(npc,"threat_danger",nil,nil,{look_position = dst and dst.killer_last_known_position}, {fast_set = true}) return elseif (st.stage == 4) then if (npc:level_vertex_id() == st.lvid) then st.dtimer = time_global() + math_random(5000,7000) st.stage = 5 state_mgr.set_state(npc,"hide_na",nil,nil,{look_position = dst and dst.killer_last_known_position}, {fast_set = true}) return end st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,st.lvid) elseif (st.stage == 5) then if (st.dtimer and time_global() < st.dtimer) then return end st.lvid = nil st.stage = 6 elseif (st.stage == 6) then local new_vid = utils_obj.try_go_cover(npc,dst and dst.killer_last_known_position or bdo and bdo:position() or npc:position()) if (new_vid) then st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,st.lvid) state_mgr.set_state(npc,"hide_na",nil,nil,{look_position = dst and dst.killer_last_known_position or dst.object and dst.object:position()}, {fast_set = true}) end end end local function script_action_danger_attacked(npc,st,bd,best_danger_object,dependent_object,bd_type) --utils_data.debug_write("script_action_danger_attacked") local bdo = dependent_object or best_danger_object local tg = time_global() --printf("stage = %s",st.stage) if (st.stage == 1) then if not (st.last_pos) then st.last_pos = bdo and vector():set(bdo:position()) end local dir = st.last_pos and vector():set(st.last_pos):sub(npc:position()) or vector():set(npc:direction()) local dist_to_bdo = st.last_pos and npc:position():distance_to_sqr(st.last_pos) or 30 local target_vertex_id = random_choice(utils_obj.try_to_strafe(npc),utils_obj.try_go_backward(npc,6),utils_obj.try_go_cover(npc,st.last_pos or npc:position())) if (target_vertex_id) then st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,target_vertex_id) state_mgr.set_state(npc,"assault",nil,nil,{look_position = st.last_pos}) st.stage = 2 return end st.dtimer = tg + math_random(5000,6000) st.stage = 3 return elseif (st.stage == 2) then if (npc:level_vertex_id() == st.lvid) then st.dtimer = tg + math_random(5000,7000) st.stage = 3 return end st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,st.lvid) elseif (st.stage == 3) then if (st.dtimer and tg < st.dtimer) then state_mgr.set_state(npc,"threat_danger",nil,nil,{look_position = st.last_pos}) return end st.stage = 1 end end function action_danger:execute() action_base.execute(self) --utils_data.debug_write("BEFORE scripted_danger_action_planner",true) local npc = self.object local best_danger = npc:best_danger() if not (best_danger) then return end if (check_script_danger(npc:id())) then script_action_danger_scripted(npc,self.a) return end if (npc:path_type() ~= game_object.level_path) then npc:set_path_type(game_object.level_path) end local best_danger_object = best_danger:object() local bd_type = best_danger:type() local dependent_object = best_danger:dependent_object() if (bd_type == danger_object.grenade) then script_action_danger_grenade(npc,self.a,best_danger,best_danger_object,dependent_object,bd_type) elseif (bd_type == danger_object.entity_corpse) then script_action_danger_corpse(npc,self.a,best_danger,best_danger_object,dependent_object,bd_type) elseif (bd_type == danger_object.attacked) then script_action_danger_attacked(npc,self.a,best_danger,best_danger_object,dependent_object,bd_type) end --utils_data.debug_write("AFTER scripted_danger_action_planner") end function action_danger:finalize() self.a.stage = 1 if (self.a.last_state) then state_mgr.set_state(self.object,self.a.last_state) end for k,v in pairs(db.used_level_vertex_ids) do db.used_level_vertex_ids[k] = nil end self.a.__warning_shot_tmr = nil utils_obj.send_to_nearest_accessible_vertex(self.object,self.object:level_vertex_id()) action_base.finalize(self) end ---------------------------------------------------------------------------------------------------------------------- -- false - engine danger, true - script danger ---------------------------------------------------------------------------------------------------------------------- class "evaluator_check_danger" (property_evaluator) function evaluator_check_danger:__init(name, storage) super (nil, name) self.a = storage end function evaluator_check_danger:evaluate() --utils_data.debug_write("eva_check_danger") if (self.a.disable) then return false end if not (IsStalker(self.object)) then return false end if not (self.object:alive()) then return false end if (self.object:best_enemy()) then return false end if (IsWounded(self.object)) then return false end if (character_community(self.object) == "zombied") then return false end local bd = self.object:best_danger() if not (bd) then return false end local id = self.object:id() if not (db.storage[id].danger_flag) then return false end if (check_script_danger(id)) then return true end local bd_type = bd:type() if (self.object:has_info("npcx_is_companion") and bd_type == danger_object.entity_corpse) then return false end if not (bd_type == danger_object.attacked or bd_type == danger_object.entity_corpse or bd_type == danger_object.grenade) then return false end return true end class "position_node" function position_node:__init(amt) self.node = {} for i=1,amt do self.node[i] = {} end end function position_node:select_best_vertex_id(object,dir,lvid,min_dist,max_dist,find_furthest) local close min_dist = min_dist and (min_dist <= max_dist and min_dist or max_dist) or 10 max_dist = max_dist and (max_dist >= min_dist and max_dist or min_dist) or 30 for i=1, #self.node do local direction = vector_rotate_y(vec_set(dir), math_random(-180,180)) self.node[i].vertex_id = level.vertex_in_direction(lvid, direction, math_random(min_dist,max_dist) ) if (self.node[i].vertex_id and utils_obj.accessible(object,self.node[i].vertex_id) and self.node[i].vertex_id ~= lvid) then self.node[i].distance = self.node[i].vertex_id == lvid and -1 or object:position():distance_to_sqr(level.vertex_position(self.node[i].vertex_id)) if not (close) then close = i --self.node[i].vertex_id end if (close) then if (find_furthest) then if (self.node[close] and self.node[close].distance < self.node[i].distance) then close = i end else if (self.node[close] and self.node[i].distance < self.node[close].distance) then close = i end end end end end return close and self.node[close] end ---------------------------------------------------------------------------------------------------------------------- -- BINDER ---------------------------------------------------------------------------------------------------------------------- function setup_generic_scheme(npc,ini,scheme,section,stype,temp) local st = xr_logic.assign_storage_and_bind(npc,ini,"danger","danger",temp) db.storage[npc:id()].danger_flag = false db.storage[npc:id()].danger_flag_scripted = nil end function add_to_binder(npc,ini,scheme,section,storage,temp) local manager = npc:motivation_action_manager() manager:remove_evaluator(stalker_ids.property_danger) manager:add_evaluator(stalker_ids.property_danger,evaluator_danger("danger", storage, npc)) manager:add_evaluator(evaid,evaluator_check_danger("script_danger",storage,npc)) local danger_action = manager:action(stalker_ids.action_danger_planner) local danger_action_planner = cast_planner(danger_action) danger_action_planner:remove_evaluator(stalker_ids.property_danger) danger_action_planner:add_evaluator(stalker_ids.property_danger,evaluator_danger("danger", storage, npc)) local wp = world_property danger_action:add_precondition( wp(evaid, false) ) temp.action = action_danger("danger", storage) temp.action:add_precondition( wp(evaid, true) ) temp.action:add_precondition( wp(stalker_ids.property_danger, true) ) temp.action:add_effect( wp(evaid,false) ) --temp.action:add_effect( wp(stalker_ids.property_danger, false) ) manager:add_action(actid,temp.action) end function configure_actions(npc,ini,scheme,section,stype,temp) local wp = world_property temp.action:add_precondition( wp(stalker_ids.property_alive, true) ) temp.action:add_precondition( wp(stalker_ids.property_enemy, false) ) temp.action:add_precondition( wp(stalker_ids.property_anomaly, false) ) local manager = npc:motivation_action_manager() local action local p = { xr_actions_id.alife, --xr_actions_id.state_mgr + 1, xr_actions_id.state_mgr + 2 } for i=1,#p do action = manager:action(p[i]) if (action) then action:add_precondition( wp(evaid,false) ) else printf("xr_danger: no action id p[%s]",i) end end end function reset_generic_scheme(npc,scheme,section,stype,st) end