---------------------------------------------------------------------------------------------------------------------- -- Схема лагерь. Чудак(и) у костра. -- автор: Диденко Руслан (Stohe) -- TODO: ---------------------------------------------------------------------------------------------------------------------- --function printf() --end local ActionToStateTable = { idle = {director = { "sit", "sit_ass", "sit_knee", "eat_bread", "eat_kolbasa", "eat_vodka", "eat_energy"}, listener = {"sit", "sit_ass", "sit_knee", "eat_bread", "eat_kolbasa", "eat_vodka", "eat_energy"}}, harmonica = {director = {"harmonica"}, listener = {"sit", "sit_ass", "sit_knee", "eat_bread", "eat_kolbasa", "eat_vodka", "eat_energy"}}, guitar = {director = {"guitar"}, listener = {"sit", "sit_ass", "sit_knee", "eat_bread", "eat_kolbasa", "eat_vodka", "eat_energy"}}, story = {director = {"sit", "sit_ass", "sit_knee"}, listener = {"sit", "sit_ass", "sit_knee", "eat_bread", "eat_kolbasa", "eat_vodka", "eat_energy"}}, } local ActionAvailabilityTable = { eat_bread = "kamp_eat_bread", eat_kolbasa = "kamp_eat_kolbasa", eat_vodka = "kamp_drink_vodka", eat_energy = "kamp_drink_energy", } local ActionTimingTable = { sit = {min = 60*2*1000,max = 60*10*1000}, sit_ass = {min =60*2*1000,max = 60*10*1000}, sit_knee = {min = 60*2*1000,max = 60*10*1000} } kamps = {} --------------------------------------------------------------------------------------------------------------------- --Evaluators ---------------------------------------------------------------------------------------------------------------------- --' Условие завершения скрипта class "evaluator_kamp_end" (property_evaluator) function evaluator_kamp_end:__init(name, storage) super (nil, name) self.a = storage end function evaluator_kamp_end:evaluate() return not xr_logic.is_active(self.object, self.a) end --' Находимся ли мы на заданной позиции class "evaluator_on_position" (property_evaluator) function evaluator_on_position:__init(name, storage) super (nil, name) self.a = storage end function evaluator_on_position:evaluate() if self.object:level_vertex_id() == self.a.pos_vertex then return true end return false end ---------------------------------------------------------------------------------------------------------------------- --Actions ---------------------------------------------------------------------------------------------------------------------- --' Идет в заданную область class "action_go_position" (action_base) function action_go_position:__init (npc_name,action_name,storage) super (nil,action_name) self.a = storage end function action_go_position:initialize() action_base.initialize(self) -- self.object:set_node_evaluator() -- self.object:set_path_evaluator() self.object:set_desired_position() self.object:set_desired_direction() self.a.pos_vertex = nil self.a.npc_position_num = nil self.a.signals = {} end function action_go_position:execute () action_base.execute (self) -- Спрашиваем где сидеть local tmp_pos_vertex, npc_position_num = kamps[self.a.center_point]:getDestVertex(self.object, self.a.radius) if tmp_pos_vertex == nil then return end if self.a.npc_position_num ~= npc_position_num then self.a.npc_position_num = npc_position_num self.a.pos_vertex = tmp_pos_vertex --' Определяем куда смотреть. self.a.pp = patrol(self.a.center_point):point(0) local dir = vector():set(math.random(-1,1), 0, math.random(-1,1)) dir:normalize() local delta_dist = math.random(0,0.5) self.a.pp.x = self.a.pp.x + dir.x * delta_dist self.a.pp.z = self.a.pp.z + dir.z * delta_dist self.object:set_dest_level_vertex_id(self.a.pos_vertex) --printf("vertex_position") local desired_direction = vector():sub(self.a.pp,level.vertex_position(self.a.pos_vertex)) --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.a.def_state_moving) end end function action_go_position:finalize () action_base.finalize (self) end --' Просто сидит и втыкает class "action_wait" (action_base) function action_wait:__init (npc_name,action_name,storage) super (nil,action_name) self.a = storage end function action_wait:initialize() action_base.initialize(self) -- self.object:set_node_evaluator() -- self.object:set_path_evaluator() self.object:set_desired_position() self.object:set_desired_direction() local avail_actions = xr_animpoint_predicates.associations[self.a.description] self.a.approved_actions = {} for k,v in pairs(avail_actions) do -- Убираем те действия, которые не подходят по прекондишну if v.predicate(self.object:id(),true)==true then table.insert(self.a.approved_actions, v) kamps[self.a.center_point]:AddAvailableAction(self.object,v.name) end end kamps[self.a.center_point]:increasePops(self.object) end function action_wait:activate_scheme() self.a.signals = {} end function action_wait:execute() -- action_base.execute (self) -- --' повернуть его лицом к центру -- state_mgr.set_state(self.object, "sit", nil, nil, {look_position = self.a.pp}) action_base.execute (self) local state = kamps[self.a.center_point]:updateNpc(self.object) --' повернуть его лицом к центру state_mgr.set_state(self.object, state, nil, nil, {look_position = self.a.pp}) end function action_wait:finalize() kamps[self.a.center_point]:decreasePops(self.object) action_base.finalize (self) end function action_wait:deactivate(npc) kamps[self.a.center_point]:removeNpc(npc) end function action_wait:death_callback(npc) kamps[self.a.center_point]:removeNpc(npc) end function action_wait:net_destroy(npc) kamps[self.a.center_point]:removeNpc(npc) end class "CKampManager" function CKampManager:__init(path) self.kamp_name = path self.patrol = patrol(path) --self.center = self.patrol:level_vertex_id(0) self.position = {{dir = vector():set(1, 0, 0), used = nil}, {dir = vector():set(1, 0, 1), used = nil}, {dir = vector():set(0, 0, 1), used = nil}, {dir = vector():set(-1, 0, 1), used = nil}, {dir = vector():set(-1, 0, 0), used = nil}, {dir = vector():set(-1, 0, -1),used = nil}, {dir = vector():set(0, 0, -1), used = nil}, {dir = vector():set(1, 0, -1), used = nil}} self.npc = {} self.population = 0 end function CKampManager:selectPosition(npc_id) -- создаем список доступных позиций --printf("KAMP. [%s] called select position", npc_id) local free = {} for k,v in pairs(self.position) do if v.used == nil then table.insert(free, k) end end --' затем из доступных позиций выбрать рандомно одну. if #free > 0 then --printf("KAMP [%s] free node > 0", npc_id) local rr = math.random(#free) self.position[free[rr]].used = npc_id self.npc[npc_id].position = free[rr] end --printf("KAMP [%s] npc table", npc_id) --print_table(self.npc) --printf("KAMP [%s] position table", npc_id) --print_table(self.position) end function CKampManager:getDestVertex(npc, radius) local npc_id = npc:id() --printf("get dest Vertex called [%s]", npc_id) if self.npc[npc_id].position == nil then --printf("-------debug_info------------- %s", self.kamp_name) --print_table(self.npc) --printf("-------debug_info------------- %s", self.kamp_name) --print_table(self.position) --printf("-------debug_info------------- %s", self.kamp_name) abort("get dest Vertex: nil [%s]", npc_id) return nil end -- высчитываем вертех по направлению -- Берем позицию в заданном направлении, затем берем ниарест точку от нее. local pp = self.patrol:point(0) local dir = self.position[self.npc[npc_id].position].dir -- Считаем рандомное отклонение направления. dir.x = dir.x + math.random(-1,1)/5 dir.z = dir.z + math.random(-1,1)/5 dir:normalize() radius = radius + math.random(-0.3,0.3) local dest_vertex = 4294967295 while dest_vertex == 4294967295 do local tmp_pos = vector():set(0,0,0) tmp_pos.x = pp.x + dir.x * radius tmp_pos.z = pp.z + dir.z * radius tmp_pos.y = pp.y dest_vertex = level.vertex_id(tmp_pos) if dest_vertex == 4294967295 then if radius < 1 then SemiLog("Invalid AI map at kamp point ["..self.kamp_name.."]") return nil else radius = radius - 0.5 end end end if not npc:accessible(dest_vertex) then --printf("vertex_position %s", tostring(dest_vertex)) local vp = level.vertex_position(dest_vertex) --printf("Nearest for npc[%s] kamp [%s] position [%s:%s:%s]", npc:name(), tostring(self.kamp_name),tostring(vp.x), tostring(vp.y), tostring(vp.z)) local nearest_vertex = npc:accessible_nearest(vp, vector():set(0,0,0)) --printf("Nearest for npc[%s] kamp [%s] position [%s:%s:%s]", npc:name(), tostring(self.kamp_name), vec_to_str(nearest_vertex)) return nearest_vertex, self.npc[npc_id].position end return dest_vertex, self.npc[npc_id].position end function CKampManager:updateNpc(npc) local tbl = {} local npc_id = npc:id() local camp_action, is_director if not self.camp then camp_action = "idle" is_director = false else camp_action, is_director = self.camp:get_camp_action(npc_id) end if(is_director) then tbl = ActionToStateTable[camp_action].director else tbl = ActionToStateTable[camp_action].listener end if self.npc[npc_id].begin == nil or time_global() - self.npc[npc_id].begin >= self.npc[npc_id].state_idle or self.npc[npc_id].is_director ~= is_director then self.npc[npc_id].begin = time_global() if (self.npc[npc_id].camp_action == "story" or self.npc[npc_id].camp_action == "idle") and (camp_action =="idle" or camp_action == "story") and self.npc[npc_id].is_director ~= is_director then self.npc[npc_id].is_director = is_director self.npc[npc_id].camp_action = camp_action return self.npc[npc_id].state_selected end local Action = "kamp" repeat local rnd = math.random(#tbl) self.npc[npc_id].state_selected = tbl[rnd] if ActionAvailabilityTable[self.npc[npc_id].state_selected] then Action = ActionAvailabilityTable[self.npc[npc_id].state_selected] else Action = "kamp" end until self.npc[npc_id].AvailableActions[Action] if ActionTimingTable[self.npc[npc_id].state_selected] ~= nil then self.npc[npc_id].state_idle = math.random(ActionTimingTable[self.npc[npc_id].state_selected].min,ActionTimingTable[self.npc[npc_id].state_selected].max) else self.npc[npc_id].state_idle = math.random(15*1000,60*1000) end self.npc[npc_id].is_director = is_director self.npc[npc_id].camp_action = camp_action end return self.npc[npc_id].state_selected end function CKampManager:addNpc(npc) if self.npc[npc:id()] ~= nil then return end self.npc[npc:id()] = {name = npc:name(), position = nil, AvailableActions = {}} self:selectPosition(npc:id()) end function CKampManager:removeNpc(npc) local npc_id = npc:id() if self.npc[npc_id] ~= nil then self.position[self.npc[npc_id].position].used = nil self.npc[npc_id] = nil end if self.camp ~= nil then self.camp:unregister_npc(npc:id()) end end function CKampManager:increasePops(npc) self.population = self.population + 1 if not self.camp then self.camp = sr_camp.get_current_camp(self.patrol:point(0)) end if self.camp ~= nil then self.camp:register_npc(npc:id()) end -- local campfire = bind_campfire.campfire_table[self.kamp_name.."_campfire"] -- if self.population > 0 and campfire ~= nil and not campfire:is_on() then -- campfire:turn_on() -- end end function CKampManager:decreasePops(npc) self.population = self.population - 1 -- local campfire = bind_campfire.campfire_table[self.kamp_name.."_campfire"] -- if self.population < 1 and campfire ~= nil and campfire:is_on() then -- campfire:turn_off() -- end end function CKampManager:AddAvailableAction(NPC,StateName) self.npc[NPC:id()].AvailableActions[StateName] = true end ---------------------------------------------------------------------------------------------------------------------- --Kamp binder ---------------------------------------------------------------------------------------------------------------------- function add_to_binder(object, ini, scheme, section, storage) local operators = {} local properties = {} local manager = object:motivation_action_manager() properties["kamp_end"] = xr_evaluators_id.stohe_kamp_base + 1 properties["on_position"] = xr_evaluators_id.stohe_kamp_base + 2 properties["contact"] = xr_evaluators_id.stohe_meet_base + 1 operators["go_position"] = xr_actions_id.stohe_kamp_base + 1 operators["wait"] = xr_actions_id.stohe_kamp_base + 3 properties["state_mgr_logic_active"] = xr_evaluators_id.state_mgr + 4 -- Evaluators manager:add_evaluator (properties["kamp_end"], this.evaluator_kamp_end ("kamp_end", storage, "kamp_end")) manager:add_evaluator (properties["on_position"], this.evaluator_on_position ("kamp_on_position", storage, "kamp_on_position")) -- Actions local action = this.action_wait (object:name(),"action_kamp_wait", 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["on_position"], true)) action:add_effect (world_property(properties["kamp_end"], true)) action:add_effect (world_property(properties["state_mgr_logic_active"], false)) manager:add_action (operators["wait"], action) xr_logic.subscribe_action_for_events(object, storage, action) action = this.action_go_position (object:name(),"action_go_kamp", 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["on_position"], false)) action:add_effect (world_property(properties["on_position"], true)) action:add_effect (world_property(properties["state_mgr_logic_active"], false)) manager:add_action (operators["go_position"], action) action = manager:action (xr_actions_id.alife) action:add_precondition (world_property(properties["kamp_end"], true)) end -- включение лагеря function set_scheme(npc, ini, scheme, section, gulag_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.center_point = utils.cfg_get_string(ini, section, "center_point", npc, true, gulag_name) st.radius = utils.cfg_get_number(ini, section, "radius", npc, false, 2) st.description = "kamp" st.base_action = "kamp" if kamps[st.center_point] == nil then kamps[st.center_point] = CKampManager(st.center_point) end kamps[st.center_point]:addNpc(npc) st.pos_vertex = nil st.def_state_moving = utils.cfg_get_string(ini, section, "def_state_moving", npc, false, "", "walk") end