---------------------------------------------------------------------------------------------------------------------- -- Схема патруль. Мужики в патруле ---------------------------------------------------------------------------------------------------------------------- function printf() end local key = nil local data = nil --'patrol states local patrol_move = 0 -- просто движение local patrol_hide = 1 -- ракоход local patrol_sprint = 2 -- очень быстро бежим local patrol_run = 3 -- просто несемся, аки лоси в кукурузу local patrol_stop = 4 -- стоим local patrols = {} local formations = {} formations["line"] = { { dir = vector ():set (-1, 0, 0), dist = 2 }, { dir = vector ():set (-1, 0, 0), dist = 4 }, { dir = vector ():set (-1, 0, 0), dist = 6 }, { dir = vector ():set (1, 0, 0), dist = 2 }, { dir = vector ():set (1, 0, 0), dist = 4 }, { dir = vector ():set (1, 0, 0), dist = 6 } } formations["back"] = { { dir = vector ():set (0.3, 0, -1), dist = 1.2 }, { dir = vector ():set (-0.3, 0, -1), dist = 2.4 }, { dir = vector ():set (0.3, 0, -1), dist = 3.6 }, { dir = vector ():set (-0.3, 0, -1), dist = 4.8 }, { dir = vector ():set (0.3, 0, -1), dist = 6 }, { dir = vector ():set (-0.3, 0, -1), dist = 7.2 } --[[ { dir = vector ():set (-0.89442718, 0, -0.44721359), dist = 2.2360680 }, { dir = vector ():set (0.89442718, 0, -0.44721359), dist = 2.2360680 }, { dir = vector ():set (-0.37139067, 0, -0.92847669), dist = 5.3851647 }, { dir = vector ():set (0.37139067, 0, -0.92847669), dist = 5.3851647 }, { dir = vector ():set (-0.55470020, 0, -0.83205032), dist = 3.6055512 }, { dir = vector ():set (0.55470020, 0, -0.83205032), dist = 3.6055512 } ]] } formations["around"] = { { dir = vector ():set (0.44721359, 0, -0.89442718), dist = 2.2360680 }, { dir = vector ():set (-0.44721359, 0, -0.89442718), dist = 2.2360680 }, { dir = vector ():set (1.0000000, 0, 0), dist = 2 }, { dir = vector ():set (-1, 0, 0), dist = 2 }, { dir = vector ():set (0.44721359, 0, 0.89442718), dist = 2.2360680 }, { dir = vector ():set (-0.44721359, 0, 0.89442718), dist = 2.2360680 } } local accel_by_curtype = { walk = "run", patrol = "rush", raid = "assault", sneak = "sneak_run", sneak_run = "assault" } class "PatrolManager" function PatrolManager:__init(path_name) self.path_name = path_name self.npc_list = {} self.current_state = "patrol" self.commander_id = -1 self.formation = "back" self.commander_lid = -1 self.commander_dir = vector ():set (0, 0, 1) self.npc_count = 0 end --' Добавление нового персонажа function PatrolManager:add_npc(npc, leader) --'validate npc if npc == nil or npc:alive() == false or self.npc_list[npc:id()] ~= nil then return end --' Проверка на то что в патруле не больше 7 сталкеров (иначе будет вылетать дальше) if self.npc_count == 7 then abort("[XR_PATROL] attempt to add more then 7 npc. [%s]", npc:name()) return end --' if this npc first in command then set him as commander self.npc_list[npc:id ()] = {soldier = npc, dir = vector ():set (1, 0, 0), dist = 0} self.npc_count = self.npc_count + 1 --' если мужик первый или считается лидером, то установим, как лидера if self.npc_count == 1 or leader == true then self.commander_id = npc:id () printf ("[XR_PATROL] ASSIGNED NPC %s AS PATROL COMMANDER", npc:name ()) end printf ("[XR_PATROL] NPC %s added to patrol manager %s", npc:name (), self.path_name) self:reset_positions () end --' Удаление персонажа function PatrolManager:remove_npc(npc) if npc == nil then return end if self.npc_list[npc:id()] == nil then return end printf ("[XR_PATROL] NPC %s removed from patrol manager %s", npc:name(), self.path_name) self.npc_list[npc:id ()] = nil self.npc_count = self.npc_count - 1 if npc:id () == self.commander_id then self.commander_id = -1 self:reset_positions () end end --' Пересчет занимаемых позиций function PatrolManager:reset_positions() local form_ = formations[self.formation] local index = 1 for key, data in pairs(self.npc_list) do --установим командора, если это еще не сделано if self.commander_id == -1 and index == 1 then self.commander_id = data.soldier:id () end -- пересчитаем позиции if self.commander_id ~= self.npc_list[key].soldier:id () then --printf("key[%s] index[%s]", tostring(key), tostring(index)) self.npc_list[key].dir = form_[index].dir self.npc_list[key].dist = form_[index].dist self.npc_list[key].vertex_id = -1 self.npc_list[key].accepted = true index = index + 1 end end end --' Установление формации function PatrolManager:set_formation(formation) if formation == nil then abort ("Invalid formation (nil) for PatrolManager[%s]", self.path_name) end if formation ~= "around" and formation ~= "back" and formation ~= "line" then abort ("Invalid formation (%s) for PatrolManager[%s]", formation, self.path_name) end self.formation = formation self:reset_positions () end --' Получить командира патруля function PatrolManager:get_commander(npc) if npc == nil then abort ("Invalid NPC on call PatrolManager:get_npc_command in PatrolManager[%s]", self.path_name) end --закешируем айдишник непися local npc_id = npc:id () --проверка непися на присутсвие в списке if self.npc_list[npc:id ()] == nil then abort ("NPC with name %s can't present in PatrolManager[%s]", npc:name (), self.path_name) end --проверка, чтобы командир не вздумал задавать глупых вопросов if npc:id () == self.commander_id then abort ("Patrol commander called function PatrolManager:get_npc_command in PatrolManager[%s]", self.path_name) end --получим данные командира local commander = self.npc_list[self.commander_id].soldier if commander == nil then abort ("Patrol commander not present in PatrolManager[%s]", self.path_name) end return commander end --' Получить параметры действия NPC function PatrolManager:get_npc_command(npc) if npc == nil then abort("Invalid NPC on call PatrolManager:get_npc_command in PatrolManager[%s]", self.path_name) end --'закешируем айдишник непися local npc_id = npc:id () --'проверка непися на присутсвие в списке if self.npc_list[npc:id ()] == nil then abort("NPC with name %s can't present in PatrolManager[%s]", npc:name (), self.path_name) end --'проверка, чтобы командир не вздумал задавать глупых вопросов if npc:id() == self.commander_id then abort("Patrol commander called function PatrolManager:get_npc_command in PatrolManager[%s]", self.path_name) end --'получим данные командира local commander = self.npc_list[self.commander_id].soldier local dir = commander:direction() local pos = vector():set(0, 0, 0) local vertex_id = commander:location_on_path(5, pos) --' Если командир сам еще не дошел до пути (расстояние до вертекса больше чем 5 метров) --' то за основную точку берем вертекс командира if level.vertex_position(vertex_id):distance_to(self.npc_list[npc_id].soldier:position()) > 5 then vertex_id = commander:level_vertex_id() end --'уберем влияние вертикальной составляющей dir.y = 0 dir:normalize() --'получим данные салаги local dir_s = self.npc_list[npc_id].dir local dist_s = self.npc_list[npc_id].dist --'расчет позиции для салаги --'сначала получим направление local angle = yaw_degree(dir_s, vector():set(0, 0, 1)) local vvv = vector_cross(dir_s, vector():set(0, 0, 1)) if vvv.y < 0 then angle = -angle end dir_s = vector_rotate_y(dir, angle) local d = 2 --'теперь аи ноду относительно командора ордена рыцарей храма local vertex = level.vertex_in_direction(level.vertex_in_direction(vertex_id, dir_s, dist_s), dir, d) self.npc_list[npc_id].vertex_id = vertex --' проверим, а не имеет ли смысла неписю скорректировать свою позицию --local distance = level.vertex_position(vertex):distance_to(self.npc_list[npc_id].soldier:position()) -- нужно считать отставание от командира, а не от расчетной точки. local distance = commander:position():distance_to(self.npc_list[npc_id].soldier:position()) if distance > dist_s + 2 then --' Игрок сильно отстал. Необходимо изменить тип перемещения. --' Тип перемещения задаем в зависимости от базового типа. local new_state = accel_by_curtype[self.current_state] if new_state ~= nil then return vertex, dir, new_state end end return vertex, dir, self.current_state end --' Установить действие НПС function PatrolManager:set_command(npc, command, formation) if npc == nil or npc:alive () == false then self:remove_npc(npc) return end if npc:id () ~= self.commander_id then return --abort ("NPC %s is not commander in PatrolManager[%s]", npc:name (), self.path_name) end self.current_state = command if self.formation ~= formation then self.formation = formation self:set_formation (formation) end self.commander_lid = npc:level_vertex_id () self.commander_dir = npc:direction () self:update () end --' Является ли указанный НПС командиром. function PatrolManager:is_commander(npc_id) return npc_id == self.commander_id end --' Находится ли командир патруля в схеме XR_MEET function PatrolManager:is_commander_in_meet() if self.commander_id == -1 then return false end local npc = self.npc_list[self.commander_id].soldier if npc ~= nil and npc:alive () == true then return xr_meet.is_meet (npc) end return false end --' Апдейт function PatrolManager:update () if tm_enabled == true then self.tm:update () end end --'------------------------------------------------------------------------------------------------------------------- --'Evaluators --'-------------------------------------------------------------------------------------------------------------------- class "evaluator_patrol_end" (property_evaluator) function evaluator_patrol_end:__init(name, storage) super (nil, name) self.st = storage end function evaluator_patrol_end:evaluate() return not xr_logic.is_active(self.object, self.st) end class "evaluator_patrol_comm" (property_evaluator) function evaluator_patrol_comm:__init(name, storage) super (nil, name) self.st = storage end function evaluator_patrol_comm:evaluate() return patrols[self.st.patrol_key]:is_commander(self.object:id()) end --'------------------------------------------------------------------------------------------------------------------- --' Actions --'-------------------------------------------------------------------------------------------------------------------- class "action_commander" (action_base) function action_commander:__init(npc, action_name, storage) super(nil, action_name) self.st = storage self.move_mgr = db.storage[npc:id()].move_mgr self.was_reset = false self.cur_state = "patrol" end function action_commander:initialize() action_base.initialize (self) self.object:set_desired_position() self.object:set_desired_direction() self:activate_scheme () end function action_commander:activate_scheme() self.st.signals = {} printf ("[XR_PATROL] SET PATROL PATH : %s", self.st.path_walk) if self.st.path_walk_info == nil then self.st.path_walk_info = utils.path_parse_waypoints(self.st.path_walk) end if self.st.path_look_info == nil then self.st.path_look_info = utils.path_parse_waypoints(self.st.path_look) end self.move_mgr:reset(self.st.path_walk, self.st.path_walk_info, self.st.path_look, self.st.path_look_info, self.st.team, self.st.suggested_state, {obj = self, func = self.formation_callback}) patrols[self.st.patrol_key]:set_command(self.object, self.cur_state, self.st.formation) end function action_commander:execute() action_base.execute(self) self.move_mgr:update() local new_state = state_mgr.get_state(self.object) local old_state = self.old_state if old_state ~= new_state then if self.st.silent ~= true then if new_state == "sneak" then xr_sound.set_sound_play(self.object:id(), "patrol_sneak") elseif new_state == "sneak_run" then xr_sound.set_sound_play(self.object:id(), "patrol_run") elseif new_state == "run" then xr_sound.set_sound_play(self.object:id(), "patrol_run") elseif new_state == "assault" then xr_sound.set_sound_play(self.object:id(), "patrol_run") elseif new_state == "rush" then xr_sound.set_sound_play(self.object:id(), "patrol_run") else if old_state == "sneak" or old_state == "sneak_run" or old_state == "run" or old_state == "assault" or old_state == "rush" then xr_sound.set_sound_play(self.object:id(), "patrol_walk") end end end self.old_state = new_state end --printf("FORMATION %s", self.st.formation) patrols[self.st.patrol_key]:set_command(self.object, new_state, self.st.formation) end ---------------------- function action_commander:finalize() if self.object:alive () == true then --printf ("ACTION_COMMANDER:FINALIZE CALLED") patrols[self.st.patrol_key]:set_command(self.object, "guard", self.st.formation) self.move_mgr:finalize() end action_base.finalize(self) end ---------------------- function action_commander:deactivate(npc) patrols[self.st.patrol_key]:remove_npc(npc) end ---------------------- function action_commander:death_callback(npc) patrols[self.st.patrol_key]:remove_npc(npc) end ---------------------- function action_commander:net_destroy(npc) self:deactivate(npc) end ---------------------- function action_commander:formation_callback(mode, number, index) if number == 0 then self.st.formation = "line" elseif number == 1 then self.st.formation = "around" elseif number == 2 then self.st.formation = "back" end printf("FORMATION CALLBACK. %s", self.st.formation) end class "action_patrol" (action_base) function action_patrol:__init (npc, action_name, storage) super (nil, action_name) self.st = storage self.move_mgr = db.storage[npc:id()].move_mgr self.l_vid = -1 self.dist = 0 self.dir = vector():set(0, 0, 1) self.cur_state = "patrol" self.on_point = false self.was_reset = false self.time_to_update = time_global () + 1000 end function action_patrol:initialize() action_base.initialize (self) self.object:set_desired_position () self.object:set_desired_direction () self.on_point = false end function action_patrol:activate_scheme() self.st.signals = {} printf ("[XR_PATROL] SET SOLDIER PATROL PATH : %s", self.st.path_walk) if self.st.path_walk_info == nil then self.st.path_walk_info = utils.path_parse_waypoints(self.st.path_walk) end if self.st.path_look_info == nil then self.st.path_look_info = utils.path_parse_waypoints(self.st.path_look) end self.move_mgr:reset(self.st.path_walk, self.st.path_walk_info, self.st.path_look, self.st.path_look_info, self.st.team, self.st.suggested_state, {obj = self, func = self.formation_callback}) end function action_patrol:execute() action_base.execute (self) --' if db.actor and xr_logic.try_switch_to_another_section (self.object, self.st, db.actor) then --' return --' end if self.time_to_update - time_global() > 0 then return end self.time_to_update = time_global() + 1000 self.l_vid, self.dir, self.cur_state = patrols[self.st.patrol_key]:get_npc_command(self.object) self.l_vid = utils.send_to_nearest_accessible_vertex(self.object, self.l_vid) local desired_direction = self.dir printf("desired_direction = %s", vec_to_str(desired_direction)) if desired_direction ~= nil and not utils.vector_cmp(desired_direction, vector():set(0,0,0)) then desired_direction:normalize() self.object:set_desired_direction(desired_direction) end self.object:set_path_type(game_object.level_path) --'перемещаемся по пути state_mgr.set_state(self.object, self.cur_state) end function action_patrol:finalize() if self.object:alive () == true then self.move_mgr:finalize() end action_base.finalize (self) end function action_patrol:formation_callback(mode, number, index) end function action_patrol:death_callback(npc) patrols[self.st.patrol_key]:remove_npc(npc) end function action_patrol:deactivate(npc) patrols[self.st.patrol_key]:remove_npc(npc) end function action_patrol:net_destroy(npc) self:deactivate (npc) end --'-------------------------------------------------------------------------------------------------------------------- --' Patrol binder --'-------------------------------------------------------------------------------------------------------------------- function add_to_binder (object, ini, scheme, section, storage) local operators = {} local properties = {} local manager = object:motivation_action_manager () properties["event"] = xr_evaluators_id.reaction properties["patrol_end"] = xr_evaluators_id.sidor_patrol_base + 0 properties["patrol_comm"] = xr_evaluators_id.sidor_patrol_base + 1 properties["state_mgr_logic_active"] = xr_evaluators_id.state_mgr + 4 operators["action_patrol"] = xr_actions_id.sidor_act_patrol operators["action_commander"] = xr_actions_id.sidor_act_patrol + 1 -- Evaluators manager:add_evaluator (properties["patrol_end"], this.evaluator_patrol_end ("patrol_end", storage, "patrol_end")) manager:add_evaluator (properties["patrol_comm"], this.evaluator_patrol_comm ("patrol_comm", storage, "patrol_comm")) -- Actions -- create action for patrol commander local action = this.action_commander (object, "action_commander", storage) action:add_precondition (world_property(stalker_ids.property_alive, true)) action:add_precondition (world_property(stalker_ids.property_danger,false)) action:add_precondition (world_property(stalker_ids.property_enemy, false)) action:add_precondition (world_property(stalker_ids.property_anomaly,false)) xr_motivator.addCommonPrecondition (action) action:add_precondition (world_property(properties["patrol_end"], false)) action:add_precondition (world_property(properties["patrol_comm"], true)) action:add_effect (world_property(properties["patrol_end"], true)) action:add_effect (world_property(properties["state_mgr_logic_active"], false)) manager:add_action (operators["action_commander"], action) xr_logic.subscribe_action_for_events (object, storage, action) action = this.action_patrol (object,"action_patrol", storage) action:add_precondition (world_property(stalker_ids.property_alive, true)) action:add_precondition (world_property(stalker_ids.property_danger, false)) action:add_precondition (world_property(stalker_ids.property_enemy, false)) action:add_precondition (world_property(stalker_ids.property_anomaly,false)) xr_motivator.addCommonPrecondition(action) action:add_precondition (world_property(properties["patrol_end"], false)) action:add_precondition (world_property(properties["patrol_comm"], false)) action:add_effect (world_property(properties["patrol_end"], true)) action:add_effect (world_property(properties["state_mgr_logic_active"], false)) manager:add_action (operators["action_patrol"], action) xr_logic.subscribe_action_for_events (object, storage, action) action = manager:action (xr_actions_id.alife) action:add_precondition (world_property(properties["patrol_end"], true)) end -- включение патруля function set_scheme(npc, ini, scheme, section, gulag_name) printf ("XR_PATROL CALLED 'SET_SCHEME' FOR NPC %s", npc:name ()) local st = xr_logic.assign_storage_and_bind (npc, ini, scheme, section) st.logic = xr_logic.cfg_get_switch_conditions (ini, section, npc) st.path_name = utils.cfg_get_string(ini, section, "path_walk", npc, true, gulag_name) st.path_walk = st.path_name st.path_look = utils.cfg_get_string(ini, section, "path_look", npc, false, gulag_name) if st.path_walk == st.path_look then abort("You are trying to set 'path_look' equal to 'path_walk' in section [%s] for npc [%s]", section, npc:name()) end st.formation = utils.cfg_get_string(ini, section, "formation", npc, false, "") st.silent = utils.cfg_get_bool(ini, section, "silent", npc, false, false) if st.formation == nil then st.formation = "back" end st.move_type = utils.cfg_get_string(ini, section, "move_type", npc, false, "") if st.move_type == nil then st.move_type = "patrol" end st.suggested_state = {} st.suggested_state.standing = utils.cfg_get_string(ini, section, "def_state_standing", npc, false, "") st.suggested_state.moving = utils.cfg_get_string(ini, section, "def_state_moving1", npc, false, "") st.suggested_state.moving = utils.cfg_get_string(ini, section, "def_state_moving", npc, false, "", st.suggested_state.moving) --' st.animation = utils.cfg_get_string(ini, section, "animation", npc, false, "") st.path_walk_info = nil --'Будут инициализированы в reset(), сейчас пути могут быть еще st.path_look_info = nil --'не загружены. st.commander = utils.cfg_get_bool(ini, section, "commander", npc, false, false) st.patrol_key = st.path_name local squad = get_object_squad(npc) if squad ~= nil then st.patrol_key = st.patrol_key .. tostring(squad.id) end if patrols[st.patrol_key] == nil then patrols[st.patrol_key] = PatrolManager(st.path_name) end patrols[st.patrol_key]:add_npc(npc, st.commander) end