Advertisement
Guest User

Untitled

a guest
Jan 5th, 2017
225
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 37.23 KB | None | 0 0
  1. local SMART_SECTION_NAME = "smart_terrains"
  2. local JOB_POSITION_THRESHOLD = 200
  3. local DEATH_IDLE_TIME = 10*60 -- секунд
  4.  
  5. local smart_terrains_by_name = {}
  6.  
  7. local path_fields = { "path_walk", "path_main", "path_home", "center_point" }
  8.  
  9. local valid_territory = {
  10.     default = true,
  11.     base = true,
  12.     resource = true,
  13.     territory = true
  14. }
  15.  
  16. --' Проверка, что нпс подходит работе
  17. local function job_avail_to_npc(npc_info, job_info, smart)
  18.     --printf("job_avail_to_npc %s job %s smart %s", npc_info.se_obj:name(), tostring(job_info.job_id), smart:name())
  19.     local job = smart.job_data[job_info.job_id]
  20.     if job ~= nil then
  21.         job = job.section
  22.     end
  23.  
  24.     if smart.dead_time[job_info.job_id] ~= nil then
  25.         return false
  26.     end
  27.  
  28.     -- Проверка условия "монстровости"
  29.     if job_info._precondition_is_monster ~= nil and job_info._precondition_is_monster ~= npc_info.is_monster then
  30.         return false
  31.     end
  32.  
  33.     -- Проверка условия текущего сквад экшна
  34.     if job_info._current_squad_action ~= nil then
  35.         local board = smart.board
  36.         local squad = npc_info.se_obj.squad_id
  37.         if squad == nil then
  38.             return false
  39.         end
  40.         local squad_action = board.squads[squad] and board.squads[squad].current_action
  41.         if squad_action == nil then
  42.             return false
  43.         end
  44.         if job_info._current_squad_action ~= squad_action.name then
  45.             return false
  46.         end
  47.  
  48.         -- Проверка атакуемой цели
  49.         if job_info._squad_attack_point ~= nil then
  50.             local attack_point = smart.board.smarts[squad_action.dest_smrt_id].smrt:name()
  51.             if job_info._squad_attack_point ~= attack_point then
  52.                 return false
  53.             end
  54.         end
  55.     end
  56.  
  57.     --' Проверяем подходит ли нпс по предикату
  58.     if job_info._precondition_function ~= nil then
  59.         if not job_info._precondition_function(npc_info.se_obj, smart, job_info._precondition_params, npc_info) then
  60.             return false
  61.         end
  62.     end
  63.     return true
  64. end
  65.  
  66.  
  67. -- Итерируемся по НПС, начинаем со свободных нпс, потом НПС на низкоприоритетных работах, потом на высокоприоритетных.
  68. -- Для каждого конкретного НПС ищем работу.
  69. -- Отсеиваем в поиске работы, приоритет которых ниже, чем у текущей.
  70. local function job_iterator(jobs, npc_data, selected_job_prior, smart)
  71.     --printf("  iterate")
  72.     -- итерируемся по работам
  73.     local current_job_prior = selected_job_prior
  74.     local selected_job_id = nil
  75.     local selected_job_link = nil
  76.     for k,v in pairs(jobs) do
  77.         -- Если приоритет у проверяемой работы ниже, чем приоритет текущей выбранной работы НПС - завершаем выполнение
  78.         if current_job_prior > v._prior then
  79.             return selected_job_id, current_job_prior, selected_job_link
  80.         end
  81.         -- Проверяем, может ли НПС занять данную работу
  82.         if job_avail_to_npc(npc_data, v, smart) then
  83.             -- Это работа-кластер или работа-описание.
  84.             if v.job_id == nil then
  85.                 -- Вызываем рекурсивно себя для списка работ кластера
  86.                 selected_job_id, current_job_prior, selected_job_link = job_iterator(v.jobs, npc_data, selected_job_prior, smart)
  87.             else
  88.                 -- Если работа пустая или ее занимаем мы сами - выбираем ее.
  89.                 --printf("    job %s section %s npc_id %s", v.job_id, self.job_data[v.job_id].section, tostring(v.npc_id))
  90.                 if v.npc_id == nil then
  91.                     return v.job_id, v._prior, v
  92.                 elseif v.job_id == npc_data.job_id then
  93.                     return v.job_id, v._prior, v
  94.                 end
  95.             end
  96.         end
  97.     end
  98.     return selected_job_id, current_job_prior, selected_job_link
  99. end
  100.  
  101. ----------------------------------------------------------------------------------------------------------------------
  102. -- Класс "se_smart_terrain". Обеспечивает поддержку smart terrain в ОФЛАЙНЕ.
  103. ----------------------------------------------------------------------------------------------------------------------
  104. class "se_smart_terrain" (cse_alife_smart_zone)
  105. function se_smart_terrain:__init(section) super(section)
  106.     self.initialized = false
  107.     self.b_registred = false
  108.  
  109.     self.npc_to_register = {}
  110.     self.npc_by_job_section = {}
  111.     self.dead_time = {}
  112.  
  113.     -- Таблица для хранения зарегистренных НПС
  114.     self.npc_info  = {}
  115. end
  116. function se_smart_terrain:on_before_register()
  117.     cse_alife_smart_zone.on_before_register(self)
  118.     self.board = sim_board.get_sim_board()
  119.     self.board:register_smart(self)
  120.     self.smart_level    = alife():level_name(game_graph():vertex(self.m_game_vertex_id):level_id())
  121.     --printf("SMARTLEVEL %s level %s", self:name(), tostring(self.smart_level))
  122. end
  123. -- регистрация объекта в симуляторе.
  124. -- вызывается симулятором.
  125. function se_smart_terrain:on_register()
  126.     cse_alife_smart_zone.on_register(self)
  127.  
  128.     printf("register smart %s", self:name())
  129.  
  130.     self.board:build_cross_table(self)
  131.     self:show()
  132.  
  133.     if self.combat_manager == nil then
  134.         self.combat_manager = sim_combat.CCombat_manager(self, self.board)
  135.     end
  136.  
  137.     smart_terrains_by_name[self:name()] = self
  138.     self.b_registred = true
  139.  
  140.     self:load_jobs()
  141.  
  142.     self.board:init_smart(self)
  143.  
  144.     if self.need_init_npc == true then
  145.         self.need_init_npc = false
  146.         self:init_npc_after_load()
  147.     end
  148.  
  149.     -- Регистрим персонажей, которые добавили до регистрации смарта. (отложенный список)
  150.     self:register_delayed_npc()
  151.  
  152.     self.check_time = time_global()
  153. end
  154. -- анрегистрация объекта в симуляторе.
  155. -- вызывается симулятором.
  156. function se_smart_terrain:on_unregister()
  157.     cse_alife_smart_zone.on_unregister(self)
  158.     self.board:unregister_smart(self)
  159.     smart_terrains_by_name[self:name()] = nil
  160. end
  161. -- чтение custom data.
  162. function se_smart_terrain:read_params()
  163.     local ini  = self:spawn_ini()
  164.     local sect = "smart_terrain"
  165.  
  166.     if not ini:section_exist( sect ) then
  167.         printf( "[smart_terrain %s] no configuration!", self:name() )
  168.         self.disabled = true
  169.         return
  170.     end
  171.  
  172.     --' Вычитка симуляционных свойств
  173.     self.sim_type       = utils.cfg_get_string(ini, "smart_terrain", "sim_type", self, false, "", "default")
  174.  
  175.     if valid_territory[self.sim_type] == nil then
  176.         abort("Wrong sim_type value [%s] in smart [%s]", self.sim_type, self:name())
  177.     end
  178.  
  179.     self.squad_capacity = utils.cfg_get_number(ini, "smart_terrain", "squad_capacity", self, false, 1)
  180.     self.player_name    = "none"  --' Хранится имя игрока, владеющего данным смартом. Для оптимизации.
  181.     self.squad_id       = utils.cfg_get_number(ini, "smart_terrain", "squad_id", self, false, 0)
  182.     self.respawn_sector = utils.cfg_get_string(ini, "smart_terrain", "respawn_sector", self, false, "")
  183.     self.respawn_radius  = utils.cfg_get_number(ini, "smart_terrain", "respawn_radius", self, false, 150)
  184.     if self.respawn_sector ~= nil then
  185.         if self.respawn_sector == "default" then
  186.             self.respawn_sector = "all"
  187.         end
  188.         self.respawn_sector = xr_logic.parse_condlist(nil, sect, "respawn_sector", self.respawn_sector)
  189.     end
  190.  
  191.  
  192.     self.important_point = utils.cfg_get_bool(ini, "smart_terrain", "important_point", self, false)
  193.     self.mutant_lair        = utils.cfg_get_bool(ini, "smart_terrain", "mutant_lair", self, false)
  194.     self.no_mutant          = utils.cfg_get_bool(ini, "smart_terrain", "no_mutant", self, false)
  195.     if self.no_mutant == true then
  196.         printf("Found no mutant point %s", self:name())
  197.     end
  198.     self.forbidden_point    = utils.cfg_get_string(ini, "smart_terrain", "forbidden_point", self, false, "")
  199.  
  200.     --' Рестрикторы для симуляции
  201.     self.def_restr      = utils.cfg_get_string(ini, "smart_terrain", "def_restr", self, false, "", nil)
  202.     self.att_restr      = utils.cfg_get_string(ini, "smart_terrain", "att_restr", self, false, "", nil)
  203.  
  204.     self.spawn_point    = utils.cfg_get_string(ini, "smart_terrain", "spawn_point", self, false, "")
  205.  
  206.     self.sim_avail      = utils.cfg_get_string(ini, "smart_terrain", "sim_avail", self, false, "")
  207.     if self.sim_avail ~= nil then
  208.         self.sim_avail = xr_logic.parse_condlist(nil, sect, "sim_avail", self.sim_avail)
  209.     end
  210.  
  211.     self.respawn        = utils.cfg_get_string(ini, "smart_terrain", "respawn", self, false, "", nil)
  212.  
  213.     self.surge_hide_avaliable = utils.cfg_get_bool(ini, "smart_terrain", "surge_hide_avaliable", self, false, false)
  214.     local hides = utils.cfg_get_string(ini, "smart_terrain", "actor_hides", self, false, "")
  215.     if(hides~=nil) then
  216.         self.actor_hides = utils.parse_names(hides)
  217.     end
  218. end
  219.  
  220. --*******************************************************
  221. -- МЕТОДЫ ДЛЯ РАБОТЫ С НПС
  222. --*******************************************************
  223. -- заполнить информацию о персонаже
  224. -- у монстров нету метода profile_name()
  225. function se_smart_terrain:fill_npc_info(obj)
  226.     local npc_info = {}
  227.     printf("filling npc_info for obj [%s]", tostring(obj:name()))
  228.  
  229.     local is_stalker = IsStalker(obj)
  230.     npc_info.se_obj         = obj
  231.     npc_info.is_monster     = not is_stalker
  232.     npc_info.need_job       = "nil" -- Специально для смены гвардов. Указывает на какую работу хочет данный чувак.
  233.     npc_info.job_prior      = -1
  234.     npc_info.job_id         = -1
  235.     npc_info.begin_job      = false
  236.  
  237.     if is_stalker then
  238.         npc_info.stype = modules.stype_stalker
  239.     else
  240.         npc_info.stype = modules.stype_mobile
  241.     end
  242.  
  243.     return npc_info
  244. end
  245.  
  246. -- добавить npc в smart terrain.
  247. function se_smart_terrain:register_npc(obj)
  248.     printf("[smart_terrain %s] register called obj=%s", self:name(), obj:name())
  249.  
  250.     if self.b_registred == false then
  251.         table.insert(self.npc_to_register, obj)
  252.         return
  253.     end
  254.  
  255.     self.npc_info[obj.id] = self:fill_npc_info(obj)
  256.  
  257.     obj.m_smart_terrain_id = self.id
  258.  
  259.  
  260.     -- Затычка на случай если мы регистримся в смарт, из которого только что сами вынесли всех врагов.
  261.     self.dead_time = {}
  262.  
  263.     -- тут надо найти чуваку работу
  264.     self:update_jobs(obj)
  265. end
  266. -- Регистрация НПС в список отложенных. Осуществляется на загрузке или на регистрации НПС, пока не зарегистрен смарт
  267. function se_smart_terrain:register_delayed_npc()
  268.     for k,v in pairs(self.npc_to_register) do
  269.         self:register_npc(v)
  270.     end
  271.     self.npc_to_register = {}
  272. end
  273. -- отпустить npc
  274. function se_smart_terrain:unregister_npc(obj)
  275.     --callstack()
  276.     printf("smart [%s] unregister npc [%s]", self:name(), obj:name())
  277.  
  278.     local n = self.npc_info[obj.id]
  279.  
  280.     if n == nil then
  281.         abort("self.npc_info[obj.id] = nil !!! obj.id=%d", obj.id)
  282.     end
  283.  
  284.     -- TODO: Тут надо выгнать чувака с занимаемой им работы
  285.     self.npc_info[obj.id].job_link.npc_id = nil
  286.     self.npc_info[obj.id] = nil
  287.  
  288.     obj:clear_smart_terrain()
  289. end
  290. -- Убрать убитого
  291. function se_smart_terrain:clear_dead(obj)
  292.     local n = self.npc_info[obj.id]
  293.  
  294.     if n == nil then
  295.         abort("self.npc_info[obj.id] = nil !!! obj.id=%d", obj.id)
  296.     end
  297.  
  298.     -- Устанавливаем таймер смерти на работе
  299.     self.dead_time[self.npc_info[obj.id].job_id] = game.get_game_time()
  300.  
  301.     self.npc_info[obj.id].job_link.npc_id = nil
  302.     self.npc_info[obj.id] = nil
  303.  
  304.     obj:clear_smart_terrain()
  305. end
  306. -- выдать объекту задание.
  307. function se_smart_terrain:task(obj)
  308.     local job_data = self.job_data[self.npc_info[obj.id].job_id]
  309.  
  310.     if not job_data then
  311.         abort("[smart_terrain %s] task: obj=%s job_path=nil, CLEARING. job_id = %s", self:name(), obj:name(), tostring(self.npc_info[obj.id].job_id))
  312.         self:unregister_npc(obj) --'Затычка против вылета.
  313.         return CALifeSmartTerrainTask("simulation_default_path", 0)
  314.     end
  315.  
  316.     obj.attack_position = job_data.attack_position
  317.  
  318.     --printf("SEETING TASK PATH %s = %s(%s)", obj:name(), job_data.alife_path, job_data.alife_point)
  319.     return CALifeSmartTerrainTask(job_data.alife_path, job_data.alife_point)
  320. end
  321.  
  322.  
  323. --*******************************************************
  324. -- Функции для работы с работами
  325. --*******************************************************
  326. -- Загрузка работ (из gulag_general)
  327. function se_smart_terrain:load_jobs()
  328.     --printf("LOAD JOBS %s", self:name())
  329.     -- Загружаем иерархию работ
  330.     self.jobs = gulag_general.load_job(self)
  331.  
  332.     -- Загружаем ltx работ.
  333.     self.ltx, self.ltx_name = xr_gulag.loadLtx(self:name())
  334.     -- Сортируем всю иерархию по уменьшению приоритета
  335.     -- Рекурсивная функция сортировки
  336.     local function sort_jobs(jobs)
  337.         for k,v in pairs(jobs) do
  338.             if v.jobs ~= nil then
  339.                 sort_jobs(v.jobs)
  340.             end
  341.         end
  342.         table.sort(jobs, function(a,b) return a._prior > b._prior end )
  343.     end
  344.  
  345. --  printf("before sort")
  346. --  store_table(self.jobs)
  347.  
  348.     sort_jobs(self.jobs)
  349.  
  350. --  printf("after sort")
  351. --  store_table(self.jobs)
  352.  
  353.  
  354.     -- Надо сделать постобработку работ. Проинитить все неиниченные поля
  355.     -- Для более быстрого доступа нужно вычленить параметры работ в отдельную таблицу вида:
  356.     --self.job_data[job_id] = {}
  357.     local id = 0
  358.     self.job_data = {}
  359.     local function get_jobs_data(jobs)
  360.         for k,v in pairs(jobs) do
  361.             if v.jobs ~= nil then
  362.                 get_jobs_data(v.jobs)
  363.             else
  364.                 if v.job_id == nil then
  365.                     print_table(self.jobs)
  366.                     abort("Incorrect job table")
  367.                 end
  368.                 self.job_data[id] = v.job_id
  369.                 if v._attack_job == true then
  370.                     self.job_data[id].attack_position = true
  371.                 end
  372.                 self.job_data[id]._prior = v._prior -- Кешируем для проверки
  373.                 v.job_id = id
  374.                 id = id + 1
  375.             end
  376.         end
  377.     end
  378.  
  379.     get_jobs_data(self.jobs)
  380.     -- Пробегаемся по работам и высчитываем для каждой работы alife_path
  381.     for k,v in pairs(self.job_data) do
  382.         local section = v.section
  383.         local ltx = v.ini_file or self.ltx
  384.         if not ltx:line_exist(section, "active") then
  385.             abort("gulag: ltx=%s  no 'active' in section %s", self.ltx_name, section)
  386.         end
  387.         local active_section = ltx:r_string(section, "active")
  388.         local path_field
  389.         for i,vv in pairs(path_fields) do
  390.             if ltx:line_exist(active_section, vv) then
  391.                 path_field = vv
  392.                 break
  393.             end
  394.         end
  395.         if not path_field then
  396.             abort("gulag: ltx=%s, there is no path in section %s", self.ltx_name, active_section)
  397.         end
  398.  
  399.         if path_field == "center_point" then --' TODO убрать затык
  400.             local path_name = "_" .. ltx:r_string(active_section, path_field)
  401.  
  402.             if v.prefix_name ~= nil then
  403.                 path_name = v.prefix_name .. path_name
  404.             else
  405.                 path_name = self:name() .. path_name
  406.             end
  407.  
  408.             if level.patrol_path_exists(path_name .. "_task") then
  409.                 v.alife_path = path_name .. "_task"
  410.             else
  411.                 v.alife_path = path_name
  412.             end
  413.  
  414.         else
  415.             if v.prefix_name ~= nil then
  416.                 v.alife_path = v.prefix_name .. "_" .. ltx:r_string(active_section, path_field)
  417.             else
  418.                 v.alife_path = self:name() .. "_" .. ltx:r_string(active_section, path_field)
  419.             end
  420.         end
  421.  
  422.         local patrol = patrol(v.alife_path)
  423.         --printf("5a %s", tostring(v.alife_path))
  424.         v.game_vertex_id = patrol:game_vertex_id(0)
  425.         --printf("5b")
  426.         v.level_id       = game_graph():vertex(v.game_vertex_id):level_id()
  427.         --printf("5c")
  428.         v.position       = patrol:point(0)
  429.         --printf("5d")
  430.  
  431.         -- Высчитываем точку, на которую нужно отправлять в оффлайне
  432.         local parsed_path = utils.path_parse_waypoints(v.alife_path)
  433.         v.alife_point = 0
  434.         local need_check = false
  435.         for kk,vv in pairs(parsed_path) do
  436.             if vv.sig == "arrive_to_wait" then
  437.                 v.alife_point = kk
  438.                 need_check = true
  439.                 break
  440.             end
  441.         end
  442.  
  443.         if v.attack_position == true then
  444.             v.attack_position = patrol:point(v.alife_point)
  445.         end
  446.  
  447. --[[
  448.         if v._prior == 55 then
  449.             -- Проверка валидности сигнала
  450.             if need_check == false then
  451.                 abort("Incorrect attack path %s", v.alife_path)
  452.             else
  453.                 -- Дебаговая проверка. Проверяем расстояние между вершиной патрульного пути и графпоинтом, которому она принадлежит.
  454.                 local arrive_vertex_id = patrol:game_vertex_id(v.alife_point)
  455.                 local arrive_vertex_position = game_graph():vertex(arrive_vertex_id):level_point()
  456.                 if arrive_vertex_position:distance_to_sqr(v.attack_position) > 25 then
  457.                     printf("graph [%s][%s][%s] point [%s][%s][%s]",
  458.                             arrive_vertex_position.x,
  459.                             arrive_vertex_position.y,
  460.                             arrive_vertex_position.z,
  461.                             v.attack_position.x,
  462.                             v.attack_position.y,
  463.                             v.attack_position.z)
  464.                     abort("!There is no graph in 'arrive_to_wait' point ["..v.alife_path.."] num [%s]", tostring(v.alife_point))
  465.                 end
  466.             end
  467.         end
  468. ]]
  469.     end
  470. end
  471.  
  472. -- Апдейт работ смарттеррейна.
  473. -- Если передается object, то значит нужно найти только для него
  474. function se_smart_terrain:update_jobs(object)
  475.     -- Сортируем НПС по увеличению приоритета занимаемой работы
  476.     if object ~= nil then
  477.         --printf("UPDATE NPC %s", object:name())
  478.         local npc_info = self.npc_info[object.id]
  479.         -- Выбираем работу
  480.         local selected_job_id, selected_job_prior, selected_job_link = job_iterator(self.jobs, npc_info, 0, self)
  481.         if selected_job_id == nil then
  482.             print_table(self.jobs)
  483.             abort("Insufficient smart_terrain jobs %s", self:name())
  484.         end
  485.  
  486.         -- Назначаем работу
  487.         --if selected_job_id ~= nil and selected_job_prior > npc_info.job_prior then
  488.         if selected_job_id ~= npc_info.job_id and selected_job_link ~= nil then
  489.             -- Установить себе выбранную работу
  490.             --printf("NPC %s FOUND JOB %s SECTION %s", npc_info.se_obj:name(), selected_job_id, self.job_data[selected_job_link.job_id].section)
  491.             -- Если НПС был на работе - выгоняем его с нее.
  492.             if npc_info.job_link ~= nil then
  493.                 self.npc_by_job_section[self.job_data[npc_info.job_link.job_id].section] = nil
  494.                 npc_info.job_link.npc_id = nil
  495.             end
  496.  
  497.             selected_job_link.npc_id = npc_info.se_obj.id
  498.             self.npc_by_job_section[self.job_data[selected_job_link.job_id].section] = selected_job_link.npc_id
  499.  
  500.             npc_info.job_id = selected_job_link.job_id
  501.             npc_info.job_prior = selected_job_link._prior
  502.             npc_info.begin_job = false
  503.             -- сохраняем ссылку на работу, для облегчения удаления
  504.             npc_info.job_link = selected_job_link
  505.  
  506.             -- завершаем текущую работу
  507.             local obj_storage = db.storage[npc_info.se_obj.id]
  508.             if obj_storage ~= nil then
  509.                 xr_logic.switch_to_section(obj_storage.object, self.ltx, "nil")
  510.                 --xr_logic.activate_by_section(obj_storage.object, self.ltx, "nil", self:name(), false)
  511.             end
  512.         end
  513.  
  514.         if npc_info.begin_job ~= true then
  515.             -- Проверяем, дошел ли персонаж до работы (то есть может ли он начать ее выполнение)
  516.             local job_data = self.job_data[npc_info.job_id]
  517.             --if job_data == nil then
  518.                 --printf("Couldnt find job, id %s", tostring(npc_info.job_id))
  519.                 --print_table(self.jobs)
  520.             --end
  521.             if self:distance_to_job_location(npc_info.se_obj, job_data) < JOB_POSITION_THRESHOLD then
  522.                 -- Начинаем выполнять работу
  523.                 --printf("[smart_terrain %s] gulag: beginJob: obj=%s job= %s", self:name(), npc_info.se_obj:name(), job_data.section)
  524.  
  525.                 npc_info.begin_job = true
  526.                 if npc_info.stype == modules.stype_mobile then
  527.                     npc_info.se_obj:smart_terrain_task_activate()
  528.                 end
  529.  
  530.                 local obj_storage = db.storage[npc_info.se_obj.id]
  531.                 if obj_storage ~= nil then
  532.                     self:setup_logic(obj_storage.object)
  533.                 end
  534.             end
  535.         end
  536.         return
  537.     end
  538.  
  539.     --printf("UPDATE JOBS %s", self:name())
  540.     table.sort(self.npc_info, function(a,b) return a.job_prior < b.job_prior end )
  541.     for k,v in pairs(self.npc_info) do
  542.         -- Выбираем работу
  543.         local selected_job_id, selected_job_prior, selected_job_link = job_iterator(self.jobs, v, 0, self)
  544.         -- Назначаем работу
  545.         --if selected_job_id ~= nil and selected_job_prior > v.job_prior then
  546.         if selected_job_id ~= v.job_id and selected_job_link ~= nil then
  547.             -- Установить себе выбранную работу
  548.             --printf("NPC %s FOUND JOB %s SECTION %s", v.se_obj:name(), selected_job_id, self.job_data[selected_job_link.job_id].section)
  549.             -- Если НПС был на работе - выгоняем его с нее.
  550.             if v.job_link ~= nil then
  551.                 self.npc_by_job_section[self.job_data[v.job_link.job_id].section] = nil
  552.                 v.job_link.npc_id = nil
  553.             end
  554.  
  555.             selected_job_link.npc_id = v.se_obj.id
  556.             self.npc_by_job_section[self.job_data[selected_job_link.job_id].section] = selected_job_link.npc_id
  557.  
  558.             v.job_id = selected_job_link.job_id
  559.             v.job_prior = selected_job_link._prior
  560.             v.begin_job = false
  561.             -- сохраняем ссылку на работу, для облегчения удаления
  562.             v.job_link = selected_job_link
  563.  
  564.             -- завершаем текущую работу
  565.             local obj_storage = db.storage[v.se_obj.id]
  566.             if obj_storage ~= nil then
  567.                 xr_logic.switch_to_section(obj_storage.object, self.ltx, "nil")
  568.                 --xr_logic.activate_by_section(obj_storage.object, self.ltx, "nil", self:name(), false)
  569.             end
  570.         end
  571.  
  572.         if v.begin_job ~= true then
  573.             -- Проверяем, дошел ли персонаж до работы (то есть может ли он начать ее выполнение)
  574.             local job_data = self.job_data[v.job_id]
  575.             if self:distance_to_job_location(v.se_obj, job_data) < JOB_POSITION_THRESHOLD then
  576.                 -- Начинаем выполнять работу
  577.                 --printf("[smart_terrain %s] gulag: beginJob: obj=%s job= %s", self:name(), v.se_obj:name(), job_data.section)
  578.  
  579.                 v.begin_job = true
  580.                 if v.stype == modules.stype_mobile then
  581.                     v.se_obj:smart_terrain_task_activate()
  582.                 end
  583.  
  584.                 local obj_storage = db.storage[v.se_obj.id]
  585.                 if obj_storage ~= nil then
  586.                     self:setup_logic(obj_storage.object)
  587.                 end
  588.             end
  589.         end
  590.     end
  591. end
  592. -- настроить логику для объекта, который в онлайне.
  593. function se_smart_terrain:setup_logic(obj)
  594.     --printf("setup npc logic %s", obj:name())
  595. --  callstack()
  596.     local npc_data = self.npc_info[obj:id()]
  597.     local job = self.job_data[npc_data.job_id]
  598.     local ltx = job.ini_file or self.ltx
  599.     local ltx_name = job.ini_path or self.ltx_name
  600.  
  601.     xr_logic.configure_schemes(obj, ltx, ltx_name, npc_data.stype, job.section, job.prefix_name or self:name())
  602.  
  603.     local sect = xr_logic.determine_section_to_activate(obj, ltx, job.section, db.actor)
  604.     if utils.get_scheme_by_section(job.section) == "nil" then
  605.         abort("[smart_terrain %s] section=%s, don't use section 'nil'!", self:name(), sect)
  606.     end
  607.  
  608.  
  609.     xr_logic.activate_by_section(obj, ltx, sect, job.prefix_name or self:name(), false)
  610. end
  611. -- получить работу, которую занимает объект
  612. function se_smart_terrain:getJob(obj_id)
  613.     return self.job_data[self.npc_info[obj_id].job_id]
  614. end
  615. -- Расстояние до работы
  616. function se_smart_terrain:distance_to_job_location(obj, job)
  617.     local obj_gv, obj_pos
  618.  
  619.     local storage = db.storage[obj.id]
  620.  
  621.     if storage == nil then
  622.         obj_gv, obj_pos = game_graph():vertex(obj.m_game_vertex_id), obj.position
  623.     else
  624.         local obj = db.storage[obj.id].object
  625.         obj_gv, obj_pos = game_graph():vertex(obj:game_vertex_id()), obj:position()
  626.     end
  627.  
  628.     local job_gv = game_graph():vertex(job.game_vertex_id)
  629.  
  630.     if obj_gv:level_id() == job_gv:level_id() then
  631.         return obj_pos:distance_to(job.position)
  632.     else
  633.         return 10000
  634.     end
  635. end
  636. -- Получение персонажа, который занимает указанную работу.
  637. function se_smart_terrain:idNPCOnJob(job_name)
  638.     return self.npc_by_job_section[job_name]
  639. end
  640. function se_smart_terrain:switch_to_desired_job(npc)
  641.     -- Берем текущую работу НПС
  642.     local npc_id = npc:id()
  643.     local npc_info = self.npc_info[npc_id]
  644.  
  645.     --printf("***** %s -> %s", npc:name(), tostring(npc_info.need_job))
  646.     local changing_npc_id = self.npc_by_job_section[npc_info.need_job]
  647.     --printf("changing_npc_id %s", tostring(changing_npc_id))
  648.  
  649.     if changing_npc_id == nil then
  650.         -- Мы не нашли с кем меняться, просто ресетим себя
  651.         self.npc_info[npc_id].job_link = nil
  652.         self.npc_info[npc_id].job_id = -1
  653.         self.npc_info[npc_id].job_prior = -1
  654.         self:update_jobs(self.npc_info[npc_id].se_obj)
  655.  
  656.         --print_table(self.npc_by_job_section)
  657.         --abort("ERROR during channging NPC")
  658.         return
  659.     end
  660.  
  661.     if self.npc_info[changing_npc_id] == nil then
  662.         -- Мы не нашли с кем меняться, просто ресетим себя
  663.         self.npc_info[npc_id].job_link = nil
  664.         self.npc_info[npc_id].job_id = -1
  665.         self.npc_info[npc_id].job_prior = -1
  666.         self:update_jobs(self.npc_info[npc_id].se_obj)
  667.  
  668.         --print_table(self.npc_by_job_section)
  669.         --abort("ERROR during channging NPC")
  670.         return 
  671.     end
  672.  
  673.     local desired_job = self.npc_info[changing_npc_id].job_id
  674.  
  675.     -- Переключаем НПС на желаемую работу
  676.     if npc_info.job_link ~= nil then
  677.         self.npc_by_job_section[self.job_data[npc_info.job_link.job_id].section] = nil
  678.         npc_info.job_link.npc_id = nil
  679.     end
  680.  
  681.     local selected_job_link = self.npc_info[changing_npc_id].job_link
  682.  
  683.     selected_job_link.npc_id = npc_info.se_obj.id
  684.  
  685.     self.npc_by_job_section[self.job_data[selected_job_link.job_id].section] = selected_job_link.npc_id
  686.  
  687.     npc_info.job_id = selected_job_link.job_id
  688.     npc_info.job_prior = selected_job_link._prior
  689.     npc_info.begin_job = true
  690.  
  691.     -- сохраняем ссылку на работу, для облегчения удаления
  692.     npc_info.job_link = selected_job_link
  693.     npc_info.need_job = "nil"
  694.  
  695.     local obj_storage = db.storage[npc_id]
  696.     if obj_storage ~= nil then
  697.         self:setup_logic(obj_storage.object)
  698.     end
  699.  
  700.     -- Освобождаем НПС, который занимает желаемую работу и говорим ему перевыбрать работу
  701.     self.npc_info[changing_npc_id].job_link = nil
  702.     self.npc_info[changing_npc_id].job_id = -1
  703.     self.npc_info[changing_npc_id].job_prior = -1
  704.     self:update_jobs(self.npc_info[changing_npc_id].se_obj)
  705. end
  706.  
  707.  
  708. --*******************************************************
  709. -- СЕЙВ/ЛОАД
  710. --*******************************************************
  711. -- сохранение
  712. function se_smart_terrain:STATE_Write(packet)
  713.     cse_alife_smart_zone.STATE_Write(self, packet)
  714.  
  715.     set_save_marker(packet, "save", false, "se_smart_terrain")
  716.  
  717.     if self.combat_manager == nil then
  718.         self.combat_manager = sim_combat.CCombat_manager(self, sim_board.get_sim_board())
  719.     end
  720.     self.combat_manager:save(packet)
  721.  
  722.     -- Информацию о НПС в смарте
  723.     local n = 0
  724.     for k,v in pairs(self.npc_info) do
  725.         n = n + 1
  726.     end
  727.  
  728.     packet:w_u8(n)
  729.     for k,v in pairs(self.npc_info) do
  730.         packet:w_u16(k)
  731.         packet:w_u8(v.job_prior)
  732.         packet:w_u8(v.job_id)
  733.         packet:w_bool(v.begin_job)
  734.         packet:w_stringZ(v.need_job)
  735.     end
  736.  
  737.     n = 0
  738.     for k,v in pairs(self.dead_time) do
  739.         n = n + 1
  740.     end
  741.     packet:w_u8(n)
  742.     for k,v in pairs(self.dead_time) do
  743.         packet:w_u8(k)
  744.         utils.w_CTime(packet, v)
  745.     end
  746.  
  747.  
  748.     set_save_marker(packet, "save", true, "se_smart_terrain")
  749. end
  750.  
  751. -- восстановление
  752. function se_smart_terrain:STATE_Read(packet, size)
  753.     cse_alife_smart_zone.STATE_Read(self, packet, size)
  754.  
  755.     -- под LevelEditor не пытаться читать из пакета ничего
  756.     if editor() then
  757.         return
  758.     end
  759.  
  760.     set_save_marker(packet, "load", false, "se_smart_terrain")
  761.     self:read_params()
  762.  
  763.     if self.combat_manager == nil then
  764.         self.combat_manager = sim_combat.CCombat_manager(self, sim_board.get_sim_board())
  765.     end
  766.     self.combat_manager:load(packet)
  767.  
  768.     -- Информацию о НПС в смарте
  769.     local n = packet:r_u8()
  770.     --printf("load %s npc", tostring(n))
  771.     self.npc_info = {}
  772.     for i = 1,n do
  773.         local id = packet:r_u16()
  774.         --printf("__ id %s", tostring(id))
  775.         self.npc_info[id] = {}
  776.         local npc_info = self.npc_info[id]
  777.  
  778.         npc_info.job_prior = packet:r_u8()
  779.         --printf("__ job_prior %s", tostring(npc_info.job_prior))
  780.         if npc_info.job_prior == 255 then
  781.             npc_info.job_prior = -1
  782.         end
  783.         npc_info.job_id = packet:r_u8()
  784.         --printf("__ job_id %s", tostring(npc_info.job_id))
  785.         if npc_info.job_id == 255 then
  786.             npc_info.job_id = -1
  787.         end
  788.         npc_info.begin_job = packet:r_bool()
  789.         --printf("__ begin_job %s", tostring(npc_info.begin_job))
  790.         npc_info.need_job = packet:r_stringZ()
  791.     end
  792.  
  793.     n = packet:r_u8()
  794.     self.dead_time = {}
  795.     --printf("load %s dead_time", tostring(n))
  796.     for i =1,n do
  797.         local job_id = packet:r_u8()
  798.         --printf("__ job_id %s", tostring(job_id))
  799.         local dead_time = utils.r_CTime(packet)
  800.         self.dead_time[job_id] = dead_time
  801.     end
  802.  
  803.     self.need_init_npc = true
  804.  
  805.     set_save_marker(packet, "load", true, "se_smart_terrain")
  806. end
  807. -- Инициализация НПС после загрузки.
  808. function se_smart_terrain:init_npc_after_load()
  809.     local function find_job(jobs, npc_info)
  810.         for k,v in pairs(jobs) do
  811.             if v.jobs ~= nil then
  812.                 find_job(v.jobs, npc_info)
  813.             else
  814.                 if v.job_id == npc_info.job_id then
  815.                     npc_info.job_link = v
  816.                     v.npc_id = npc_info.se_obj.id
  817.                     return
  818.                 end
  819.             end
  820.         end
  821.     end
  822.  
  823.  
  824.     local sim = alife()
  825.     --printf("[%s] init_npc_after_load", self:name())
  826.     for k,v in pairs(self.npc_info) do
  827.         local npc_info = self:fill_npc_info(sim:object(k))
  828.  
  829.         npc_info.job_prior = v.job_prior
  830.         npc_info.job_id = v.job_id
  831.         npc_info.begin_job = v.begin_job
  832.         npc_info.need_job = v.need_job
  833.  
  834.         --Теперь надо найти данную работу и выставить ссылку на нее.
  835.         find_job(self.jobs, npc_info)
  836.  
  837.         self.npc_info[k] = npc_info
  838.         if npc_info.job_link ~= nil then
  839.             self.npc_by_job_section[self.job_data[npc_info.job_link.job_id].section] = k
  840.         end
  841.     end
  842. end
  843.  
  844.  
  845.  
  846.  
  847.  
  848.  
  849.  
  850.  
  851.  
  852.  
  853.  
  854.  
  855. -- Положить предмет в инвентори бокс смарта, при условии что он существует
  856. function se_smart_terrain:call_respawn()
  857.     if self.respawn then
  858.         -- TODO: Вставить появление вещей в инвентори боксе
  859.     end
  860. end
  861.  
  862. --' Возвращает отформатированную строку свойств смарта
  863. function se_smart_terrain:get_smart_props()
  864.     local props = smart_names.get_smart_terrain_name(self)
  865.     if(props==nil) or (_G.dev_debug) then
  866.         props = self:name().."  ["..self.id.."]\\n"..
  867.                   "player = "..self.player_name.."\\n"..
  868.                   self.sim_type.." : "..tostring(self.sim_value).."\\n"..
  869.                   "squad_id = "..tostring(self.squad_id).."\\n"..
  870.                   "capacity = "..tostring(self.squad_capacity).." ("..
  871.                   sim_board.get_sim_board():get_smart_population(self)..")\\n"
  872.  
  873.         if self.respawn_sector ~= nil then
  874.             props = props.."\\nrespawn_sector: "..tostring(self.respawn_sector).."\\n"
  875.         end
  876.  
  877.         --' Добавляем информацию о находящихся в смарте отрядах
  878.         for k,v in pairs(sim_board.get_sim_board().smarts[self.id].squads) do
  879.             power = tostring(v.squad_power)
  880.             props = props .. tostring(v.squad_id) .. " : " .. power .. "\\n"
  881.         end
  882.     end
  883.     return props
  884. end
  885.  
  886.  
  887.  
  888. --' Отрисовка смарта на игровом поле
  889. function se_smart_terrain:show()
  890.     local time = time_global()
  891.     if(self.showtime~=nil) and (self.showtime+200>=time) then
  892.         return
  893.     end
  894.     self.showtime = time
  895.  
  896.     local player = self.player_name
  897.     local spot = "none"
  898.     if(player~="none") then
  899.         local relation = 0
  900.         if(db.actor) then
  901.             relation = db.actor:community_goodwill(player)+game_relations.get_factions_community(player, alife():actor():community())
  902.         else
  903.             relation = game_relations.get_factions_community(player, alife():actor():community())
  904.         end
  905.         if(relation==self.relation) then
  906.             return
  907.         else
  908.             if(relation>=1000) then
  909.                 spot = "friend"
  910.             elseif(relation<=-1000) then
  911.                 spot = "enemy"
  912.             else
  913.                 spot = "neutral"
  914.             end
  915.         end
  916.         self.relation = relation
  917.     end
  918.  
  919.     if(self.smrt_showed_spot==spot) then
  920.         level.map_change_spot_hint(self.id, "alife_presentation_smart_"..self.sim_type.."_"..self.smrt_showed_spot, self:get_smart_props())
  921.         return
  922.     end
  923.     if(_G.dev_debug) then
  924.         if(self.smrt_showed_spot~=nil) then
  925.             level.map_remove_object_spot(self.id, "alife_presentation_smart_"..self.sim_type.."_"..self.smrt_showed_spot)
  926.         end
  927.         level.map_add_object_spot(self.id, "alife_presentation_smart_"..self.sim_type.."_"..spot, self:get_smart_props())
  928.         self.smrt_showed_spot = spot
  929.     else
  930. --[[
  931.         if(self.sim_type=="base") then
  932.             if(self.smrt_showed_spot~=nil) then
  933.                 level.map_remove_object_spot(self.id, "alife_presentation_smart_base_"..self.smrt_showed_spot)
  934.             end
  935.             level.map_add_object_spot(self.id, "alife_presentation_smart_base_"..spot, self:get_smart_props())
  936.             self.smrt_showed_spot = spot
  937.         else
  938. ]]
  939.             if(self.smrt_showed_spot~=nil) and
  940.               (level.map_has_object_spot(self.id, "alife_presentation_smart_"..self.sim_type.."_"..self.smrt_showed_spot)~=0)
  941.             then
  942.                 level.map_remove_object_spot(self.id, "alife_presentation_smart_base_"..self.smrt_showed_spot)
  943.             end
  944. --      end
  945.     end
  946. end
  947. --' Обновление информации о смарте на игровом поле
  948. function se_smart_terrain:refresh()
  949.     self:show()
  950. end
  951. --' Убирание отрисовки смарта на игровом поле
  952. function se_smart_terrain:hide()
  953.     if self.smrt_showed_spot == nil then
  954.         return
  955.     end
  956.     level.map_remove_object_spot(self.id, "alife_presentation_smart_"..self.sim_type.."_"..self.smrt_showed_spot)
  957. end
  958.  
  959. -- Обновление.
  960. -- В онлайне вызывается через binder.
  961. -- Также может вызваться принудительно из xr_effects
  962. function se_smart_terrain:update()
  963.     cse_alife_smart_zone.update( self )
  964.  
  965.     self.combat_manager:update()
  966.  
  967.     local current_time = time_global()
  968.     if self.check_time~=nil and current_time < self.check_time then
  969.         return
  970.     end
  971.     if db.actor ~= nil then
  972.         local distance = db.actor:position():distance_to_sqr(self.position)
  973.         local idle_time = math.max(60, 0.003 * distance)
  974.         self.check_time = current_time + idle_time
  975.     else
  976.         self.check_time = current_time + 10
  977.     end
  978.  
  979.     -- Проверяем, не истек ли запрет на занимание работы, на которой убили НПС
  980.     local current_time = game.get_game_time()
  981.     for k,v in pairs(self.dead_time) do
  982.         if current_time:diffSec(v) >= DEATH_IDLE_TIME then
  983.             self.dead_time[k] = nil
  984.         end
  985.     end
  986.  
  987.     -- Перевыбор работ
  988.     self:update_jobs()
  989. end
  990.  
  991.  
  992.  
  993. -- установить логику и сообщить смарту, что объект перешёл в онлайн.
  994. -- вызывается из net_spawn() объектов
  995. function setup_gulag_and_logic_on_spawn(obj, st, sobject, stype, loaded)
  996.     local sim = alife()
  997.     if sim then
  998.         local strn_id = sobject.m_smart_terrain_id
  999.         printf( "setup_gulag_and_logic_on_spawn obj=%s, strn_id=%s, loaded=%s", obj:name(), tostring(strn_id), tostring(loaded))
  1000.  
  1001.         if strn_id ~= nil and strn_id ~= 65535 then
  1002.             local strn                   = sim:object(strn_id)
  1003.             local need_setup_logic       = strn.npc_info[obj:id()].begin_job == true and not loaded
  1004.  
  1005.             if need_setup_logic then
  1006.                 strn:setup_logic(obj)
  1007.             else
  1008.                 xr_logic.initialize_obj(obj, st, loaded, db.actor, stype)
  1009.             end
  1010.         else
  1011.             xr_logic.initialize_obj(obj, st, loaded, db.actor, stype)
  1012.         end
  1013.     else
  1014.         xr_logic.initialize_obj(obj, st, loaded, db.actor, stype)
  1015.     end
  1016. end
  1017.  
  1018. -- Убираем объект из смарта при смерти
  1019. function on_death(obj)
  1020.     local sim = alife()
  1021.     if sim then
  1022.         local obj = sim:object(obj.id)
  1023.         if obj == nil then return end
  1024.         local strn_id = obj:smart_terrain_id()
  1025.         if strn_id ~= 65535 then
  1026.             printf("clear dead object %s", obj:name())
  1027.             sim:object(strn_id):clear_dead(obj)
  1028.         end
  1029.     end
  1030. end
  1031.  
  1032. --------------------
  1033. -- прочитать секцию [smart_terrains]
  1034. -- вызывается объектами, которые могут ходить под smart terrain
  1035. function read_smart_terrain_conditions( self )
  1036.     if self.ini and self.ini:section_exist(SMART_SECTION_NAME) then
  1037.         local conds = {}
  1038.         local accepts = false
  1039.         local result, field, str
  1040.         local n = self.ini:line_count(SMART_SECTION_NAME)
  1041.         if n > 0 then
  1042.             for i = 0, n-1 do
  1043.                 result, field, str = self.ini:r_line(SMART_SECTION_NAME, i, "", "" )
  1044.                 conds[field] = xr_logic.parse_condlist(self, SMART_SECTION_NAME, field, str)
  1045.             end
  1046.             return conds
  1047.         end
  1048.     end
  1049.     return nil
  1050. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement