Сповіщення
Очистити все

Смарт терейни та гулаги


Ранг:
Майстер
Роль:
Гість
Записи:
752
Приєднався:
2 роки тому
 

Останнім часом люди постійно мене питали, як створити скрипт гулага для смартеррейнів. Щоб не відповідати на кожен окремий лист, я вирішив написати невеликий тутор. Перш ніж почати, хотілося б попередити, що не є експертом у подібних речах і деякі моменти мені все ще не зрозумілі до кінця, так що ймовірно я не зможу пояснити все правильно. Також я використовуватиму 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 потрібні розставлені точки шляхів для нього.


   
Цитата