432 lines
16 KiB
Text
432 lines
16 KiB
Text
----------------------------------------------------------------------------------------------------------------------
|
||
-- Схема лагерь. Чудак(и) у костра.
|
||
-- автор: Диденко Руслан (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
|