Smart Terrain Tutorial
From Mod Wiki
Revision as of 18:08, 1 July 2008 (edit) Don Reba (Talk | contribs) m ← Previous diff |
Current revision (01:50, 2 June 2009) (edit) (undo) Orion (Talk | contribs) (→Links) |
||
(4 intermediate revisions not shown.) | |||
Line 1: | Line 1: | ||
Lately some people asked me how to create and script smart terrains. To avoid responding for each single email i decided to create this little tutorial. Before i start i would like to tell you that i'm not an expert in this kind of stuff and some things are still not clear for me so i might not be able to explain it properly. | Lately some people asked me how to create and script smart terrains. To avoid responding for each single email i decided to create this little tutorial. Before i start i would like to tell you that i'm not an expert in this kind of stuff and some things are still not clear for me so i might not be able to explain it properly. | ||
- | My | + | My English isn't perfect so please bear with me. Also i will use ST as Smart Terrain abbreviation. This tutorial treats about npc's STs but for mutants it's almost the same. I assume you are familiar with: |
- lua programming | - lua programming | ||
Line 11: | Line 11: | ||
1. Spawning ST: | 1. Spawning ST: | ||
- | + | <source lang="ini"> | |
- | + | [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 | ||
+ | </source> | ||
this is actually the most important stuff: | this is actually the most important stuff: | ||
- | + | <source lang="ini"> | |
- | + | type = esc_bandits_smart_terrain | |
- | + | cond = {-infoportion} | |
- | + | capacity = 3 | |
- | + | squad = 1 | |
- | | + | groups = 5 |
+ | respawn = esc_respawn_inventory_box_0002</source> | ||
+ | </source> | ||
'''type''' is a name of our new ST (required) | '''type''' is a name of our new ST (required) | ||
Line 61: | Line 65: | ||
2. Spawning npcs/mutants and assigning (binding) them to our ST: in this case for each mutant/npc we have to add to its logic: | 2. Spawning npcs/mutants and assigning (binding) them to our ST: in this case for each mutant/npc we have to add to its logic: | ||
- | + | <source lang="ini"> | |
- | + | custom_data = <<END | |
- | + | [smart_terrains] | |
- | + | esc_bandits_smart_terrain = true | |
+ | END | ||
+ | </source> | ||
3. Adding job (logic) for each npc/mutant from our ST (for each state). Let's say our ST have two states: state 0 (describes what npcs/mutants are doing during day) and state 1 (during night). We have 3 bandits, so say: | 3. Adding job (logic) for each npc/mutant from our ST (for each state). Let's say our ST have two states: state 0 (describes what npcs/mutants are doing during day) and state 1 (during night). We have 3 bandits, so say: | ||
Line 74: | Line 80: | ||
There are at least 3 ways to add logic (job) for each npc/mutant, we will use the most common one, i.e. we will add logic to config\misc\gulag_escape.ltx file. It may looks like this: | There are at least 3 ways to add logic (job) for each npc/mutant, we will use the most common one, i.e. we will add logic to config\misc\gulag_escape.ltx file. It may looks like this: | ||
- | + | <source lang="ini"> | |
- | + | ;-- bandit1 (walker -> state 0, i.e. during day) | |
- | + | [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 = no_meet | |
- | + | ||
- | + | ;-- bandit1 (kamp -> state 1, i.e. during night) | |
- | + | [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 -> state 0, i.e. during day) | |
- | + | [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 -> state 1, i.e. during night) | |
- | + | [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 -> state 0 and 1, i.e. during day/night) | |
- | + | [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 | ||
+ | </source> | ||
4. Now we have to script our ST. In this case we will add our code to scripts\gulag_escape.script file. There are several things we need to do here (each of this steps is required): | 4. Now we have to script our ST. In this case we will add our code to scripts\gulag_escape.script file. There are several things we need to do here (each of this steps is required): | ||
Line 127: | Line 135: | ||
- load logic (jobs) for each npc/mutant, for each state -> function load_job(...) | - load logic (jobs) for each npc/mutant, for each state -> function load_job(...) | ||
- | + | <source lang="lua"> | |
- | + | if type == "esc_bandits_smart_terrain" then | |
- | + | t = {} | |
- | + | ;-- section is a "link" to logic defined in ltx file | |
- | + | 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 | |
- | + | ;-- priority | |
- | + | t.prior = 100 | |
- | + | ;-- npc will use this logic if ST switched to this state | |
- | + | ;-- in this case - state 0 (day) | |
- | + | t.state = {0} | |
- | + | ;-- no idea about squad and group | |
- | + | t.squad = squad | |
- | + | t.group = groups[1] | |
- | + | ;-- no idea what means position_threshold | |
- | + | t.position_threshold = 100 | |
- | + | ;-- describes whether npc in this state is online or offline | |
- | + | ;-- online = true by default | |
- | + | t.online = true | |
- | + | ;-- describes restrictors (where npc can/can't go) | |
- | + | t.in_rest = "" | |
- | + | t.out_rest = "" | |
- | + | ;-- because of the way how jobs are assigning by | |
- | + | ;-- smart_terrain.script you never know which job | |
- | + | ;-- will be used by which npc; if you want to ensure | |
- | + | ;-- that certain job is used by certain npc then | |
- | + | ;-- you have to use predicate function; in this case | |
- | + | ;-- we want this job to be used by expert (master) bandit | |
- | + | t.predicate = function(obj_info) return obj_info.rank >= 900 end | |
- | + | table.insert(sj, t) | |
- | + | ||
- | + | 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 -> state 0 (day) | |
- | + | 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 -> state 1 (night) | |
- | + | 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 -> state 0 (day) and state 1 (night) | |
- | + | 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 | ||
+ | </source> | ||
One more thing about ST states, it's up to you how many states your ST have. The important thing is to add logic for each state. For instance your ST can have those states: | One more thing about ST states, it's up to you how many states your ST have. The important thing is to add logic for each state. For instance your ST can have those states: | ||
Line 196: | Line 206: | ||
And another thing, i'm sick when i have to fill so many tables, so i usually use this function: | And another thing, i'm sick when i have to fill so many tables, so i usually use this function: | ||
- | + | <source lang="lua"> | |
- | + | 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 | ||
+ | </source> | ||
So using above function, we can load logic like this: | So using above function, we can load logic like this: | ||
- | + | <source lang="lua"> | |
- | + | 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 | ||
+ | </source> | ||
- automatically change job for each npc/mutant -> function load_states(...) | - automatically change job for each npc/mutant -> function load_states(...) | ||
- | + | <source lang="lua"> | |
- | + | 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 -- switch all mutants/npc to daily job | |
- | + | else | |
- | + | return 1 -- switch all mutants/npc to nightly job | |
- | + | end | |
- | + | end | |
+ | end | ||
+ | </source> | ||
- ensure that our ST will be used only by bandits -> function checkStalker(...) | - ensure that our ST will be used only by bandits -> function checkStalker(...) | ||
- | + | <source lang="lua"> | |
- | + | if gulag_type == "esc_bandits_smart_terrain" then | |
- | + | return npc_community == "bandit" | |
+ | end | ||
+ | </source> | ||
+ | == Author == | ||
+ | |||
+ | [[Team:Dez0wave]] | ||
+ | |||
+ | ==Links== | ||
+ | Discussion: | ||
http://www.gsc-game.com/main.php?t=community&s=forums&s_game_type=xr&thm_page=77&thm_id=14712&sec_id=16&page=1#199726 | http://www.gsc-game.com/main.php?t=community&s=forums&s_game_type=xr&thm_page=77&thm_id=14712&sec_id=16&page=1#199726 | ||
+ | |||
+ | Check our tool for smart terrain debugging and waypoint exporting: | ||
+ | |||
+ | Download: http://sdk.stalker-game.com/en/images/d/d7/Smartterrain_and_Waypoint_Tools_by_dez0wave.zip | ||
+ | |||
+ | Mirror: http://ifolder.ru/7179080 | ||
[[Category:Articles]] | [[Category:Articles]] |
Current revision
Lately some people asked me how to create and script smart terrains. To avoid responding for each single email i decided to create this little tutorial. Before i start i would like to tell you that i'm not an expert in this kind of stuff and some things are still not clear for me so i might not be able to explain it properly. My English isn't perfect so please bear with me. Also i will use ST as Smart Terrain abbreviation. This tutorial treats about npc's STs but for mutants it's almost the same. I assume you are familiar with:
- lua programming - all.spawn editing - waypoints (patrols) - ai schemes
First you have to think about your ST, where to spawn it, how many npcs/mutants will "work" for your ST, when ST should be activated/terminated, how many states your ST will have and so on. I guess the best way would be to show you how to get it working by an example. So let's say we want to spawn ST in Cordon for 3 bandits.
1. Spawning 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
this is actually the most important stuff:
type = esc_bandits_smart_terrain cond = {-infoportion} capacity = 3 squad = 1 groups = 5 respawn = esc_respawn_inventory_box_0002
</source>
type is a name of our new ST (required) cond describes conditions which have to be met (optional) capacity ST npcs/mutants quantity i.e. how many npcs/mutants our ST can handle (required) squad, groups no idea what they are used for, maybe they are not used at all but i might be wrong (both are optional) respawn name of the stash (blue box) where items will be spawn when ST respawn will be called (optional)
2. Spawning npcs/mutants and assigning (binding) them to our ST: in this case for each mutant/npc we have to add to its logic:
custom_data = <<END [smart_terrains] esc_bandits_smart_terrain = true END
3. Adding job (logic) for each npc/mutant from our ST (for each state). Let's say our ST have two states: state 0 (describes what npcs/mutants are doing during day) and state 1 (during night). We have 3 bandits, so say:
- bandit1: walker (state 0) and kamp (state 1) - bandit2: guard (state 0) and sleeper (state 1) - bandit3: walker (state 0 and 1 <= he's doing the same at day and night)
There are at least 3 ways to add logic (job) for each npc/mutant, we will use the most common one, i.e. we will add logic to config\misc\gulag_escape.ltx file. It may looks like this:
;-- bandit1 (walker -> state 0, i.e. during day) [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 = no_meet ;-- bandit1 (kamp -> state 1, i.e. during night) [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 -> state 0, i.e. during day) [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 -> state 1, i.e. during night) [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 -> state 0 and 1, i.e. during day/night) [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. Now we have to script our ST. In this case we will add our code to scripts\gulag_escape.script file. There are several things we need to do here (each of this steps is required):
- load logic (jobs) for each npc/mutant, for each state -> function load_job(...)
if type == "esc_bandits_smart_terrain" then t = {} ;-- section is a "link" to logic defined in ltx file 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 ;-- priority t.prior = 100 ;-- npc will use this logic if ST switched to this state ;-- in this case - state 0 (day) t.state = {0} ;-- no idea about squad and group t.squad = squad t.group = groups[1] ;-- no idea what means position_threshold t.position_threshold = 100 ;-- describes whether npc in this state is online or offline ;-- online = true by default t.online = true ;-- describes restrictors (where npc can/can't go) t.in_rest = "" t.out_rest = "" ;-- because of the way how jobs are assigning by ;-- smart_terrain.script you never know which job ;-- will be used by which npc; if you want to ensure ;-- that certain job is used by certain npc then ;-- you have to use predicate function; in this case ;-- we want this job to be used by expert (master) bandit t.predicate = function(obj_info) return obj_info.rank >= 900 end table.insert(sj, t) 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 -> state 0 (day) 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 -> state 1 (night) 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 -> state 0 (day) and state 1 (night) 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
One more thing about ST states, it's up to you how many states your ST have. The important thing is to add logic for each state. For instance your ST can have those states:
0 - npcs are offline 1 - npcs are online (day) 2 - npcs are online (night) 3 - npcs are online, they decided to assault other ST 4 - npcs are online and actor attacks them
And another thing, i'm sick when i have to fill so many tables, so i usually use this function:
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
So using above function, we can load logic like this:
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
- automatically change job for each npc/mutant -> 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 -- switch all mutants/npc to daily job else return 1 -- switch all mutants/npc to nightly job end end end
- ensure that our ST will be used only by bandits -> function checkStalker(...)
if gulag_type == "esc_bandits_smart_terrain" then return npc_community == "bandit" end
Author
Links
Check our tool for smart terrain debugging and waypoint exporting:
Download: http://sdk.stalker-game.com/en/images/d/d7/Smartterrain_and_Waypoint_Tools_by_dez0wave.zip
Mirror: http://ifolder.ru/7179080