--[[ scheme_type: active_section author: Alundaio TODO: Fix use camp HiveGame: Fix use camp --]] --------------- beh_evid = 188111 beh_actid = 188111 --------------- ----------------------------- local level = level local alife = alife ----------------------------- ----------------------------- local keep_state_min_time = 2000 local function angle_dir(ang) return vector():setHP(ang.y,ang.x):normalize() end local function init_custom_data(npc, ini, section, st, scheme) local def = {} --[[ {+npcx_beh_relax} --]] def.behavior_state = "{+npcx_beh_wait -npcx_beh_relax} beh_wait, {+npcx_beh_patrol_mode !is_squad_commander} beh_move, {+npcx_beh_patrol_mode} beh_patrol, {+npcx_beh_relax} beh_patrol, beh_move" def.goto_target = "{!is_squad_commander} commander, actor" def.walk_dist = "{+npcx_beh_patrol_mode} 60, {+npcx_beh_relax} 60, 3" def.jog_dist = "{+npcx_beh_patrol_mode} 120, {+npcx_beh_relax} 120, 6" def.keep_distance = "{+npcx_beh_distance_far} far, near" def.near_desired_dist = "{+npcx_beh_patrol_mode =is_squad_commander} 1, {!is_squad_commander} 2, 3" def.far_desired_dist = "{+npcx_beh_patrol_mode =is_squad_commander} 1, {!is_squad_commander} 2, 8" def.wait_cond = "{+npcx_beh_substate_stealth} hide, guard" def.walk_cond = "{+npcx_beh_patrol_mode -npcx_beh_substate_stealth} patrol,{+npcx_beh_substate_stealth} sneak, patrol" def.jog_cond = "{+npcx_beh_substate_stealth} sneak_run, rush" def.run_cond = "{+npcx_beh_substate_stealth} rush, assault" def.delay_cond = "{+npcx_beh_substate_stealth} hide, guard" --def.use_camp_cond = "{+npcx_beh_relax} true, false" st.behavior_state = ini:r_string_to_condlist(section,"behavior_state",def.behavior_state) st.goto_target = ini:r_string_to_condlist(section,"target",def.goto_target) st.walk_dist = ini:r_string_to_condlist(section,"walk_dist",def.walk_dist) st.jog_dist = ini:r_string_to_condlist(section,"jog_dist",def.jog_dist) st.keep_distance = ini:r_string_to_condlist(section,"keep_dist",def.keep_distance) st.near_desired_dist = ini:r_string_to_condlist(section,"near_desired_dist",def.near_desired_dist) st.far_desired_dist = ini:r_string_to_condlist(section,"far_desired_dist",def.far_desired_dist) st.wait_cond = ini:r_string_to_condlist(section,"wait_anim",def.wait_cond) st.walk_cond = ini:r_string_to_condlist(section,"walk_anim",def.walk_cond) st.jog_cond = ini:r_string_to_condlist(section,"jog_anim",def.jog_cond) st.run_cond = ini:r_string_to_condlist(section,"run_anim",def.run_cond) st.delay_cond = ini:r_string_to_condlist(section,"delay_anim",def.delay_cond) st.path_end = ini:r_string_to_condlist(section,"path_end") st.sound_idle = ini:r_string_ex(section,"sound_idle") st.use_camp = ini:r_bool_ex(section,"use_camp") st.camp_found = false st.signals = {} st.path_index = load_var(npc,"path_index",1) st.wait_delay = nil st.am_i_reached = nil st.rally_lvid = nil end function am_i_reached(npc,index) local st = npc and db.storage[npc:id()] if (index) then return st and st.active_scheme == "beh" and st.beh.am_i_reached and st.beh.path_index == index end return st and st.active_scheme == "beh" and st.beh.am_i_reached end local used_vids = {} local assoc_tbl = { idle = {director = {"sit"}, listener = {"sit","sit_ass","sit_knee","sit_ass_weapon","sit_ass_eat_bread","sit_ass_drink_vodka","sit_ass_drink_energy","sit_ass_eat_kolbasa"}}, harmonica = {director = {"play_harmonica"}, listener = {"sit","sit_ass","sit_knee","sit_ass_weapon","sit_ass_eat_bread","sit_ass_drink_vodka","sit_ass_drink_energy","sit_ass_eat_kolbasa"}}, guitar = {director = {"play_guitar"}, listener = {"sit","sit_ass","sit_knee","sit_ass_weapon","sit_ass_eat_bread","sit_ass_drink_vodka","sit_ass_drink_energy","sit_ass_eat_kolbasa"}}, story = {director = {"sit"}, listener = {"sit","sit_ass","sit_knee","sit_ass_weapon","sit_ass_eat_bread","sit_ass_drink_vodka","sit_ass_drink_energy","sit_ass_eat_kolbasa"}}, } --------------------------------Action Base Evaluator --------------------------------------------------------------------- class "evaluator_beh" (property_evaluator) function evaluator_beh:__init(storage, name) super (nil, name) self.st = storage self.st.approved_actions = {"play_harmonica","play_guitar","sit","sit_ass","sit_knee","sit_ass_weapon","sit_ass_eat_bread","sit_ass_drink_vodka","sit_ass_drink_energy","sit_ass_eat_kolbasa" } end function evaluator_beh:evaluate() return xr_logic.is_active(self.object, self.st) or false end --------------------------------------Action Base -------------------------------------------------------------------------------- class "action_beh" (action_base) function action_beh:__init(npc_name, action_name, storage) super (nil, action_name) self.st = storage end function action_beh:initialize() action_base.initialize(self) self.keep_state_until = time_global() self.object:set_sight(look.direction, self.object:direction() , 0) end function action_beh:execute() action_base.execute(self) local keep_distance = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.keep_distance) self.st.desired_distance = tonumber(xr_logic.pick_section_from_condlist(db.actor, self.object, self.st[keep_distance.."_desired_dist"])) self.st.behavior = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.behavior_state,"behavior_state") or "beh_move" self.st.wait_animation = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.wait_cond) self.st.walk_animation = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.walk_cond) self.st.jog_animation = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.jog_cond) self.st.run_animation = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.run_cond) self.st.delay_animation = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.delay_cond) if (self.st.last_behavior ~= self.st.behavior) then self.st.last_behavior = self.st.behavior self.st.rally_lvid = nil end if (self:set_desired_target() and self[self.st.behavior]) then self[self.st.behavior](self) end ------------------ SURGE ------------------ if (xr_conditions.surge_started()) then if (self.object:dont_has_info("npcx_beh_surge")) then self.object:give_info_portion("npcx_beh_surge") end else if (self.object:has_info("npcx_beh_surge")) then self.object:disable_info_portion("npcx_beh_surge") end end if (self.object:dont_has_info("npcx_beh_surge")) then if (se_load_var(self.object:id(), self.object:name(), "cover") ~= nil) then se_save_var(self.object:id(), self.object:name(), "cover", nil) end end ------------------ SURGE ------------------ ------------------ RELAX ------------------ if (self.object:dont_has_info("npcx_beh_relax")) then if (se_load_var(self.object:id(), self.object:name(), "campfire_step") ~= nil) then se_save_var(self.object:id(), self.object:name(), "campfire_step", nil) end if (self.st.camp ~= nil) then xr_sound.stop_sounds_by_id(self.object:id()) self.st.camp:unregister_npc(self.object:id()) self.st.camp = nil end end ------------------ RELAX ------------------ end function action_beh:set_desired_target() local target = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.goto_target) if (target == nil or target == "" or target == "nil") then return false end if (self.st.desired_target) then if (self.st.target ~= target) then self.st.target = target self.st.desired_target.__keep_until = nil self.st.rally_lvid = nil end local tg = time_global() if (self.st.desired_target.__keep_until) then -- do forever if keep_until is -1 if (self.st.desired_target.__keep_until == -1) then return true end if (tg < self.st.desired_target.__keep_until) then return true end end if (self.st.desired_target.level_vertex_id) then db.used_level_vertex_ids[self.st.desired_target.level_vertex_id] = nil end end self.st.target = target local npc = self.object local t = self.st.desired_target and empty_table(self.st.desired_target) or {} local function set_position_data(id) local obj = level.object_by_id(id) local sobj = alife_object(id) if (obj) then t.level_vertex_id = obj:level_vertex_id() t.game_vertex_id = obj:game_vertex_id() t.position = obj:position() t.direction = obj:direction() t.object = obj elseif (sobj) then t.level_vertex_id = sobj.m_level_vertex_id t.game_vertex_id = sobj.m_game_vertex_id t.position = sobj.position t.direction = self.object:position():sub(t.position) t.object = sobj end end local obj = get_story_object(target) if (obj) then set_position_data(obj:id()) elseif (target == "enemy") then local st = db.storage[self.object:id()] local be = self.object:best_enemy() or st and st.enemy_id and level.object_by_id(st.enemy_id) if (be and be:alive()) then t.level_vertex_id = be:level_vertex_id() t.position = be:position() t.direction = self.object:position():sub(t.position) t.object = be end elseif (target == "cover_near_actor") then local vid = utils_obj.find_close_cover(self.object,db.actor:position(),db.actor:position()) or self.object:level_vertex_id() if (vid) then t.level_vertex_id = vid t.position = level.vertex_position(vid) t.direction = self.object:position():sub(t.position) t.object = db.actor db.used_level_vertex_ids[vid] = self.object:id() t.__keep_until = time_global() + 3000 self.st.behavior = "beh_cover" end elseif (target == "cover_wait") then local vid = utils_obj.try_go_cover(self.object,self.object,self.st.desired_target and self.st.desired_target.level_vertex_id or nil,3) or self.object:level_vertex_id() if (vid) then db.used_level_vertex_ids[vid] = self.object:id() t.level_vertex_id = vid t.position = level.vertex_position(vid) t.direction = self.object:position():sub(t.position) local dist = db.actor:position():distance_to_sqr(self.object:position()) t.object = dist <= 5 and db.actor or nil t.__keep_until = -1 self.st.behavior = "beh_cover" end elseif (target == "task_target_anomaly") then local name = db.actor and load_var(db.actor,"task_target_anomaly") if (name) then local anomaly = db.anomaly_by_name[name] if (anomaly and anomaly.object) then local vid = utils_obj.try_go_cover(self.object,anomaly.object:position(),self.st.desired_target and self.st.desired_target.level_vertex_id or nil) if (vid) then db.used_level_vertex_ids[vid] = self.object:id() t.level_vertex_id = vid t.position = level.vertex_position(vid) t.direction = self.object:position():sub(t.position) t.object = anomaly.object t.__keep_until = time_global() + 8000 self.st.behavior = "beh_cover" end end end elseif (target == "task_target_hostage") then local vid = utils_obj.try_go_cover(self.object,self.object,self.st.desired_target and self.st.desired_target.level_vertex_id or nil,3) or self.object:level_vertex_id() if (vid) then db.used_level_vertex_ids[vid] = self.object:id() t.level_vertex_id = vid t.position = level.vertex_position(vid) t.direction = self.object:position():sub(t.position) --t.object = db.actor t.__keep_until = -1 self.st.behavior = "beh_cover" self.st.rally_lvid = nil end ------------------ SURGE ------------------ elseif (xr_conditions.surge_started()) then local tg = time_global() local actor = db.actor local npcpos = npc:position() if (se_load_var(npc:id(), npc:name(), "cover") ~= nil) then local cover = se_load_var(npc:id(), npc:name(), "cover") local coverl = se_load_var(npc:id(), npc:name(), "coverlast") local dist = se_load_var(npc:id(), npc:name(), "coverdist") local timerl = se_load_var(npc:id(), npc:name(), "covertimel") local timern = se_load_var(npc:id(), npc:name(), "covertimen") if (coverl == nil) then coverl = npcpos end if (timerl == nil) then timerl = tg end if (timern == nil) then timern = tg end if (tg > timerl) then if (npcpos:distance_to_sqr(cover) < 3) then se_save_var(npc:id(), npc:name(), "covertimel", nil) se_save_var(npc:id(), npc:name(), "covertimen", nil) elseif (npcpos:distance_to_sqr(cover) < dist or npcpos:distance_to_sqr(coverl) > 1) then dist = npcpos:distance_to_sqr(cover) se_save_var(npc:id(), npc:name(), "coverlast", npcpos) se_save_var(npc:id(), npc:name(), "coverdist", dist) se_save_var(npc:id(), npc:name(), "covertimen", nil) se_save_var(npc:id(), npc:name(), "covertimel", tg+2000) else se_save_var(npc:id(), npc:name(), "coverdist", nil) se_save_var(npc:id(), npc:name(), "covertimel", nil) se_save_var(npc:id(), npc:name(), "covertimen", tg+5000) end elseif (tg > timern) then se_save_var(npc:id(), npc:name(), "cover", nil) se_save_var(npc:id(), npc:name(), "coverlast", nil) se_save_var(npc:id(), npc:name(), "coverdist", nil) se_save_var(npc:id(), npc:name(), "covertimel", nil) se_save_var(npc:id(), npc:name(), "covertimen", nil) end -- Move to savezone -- if (dist == nil) then t.position = actor:position() t.level_vertex_id = actor:level_vertex_id() t.direction = actor:direction() self.st.desired_target = t self.st.behavior = "beh_move" else t.position = cover t.level_vertex_id = level.vertex_id(cover) t.direction = npc:position():sub(cover) self.st.desired_target = t self.st.behavior = "beh_path" end elseif (hg_companion) then se_save_var(npc:id(), npc:name(), "coverlast", nil) se_save_var(npc:id(), npc:name(), "coverdist", nil) se_save_var(npc:id(), npc:name(), "covertimel", nil) se_save_var(npc:id(), npc:name(), "covertimen", nil) local covers = hg_companion:get_covers() local dist = 10000 -- Search savezone -- local vid for name,id in pairs(covers) do local cover = db.zone_by_name[name] if (cover) then if (npcpos:distance_to_sqr(cover:position()) < dist) then dist = npcpos:distance_to_sqr(cover:position()) se_save_var(npc:id(), npc:name(), "cover", cover:position()) se_save_var(npc:id(), npc:name(), "coverlast", npcpos) se_save_var(npc:id(), npc:name(), "coverdist", dist) se_save_var(npc:id(), npc:name(), "covertimel", tg+2000) end end end t.position = actor:position() t.level_vertex_id = actor:level_vertex_id() t.direction = actor:direction() self.st.desired_target = t self.st.behavior = "beh_move" end ------------------ SURGE ------------------ ------------------ RELAX ------------------ elseif (self.object:has_info("npcx_beh_relax")) then local name = npc:character_name() local npcid = npc:id() local function save_step(npc, step) se_save_var(npc:id(), npc:name(), "campfire_step", step) end if (se_load_var(npcid, npc:name(), "campfire_step") ~= nil) then if (se_load_var(npcid, npc:name(), "campfire") ~= nil) then local step = se_load_var(npcid, npc:name(), "campfire_step") local campfire = se_load_var(npcid, npc:name(), "campfire") local vid = se_load_var(npcid, npc:name(), "campfire_vid") local look = se_load_var(npcid, npc:name(), "campfire_look") local npos = npc:position() if (step < 1) then t.level_vertex_id = vid t.position = level.vertex_position(vid) t.direction = self.object:position():sub(t.position) -- Move to camp -- self.st.desired_target = t self.st.behavior = "beh_path" -- Moved to camp -- if (npos:distance_to_sqr(t.position) < 1) then local tg = time_global() -- Sleep or talk-- local sleep = math.random(100) if (sleep < 5) then self.current_action = "sleep_sit" else self.current_action = "sit_ass" end state_mgr.set_state(self.object, self.current_action, nil, nil, {look_position = look}) se_save_var(npcid, npc:name(), "campfire_time", tg + 5000) --printdbg("%s came to campfire", npc:character_name()) save_step(npc, 1) end else -- ReMoved to camp -- if (npos:distance_to_sqr(level.vertex_position(vid)) > 2) then save_step(npc, 0) end -- Camp -- local state = state_mgr.get_state(self.object) local camp = sr_camp.get_current_camp(npc:position()) if (camp) then if (state ~= "sleep_sit") then self.st.camp = camp camp:register_npc(npc:id()) self.in_camp = true elseif (state == "sleep_sit") then if (self.in_camp) then camp:unregister_npc(npc:id()) self.in_camp = nil self.st.camp = nil end end elseif (self.in_camp == true and not self.st.camp) then self.st.camp:unregister_npc(npc:id()) self.in_camp = nil self.st.camp = nil end -- Animation -- local tg = time_global() local tg_delay = se_load_var(npcid, npc:name(), "campfire_time") if (tg > tg_delay) then local state = self.current_action local new_state = self:relax_animations() if (new_state ~= state) then --local camp_action, is_director = self.st.camp:get_camp_action(self.object:id()) --local role = is_director and "director" or "listener" --printdbg("%s is %s", npc:character_name(), role) state_mgr.set_state(self.object, new_state, nil, nil, {look_position = look}) se_save_var(npcid, npc:name(), "campfire_time", tg + math.random(120000,240000)) end end -- Sound -- self.st.sound_tg = self.st.sound_tg or tg + math.random(10000,30000) if (tg > self.st.sound_tg) then local sound = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.sound_idle) if (sound and sound ~= "nil" and sound ~= "") then xr_sound.set_sound_play(self.object:id(), sound) end self.st.sound_tg = nil end end end else -- Save campfire -- if (se_load_var(npcid, npc:name(), "campfire") ~= nil) then if (se_load_var(npcid, npc:name(), "fire") ~= nil) then local fire = se_load_var(npcid, npc:name(), "fire") if (not fire:is_on()) then fire:turn_on() end end local vid,look local campfire = se_load_var(npcid, npc:name(), "campfire") local dir = vector():set(campfire:direction()) local pos = campfire:position() local member = se_load_var(npcid, npc:name(), "member_group") or 1 local all = se_load_var(npcid, npc:name(), "member_all") or 1 vid = self:vertex_create(pos,vector_rotate_y(dir,360/all*member),3) look = pos if (vid ~= nil) then se_save_var(npcid, npc:name(), "campfire_vid", vid) se_save_var(npcid, npc:name(), "campfire_look", look) --printdbg("%s found campfire", npc:character_name()) save_step(npc, 0) return end end local npcpos = npc:position() local dist = 10000 -- Search campfire -- for id,binder in pairs(bind_campfire.campfires_all) do if (binder and binder.campfire) then if (npcpos:distance_to_sqr(binder.object:position()) < dist) then se_save_var(npcid, npc:name(), "campfire", binder.object) se_save_var(npcid, npc:name(), "fire", binder.campfire) dist = npcpos:distance_to_sqr(binder.object:position()) end end end return end ------------------ RELAX ------------------ elseif (target == "waypoint") then t = self:get_current_waypoint() self.st.behavior = "beh_path" elseif (target == "commander") then local squad = get_object_squad(self.object) if not(squad:commander_id() == self.object:id()) then set_position_data(squad:commander_id()) end elseif (string.find(target,"lvid:")) then local vid,pos for s in string.gmatch(target,"lvid:(%d+)") do vid = tonumber(s) end if (vid) then t.level_vertex_id = vid t.position = level.vertex_position(vid) t.direction = self.object:position():sub(t.position) t.object = nil end elseif (string.find(target,"id:")) then local id for s in string.gmatch(target,"id:(%w+)") do id = tonumber(s) end if (id) then set_position_data(id) end elseif (target == "scan_area") then self.st.behavior = "beh_cover" end if not (t.level_vertex_id) then return false end self.st.desired_target = t return true end function action_beh:beh_cover() if (self.st.desired_target and self.st.desired_target.level_vertex_id and self.object:level_vertex_id() ~= self.st.desired_target.level_vertex_id) then state_mgr.set_state(self.object, "rush") utils_obj.send_to_nearest_accessible_vertex(self.object,self.st.desired_target.level_vertex_id) return end local t = time_global() if (self.keep_state_until and t < self.keep_state_until) then return end self.keep_state_until = t + keep_state_min_time local new_state = self.st.wait_animation or "hide" if (new_state == "rand") then new_state = random_choice("binocular","idle","hide","caution") end state_mgr.set_state(self.object,new_state,nil,nil,{look_object = self.st.desired_target and self.st.desired_target.object or nil}) end function action_beh:beh_wait() local new_state = self.st.wait_animation local state = state_mgr.get_state(self.object) if (new_state and new_state ~= state) then local target = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.goto_target) local lobj = target and target ~= "" and get_story_object(target) state_mgr.set_state(self.object, new_state,nil, nil, {look_object = lobj }) end end function action_beh:set_state() local t = time_global() if (self.keep_state_until and t < self.keep_state_until) then return end self.keep_state_until = t + keep_state_min_time local new_state = self.st.wait_animation local dist_walk = tonumber(xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.walk_dist) or 5) or 5 local dist_jog = tonumber(xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.jog_dist) or 10) or 10 if (self.st.assist_point == nil or self.st.assist_point == self.object:level_vertex_id()) then new_state = self.st.wait_animation elseif (self.dist_to_assist_pt <= dist_walk ) then new_state = self.st.walk_animation elseif (self.dist_to_assist_pt <= dist_jog ) then new_state = self.st.jog_animation else new_state = self.st.run_animation end state_mgr.set_state(self.object, new_state,nil, nil, nil, { fast_set = true, animation = 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,distance) local closest local data_companion = hg_companion:return_squad() if (object:has_info("npcx_is_companion") and data_companion[object:id()] ~= nil) then local actor = db.actor local hg_angle = 40 local hg_angle_rnd = 1 local movement = hg_companion:return_movement() local hg_dist = 1 if (hg_companion) then hg_dist = hg_companion:return_distance() end local k = data_companion[object:id()] local m = k.move local d = k.dist local action = k.action local s = 1 local angle = 180 -- Патруль if (object:has_info("npcx_beh_patrol_mode")) then distance = 3*(hg_dist/2) s = k.squad m = k.number angle = 180-(hg_angle+20)*(s-2)+(hg_angle+20)*2*(m-1) else local formation = true if (hg_companion) then formation = hg_companion:return_formation() end lvid = actor:level_vertex_id() dir = actor:direction() if (time_global() > 10000) then local lpos = se_load_var(object:id(), object:name(), "lpos") if (lpos == nil) then se_save_var(object:id(), object:name(), "lpos", actor:position()) elseif (actor:position():distance_to_sqr(lpos) > 3) then if (formation == true) then lvid = level.vertex_id(actor:position()) dir = actor:position():sub(lpos) end se_save_var(object:id(), object:name(), "lpos", actor:position()) se_save_var(object:id(), object:name(), "lvid", lvid) se_save_var(object:id(), object:name(), "ldir", dir) else lvid = se_load_var(object:id(), object:name(), "lvid") dir = se_load_var(object:id(), object:name(), "ldir") if (lvid == nil) then lvid = actor:level_vertex_id() end if (dir == nil) then dir = actor:direction() end end end -- Походный строй if (movement == 0) then distance = d*m*(hg_dist/2) -- Атакующий строй elseif (movement == 1) then distance = d*hg_dist if (m < 3) then m = m if (data_companion.move > 1 or data_companion.move == 1) then s = 2 else s = data_companion.move end angle = 180-hg_angle*(s-1)+hg_angle*2*(m-1)+math.random(-hg_angle_rnd, hg_angle_rnd) elseif (m < 6) then m = m - 2 if (data_companion.move > 4) then s = 3 if (m == 2) then hg_dist = 1.4 else hg_dist = 1.6 end else s = data_companion.move - 2 hg_dist = 1.6 end angle = 180-(hg_angle-20)*(s-1)+(hg_angle-20)*2*(m-1)+math.random(-hg_angle_rnd, hg_angle_rnd) else m = m - 5 s = data_companion.move - 5 angle = 180-(hg_angle-30)*(s-1)+(hg_angle-30)*2*(m-1)+math.random(-hg_angle_rnd, hg_angle_rnd) hg_dist = 2 end -- Защитный строй elseif (movement == 2) then distance = d*hg_dist if (m < 4) then s = 2 -- Дозорный (идет чуть спереди всегда на одном расстоянии) if (m == 1) then angle = -16+math.random(-hg_angle_rnd, hg_angle_rnd) distance = 16 -- Боковые (идёт слева и справа) else angle = 180-(hg_angle+60)*(s-1)+(hg_angle+60)*2*(m-2)+math.random(-hg_angle_rnd, hg_angle_rnd) hg_dist = 1.6 end elseif (m < 6) then m = m - 3 -- Тыловые (идут сзади) if (data_companion.move > 4) then s = 2 else s = data_companion.move - 3 end angle = 180-hg_angle*(s-1)+hg_angle*2*(m-1)+math.random(-hg_angle_rnd, hg_angle_rnd) hg_dist = 1.4 else -- Резерв (идут сзади) m = m - 5 s = data_companion.move - 5 angle = 180-(hg_angle-30)*(s-1)+(hg_angle-30)*2*(m-1)+math.random(-hg_angle_rnd, hg_angle_rnd) hg_dist = 2.4 end -- Цепь elseif (movement == 3) then distance = (d + d*math.floor((m-1)/2))*hg_dist angle = 90 + 180*math.floor((m)/2) end end for i=1, #self.node do if (bit_and(i,1)) then self.node[i].desired_direction = vector_rotate_y(dir, -angle) else self.node[i].desired_direction = vector_rotate_y(dir, angle) end self.node[i].vertex_id = level.vertex_in_direction(lvid, self.node[i].desired_direction, distance) if (self.node[i].vertex_id and utils_obj.accessible(object,self.node[i].vertex_id)) 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 (closest) then closest = self.node[i].vertex_id end if (self.node[i-1] and self.node[i-1].distance and self.node[i-1].distance < self.node[i].distance) then closest = self.node[i-1].vertex_id end end end else for i=1, #self.node do if (bit_and(i,1)) then self.node[i].desired_direction = vector_rotate_y(dir, -math.random(170,190)) else self.node[i].desired_direction = vector_rotate_y(dir, math.random(170,190)) end self.node[i].vertex_id = level.vertex_in_direction(lvid, self.node[i].desired_direction, distance) if (self.node[i].vertex_id and utils_obj.accessible(object,self.node[i].vertex_id)) 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 (closest) then closest = self.node[i].vertex_id end if (self.node[i-1] and self.node[i-1].distance and self.node[i-1].distance < self.node[i].distance) then closest = self.node[i-1].vertex_id end end end end return closest and closest ~= -1 and closest end function action_beh:beh_move() if not (self.st.desired_target.level_vertex_id) then return end if (self.st.desired_target.object and not simulation_objects.is_on_the_same_level(self.object,self.st.desired_target.object)) then local gvid = self.st.desired_target.game_vertex_id or self.st.desired_target.object.m_game_vertex_id self.object:set_dest_game_vertex_id(gvid) else local select_new_pt = false local target local dist_from_self_to_goto_target = self.object:position():distance_to(self.st.desired_target.position) if (self.st.rally_lvid or dist_from_self_to_goto_target >= self.st.desired_distance) then select_new_pt = true end if ( select_new_pt ) then local node = position_node(2) self.st.assist_point = self.st.rally_lvid or node:select_best_vertex_id(self.object,self.st.desired_target.direction,self.st.desired_target.level_vertex_id,self.st.desired_distance) or self.st.desired_target.level_vertex_id --self.object:level_vertex_id() self.keep_state_until = nil end if ( self.st.assist_point ) then --self.object:set_dest_level_vertex_id(self.st.assist_point) self.object:set_path_type(game_object.level_path) self.object:set_desired_direction() self.st.assist_point = utils_obj.send_to_nearest_accessible_vertex(self.object,self.st.assist_point,"axr_beh") self.object:set_desired_position(level.vertex_position(self.st.assist_point)) end end self.dist_to_assist_pt = self.st.assist_point and level.vertex_position(self.st.assist_point):distance_to(self.object:position()) or 0 --self.dist_from_self_to_goto_target = dist_from_self_to_goto_target self:set_state() end function action_beh:beh_path() if not (self.st.desired_target.level_vertex_id) then return end local next_index = self:next_waypoint_index() if (self.object:level_vertex_id() ~= self.st.desired_target.level_vertex_id) or ((next_index) and (not self.st.desired_target.delay or self.st.desired_target.delay < 0)) then self.st.am_i_reached = nil if (self.object:level_vertex_id() == self.st.desired_target.level_vertex_id) then self:increment_waypoint_index() self.st.desired_target = self:get_current_waypoint() if not (self.st.desired_target.level_vertex_id) then return end end self.object:set_path_type(game_object.level_path) self.object:set_desired_direction() --self.object:set_dest_level_vertex_id(self.st.desired_target.level_vertex_id) --printf("lvid = %s",self.st.desired_target.level_vertex_id) self.st.assist_point = utils_obj.send_to_nearest_accessible_vertex( self.object, self.st.desired_target.level_vertex_id, "axr_beh") --self.object:set_desired_position(self.st.desired_target.position) local pos = vector():set(self.object:position()) pos = vector():set(pos.x,self.st.desired_target.position.y,pos.z) self.dist_to_assist_pt = pos:distance_to(self.st.desired_target.position) self:set_state() return end self.st.am_i_reached = true if (self.st.desired_target.delay ~= 0) then if not (self.st.desired_target.delay) then self.st.desired_target.delay = 0 end local tg = time_global() if not (self.st.wait_delay) then self.st.wait_delay = tg + self.st.desired_target.delay end if (tg > self.st.wait_delay) then self:increment_waypoint_index() return end else local mgr = db.storage[self.object:id()].state_mgr if (mgr) then mgr.animation:set_state(nil) if (mgr.animation.states.anim_marker == nil) then self:increment_waypoint_index() return end end end local state = state_mgr.get_state(self.object) local new_state = self.st.desired_target.delay_animation or self.st.delay_animation --[[ if (self.st.use_camp) then self:fill_approved_actions(new_state) self.camp = sr_camp.get_current_camp(self.object:position()) if (self.camp and not self.in_camp) then self.camp:register_npc(self.object:id()) self.in_camp = true end if (self.in_camp) then local camp_action, is_director = self.camp and self.camp:get_camp_action(self.object:id()) if (camp_action) then local tbl = is_director and assoc_tbl[camp_action].director or assoc_tbl[camp_action].listener local descr = new_state == "sit_knee" and "sit_ass" or new_state local t = {} local found for i=1,#tbl do for n=#self.st.approved_actions, 1, -1 do if (self.st.approved_actions[n].name == descr..tbl[i]) then table.insert(t,self.st.approved_actions[n].name) found = true end end end local anm = found and t[math.random(#t)] if (anm and anm ~= "") then if (self.st.desired_target.animpoint) then printf("camp_action = %s",anm) state_mgr.set_state(self.object, anm, nil, nil, nil, {animation_position = self.st.desired_target.animpoint, animation_direction = self.st.desired_target.animpoint_dir}) else state_mgr.set_state(self.object, anm) end return end end end elseif (self.in_camp) then self.camp:unregister_npc(self.object:id()) self.in_camp = nil else local sound = self.st.desired_target.sound_idle or xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.sound_idle) if (sound and sound ~= "nil" and sound ~= "") then xr_sound.set_sound_play(self.object:id(), sound) end end if (new_state and new_state ~= state) then if (self.st.desired_target.animpoint) then state_mgr.set_state(self.object, new_state,nil, nil,{look_object = self.st.desired_target.look_object, look_position = self.st.desired_target.look_position }, {animation_position = self.st.desired_target.animpoint, animation_direction = self.st.desired_target.animpoint_dir}) if (new_state == "zat_b106_wounded_idle") then local mgr = db.storage[self.object:id()].state_mgr if (mgr) then mgr.animation:set_state(nil,true) mgr.animation:set_control() end end else state_mgr.set_state(self.object, new_state,nil, nil,{look_object = self.st.desired_target.look_object, look_position = self.st.desired_target.look_position }) end end --]] end ------------------------------- RELAX ----------------------------------------------------------------- function action_beh:vertex_create(pos, dir, radius) local vertex_id = 4294967295 local tmp_pos = VEC_ZERO while (vertex_id >= 4294967295) do tmp_pos.x = pos.x + dir.x * radius tmp_pos.z = pos.z + dir.z * radius tmp_pos.y = pos.y vertex_id = level.vertex_id(tmp_pos) if (vertex_id >= 4294967295 or used_vids[vertex_id]) then if (radius > 1) then radius = radius - 1 else break end end end return vertex_id end function action_beh:relax_animations() if (self.st.camp ~= nil) then local camp_action, is_director = self.st.camp:get_camp_action(self.object:id()) end if not (camp_action) then camp_action = "idle" end local role = is_director and "director" or "listener" local tbl = assoc_tbl[camp_action][role] self.current_action = tbl and tbl[math.random(#tbl)] or "sit_ass" return self.current_action end ------------------------------- RELAX ----------------------------------------------------------------- function action_beh:get_current_waypoint(use_var) local st = db.storage[self.object:id()] local str if (self.object:has_info("npcx_beh_patrol_mode")) then str = se_load_var(self.object:id(),self.object:name(),"pathpoint"..self.st.path_index) --load_var(self.object,"pathpoint"..self.st.path_index) else str = st.active_section and st.ini:r_string_ex(st.active_section,"pt"..tostring(self.st.path_index)) end if not (str) then return {} end if (self.st.desired_target and self.st.path_index == self.st.desired_target.index and st.active_section == self.st.desired_target.active_section) then return self.st.desired_target end local t = {} t.index = self.st.path_index t.active_section = st.active_section for s in string.gmatch(str,"look:(%A+)") do local p = str_explode(s,",") t.look_position = vector():set(tonumber(p[1]),tonumber(p[2]),tonumber(p[3])) end for s in string.gmatch(str,"look_f:(%S+)") do local p = str_explode(s,"@") -- printf('%s|%s (%s)', p[1], p[2], #p) if p and #p == 2 then local script = p[1] local func = p[2] t.look_position = _G[script][func](self.object) end end for s in string.gmatch(str,"animpoint:(%A+)") do local p = str_explode(s,",") t.animpoint = vector():set(tonumber(p[1]),tonumber(p[2]),tonumber(p[3])) p[4] = p[4] and tonumber(p[4]) or 0 p[5] = p[5] and tonumber(p[5]) or 0 p[6] = p[6] and tonumber(p[6]) or 0 t.animpoint_dir = vector():set(p[4],p[5],p[6]) end for s in string.gmatch(str,"pos:(%A+)") do local p = str_explode(s,",") t.position = vector():set(tonumber(p[1]),tonumber(p[2]),tonumber(p[3])) end for s in string.gmatch(str,"sig:(%a+)") do t.sig = s end for s in string.gmatch(str,"sound_idle:(%a+)") do t.sound_idle = s end for s in string.gmatch(str,"look_object:(%a+)") do if (s ~= "enemy") then t.look_object = get_story_object(s) else local st = db.storage[self.object:id()] local be = self.object:best_enemy() or st and st.enemy_id and level.object_by_id(st.enemy_id) t.look_object = be and be:alive() and self.object:see(be) and be end end for s in string.gmatch(str,"look_object_f:(%S+)") do local p = str_explode(s,"@") if p and #p == 2 then local script = p[1] local func = p[2] t.look_object = _G[script][func](self.object) end end for s in string.gmatch(str,"look_job:(%a+)") do s = str_explode(s,",") local board = SIMBOARD local smart = s[1] and board and board.smarts_by_names[s[1]] local obj = smart and smart.npc_by_job_section["logic@"..s[2]] obj = obj and level.object_by_id(obj) if (obj) then t.look_object = obj --t.look_position = obj:bone_position("bip01_neck") end end local pt = str_explode(str,"|") pt = pt[1] and str_explode(pt[1],",") if not (pt) then return end if not (t.position) then t.level_vertex_id = tonumber(pt[1]) t.position = level.vertex_position(t.level_vertex_id) t.delay = pt[2] and tonumber(pt[2]) or 0 t.delay_animation = trim(pt[3]) else t.level_vertex_id = level.vertex_id(t.position) t.delay = pt[1] and tonumber(pt[1]) or 0 t.delay_animation = trim(pt[2]) end t.direction = self.object:position():sub(t.position) return t end function action_beh:next_waypoint_index() local st = db.storage[self.object:id()] local inc = self.st.path_reverse and -1 or 1 if (self.object:has_info("npcx_beh_patrol_mode")) then return se_load_var(self.object:id(),self.object:name(),"pathpoint"..(self.st.path_index+inc))--load_var(self.object,"pathpoint"..tostring(self.st.path_index+inc)) end return st.active_section and st.ini:r_string_ex(st.active_section,"pt"..tostring(self.st.path_index+inc)) end function action_beh:increment_waypoint_index() local st = db.storage[self.object:id()] local inc = self.st.path_reverse and -1 or 1 local next_index if (self.object:has_info("npcx_beh_patrol_mode")) then next_index = se_load_var(self.object:id(),self.object:name(),"pathpoint"..(self.st.path_index+inc))--load_var(self.object,"pathpoint"..self.st.path_index+inc) else next_index = st.active_section and st.ini:r_string_ex(st.active_section,"pt"..self.st.path_index+inc) end if (next_index) then self.st.am_i_reached = nil self.st.path_index = self.st.path_index + inc if (self.st.path_reverse and self.st.path_index == 1) then self.st.path_reverse = nil end else local new_section = self.st.path_end and xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.path_end) if (new_section and new_section ~= "nil" and new_section ~= "") then if (new_section == "reverse") then self.st.path_reverse = true self.st.path_index = self.st.path_index - 1 elseif (new_section == "loop") then self.st.path_index = 1 else xr_logic.switch_to_section(self.object, st.ini, new_section) self.st.am_i_reached = nil self.st.path_index = 1 end end end if (self.st.desired_target.sig) then self.st.signals[self.st.desired_target.sig] = true end self.st.wait_delay = nil save_var(self.object,"path_index",self.st.path_index) end function action_beh:fill_approved_actions(state) local app_act = self.st.approved_actions iempty_table(app_act) local size_t = 0 if (state == "sit_knee") then state = "sit_ass" end self.st.description = state local t = { "eat_bread", "drink_energy", "drink_vodka", "eat_kolbasa", "guitar", "harmonica", "weapon" } for i=1, 6 do if (axr_beh["can_"..t[i]] and axr_beh["can_"..t[i]](self.object)) then size_t = size_t + 1 app_act[size_t] = { name = state.."_"..t[i] } end end end function action_beh:finalize() action_base.finalize(self) if (self.in_camp) then if self.camp == nil then return end self.camp:unregister_npc(self.object:id()) self.in_camp = nil end if (self.st.desired_target and self.st.desired_target.level_vertex_id) then db.used_level_vertex_ids[self.st.desired_target.level_vertex_id] = nil end save_var(self.object,"path_index",1) end -- -------------------------- Scheme Handlers ------------------------------------------------------------ function set_scheme(npc, ini, scheme, section, gulag_name) local st = xr_logic.assign_storage_and_bind(npc,ini,"beh",section) st.logic = xr_logic.cfg_get_switch_conditions(ini, section, npc) init_custom_data(npc, ini, section, st, "beh") st.sound_idle = ini:r_string_ex(section,"sound_idle") st.reach_movement = xr_logic.parse_condlist(npc, "reach_movement", "reach_movement", ini:r_string_ex(section,"reach_movement") or "patrol") end function add_to_binder(npc, ini, scheme, section, storage) if not npc then return end local manager = npc:motivation_action_manager() if not manager then return end local wp = world_property local evaluator = evaluator_beh(storage, "evaluator_beh") local action = action_beh(npc, "action_beh", storage) if not evaluator or not action then return end manager:add_evaluator(beh_evid,evaluator) action:add_precondition( wp(beh_evid, true) ) action:add_effect( wp(beh_evid, false) ) action:add_effect( wp(xr_evaluators_id.state_mgr + 4, false) ) manager:add_action(beh_actid,action) xr_logic.subscribe_action_for_events(npc, storage, action) action:add_precondition( wp(stalker_ids.property_alive,true) ) action:add_precondition( wp(stalker_ids.property_enemy,false) ) action:add_precondition( wp(stalker_ids.property_danger,false) ) --if (_G.schemes["corpse_detection"]) then --action:add_precondition( wp(xr_evaluators_id.corpse_exist, false) ) --end --if (_G.schemes["gather_items"]) then --action:add_precondition( wp(xr_gather_items.evaid, false) ) --end --action:add_precondition( wp(stalker_ids.property_items, false) ) --action:add_precondition( wp(xr_evaluators_id.wounded_exist,false) ) action:add_precondition( wp(xr_evaluators_id.stohe_meet_base + 1, false) ) -- action:add_precondition( wp(xr_evaluators_id.sidor_wounded_base + 0, false) ) action:add_precondition( wp(xr_evaluators_id.abuse_base, false) ) --printf("ACTION_ALIFE_ID(axr_beh.add_to_binder): " .. tostring(xr_actions_id.alife)) action = manager:action(xr_actions_id.alife) if action then action:add_precondition( wp(beh_evid, false) ) end action = manager:action(xr_actions_id.state_mgr + 2) if action then action:add_precondition( wp(beh_evid, false) ) end end --[[ function reset_scheme(loading, npc) local st = db.storage[npc:id()] if (st.beh) then init_custom_data(npc, st.ini, section, st.beh, scheme) end end --]]