[smart_terrain] Pomoc!

Regulamin forum
Dział "Tworzenie Modyfikacji" służy do zadawania pytań odnośnie tworzenia modyfikacji, edytowania plików gry, oraz pisania tzw. tutoriali, czyli poradników odnośnie modyfikowania gry.
Nie zadajemy tutaj pytań odnośnie modyfikacji gotowych (nie dyskutujemy o nich!), prócz prób ich połączenia etc.


Przy cytowaniu plików *.LTX stosować tag [SYNTAX="ini"], przy cytowaniu plików *.script [SYNTAX="lua"] a przy cytowaniu plików *.xml - [SYNTAX="xml"].

[smart_terrain] Pomoc!

Postprzez max1071 w 12 Gru 2023, 12:08

Hejka Stalkerzy :E

ostatnio trochę więcej czasu i wróciło się do modowania...
ale jest jeden chaczyk...
nic nie zostało zmodyfikowane odnośnie smart_terrain-ów ale(są dwie jedna daje druga nie(dobra nie zaciemniam przekazu))...
otóż...
jest pewien problem.
stalker coś ostatnio w hu..śtaweczki leci i nie usuwa mi np wspisów o postaciach w smart-ach...
czysty przykład ze tartaku bo to głównie jak na razie o niego chodzi... są te przezajeqrwabiste zombiaczki ...
no i jak je się zabije.. spojler... nie grałeś to odpuść sobie z dwie liniki! otóż... idziesz ich najedwabić bądź nie... wbijasz na tartak łapiesz narzędzia pod pachę... no i w tym momencie... jeśli ich zabiłeś lub nie gra powinna po zat_b104 wyrejestrować zombiaczki chociaż tego za cholerę nie robi...
i daje overflowa na tablice do zapisu postaci... później problemy z tym związane są takie że w zonie nielimiotowana ilość mutantów się spawni za pomocą pewnie między-innymi tegoż smart_terrain-u.
niby wielkie halo zaraz ktoś powie ale trzeba liczyć się z tym że im więcej postaci jest rejestrowanych i wyrejestrowywanych gra.. staję się nie realna ponieważ po przekroczeniu danych statystyk... gra centralnie się sypie... zaraz zaczyna brakować ID dla postaci itd...
zatem chyba znalazłem buga w samym silniku gry...
prosiłbym o pomoc w jego rozwiązaniu bo nie mam pojęcia jak to zrobić by konkretnie ze smart-ów stalkerzy jak i mutaki były w odpowiedni sposób usuwani.

bug objawia się tym że :

if self.population < 0 then
abort("Smart_terrain [%s] population can't be less than zero!!!", self:name())
end

to wysakuje w konsoli w %s wiadomo będzie nazwa w tym momencie "zat_b104_zombied"
więc... jeśli przekroczy wartość dodanych do 255... automatycznie zamienia się ono na - i liczy w tył..
a więc jeśli już ten bug następuję nie zapiszesz dalej gry bo skrypt zwyczajnie na to nie pozwoli.

------ upd 15-12

znalazłem coś ciekawego... otóż w pliku samym smart_terrain
po zabiciu danego stalkera ona automatycznie tworzy clear_dead
ale nic po za tym... może i tu jest pies pogrzebany?

------ upd 22-12

naprawiłem mam nadzieję częściowo ten błąd, stalker xbug-engine to normalnie... szkoda gadać.
rozwiązanie wpadłem takie że po prostu przerobiłem pliki gry i dałem mini debugger by sprawdzać co gdzie jak i z czym mieli
załączam plik smart_terrain.script
Kod: [ Pobierz ] [ Ukryj ]
local DEATH_IDLE_TIME = 10*60 -- секунд
local RESPAWN_IDLE = 1000   -- секунд игрового времени
local RESPAWN_RADIUS = 150 -- радиус респауна(если актер ближе, то не спаунить)
SMART_TERRAIN_SECT = "smart_terrain"
smart_terrains_by_name = {}
local locations_ini                     = ini_file("misc\\smart_terrain_masks.ltx")
local sm_dev_debug=true

db.stalker_smart={}

function printfb(a,...)
--debuger (CUT)
end

nearest_to_actor_smart = {id = nil , dist = math.huge}

local path_fields = { "path_walk", "path_main", "path_home", "center_point" }

local valid_territory = {
        default = true,
        base = true,
        resource = true,
        territory = true
}

--' Проверка, что нпс подходит работе
local function job_avail_to_npc(npc_info, job_info, smart)
        --printf("job_avail_to_npc %s job %s smart %s", npc_info.se_obj:name(), tostring(job_info.job_id), smart:name())
        local job = smart.job_data[job_info.job_id]
        if job ~= nil then
                job = job.section
        end

        if smart.dead_time[job_info.job_id] ~= nil then
                return false
        end

        -- Проверка условия "монстровости"
        if job_info._precondition_is_monster ~= nil and job_info._precondition_is_monster ~= npc_info.is_monster then
                return false
        end

        --' Проверяем подходит ли нпс по предикату
        if job_info._precondition_function ~= nil then
                if not job_info._precondition_function(npc_info.se_obj, smart, job_info._precondition_params, npc_info) then
                        return false
                end
        end
        return true
end


-- Итерируемся по НПС, начинаем со свободных нпс, потом НПС на низкоприоритетных работах, потом на высокоприоритетных.
-- Для каждого конкретного НПС ищем работу.
-- Отсеиваем в поиске работы, приоритет которых ниже, чем у текущей.
local function job_iterator(jobs, npc_data, selected_job_prior, smart)
        --printf("  iterate")
        -- итерируемся по работам
        local current_job_prior = selected_job_prior
        local selected_job_id = nil
        local selected_job_link = nil
        for k,v in pairs(jobs) do
                -- Если приоритет у проверяемой работы ниже, чем приоритет текущей выбранной работы НПС - завершаем выполнение
                if current_job_prior > v._prior then
                        return selected_job_id, current_job_prior, selected_job_link
                end
                -- Проверяем, может ли НПС занять данную работу
                if job_avail_to_npc(npc_data, v, smart) then
                        -- Это работа-кластер или работа-описание.
                        if v.job_id == nil then
                                -- Вызываем рекурсивно себя для списка работ кластера
                                selected_job_id, current_job_prior, selected_job_link = job_iterator(v.jobs, npc_data, selected_job_prior, smart)
                        else
                                -- Если работа пустая или ее занимаем мы сами - выбираем ее.
                                if v.npc_id == nil then
                                        return v.job_id, v._prior, v
                                elseif v.job_id == npc_data.job_id then
                                        return v.job_id, v._prior, v
                                end
                        end
                end
        end
        return selected_job_id, current_job_prior, selected_job_link
end

-- Расстояние до работы
local function arrived_to_smart(obj, smart)
        local obj_gv, obj_pos

        local storage = db.storage[obj.id]

        if storage == nil then
                obj_gv, obj_pos = game_graph():vertex(obj.m_game_vertex_id), obj.position
        else
                local obj = db.storage[obj.id].object
                obj_gv, obj_pos = game_graph():vertex(obj:game_vertex_id()), obj:position()
        end

        local smart_gv = game_graph():vertex(smart.m_game_vertex_id)

        if obj.group_id then
                local squad = smart.board.squads[obj.group_id]
                if squad ~= nil and squad.current_action then
                        if squad.current_action.name == "reach_target" then
                                local squad_target = simulation_objects.get_sim_obj_registry().objects[squad.assigned_target_id]
                                if squad_target ~= nil then
                                        return squad_target:am_i_reached(squad)
                                else
                                        return alife():object(squad.assigned_target_id):am_i_reached(squad)
                                end
                        elseif squad.current_action.name == "stay_point" then
                                return true
                        end
                end
        end

        if obj_gv:level_id() == smart_gv:level_id() then
                return obj_pos:distance_to_sqr(smart.position) <= 10000 --Ближе 100 метров
        else
                return false
        end
end

----------------------------------------------------------------------------------------------------------------------
-- Класс "se_smart_terrain". Обеспечивает поддержку smart terrain в ОФЛАЙНЕ.
----------------------------------------------------------------------------------------------------------------------
class "se_smart_terrain" (cse_alife_smart_zone)
function se_smart_terrain:__init(section) super(section)
        self.initialized        = false
        self.b_registred        = false
        self.population         = 0

        self.npc_to_register = {}
        self.npc_by_job_section = {}
        self.dead_time = {}

        -- Таблица для хранения зарегистренных НПС
        self.npc_info  = {} -- Те, кто уже пришел и стал на работу
        self.arriving_npc  = {} -- Только идущие на работу.
end
function se_smart_terrain:on_before_register()
        cse_alife_smart_zone.on_before_register(self)
        self.board = sim_board.get_sim_board()
        self.board:register_smart(self)
        self.smart_level        = alife():level_name(game_graph():vertex(self.m_game_vertex_id):level_id())
        --printf("SMARTLEVEL %s level %s", self:name(), tostring(self.smart_level))
end

function se_smart_terrain:on_register()
        cse_alife_smart_zone.on_register(self)
        -- Проверяем кастомдату обьекта на наличие стори айди.
        story_objects.check_spawn_ini_for_story_id(self)
        simulation_objects.get_sim_obj_registry():register(self)

        printf("register smart %s", self:name())

        if sm_dev_debug then
                self:refresh()
        end

        printf("Returning alife task for object [%s] game_vertex [%s] level_vertex [%s] position %s", self.id, self.m_game_vertex_id, self.m_level_vertex_id, vec_to_str(self.position))
        self.smart_alife_task = CALifeSmartTerrainTask(self.m_game_vertex_id, self.m_level_vertex_id)

        smart_terrains_by_name[self:name()] = self
        self.b_registred = true

        self:load_jobs()

        self.board:init_smart(self)

        if self.need_init_npc == true then
                self.need_init_npc = false
                self:init_npc_after_load()
        end

        -- Регистрим персонажей, которые добавили до регистрации смарта. (отложенный список)
        self:register_delayed_npc()

        self.check_time = time_global()
end
-- анрегистрация объекта в симуляторе.
-- вызывается симулятором.
function se_smart_terrain:on_unregister()
        cse_alife_smart_zone.on_unregister(self)
        self.board:unregister_smart(self)
        smart_terrains_by_name[self:name()] = nil
        unregister_story_object_by_id(self.id)
        simulation_objects.get_sim_obj_registry():unregister(self)
end
-- чтение custom data.
function se_smart_terrain:read_params()
        self.ini  = self:spawn_ini()

        if not self.ini:section_exist( SMART_TERRAIN_SECT ) then
                abort( "[smart_terrain %s] no configuration!", self:name() )
                self.disabled = true
                return
        end
        local filename = utils.cfg_get_string(self.ini, SMART_TERRAIN_SECT, "cfg",                              self, false, "")
        local fs = getFS()
        if filename then
                if fs:exist("$game_config$",filename) then
                        self.ini = ini_file(filename)
                else
                        abort("There is no configuration file [%s] in smart_terrain [%s]", filename,    self:name())
                end
        end
        local ini = self.ini
        self.sim_type           = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "sim_type",             self, false, "", "default")

        --' Вычитка симуляционных свойств
        if valid_territory[self.sim_type] == nil then
                abort("Wrong sim_type value [%s] in smart [%s]", self.sim_type, self:name())
        end

        self.squad_id           = utils.cfg_get_number(ini, SMART_TERRAIN_SECT, "squad_id",             self, false, 0)
        self.respawn_sector = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "respawn_sector",   self, false, "")
        self.respawn_radius  = utils.cfg_get_number(ini, SMART_TERRAIN_SECT, "respawn_radius",  self, false, 150)
        if self.respawn_sector ~= nil then
                if self.respawn_sector == "default" then
                        self.respawn_sector = "all"
                end
                self.respawn_sector = xr_logic.parse_condlist(nil, SMART_TERRAIN_SECT, "respawn_sector", self.respawn_sector)
        end

        self.mutant_lair                = utils.cfg_get_bool(ini, SMART_TERRAIN_SECT, "mutant_lair",    self, false)
        self.no_mutant                  = utils.cfg_get_bool(ini, SMART_TERRAIN_SECT, "no_mutant",              self, false)
        if self.no_mutant == true then
                printf("Found no mutant point %s", self:name())
        end
        self.forbidden_point    = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "forbidden_point", self, false, "")

        --' Рестрикторы для симуляции
        self.def_restr          = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "def_restr",    self, false, "", nil)
        self.att_restr          = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "att_restr",    self, false, "", nil)
        self.safe_restr         = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "safe_restr",   self, false, "", nil)

        self.spawn_point        = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "spawn_point",  self, false, "")

        self.arrive_dist        = utils.cfg_get_number(ini, SMART_TERRAIN_SECT, "arrive_dist",  self, false, 30)

--      self.max_population = utils.cfg_get_number(ini, SMART_TERRAIN_SECT, "max_population", self, false, 0)
        local max_population = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "max_population", self, false, "", 0)
        local parsed_condlist = xr_logic.parse_condlist(nil, SMART_TERRAIN_SECT, "max_population", max_population)
        self.max_population = tonumber(xr_logic.pick_section_from_condlist(get_story_object("actor"), nil, parsed_condlist))

--      self.sim_avail          = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "sim_avail",    self, false, "")
--      if self.sim_avail ~= nil then
--              self.sim_avail = xr_logic.parse_condlist(nil, SMART_TERRAIN_SECT, "sim_avail",  self.sim_avail)
--      end

        local respawn_params = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "respawn_params",          self, false, "", nil)
        self.respawn_only_smart = utils.cfg_get_bool(ini, SMART_TERRAIN_SECT, "respawn_only_smart", self, false, false)

        local smart_control_section = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "smart_control", self, false, "", nil)

        if smart_control_section ~= nil then
                self.base_on_actor_control = smart_terrain_control.CBaseOnActorControl(self, ini, smart_control_section)
        end
        self.respawn_point = false
        if respawn_params ~= nil then
                self:check_respawn_params(respawn_params)
        end


        if level.patrol_path_exists(self:name() .. "_traveller_actor") then
                printf("Smart_terrain [%s] has no traveller_actor path!!!!!", self:name())
                self.traveler_actor_path = self:name() .. "_traveller_actor"
        end

        if level.patrol_path_exists(self:name() .. "_traveller_squad") then
                printf("Smart_terrain [%s] has no traveller_squad path!!!!!", self:name())
                self.traveler_squad_path = self:name() .. "_traveller_squad"
        end

        if not locations_ini:section_exist(self:name()) then
                printf("! SMART_TERRAIN [%s] has no terrain_mask section in smart_terrain_masks.ltx!!!",self:name())
        end
end

--*******************************************************
-- МЕТОДЫ ДЛЯ РАБОТЫ С НПС
--*******************************************************
-- заполнить информацию о персонаже
-- у монстров нету метода profile_name()
function se_smart_terrain:fill_npc_info(obj)
        local npc_info = {}
        printf("filling npc_info for obj [%s]", tostring(obj:name()))

        local is_stalker = IsStalker(obj)
        npc_info.se_obj                 = obj
        npc_info.is_monster             = not is_stalker
        npc_info.need_job               = "nil" -- Специально для смены гвардов. Указывает на какую работу хочет данный чувак.
        npc_info.job_prior              = -1
        npc_info.job_id                 = -1
        npc_info.begin_job              = false

        if is_stalker then
                npc_info.stype = modules.stype_stalker
        else
                npc_info.stype = modules.stype_mobile
        end

        return npc_info
end

function se_smart_terrain:refresh_script_logic(obj_id)
        local object = alife():object(obj_id)
        local stype = modules.stype_mobile
        if IsStalker(object) then
                stype = modules.stype_stalker
        end
        xr_logic.initialize_obj(db.storage[object.id].object, db.storage[object.id], false, db.actor, stype)
end

-- добавить npc в smart terrain.
function se_smart_terrain:register_npc(obj,a)

        if self.b_registred == false then
                table.insert(self.npc_to_register, obj)
                return
        end

        if a == nil then
                a = "Not Defined!"
        end

        self.population = self.population + 1

        local obj_n=obj:name()

--      if string.find(self:name(), "zat_b104_zombied") then
                printfb("[smart_terrain %s] register called obj=%s , population:%s , cf_s_s: %s", self:name(), obj_n, tostring(self.population), tostring(a))
--      end

        db.stalker_smart[obj.id]=self.id

        -- Только для монстров, чтобы ходили по смартам.
        if not IsStalker(obj) then
                obj:smart_terrain_task_activate()
        end

        obj.m_smart_terrain_id = self.id
        if arrived_to_smart(obj, self) then
                printfb("npc_info!")
                self.npc_info[obj.id] = self:fill_npc_info(obj)

                -- Затычка на случай если мы регистримся в смарт, из которого только что сами вынесли всех врагов.
                self.dead_time = {}

                -- тут надо найти чуваку работу
                self:select_npc_job(self.npc_info[obj.id])
        else
                printfb("arriving!")
                self.arriving_npc[obj.id] = obj
        end
end
-- Регистрация НПС в список отложенных. Осуществляется на загрузке или на регистрации НПС, пока не зарегистрен смарт
function se_smart_terrain:register_delayed_npc()
        for k,v in pairs(self.npc_to_register) do
                self:register_npc(v,"added from smart_terrain")
        end
        self.npc_to_register = {}
end
-- отпустить npc
function se_smart_terrain:unregister_npc(obj,a,b)
        --callstack()

        if a == nil then
                a = "Not Defined!"
        end

        self.population = self.population - 1

        local obj_n=obj:name()

        db.stalker_smart[obj.id]=nil

--      if string.find(self:name(), "zat_b104_zombied") then
                printfb("[smart_terrain %s] unregister called obj=%s , population:%s , cf_s_s: %s", self:name(), obj_n, tostring(self.population), tostring(a))
--      end

        if self.npc_info[obj.id] ~= nil then
                -- TODO: Тут надо выгнать чувака с занимаемой им работы
                self.npc_info[obj.id].job_link.npc_id = nil
                self.npc_info[obj.id] = nil
                obj:clear_smart_terrain()

                if db.storage[obj.id] ~= nil then
                        local object = db.storage[obj.id].object
                        local stype = modules.stype_mobile
                        if IsStalker(obj) then
                                stype = modules.stype_stalker
                        end
                        xr_logic.initialize_obj(object, db.storage[obj.id], false, db.actor, stype)
                end
                return
        end
        if self.arriving_npc[obj.id] ~= nil then
                self.arriving_npc[obj.id] = nil
                obj:clear_smart_terrain()
                return
        end

        abort("self.npc_info[obj.id] = nil !!! obj.id=%d", obj.id)
end
-- Убрать убитого
function se_smart_terrain:clear_dead(obj,sid)
        printfb("sm:clear_dead: [%s][%s]",obj:name(),tostring(sid))
        self:unregister_npc(obj,"unregister from smart_terrain clear_dead")
end
-- выдать объекту задание.
function se_smart_terrain:task(obj)
        if self.arriving_npc[obj.id] ~= nil then
                return self.smart_alife_task
        end
        return self.job_data[self.npc_info[obj.id].job_id].alife_task
end


--*******************************************************
-- Функции для работы с работами
--*******************************************************
-- Загрузка работ (из gulag_general)
function se_smart_terrain:load_jobs()
        --printf("LOAD JOBS %s", self:name())
        -- Загружаем иерархию работ
        self.jobs = gulag_general.load_job(self)

        -- Загружаем ltx работ.
        self.ltx, self.ltx_name = xr_gulag.loadLtx(self:name())
        -- Сортируем всю иерархию по уменьшению приоритета
        -- Рекурсивная функция сортировки
        local function sort_jobs(jobs)
                for k,v in pairs(jobs) do
                        if v.jobs ~= nil then
                                sort_jobs(v.jobs)
                        end
                end
                table.sort(jobs, function(a,b) return a._prior > b._prior end )
        end

--      if self:name() == "jup_a10_smart_terrain" then
--              printf("before sort")
--              store_table(self.jobs)
--      end

        sort_jobs(self.jobs)

        --if self:name() == "jup_a10_smart_terrain" then
        --      printf("after sort")
        --      store_table(self.jobs)
        --end


        -- Надо сделать постобработку работ. Проинитить все неиниченные поля
        -- Для более быстрого доступа нужно вычленить параметры работ в отдельную таблицу вида:
        --self.job_data[job_id] = {}
        local id = 0
        self.job_data = {}
        local function get_jobs_data(jobs)
                for k,v in pairs(jobs) do
                        if v.jobs ~= nil then
                                get_jobs_data(v.jobs)
                        else
                                if v.job_id == nil then
                                        print_table(self.jobs)
                                        abort("Incorrect job table")
                                end
                                self.job_data[id] = v.job_id

                                self.job_data[id]._prior = v._prior -- Кешируем для проверки
                                v.job_id = id
                                id = id + 1
                        end
                end
        end

        get_jobs_data(self.jobs)
        -- Пробегаемся по работам и высчитываем для каждой работы alife_task
        for k,v in pairs(self.job_data) do
                local section = v.section
                local ltx = v.ini_file or self.ltx
                if not ltx:line_exist(section, "active") then
                        abort("gulag: ltx=%s  no 'active' in section %s", self.ltx_name, section)
                end
                local active_section = ltx:r_string(section, "active")


--              printf("job_type %s  job_section %s", tostring(v.job_type), tostring(section))
                -- В зависимости от типа работы по разному считаем alife_path
                if v.job_type == "path_job" then  -- работа задается патрульным путем
                        local path_field
                        for i,vv in pairs(path_fields) do
                                if ltx:line_exist(active_section, vv) then
                                        path_field = vv
                                        break
                                end
                        end

                        --printf("path_field %s  prefix_name %s active_section %s", tostring(path_field), tostring(v.prefix_name), tostring(active_section))
                        local path_name = ltx:r_string(active_section, path_field)
                        if v.prefix_name ~= nil then
                                path_name = v.prefix_name .. "_" .. path_name
                        else
                                path_name = self:name() .. "_" .. path_name
                        end

                        if path_field == "center_point" then --' TODO убрать затык когда переделаем кемпы на смарткаверы
                                if level.patrol_path_exists(path_name .. "_task") then
                                        path_name = path_name .. "_task"
                                end
                        end

                        v.alife_task = CALifeSmartTerrainTask(path_name)

                elseif v.job_type == "smartcover_job" then -- работа задается смарткавером
                        local smartcover_name = ltx:r_string(active_section, "cover_name")
                        local smartcover = se_smart_cover.registered_smartcovers[smartcover_name]
                        if smartcover == nil then
                                abort("There is an exclusive job with wrong smatrcover name [%s]    smartterrain [%s]", tostring(smartcover_name), self:name())
                        end
                        printf("Returning alife task for object [%s] game_vertex [%s] level_vertex [%s] position %s", smartcover.id, smartcover.m_game_vertex_id, smartcover.m_level_vertex_id, vec_to_str(smartcover.position))
                        v.alife_task = CALifeSmartTerrainTask(smartcover.m_game_vertex_id, smartcover.m_level_vertex_id)

                elseif v.job_type == "point_job" then   -- работа задается позицией
                        v.alife_task = self.smart_alife_task
                end

                v.game_vertex_id = v.alife_task:game_vertex_id()
                v.level_id               = game_graph():vertex(v.game_vertex_id):level_id()
                v.position       = v.alife_task:position()
        end
end

-- Апдейт работ смарттеррейна.
-- Если передается object, то значит нужно найти только для него
function se_smart_terrain:update_jobs()
        self:check_alarm()
        --printf("UPDATE JOBS %s", self:name())

        -- Проверяем, дошел ли кто-то до смарта
        for k,v in pairs(self.arriving_npc) do
                if arrived_to_smart(v, self) then
                        self.npc_info[v.id] = self:fill_npc_info(v)

                        -- Затычка на случай если мы регистримся в смарт, из которого только что сами вынесли всех врагов.
                        self.dead_time = {}

                        -- тут надо найти чуваку работу
                        self:select_npc_job(self.npc_info[v.id])

                        self.arriving_npc[k] = nil
                end
        end

        -- Сортируем НПС по увеличению приоритета занимаемой работы
        table.sort(self.npc_info, function(a,b) return a.job_prior < b.job_prior end )
        for k,v in pairs(self.npc_info) do
                self:select_npc_job(v)
        end
end
-- Выбор работы для персонажа
function se_smart_terrain:select_npc_job(npc_info)
        -- Выбираем работу
        local selected_job_id, selected_job_prior, selected_job_link = job_iterator(self.jobs, npc_info, 0, self)
        if selected_job_id == nil then
                print_table(self.jobs)
                abort("Insufficient smart_terrain jobs %s", self:name())
        end

        -- Назначаем работу
        if selected_job_id ~= npc_info.job_id and selected_job_link ~= nil then
                -- Установить себе выбранную работу
                --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)
                -- Если НПС был на работе - выгоняем его с нее.
                if npc_info.job_link ~= nil then
                        self.npc_by_job_section[self.job_data[npc_info.job_link.job_id].section] = nil
                        npc_info.job_link.npc_id = nil
                end

                selected_job_link.npc_id = npc_info.se_obj.id
                self.npc_by_job_section[self.job_data[selected_job_link.job_id].section] = selected_job_link.npc_id

                npc_info.job_id = selected_job_link.job_id
                npc_info.job_prior = selected_job_link._prior
                npc_info.begin_job = false
                -- сохраняем ссылку на работу, для облегчения удаления
                npc_info.job_link = selected_job_link

                -- завершаем текущую работу
                local obj_storage = db.storage[npc_info.se_obj.id]
                if obj_storage ~= nil then
                        xr_logic.switch_to_section(obj_storage.object, self.ltx, "nil")
                end
        end

        if npc_info.begin_job ~= true then
                -- Проверяем, дошел ли персонаж до работы (то есть может ли он начать ее выполнение)
                local job_data = self.job_data[npc_info.job_id]
                -- Начинаем выполнять работу
                printf("[smart_terrain %s] gulag: beginJob: obj=%s job= %s", self:name(), npc_info.se_obj:name(), job_data.section)
                -- Смена работы, очищаем память для оффлайнового обьекта.
                db.offline_objects[npc_info.se_obj.id] = {}
                npc_info.begin_job = true

                local obj_storage = db.storage[npc_info.se_obj.id]
                if obj_storage ~= nil then
                        self:setup_logic(obj_storage.object)
                end
        end
end
-- настроить логику для объекта, который в онлайне.
function se_smart_terrain:setup_logic(obj)
        --printf("setup npc logic %s", obj:name())
--      callstack()
        local npc_data = self.npc_info[obj:id()]
        local job = self.job_data[npc_data.job_id]
        local ltx = job.ini_file or self.ltx
        local ltx_name = job.ini_path or self.ltx_name

        xr_logic.configure_schemes(obj, ltx, ltx_name, npc_data.stype, job.section, job.prefix_name or self:name())

        local sect = xr_logic.determine_section_to_activate(obj, ltx, job.section, db.actor)
        if utils.get_scheme_by_section(job.section) == "nil" then
                abort("[smart_terrain %s] section=%s, don't use section 'nil'!", self:name(), sect)
        end

        xr_logic.activate_by_section(obj, ltx, sect, job.prefix_name or self:name(), false)
end
-- получить работу, которую занимает объект
function se_smart_terrain:getJob(obj_id)
        return self.npc_info[obj_id] and self.job_data[self.npc_info[obj_id].job_id]
end
-- Получение персонажа, который занимает указанную работу.
function se_smart_terrain:idNPCOnJob(job_name)
        return self.npc_by_job_section[job_name]
end
function se_smart_terrain:switch_to_desired_job(npc)
        -- Берем текущую работу НПС
        local npc_id = npc:id()
        local npc_info = self.npc_info[npc_id]

        --printf("***** %s -> %s", npc:name(), tostring(npc_info.need_job))
        local changing_npc_id = self.npc_by_job_section[npc_info.need_job]
        --printf("changing_npc_id %s", tostring(changing_npc_id))

        if changing_npc_id == nil then
                -- Мы не нашли с кем меняться, просто ресетим себя
                self.npc_info[npc_id].job_link = nil
                self.npc_info[npc_id].job_id = -1
                self.npc_info[npc_id].job_prior = -1
                self:select_npc_job(self.npc_info[npc_id])

                --print_table(self.npc_by_job_section)
                --abort("ERROR during channging NPC")
                return
        end

        if self.npc_info[changing_npc_id] == nil then
                -- Мы не нашли с кем меняться, просто ресетим себя
                self.npc_info[npc_id].job_link = nil
                self.npc_info[npc_id].job_id = -1
                self.npc_info[npc_id].job_prior = -1
                self:select_npc_job(self.npc_info[npc_id])

                --print_table(self.npc_by_job_section)
                --abort("ERROR during channging NPC")
                return
        end

        local desired_job = self.npc_info[changing_npc_id].job_id

        -- Переключаем НПС на желаемую работу
        if npc_info.job_link ~= nil then
                self.npc_by_job_section[self.job_data[npc_info.job_link.job_id].section] = nil
                npc_info.job_link.npc_id = nil
        end

        local selected_job_link = self.npc_info[changing_npc_id].job_link

        selected_job_link.npc_id = npc_info.se_obj.id

        self.npc_by_job_section[self.job_data[selected_job_link.job_id].section] = selected_job_link.npc_id

        npc_info.job_id = selected_job_link.job_id
        npc_info.job_prior = selected_job_link._prior
        npc_info.begin_job = true

        -- сохраняем ссылку на работу, для облегчения удаления
        npc_info.job_link = selected_job_link
        npc_info.need_job = "nil"

        local obj_storage = db.storage[npc_id]
        if obj_storage ~= nil then
                self:setup_logic(obj_storage.object)
        end

        -- Освобождаем НПС, который занимает желаемую работу и говорим ему перевыбрать работу
        self.npc_info[changing_npc_id].job_link = nil
        self.npc_info[changing_npc_id].job_id = -1
        self.npc_info[changing_npc_id].job_prior = -1
        self:select_npc_job(self.npc_info[changing_npc_id])
end


--*******************************************************
-- СЕЙВ/ЛОАД
--*******************************************************
-- сохранение
function se_smart_terrain:STATE_Write(packet)
        cse_alife_smart_zone.STATE_Write(self, packet)

        set_save_marker(packet, "save", false, "se_smart_terrain")

        -- Информацию о НПС, идущих в смарт
        local n = 0
        for k,v in pairs(self.arriving_npc) do
                n = n + 1
        end
        packet:w_u8(n)
        for k,v in pairs(self.arriving_npc) do
                packet:w_u16(k)
        end


        -- Информацию о НПС в смарте
        n = 0
        for k,v in pairs(self.npc_info) do
                n = n + 1
        end

        packet:w_u8(n)
        for k,v in pairs(self.npc_info) do
                packet:w_u16(k)
                packet:w_u8(v.job_prior)
                packet:w_u8(v.job_id)
                packet:w_bool(v.begin_job)
                packet:w_stringZ(v.need_job)
        end

        n = 0
        for k,v in pairs(self.dead_time) do
                n = n + 1
        end
        packet:w_u8(n)
        for k,v in pairs(self.dead_time) do
                packet:w_u8(k)
                utils.w_CTime(packet, v)
        end

        if self.base_on_actor_control ~= nil then
                packet:w_bool(true)
                self.base_on_actor_control:save(packet)
        else
                packet:w_bool(false)
        end

        if self.respawn_point then
                packet:w_bool(true)
                local n = 0
                for k,v in pairs(self.already_spawned) do
                        n = n + 1
                end
                packet:w_u8(n)
                for k,v in pairs(self.already_spawned) do
                        packet:w_stringZ(k)
                        packet:w_u8(v.num)
                end

                if self.last_respawn_update ~= nil then
                        packet:w_bool(true)
                        utils.w_CTime(packet, self.last_respawn_update)
                else
                        packet:w_bool(false)
                end
        else
                packet:w_bool(false)
        end

        if self.population < 0 then
                abort("Smart_terrain [%s] population can't be less than zero!!!", self:name())
        end
        packet:w_u8(self.population)

        set_save_marker(packet, "save", true, "se_smart_terrain")
end

-- восстановление
function se_smart_terrain:STATE_Read(packet, size)
        cse_alife_smart_zone.STATE_Read(self, packet, size)

        -- под LevelEditor не пытаться читать из пакета ничего
        if editor() then
                return
        end

        set_save_marker(packet, "load", false, "se_smart_terrain")
        self:read_params()

        -- Информацию о НПС, идущих в смарт
        local n = packet:r_u8()
        self.arriving_npc = {}
        for i = 1,n do
                local id = packet:r_u16()
                self.arriving_npc[id] = false
        end

        -- Информацию о НПС в смарте
        n = packet:r_u8()
        --printf("load %s npc", tostring(n))
        self.npc_info = {}
        for i = 1,n do
                local id = packet:r_u16()
                --printf("__ id %s", tostring(id))
                self.npc_info[id] = {}
                local npc_info = self.npc_info[id]
                npc_info.job_prior = packet:r_u8()
                --printf("__ job_prior %s", tostring(npc_info.job_prior))
                if npc_info.job_prior == 255 then
                        npc_info.job_prior = -1
                end
                npc_info.job_id = packet:r_u8()
                --printf("__ job_id %s", tostring(npc_info.job_id))
                if npc_info.job_id == 255 then
                        npc_info.job_id = -1
                end
                npc_info.begin_job = packet:r_bool()
                --printf("__ begin_job %s", tostring(npc_info.begin_job))
                npc_info.need_job = packet:r_stringZ()
        end

        n = packet:r_u8()
        self.dead_time = {}
        --printf("load %s dead_time", tostring(n))
        for i =1,n do
                local job_id = packet:r_u8()
                --printf("__ job_id %s", tostring(job_id))
                local dead_time = utils.r_CTime(packet)
                self.dead_time[job_id] = dead_time
        end

        self.need_init_npc = true

        if self.script_version > 9 then
                if packet:r_bool() == true then
                        --self.base_on_actor_control
                        self.base_on_actor_control:load(packet)
                end
        end

        local respawn_point = packet:r_bool()
        --printf("LOAD RESPAWN %s", self:name())
        if respawn_point then
                n = packet:r_u8()
                for i = 1, n do
                        local id = packet:r_stringZ()
                        local num = packet:r_u8()
                        self.already_spawned[id].num = num
                end

                if self.script_version > 11 then
                        local exist = packet:r_bool()
                        if exist then
                                self.last_respawn_update = utils.r_CTime(packet)
                        else
                                self.last_respawn_update = nil
                        end
                end
        end

        self.population = packet:r_u8()

        set_save_marker(packet, "load", true, "se_smart_terrain")
end
-- Инициализация НПС после загрузки.
function se_smart_terrain:init_npc_after_load()
        local function find_job(jobs, npc_info)
                for k,v in pairs(jobs) do
                        if v.jobs ~= nil then
                                find_job(v.jobs, npc_info)
                        else
                                if v.job_id == npc_info.job_id then
                                        npc_info.job_link = v
                                        v.npc_id = npc_info.se_obj.id
                                        return
                                end
                        end
                end
        end


        local sim = alife()
        --printf("[%s] init_npc_after_load", self:name())
        for k,v in pairs(self.arriving_npc) do
                local sobj = sim:object(k)
                if sobj ~= nil then
                        self.arriving_npc[k] = sobj
                else
                        self.arriving_npc[k] = nil
                end
        end

        for k,v in pairs(self.npc_info) do
                local sobj = sim:object(k)
                if sobj ~= nil then
                        local npc_info = self:fill_npc_info(sobj)

                        npc_info.job_prior = v.job_prior
                        npc_info.job_id = v.job_id
                        npc_info.begin_job = v.begin_job
                        npc_info.need_job = v.need_job

                        --Теперь надо найти данную работу и выставить ссылку на нее.
                        find_job(self.jobs, npc_info)

                        self.npc_info[k] = npc_info
                        if npc_info.job_link ~= nil then
                                self.npc_by_job_section[self.job_data[npc_info.job_link.job_id].section] = k
                        end
                else
                        self.npc_info[k] = nil
                end
        end
end

--' Возвращает отформатированную строку свойств смарта
function se_smart_terrain:get_smart_props()
        local props = smart_names.get_smart_terrain_name(self)
        if(props==nil) or (sm_dev_debug) then
                props = self:name().."  ["..self.id.."]\\n"..self.sim_type.."\\n".."squad_id = "..tostring(self.id).."\\n".."capacity = "..tostring(self.max_population).." ("..sim_board.get_sim_board():get_smart_population(self)..")\\n"

                if self.respawn_point ~= nil and self.already_spawned ~= nil then
                        props = props.."\\nalready_spawned :\n"
                        for k,v in pairs(self.already_spawned) do
                                props = try_prt(props).."["..try_prt(k).."] = "..try_prt(v.num).."("..try_prt(xr_logic.pick_section_from_condlist(db.actor, nil,self.respawn_params[k].num))..")\\n"
                        end
                        if self.last_respawn_update then
                                props = try_prt(props).."\\ntime_to_spawn:"..try_prt(tostring(RESPAWN_IDLE - game.get_game_time():diffSec(self.last_respawn_update))).."\\n"
                        end
                        props = try_prt(props).."\\npopulation:"..try_prt(tostring(self.population)).."\\n"
                end

                --' Добавляем информацию о находящихся в смарте отрядах
                for k,v in pairs(sim_board.get_sim_board().smarts[self.id].squads) do
                        props = try_prt(props)..try_prt(tostring(v.id)).."\\n"
                end
        end
        return props
end



--' Отрисовка смарта на игровом поле
function se_smart_terrain:show()
        local time = time_global()
        if(self.showtime~=nil) and (self.showtime+200>=time) then
                return
        end
        self.showtime = time

        local player = self.player_name
        local spot = "neutral"

        if self.sim_avail == nil or xr_logic.pick_section_from_condlist(db.actor or alife():actor(), self, self.sim_avail) == "true" then
                spot = "friend"
        else
                spot = "enemy"
        end


        if(self.smrt_showed_spot==spot) then
                level.map_change_spot_hint(self.id, "alife_presentation_smart_"..self.sim_type.."_"..self.smrt_showed_spot, self:get_smart_props())
                return
        end

        if(sm_dev_debug) then
                if(self.smrt_showed_spot~=nil) then
                        level.map_remove_object_spot(self.id, "alife_presentation_smart_"..self.sim_type.."_"..self.smrt_showed_spot)
                end
                level.map_add_object_spot(self.id, "alife_presentation_smart_"..self.sim_type.."_"..spot, self:get_smart_props())
                self.smrt_showed_spot = spot
        else
                if(self.smrt_showed_spot~=nil) and
                        (level.map_has_object_spot(self.id, "alife_presentation_smart_"..self.sim_type.."_"..self.smrt_showed_spot)~=0)
                then
                        level.map_remove_object_spot(self.id, "alife_presentation_smart_base_"..self.smrt_showed_spot)
                end
        end
end
--' Обновление информации о смарте на игровом поле
function se_smart_terrain:refresh()
        self:show()
end
--' Убирание отрисовки смарта на игровом поле
function se_smart_terrain:hide()
        if self.smrt_showed_spot == nil then
                return
        end
        level.map_remove_object_spot(self.id, "alife_presentation_smart_"..self.sim_type.."_"..self.smrt_showed_spot)
end


local function is_only_monsters_on_jobs(npc_info)
        for k,v in pairs (npc_info) do
                if v.is_monster == false then
                        return false
                end
        end
        return true
end

-- Обновление.
-- В онлайне вызывается через binder.
-- Также может вызваться принудительно из xr_effects
function se_smart_terrain:update()
        cse_alife_smart_zone.update( self )
        if sm_dev_debug then
                self:refresh() -- Не забыть потом заремить
        end

        local current_time = time_global()

        if simulation_objects.is_on_the_same_level(self, alife():actor()) then
                local dist_to_actor = self.position:distance_to(alife():actor().position)
                local old_dist_to_actor = (nearest_to_actor_smart.id == nil and nearest_to_actor_smart.dist) or alife():object(nearest_to_actor_smart.id).position:distance_to(alife():actor().position)
                if dist_to_actor < old_dist_to_actor then
                        nearest_to_actor_smart.id = self.id
                        nearest_to_actor_smart.dist = dist_to_actor
                end
        end

        -- Апдейт респауна отрядов симуляции.
        if self.respawn_params ~= nil then
                self:try_respawn()
        end

        if self.check_time~=nil and current_time < self.check_time then
                return
        end

        --проверить есть ли кто-то в смарте, если есть и костры не включены то включить,
        --еще проверить есть ли актер, чтоб была гарантия что костры проспонились...
        if is_only_monsters_on_jobs(self.npc_info) and self.campfires_on then
                bind_campfire.turn_off_campfires_by_smart_name(self:name())
                self.campfires_on = false
        elseif not is_only_monsters_on_jobs(self.npc_info) and not self.campfires_on then
                bind_campfire.turn_on_campfires_by_smart_name(self:name())
                self.campfires_on = true
        end

        if db.actor ~= nil then
                local distance = db.actor:position():distance_to_sqr(self.position)
                local idle_time = math.max(60, 0.003 * distance)
                self.check_time = current_time + idle_time
        else
                self.check_time = current_time + 10
        end

        -- Проверяем, не истек ли запрет на занимание работы, на которой убили НПС
        local current_time = game.get_game_time()
        for k,v in pairs(self.dead_time) do
                if current_time:diffSec(v) >= DEATH_IDLE_TIME then
                        self.dead_time[k] = nil
                end
        end

        -- Перевыбор работ
        self:update_jobs()

        -- Апдейтим контрол реакции базы на игрока
        if self.base_on_actor_control ~= nil then
                self.base_on_actor_control:update()
        end
        -- Апдейт доступности для симуляции.
        simulation_objects.get_sim_obj_registry():update_avaliability(self)
end
-- Переведение смарта в напряженное состояние
function se_smart_terrain:set_alarm()
        self.smart_alarm_time = game.get_game_time()
end
-- Проверяет. а не прошел ли аларм в смарте
function se_smart_terrain:check_alarm()
        if self.smart_alarm_time == nil then
                return
        end
        if game.get_game_time():diffSec(self.smart_alarm_time) > 21600 then -- 6 Игровых часов
                self.smart_alarm_time = nil
        end
end


-- установить логику и сообщить смарту, что объект перешёл в онлайн.
-- вызывается из net_spawn() объектов
function setup_gulag_and_logic_on_spawn(obj, st, sobject, stype, loaded)
        local sim = alife()
        local sobject = alife():object(obj:id())
        if sim ~= nil and sobject then
                local strn_id = sobject.m_smart_terrain_id
                printf( "setup_gulag_and_logic_on_spawn obj=%s, strn_id=%s, loaded=%s", obj:name(), tostring(strn_id), tostring(loaded))

                if strn_id ~= nil and strn_id ~= 65535 then
                        local strn                   = sim:object(strn_id)
                        local need_setup_logic       = (not loaded) and (strn.npc_info[obj:id()] and strn.npc_info[obj:id()].begin_job == true)

                        if need_setup_logic then
                                strn:setup_logic(obj)
                        else
                                xr_logic.initialize_obj(obj, st, loaded, db.actor, stype)
                        end
                else
                        xr_logic.initialize_obj(obj, st, loaded, db.actor, stype)
                end
        else
                xr_logic.initialize_obj(obj, st, loaded, db.actor, stype)
        end
end

-- Убираем объект из смарта при смерти
function on_death(obj)
        local sim = alife()
        if sim then
                local obj = sim:object(obj.id)
                if obj == nil then return end
                local strn_id = obj:smart_terrain_id()
                if strn_id == 65535 then
                        strn_id=db.stalker_smart[obj.id]
                end
                printfb("sm.on_death [%s][%s]",obj:name(),tostring(strn_id))
                if strn_id ~= 65535 and strn_id ~= nil then
                        sim:object(strn_id):clear_dead(obj,strn_id)
                end
        end
end


--***********************************************************************************************
--*                                                                             SIMULATION_TARGET_SMART                                                                 *
--***********************************************************************************************
-- Получить позицию, левел вертекс, гейм вертекс обьекта.
function se_smart_terrain:get_location()
        return self.position, self.m_level_vertex_id, self.m_game_vertex_id
end

-- Достигнут ли я отрядом выбравшим меня как цель.
function se_smart_terrain:am_i_reached(squad)
        local squad_pos, squad_lv_id, squad_gv_id = squad:get_location()
        local target_pos, target_lv_id, target_gv_id = self:get_location()
        if game_graph():vertex(squad_gv_id):level_id() ~= game_graph():vertex(target_gv_id):level_id() then
                return false
        end
        if IsMonster(alife():object(squad:commander_id())) and squad:get_script_target() == nil then
                return squad_pos:distance_to_sqr(target_pos) <= 25
        end
        return squad.always_arrived or squad_pos:distance_to_sqr(target_pos) <= self.arrive_dist^2
end

-- Вызывается 1 раз после достижения меня отрядом выбравшим меня как цель.
function se_smart_terrain:on_after_reach(squad)
        for k in squad:squad_members() do
                local obj = k.object
                squad.board:setup_squad_and_group(obj)
        end
        squad.current_target_id = self.id
end

-- Вызывается 1 раз в момент выбора меня как цели.
function se_smart_terrain:on_reach_target(squad)
--      squad.sound_manager:set_storyteller(squad:commander_id())
--      squad.sound_manager:set_story("squad_begin_attack")
        squad:set_location_types(self:name())
        self.board:assign_squad_to_smart(squad, self.id)
        for k in squad:squad_members() do
                if db.offline_objects[k.id] ~= nil then
                        db.offline_objects[k.id] = {}
                end
        end
--      self.board:exit_smart(squad, squad.smart_id)
end

-- Возвращает CALifeSmartTerrainTask на меня, вызывается из smart_terrain:task()
function se_smart_terrain:get_alife_task()
        return self.smart_alife_task
end

function smart_terrain_squad_count(board_smart_squads)
        local count = 0
        for k,v in pairs(board_smart_squads) do
                if v:get_script_target() == nil then
                        count = count + 1
                end
        end
        return count
end

function se_smart_terrain:sim_available()
        if self.base_on_actor_control ~= nil and self.base_on_actor_control.status ~= smart_terrain_control.NORMAL then
                return false
        end
        return true
end

local is_squad_monster =
{
        ["monster_predatory_day"] = true,
        ["monster_predatory_night"] = true,
        ["monster_vegetarian"] = true,
        ["monster_zombied_day"] = true,
        ["monster_zombied_night"] = true,
        ["monster_special"] = true
}
function surge_stats()
        local sim_obj_registry = simulation_objects.get_sim_obj_registry().objects
        local sim_squads = {
                ["zaton"] = {},
                ["jupiter"] = {},
                ["pripyat"] = {}
        }
        local sim_smarts = {
                ["zaton"] = {},
                ["jupiter"] = {},
                ["pripyat"] = {}               
        }
        for k,v in pairs(sim_obj_registry) do
                if v:clsid() == clsid.smart_terrain and tonumber(v.props["surge"]) > 0 then
                        local level_name = alife():level_name(game_graph():vertex(v.m_game_vertex_id):level_id())
                        if sim_smarts[level_name] ~= nil then
                                table.insert(sim_smarts[level_name], v)
                        end
                end
                if v:clsid() == clsid.online_offline_group_s then
                        local squad_params = sim_board.simulation_activities[v.player_id]
                        if squad_params ~= nil then
                                local smart_params = squad_params.smart.surge
                                if smart_params ~= nil then
                                        local level_name = alife():level_name(game_graph():vertex(v.m_game_vertex_id):level_id())
                                        if sim_squads[level_name] ~= nil then
                                                table.insert(sim_squads[level_name], v)
                                        end
                                end
                        end
                end
        end
        local function print_smarts_and_squads_by_level(level_name)
                printf("LEVEL: [%s]", level_name)
                local max_capacity_total = 0
                for i = 1, #sim_smarts[level_name] do
                        local smart = sim_smarts[level_name][i]
                        max_capacity_total = max_capacity_total + smart.max_population
                        local squad_count = smart_terrain_squad_count(sim_board.get_sim_board().smarts[smart.id].squads)
                        printf("smart: [%s] max_population [%d] squad_count [%d]", smart:name(),smart.max_population, squad_count)
                end
                printf("TOTAL: capacity total : [%d] squads total [%d]" , max_capacity_total, #sim_squads[level_name])
        end
        print_smarts_and_squads_by_level("zaton")
        print_smarts_and_squads_by_level("jupiter")
        print_smarts_and_squads_by_level("pripyat")
end
-- Мой прекондишн.
function se_smart_terrain:target_precondition(squad, need_to_dec_population)
        if self.respawn_only_smart == true then
                return false
        end

        local squad_count = smart_terrain_squad_count(self.board.smarts[self.id].squads)
        if need_to_dec_population then
                squad_count = squad_count - 1
        end
        if squad_count ~= nil and (self.max_population <= squad_count) then
                --printf("smart terrain [%s] precondition returns false for squad [%s]", self:name(), squad:name())
--              if tonumber(self.props["surge"]) > 0 and xr_conditions.surge_started() then
--                      printf("SURGE_SMART_STATS : smart [%s]\n max_population = %d \ squad_count = %d", self:name(), self.max_population, squad_count)
--              end
                return false
        end

        local squad_params = sim_board.simulation_activities[squad.player_id]
        if squad_params == nil or squad_params.smart == nil then
                --printf("smart terrain [%s] precondition returns false for squad [%s]", self:name(), squad:name())
                return false
        end

        if tonumber(self.props["resource"] )> 0 then
                local smart_params = squad_params.smart.resource
                if smart_params ~= nil and smart_params.prec(squad, self) then
                        return true
                end
        end
        if tonumber(self.props["base"] )> 0 then
                local smart_params = squad_params.smart.base
                if smart_params ~= nil and smart_params.prec(squad, self) then
                        return true
                end
        end
        if tonumber(self.props["lair"] )> 0 then
                local smart_params = squad_params.smart.lair
                if smart_params ~= nil and smart_params.prec(squad, self) then
                        return true
                end
        end
        if tonumber(self.props["territory"] )> 0 then
                local smart_params = squad_params.smart.territory
                if smart_params ~= nil and smart_params.prec(squad, self) then
                        return true
                end
        end
        if tonumber(self.props["surge"] )> 0 then
                local smart_params = squad_params.smart.surge
                if smart_params ~= nil and smart_params.prec(squad, self) then
                        return true
                end
        end
        --printf("smart terrain [%s] precondition returns false for squad [%s]", self:name(), squad:name())
        return false


--[[
        local squad_count = smart_terrain_squad_count(self.board.smarts[self.id].squads)
        if squad_count ~= nil and (self.max_population <= squad_count) then return false end


        if squad.player_id == "stalker" and in_time_interval(9,19) and tonumber(self.props["resource"] )> 0 then
                return true
        end
        --if squad.player_id ~= "monster_predatory" and squad.player_id ~= "monster_vegetarian" then

        if not is_squad_monster[squad.player_id] then
                if tonumber(self.props["base"]) > 0 and in_time_interval(20,8) then
                        return true
                end
                if tonumber(self.props["base"] ) > 0 and xr_conditions.surge_started() then
                        return true
                end
        else
                if tonumber(self.props["lair"] ) > 0 and xr_conditions.surge_started() then
                        return true
                end
                if tonumber(self.props["lair"] ) > 0 and in_time_interval(7,20) then
                        return true
                end
        end
        return false
]]

end

-- Посчитать мой приоритет для отряда.
function se_smart_terrain:evaluate_prior(squad)
        return simulation_objects.evaluate_prior(self, squad)
end

-- Респаун симуляции.

function se_smart_terrain:check_respawn_params(respawn_params)
        --printf("CHECK RESPAWN PARAMS %s", self:name())
        self.respawn_params     = {}
        self.already_spawned    = {}
        self.respawn_point              = true
        if not self.ini:section_exist(respawn_params) then
                abort("Wrong smatr_terrain respawn_params section [%s](there is no section)", respawn_params)
        end
        local n = self.ini:line_count(respawn_params)
        if n == 0 then
                abort("Wrong smatr_terrain respawn_params section [%s](empty params)", respawn_params)
        end
        for j=0,n-1 do
                local result, prop_name, prop_condlist = self.ini:r_line(respawn_params,j,"","")
                if not self.ini:section_exist(prop_name) then
                        abort("Wrong smatr_terrain respawn_params section [%s] prop [%s](there is no section)", respawn_params, prop_name)
                end
                local spawn_squads = utils.cfg_get_string(self.ini, prop_name, "spawn_squads",  self, false, "", nil)
                local spawn_num         = utils.cfg_get_string(self.ini, prop_name, "spawn_num",        self, false, "", nil)
                if spawn_squads == nil then
                        abort("Wrong smatr_terrain respawn_params section [%s] prop [%s] line [spawn_squads](there is no line)", respawn_params, prop_name)
                elseif spawn_num == nil then
                        abort("Wrong smatr_terrain respawn_params section [%s] prop [%s] line [spawn_num](there is no line)", respawn_params, prop_name)
                end
                spawn_squads = utils.parse_names(spawn_squads)
                spawn_num = xr_logic.parse_condlist(nil, prop_name, "spawn_num", spawn_num)
                self.respawn_params[prop_name]                  = {}
                self.already_spawned[prop_name]                 = {}
                self.respawn_params[prop_name].squads   = spawn_squads
                self.respawn_params[prop_name].num              = spawn_num
                self.already_spawned[prop_name].num             = 0
        end
end

function se_smart_terrain:call_respawn()
        local available_sects = {}
        printf("respawn called from smart_terrain [%s]", self:name())
        for k,v in pairs(self.respawn_params) do
                if tonumber(xr_logic.pick_section_from_condlist(db.actor, nil,v.num)) > self.already_spawned[k].num then
                        table.insert(available_sects,k)
                end
        end
        if #available_sects > 0 then
                local sect_to_spawn = available_sects[math.random(1,#available_sects)]
                local sect_to_spawn_params = self.respawn_params[sect_to_spawn]
                local squad = sect_to_spawn_params.squads[math.random(1,#sect_to_spawn_params.squads)]
                squad = self.board:create_squad(self, squad)
                squad.respawn_point_id = self.id
                squad.respawn_point_prop_section = sect_to_spawn
                self.board:enter_smart(squad, self.id)
                for m in squad:squad_members() do
                        self.board:setup_squad_and_group(m.object)
                end
                self.already_spawned[sect_to_spawn].num = self.already_spawned[sect_to_spawn].num + 1
        end
end

function se_smart_terrain:try_respawn()
        --printf("TRY RESPAWN %s", self:name())
        local curr_time = game.get_game_time()
        if self.last_respawn_update == nil or curr_time:diffSec(self.last_respawn_update) > RESPAWN_IDLE then
                self.last_respawn_update = curr_time

                if self.sim_avail ~= nil and xr_logic.pick_section_from_condlist(db.actor or alife():actor(), self, self.sim_avail) ~= "true" then return end

                local squad_count = smart_terrain_squad_count(self.board.smarts[self.id].squads)
                if self.max_population <= squad_count then printf("%s cannot respawn due to squad_count %s of %s", self:name(), self.max_population, squad_count) return end

                local dist_to_actor = alife():actor().position:distance_to_sqr(self.position)
                if dist_to_actor < RESPAWN_RADIUS^2 then printf("%s cannot respawn due to distance", self:name()) return end

                self:call_respawn()
        end
end
 

jakby coś nie działało ponieważ włączone jest debugowanie smart-ów
to służę pomocą ;)
Awatar użytkownika
max1071
Stalker

Posty: 135
Dołączenie: 04 Gru 2010, 13:07
Ostatnio był: 09 Cze 2023, 20:21
Frakcja: Wolność
Ulubiona broń: TRs 301
Kozaki: 24

Reklamy Google

Powróć do Zew Prypeci

Kto jest na forum

Użytkownicy przeglądający to forum: Brak zarejestrowanych użytkowników oraz 1 gość