Перейти к контенту
Rolan

[CoP] Вопросы по SDK

Рекомендуемые сообщения

"Молоко" будет всегда, если на драфте компилить. На максе если компилить, то надо будет маску террейна делать, а то на динамике тоже будет "молоко".

Суффикс _S скорее всего означает "shape". Не думаю, что это сильно важно.

Wo1fRam.gif
Мой канал на YouTube

Бессмысленно осмысливать смысл неосмысленными мыслями.

Ссылка на комментарий
......с добавлением "_S"
 

У терейна локации в состав входят несколько элементов - трава, земля, асфальт, песок и т.п. К каждому элементу присваевается одна и та же текстура терейна, так вот - во многих случиях при экспорте в СДК остаётся одна текстура у терейна, остальные "сливаются" в неё так как у элементов имя текстуры одинакокое. В таком случае мы не сможем в СДК каждому элементу присвоить свой шейдер так как у нас осталась одна  присвоенная текстура.

Для этого и применяют разные имена материалов чтоб не потерять разграничение элементов в едином терейне. Не обязательно писать приставку "_S" , можно просто каждому элементу дать своё имя: grass, grunt, asfalt, sand и т.д.

Genior.gif

button.png

Ссылка на комментарий

О! Алилуйя! Скомпилированный на максимуме уровень запустился!!! Скопировал ВСЕ текстуры папки terrain с gamedat'ы из SDK в папку с игрой. НО... текстур в игре так и не появилось, более того - вода стала черной. Ну - хоть какое-то продвижение.

Обязательна ли маска если на уровне у меня нет травы? Я маску не применял.

Второй вопрос: Какое значение должно быть у параметра Base в ImageEditor когда я импортирую в SDK текстуру терреина? Я выставил так:

Type - Terrain

Alpha - present

Format - DXT1

Base - Blin <-> Phong (еще есть OrenNayar <-> Blin, Phong <-> Metal, Metal <-> OrenNayar - какой ставить? Хотя последние два на вряд ли)

Так же есть вкладка Details, в котрой есть параметр Texture в котором у меня ничего не присвоено. Может что в настройках здесь напутал?

1847776.jpg

Изменено пользователем Jeka81

С#Н#Т#Р# (CoP 1.6.02)

Ссылка на комментарий

Здравствуйте.

Проблема с Аивраппером: при запуске батника с содержимым

 

aiwrapper -draft -f agroprom


pause

 

 

идёт запуск xrAI, затем, мгновенно, происходит вылет. Вот информация из ЛОГа:

 

 

log started (console and aiwrapper.log)
loading aiwrapper.ini
updating $2215_game_levels$\agroprom\build.aimap
updating $2215_game_levels$\agroprom\build.cform
updating $2215_game_levels$\agroprom\build.prj
launching 'xrAI.exe -draft -f agroprom'
updating $game_levels$\agroprom\level.ai

 

 

Что ему не нравится?

P.S. Сборка локации происходила на СДК ЗП и скомпилирована на драфте.

Работы на Artstationhttps://www.artstation.com/artist/stalker_lom

Ссылка на комментарий

А при чём тут аивраппер? О_о.

Или я чего-то не понимаю? Это же не относится к СоР, он же для Теней.

Порядок запуска батников из папки \X-Ray CoP SDK\editors: tool_compile_xrLC_all, tool_compile_xrAI_draft, tool_create_spawn.

Вместо <%1> название локации.

  • Нравится 1

Обещаю, что никого в ответ минусить не стану) Чмоки)))

Ссылка на комментарий

Третью неделю бьюсь об одно и то-же - не могу запустить свою локацию (ровная плоскость 50*50 м). Уже в 3d max'e сделал новую - экспортировал в object. Все прошло успешно, в LevelEditor сцена создана, актер поставлен, граф-поинты расставлены (2 шт), свет, дефолтный сектор. Если раньше компиляция на максимуме проходила нормально, но сама игра не запускалась, то на этот раз уже сам компилятор вылетает с таким логом:

FATAL ERROR:

 Expression    : assertion failed
 Function      : CBuild::PreOptimize
 File          : D:\prog_repository\sources\trunk\utils\xrLC\xrPreOptimize.cpp
 Line          : 79
 Description   : ix<=HDIM_X && iy<=HDIM_Y && iz<=HDIM_Z

 

 

Что это значит?

С#Н#Т#Р# (CoP 1.6.02)

Ссылка на комментарий

Если у тебя просто ровная плоскость то ошибка скорее всего из-за этого. Попробуй изменить немного геометрию.

Ссылка на комментарий

Здравствуйте, снова с вопросами к вам...

Собрал таки локацию в первый раз - красота, хоть и на драфте, но пришла в голову мысль облагородить статичные модели (я над Агропромом колдую: вот помните в ТЧ у НИИ вертолет? Я решил сделать так, чтобы можно было залазить в "пассажирский вагон" и по аналогии так ещё с некоторыми моделями) так меня насторожило то, что СДК отказывается делать для этих моделей ЛОДы (а модели будут по полигональности несколько богаче нежели "старые" модели), ну, покумекал и решил снова собирать так как есть. И получилось, возле НИИ вертолет как ему и положено лежит, однако в озерце над водою застыли в позах неудачливых водителей "буханки" пара трупов вояк... Собственно, новая модель "Жигули" тоже исчезла. Из за чего СДК "съел" модели? Подозрения у меня есть:

 

Вот этот кусочек меня насторожил, может это из за этого, однако же "Жигули" делалась на основе модели от GSC и полигонаж у неё меньше (двери, капот и сиденья отсутствуют)...

...
* New phase started: Merging geometry...
| Processing...
| | ! ERROR: subdiv #98318 has more than 4096 faces (6739)
| | 2254 subdivisions.
| | ! ERROR: subdiv #2253 has more than 4096 faces (6739)

 

 

И ещё вопрос такого плана: я уже давно подключил Кордон из ТЧ, но сейчас более-менее научился работать с СДК и хочу:

а) заменить террейн на версию ЧН и "посадить на этот террейн несколько новых объектов и просчитать АИ-сетку.

б) в некоторых местах сделать мелкую корректировку геометрии (сделать дырку в колючей проволоке, укрепить блок-пост и т.п.) и соответственно переделать АИ-сетку.

В каком случае после компиляции Кордон будет играбелен без переделки all.spawn (ибо у меня там множества точек путей квестовым NPC налажено, много будет переделывать в плане пересчёта level_vertex ) и вообще, будет ли он играбелен?

 

Ну, и последний вопрос: как высчитываются level_vertex и game_vertex ? По Террейну?

 

Заранее спасибо.

Работы на Artstationhttps://www.artstation.com/artist/stalker_lom

Ссылка на комментарий

Здравствуйте,тут такой вопрос.

Я сделал симмуляционный сквад.Появился вылет.

Expression : !m_error_code


Function : raii_guard::~raii_guard
File : D:\prog_repository\sources\trunk\xrServerEntities\script_storage.cpp
Line : 748
Description : ... - Зов Припяти\gamedata\scripts\smart_terrain.script:1256: attempt to compare nil with number

 

Вообще со сквадами у меня все хорошо,единственная проблема в симмуляционных сквадах.Уже как месяц пытаюсь разобраться.Вот и пришел к вам.

Вот что я делал.

simulation прописал вот что:

esc_stalker_base_default3_squad = esc_stalker_base
esc_stalker_base_default4_squad = esc_stalker_base

 

 

[esc_stalker_base]:default_base - база с которой 2 дефолтовых сквада должны идти.
sim_avail = true

[esc_stalker_base_default3_squad]:default_squad - 1-й дефолтовый сквад.
sim_avail = true

[esc_stalker_base_default4_squad]:default_squad - 2-й дефолтовый сквад.
sim_avail = true

[esc_lost_stoyanka]:default_resource - это свободный смарт,туда кто нибудь из сквадов должен идти.
sim_avail = true

[esc_lost_stoyanka2]:default_resource - это тоже свободный смарт и тоже кто нибудь из сквадов должен идти.
sim_avail = true

 

[esc_stalker_base_default3_squad]:online_offline_group
faction = stalker
npc = sim_default_stalker_1, sim_default_stalker_0, sim_default_stalker_0, sim_default_stalker_0
;spawn_point = esc_stalker_base_mini_bar_spawn_point
story_id = esc_stalker_base_default3_squad

[esc_stalker_base_default4_squad]:online_offline_group
faction = stalker
npc = sim_default_stalker_1, sim_default_stalker_1, sim_default_stalker_0, sim_default_stalker_0
;spawn_point = esc_stalker_base_mini_bar_spawn_point
story_id = esc_stalker_base_default4_squad
Тут в принципе 2 дефолтовых сквада.

 

[esc_stalker_base]
255,255,255,144
[esc_lost_stoyanka]
255,255,255,145
[esc_lost_stoyanka2]
255,255,255,146

 

Вот esc_lost_stoyanka:


[smart_terrain];esc_lost_stoyanka
squad_id = 4
max_population = 1
Вот esc_lost_stoyanka2:
[smart_terrain];esc_lost_stoyanka2
squad_id = 5
max_population = 1

Вроде все.
Что может быть не так?

Ссылка на комментарий

А нафиг мне разбираться если там все нормально!!!!
Но специально для тебя вот:

[smart_terrain];esc_stalker_base
squad_id = 3
max_population = 10
 
[exclusive]
!Скрыто!
Изменено пользователем dmitry-strelok
Ссылка на комментарий

У тебя smart_terrain.script отличается от оригинального, потому что на 1256 строке нет операторов сравнения, о чем говорится в логе. Поэтому опять можно только гадать, чего там исправлялось или дописывалось. Покажи хотябы эту строку, а лучше конечно весь файл

Ссылка на комментарий

@dmitry-strelok, Посмотри в  SDK правильно ли установил параметры в  smart_terrain, проверь вписан твой левел в smart_terrain.script и в sim_board.script

Ссылка на комментарий

Я заново по правил скрипты и все равно результат тот же.Может статья какая нибудь не полная.Параметры такие.

В restrictor type стоит NOT A restrictor.
Имя все норм.
Хз почему вылет.
Могу скрипты скинуть 2.
Надо?

Вот скрипты.

--'******************************************************


--'* Реестр смарт-террейнов. Игровое поле симуляции.
--'******************************************************

--' В этом ltx хранятся дескрипторы сквадов.
squad_ltx = system_ini()
setting_ini = ini_file("misc\\simulation.ltx")

local group_id_by_levels = {zaton = 1,
pripyat = 2,
jupiter = 3,
labx8 = 4,
jupiter_underground = 5,
escape = 6}

local board = nil

simulation_activities = {
stalker = {
squad = nil,
smart = { base = { prec = function(squad, target) return in_time_interval(18,8) and not xr_conditions.surge_started() and not travel_manager.check_squad_for_enemies(squad) and (target:name() == "zat_stalker_base_smart" or target:name() == "jup_a6" or target:name() == "pri_a16") end },
surge = { prec = function() return xr_conditions.surge_started() end },
territory= { prec = function() return in_time_interval(8,18) and not xr_conditions.surge_started() end },
resource = { prec = function(squad, target) return in_time_interval(8,18) and not xr_conditions.surge_started() end } -- and squad:has_detector()
},
actor = nil
},
bandit = {
squad = { stalker = { prec = function(squad, target) return in_time_interval(8,21) and not xr_conditions.surge_started() and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
smart = { base = { prec = function(squad, target) return in_time_interval(21,8) and not xr_conditions.surge_started() and not travel_manager.check_squad_for_enemies(squad) and (target:name() == "zat_stalker_base_smart" or target:name() == "jup_a10_smart_terrain") end },
territory= { prec = function() return in_time_interval(8,21) and not xr_conditions.surge_started() end },
surge = { prec = function() return xr_conditions.surge_started() end }
},
actor = { prec = function(squad, target) return has_alife_info("sim_bandit_attack_harder") and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
dolg = {
squad = { freedom = { prec = function(squad, target) return in_time_interval(8,19) and not xr_conditions.surge_started() and simulation_objects.sim_dist_to(squad, target) <= 150 end },
monster_predatory_day = { prec = function(squad, target) return in_time_interval(8,19) and not xr_conditions.surge_started() and simulation_objects.sim_dist_to(squad, target) <= 150 end },
monster_predatory_night = { prec = function(squad, target) return in_time_interval(8,19) and not xr_conditions.surge_started() and simulation_objects.sim_dist_to(squad, target) <= 150 end },
monster_vegetarian = { prec = function(squad, target) return in_time_interval(8,19) and not xr_conditions.surge_started() and simulation_objects.sim_dist_to(squad, target) <= 150 end },
monster_zombied_day = { prec = function(squad, target) return in_time_interval(8,19) and not xr_conditions.surge_started() and simulation_objects.sim_dist_to(squad, target) <= 150 end },
monster_special = { prec = function(squad, target) return in_time_interval(8,19) and not xr_conditions.surge_started() and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
smart = { base = { prec = function(squad, target) return in_time_interval(19,8) and not xr_conditions.surge_started() and not travel_manager.check_squad_for_enemies(squad) and (target:name() == "zat_stalker_base_smart" or target:name() == "jup_a6" or target:name() == "pri_a16") end },
territory= { prec = function() return in_time_interval(8,19) and not xr_conditions.surge_started() end },
surge = { prec = function() return xr_conditions.surge_started() end }
},
actor = nil
},
freedom = {
squad = { dolg = { prec = function(squad, target) return in_time_interval(8,19) and not xr_conditions.surge_started() and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
smart = { base = { prec = function(squad, target) return in_time_interval(19,8) and not xr_conditions.surge_started() and not travel_manager.check_squad_for_enemies(squad) and (target:name() == "zat_stalker_base_smart" or target:name() == "jup_a6" or target:name() == "pri_a16") end },
territory= { prec = function() return in_time_interval(8,19) and not xr_conditions.surge_started() end },
surge = { prec = function() return xr_conditions.surge_started() end }
},
actor = nil
},
killer = {
squad = nil,
smart = { territory= { prec = function() return not xr_conditions.surge_started() end },
surge = { prec = function() return xr_conditions.surge_started() end }
},
actor = { prec = function(squad, target) return simulation_objects.sim_dist_to(squad, target) <= 150 end }

},
zombied = {
squad = nil,
smart = { territory= { prec = function() return true end },
lair = { prec = function() return true end}
},
actor = nil

},
monster_predatory_day = {
squad = { monster_vegetarian = { prec = function() return in_time_interval(6,19) end },
stalker = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
bandit = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
dolg = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
freedom = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
killer = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
smart = { territory= { prec = function() return in_time_interval(6, 19) end },
lair = { prec = function() return in_time_interval(19,6) end }
},
actor = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
monster_predatory_night = {
squad = { monster_vegetarian = { prec = function() return in_time_interval(21,6) end },
stalker = { prec = function(squad, target) return in_time_interval(19,6) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
bandit = { prec = function(squad, target) return in_time_interval(19,6) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
dolg = { prec = function(squad, target) return in_time_interval(19,6) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
freedom = { prec = function(squad, target) return in_time_interval(19,6) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
killer = { prec = function(squad, target) return in_time_interval(19,6) and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
smart = { territory= { prec = function() return in_time_interval(19,6) end },
lair = { prec = function() return in_time_interval(6,19) end }
},
actor = { prec = function(squad, target) return in_time_interval(19,6) and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
monster_vegetarian = {
squad = nil,
smart = {
lair = { prec = function() return true end }
},
actor = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
monster_zombied_day = {
squad = { stalker = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
bandit = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
dolg = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
freedom = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
killer = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
smart = { territory= { prec = function() return not xr_conditions.surge_started() end },
lair = { prec = function() return in_time_interval(19,6) end }
},
actor = { prec = function(squad, target) return in_time_interval(6, 19) and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
monster_zombied_night = {
squad = { stalker = { prec = function(squad, target) return in_time_interval(19,6) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
bandit = { prec = function(squad, target) return in_time_interval(19,6) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
dolg = { prec = function(squad, target) return in_time_interval(19,6) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
freedom = { prec = function(squad, target) return in_time_interval(19,6) and simulation_objects.sim_dist_to(squad, target) <= 150 end },
killer = { prec = function(squad, target) return in_time_interval(19,6) and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
smart = { territory= { prec = function() return in_time_interval(19,6) end },
lair = { prec = function() return in_time_interval(6,19) end }
},
actor = { prec = function(squad, target) return in_time_interval(19,6) and simulation_objects.sim_dist_to(squad, target) <= 150 end }
},
monster_special = {
squad = nil,
smart = { lair = { prec = function() return true end }
},
actor = nil
},
}








class "sim_board"
function sim_board:__init()
--' Таблица содержащая смарты и данные о них в формате:
--' smart = {smrt, targets = {}, dangers = {}, squads = {}, stayed_squad_quan = 0}
self.smarts = {}
self.simulation_started = true

--' Таблица смартов по именам.
self.smarts_by_names = {}

--' Таблица существующих отрядов.
self.squads = {}

--' отрисовывается ли сейчас поле на карте.
--self.show_flag = false

--' Таблица спаунсмартов. Смарт = уровень.
self.spawn_smarts = {}
-- Таблица для логовов мутантов на уровнях
self.mutant_lair = {}

-- Временная таблица
self.tmp_assigned_squad = {}
self.tmp_entered_squad = {}

printf("CREATE NEW SIM BOARD [%s]", tostring(self.sid))
end
function sim_board:start_sim()
self.simulation_started = true
end
function sim_board:stop_sim()
self.simulation_started = false
end
-- Перевод игрока в группировку.
function sim_board:set_actor_community(community)
---- Устанавливаем группировку игрока
db.actor:set_character_community(actor_communitites[community], 0, 0)
end
--' Регистрация нового смарта.
function sim_board:register_smart(obj)
printf("register_smart %s", obj:name())
if self.smarts[obj.id] ~= nil then
abort("Smart already exist in list [%s]", obj:name())
end

self.smarts[obj.id] = {smrt = obj, squads = {}, stayed_squad_quan = 0}

self.smarts_by_names[obj:name()] = obj

end
-- Инициализация смарта
function sim_board:init_smart(obj)
if self.tmp_assigned_squad[obj.id] ~= nil then
for k,v in pairs(self.tmp_assigned_squad[obj.id]) do
self:assign_squad_to_smart(v, obj.id)
end
self.tmp_assigned_squad[obj.id] = nil
end
if self.tmp_entered_squad[obj.id] ~= nil then
for k,v in pairs(self.tmp_entered_squad[obj.id]) do
self:enter_smart(v, obj.id)
end
self.tmp_entered_squad[obj.id] = nil
end
end
--' АнРегистрация смарта.
function sim_board:unregister_smart(obj)
if self.smarts[obj.id] == nil then
abort("Trying to unregister nil smart [%s]", obj:name())
end

self.smarts[obj.id] = nil
end

--' Создание нового отряда
function sim_board:create_squad(spawn_smart, sq_id)
printf("create squad called")
local squad_id = tostring(sq_id)

local squad = alife():create(squad_id,spawn_smart.position,
spawn_smart.m_level_vertex_id,
spawn_smart.m_game_vertex_id)
--local squad = squad_class(self, squad_id, faction_name, settings_id)
--squad:init_squad(spawn_smart)
printf("Creating squad[%s] in smart[%s]", squad_id, spawn_smart:name())
--' Определяем в каком смарте создать новый отряд
squad:create_npc(spawn_smart)
squad:set_squad_relation()
self:assign_squad_to_smart(squad, spawn_smart.id)

--' Пересчитываем Team, Squad, Group
for k in squad:squad_members() do
local obj = k.object
squad.board:setup_squad_and_group(obj)
end
return squad
end
--' Удалить отряд
function sim_board:remove_squad(squad)
printf("Remove squad %s", tostring(squad.id))
if squad.current_action == nil or
squad.current_action.dest_smrt == nil
then
squad.board:exit_smart(squad, squad.smart_id)
end
--squad:hide()

self:assign_squad_to_smart(squad, nil)

squad:remove_squad()
end
--' Назначение отряда в смарт.
function sim_board:assign_squad_to_smart(squad, smart_id)
-- Если смарта нету (например на загрузке он еще не загружен), то мы загоняем данные
-- во временную таблицу. И на загрузке смарта автодобавляем их.
--printf("assigned squad %s to smart [%s].", tostring(squad.id), tostring(smart_id))

if smart_id ~= nil and self.smarts[smart_id] == nil then
-- printf(" to tmp")
if self.tmp_assigned_squad[smart_id] == nil then
self.tmp_assigned_squad[smart_id] = {}
end
table.insert(self.tmp_assigned_squad[smart_id], squad)
return
end


--' Убираем отряд из старого смарта
local old_smart_id = nil
if squad.smart_id ~= nil then
old_smart_id = squad.smart_id
end
if old_smart_id ~= nil and self.smarts[old_smart_id] ~= nil then
self.smarts[old_smart_id].squads[squad.id] = nil
self.smarts[old_smart_id].smrt:refresh()
end

if smart_id == nil then
squad:assign_smart(nil)
return
end

squad:assign_smart(self.smarts[smart_id].smrt)

--' Прописываем отряд в новом смарте.
self.smarts[smart_id].squads[squad.id] = squad

self.smarts[smart_id].smrt:refresh()
end
--' Отряд покидает смарт (уменьшаются ресурсы)
function sim_board:exit_smart(squad, smart_id)
if smart_id == nil then
return
end

if squad.entered_smart ~= smart_id then
return
end

squad.entered_smart = nil

local smart = self.smarts[smart_id]

if smart == nil then
abort("Smart nil while smart_id not nil [%s]", tostring(smart_id))
end

printf("Squad %s exit smart %s. Quan = %s", tostring(squad.id), smart.smrt:name(), tostring(smart.stayed_squad_quan))
smart.stayed_squad_quan = smart.stayed_squad_quan - 1
smart.squads[squad.id] = nil

--smart.smrt:refresh()
end
--' Отряд занимает смарт (увеличиваются ресурсы)
function sim_board:enter_smart(squad, smart_id, after_load)
callstack()
if self.smarts[smart_id] == nil then
printf(" to tmp")
if self.tmp_entered_squad[smart_id] == nil then
self.tmp_entered_squad[smart_id] = {}
end
table.insert(self.tmp_entered_squad[smart_id], squad)
return
end

local smart = self.smarts[smart_id]

if squad.entered_smart ~= nil then
abort("Couldn enter to smart, becouse i'm not leave old one. Squad [%s]", squad.id)
end
squad.entered_smart = smart_id
printf("Squad %s enter smart %s. Quan = %s", tostring(squad.id), smart.smrt:name(), tostring(smart.stayed_squad_quan))

--smart.smrt:refresh()

smart.stayed_squad_quan = smart.stayed_squad_quan + 1
squad.items_spawned = false
end

-- установить squad и group в соответствии с работой
function sim_board:setup_squad_and_group(obj)
--printf("tsg["..obj:name().."]")
local level_name = level.name()
--' Группу берем из уровня.
local obj = alife():object(obj.id)

local group = group_id_by_levels[level_name] or 0

change_team_squad_group(obj, obj.team, obj.squad, group)

--' Сквад берем из смарта.
local squad = alife():object(obj.group_id)
if squad == nil then
change_team_squad_group(obj, obj.team, 0, obj.group)
--printf("TSG1: [%s][%s][%s]", tostring(obj.team), tostring(obj.squad), tostring(obj.group))
return
end

local smart = nil
if squad.current_action ~= nil and squad.current_action.name == "reach_target" then
smart = alife():object(squad.assigned_target_id)
elseif squad.smart_id ~= nil then
smart = alife():object(squad.smart_id)
end

if smart == nil then
change_team_squad_group(obj, obj.team, 0, obj.group)
--printf("TSG2: [%s][%s][%s]", tostring(obj.team), tostring(obj.squad), tostring(obj.group))
return
end
local obj_sq = 0
if smart:clsid() == clsid.smart_terrain then
obj_sq = smart.squad_id
end
change_team_squad_group(obj, obj.team, obj_sq, obj.group)
--printf("TSG3: [%s][%s][%s]", tostring(obj.team), tostring(obj.squad), tostring(obj.group))
end

--' Заполнение стартового расположения
function sim_board:fill_start_position()
if self.start_position_filled == true then
return
end
--printf("FILL START POSITION [%s]", self.player_name)
self.start_position_filled = true
for level in game_graph():levels() do
local section_name = "start_position_" .. alife():level_name(level.id)

if not setting_ini:section_exist(section_name) then
return
end

local n = setting_ini:line_count(section_name)
for i=0,n-1 do
local result, id, value = setting_ini:r_line(section_name,i,"","")
local smrt_names = utils.parse_names(value)
for k,v in pairs(smrt_names) do
local smart = self.smarts_by_names[v]
if smart == nil then
abort("Wrong smart name [%s] in start position", tostring(v))
end
local squad = self:create_squad(smart, id)
self:enter_smart(squad, smart.id)
--squad:update()
end
end
end
end


--' Возвратить смарт по его имени.
function sim_board:get_smart_by_name(name)
return self.smarts_by_names[name]
end
--' Возвращает количество отрядов в смарте.
function sim_board:get_smart_population(smart)
return self.smarts[smart.id].stayed_squad_quan
end

--' Получение игрового поля.
function get_sim_board()
if board == nil then
board = sim_board()
end
return board
end


function sim_board:get_squad_target(squad)
--printf("List of available target for %s", squad.id)

local available_targets = {}
local most_priority_task = nil
local max_prior = 0
for k,v in pairs(simulation_objects.get_sim_obj_registry().objects) do
local curr_prior = 0
-- Проверка против выдачи таска на себя.
if v.id ~= squad.id then
curr_prior = v:evaluate_prior(squad)
end
if curr_prior > 0 then
local target_tbl = {prior = curr_prior, target = v}
table.insert(available_targets, target_tbl)
end
end
if #available_targets > 0 then
table.sort(available_targets, function(a, B) return a.prior > b.prior end)
local max_id = math.floor(0.3 * #available_targets)
if max_id == 0 then max_id = 1 end
most_priority_task = available_targets[math.random(max_id)].target
end
--print_table(available_targets)
--printf("end of List of available target for %s", squad.id)
return most_priority_task or (squad.smart_id and alife():object(squad.smart_id)) or squad
end

local targets_by_squad_id = {}

--[[function sim_board:reset_targets()
for k,v in pairs(self.squads) do



end]]--
--' Обнуление списка на создании игры.
function clear()
board = nil
end

--' Тестовые функции
function print()
--print_table(get_sim_board().smarts)
end

 

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")

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 dev_dedug 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)
printf("[smart_terrain %s] register called obj=%s", self:name(), obj:name())
self.population = self.population + 1

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

-- Только для монстров, чтобы ходили по смартам.
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
self.npc_info[obj.id] = self:fill_npc_info(obj)

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

-- тут надо найти чуваку работу
self:select_npc_job(self.npc_info[obj.id])
else
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)
end
self.npc_to_register = {}
end
-- отпустить npc
function se_smart_terrain:unregister_npc(obj)
--callstack()
printf("smart [%s] unregister npc [%s]", self:name(), obj:name())

self.population = self.population - 1

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)
if self.npc_info[obj.id] ~= nil then
-- Устанавливаем таймер смерти на работе
self.dead_time[self.npc_info[obj.id].job_id] = game.get_game_time()

self.npc_info[obj.id].job_link.npc_id = nil
self.npc_info[obj.id] = nil
obj:clear_smart_terrain()
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: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 (_G.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 = props.."["..k.."] = "..v.num.."("..xr_logic.pick_section_from_condlist(db.actor, nil,self.respawn_params[k].num)..")\\n"
end
if self.last_respawn_update then
props = props.."\\ntime_to_spawn:"..tostring(RESPAWN_IDLE - game.get_game_time():diffSec(self.last_respawn_update)).."\\n"
end
end

--' Добавляем информацию о находящихся в смарте отрядах
for k,v in pairs(sim_board.get_sim_board().smarts[self.id].squads) do
props = props .. 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(_G.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 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
printf("clear dead object %s", obj:name())
sim:object(strn_id):clear_dead(obj)
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"] = {},
["escape"] = {}
}
local sim_smarts = {
["zaton"] = {},
["jupiter"] = {},
["pripyat"] = {},
["escape"] = {}
}
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]
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")
print_smarts_and_squads_by_level("escape")
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

 

--------------------------------------------------------------------------------
-- Surge manager class ---------------------------------------------------------
-- Made by Peacemaker ----------------------------------------------------------
-- 05.03.07 --------------------------------------------------------------------
--------------------------------------------------------------------------------
local surge_manager = nil

local surge_shock_pp_eff = 1
local earthquake_cam_eff = 2
local sleep_cam_eff = 3
local sleep_fade_pp_eff = 4

local START_MIN_SURGE_TIME = 2*60*60
local START_MAX_SURGE_TIME = 4*60*60
local MIN_SURGE_TIME = 12*60*60
local MAX_SURGE_TIME = 24*60*60
local prev_sec = 0

local immuned_to_surge_squads = {
["monster_predatory_day"] = true,
["monster_predatory_night"] = true,
["monster_vegetarian"] = true,
["monster_zombied_day"] = true,
["monster_zombied_night"] = true,
["monster_special"] = true,
["monster"] = true,
["zombied"] = true
}


class "CSurgeManager"
function CSurgeManager:__init()
end

function CSurgeManager:initialize()
self.ini = ini_file("misc\\surge_manager.ltx")

self.levels_respawn = {zaton = false, jupiter = false, pripyat = false, escape = false}

self.started = false
self.finished = true
self.time_forwarded = false
self.skip_message = false

self.task_given = false
self.effector_set = false
self.second_message_given = false
self.ui_disabled = false
self.blowout_sound = false

self.surge_time = 190
self.inited_time = game.CTime()
self.last_surge_time = game.get_game_time()
self._delta = math.random(START_MIN_SURGE_TIME, START_MAX_SURGE_TIME) -- global minutes, время между выбросами

self.count = 0
self.covers = {}

self.condlist = {}
self.survive = {}

local ini = self.ini
local cond_string = "true"
if(ini:line_exist("settings", "condlist")) then
cond_string = ini:r_string("settings", "condlist")
end
self.condlist = xr_logic.parse_condlist(nil, "surge_manager", "condlist", cond_string)

cond_string = "false"
if(ini:line_exist("settings", "survive")) then
cond_string = ini:r_string("settings", "survive")
end
self.survive = xr_logic.parse_condlist(nil, "surge_manager", "survive_condlist", cond_string)

self:init_surge_covers()

self.surge_message = ""
self.surge_task_sect = ""
self.loaded = false
end

function CSurgeManager:init_surge_covers()
local ini = self.ini
for i = 0, ini:line_count("list")-1 do
temp1, id, temp2 = ini:r_line("list", i, "", "")
local zone = db.zone_by_name[id]
if zone ~= nil then
self.count = self.count + 1
self.covers[self.count] = zone
if(ini:line_exist(id, "condlist")) then
self.covers[self.count].condlist = {}
self.covers[self.count].condlist = xr_logic.parse_condlist(npc, id, "condlist", ini:r_string(id, "condlist"))
end
end
end
end

function CSurgeManager:get_nearest_cover()
if(self.loaded) then
self:init_surge_covers()
end
local hides = {}
utils.copy_table(hides, self.covers)
if(self.count>0) then
for k,v in pairs(hides) do
if (v.condlist) then
local sect = xr_logic.pick_section_from_condlist(db.actor, nil, v.condlist)
if(sect~="true" and sect~=nil) then
table.remove(hides, k)
end
end
end
local nearest_cover_id = hides[1]:id()
local nearest_cover_dist = hides[1]:position():distance_to(db.actor:position())
for k,v in pairs(hides) do
if db.storage[v:id()].object:inside(db.actor:position()) then
return v:id()
end
local dist = v:position():distance_to(db.actor:position())
if(dist if(v.condlist) then
local sect = xr_logic.pick_section_from_condlist(db.actor, nil, v.condlist)
if(sect=="true") then
nearest_cover_id = v:id()
nearest_cover_dist = dist
end
else
nearest_cover_id = v:id()
nearest_cover_dist = dist
end
end
end
if(nearest_cover_id==hides[1]:id()) then
if(hides[1].condlist) then
local sect = xr_logic.pick_section_from_condlist(db.actor, nil, hides[1].condlist)
if(sect~="true" and sect~=nil) then
return nil
end
end
end
return nearest_cover_id
else
return nil
end
end

function CSurgeManager:update()
if(device().precache_frame > 1) then
return
end
if not(self.started) then
local g_time = game.get_game_time()
if(self.time_forwarded) then
log("--->delta="..tostring(self._delta))
log("--->diffSec="..tostring(g_time:diffSec(self.last_surge_time)))
local diff = math.abs(self._delta - g_time:diffSec(self.last_surge_time))
log("--->diff="..tostring(diff))
if(diff<3600) then
self._delta = 3*3600+g_time:diffSec(self.last_surge_time)
end
self.time_forwarded = false
end
if(g_time:diffSec(self.last_surge_time) < self._delta) then
return
end
if(xr_logic.pick_section_from_condlist(get_story_object("actor"), nil, self.condlist) ~= "true") then
return
end
if not(self:get_nearest_cover()) then
return
end
self:start()
return
end
-------------------------------------------------------------------- update here
local diff_sec = math.ceil(game.get_game_time():diffSec(self.inited_time)/level.get_time_factor())
if(prev_sec~=diff_sec) then
prev_sec = diff_sec
log("------>diff_sec="..tostring(diff_sec))
local cover = self:get_nearest_cover()
if(cover==nil and self.count==0) then
self:init_surge_covers()
return
end
local level_name = level.name()
if(level_name=="labx8" or level_name=="jupiter_underground") then
printf("Surge stopped because of level!")
self:end_surge()
return
end
if(diff_sec>=self.surge_time) then
if(level) then
if(level.name()=="zaton") then
xr_sound.set_sound_play(db.actor:id(), "zat_a2_stalker_barmen_after_surge")
elseif(level.name()=="jupiter") then
xr_sound.set_sound_play(db.actor:id(), "jup_a6_stalker_medik_after_surge")
elseif not has_alife_info("pri_b305_fifth_cam_end") then
xr_sound.set_sound_play(db.actor:id(), "pri_a17_kovalsky_after_surge")
end
end
self:end_surge()
else
if(self.loaded) then
if(self.blowout_sound)then
xr_sound.play_sound_looped(db.actor:id(), "blowout_rumble")
end
if(self.effector_set) then
level.add_pp_effector("surge_shock.ppe", surge_shock_pp_eff, true)
end
if(self.second_message_given) then
xr_sound.play_sound_looped(db.actor:id(), "surge_earthquake_sound_looped")
level.add_cam_effector("camera_effects\\earthquake.anm", earthquake_cam_eff, true, "")
end
self.loaded = false
end
self:launch_rockets()
if(self.effector_set) then
level.set_pp_effector_factor(surge_shock_pp_eff, diff_sec/90, 0.1)
end
if(self.blowout_sound) then
xr_sound.set_volume_sound_looped(db.actor:id(), "blowout_rumble", diff_sec/180)
end
if(diff_sec>=140 and not(self.ui_disabled) and (cover==nil or not(db.storage[cover].object:inside(db.actor:position())))) then
local att = 1-(185-diff_sec)/(185-140)
att = att*att*att*0.3
local h = hit()
h.type = hit.telepatic
h.power = att
h.impulse = 0.0
h.direction = vector():set(0,0,1)
h.draftsman = db.actor
if(xr_logic.pick_section_from_condlist(get_story_object("actor"), nil, self.survive)=="true") then
if(db.actor.health<=h.power) then
h.power = db.actor.health - 0.05
if(h.power<0) then
h.power = 0
end
end
end
db.actor:hit(h)
end
if(diff_sec>=185) and not(self.ui_disabled) then
self:kill_all_unhided()
self.ui_disabled = true
elseif(diff_sec>=140) and not(self.second_message_given) then
if(level) then
if(level.name()=="zaton") then
xr_sound.set_sound_play(db.actor:id(), "zat_a2_stalker_barmen_surge_phase_2")
elseif(level.name()=="jupiter") then
xr_sound.set_sound_play(db.actor:id(), "jup_a6_stalker_medik_phase_2")
elseif not has_alife_info("pri_b305_fifth_cam_end") then
xr_sound.set_sound_play(db.actor:id(), "pri_a17_kovalsky_surge_phase_2")
end
end
xr_sound.play_sound_looped(db.actor:id(), "surge_earthquake_sound_looped")
level.add_cam_effector("camera_effects\\earthquake.anm", earthquake_cam_eff, true, "")
self.second_message_given = true
elseif(diff_sec>=100) and not(self.effector_set) then
level.add_pp_effector("surge_shock.ppe", surge_shock_pp_eff, true)
-- level.set_pp_effector_factor(surge_shock_pp_eff, 0, 10)
self.effector_set = true
elseif(diff_sec>=35) and not(self.blowout_sound)then
xr_sound.set_sound_play(db.actor:id(), "blowout_begin")
xr_sound.play_sound_looped(db.actor:id(), "blowout_rumble")
xr_sound.set_volume_sound_looped(db.actor:id(), "blowout_rumble", 0.25)
self.blowout_sound = true
elseif(diff_sec>=0) and not(self.task_given)then
if(level) then
if(level.name()=="zaton") then
xr_sound.set_sound_play(db.actor:id(), "zat_a2_stalker_barmen_surge_phase_1")
elseif(level.name()=="jupiter") then
xr_sound.set_sound_play(db.actor:id(), "jup_a6_stalker_medik_phase_1")
elseif not has_alife_info("pri_b305_fifth_cam_end") then
xr_sound.set_sound_play(db.actor:id(), "pri_a17_kovalsky_surge_phase_1")
end
end
level.set_weather_fx("fx_surge_day_3")
self:give_surge_hide_task()
self.task_given = true
end
end
end
end

function CSurgeManager:start(manual)
local Y, M, D, h, m, s, ms = 0, 0, 0, 0, 0, 0, 0
Y, M, D, h, m, s, ms = self.last_surge_time:get(Y, M, D, h, m, s, ms)
if(manual) then
self.inited_time = game.get_game_time()
else
self.inited_time:set(Y, M, D, h, m, s + self._delta, ms)
end

diff_sec = math.ceil(game.get_game_time():diffSec(self.inited_time)/level.get_time_factor())
log("--->start diff_sec="..tostring(diff_sec))
local level_name = level.name()
if(level_name=="labx8" or level_name=="jupiter_underground") then
printf("Surge skipped because of level!")
self.skip_message = true
self:skip_surge()
return
end
if(diff_sec+6>self.surge_time)then
printf("Surge skipped while time forwarding!")
self:skip_surge()
else
self.started = true
self.finished = false
-- autosave
if not has_alife_info("pri_b305_fifth_cam_end") or has_alife_info("pri_a28_actor_in_zone_stay") then
xr_effects.scenario_autosave(nil,nil,{"st_save_uni_surge_start"})
end
end
end

function CSurgeManager:skip_surge()
local Y, M, D, h, m, s, ms = 0, 0, 0, 0, 0, 0, 0
Y, M, D, h, m, s, ms = self.inited_time:get(Y, M, D, h, m, s, ms)
self.last_surge_time:set(Y, M, D, h, m, s + self.surge_time, ms)

self.started = false
self.finished = true
self.levels_respawn = {zaton = true, jupiter = true, pripyat = true, escape = true}
self._delta = math.random(MIN_SURGE_TIME, MAX_SURGE_TIME)
self.surge_message = ""
self.surge_task_sect = ""
self.task_given = false

self.effector_set = false
self.second_message_given = false
self.ui_disabled = false
self.blowout_sound = false
prev_sec = 0

self:respawn_artefacts_and_replace_anomaly_zone()
xr_statistic.inc_surges_counter()
if (not self.skip_message) then
news_manager.send_tip(db.actor, "st_surge_while_asleep", nil, "recent_surge", nil, nil)
self.skip_message = true
end
end

function CSurgeManager:end_surge(manual)
self.started = false
self.finished = true
self.levels_respawn = {zaton = true, jupiter = true, pripyat = true, escape = true}
self.last_surge_time = game.get_game_time()
self._delta = math.random(MIN_SURGE_TIME, MAX_SURGE_TIME)
self.surge_message = ""
self.surge_task_sect = ""
self.task_given = false

if(self.effector_set) then
xr_sound.stop_sound_looped(db.actor:id(), "blowout_rumble")
end
if(self.second_message_given) then
xr_sound.stop_sound_looped(db.actor:id(), "surge_earthquake_sound_looped")
end
level.remove_pp_effector(surge_shock_pp_eff)
level.remove_cam_effector(earthquake_cam_eff)

if(manual or (self.time_forwarded and level_weathers.get_weather_manager().weather_fx)) then
level.stop_weather_fx()
-- level_weathers.get_weather_manager():select_weather(true)
level_weathers.get_weather_manager():forced_weather_change()
end

self.effector_set = false
self.second_message_given = false
self.ui_disabled = false
self.blowout_sound = false
prev_sec = 0

for k,v in pairs(db.signal_light) do
v:stop_light()
v:stop()
end

if self.loaded then
self:kill_all_unhided()
end

self:respawn_artefacts_and_replace_anomaly_zone()
xr_statistic.inc_surges_counter()
end

function CSurgeManager:respawn_artefacts_and_replace_anomaly_zone()
local lvl_nm = level.name()
if(self.levels_respawn[lvl_nm]) then
self.levels_respawn[lvl_nm] = false
end
local anomalies = db.anomaly_by_name
for k,v in pairs(anomalies) do
v:respawn_artefacts_and_replace_anomaly_zone()
--printf("respawn artefacts in anomal zone [%s]", tostring(k))
end
pda.change_anomalies_names()
end

function CSurgeManager:give_surge_hide_task()
if(self.surge_message~="empty") then
local mess = ""
if(self.surge_message=="") then
local time = 0
mess = game.translate_string("hide_from_surge_message")
else
mess = game.translate_string(self.surge_message)
end
end
if(self.surge_task_sect~="empty") then
if(self.surge_task_sect=="") then
task_manager.get_task_manager():give_task("hide_from_surge")
else
task_manager.get_task_manager():give_task(self.surge_task_sect)
end
end
end

function get_squad_members(squad_id)
local npcs_tbl = {}
local squad = alife():object(squad_id)
if(squad) then
for obj in squad:squad_members() do
npcs_tbl[obj.id] = true
end
end
return npcs_tbl
end

function check_squad_level(squad_id)
local squad = alife():object(squad_id)
if(squad) then
local squad_level = alife():level_name(game_graph():vertex(squad.m_game_vertex_id):level_id())
if(squad_level==level.name()) then
return true
end
end
return false -- can't delete squad on other level
end

function check_squad_community_and_story_id(squad_id)
local squad = alife():object(squad_id)
if(squad) then
if(immuned_to_surge_squads[squad.player_id]) then
return false -- can't delete squad immune to surge
end
if(get_object_story_id(squad.id)) then
return false -- can't delete squad with story id
end
end
return true
end

function check_squad_community(squad_id)
local squad = alife():object(squad_id)
if(squad) then
if(immuned_to_surge_squads[squad.player_id]) then
return false -- can't delete squad immune to surge
end
end
return true
end

function check_squad_smart_props(squad_id)
local squad = alife():object(squad_id)
if(squad) then
local board = sim_board.get_sim_board()
if(board and squad.smart_id and board.smarts[squad.smart_id]) then
local smart = board.smarts[squad.smart_id].smrt
if(tonumber(smart.props["surge"])<=0) then
return true
end
end
end
return false -- can't delete squad in his smart if squad is in cover
end

function CSurgeManager:kill_all_unhided()
local h = hit()
h.type = hit.fire_wound
h.power = 0.9
h.impulse = 0.0
h.direction = vector():set(0,0,1)
h.draftsman = db.actor
for k,v in pairs(bind_crow.crow_storage) do
local obj = alife():object(v)
if obj then
local crow = level.object_by_id(obj.id)
if(crow and crow:alive()) then
crow:hit(h)
end
end
end

local board = sim_board.get_sim_board()
for k,v in pairs(board.squads) do
local squad = v
if(check_squad_level(squad.id)) then
if(check_squad_community_and_story_id(squad.id)) then
local squad_npcs = get_squad_members(squad.id)
for kk,vv in pairs(squad_npcs) do
local obj = alife():object(kk)
if(obj and not(get_object_story_id(obj.id))) then
if(check_squad_smart_props(squad.id)) then
printf("Releasing npc [%s] from squad [%s] because of surge!",obj:name(), squad:name())
local cl_obj = level.object_by_id(obj.id)
if cl_obj ~= nil then
cl_obj:kill(cl_obj)
else
obj:kill()
end
else
local release = true
for i = 1,#self.covers do
local sr = self.covers
if(sr and sr:inside(obj.position)) then
release = false
end
end
if(release) then
printf("Releasing npc [%s] from squad [%s] because of surge!",obj:name(), squad:name())
local cl_obj = level.object_by_id(obj.id)
if cl_obj ~= nil then
cl_obj:kill(cl_obj)
else
obj:kill()
end
end
end
end
end
end
end
end

local cover = self:get_nearest_cover()
if(db.actor and db.actor:alive()) then
if not(cover and db.storage[cover] and db.storage[cover].object:inside(db.actor:position())) then
if has_alife_info("anabiotic_in_process") then
local counter_name = "actor_marked_by_zone_cnt"
local cnt_value = xr_logic.pstor_retrieve(db.actor, counter_name, 0)
xr_logic.pstor_store(db.actor, counter_name, cnt_value + 1)
end
--[[
local hud = get_hud()
hud:HideActorMenu()
hud:HidePdaMenu()
db.actor:stop_talk()
level.disable_input()
level.hide_indicators_safe()
db.actor:hide_weapon()
]]--
xr_effects.disable_ui_only(db.actor, nil)
if(xr_logic.pick_section_from_condlist(get_story_object("actor"), nil, self.survive)~="true") then
self:kill_all_unhided_after_actor_death()
db.actor:kill(db.actor)
return
else
level.add_cam_effector("camera_effects\\surge_02.anm", sleep_cam_eff, false, "surge_manager.surge_callback")
level.add_pp_effector("surge_fade.ppe", sleep_fade_pp_eff, false)
db.actor.health = db.actor.health-0.05
end
end
end
end

function CSurgeManager:kill_all_unhided_after_actor_death()
local board = sim_board.get_sim_board()
for k,v in pairs(board.squads) do
local squad = v
if(check_squad_level(squad.id)) then
if(check_squad_community(squad.id)) then
local squad_npcs = get_squad_members(squad.id)
for kk,vv in pairs(squad_npcs) do
local obj = alife():object(kk)
if obj then
local release = true
for i = 1,#self.covers do
local sr = self.covers
if(sr and sr:inside(obj.position)) then
release = false
end
end
if(release) then
printf("Releasing npc [%s] from squad [%s] after actors death because of surge!",obj:name(), squad:name())
local cl_obj = level.object_by_id(obj.id)
if cl_obj ~= nil then
cl_obj:kill(cl_obj)
else
obj:kill()
end
end
end
end
end
end
end
end

function surge_callback()
level.add_cam_effector("camera_effects\\surge_01.anm", sleep_cam_eff, false, "surge_manager.surge_callback2")
-- level.stop_weather_fx()
-- level.change_game_time(0,0,15)
-- level_weathers.get_weather_manager():forced_weather_change()
end

function surge_callback2()
xr_effects.enable_ui(db.actor, nil)
--[[
level.enable_input()
level.show_indicators()
db.actor:restore_weapon()
]]--
end

function CSurgeManager:launch_rockets()
for k,v in pairs(db.signal_light) do
if not(v:is_flying()) then
v:launch()
end
end
end

function CSurgeManager:save(packet)
set_save_marker(packet, "save", false, "SurgeHide")
packet:w_bool(self.finished)
packet:w_bool(self.started)
utils.w_CTime(packet, self.last_surge_time)
if(self.started) then
utils.w_CTime(packet, self.inited_time)

packet:w_bool(self.levels_respawn.zaton)
packet:w_bool(self.levels_respawn.jupiter)
packet:w_bool(self.levels_respawn.pripyat)
packet:w_bool(self.levels_respawn.escape)

packet:w_bool(self.task_given)
packet:w_bool(self.effector_set)
packet:w_bool(self.second_message_given)
packet:w_bool(self.ui_disabled)
packet:w_bool(self.blowout_sound)

packet:w_stringZ(self.surge_message)
packet:w_stringZ(self.surge_task_sect)
end
packet:w_u32(self._delta)
set_save_marker(packet, "save", true, "SurgeHide")
end

function CSurgeManager:load(packet)
set_save_marker(packet, "load", false, "SurgeHide")
self:initialize()
self.finished = packet:r_bool()
self.started = packet:r_bool()
self.last_surge_time = utils.r_CTime(packet)
if(self.started) then
self.inited_time = utils.r_CTime(packet)

self.levels_respawn.zaton = packet:r_bool()
self.levels_respawn.jupiter = packet:r_bool()
self.levels_respawn.pripyat = packet:r_bool()
self.levels_respawn.escape = packet:r_bool()

self.task_given = packet:r_bool()
self.effector_set = packet:r_bool()
self.second_message_given = packet:r_bool()
self.ui_disabled = packet:r_bool()
self.blowout_sound = packet:r_bool()

self.surge_message = packet:r_stringZ()
self.surge_task_sect = packet:r_stringZ()
end
self._delta = packet:r_u32()
self.loaded = true
set_save_marker(packet, "load", true, "SurgeHide")
end
--------------------------------------------------------------------------------
function get_surge_manager()
if surge_manager == nil then
surge_manager = CSurgeManager()
end
return surge_manager
end

function start_surge(p)
local m = get_surge_manager()
if(m:get_nearest_cover()) then
m:start(true)
else
printf("Error: Surge covers are not set! Can't manually start")
end
end

function actor_in_cover()
local m = get_surge_manager()
local cover_id = m:get_nearest_cover()
if (cover_id ~= nil) and (db.storage[cover_id].object:inside(db.actor:position())) then
return true
else
return false
end
end

function stop_surge()
local m = get_surge_manager()
if(m.started) then
m:end_surge(true)
end
end

function get_task_descr()
local descr = ""
if(actor_in_cover()) then
descr = game.translate_string("hide_from_surge_descr_2_a")
else
descr = game.translate_string("hide_from_surge_descr_1_a")
end
return descr
end

function get_task_target()
local m = get_surge_manager()
if(actor_in_cover()) then
return nil
end
return m:get_nearest_cover()
end

function set_surge_message(mess)
local m = get_surge_manager()
m.surge_message = mess
end

function set_surge_task(task)
local m = get_surge_manager()
m.surge_task_sect = task
end

function is_started()
local m = get_surge_manager()
return m.started
end

function is_killing_all()
local m = get_surge_manager()
if(m.started and m.ui_disabled) then
return true
end
return false
end

function is_finished()
local m = get_surge_manager()
return m.finished == true
end

function resurrect_skip_message()
local m = get_surge_manager()
m.skip_message = false
end

function sound_started()
local m = get_surge_manager()
return m.started and m.blowout_sound
end

)>

Все.Это все скрипты которые я трогал.

 

Такие огромные тексты врядли кто прокручивать станет. Лучше заливай файлики на обменник и выкладывай ссылкой.

Для менее огромных кодов, используй тег "код", дабы сохранять форматирование.

И пиши без "хз".

Cyclone

Изменено пользователем Cyclone
Ссылка на комментарий

В логе явно говориться, что на строке 1256 идёт сравнение числа с nil, смотрим строку 1256:


if squad_count ~= nil and (self.max_population <= squad_count) then

И тут невооружённым глазом видно, что nil-ом может быть только self.max_population. Следовательно, для какого-то из смартов он не указан.
Ссылка на комментарий

А может из-за того,что у меня на локации несколько обычных смартов?Просто я их в заранее поставил но в конфигах не трогал.



Пойду уберу их.Просто мне в прошлый раз сказали,что пустой смарт не влияет и вылета не должно быть.

Ссылка на комментарий

Если в пустых  smart_terrain  есть ссылка на script\"твоя локация"\smart, а там не прописано максимальная пуполяция на них то вполне возможно вылет из-за этого.

Изменено пользователем stalker_bes
Ссылка на комментарий

Нет,они просто пустые.



Убрал смарты лишние,те которые пустые были,вообщем все работает!Они идут по свободным смартам:))
Спасибо огромное,что хоть подсказку дали про пустышки!

Ссылка на комментарий

Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

Создать аккаунт

Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!

Зарегистрировать новый аккаунт

Войти

Есть аккаунт? Войти.

Войти
  • Недавно просматривали   0 пользователей

    Ни один зарегистрированный пользователь не просматривает эту страницу.

AMK-Team.ru

×
×
  • Создать...