Divergent/mods/AI Combat Pack/gamedata/scripts/rx_combat.script

468 lines
18 KiB
Plaintext
Raw Blame History

---- AI Additions ----
---- Rulix aka Bak ----
---- 27.07.2016
function printf(s, ...)
-- rx_utils.printf("com:"..s,...)
end
ASSERTX(rx_ai.rx_ini:section_exist("combat"),"no section [combat] in rx_ini")
local run_on_hit = rx_utils.read_from_ini(rx_ai.rx_ini,"combat","run_on_hit",false,0)
--local set_inertia_time_5_10,set_inertia_time_15
local md_enabled = rx_utils.read_from_ini(rx_ai.rx_ini,"combat","monster_defend",false,0)
local md_max_dist = 12*12
local md_walk_dist = 9*9
local md_run_dist = 5*5
local md_min_rank = 0.2/rx_utils.rank_unit
local md_monsters = {
-- actor = 0,
-- stalker = 0,
bloodsucker = math.pi*0.3,
boar = 0,
burer = 0,
cat = 0,
chimera = math.pi*0.1,
-- controller = 0,
dog = math.pi*0.2,
flesh = 0,
fracture = 0,
giant = 0,
-- poltergeist = 0,
pseudodog = 0,
psy_dog = math.pi*0.8,
psy_dog_phantom = 0,
snork = math.pi*0.1,
tushkano = 0,
zombie = 0,
}
local t = {}
for name,cls_id in pairs(rx_utils.creatures_clslist) do
t[cls_id] = md_monsters[name] or nil
end
md_monsters = t
t = nil
local function suitable_target(npc,target)
return target:alive() and md_monsters[target:clsid()] and npc:position():distance_to_sqr(target:position()) < md_max_dist --npc:max_ignore_monster_distance()
end
class "evaluator_monster_defend" (property_evaluator)
function evaluator_monster_defend:__init(npc,storage,name) super (nil,name)
self.st = storage
self.st.check_time = 0
rx_ai.subscribe_for_events(npc,self)
end
function evaluator_monster_defend:evaluate()
local npc = self.object
-- if npc:critically_wounded() then
-- return false
-- end
local be = npc:best_enemy()
if not (be and npc:best_weapon()) then
return false
end
local target = self.st.target and (db.storage[self.st.target] and db.storage[self.st.target].object or level.object_by_id(self.st.target))
if target and suitable_target(npc,target) and (npc:see(target) or target:see(npc)) then
return true
else
self.st.target = nil
end
if self.st.check_time > time_global() then
return false
end
self.st.check_time = time_global()+700
if npc:see(be) and suitable_target(npc,be) then
self.st.target = be:id()
return true
end
return false
end
function evaluator_monster_defend:hit_callback(amount,_,who)
local npc = self.object
if who and amount > 2 and suitable_target(npc,who) then
self.st.target = who:id()
end
end
class "action_monster_defend" (planner_action)
function action_monster_defend:__init (npc,storage,action_name,planner) super (nil,action_name)
self.st = storage
self.evaluator_ready_to_kill = planner:evaluator(stalker_ids.property_ready_to_kill)
self.rand_dif = math.random(-30,30)/100
self.ddr = math.random() < 0.5 and 1 or -1
end
function action_monster_defend:initialize()
action_base.initialize(self)
local npc = self.object
npc:set_desired_position()
npc:set_desired_direction()
npc:set_mental_state(anim.danger)
npc:set_path_type(game_object.level_path)
npc:set_dest_level_vertex_id(npc:level_vertex_id())
end
function action_monster_defend:execute()
action_base.execute(self)
local npc = self.object
local target = self.st.target and (db.storage[self.st.target] and db.storage[self.st.target].object or level.object_by_id(self.st.target))
if not target then
return
end
if npc:path_type() ~= game_object.level_path then
npc:set_path_type(game_object.level_path)
end
local wpn = npc:best_weapon()
if not wpn then
return
end
if self.evaluator_ready_to_kill:evaluate() ~= true and wpn:get_ammo_in_magazine() ~= 0 then -- <09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
if self.weapon_fix_time then
if self.weapon_fix_time < time_global() then
self.st.target = nil
self.st.check_time = time_global()+5000
return
end
else
self.weapon_fix_time = time_global()+4000
end
else
self.weapon_fix_time = nil
end
if npc:id()%2 == 0 and wpn:get_ammo_in_magazine() == 0 and not npc:path_completed() then
npc:set_sight(look.path_dir,nil,0)
npc:set_item(object.aim1,wpn)
elseif not npc:see(target) then
npc:set_sight(target,true,false)
npc:set_item(object.aim1,wpn)
else
local fire_point = rx_utils.safe_bone_pos(target,"bip01_neck") -- target:center() rx_utils.safe_bone_pos(target,"bip01_neck") target:bone_position(rx_utils.get_fire_bone(target:section()))
-- fire_point.y = fire_point.y+0.15
npc:set_sight(look.direction,fire_point:sub(npc:bone_position("bip01_neck")))
-- npc:set_sight(look.point,fire_point,true)
local magsize = math.min(rx_utils.get_mag_size(wpn:section()),50)
local min_queue = math.floor(magsize/5)
local max_queue = math.ceil(magsize/3)
npc:set_item(object.fire1,wpn,math.random(min_queue,max_queue),700)
end
local dist = npc:position():distance_to_sqr(target:position())
local target_be = target:get_enemy()
if not (target_be and target_be:id() == npc:id()) then
if dist < md_walk_dist then
npc:set_movement_type(move.walk)
local vertex = self:get_dodge_vertex(npc:level_vertex_id(),target:position(),0,1)
if vertex then
npc:set_dest_level_vertex_id(vertex)
end
end
return
end
if dist < md_run_dist then
npc:set_movement_type(move.run)
npc:set_body_state(move.standing)
elseif dist < md_walk_dist then
npc:set_movement_type(move.walk)
else
return
end
local vertex = self:get_dodge_vertex(npc:level_vertex_id(),target:position(),md_monsters[target:clsid()])
if not vertex then
self.ddr = -self.ddr
self.st.target = nil
self.st.check_time = time_global()+4000
return
end
npc:set_dest_level_vertex_id(vertex)
end
function action_monster_defend:finalize()
action_base.finalize(self)
self.weapon_fix_time = nil
end
local _H_step = math.pi/6
function action_monster_defend:get_dodge_vertex(lvid,pos,imod,tc)
local npc = self.object
local npc_pos = npc:position()
local dir = vector():sub(npc:position(),pos)
local dH,dP,res = dir:getH()+self.rand_dif,dir:getP()
local found,mod
imod = imod or 0
for i=0,(tc or 5) do
mod = imod > 0 and imod+i*_H_step or imod-i*_H_step
if mod > 2.6 then
break
end
res = npc:vertex_in_direction(lvid,vector():setHP(dH+mod*self.ddr,dP),4)
if (lvid ~= res and npc:accessible(res) and level.vertex_position(res):distance_to_sqr(npc_pos) > 8) then
found = true
break
end
end
return found and res
end
class "action_take_cover" (action_base)
function action_take_cover:__init (npc,storage,action_name,action) super (nil,action_name)
self.st = storage
self.inherited = action
end
function action_take_cover:initialize()
self.inherited:initialize()
local npc = self.object
if math.random() > npc.health-0.15 then
npc:set_movement_type(move.run)
end
local wpn = npc:best_weapon()
self.max_queue = wpn and math.ceil(rx_utils.get_mag_size(wpn:section())/5) or 5
rx_ai.subscribe_for_events(npc,self)
end
function action_take_cover:execute()
self.inherited:execute()
local npc = self.object
if self.use_crouch == true then
npc:set_body_state(move.crouch)
end
local enemy = npc:best_enemy()
if enemy and npc:see(enemy) then
local npc_pos,enemy_pos = npc:position(),enemy:position()
if math.abs(npc_pos.y - enemy_pos.y) > 2 then
local int = npc_pos:distance_to(enemy_pos)*26
npc:set_item(object.fire1,npc:best_weapon(),math.random(1,self.max_queue),int < 400 and 400 or int > 2500 and 2500 or int)
end
end
end
function action_take_cover:hit_callback(amount)
local npc = self.object
npc:set_movement_type(move.run)
if self.use_crouch == nil then
self.use_crouch = math.random() < 0.35
local wpn = npc:best_weapon()
self.max_queue = wpn and math.ceil(rx_utils.get_mag_size(wpn:section())/3) or 10
end
end
function action_take_cover:finalize()
self.inherited:finalize()
self.use_crouch = nil
rx_ai.unsubscribe_from_events(self.object:id(),self)
end
class "evaluator_sudden_attack" (property_evaluator)
function evaluator_sudden_attack:__init(npc,storage,name) super (nil,name)
self.st = storage
-- rx_ai.subscribe_for_events(npc,self)
end
function evaluator_sudden_attack:evaluate()
local npc = self.object
local be = npc:best_enemy()
if not be then
return true
end
if self.st.enable_panic then
return false
end
local dist = npc:position():distance_to(be:position())
if dist > 150 then
self.storage:set_property(stalker_ids.property_use_suddenness,false)
return true
end
return true
end
function evaluator_sudden_attack:hit_callback(amount,_,who)
if who and amount > 2 then
self.storage:set_property(stalker_ids.property_use_suddenness,false)
end
end
class "evaluator_low_cover" (property_evaluator)
function evaluator_low_cover:__init(npc,storage,name) super (nil,name)
self.st = storage
end
function evaluator_low_cover:evaluate()
local npc = self.object
local be = npc:best_enemy()
if not be then
return false
end
if not self.storage:property(stalker_ids.property_in_cover) then
return false
end
if not npc:best_weapon() then
return false
end
local npc_pos,enemy_pos = npc:position(),npc:memory_position(be)
local cover = npc:best_cover(npc_pos, enemy_pos, 1, 5, 150)
if not cover then
return false
end
local cover_pos = cover:position()
if npc_pos:distance_to(cover_pos) > 0.1 then
return false
end
local lvid = cover:level_vertex_id()
local dir = vector():sub(enemy_pos,cover_pos)
local low = level.low_cover_in_direction(lvid,dir)
local high = level.high_cover_in_direction(lvid,dir)
return (low+0.2 < high)
end
----------------------------------------------------------------------------------------------------------------------
-- BINDER
----------------------------------------------------------------------------------------------------------------------
evid_monster_defend = rx_ai.base_id+45
evid_sudden_attack = evid_monster_defend+1
actid_monster_defend = stalker_ids.action_get_distance
function add_combat(npc,st)
local storage = rx_ai.get_storage(npc:id(),"combat_add")
local manager = npc:motivation_action_manager()
local combat_action = manager:action(stalker_ids.action_combat_planner)
local combat_action_planner = cast_planner(combat_action)
-- MONSTER DEFEND
if md_enabled and npc:character_rank() >= md_min_rank then
combat_action_planner:add_evaluator(evid_monster_defend,evaluator_monster_defend(npc,storage,"eva_monster_defend"))
local new_action = action_monster_defend(npc,storage,"monster_defend",combat_action_planner)
new_action:add_precondition(world_property(stalker_ids.property_critically_wounded,false))
new_action:add_precondition(world_property(stalker_ids.property_danger_grenade,false))
new_action:add_precondition(world_property(stalker_ids.property_use_suddenness,false))
-- new_action:add_precondition(world_property(stalker_ids.property_ready_to_kill,true))
new_action:add_precondition(world_property(stalker_ids.property_inside_anomaly+3,false)) -- eWorldPropertyInSmartCover
new_action:add_precondition(world_property(stalker_ids.property_panic,false))
new_action:add_precondition(world_property(stalker_ids.property_pure_enemy+2,false)) -- eWorldPropertyEnemyWounded
new_action:add_precondition(world_property(stalker_ids.property_pure_enemy+5,false)) -- eWorldPropertyPlayerOnThePath
-- new_action:add_precondition(world_property(stalker_ids.property_critically_wounded+4,false)) -- eWorldPropertyTooFarToKillEnemy
-- new_action:add_precondition(world_property(stalker_ids.property_pure_enemy,true))
new_action:add_precondition(world_property(evid_monster_defend,true))
new_action:add_effect(world_property(stalker_ids.property_pure_enemy,false))
new_action:add_effect(world_property(evid_monster_defend,false))
-- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
-- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
combat_action_planner:remove_action(stalker_ids.action_get_distance) -- <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
combat_action_planner:add_action(stalker_ids.action_get_distance,new_action)
end
-- TAKE COVER
if run_on_hit then
local old_action = combat_action_planner:action(stalker_ids.action_take_cover)
old_action:remove_effect(stalker_ids.property_in_cover)
old_action:remove_effect(stalker_ids.property_looked_out)
old_action:remove_effect(stalker_ids.property_position_holded)
old_action:remove_effect(stalker_ids.property_enemy_detoured)
-- combat_action_planner:remove_action(stalker_ids.action_take_cover)
local new_action = action_take_cover(npc,storage,"monster_defend",old_action)
new_action:add_precondition(world_property(stalker_ids.property_critically_wounded,false))
new_action:add_precondition(world_property(stalker_ids.property_danger_grenade,false))
new_action:add_precondition(world_property(stalker_ids.property_use_suddenness,false))
new_action:add_precondition(world_property(stalker_ids.property_item_to_kill,true))
new_action:add_precondition(world_property(stalker_ids.property_item_can_kill,true))
new_action:add_precondition(world_property(stalker_ids.property_ready_to_kill,true))
new_action:add_precondition(world_property(stalker_ids.property_in_cover,false))
new_action:add_precondition(world_property(stalker_ids.property_inside_anomaly+3,false)) -- eWorldPropertyInSmartCover
new_action:add_precondition(world_property(stalker_ids.property_pure_enemy+2,false)) -- eWorldPropertyEnemyWounded
new_action:add_precondition(world_property(stalker_ids.property_pure_enemy+5,false)) -- eWorldPropertyPlayerOnThePath
new_action:add_effect(world_property(stalker_ids.property_in_cover,true))
new_action:add_effect(world_property(stalker_ids.property_looked_out,false))
new_action:add_effect(world_property(stalker_ids.property_position_holded,false))
new_action:add_effect(world_property(stalker_ids.property_enemy_detoured,false))
combat_action_planner:add_action(stalker_ids.action_take_cover,new_action)
end
-- SUDDEN ATTACK
-- combat_action_planner:add_evaluator(evid_sudden_attack,evaluator_sudden_attack(npc,storage,"eva_sudden_attack"))
-- local old_action = combat_action_planner:action(stalker_ids.action_sudden_attack)
-- old_action:add_precondition(world_property(evid_sudden_attack,true))
-- LOW COVER
-- combat_action_planner:remove_evaluator(stalker_ids.property_inside_anomaly+2) -- eWorldPropertyLowCover
-- combat_action_planner:add_evaluator(stalker_ids.property_inside_anomaly+2,evaluator_low_cover(npc,storage,"eva_low_cover")) -- eWorldPropertyLowCover
-- if not set_inertia_time_15 then
-- local danger_action_planner = cast_planner(manager:action(stalker_ids.action_danger_planner))
-- local danger_unknown_planner = cast_planner(danger_action_planner:action(stalker_ids.action_danger_unknown_planner))
-- set_inertia_time_15 = danger_unknown_planner:action(stalker_ids.action_danger_unknown_look_around).initialize -- 15000
-- local danger_indirection_planner = cast_planner(danger_action_planner:action(stalker_ids.action_danger_in_direction_planner))
-- set_inertia_time_5_10 = danger_indirection_planner:action(stalker_ids.action_danger_in_direction_hold_position).initialize -- 5000-10000
-- end
-- local low_cover_action = combat_action_planner:action(stalker_ids.action_kill_if_enemy_critically_wounded+8)
-- local low_cover_planner = cast_planner(low_cover_action)
-- st.lowcover_hold_position_act = low_cover_planner:action(stalker_ids.action_hold_position)
end
----------------------------------------------------------------------------------------------------------------------
-- UPDATE
----------------------------------------------------------------------------------------------------------------------
function combat_planner_update(npc,st)
if not st.combat_planner then
st.combat_planner = cast_planner(st.planner:action(stalker_ids.action_combat_planner))
end
local action_id = st.combat_planner:current_action_id()
-- <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if action_id == stalker_ids.action_sudden_attack and not npc:active_item() then
npc:set_item(object.idle,npc:best_weapon()) --rx_utils.get_weapon(npc)
end
if action_id ~= st.combat_planner_action_id then
st.combat_planner_action_id = action_id
st.combat_planner_fix_timer = nil
if action_id == stalker_ids.action_get_ready_to_kill or action_id == stalker_ids.action_get_ready_to_kill+1 then
st.combat_planner_fix_timer = time_global()+3300
-- elseif action_id == stalker_ids.action_look_out then
-- st.combat_planner_fix_timer = time_global()+15000
-- elseif action_id == stalker_ids.action_critically_wounded then
-- st.combat_planner_fix_timer = time_global()+5000
-- elseif action_id == stalker_ids.action_kill_if_enemy_critically_wounded+8 then -- eWorldOperatorLowCover
-- set_inertia_time_5_10(st.lowcover_hold_position_act)
elseif action_id == stalker_ids.action_search_enemy then
if math.random() < 0.3 then
npc:set_movement_type(move.run)
end
end
elseif st.combat_planner_fix_timer and st.combat_planner_fix_timer < time_global() then
st.combat_planner_fix_timer = nil
if action_id == stalker_ids.action_get_ready_to_kill then
-- <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> strap
-- <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
local wpn = npc:active_item()
if not npc:weapon_unstrapped() then
npc:drop_item(wpn)
elseif wpn and wpn:get_ammo_in_magazine() == wpn:get_ammo_total() then
wpn:unload_magazine()
end
elseif action_id == stalker_ids.action_get_ready_to_kill+1 then -- eWorldOperatorGetReadyToDetour
-- <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
local wpn = npc:active_item()
local aminmag = wpn and wpn:get_ammo_in_magazine()
if wpn and aminmag == wpn:get_ammo_total() then
local magsize = rx_utils.get_mag_size(wpn:section())
wpn:set_ammo_elapsed(aminmag < magsize and magsize or aminmag+10)
end
-- elseif action_id == stalker_ids.action_look_out then
-- -- <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
-- local enemy = npc:best_enemy()
-- local dist = npc:position():distance_to(npc:memory_position(enemy))
-- npc:enable_memory_object(enemy,false)
-- elseif action_id == stalker_ids.action_critically_wounded then
end
end
end