Останнім часом люди постійно мене питали, як створити скрипт гулага для смартеррейнів. Щоб не відповідати на кожен окремий лист, я вирішив написати невеликий тутор. Перш ніж почати, хотілося б попередити, що не є експертом у подібних речах і деякі моменти мені все ще не зрозумілі до кінця, так що ймовірно я не зможу пояснити все правильно. Також я використовуватиму ST як абревіатуру смартеррейну. Цей тутор присвячений НПС у ST, але він також згодиться і для мутантів. Припускаю, що ви знайомі з:
- луа програмуванням
- редагуванням all.spawn
- робота з вейпоінтами (патрульними шляхами)
- утилітами аї
У першу чергу подумайте про своє ST, де б його спанити, скільки нпс/мутантів будуть в ньому виконувати певну "роботу", коли ST повинен активуватися/відключатися, скільки і які стану буде у вашого ST і тд. Думаю, найбільш ефективний спосіб пояснити вам, як це працює на прикладі. Давайте припустимо. ми хочемо поставити ST(smart terrain) на Кордоні для 3х бандитів.
1. Спавн ST:
[2515] ; cse_abstract properties (основные параметры) section_name = smart_terrain name = esc_bandits_smart_terrain position = 131.02030944824,0.065616846084595,-248.9094543457 direction = 0,0,0 ; cse_alife_object properties (параметры объекта) game_vertex_id = 635 distance = 9.09999942779541 level_vertex_id = 363757 object_flags = 0x==3e custom_data = <<END [smart_terrain] type = esc_bandits_smart_terrain cond = {-infoportion} capacity = 3 squad = 1 groups = 5 respawn = esc_respawn_inventory_box_0002 END ; cse_shape properties (параметры шейпа объекта) shapes = shape0 shape0:type = sphere shape0:offset = 0,0,0 shape0:radius = 20.55957102775574 ; cse_alife_space_restrictor properties (параметры рестриктора) restrictor_type = 3 ; se_smart_terrain properties (параметры смарттеррейна)
Це найважливіша частина:
type = esc_bandits_smart_terrain cond = {-infoportion} capacity = 3 squad = 1 groups = 5 respawn = esc_respawn_inventory_box_0002</source>
type назва вашого нового ST(обов'язково)
cond описує умови, які необхідні для включення гулага(за бажанням)
capacity кількість мутантів/нпс, яка може вмістити смарттеррейн(обов'язково)
squad, groups - номер сквада та кількість груп(за бажанням)
respawn назва схованки (синя коробка) куди спавняться предмети, коли ми викличемо респавн в ST.(за бажанням)
2. Співн нпс/мутантів та призначення(біндинг) їх до нашого ST: для цього ми повинні додати кожному мутанту/нпс певну логіку:
custom_data = <<END [ smart_terrains ] esc_bandits_smart_terrain = true END
Якщо таких нпс не виявиться то гулаг вибере собі населення з числа нпс, що спалися в Зоні, з відповідними параметрами. Навіть якщо вони на іншій локації.
3. Додаємо "роботу" (логіка) для кожного нпс/мутанта з нашого ST(для кожного стану). Припусті у ST їх два: стан 0 (описує які нпс/мутанти "працюють" вдень) та стан 1 (вночі). У нас 3 бандити, визначаємося:
- bandit1: walker (стан 0) і kamp (стан 1)
- bandit2: guard (стан 0) і sleeper (стан 1)
- bandit3: walker (стан 0 і 1 <= він робить також і вдень, і вночі)
У нас є 3 способи додати логіку (роботу) для кожного нпс/мутанта, ми будемо використовувати найбільш загальноприйнятий спосіб, додамо логіку до файлу config\misc\gulag_escape.ltx. Вона має виглядати приблизно так:
;-- bandit1 (walker(прогулюється) -> стан 0, вдень) [ logic@esc_bandits_smart_terrain_bandit1_walker ] active = walker@esc_bandits_smart_terrain_bandit1 [ walker@esc_bandits_smart_terrain_bandit1 ] path_walk = bandit1_walk danger = danger_condition@ esc_bandits_smart_terrain def_state_moving1 = patrol def_state_moving2 = patrol def_state_moving3 = patrol meet = ;-- bandit1 (kamp(табір) -> стан 1, вночі) [ logic@esc_bandits_smart_terrain_bandit1_kamp ] active = kamp@esc_bandits_smart_terrain_bandit1 [ kamp@esc_bandits_smart_terrain_bandit1 ] center_point = bandit_kamp path_walk = bandit_kamp_task ;-- bandit2 (guard(охоронець) -> стан 0, вдень) [ logic@esc_bandits_smart_terrain_bandit2_walker ] active = walker@esc_bandits_smart_terrain_bandit2 [ walker@esc_bandits_smart_terrain_bandit2 ] path_walk = bandit2_walk path_look = bandit2_look danger = danger_condition@esc_bandits_smart_terrain ;-- bandit2 (sleeper(сплячий) -> стан 1, вночі) [ logic@esc_bandits_smart_terrain_bandit2_sleeper ] active = sleeper@esc_bandits_smart_terrain_bandit2 [ sleeper@esc_bandits_smart_terrain_bandit2 ] path_main = bandit2_sleep wakeable = false ;-- bandit3 (guard -> стан 0 і 1, вдень/вночі) [ logic@esc_bandits_smart_terrain_bandit3_walker ] active = walker@esc_bandits_smart_terrain_bandit3 [ walker@esc_bandits_smart_terrain_bandit3 ] path_walk = bandit3_walk path_look = bandit3_look [ danger_condition@esc_bandits_smart_terrain ] ignore_distance_corpse = 0 ignore_distance = 0
4. Тепер нам потрібно заскриптувати наш ST. Тож додамо наш код у фаїл скрипта \gulag_escape.script. Є ще кілька моментів, які ми повинні тут доробити (кожен із цих кроків є обов'язковим):
- вантажимо логіку (роботу) для кожного нпс/мутанта і для кожного стану -> function load_job(...)
if type == "esc_bandits_smart_terrain" then t = { } ; - "сполучна секція" для логіки, визначаємо ltx фаїлом t.section = "logic@esc_bandits_smart_terrain_bandit1_walker" ; - no idea, probably describes after what time ; - npc will use this job again (?) t.idle = 0 ; - no idea but i guess it's optional t.timeout = 0 ; - пріоритет t.prior = 100 ; - нпс буде використовувати цю логіку ; - якщо ST переключиться в цей стан ; - у цьому випадку - стан 0 (день ) t.state = {0} ; - Який squad і group призначиться персонажу, який прийняв цю роботу. t.squad = squad t.group = groups [ 1 ] ; -- no idea what means position_threshold t.position_threshold = 100 ; - Описує нпс в цьому стані: онлайн або офлайн ; - онлайн = істина по дефолту t.online = true ; - описує рестриктори (куди нпс можуть/не можуть піти) t.in_rest = "" t.out_rest = "" ; -- через особливий спосіб присвоєння робіт у ; - smart_terrain.script ви ніколи не знаєте, яка робота ; -- використовуватиметься кожним НПС; якщо ви хочете бути впевненим ; - що конкретний НПС взяв конкретну роботу, тоді ; - Вам потрібно заюзати предикатну функцію; у цьому випадку ; - ми хочемо , щоб ця робота привласнилася майстру бандиту t.predicate = function ( obj_info ) return obj_info.rank >= 900 end table.insert t = { section = "logic@esc_bandits_smart_terrain_bandit1_kamp" ,idle = 0 , timeout = 0 , prior = 100 , state = {1} , squad = squad, group = groups [ 1 ] , position_threshold = 100 , online = true , in_rest = " " , out_rest = "" , predicate = function ( obj_info ) return obj_info.rank >= 900 end } table.insert ( sj, t ) ; -- bandit2 -> стан 0 (день) t = { section = "logic@esc_bandits_smart_terrain_bandit2_walker" , idle = 0 , prior = 5 , state = {0} , squad = squad, group = groups [ 1 ] , in_rest = "" , out_rest = "" } table.insert ( sj, t ) ; -- bandit2 -> стан 1 (ніч) t = { section = "logic@esc_bandits_smart_terrain_bandit2_sleeper" , idle = 0 , prior = 5 , state = {1} , squad = squad, group = groups [ 1 ] , in_rest = "" , out_rest = "" } table.insert ( sj, t ) ; -- bandit3 -> стан 0 (день) та стан 1 (ніч) t = { section = "logic@esc_bandits_smart_terrain_bandit3_walker" , idle = 0 , prior = 5 , state = { 0 , 1 } , squad = squad, group = groups [ 1 ] , in_rest = "" , out_rest = "" } table.insert ( sj, t ) end
Ще один момент про стан ST, все залежить від того скільки у вас їх в ST. Ще однією важливою річчю є додавання логіки для кожного стану. Наприклад у вашому ST такі стани:
0 - нпс оффлайн
1 - нпс онлайн (день)
2 - нпс онлайн (ніч)
3 - нпс онлайн, вони вирішили напасти на інший ST
4 - нпс онлайн, актор напав на них
До відома, мене не прет заповнювати таку велику кількість таблиць, так що зазвичай я використовую цю функцію:
function fill_tbl(section, idle, prior, states, squad, group, in_rest, out_rest, online, gulag_name) local tbl = {} tbl.section = "logic@" .. gulag_name .. "_" .. section tbl.idle = idle tbl.prior = prior tbl.state = {} for index = 1, #states do table.insert(tbl.state, states[index]) end tbl.squad = squad tbl.group = group tbl.in_rest = in_rest tbl.out_rest = out_rest tbl.online = online return tbl end
Використовуючи функцію вище, ми можемо завантажувати логіку на кшталт цієї:
if type == "esc_bandits_smart_terrain" then local t = table.insert(sj, fill_tbl("bandit1_walker", 0, 100, {0}, squad, groups[1], "", "", true, type)) t.timeout = 0 t.position_threshold = 100 t.predicate = function(obj_info) return obj_info.rank >= 900 end table.insert(sj, t) t = table.insert(sj, fill_tbl("bandit1_kamp", 0, 100, {1}, squad, groups[1], "", "", true, type)) t.timeout = 0 t.position_threshold = 100 t.predicate = function(obj_info) return obj_info.rank >= 900 end table.insert(sj, t) table.insert(sj, fill_tbl("bandit2_walker", 0, 5, {0}, squad, groups[1], "", "", true, type)) table.insert(sj, fill_tbl("bandit2_sleeper", 0, 5, {1}, squad, groups[1], "", "", true, type)) table.insert(sj, fill_tbl("bandit3_walker", 0, 5, {0, 1}, squad, groups[1], "", "", true, type)) end
- автоматично змінюємо роботу для кожного нпс/мутанта -> function load_states(...)
if type == "esc_bandits_smart_terrain" then return function(gulag) if not db.actor then return gulag.state end if level.get_time_hours() >= 5 and level.get_time_hours() <= 22 then return 0 -- переключает всех мутантов/нпс на дневную работу else return 1 -- ереключает всех мутантов/нпс на ночную работу end end end
- переконайтеся, що наш ST буде використовуватися тільки для бандитів -> function checkStalker(...)
if gulag_type == "esc_bandits_smart_terrain" then return npc_community == "bandit" end
Також існують універсальні гулаги General_lager для сталкерів. Вони вважаються спрощеними гулагами.
Наприклад, створюємо смарт:
[ 9999 ] ; cse_abstract properties ( основні параметри ) section_name = smart_terrain name = esc_gen_lager position = 131.02030944824 , 0.065616846084595 , -248.9094543457 direction = 0 , 0 , 0 ; cse_alife_object properties ( параметри об'єкта ) game_vertex_id = 635 distance = 9.09999942779541 level_vertex_id = 363757 object_flags = 0x==3e custom_data = <<END [ smart_terrain ] type = general_lager capacity = 3 ; місткість communities = bandit; ком'юніті населення END ; cse_shape properties ( параметри шейпа об'єкта ) shapes = shape0 shape0: type = sphere shape0:offset = 0 , 0 , 0 shape0:radius = 20.55957102775574 ; cse_alife_space_restrictor properties ( параметри рестриктора ) restrictor_type = 3 ; se_smart_terrain properties ( параметри смарттеррейну )
General_lager автоматично збере всі точки шляхів на рівні, що починаються на ім'я смарта (у нашому випадку esc_gen_lager) і розділить їх назви таким чином: (ім'я_смарта)_(аи схема)_(номер якщо потрібно, наприклад якщо 2 walkerа то 1 і 2 відповідно)_ (підналаштування схеми)_(стан гулага 1 або 0, день або ніч) приклад: esc_gen_lager _walker _1 _walk _1 (walk те ж що і path_walk, look це path_look відповідно)
Сталкер, що прийшов на general_lager, рандомно прийме будь-яку вільну роботу роботою вважається комбінація схем з одним номером. наприклад, esc_gen_lager_walker_1_walk_1 і esc_gen_lager_walker_1_look_1 це схема денної роботи одного персонажа з гулагу.
Так що для кожного general_lager потрібні розставлені точки шляхів для нього.