All.spawn : common data

Common data

There are some common fields in all spawn entities:
section_name = <string>
name of section defined in ltx file
name = <string>
entity name; should be unique if you are going to use entity name in scripts
position = <vector>
object spawn position
direction = <vector>
object spawn direction
game_vertex_id = <number>
game vertex id
level_vertex_id = <number>
level vexted id
object_flags = <hex_number>
object flags
visual_name = <string>
path to object mesh (*.ogf); extension can be skipped
health = <number>
object health


All.spawn : Stalkers and mutants

Stalkers and mutants

To spawn stalker use section

stalker

or

stalker_trader

. To spawn mutants, use proper mutant section name.
If you edit field which has 'update' version, set the same value for both fields. For instance if you want to change object spawn position, set the same value for 'position' and 'upd:position' fields!

If you want to add restrictors for certain npc, add them in smart terrain jobs. If npc doesn't have assigned smart terrain and depends on custom_data logic then add restrictors to fields 'base_in_restrictors' and 'base_out_restrictors'
money = <number>
amount of money which npc will have during trading; actually game is using money value from character's profile (xml)
character_profile = <string>
id of character's profile (xml)
upd:start_dialog = <string_list>
comma-separated list of character's dialogs; game is using dialogs from character's profile
visual_name = <string>
path to object's mesh; actually game will use visual defined in stalker's profile/mutants section
position, upd:position = <number>
object spawn position
direction = <number>
object spawn direction
distance = <number>
how far object can see enemies; bigger values for snipers
custom_data = <string>
object's logic
story_id = <number>
object story id
g_team, upd:g_team = <number>
object's team
g_squad, upd:g_squad = <number>
object's squad
g_group, upd:g_group = <number>
object's group
health, upd:health = <number>
object's health; to spawn corpse set

health = 0

and

upd:health = 0

base_out_restrictors = <string_list>
comma-separated list of out restrictors
base_in_restrictors = <string_list>
comma-separated list of in restrictors
dynamic_out_restrictions = <string_list>
dynamic_in_restrictions = <string_list>
upd:timestamp = <hex_number>
upd:creature_flags = <hex_number>
upd:o_model = <number>
upd:o_torso = <vector>
upd:next_game_vertex_id = 65535
upd:prev_game_vertex_id = 65535
upd:distance_from_point = 0
upd:distance_to_point = 0
Stalker example
[1459]
; cse_abstract properties
section_name = stalker
name = agr_tower1_soldier
position = -107.05786895752,7.38596820831299,-137.001205444336
direction = 0,0,0

; cse_alife_trader_abstract properties
money = 5000
character_profile = agr_soldier_sniper

; cse_alife_object properties
game_vertex_id = 696
distance = 6.29999971389771
level_vertex_id = 132866
object_flags = 0xffffffff
custom_data = «END
[logic]
active = camper
on_hit = hit
on_death = death

[death]
on_info = %+agr_sniper_killed_2%

[camper]
path_look = agr_tower1_look
path_walk = agr_tower1_stand
radius = 1
sniper = true
def_state_campering = threat_na
def_state_campering_fire = threat_fire

[hit]
on_info = %+agr_nii_sniper_hit%

[dont_spawn_character_supplies]

[spawn]
wpn_ak74
ammo_5.45x39_fmj = 1

[smart_terrains]
none = true
END

; cse_visual properties
visual_name = actors\soldier\soldier_bandana_6

; cse_alife_creature_abstract properties
g_team = 0
g_squad = 3
g_group = 7
health = 1
dynamic_out_restrictions =
dynamic_in_restrictions =

upd:health = 1
upd:timestamp = 0
upd:creature_flags = 0
upd:position = -107.05786895752,7.38596820831299,-137.001205444336
upd:o_model = 0
upd:o_torso = 0,0,0
upd:g_team = 0
upd:g_squad = 3
upd:g_group = 7

; cse_alife_monster_abstract properties
base_out_restrictors = agr_space_restrictor_tower1

upd:next_game_vertex_id = 65535
upd:prev_game_vertex_id = 65535
upd:distance_from_point = 0
upd:distance_to_point = 0

; cse_alife_human_abstract properties
predicate5 = 1,0,2,0,2
predicate4 = 0,2,1,2

; cse_ph_skeleton properties

upd:start_dialog =

; se_stalker properties
Mutant example
[90]
; cse_abstract properties
section_name = m_flesh_e
name = esc_suicidal_flesh
position = -267.955017089844,-10.9724931716919,-69.33203125
direction = 0,-0.00264087901450694,0.141791999340057

; cse_alife_object properties
game_vertex_id = 10
distance = 28.6999988555908
level_vertex_id = 3619
object_flags = 0xfffffff3
custom_data = «END
[logic]
active = mob_walker1
on_death = mob_death

[mob_death]
on_actor_in_zone = esc_suicidal_flash_restrictor| %+tutorial_flesh_die%

[mob_walker1]
braindead = true
path_walk = esc_tut_flesh_bush_walk
path_look = esc_tut_flesh_bush_look
on_info = {=mob_was_hit} mob_walker2 %+tutorial_flesh_start%
on_actor_in_zone = esc_suicidal_flash_restrictor | mob_walker2 %+tutorial_flesh_start%

[mob_walker2]
braindead = true
path_walk = esc_tut_flesh_anomaly_walk
path_look = esc_tut_flesh_anomaly_look
on_npc_in_zone = 029|esc_flesh_destroy|nil %=hit_npc(esc_hit_point:bip01_spine1:500:0) +tutorial_flesh_die%

END
story_id = 29

; cse_visual properties
visual_name = monsters\flesh\flesh

; cse_alife_creature_abstract properties
g_team = 12
g_squad = 1
g_group = 1
health = 0.550000011920929
dynamic_out_restrictions =
dynamic_in_restrictions =

upd:health = 0.550000011920929
upd:timestamp = 0
upd:creature_flags = 0
upd:position = -267.955017089844,-10.9724931716919,-69.33203125
upd:o_model = 0
upd:o_torso = -0.00264087901450694,0.141791999340057,0
upd:g_team = 12
upd:g_squad = 1
upd:g_group = 1

; cse_alife_monster_abstract properties

upd:next_game_vertex_id = 65535
upd:prev_game_vertex_id = 65535
upd:distance_from_point = 0
upd:distance_to_point = 0

; cse_ph_skeleton properties

; cse_alife_monster_base properties

; se_monster properties


All.spawn : space restrictors

Space restrictors

Space restrictors are special areas used for perfoming certain actions (space restrictors schemes) when actor is inside the restrictor. Each restrictor has:
  • its type (0, 2, 3)
  • shape (box, sphere)
  • custom_data (optional) field
[86]
; cse_abstract properties
section_name = space_restrictor
name = trader_zone
position = -251.190521240234,-22.776123046875,-134.453689575195
direction = 0,8.58306884765625e-006,0

; cse_alife_object properties
game_vertex_id = 0
distance = 0
level_vertex_id = 594270
object_flags = 0xffffffbe
custom_data = «END
[logic]
active = sr_no_weapon

[sr_no_weapon]
END

; cse_shape properties
shapes = shape0
shape0:type = box
shape0:axis_x = 14.8584299087524,0,0
shape0:axis_y = 0,4.67763805389404,0
shape0:axis_z = 0,0,4.59808111190796
shape0:offset = 0,0,0

; cse_alife_space_restrictor properties
restrictor_type = 3


All.spawn : Anomalies

Anomalies

To spawn anomalies, use proper section names defined in

config\misc\zone_*.ltx

. To create moving anomalies (like in Agroprom underground or Cordon tunnel) use

enabled_time, disabled_time, start_time_shift

.
max_power = <number>
anomaly power; if

max_power = 0

then power is taken from ltx file
enabled_time = <number>
how long anomaly will move (in seconds). After this time, anomaly will move to its original spawn position and (switched offline) will wait

disabled_time

seconds for next movement
disabled_time = <number>
how long anomaly will wait before move (in seconds)
start_time_shift = <number>
how much time (in seconds) anomaly have to wait before movement - use only in anomaly chain (see below)
offline_interactive_radius = <number>
radius in offline (?)
artefact_spawn_count = <number>
max number of artefacts to spawn (?)
artefact_position_offset = <hex_number>
artefact spawn position (?)
Anomaly movement

You can create a chain of moving anomalies however the place should be carefully choosen because game is calculating positions for moving anomalies. It works like this: anomaly wait certain amount of time for activation (

start_time_shift

). When anomaly is activated it moves during amount of time specified in

enabled_time

field. After that anomaly is switched offline and moved to its spawn position. Then the anomaly waits

disabled_time

seconds for next movement -

start_time_shift

.

Example: we have 4 anomalies, they create a chain:
anomaly:            1st  2nd  3rd  4th
enabled_time:        5    3    4    6
disabled_time:      13   10    6    4
start_time_shift:    0    5    8   12
First anomaly will move for 5 seconds, after that it will be disabled for 13 seconds. Right after first anomaly, the second one will be enabled for 3 seconds and then disabled for 15 seconds (start_time_shift + disabled_time). Then third anomaly, which was disabled for 8 seconds, will be active for 4 seconds and after that - will be disabled for 14 seconds. Last anomaly will move for 6 seconds and then will wait 16 seconds for next activation. Now, 13 seconds passed so the first anomaly will be activated again and the whole cycle will be repeated.

Example:
[263]
; cse_abstract properties
section_name = zone_witches_galantine
name = esc_zone_witches_galantine
position = -78.4358367919922,-1.29666185379028,126.609664916992
direction = 0,0,0

; cse_alife_object properties
game_vertex_id = 163
distance = 28
level_vertex_id = 190900
object_flags = 0xffffff3e

; cse_shape properties
shapes = shape0
shape0:type = sphere
shape0:offset = 0,0,0
shape0:radius = 4.90300559997559

; cse_alife_space_restrictor properties
restrictor_type = 2

; cse_alife_custom_zone properties
max_power = 0
enabled_time = 9
disabled_time = 15

; cse_alife_anomalous_zone properties
offline_interactive_radius = 30
artefact_spawn_count = 32
artefact_position_offset = 0x6a0

; se_zone_anom properties


All.spawn : Anomaly zones

Anomaly zone

Anomaly zone is a space restrictor which covers certain area and contains anomalies. Artifact is jumping inside, following specified path. Upon entrance, actor is instantly hit by force specified in zone settings, special post process effect is displayed and sound is muted (vacuum-like effect :) ).

If actor visits anomaly zone for a first time, infoportion is given (if specified in anomaly settings), so we can use it for some stats or achievements. Each anomaly zone is updated in both: on-line and off-line states, so no matter where actor is, anomaly zone is updated (to calculate probability to spawn artifacts, spawn artifacts during blowout, etc).

To spawn anomaly zone, select

spawns\lost alpha\anomaly_zone

and give it some unique name. This name is then needed for setting zone parameters. Open file

config\lost_alpha\anom_zone\anom_fields.ltx

and create new section with name you gave to spawned anomaly zone and specify parameters.

Anomaly zone parameters:
spawn_af_on_zone_spawn
whether spawn artifact when anomaly zone is being spawned; if this field is set to

true

, then artifact will be spawned when anomaly zone is spawned, that is – when new game is created (so this is a one-time-only spawn); you can define artifact type in field

af_on_zone_spawn

; default value is

false

, so no artifacts will be spawned on anomaly zone spawn
af_on_zone_spawn
section name of artifact to spawn when anomaly zone is spawned; this field is utilized only if

spawn_af_on_zone_spawn

is set to

true

; default value is

af_medusa

, that is - jellyfish
idle_time
time to spawn artifacts (in game minutes); after every

idle_time

minutes script will test conditions to spawn artifacts and depending on result artifacts will be spawned or not; default value is

10

minutes
cond
condition list to spawn artifacts; each condition has to end with probability value; you can use value

0

(min) to disable artifact spawning or

100

(max) which means that after every

idle_time

minutes artifact will be spawned (artifact can be spawned only if there’s no artifact already in zone); default value is

nil

which evaluates to

0

, that is – artifacts won’t be spawned at all
on_blowout
whether spawn artifacts after blowout or not; available values are:

always

– spawn artifact after each blowout,

never

– do not spawn artifacts after blowout or

use_cond

– use condition list specified in cond field to calculate whether anomaly zone can spawn artifacts after blowout; default value is

never

hit_type
type of hit which will be used to harm actor inside anomaly zone; available values are:

chemical_burn, burn, radiation, shock, strike, telepathic, wound;

default value is

radiation

postprocess
post process effect which will be displayed when actor enters anomaly zone; it can be either existing *.ppe file (good vanilla post process effects are:

dead_zone.ppe, psy_antenna.ppe, psy_antenna_indoor.ppe, radiation.ppe

) or custom scripted post process (read below); if no post process is given (default value), then anomaly zone won’t display any post process effect when actor is inside zone
npc_accessible
Boolean value which determines whether NPCs can search this anomaly zone for artifacts; thanks to this we can block some anomaly zones so NPCs won’t enter them; default value is

false

, which means that NPC won’t enter this anomaly zone
actor_first_visit
infoportion which will be given when actor visit this anomaly zone for a first time; this can be used for stat or achievements purposes; by default, if no infoportion is given, then script won’t give any infoportions
artefacts
comma separated list with artifact section names and probability values; this defines what types of artifacts can be spawned by anomaly zone based on given probability values; this field is necessary, after all this is main purpose of anomaly zone – to spawn artifacts, so if you don’t want this anomaly zone to spawn artifacts, you still have to specify at least one artifact here and then you can block spawning with cond field
af_paths
artifact jumping path, that is waypoints names; you can specify more then one waypoint name (separated with comma) and then script will randomly pick one of paths (gives more randomness); this field is necessary – same reasons as with

artefacts

field
af_jump_speed
speed coefficient for artifact which determines how fast artifact will jump; default value is

400

spawn_anomalies
Boolean value which determines whether anomaly zone should spawn anomalies or not; in other words, if this field is set to

true

then anomalies will be spawned via script; default value is

false

and to be honest, using level editor you have more options to spawn anomalies (for instance adjusting anomalies shape, offset and radius)
anom_pos
name of waypoint which holds spawn positions for anomalies; this is usable only if you set

spawn_anomalies

field to

true

anoms
comma separated list with anomaly names and quantities; this determines what anomalies will be spawned; utilized only if

spawn_anomalies

field is set to

true

Example – if you spawned anomaly zone called test_esc_anom_zone:
[test_esc_anom_zone]
spawn_af_on_zone_spawn = true
af_on_zone_spawn = af_cristall_flower
spawn_anomalies = true
anom_pos = esc_anom_zone_test_anompos
idle_time = 5
cond = 100
on_blowout = always
npc_accessible = true
af_jump_speed = 600
artefacts = af_medusa, 6, af_cristall_flower, 5
af_paths = esc_anom_zone_test_afpath
postprocess = fallout_light
hit_type = shock
actor_first_visit = barin_anim_test

Scripted post process effects

You can use already defined effects or add new ones (in that case add effect name to

all

section) – check file

config\lost_alpha\anom_zone\anom_zone_pp.ltx.

Each effect is defined as:
[effector_name]
color_gray = R, G, B [0.0-0.5, 0.0-0.5, 0.0-0.5]
color_base = R, G, B [0.0-1.0, 0.0-1.0, 0.0-1.0]
color_add = R, G, B [0.0-1.0, 0.0-1.0, 0.0-1.0]
noise = grain, intensity, fps [0.0-1.0, 0.0-1.0, 1-100]
dual = horizontal, vertical [0.0-1.0, 0.0-1.0]
gray = gray [0.0-1.0]
blur = blur [0.0-1.0]
You can use

ui_pp_test.script

to create new effects:

Attaching anomalies to anomaly zone – only for level editor

If you are spawning anomalies in level editor, then first spawn anomaly zone, give it some shape, radius, unique name and edit zone parameters (as described above). Then spawn anomalies, for each anomaly edit its

custom_data

field and add anomaly zone name. For instance, if you spawned anomaly zone called esc_anom_zone then every anomaly which belongs to this anomaly zone should have this custom data:
[anom_zone]
belongs_to = esc_anom_zone
This is required for script which needs to know what anomalies belongs to anomaly zone (because script needs to change anomaly restrictor type, so NPCs can evade those anomalies). If you decided to use script to spawn anomalies (field

spawn_anomalies

in anomaly zone settings) then script will automatically adjust

custom_data

field for those anomalies.

Details of implenting this scheme and old version of tutorial can be found here : anomaly fields


All.spawn : blowout and shelters

Blowout – configuration blowout and shelters

Blowout configuration can be found in

config\lost_alpha\blowout

folder. File

config.ltx

contains section with main configuration called

blowout

. Keep in mind that most of these parameters are global, that is setting for instance

kill_birds

field to false, will override level specific settings, even if you define

kill_birds

as

true

in level settings.

Here’s description of all parameters:

  • enabled

    – whether blowout is enabled (

    true

    ) or disabled (

    false

    )

  • interval

    – measured in game hours, defines blowout interval; basically if blowout is enabled then after every

    interval

    hours, script will check conditions and decide whether blowout can be started or not – blowout won’t happen every

    interval

    hours because it depends on conditions; condition can be set for each level separately, plus it also calculates whether actor is sleeping – this fixes problem with odd visual weather effects if player is sleeping and blowout occurs – so in other words, blowout won’t start if actor is sleeping and in that way, timer will be reset to 1 hour (which prolongs next blowout for 1 hour)

  • show_blowout_timer

    – whether show blowout timer on HUD (

    true

    ) or not (

    false

    )

  • timer_type

    – type of HUD timer – will be displayed only if

    show_blowout_timer

    field is set to

    true;

    available values are either

    timer_hud

    or

    hud_timer_text

    – difference is that one of them is displaying only blinking text (

    hud_timer_text

    ) while the other (

    timer_hud

    ) displays also frame image with clock icon and blinking text

  • show_blowout_warning

    – whether show blowout warning message on HUD (

    true

    ) or not (

    false

    )

  • show_hideplaces

    – whether show blowout shelters on map (

    true

    ) or not (

    false

    )

  • spawn_artefacts

    – whether blowout will spawn artifacts (

    true

    ) or not (

    false

    ). Artifacts are actually spawned by anomaly zone script which also calculates conditions to spawn artifacts so setting spawn_artefacts to

    true

    does not necessarily means that each anomaly zone will spawn artifacts

  • spawn mutants

    – whether spawn mutants (

    true

    ) or not (

    false

    ); what types of mutants can be spawned is defined in

    config\lost_alpha\blowout\mutants.ltx

    for each level (sections), you can use inheritance between sections but be sure to override (by setting quantity value to 0) mutants you don’t want to spawn on certain levels; spawn positions are defined in file

    config\lost_alpha\blowout\spawn_data.ltx

  • spawn_phantoms

    – whether spawn phantoms (

    true

    ) or not (

    false

    )

  • kill_birds

    – whether kill the birds during blowout (

    true

    ) or not (

    false

    )

    • Defining blowout behavior for each level

    You can define settings for each level separately, just edit level settings in

    config\lost_alpha\blowout\config.ltx

    file. Here’s a list of parameters you can adjust:

  • radiation_strength

    – how lethal radiation wave will be; use float value, min is 0.0 (which means: no harm at all); radiation affects only actor; default value is

    5.0

  • spawn_artefacts

    – whether spawn artifacts (

    true

    ) or not (

    false

    ); if global value (

    blowout

    section) of

    spawn_artefacts

    is

    false

    , then artifacts won’t be spawned no matter what value you set for

    spawn_artefacts

    in level setting section; in case of underground levels, if it doesn’t have any anomaly zones which would eventually spawn artifacts, you can set

    false

    value for

    spawn_artefacts

  • spawn_mutants

    – whether spawn mutants (

    true

    ) or not (

    false

    ); if global value (

    blowout

    section) of spawn_mutants is

    false

    , then mutants won’t be spawned no matter what value you set for

    spawn_mutants

    in level setting section

  • spawn_phantoms

    – whether spawn phantoms (

    true

    ) or not (

    false

    ); if global value (

    blowout

    section) of

    spawn_phantoms

    is

    false

    , then phantoms won’t be spawned no matter what value you set for

    spawn_phantoms

    in level setting section

  • kill_birds

    – whether kill birds (

    true

    ) or not (

    false

    ); if global value (

    blowout

    section) of

    kill_birds

    is

    false

    , then crows won’t be killed no matter what value you set for

    kill_birds

    in level setting section; for underground levels this field is set to

    false

    by script

  • condition

    – condition list which defines conditions for blowout; value

    never

    can be set to disable blowout on certain level

    • Blowout shelters

    This is basically a space restrictor which defines safety areas for actor and NPCs. To spawn, select

    spawns\lost alpha\blowout_shelter

    and set some unique name for spawned shelter. Name has to be unique because it is also used in shelter definition. Once spawned, open

    config\lost_alpha\blowout\shelter.ltx

    file and add new section using name you gave to spawned shelter. This is important because if section with the same name as shelter restrictor cannot be find then shelter won’t work.

    Each shelter contains fields:

  • factions

    – comma separated list of faction names; this defines what factions will be accepted by shelter (who can hide in this shelter); actor is not supported here because he can hide where ever he wants. You can use const value

    all

    and in that case shelter will accept any community. Each level should contain one or more shelters for each faction which is residing on that level, plus extra shelters which can accept any faction (for situations when some faction member which is not allowed on that level is passing through to next level and blowout occurs, or if other shelters are overcrowded). You can also define one shelter for allied factions, like for instance Duty + Stalkers etc. Available factions:

    stalker, bandit, killer, ecolog, military, dolg, freedom, monolith, dark_stalker

    or const value

    all

    (all factions allowed)

  • capacity

    – number of NPCs shelter can accept; this depend on how big shelter is and how many places are there to accommodate NPCs

  • pos_X

    – (

    X

    is position number, not greater then

    capacity

    ) – name of the waypoint which contains 2 points: point 0 is position inside shelter (which NPC will occupy during blowout) and point 1 specifies look position. You can add animation flag to any point and that animation will be used for NPC during blowout. If no animation flag is given then script will randomly select one of predefined animations. Available animations:

    wait, stand_wait, guard, ward, sit, sit_knee, sit_ass.

Here’s an example of position waypoint:
[esc_village_basement1_pos1]
points = p0,p1
p0:name = name00|a=stand_wait
p0:position = -148.761138916016,-30.1068019866943,-358.168518066406
p0:game_vertex_id = 17
p0:level_vertex_id = 100794
p0:links = p1(1)
p1:name = name01
p1:position = -147.65705871582,-29.7977733612061,-371.756530761719
p1:game_vertex_id = 15
p1:level_vertex_id = 102976
Shelter section example (assuming you spawned shelter restrictor called esc_village_basement1):
[esc_village_basement1]
factions = stalker, dolg
capacity = 4
pos_1 = esc_village_basement1_pos1
pos_2 = esc_village_basement1_pos2
pos_3 = esc_village_basement1_pos3
pos_4 = esc_village_basement1_pos4


All.spawn : Projectors

Projectors

To spawn static projector use section

search_light

. To spawn dynamic projectors set ph_projector scheme.

Static projector:
[5373]
; cse_abstract properties
section_name = search_light
name = mil_search_light
position = -337.226257324219,-10.8047227859497,389.056121826172
direction = -0.0882880091667175,-1.7024759054184,-0.0493709892034531

; cse_alife_object properties
game_vertex_id = 1545
distance = 0
level_vertex_id = 22
object_flags = 0xffffff3a

; cse_visual properties
visual_name = equipments\projector
Dynamic projector:
[123]
; cse_abstract properties
section_name = search_light
name = esc_projector_0001
position = -162.704177856445,-24.1463871002197,-349.41064453125
direction = -0.0882880091667175,-0.302475899457932,-0.0493709892034531

; cse_alife_object properties
game_vertex_id = 31
distance = 0
level_vertex_id = 86409
object_flags = 0xffffff3a
custom_data = «END
[ph_projector]
path_look = esc_projector_look
rnd_wp = false
duration = 15000
rot_hspeed = 0.52
rot_vspeed = 0.22
END

; cse_visual properties
visual_name = equipments\projector


All.spawn : Lamps

Lamps

We have light manager scheme which can operate with phy handling lamps, more additional info here ph_light_mgr.


All.spawn : physical objects

Physical Objects and destroyable objects

Along with common data, physical objects contains fields:

  • collide

    - for collision with objects

  • fixed_bones = <bone_name>

    - responsible to stick the object to the ground

  • physic_type = 0x3

    - object type

  • mass = <number>

    - object mass
[65]
; cse_abstract properties
section_name = physic_object
name = trader_pribor_0002
position = -245.461166381836,-23.9442577362061,-133.349502563477
direction = 0,-0.0872577428817749,0

; cse_alife_object properties
game_vertex_id = 0
distance = 0
level_vertex_id = 594270
object_flags = 0xfffffffa
custom_data = «END
[collide]
ignore_static
END

; cse_visual properties
visual_name = physics\decor\priemnik_gorizont

; cse_ph_skeleton properties
skeleton_name =

; cse_alife_object_physic properties
physic_type = 0x3
mass = 10
fixed_bones = link

; -------------------------------------------------------------------------
; destroyable object
; -------------------------------------------------------------------------

[189]
; cse_abstract properties
section_name = physic_destroyable_object
name = esc_matugalnik
position = -141.698059082031,-22.8454513549805,-353.901885986328
direction = 0.0872493088245392,3.1415901184082,-9.62032299867133e-006

; cse_alife_object properties
game_vertex_id = 0
distance = 0
level_vertex_id = 594270
object_flags = 0xffffff3a
custom_data = «END
[logic]
active = ph_sound@humor
on_hit = hit

[ph_sound@humor]
snd = humor
on_info = {+esc_matugalnik_off} nil

[hit]
on_info = %+esc_matugalnik_off%
END
story_id = 18

; cse_visual properties
visual_name = physics\decor\rupor

; cse_ph_skeleton properties

; cse_alife_object_physic properties
physic_type = 0x3
mass = 10


All.spawn : Cameras

Camera

Camera is moving (rotating) left and right, eventually stopping on each end point and waiting specified amount of time (pause). During rotation, camera is detecting objects which appears inside camera’s visibility area (called camera zone). Default algorithm for detecting objects visible by camera lens (default GSC script) is not working well in Stalker as it is possible that camera can spot objects hiding behind camera (how the f*ck is that possible is beyond me, I have tried everything and it’s still buggy …). Algorithm is based on creating a scene (camera zone) where camera can see objects. This is done by calculating FOV angles and distance (D) from lens, so basically any object inside scene built by FOV angles and in certain distance from camera lens is visible by camera. http://en.wikipedia.org/wiki/Angle_of_view
To fix enemy detection, I’ve added a space restrictor (camera zone) bound to camera. So algorithm works like that: if enemy is inside any camera zone bound to camera (can be more then one) then script builds camera scene (FOV, object – lens distance) and if object is inside camera scene then it can be spotted. This isn’t a perfect solution, however at least it works and besides, using camera zones it is easier to specify where camera can spot enemies (useful for places with many obstacles).

Project files

- sounds\camera\beep.ogg – beeping sound if camera spot an enemy
- sounds\camera\move.ogg – rotation sound
- meshes\equipment\camera_cover.ogf – hack for hit callbacks (read below)
- config\scripts\camera.ltx – camera logic sections (*)
- config\scripts\camera_cover.ltx – wooden box logic (for hit callback hack) (*)
- spawns\all.spawn – spawn sections for camera and camera zone
- config\camera.ltx – sections for camera and camera zone (**)
- scripts: ph_camera, bind_camera_zone, modules, xr_effects

* logic can be defined in all.spawn, although it is easier to put logic in external files – we don’t have to recompile all.spawn each time we edit camera’s logic
** use level editor instead – menu spawns\lost alpha\camera for camera and spawns\lost alpha\camera_zone for camera zone

Spawning

Camera has to be spawned as

camera

section (O_SEARCH class, same as projector) so it can rotate left – right and up – down. To spawn camera, select

spawns\lost alpha\camera

and edit logic – preferably specify a path to ltx file with camera logic (external ltx file is easier to edit later on – no need to recompile all.spawn). Be sure to change camera name to some unique name. This is very important because of hack for camera hit callbacks. Here’s an example of spawned camera (important parts are highlighted, camera.ltx with camera logic is described in section camera logic settings):
[852]
; cse_abstract properties
section_name = camera
name = temp_camera
position = -146.399993896484,-27.5,-363
direction = 0,89.5,0
; cse_alife_object properties
game_vertex_id = 31
distance = 0
level_vertex_id = 86409
object_flags = 0xffffff3a
custom_data = «END
[logic]
cfg = scripts\camera.ltx
END
; cse_visual properties
visual_name = equipments\camera
Then spawn camera cover – this is a hack for camera hit callbacks. Basically, if we spawn camera as O_SEARCH class object, it can rotate but cannot receive hit/death callbacks. If we spawn camera as physic_destroyable_object it can receive callbacks but cannot rotate. To fix this we use camera cover spawned as physic_destroyable_object. This is a wooden crate (box) with invisible shadders which covers the whole camera. Idea is this: if user is shooting towards camera, all the bullets will hit that wooden box which then will receive hit callback. Using this callback camera will be deactivated (explodes). This is why camera must have unique name because its name has to be used in camera cover’s logic.

Here’s an example of camera cover (important parts are highlighted):
[853]
; cse_abstract properties
section_name = physic_destroyable_object
name = tmp_box_on_camera
position = -147.399993896484,-27.2000007629395,-363
direction = 1.60000002384186,0,1.60000002384186
; cse_alife_object properties
game_vertex_id = 31
distance = 0
level_vertex_id = 86409
object_flags = 0xffffff3a
custom_data = «END
[logic]
cfg = scripts\camera_cover.ltx
END
; cse_visual properties
visual_name = equipments\camera_cover
; cse_ph_skeleton properties
skeleton_flags = 1
; cse_alife_object_physic properties
physic_type = 0x3
mass = 10
fixed_bones = link
Just make sure that you spawn camera cover as physic_destroyable_object (so it can receive hit/death callbacks) and that camera is covered by this camera cover box, also that it has fixed bones specified (so it can float in the air).

Now take a look at camera cover logic:
[logic]
active = ph_idle
on_hit = hit

[hit]
on_info = %=camera_got_hit(temp_camera)%

[ph_idle]
So when camera cover receive hit callback, function camera_got_hit() will be called. As an argument, specify camera name (this is why camera name has to be unique). We can eventually use story ids if using names is problematic (just let me know).

Now, since we have spawned camera and camera cover it’s time to spawn camera zones and add logic for camera. First let’s handle camera zones. Those are simple space restrictors, so you can use any shape you see fit to define zones where camera can see enemies. You can even define several camera zones for one camera. Just remember that camera zone names must be unique because they are needed in camera logic.

To spawn camera zone select

spawns\lost alpha\camera_zone.

Here’s an example of camera zone (important parts are highlighted):

[8625]
; cse_abstract properties
section_name = camera_zone
name = esc_cam_zone_01
position = -152.985061645508,-29.9456653594971,-363.080261230469
direction = 0,0,0
; cse_alife_object properties
game_vertex_id = 33
distance = 0
level_vertex_id = 96336
object_flags = 0xffffff3e
; cse_shape properties
shapes = shape0
shape0:type = sphere
shape0:offset = 0,0,0
shape0:radius = 4
; cse_alife_space_restrictor properties
restrictor_type = 3

Camera logic settings


Safe

Just an ordinary safe protected with code and containing some items. To spawn it, select menu

spawns\lost alpha\safe_object

and set some nice safe object as visual mesh. Then add logic with parameters. Here’s an example of spawned safe – just remember to change visual object and maybe add

fixed_bones

(after all, safe should be rather heavy =) )
[852]
; cse_abstract properties
section_name = safe_object
name = box_safes
position = -155.401016235352,-29.1165771484375,-358.429992675781
direction = 0,0,0
; cse_alife_object properties
game_vertex_id = 33
distance = 0
level_vertex_id = 94107
object_flags = 0xfffffffa
custom_data = «END
[collide]
ignore_static
[logic]
cfg = scripts\esc_safe.ltx
END
; cse_visual properties
visual_name = physics\box\expl_dinamit
; cse_ph_skeleton properties
skeleton_name = idle
; cse_alife_object_physic properties
physic_type = 0x3
mass = 10

Logic parameters:

  • code – comma separated digit values, that’s the code to unlock safe
  • items – comma separated list of pairs: item section name, quantity; those are items which will be
    • placed initially in safe; if you omit quantity value for some item – value 1 will be assumed, but better define all values.
Here’s an example of safe logic:
[logic]
active = ph_safe

[ph_safe]
code = 1, 2, 3
items = medkit, 3, bread, 1


All.spawn : respawners

Respawners

Respawners are used for spawning mutants/npcs and items. To spawn respawner you have to use

respawn

as section name and provide custom_data field with

respawn

section. There are two types of respawners used in game.

Alife respawners

Used for spawning mutants and npcs. Alife respawners are updated in offline and online mode. Respawners are enabled only if (all below conditions have to be met):
  • respawn probability is grater than 0 - that is when respawn conditions are met
  • respawn idle time elapsed (

    idle_time

    field)

  • max_spawn

    value wasn't exceeded, that is respawner spawned less objects than it is defined in

    max_spawn

    field
  • distance between actor and respawner is greater than value of

    respawn_radius

    field. This applies only to online respawners. This means you can force respawner to be enabled only in offline mode by setting

    respawn_radius

    to

    -1

- alife respawners will spawn npcs and mutants if all conditions are met, however if there is no smart terrain with available task for spawned npc/mutant, then spawned npc/mutant will be released

- when mutant/npc is spawned then its id is saved, if spawned mutant/npc dies or se_respawn.script cannot localize it then its id is removed thus respawner will always be available (unless conditions won't be met)
[345]
; cse_abstract properties
section_name = respawn
name = esc2_respawn_bandits_fabrika
position = 301.152526855469,16.7477245330811,24.7992668151855
direction = 0,0,-1.07722399889099e-007

; cse_alife_object properties
game_vertex_id = 139
distance = 19.6000003814697
level_vertex_id = 565014
object_flags = 0xffffff3e
custom_data = «END
[respawn]
respawn_section = esc_bandit_respawn_2, 12, esc_bandit_respawn_1, 9
max_spawn = 6
idle_spawn = medium
conditions = {+esc_kill_bandits_quest_kill} 80, 0
END

; cse_shape properties
shapes = shape0
shape0:type = sphere
shape0:offset = 0,0,0
shape0:radius = 1

; cse_alife_space_restrictor properties
restrictor_type = 0

; se_respawn properties
respawn_section = <list: string, number>
list with section names and spawn probabilities; if probability value isn't specified then value

1

is set; section names are defined in file

config\creatures\spawn_sections.ltx

max_spawn = <number> { default: 1 }
how many npcs/mutants respawner can spawn; if respawner spawned

max_spawn

npcs/mutants then it will be disabled and enabled again when spawned mutants/npcs dies or se_respawn script won't be able to localize them
max_count = <number> { default: -1 }
if

max_count

value is different than

-1

then respawner will spawn items until it exceed

max_count

value
min_count = <number> { default: 0 }
if

min_count

value is greater than 0 then respawner will ignore

max_spawn

and will spawn npcs/mutants

min_count

times
idle_spawn = <number, number> or <const> : seldom | medium | often
time which have to elapse to spawn npcs/mutants. This field affects only online respawners
item_spawn = <boolean> { default: false }
this field is designed for item respawners; either do not use that field or set it to

false

conditions = <condition_list> { default: 100 }
conditions which have to be met to enable respawner; use numbers as sections which will be used as probability value, such as:
; if actor has 'detector' then respawner is enabled (100%)
; no 'detector' but actor has 'infoportion' - respawned enabled in 80%
; no 'detector' and no 'infoportion' - respawner disabled

conditions = {=actor_has_item(decoder)} 100, {+infoportion} 80, 0
respawn_radius = <number> { default: -1 }
distance between actor and respawner; npcs/mutants will be spawned only if actor is further than defined radius. This field affects only online respawners. By setting

respawn_radius = -1

you force respawner to spawn alife only in offline mode

Item respawners

Used for spawning items in simulation boxes (stashes). This type of respawners are called by smart terrains, that is if you want to set respawner for certain simulation box, you have to specify field

respawn

(with name of simulation box) in section

smart_terrain

Item respawners are called by smart terrains before npcs start performing smart terrain's tasks. If smart terrain has 5 npcs and they start working then smart terrain will call assigned respawn 5 times which means that all the items which suppose to be spawned by respawner will be spawned pretty fast.

Usually if smart terrain contains at least 5 npcs then all the items will be spawned at once. This means that respawner won't be enabled for npcs which will start working for smart terrain later (when smart terrain will be empty and then refilled by npcs). To prevent this you can use set conditions for respawner.
; -------------------------------------------
; smart terrain with 'respawn' field
; -------------------------------------------

[101]
; cse_abstract properties
section_name = smart_terrain
name = esc_lager
position = -208.551208496094,-20.0917911529541,-141.64958190918
direction = 0.062321275472641,0.00316426996141672,0.0140644172206521

; cse_alife_object properties
game_vertex_id = 61
distance = 4.19999980926514
level_vertex_id = 44622
object_flags = 0xffffffbe
custom_data = «END
[smart_terrain]
type = esc_lager
capacity = 13
squad = 1
groups = 5
respawn = esc_respawn_inventory_box_0000
END

; cse_shape properties
shapes = shape0
shape0:type = sphere
shape0:offset = 0,0,0
shape0:radius = 4.5480751991272

; cse_alife_space_restrictor properties
restrictor_type = 3

; se_smart_terrain properties

; -------------------------------------------
; respawn for 'esc_lager' smart terrain
; -------------------------------------------

[546]
; cse_abstract properties
section_name = respawn
name = esc_respawn_inventory_box_0000
position = -211.399993896484,-20.020471572876,-142.100006103516
direction = 0.0341970697045326,0.35481470823288,-0.0126125095412135

; cse_alife_object properties
game_vertex_id = 61
distance = 2.09999990463257
level_vertex_id = 41938
object_flags = 0xffffff3e
custom_data = «END
[respawn]
respawn_section = wpn_pm,2,vodka,2,medkit,1,energy_drink,2,ammo_9x18_fmj,3
idle_spawn = -1
parent = 2000
item_spawn = true
max_count = 7
END

; cse_shape properties
shapes = shape0
shape0:type = box
shape0:axis_x = 0.560799837112427,0,0
shape0:axis_y = 0,0.697599709033966,0
shape0:axis_z = 0,0,1.15233147144318
shape0:offset = 0,0,0

; cse_alife_space_restrictor properties
restrictor_type = 3

; se_respawn properties

; -------------------------------------------
; simulation box to spawn items from respawn
; -------------------------------------------

[653]
; cse_abstract properties
section_name = inventory_box
name = level_prefix_inventory_box
position = -212.106658935547,-20.0091133117676,-141.916839599609
direction = -0.0120084341615438,1.90779304504395,-0.0344137884676456

; cse_alife_object properties
game_vertex_id = 0
distance = 0
level_vertex_id = 594270
object_flags = 0xffffff3f
story_id = 2000

; cse_visual properties
visual_name = physics\equipment_cache\equipment_box_01_case
respawn_section = <list: string, number>
list with item names (sections) and spawn probabilities; if probability value isn't specified then value

1

is set
max_count = <number> { default: -1 }
how many times respawner will spawn items specified in

respawn_section

. Default value (

-1

) will cause respawner to spawn items each time the npc start working for gulag (unless

max_spawn

will be set to 0 or negative value), so make sure you set this field to positive value
min_count = <number> { default: 0 }
this field is not used in case of item respawners (unless you edit

idle_spawn

and

respawn_radius

)
max_spawn = <number> { default: 1 }
if you set this field to value equal or less then 0 then respawner won't spawn items. If you set value greater then 1 and

max_count

value to negative value then respawner will spawn too much items. It's better to leave the default value for this field
idle_spawn = <number, number> or <const> : seldom | medium | often
for item respawners this field have to be set to

-1

item_spawn = <boolean> { default: false }
for item respawners this field have to be set to

true

parent = <number>
story id of simulation box
respawn_radius = <number> { default: -1 }
for item respawners it's better to not use this field; either skip it or set to

-1

conditions = <condition_list> { default: 100 }
conditions which have to be met to enable respawner


All.spawn : smart terrains

Smart terrains

To spawn smart terrain use

smart_terrain

as section name, fill smart terrain's shape and custom data. More about smart terrains you may find here
[101]
; cse_abstract properties
section_name = smart_terrain
name = esc_lager
position = -208.551208496094,-20.0917911529541,-141.64958190918
direction = 0.062321275472641,0.00316426996141672,0.0140644172206521

; cse_alife_object properties
game_vertex_id = 61
distance = 4.19999980926514
level_vertex_id = 44622
object_flags = 0xffffffbe
custom_data = «END
[smart_terrain]
type = esc_lager
capacity = 13
squad = 1
groups = 5
respawn = esc_respawn_inventory_box_0000
END

; cse_shape properties
shapes = shape0
shape0:type = sphere
shape0:offset = 0,0,0
shape0:radius = 4.5480751991272

; cse_alife_space_restrictor properties
restrictor_type = 3

; se_smart_terrain properties


All.spawn : Level changers

Level changers

To spawn level changer use section

level_changer

. Edit position, story_id, shape, game and level vertex, then edit fields:
custom_data
add to custom_data

pt_move_if_reject

section with

path

field. This field specifies name of patrol with two points used when actor reject level changing. Point

p0

is used for reject position, point

p1

- reject look point.
[pt_move_if_reject]
path = esc_way_if_reject
dest_game_vertex_id = <number>
destination (spawn) game vertex id
dest_level_vertex_id = <number>
destination (spawn) level vertex id
dest_position = <vector>
spawn position
dest_direction = <vector>
spawn direction; to rotate actor after spawn (if needed)
dest_level_name = <string>
destination level name
dest_graph_point = <string>
destination graph point
silent_mode = <number>
whether display message box with question about changing level; possible values:
silent_mode = 0
the message box will be displayed;
silent_mode = 1
no message box
To display level changer icon on map, edit

level_tasks.script

in function

add_lchanger_location()

:
-- story_id - the one you set in all.spawn entity
local obj = sim:story_object(story_id)
if obj then
    level.map_add_object_spot(obj.id, "level_changer", mapspot_hint)
end
Example:
[285]
; cse_abstract properties
section_name = level_changer
name = exit_to_garbage_01
position = 29.34641456604,16.5561962127686,686.241638183594
direction = 0,0,0

; cse_alife_object properties
game_vertex_id = 1
distance = 7
level_vertex_id = 309788
object_flags = 0xffffff3e
custom_data = «END
[pt_move_if_reject]
path = esc_way_if_reject
END
story_id = 91

; cse_shape properties
shapes = shape0
shape0:type = box
shape0:axis_x = 2.22939944267273,0,0
shape0:axis_y = 0,4.83859968185425,0
shape0:axis_z = 0,0,2.22939944267273
shape0:offset = 0,0,0

; cse_alife_space_restrictor properties
restrictor_type = 3

; cse_alife_level_changer properties
dest_game_vertex_id = 253
dest_level_vertex_id = 209876
dest_position = 36.4117431640625,0.481237411499023,-305.824523925781
dest_direction = 0.0111562451347709,-0.000250374461757019,0.0224392339587212
dest_level_name = L02_Garbage
dest_graph_point = start_actor_01


What's new?

Updated schemes:

npc_follower
ph_camera
ph_computer
sr_cutscene
ph_light_mgr
ph_projector
ph_radio

New schemes:

npc_draining
sr_blocker
sr_generator_waves
sr_play_anim
mob_dogmeat

SDK articles:

Anom zones
Blowout and Shelters
Cameras


Patrols

Stalkers and mutants are moving around levels using paths (patrols). Patrol is a set of points (waypoints). Each point consists of:
  • index (points are numbered starting from zero)
  • name (plus optional flags)
  • flags (needed if current point should be linked with look point)
  • position
  • game vertex id
  • level vertex id
  • links to other points (if certain patrol has more points)
Here's example of patrol structure with one waypoint:
[patrol_name]
points = p0
p0:name = wp00
p0:position = -7.7667064666748,4.20586109161377,92.5323257446289
p0:game_vertex_id = 2015
p0:level_vertex_id = 8143
Usually patrols contains more waypoints. To link waypoints (to build a path) we use

links

field, we specify point number (index) and, in brackets, how many times npc should visit this point:

[patrol_name]
points = p0,p1,p2
p0:name = name00
p0:position = 194.54,-12.74,88.88
p0:game_vertex_id = 1936
p0:level_vertex_id = 53905
p0:links = p1(1)

p1:name = name01
p1:position = 200.08,-13.47,83.33
p1:game_vertex_id = 1936
p1:level_vertex_id = 53988
p1:links = p2(1)

p2:name = name02
p2:position = 210.45,-14.32,90.17
p2:game_vertex_id = 1936
p2:level_vertex_id = 53851
You can treat patrols as graphs, that is you can build more complex paths if you link the points properly:

[patrol_name]
points = p0,p1,p2,p3,p4,p5,p6,p7,p8
p0:name = wp00
p0:position = 314.83,-50.85,-242.48
p0:game_vertex_id = 2076
p0:level_vertex_id = 73292
p0:links = p1(1),p2(1)

p1:name = wp01
p1:position = 424.16,-54.21,-271.91
p1:game_vertex_id = 2112
p1:level_vertex_id = 136257
p1:links = p0(1)

p2:name = wp02
p2:position = 474.37,-52.75,-289.73
p2:game_vertex_id = 2063
p2:level_vertex_id = 155756
p2:links = p3(1)

p3:name = wp03
p3:position = 401.85,-49.21,-211.78
p3:game_vertex_id = 2101
p3:level_vertex_id = 125266
p3:links = p4(1)

p4:name = wp04
p4:position = 357.94,-46.78,-127.94
p4:game_vertex_id = 2097
p4:level_vertex_id = 97001
p4:links = p5(1),p6(1)

p5:name = wp05
p5:position = 456.81,-54.48,-112.76
p5:game_vertex_id = 2051
p5:level_vertex_id = 149428
p5:links = p4(1)

p6:name = wp06
p6:position = 508.80,-52.80,-143.09
p6:game_vertex_id = 2048
p6:level_vertex_id = 170309
p6:links = p7(1)

p7:name = wp07
p7:position = 413.21,-50.64,-45.82
p7:game_vertex_id = 2037
p7:level_vertex_id = 130973
p7:links = p8(1)

p8:name = wp08
p8:position = 502.43,-50.77,-161.45
p8:game_vertex_id = 2048
p8:level_vertex_id = 167469
p8:links = p0(1)
Waypoints are limited by ai map, that is, if ai map does not contain certain area then this area is not accessible for npc. Usually if you set inaccessible point for npc the game will crash with similar description (when npc will try to reach that point):

*** you will get similar error in case of improperly defined restrictors ***
DEFAULT OUT RESTRICTIONS :
DEFAULT IN RESTRICTIONS  :
     camp_fire_0002
     camp_fire_0003
     camp_fire_0005
     gar_sniper_in_restrictor
     gar_space_restrictor
     gar_vagon_in_restrictor
     gar_zone_mosquito_bald_weak_0002
OUT RESTRICTIONS         :
     gar_sniper_in_restrictor
IN RESTRICTIONS          :
     camp_fire_0002
     camp_fire_0003
     camp_fire_0005
     gar_space_restrictor
     gar_vagon_in_restrictor
     gar_zone_mosquito_bald_weak_0002

FATAL ERROR

[error]Expression    : vertex || show_restrictions(m_object)
[error]Function      : CPatrolPathManager::select_point
[error]File          : D:\xray-svn\xr_3da\xrGame\patrol_path_manager.cpp
[error]Line          : 156
[error]Description   : any vertex in patrol path [gar_smart_bandit_large_lager_raid_1_walk]
in inaccessible for object [gar_sniper_bandit]

stack trace:

Scheduler tried to update object gar_sniper_bandit
If you set large distance between points then npc will try to reach points using paths calculated by ai map. For instance, there is an obstacle, you may decide how the npc should avoid the obstacle to reach last point (path1, path2) or if the direction desn't matter, you may assign the engine to choose the path (path3):

Linking external points

Several ai schemes are using two points: patrol to walk/sleep/fly/drive and patrol with points to look/shoot at. To link them we use

flags

field. Flags are hexadecimal numbers, engine use them to link certain walk point with certain look point. For instance we have a npc with walker scheme:
[walker@first_point]
path_walk = patrol_walk1
path_look = patrol_look
on_signal = on_point_p0 | walker@second_point

[walker@second_point]
path_walk = patrol_walk2
path_look = patrol_look
We use 3 patrols: first npc will walk along patrol_walk1 path looking towards p0 point from patrol_look (because p0 point from patrol_walk1 has the same flag as point p0 from partol_look). Then when npc reach p0 point of patrol_walk1 path, he will use patrol_walk2 path to walk looking towards p1 point from patrol_look
[patrol_walk1]
points = p0
p0:name = wp00
p0:flags = 0x1
p0:position = 12.3218793869019,-0.299999237060547,-64.4775466918945
p0:game_vertex_id = 1958
p0:level_vertex_id = 14724

[patrol_walk2]
points = p0
p0:name = wp00
p0:flags = 0x2
p0:position = 12.3218793869019,-0.299999237060547,-64.4775466918945
p0:game_vertex_id = 1958
p0:level_vertex_id = 14724

[patrol_look]
points = p0,p1
p0:name = wp00|sig=on_point_p0
p0:flags = 0x1
p0:position = -7.7667064666748,4.20586109161377,92.5323257446289
p0:game_vertex_id = 2015
p0:level_vertex_id = 8143
p0:links = p1(1)

p1:name = wp01
p1:flags = 0x2
p1:position = -9.7667064666748,6.20586109161377,98.5323257446289
p1:game_vertex_id = 2014
p1:level_vertex_id = 8145
One point of path_walk can correspond to several points of path_look. If this is the case, the character will randomly choose one of the suitable points.

If npc is walking along path walk and there is no need for him to stop or the point he should look towards is right ahead - there is no need to specify path_look. However, if npc is guarding certain spot, that is, he is standing and looking at certain point - then path_look is necessary, otherwise npc will randomly choose point to look at.

Points flags

Waypoints may have special flags used in scheme scripts to modify object behaviour. We specify flags as table elements in point's name field, such as:
p0:name = wp00 | flag1 = value | flag2 = value | ...

path_walk flags:

a = animation_name { default: "idle" }
body state for movement; list of all possible animations can be found in

state_lib.script

p = percentage_value { default: 100 }
probability of stopping at the point (0 - 100). The character never misses the point if default value (100) is set
s = sound_theme1, sound_theme2, ... { by default flag is disabled }
character speak while moving along a path, each point must have a list of topic he can speak about. Several topics can be separated by commas
sig = signal_name
set signal (before turning towards point) with the given name on arrival at the point. Signal name can be used in scheme section (

on_signal

field)

path_look flags:

a = animation_name { default: "idle" }
body state for movement; list of all possible animations can be found in

state_lib.script

p = percentage_value { default: 100 }
probability with which the character will look at certain point
sig = signal_name
set signal (after turning towards point) with the given name on arrival at the point. Signal name can be used in scheme section (

on_signal

field)
t = miliseconds { default: 5000 }
time in milliseconds to look at the point. Valid values are in the range [1000, 30000],

*

means unlimited time. For terminal waypoints of path_walk that have no more than one corresponding path_look, the value of

t

is always considered infinite and does not have to be set.
syn
this flag will halt setting the signal until all characters with the given team arrive. The team is set as a text string in custom_data field. The given character will play its idle animation until the others (from the same team) arrive.
sigtm = signal_name
sets a signal when the time_callback is called by the state manager. If

t = 0

, then the signal will be set after playing the init animation. This is used, for instance, with the press animation, which consists of two parts:

1 — pressing the button
2 — lowering the arm.
path_look: wp00 | a = press | t = 0 | sigtm = pressed.
scheme section: on_signal = pressed | other_scheme.

Patrols and scripts

If you are going to work with patrols in scripts, then those functions my be useful:
  • test whether certain patrol exist:
    • local patrol_name = "notexisting_patrol_name"

if not level.patrol_path_exists(patrol_name) then
    abort("'%s' patrol does not exist!", patrol_name)
end

-- or eventually:
local ptr = patrol(patrol_name)
if ptr == nil then
    abort("'%s' patrol does not exist!", patrol_name)
end
  • iterating through all patrol points:
    • local point_position, point_name = nil, nil
local point_index, point_flag = nil, nil
local lvid, gvid, is_terminal = nil, nil, nil

local patrol_name = "esc_bandit_guard_walk"

local ptr = patrol(patrol_name)
local points_num = ptr:count()

-- points are indexed starting from zero
for index = 0, points_num - 1 do
    point_position = ptr:point(index)

    point_name = ptr:name(index)
    point_index = ptr:index(point_name)

    lvid = ptr:level_vertex_id(index)
    gvid = ptr:game_vertex_id(index)

    -- is it last point in patrol
    is_terminal = ptr:terminal(index)

    -- iterate current point's flags
    for flag_index = 0, 31 do
        point_flag = ptr:flag(index, flag_index)
    end
end
  • test whether the character reached certain waypoint:
    •
local ptr = patrol("esc_bandit_guard_walk")
local npc = level.object_by_id(123)

for index = 0, ptr:count() - 1 do
    if utils.stalker_at_waypoint(npc, ptr, index) then
        print("stalker '%s' reached %d point", npc:name(), index)
        break
    end
end
  • find nearest point to given position:
    • local vec = vector():set(1.23, 4.56, 7.89)
local nearest = patrol("esc_bandit_guard_walk"):get_nearest(vec)
  • find nearest point to given object (npc/mutant):
    • local npc = level.object_by_id(123)
local ptr = patrol("esc_bandit_guard_walk")

local idx = utils.get_nearest_waypoint(npc, "esc_bandit_guard_walk", ptr, ptr:count())

function vec2str(v)
    return v.x .. "," .. v.y .. "," .. v.z
end

if idx ~= nil then
    print("nearest waypoint:")
    print("index = %d", idx)
    print("position = %s", vec2str(ptr:point(idx)))
end
  • to get access to waypoint flags
    • [esc_bandit_guard_walk]
points = p0,p1
p0:name = wp00|a=rush|sig=reached_point
p0:position = -7.7667064666748,4.20586109161377,92.5323257446289
p0:game_vertex_id = 2015
p0:level_vertex_id = 8143
p0:links = p1(1)

p1:name = wp01|p = 70
p1:position = -9.7667064666748,6.20586109161377,98.5323257446289
p1:game_vertex_id = 2014
p1:level_vertex_id = 8145

    local pathname = "esc_bandit_guard_walk"
local ptr = patrol(pathname)
local tbl = utils.parse_waypoint_data(pathname, ptr:flags(0), ptr:name(0))

-- then tbl will contain:
tbl["a"] = "rush"
tbl["sig"] = "reached_point"

-- you may also use a function to parse flags from all waypoints:

local tbl = utils.path_parse_waypoints(pathname)

-- then tbl will contain:

-- waypoint wp00
tbl[0]["a"] = "rush"
tbl[0]["sig"] = "reached_point"

-- waypoint wp01
tbl[1]["p"] = 70
  • to assign flags to waypoint and parse the flags
    • local pathname = "esc_bandit_guard_walk"
local ptr = patrol(pathname)

-- we can set flags to certain waypoints, to do this
-- we pass array arguments like this:
-- {waypoint_index, "waypoint_name|flag=value"}

-- local f = {0, "wp00|a=guard"}
-- local new_flags = flags32()
-- new_flags:assign(f)

local tbl = utils.path_parse_waypoints_from_arglist(pathname,
    ptr:count(), {0, "wp00"}, {1, "wp01|ret=1"})


Smart Terrains

General informations

By a smart terrain we mean a zone that, upon entry, captures a stalker/mutant in a gulag and makes him do work for this gulag. After a period of time stalker/mutant can escape from the gulag and walks free.

Smart terrain:
  • does not keep stalkers/mutants online. The standard online radius works.
  • stalkers/mutants go to the nearest available task
  • stalkers/mutants perform tasks without respect to whether they are online or offline
  • in offline state smart terrain works the same way as in online: switches states, allocates tasks
  • smart terrain tasks can be located in different levels
  • simulation is manifested in migration of characters between different smart terrains allowing characters to enter certain smart terrain

Smart terrains conditions

Stalkers/mutants can be given conditions for entering certain smart terrains. Permissions for characters to enter certain smart terrains are set in the

[smart_terrains]

section. You can use pairs smart_terrain_name = condlist, such as:
[smart_terrains]
smart_terrain_name_1 = condition_list_1
smart_terrain_name_2 = condition_list_2
If a condition is met for some smart terrain, then such smart terrain is called exclusive (stalker/mutant is exclusive for this smart terrain). If a character has at least one exclusive smart terrain, then he will only agree to go to that one. If he has no exclusive smart terrains, he will agree to go to any available smart terrain (object is non exclusive). If a stalker/mutant enters smart terrain, then he will remain in it until his time elapses or the condition for that smart terrain won't be met anymore.

There is a reserved combination none = true. If it is set, then the character won't accept any smart terrains. This character will only rely on his own logic (custom_data):
[smart_terrains]
none = true
For every smart terrain you have to:
  1. Set a smart terrain with the required shape; it is not recommended to make them large, since size affects performance.
  2. Specify the settings for smart terrain in its custom data.
  3. Set paths for the corresponding behavior schemes.

Smart terrain custom_data settings

custom_data = «END
[smart_terrain]
type = ;-- <string>
preset = ;-- <string>
capacity = ;-- <number>
idle = ;-- <number, number>
duration = ;-- <number, number>
stay = ;-- <const: quick | medium | long | default>
respawn = ;-- <string>
cond = ;-- <condition_list>
squad = ;-- <number>
groups = ;-- <numbers_list>
communities = ;-- <string_list>
END

  • type

    - smart terrain type; the name should be meaningful, usually this format is used: LevelName_CustomName_EventuallyNumber, like for instance: esc_bandits_car_park

  • preset

    - smart terrain preset name. Presets describes what kind of mutants/npcs can join smart terrain (community and rank). You may add your own preset, otherwise preset with current level name will be used (if it exists). Presets are stored in file

    config\misc\smart_terrain_presets.ltx

    ; for instance:
    • ; smart terrains's custom data:

[smart_terrain]
capacity = 10
preset = l01_escape_killers

; config\misc\smart_terrain_presets.ltx

[l01_escape_killers]
killer = experienced

  • capacity

    - how many npcs/mutants this smart terrain can handle; capacity should always be set and it should be less than or equal to the number of smart terrain's tasks.

  • idle (min, max)

    - duration of time the smart terrain remains active after the last stalker/mutant leaves

  • duration

    - ???

  • stay

    - duration of time the stalker/mutant remains under the smart terrain; permanently by default -

    config\misc\smart_terrain.ltx

  • respawn

    - name of stash (respawner is called with the given name every time someone from the smart terrain starts working on a task)

  • cond

    - list of conditions necessary to create a gulag; if the condition is not met, the gulag is disbanded and all its inmates begin to be controlled by logic specified in their custom_data

  • squad

    - the squad to set for all stalkers in the gulag

  • groups

    - comma-separated list of groups

  • communities

    - comma-separated list of communities which can belong to this smart terrain; can be set in gulag ltx file
- if either squad or groups is not set, then the corresponding properties of stalkers will not change
- if the stalker/mutant is already under some smart terrain's control, he will ignore the others
- all time is set in hours of game time and can be fractional

Gulags

A gulag combines several stalkers/mutants under centralized control. Main features:
  • gulag has a task list. A task is a behavioral scheme or a chain of schemes.
  • tasks have priorities.
  • gulag assigns entering stalkers/mutants to jobs with highest priority first.
  • gulags have states. Each condition corresponds to a unique set of tasks, different from the set of any other condition.
  • there can be several gulags under a single smart terrain. They can be set in several sections [gulag2], [gulag3], etc. After a stalker/mutant enters the smart terrain, one of the available gulags will be chosen at random.
A gulag is created like this:
  1. First, a list of states for the gulag has to be determined: day, night, calm, active, etc. Simple gulags might have only one state, a complex one should have many. This introduces more variety and looks better.

  2. Determine the maximum number of people that can be controlled by the gulag. That is, determine the gulag's capacity. It should be such that in any state the gulag should be able to find a task for every person.

  3. For every state the gulag should have a list of tasks. These tasks can be active (centry, patrol, etc) or passive (sitting at a campfire, sleep). Each job has its own priority. Correspondingly, passive tasks should have lower priority.

  4. Smart terrain should be given a meaningful name. This name will also serve as a prefix for all patrol paths for this gulag. For instance, if you call the terrain esc_blockpost, then all patrol paths should start with this prefix, as esc_blockpost_guard_walk, for instance. Custom data for the zone should contain settings for the gulag.

  5. In script

    scripts\gulag_<level_name>.script

    you should set the condition under which stalkers are captured by the gulag. The function

    checkNPC

    should be extended with the condition:
    6. if gulag_type == "gulag_type_name" then
  return npc_community == "community_name"
end

-- for instance:

if gulag_type == "gar_dolg" then
  return npc_community == "dolg"
end
    This function takes two parameters: gulag type and character's community. In this case, gulag with type

    gar_dolg

    will accept all characters from faction Duty.
    You can also write in custom data for smart terrain: communities = faction1, faction2, ... If this field is not set, then the function checkNPC is used. If it is set, then the smart terrain will only accept characters from the specified list (however function checkNPC will also be called)
  6. In script

    scripts\gulag_<level_name>.script

    you should desribe how gulag states should switch. Function

    loadStates

    takes two arguments: gulag name and type. The gulag's state is described as a function returning gulag state number, for instance:
    7. function loadStates(gname, type)
  if type == "gar_maniac" then
    return function(gulag)
      if level.get_time_hours() >= 7 and level.get_time_hours() <= 22 then
        return 0  -- day
      else
        return 1  -- night
      end
    end
  end
end
    In this case, if it's between 7 and 22 hours, the gulag is in daytime mode, otherwise in nighttime mode.

  7. In script

    scripts\gulag_<level_name>.script

    you should describe a gulag's tasks. The function

    loadJob

    loads all the allowed tasks. The function itself takes the following parameters:

    • sj

      - gulag task table

    • gname

      - smart terrain name; used as a prefix

    • type

      - gulag's type

    • squad

      - if we need to override the native stalker squads

    • groups

      - if we need to override the native stalker groups

    For each task you should set a squad and a group to assign a stalker to upon recieving a task. The following gulag describes behavior of a single npc, but there is usually many more. The given npc in zero state (daytime) performs one task, in first state (nighttime) another task:
    function loadJob(sj, gname, type, squad, groups)
  if type == "gar_maniac" then
    t =
    {
      section = "logic@gar_maniac_camper",
      idle = 0,	prior = 5, state = {0},
      squad = squad, groups = groups[1],
      in_rest = "", out_rest = "",
      info_rest =  ""
    }
    table.insert(sj, t)

    t =
    {
      section = "logic@gar_maniac_sleeper",
      idle = 0, prior = 5, state = {1},
      squad = squad, groups = groups[1],
      in_rest = "", out_rest = "",
      info_rest =  ""
    }
    table.insert(sj, t)
  end
end
    Because filling such tables is rather tiresome, you may define function to handle all data which you pass as arguments:
    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

function loadJob(sj, gname, type, squad, groups)
  if type == "gar_maniac" then
    t = fill_tbl("camper", 0, 5, {0}, squad, groups[1], "", "", true, type)
    table.insert(sj, t)

    t = fill_tbl("sleeper", 0, 5, {1}, squad, groups[1], "", "", true, type)
    table.insert(sj, t)
  end
end
    Fields descriptions:

    • section

      - section in

      config\misc\gulag_<level_name>.ltx

      , in which the real behavior scheme corresponding to this task is set

    • idle

      - pause between repeated execution of the same tasks. Pause is usually set for patrols.

    • prior

      - task priority. At first stalkers occupy the higher priority tasks. The higher the number, the higher the priority.

    • state

      - task state

    • squad

      - squad number

    • groups

      - group will be chosen from the group array set in custom data.

    • in_rest, out_rest

      - restrictors set for the character for this task

      »

      in_restrictor

      defines a zone which is not accessible for npc
      »

      out_restrictor

      defines a zone which npc cannot leave

    • info_rest

      - sets the name of the restrictor, inside of which the person performing the task is guarded from all dangers (???)

    • online = true/false

      - the character is always online (

      online = true

      ) / offline (

      online = false

      ) when performing this task; if

      online

      field isn't set then character may switch between online and offline which depends on alife radius

    • timeout

      - ???

    • position_threshold

      - ???
    • task description may also contain additional conditions for when the stalker/mutant can recieve the task. You can use predicates to assign certain tasks to only certain npcs/mutants. For instance the given task can only be carried out by a npc with the

      soldier_commander

      profile:
      • predicate = function(obj)
    return obj:profile_name() == "soldier_commander"
end
      In predicate functions a character information table is passed instead of game_object. Character information table contains fields:

      • name
      • community
      • class_id
      • story_id
      • profile_name
      • section_name
      • rank
      • is_sniper
      -- by name and community
predicate = function(npc_info)
    if npc_info.name == "esc_outpost_soldier_trader" and
        npc_info.community == "military"
    then
        return true
    end
    return false
end

-- by section_name:
predicate = function(npc_info)
    return npc_info.section_name == "pseudodog_arena"
end

-- sniper with rank > 300
predicate = function(npc_info)
    if npc_info.is_sniper == true and npc_info.rank > 300 then
        return true
    end
    return false
end

-- military trader with story_id:
predicate = function(npc_info)
    if npc_info.profile_name == "esc_soldier_trader" and
        npc_info.class_id == clsid.script_trader and
        npc_info.story_id == 1234
    then
        return true
    end
    return false
end
  8. In file

    config\misc\gulag_<level_name>.ltx

    you should specify which behavior schemes correspond to each task. For instance, for the aforementioned gar_maniac:
    9. ;----------------------------
;-- GARBAGE MANIAC
;----------------------------
[logic@gar_maniac_camper]
active = camper@gar_maniac_camper

[camper@gar_maniac_camper]
path_walk = walk1
path_look = look1

[logic@gar_maniac_sleeper]
active = sleeper@gar_maniac_sleeper

[sleeper@gar_maniac_sleeper]
path_main = sleep
wakeable  = true
    • paths should be specified without a prefix. That is, if the smart terrain was called gar_maniac, a path on the level (all.spawn) might be called gar_maniac_walk1, but in file

      config\misc\gulag_<level_name>.ltx

      should be specified as simply walk1

    • section names should be appended with @ and the gulag name and, possibly, additional information, for instance walker2@rad_antena_gate

    • for each task, logic section names should be appended with @ and the gulag name, additional data, and active behavior scheme, for instance logic@rad_antena_gate_walker2

Dynamic LTX

You can use external ltx files (

config\misc\gulag_*.ltx

) to store jobs for smart terrains, you can also use dynamic ltx for trivial smart terrains. To do so, in script

scripts\gulag_<level_name>.script

use function

load_ltx

function load_ltx(gname, type)
  if type == "gar_maniac" then
    return load_ltx_gar_maniac(gname, type)
  end

  return nil
end

function load_ltx_gar_maniac(gname, type)
  local ltx = "[logic@" .. gname .. "_camper]\n" ..
  "active = camper@" .. gname .. "_camper\n" ..

  "[camper@" .. gname .. "_camper]\n" ..
  "path_walk = walk1\n" ..
  "path_look = look1\n" ..

  "[logic@" .. gname .. "_sleeper]\n" ..
  "active = sleeper@" .. gname .. "_sleeper\n" ..

  "[sleeper@" .. gname .. "_sleeper]\n" ..
  "path_main = sleep\n" ..
  "wakeable  = true\n"

  return ltx
end

New gulag script

If you have new map attached to single player then you can use one of the vanilla gulag scripts (

scripts\gulag_*.script

) to load tasks for smart terrain, or you can add new script for your map. Create new script and call it

gulag_levelname.script

and add all needed functions. Then open

xr_gulag.script

and add to

level_gulags

array your script. Next, add groups number for your level smart terrains in

level_groups

array. Finally, open

smart_terrain.script

and add level group for your level's smart terrains in

level_groups

array.

To store jobs definitions create new ltx file in

config\misc\gulag_levelname.ltx

then edit

config\misc\gulag_tasks.ltx

and include your ltx file.


smart_terrain

init(section)

purpose:
constructor of smart terrain class

detect_probability()

purpose:
method of

cse_alife_smart_zone

(base class of smart terrain class)

STATE_Write(packet)

purpose:
method saves all needed data
arguments:

packet <net_packet>

- stream writer

STATE_Read(packet, size)

purpose:
method to read saved smart terrain's data
arguments:

packet <net_packet>

- stream reader

size <number>

- ???

initialize_if_needed()

purpose:
method collects all info about npcs in smart terrain and preserve exclusives npcs

preserve_exclusives()

purpose:
method preserves exclusives npcs and calculates nonexclusive npcs

get_se_objs()

purpose:
method fills informations about all npcs (server objects)

fill_npc_info(obj, npc_info)

purpose:
fills informations about given npc in

npc_info

table
arguments:

obj

- npc or mutant

npc_info

- table with informations about npcs
returns:

npc_info

- table with informations about object
remarks:

npc_info

contains fields:
  • se_obj
  • class_id
  • section_name
  • name
  • story_id
  • community
  • profile_name
  • rank
  • se_obj
  • group
  • squad
  • team
  • is_sniper
  • is_sniper_name
  • cond - is set using field

    smart_terrain_conditions

    from se_monster or se_stalker scripts

get_obj_community(obj)

purpose:
method gets given object's community
arguments:

obj

- npc or mutant
returns:

faction, true

- if object is stalker

mutant_type, false

- if object is mutant

read_params()

purpose:
method reads data from smart terrain's custom_data field
remarks:
Special fields are set:
  • npc_info
  • gparams (gulag params)
    • type (

      type

      field)
    • preset_name (

      preset

      field)
    • capacity (

      capacity

      field)
    • squad (

      squad

      field)
    • idle_min, idle_max (

      idle

      field) : 0, 0 by default
    • duration_min, duration_max (

      duration

      field) : 10000, 10001 by default
    • stay_min, stay_max (

      stay

      field) : file: smart_terrain.ltx, section: smart_terrain_stay_time
    • respawn (

      respawn

      field)
    • cond (

      cond

      field)
    • groups (

      groups

      field)
  • accepted_communities (

    communities

    field)
  • rank_inc_min = 1 (file: game_relations.ltx, section: smart_terrain_rank_change)
  • rank_inc_max = 30 (file: game_relations.ltx, section: smart_terrain_rank_change)
  • population_locked = false
  • gulag_working = false
  • gulag = xr_gulag.script

check_preset(npc_community, npc_rank, preset_name)

purpose:
method check if given npc fits to the smart terrain preset
arguments:

npc_community

- faction name

npc_rank

- rank

preset_name

- preset name (config\misc\smart_terrain_presets.ltx)
returns:

true

- if preset fits or doesn't exist

false

- otherwise

smart_terrain_accepts_obj(obj, obj_agreement)

purpose:
method check if smart terrain can accept object
arguments:

obj

- stalker or mutant

obj_agreement

- object accept smart terrain - agreement type:
  • disagreed = 0
  • agreed = 1
  • agreed_exclusive = 2
returns:

false

- if

  • obj_agreement == disagreed

  • obj_agreement ~= agreed_exclusive

    or there is no free place for nonexclusive npcs

  • obj_agreement ~= agreed_exclusive

    and object's rank doesn't fit to smart terrain's preset
  • object's community is not accepted by smart terrain
  • object's level group differs from smart terrain's level group -

    level_groups

    array
  • there is no job for object in current smart terrain's state

true

- there is available job for object in current smart terrain's state

obj_accepts_smart_terrain(obj)

purpose:
method decides which smart terrain should be assigned for object using data set in

smart_terrains

section in

custom_data

object's field
arguments:

obj

- stalker or mutant
returns:

  • agreed

    - there is no section smart_terrains in custom_data object's field

  • agreed_exclusive

    - section smart_terrains in custom_data object's field contains current smart terrain and conditions are met

  • disagreed

    - there is no condition for smart terrains in smart_terrains field or there is

    none = true

enabled(obj)

purpose:
method checks whether smart terrain is enabled for object
arguments:

obj

- stalker or mutant
returns:

  • true

    - gulag is available, gulag population isn't locked, object accepts smart terrain and smart terrain accepts object

  • false

    - otherwise

suitable(obj)

purpose:
method counts how many free slots are in all available smart terrains
arguments:

obj

- stalker or mutant
returns:

<number>

- number of free slots in all available smart terrains + 100000 if object accepts current smart terrain (object is exclusive for current smart)

register_npc(obj)

purpose:
method adds object to smart terrain
arguments:

obj

- stalker or mutant

unregister_npc(obj)

purpose:
method removes object from smart terrain
arguments:

obj

- stalker or mutant

update_obj_rank(obj)

purpose:
method increase npc rank
arguments:

obj

- stalker
remarks:

npc rank is increased as:

old rank + random(self.rank_inc_min, self.rank_inc_max)

kick_someone_non_exclusive(new_npc_info)

purpose:
method removes non exclusive object from smart terrain if it is needed (when new object is added to smart terrain and there is no free job for object, then non exclusive object is removed from smart)
arguments:

new_npc_info

- npc_info array for new object which should be added to smart terrain

task(obj)

purpose:
method returns smart terrain task for object
arguments:

obj

- stalker or mutant
returns:

<CALifeSmartTerrainTask *>

- smart terrain task

is_gulag_available()

purpose:
function checks if smart terrain is enabled or not
returns:

true

- if

cond

field is not specified or conditions are met

false

- if conditions are not met

show_smart_debug_spot(avail)

purpose:
debug function to mark/unmark smart terrains on PDA's map
arguments:

avail <boolean>

- whether mark smart terrain on map or remove mapspot

lock_population(need_lock)

purpose:
function locks/unlocks population of smart terrain
arguments:

need_lock <boolean>

- whether lock the population or not

create_gulag()

purpose:
method initializes gulag and sets duration end timer and update check timer

remove_gulag(disable)

purpose:
method unregister all npcs and creates new gulag
arguments:

disable <boolean>

- whether gulag should be disabled

fill_monster_classes()

purpose:
function fills

monster_classes

array

get_level_name()

purpose:
function returns level name on which smart terrain is spawned
returns:

<string>

- level name

on_register()

purpose:
method register current smart terrain

register_if_needed()

purpose:
initialization method (gulag, preset, respawn)

on_unregister()

purpose:
method removes current smart terrain

call_respawn()

purpose:
method tries to spawn stuff in simulation boxes (offline spawn)

stat_get_accepted_communities()

purpose:
debug method
returns:

<string>

- smart terrain's accepted communities and ranks

stat()

purpose:
smart terrain debug method
returns:

<string>

- smart terrain's name, level, gulags availability, population, capacity, communities

update()

purpose:
method updates gulags

on_death(obj_id)

purpose:
function called when object dies
arguments:

obj_id

- object's (stalker, npc) id

unregister_npc(npc)

purpose:
function removes object from smart terrain
arguments:

npc

- stalker or mutant

read_smart_terrain_conditions(self)

purpose:
function returns smart terrain's conditions
returns:

conditions

- if smart terrains has conditions

nil

- otherwise

stat()

purpose:
function prints out debug info about all smart terrains


smart_terrain_params

read_preset(preset_name)

purpose:
function reads communities and ranks from

config\misc\smart_terrain_presets.ltx

for given preset
arguments:

preset_name <string>

- preset name
returns:

false

if given preset doesn't exist
array with presets otherwise
;-- config\misc\smart_terrain_presets.ltx

[l02_garbage]
stalker = novice, experienced
dolg    = novice, experienced, veteran
bandit  = novice, experienced

presets = smart_terrain_params.read_preset("l02_garbage")

;-- then preset:
preset =
{
  stalker = {0, 300},
  bandit = {0, 900},
  dolg = {0, 300}
}

load_params()

purpose:
function reads stay time params from

config\misc\smart_terrain.ltx

and sets them in

stay_times

array
stay_times =
{
  quick   = {2, 5},
  medium  = {6, 8},
  long    = {12, 14},
  default = {10000, 10001}
}

;-- config\misc\smart_terrain.ltx

[smart_terrain_stay_time]
quick   = 2,  5
medium  = 6,  8
long    = 12, 14
default = 10000, 10001

get_stay_time_interval(name)

purpose:
function returns min and max stay time interval for given interval
arguments:

name <string>

- stay time interval name
returns:

<number, number>

- min and max stay time interval for given interval
local min, max = smart_terrain_params.get_stay_time_interval("quick")
-- min = 2 <= stay_times["quick"][1]
-- max = 5 <= stay_times["quick"][2]

get_preset(preset_name)

purpose:
for given preset function returns preset's data
arguments:

preset_name <string>

- preset name
;-- config\misc\smart_terrain_presets.ltx

[l02_garbage]
stalker = novice, experienced
dolg    = novice, experienced, veteran
bandit  = novice, experienced

presets = smart_terrain_params.get_preset("l02_garbage")

;-- then preset:
preset =
{
  stalker = {0, 300},
  bandit = {0, 900},
  dolg = {0, 300}
}


xr_gulag

__init(smrttrn, type, squad, groups, npc_info, capacity)

purpose:
constructor of gulag class
arguments:

smrttrn <se_smart_terrain *>

- pointer to smart terrain server object

type <string>

- smart terrain type

squad <number>

- smart terrain squad number

groups <array>

- table with groups

npc_info <array>

- array with info about npcs which belongs to smart terrain

capacity <number>

- gulag capacity
remarks:
Constructor set several fields:
  • smrttrn - pointer to smart terrain
  • name - all.spawn name of smart terrain
  • type - smart terrain type
  • squad - smart terrain squad number
  • groups - table with groups
  • npc_info - info about all npcs which belongs to smart terrain
  • capacity - smart terrain capacity
  • population - gulag population
  • population_comed - how many npcs are currently in gulag
  • population_non_exclusive - how many non exclusive npcs
  • Object -
  • Object_begin_job
  • Object_didnt_begin_job
  • ObjectJob
  • JobLoaded
  • Job_avail_in_state
  • state - gulag state
  • stateBegin
  • casualities - gulag casualities


AI behaviour schemes

General informations

AI schemes controls objects behaviour. In Stalker we have 5 types of ai schemes:
  • stalker schemes
  • mutant schemes
  • space restrictor schemes
  • physical object schemes
  • helicopter schemes
Each ai scheme is set by object binder (stalker and mutant schemes are set by gulag called from binder) that is, we can say that ai scheme is placed between object binder and object itself. Object can remain under control of one scheme, however usually several schemes are beeing switched thus object is controlled by more than one scheme.

Scheme execution

Script

logic

controls scheme switching,

custom_data

field in object definition (all.spawn) must have a

logic

section. The

logic

section starts ai schemes execution and must contain one of the fields:
active = <condition_list>
the active scheme to be ran first
[logic]
active = section_name

[section_name]
; section_settings

; for instance:

[logic]
active = walker

[walker]
path_walk = esc_soldier_walk
path_look = esc_soldier_look
cfg = <string>
path to external (Ltx) file with other ai schemes sections, if

cfg

is set, then the file content will be used for object's settings (base folder for ltx files is

gamedata\config\

):
[logic]
cfg = path\file_name.ltx

; for instance:

[logic]
cfg = scripts\esc_trader_door.ltx

; --------------------------------------------
; gamedata\config\scripts\esc_trader_door.ltx

[logic]
active = ph_door@locked

[ph_door@locked]
locked = true
snd_open_start = trader_door_unlock
on_info = {+esc_trader_can_leave} ph_door@closed %=play_snd(device\door_servomotor)%

[ph_door@closed]
closed = true
locked = false
on_use = ph_door@open %-esc_close_door%
snd_open_start = trader_door_open_start
snd_close_start = trader_door_close_start
snd_close_stop = trader_door_close_stop

[ph_door@open]
closed = false
locked = false
on_use = ph_door@closed
on_info = {+esc_close_door} ph_door@closed
snd_open_start = trader_door_open_start
snd_close_start = trader_door_close_start
snd_close_stop = trader_door_close_stop

Scheme sections switching

Scheme switching is carried out with the additional conditions which are set in the section of the current (active) scheme. Following switch conditions can be used in all ai schemes:
Note: If logic switches between several schemes having the same names (such as several walker schemes), then they can be numbered (walker1, walker2) or more informative names can be given after @ (for instance: walker@day, walker@alarm).
on_actor_dist_le = number | scheme
distance to the player <= number
on_actor_dist_le_nvis = number | scheme
distance to the player <= number without a visibility check
on_actor_dist_ge = number | scheme
distance to the player >= number
on_actor_dist_ge_nvis = number | scheme
distance to the player >= number without a visibility check
on_signal = signal | scheme
fires on signal given when object reach certain waypoint
on_info = condition_list
fires certain sections (depending on conditions)
on_timer = msec | scheme
fires after the given number of ms
on_game_timer = sec | scheme
fires after the given number of game seconds
on_actor_in_zone = restrictor_name | scheme
if the actor is in the zone (restrictor name have to be given)
on_actor_not_in_zone = restrictor_name | scheme
if the actor is not in the zone (restrictor name have to be given)
on_npc_in_zone = npc_story_id | restrictor_name | scheme
if an npc is in the zone (npc story_id and restrictor name have to be given)
on_npc_not_in_zone = npc_story_id | restrictor_name | scheme
if an npc is not in the zone (npc story_id and restrictor name have to be given)
on_actor_inside = scheme
the zone checks if the player is inside
on_actor_outside = scheme
the zone checks if teh player is outside
Note: All above conditions can be numbered, for instance: on_timer2 = ..., on_timer3 = ... and so on. However you can set different conditions using comma separated list so using numbered conditions isn't needed: on_info = {walker_conditions} walker, {sleeper_conditions} sleeper, nil
You can control scheme section switching by using function calls or infoportions:
  • condition functions - function have to return boolean value, all the condition functions are stored in

    gamedata\scripts\xr_conditions.script

    . Function call is performed in braces parenthesis including equality sign (=) which means that function have to return

    true

    or exclamation mark (!) which means that function have to return

    false

    :
    ; if is_day() == true then goto section 'daily_scheme'
; goto section 'nightly_scheme' otherwise

on_info = {=is_day()} daily_scheme, {!is_day()} nightly_scheme

[daily_scheme]
; scheme settings

[nightly_scheme]
; scheme settings

; ---------------------------------
; this can be also written as:

on_info = {=is_day()} daily_scheme, nightly_scheme

[daily_scheme]
; scheme settings

[nightly_scheme]
; scheme settings
    If function doesn't use any arguments then function's parenthesis can be skipped, otherwise arguments are passed as a list using colon to separate each argument:
    ; no arguments, so function's parenthesis can be skipped
on_info = {=is_day} daily_scheme

; arguments are separated with colon
on_info2 = {=gulag_casualities_ge(esc_blokpost:3)} nil

[daily_scheme]
; scheme settings
  • effect functions - function perform action so returned value isn't important. All the effect functions are stored in

    gamedata\scripts\xr_effects.script

    . Function call is performed between percentage signs including equality sign (=):
    on_info = {+esc_kill_bandits_quest_kill} %=hit_npc(esc_killers_kamp_wait:bip01_spine1:1000:0)%
  • infoportions - you can use infoportions as conditions (+ sign means that actor must have the infoportion, - sign means the opposite):
    on_info = {+esc_kill_bandits_quest_done} bandits_killed
on_info2 = {-esc_kill_bandits_quest_done} bandits_not_killed_yet

[bandits_killed]
; scheme settings

[bandits_not_killed_yet]
; scheme settings
    or you can use infoportions as effect (+ sign means: give actor an infoportion, - sign means: get back an infoportion):
    on_info = {=gulag_empty(esc_bandits)} bandits_killed %+esc_kill_bandits_quest_done%

[bandits_killed]
on_info = {+esc_kill_bandits_quest_done} nil %-esc_kill_bandits_quest_done%
Several conditions or effects should be separated with spaces:
; if actor has info1 and info3 but hasn't info2 then walker scheme
; will be activated, actor will receive info4 and func will be called
on_info = {+info1 -info2 +info3} walker %+info4 =func%
You can set several sections separated by commas. They will be hanled from left to right. After the first condition fires, the handling stops. In the example below, if info1 is set, then walker2 is enabled, if info2 is set and info1 isn't, then walker3 is enabled, otherwise walker4:
on_info = {+info1} walker2, {+info2} walker3, walker4
You can also set conditions in the active field of logic section, such us:
[logic]
active = {=actor_friend} walker@friendly, walker@enemy
You can use the keyword

never

(which means that the condition is always false) or

always

(which means that the condition is always true) in logic sections. Below section enables combat ignoring if the NPC's enemy is actor which is wearing a suit, but disables it if the player isn't wearing the suit. In this case the effects of the

never

section will work. Choosing the

never

section is equivalent to not having a section, but effects between percentage signs work:
combat_ignore_cond = {=actor_enemy =actor_has_suit} always,
    {=actor_enemy} never %...effects...%
Here is an example of work with the

nil

section. The

nil

section takes a character, monster, or object from under a scripted scene and puts him in the engine's control. This is necessary if some condition does not need to be checked again after firing off once. This saves resources that would otherwise be wasted on checking the condition.
[logic]
active = sr_idle

[sr_idle]
on_actor_inside = nil %+esc_actor_inside%
That is, after the actor enters the restrictor, he gets an infoportion, and the restrictor moves into the

nil

section.
Note: scripts cannot be returned from the nil section!

An example of fairly complex logic

[logic]
active        = walker
combat_ignore = combat_ignore
on_hit        = hit
on_death      = death

[hit]
on_info = %+alert%

[death]
on_info = %+alert +trup3%

[walker]
path_walk          = walk_svoboda3
path_look          = look_svoboda3
combat_ignore_cond = {-alert}
on_timer           = 25000 | remark

[remark]
anim               = idle
snd                = stalker_talk_kampfire
no_move            = true
no_rotate          = true
on_hit             = hit
on_death           = death
combat_ignore_cond = {-alert}

[combat_ignore]
Lets look at this step by step. At first, the stalker works according to the walker scheme. While at it, he ignores combat until the alert infoportion is set. Then he waits 25 seconds and switches to the remark scheme. In remark he plays an idle animation, chats on specified subjects, does not turn, does not move, still ignores combat. If he is hit or killed (on_death), he will get an alert infoportion and stops ignoring combat (obviously, if he is a corpse, that won't help, but there are three of them on the scene, and then the rest will spring into action). If he is killed, he will also get a trup3 infoportion, which will announce that this stalker is dead.

Now, here is his opponent's logic:
[logic]
active        = walker
combat_ignore = combat_ignore

[walker]
path_walk          = soldier_walk1
path_look          = soldier_look1
combat_ignore_cond = always
team               = assault_group
on_signal          = assault | camper

[camper]
path_walk = soldier_walk1_2
path_look = soldier_look1_2
radius    = 5
on_info   = {+trup1 +trup2 +trup3} walker2

[walker2]
path_walk = soldier_walk1_3
path_look = soldier_look1_3

[combat_ignore]
He is in the walker scheme, ignores combat (in any situation, by the way). He is part of the assault_group. When he comes to the endpoint of his route (where he is synchronized with the rest of the group, as is specified in the paths) he gets the assault signal and switches to the camper scheme. In this scheme he does not have combat_ignore specified and, therefore, starts shooting at the enemy. After all three oppponents are dead, with them setting infoportions trup1, trup, and trup3 upon dying, he switches to walker2 (goes to the campfire).

Data types

You can configure ai schemes by setting certain variables, you can set variables in scheme section:
[logic]
active = sr_psy_antenna

[sr_psy_antenna]
eff_intensity = 100
hit_intensity = 0
mute_sound_threshold = 1
Scheme script reads variables in function

set_scheme()

. To read variables you can use functions:
utils.cfg_get_bool(ini, section, field, object, mandatory, default_value)
to read

boolean

value
utils.cfg_get_string(ini, section, field, object, mandatory, gulag_name, default_value)
to read

string

value
utils.cfg_get_number(ini, section, field, object, mandatory, default_value)
to read

number

value
xr_logic.cfg_get_condlist(ini, section, field, object)
to read

condition_list

value
xr_logic.cfg_get_number_and_condlist(ini, section, field, object)
to read

number

and

condition_list

xr_logic.cfg_get_string_and_condlist(ini, section, field, object)
to read

string

and

condition_list

parse_names(string_list)
to read

string_list

Obviously you can implement your own functions to parse variables:
[logic]
active = my_scheme

[my_scheme]
spawn_vector = 334.4873046875, 15.932071685791, -17.644674301147
spawn_item_quantity = wpn_abakan, 1, bread, 3, bandage, 10
spawn_once = true

function parse_string_number(str)
    local tbl = {}
    for word in string.gmatch(str, "([%w_%-%.]+)%p*") do
        table.insert(tbl, word)
    end

    local ret_tbl = {}

    for index = 1, #tbl, 2 do
        ret_tbl[tbl[index]] = tbl[index + 1]
    end

    return ret_tbl
end

function cfg_get_stringlist(ini, section, field, default_value)
    if section and ini then
        if ini:section_exist(section) and ini:line_exist(section, field) then
            return parse_string_number(ini:r_string(section, field))
        end
    end

    return default_val
end

function cfg_get_vector(ini, section, field, default_value)
    if section and ini then
        if ini:section_exist(section) and ini:line_exist(section, field) then
            return ini:r_vector(section, field)
        end
    end

    return default_val
end

function set_scheme(obj, ini, scheme, section, gulag_name)
    local storage = xr_logic.assign_storage_and_bind(obj, ini, scheme, section)

    storage.sp_vec = cfg_get_vector(ini, section, "spawn_vector", nil)
    storage.sp_iq = cfg_get_stringlist(ini, section, "spawn_item_quantity", {})
    storage.sp_once = utils.cfg_get_bool(ini, section, "spawn_once", obj, false, true)
end

Scheme overrides

Some ai schemes can works simultaneously with other schemes. However in such situation one of the schemes overrides another one. For instance npc has active walker and heli_hunter schemes. In this case heli_hunter scheme is "more important" then walker scheme. In evaluator of heli_hunter scheme we check if heli_hunter is still active, that is, whether heli_hunter scheme overrides walker scheme. Npc is walking along path_walk, if the heli appears then npc will use heli_hunter scheme and will try to shoot down the heli instead of continuing walking.
function evaluator_shoot:evaluate()
  if not self.a.enabled then
    return false
  end

  overrides = xr_logic.generic_scheme_overrides(self.object)

  if overrides and overrides.heli_hunter ~= nil then
    -- if heli_hunter scheme is still active (overrides)
    if xr_logic.pick_section_from_condlist(db.actor,
      self.object, overrides.heli_hunter) ~= nil
    then
      -- check other conditions
    end
  end
  return false
end

[logic]
active = walker1
heli_hunter = heli

[heli]
dist = 40

[spawner]
cond = {+aes_desant_wave3}

[walker1]
path_walk = aes_monolit_heli_hunter_walk1
path_look = aes_monolit_heli_hunter_look1
heli_hunter = true

[smart_terrains]
none = true


A-Life, Emergent AI and S.T.A.L.K.E.R.: An Interview with Dmitriy Iassenev

A-Life, Emergent AI and S.T.A.L.K.E.R.:
An Interview with Dmitriy Iassenev

» Alex J. Champandard
» source: AiGameDev.com

Ever since it was first announced, the game S.T.A.L.K.E.R. has captured the imagination of avid gamers fascinated by the idea of a massive world populated by an A-Life system. The game also piqued the interest of AI developers here at AiGameDev.com, making it into the finalists for best AI and runner up for technical innovation in the Game AI Awards for 2007.

It’s therefore a pleasure to announce this exclusive interview with Dmitriy Iassenev, the mastermind behind the AI and A-Life system in S.T.A.L.K.E.R., who agreed to answer some questions about the engine and its implementation. As it turns out, there’s some pretty amazing technology under the hood to — and not only on the surface!

Alex Champandard: Hi, Dmitriy. Thanks for taking the time to answer these questions. Could you introduce yourself briefly and tell us about your background in AI and game development?

Dmitriy Iassenev: Hi, Alex. I am the lead programmer at GSC Game World on S.T.A.L.K.E.R.: Clear Sky.

The first thing I got acquainted with in this matter was the intelligence for the board game filler at the AI course in the university. Further on, this interest grew into the creation of a program that can play othello(reversi); it did not become the best, though it had won one championship on a synchro-random games (opponents play the same position for white and black simultaneously, after finishing both games the winner is determined by sum of the results). After graduating from university in 2002, I started working at GSC Game World as the artificial intelligence programmer on S.T.A.L.K.E.R.: Shadow of Chernobyl.

Alex Champandard: One of the key features of S.T.A.L.K.E.R. is the A-Life system. Could you describe what you think is the essence of A-life, and how it can be applied?

Dmitriy Iassenev: The gist of the A-life is that the characters in the game live their own lives and exist all the time, not only when they are in the player’s field of view. It eventually runs counter to the customary optimization processes used in games development (why perform operations invisible to the player?). Thus, such a scheme is reasonable to be used only when you know exactly what you want to have in the end. We had the game designers’ requirements to have the characters that could not only live inside a certain level, but move between the levels, memorizing the information they obtained during their existence. Consequently, we have decided that each character should come with only one logical essence regardless of the level he is at; whereas we could try to implement that with various tricks involved.

Alex Champandard: There’s a fine line between a game and a simulation. How far does your implementation in S.T.A.L.K.E.R. take these ideas of A-Life?

Dmitriy Iassenev: Our A-life implementation does not claim to be full and pure. The concept of a living world, where each character has his own goal is vast and complicated, indeed. Therefore, its realization to the fullest extent is a large and difficult task. Especially, when the matter concerns shooters, with their detailed elaboration of the actions, and when good-looking animations can be more significant than the high-level character actions. (This is much spoken about; the question of why the AI methods of RTS cannot be carried over to FPS has been discussed at your web-site, for example. I would also like to ask the AI developers in FPS games, how much time they devote to low level and how much to the high. I’d say that it would be great if the ratio is 10 to 1.)

Alex Champandard: Could you tell us more about how you implemented the A-Life?

Dmitriy Iassenev: Our A-life implementation is quite simple. We have introduced two terms that characterize 2 patterns of character’s behaviour, different in implementation details: offline and online. The offline behaviour is very simple: the character does not play animations or sounds, does not manage his inventory actively, does not build detailed and smooth paths (although he does build paths according to the global navigation graph, but more on that later), etc. In contrast, online behaviour is fully detailed. Thus, offline behaviour can be considered the LoD (level of detail) of the online.

In our system the player acts in a level, other characters live in other levels, i.e. they are offline, i.e. they are using the offline behaviour. Moreover, considering the high density of characters in a level, not all characters in the player’s level have the online behaviour, but only those who are in a predefined radius from the player (can vary from level to level, but usually it is about 150 meters), or what the game designer sets it to. To achieve this, the A-life system follows the player’s and offline characters’ movements, and switches the latter to online/offline as necessary.

Next, navigation of objects in online and offline should be mentioned. Our game has levels, for each of which a separate navigation graph is build and is used by characters for movement in online mode. We call it the detailed graph. For each graph a less detailed version is created, the vertices of which can be connected with vertices of graphs from other levels. This is the graph used by characters for navigation in offline. It is also used by online characters for carrying out strategic objectives. For example, if an online character decides to move to the location on another level, he searches for the path on the global graph, then uses the current level’s detailed graph to create the path from his current position to a vertex of the global graph. If that point is already on another level, the character teleports there and switches to offline mode. To prevent the player from seeing this, we have placed these points further than the player’s transition points, somewhere "around the corner."

Alex Champandard: What were the other important parts of the system for developing the A-Life in S.T.A.L.K.E.R.?

Dmitriy Iassenev: Every character in the game always has a goal: to uncover the mystery of the Zone. In early versions of the simulator, a character knew one or several dealers, which had sets of quests that they generated based on the map of anomalous activity and requests from organizations, which wanted to get rich from artefacts found in the Zone. Completion of a quest brought the character closer to his goal. There was only one type of quest at the time — “bring an artefact”. The character would take on a quest to search for the artefact, go to the approximate area in which it could be found, if able to find the artefact, brought it back to the dealer, traded it, then picked a new quest. Along the way he could encounter enemies and fight them. In offline mode this was simulated as a TBS: the opponents took turns making moves, the result calculated based on a formula and the random factor. The effect was that one opponent could run away from another, sometimes characters got killed, sometimes they did not even notice each other or decided to avoid confrontation. If the character was a stalker and met a neutral or a friend, they traded with each other, guided by a developed trading scheme. All of this worked both in offline and in online modes. To notify player about the offline activity, we used news, which were generated if some character could see an event or its consequences.

It was planned that the number of types of quests would later increase, and character behaviour would diversify due to introduction of requirements of food and sleep. We also planned that our characters would be able to uncover the mystery of the Zone before the player, or at least collect a certain amount of information.

Then we changed the way quests were generated. Characters would now get quests not from dealers, but from the so-called smart terrains (not the same as in Sims). A character’s life now worked in such a way, that he took the quest generated by some smart terrain, then walked to the destination it had set for him. After getting there, he would get points, and the smart terrain would take him under control for some time. Under the control of a smart terrain, the character prioritized and carried out the available tasks. This is how the game got faction bases, stalkers by campfires, and such.

That is, migration of characters from place to place, their movement between levels, all was caused by changing goals.

Alex Champandard: How do these ideas tie in to the character AI at the lower level?

Dmitriy Iassenev: Regarding AI at the single character level, we should look at the two models: offline and online, separately, even though they have some things in common.

Offline character AI is very simple: if there is no goal, try to get one. If a goal cannot be found, wonder about aimlessly. If there is a goal, go to the place at which it can be achieved. When a character is under smart terrain control, he can get additional commands for movement, but performs no other actions.

Online character AI consists of three layers:

  • collection and processing of information
  • making decisions
  • a set of low-level controllers
A character has three types of receptors: visual, sound, and damage. Out of the information received from receptors, the character picks the interesting bits — enemies, threats, objects that can be picked up.

Alex Champandard: How do characters make decisions in S.T.A.L.K.E.R.?

Dmitriy Iassenev: The decision-making model has undergone many changes during the course of its development. There have been four iterations in total. First, we used hand written stack-based FSM (Finite State Machine). Then we switched to a hierarchical FSM, then I read the remarkable GOAP (Goal Oriented Action Planning) article by Jeff Orkin in Game Programing Wisdom II, then an article about motivational graphs, and we started using motivational graphs for goal selection and GOAP for action selection.

There are several nuances here, which I have discussed in correspondence with Jeff Orkin. We have decided to use planning exclusively for action selection. That is, we only needed a plan for selecting the first action. All the other parts of the plan were ignored. The value of the first action is in the fact that it is chosen not arbitrarily, but in the context of some plan. This is very good for debugging — you always know what your character is doing and, more importantly, why. This also makes it easier to tell if a plan is still valid, since our plan is always valid.

To keep the plan valid, our implementation of GOAP kept querying those properties of the world that it needed to create the plan of least weight. We did not keep rebuilding plans, but only queried the properties of the world that the planner used to create the current plan. If any one of them changed value, then the whole plan was rebuilt.

On the other hand, a character must always have some non-empty plan. An empty plan meant that the character did not know what to do, since there was no sequence of actions that could transfer current world state to the target one using a set of available operators (or its attainment required too many vertices to visit, which happened, though rarely. In 99% of cases it meant that there was no such sequence, but the planner could not prove it, due to the limit on the number of visited vertices). The condition of existence of a non-empty plan demanded that we make a property of the world that always had a single value, which is probably not very elegant.

To set a character’s (stalker’s) behaviour we used several dozens of operators (about 70). To easily control such a number of operators, we used hierarchies of planners. That is, an operator could itself be a planner. Interaction of planners of different levels was carried out like this: if the top-level planner changed plan, with which the current state also changed, then the previous planner was informed that it should finish. If the operator was itself a planner, it informed its own current planner, and so on. It should be noted that finishing was an instantaneous action, i.e. if the action could not be stopped at once, this had to be taken into account not only at its level, but also at others, which introduces additional complexity.

That is why we decided not to use GOAP for the monsters behaviours, since monsters rely extremely on animations which cannot be interrupted or blended. Therefore we used hierarchical FSM for them, but, actually, it did not solve non-instantaneous action issue. In prequel we get solution to this complexity by transferring part of the logic into the low level controllers: high level logic selects goals for the low-level controllers. Therefore instantaneous action change in the high level doesn’t mean instantaneous action change in the low level.

There is also a nuance in combining two methods for manipulating logic. At first, we introduced motivational graphs for taking some load off the GOAP planner. Eventually it was found that the planner coped with its task well, and the search never exceeded 200 vertices. Besides, determining the logic for traversing targets consisted of the topology graph author taking some of the work onto himself. Eventually, we stopped using motivational graphs at all, since it became easier to set all logic in a single place. Different goals were used only for live and dead characters (for clutching the trigger while dying).

Low-level controllers were responsible for animation, movement, controlling objects, sound, character orientation. Implementation of these controllers is not particularly interesting, except that we also used GOAP for controlling objects (and quite successfully, at that). In my view, it would be interesting to see an approach using a controller manager that would perform some partially ordered planning on operators of low-level controllers. Here, of course, the speed aspect of its performance is very important, since low-level controllers are typically updated each frame. On the other hand, it could also take up many issues with implicit interaction between the controllers. Maybe it could even be used not only for logic of low-level controllers, but for all of a character’s logic, as has been discussed on your site (Rethinking the AI/Animation Interface).

Alex Champandard: Since the final game had a story element to it, you also had certain areas in the game that were “scripted” in a more traditional way. Could you tell us more about how you integrated the A-Life system with the scripts?

Dmitriy Iassenev: As has been mentioned above, quests in the game are generated by smart terrains. This also applies to the storyline. Story quests have higher priority. Moreover, storyline areas are surrounded by so-called space restrictors. Their purpose consists of keeping the storyline characters from running too far away, and non-storyline characters from interfering with storyline elements. That is, there a significant amount of control over simulation. On the other hand, after the player completes the storyline scene, storyline characters become regular and follow regular rules, i.e. choose quests and go carry them out in the appropriate smart terrain locations. At the same time, the area is freed from all limitations.

Alex Champandard: Was the process of integrating these scripted areas with A-Life a difficult process for the designers? How did they approach the process?

Dmitriy Iassenev: It was difficult without space restrictors. Then, any “outsider” character would either break the storyline scene by distracting other characters, or, if they were not permitted to pay attention, stupid situations would occur, like having a character be bitten by a dog, not pay it any attention and finally die fully armed.

To generalize, one could say that space restrictors, along with careful management of information coming from receptors, solved this problem practically completely, in time.

Alex Champandard: In retrospect, what parts of the system and/or development process were you particularly happy with?

Dmitriy Iassenev: I was very happy with the work done when I followed a stalker from one level to another, saw how he searched for artefacts, found them, returned to the dealer, approached him, traded, picked a new quest, went on — too bad this did not make it into the original game. It was very interesting to witness.

When we were just implementing GOAP, it was very interesting to watch two hostile unarmed characters that knew were to get weapons and ammo: they first run to the weapons, find that they have no ammo, then run to the ammo, the first one to get there causes the other to panic if he is still far away from weapons and ammo, the panickier gets wounded and falls, the other approaches to finish him (this did not make it into the original game, since all characters received unlimited ammo, and such situations could not take place).

It was very interesting to watch a fight of several dogs and one stalker — it was breathtaking to see this sight, as he flees the dogs, switching covers. Sometimes he died quickly, sometimes managed to take down 4-5 dogs, and sometimes all 6.

Alex Champandard: How about disappointments? Was there anything in particular about the system or its development that you would have done differently?

Dmitriy Iassenev:

In simulation: I would very much like to play a game in which characters would live their own lives, each would have his own goal in the game, each would have human-like (or some specific for monsters) needs that the character would have to satisfy. Now, instead of creation of an algorithm for choice of current needs and their satisfaction, a simpler model has been used, which offloaded this work to the smart terrains, responsible for change of tasks by characters, able to assign tasks like “sleeping”, which, of course, is not the same thing.

In visuals: much can be done to improve fights, teamwork, efficiency of combat and efficiency of characters’ actions. I really want to switch from imitation of team actions, to real team AI, even if it means only a few actions.

In intellect: adaptation and teaching characters the player’s game style. The emotional part would also be very interesting.

Miscellaneous: I would really like to teach characters to adequately react to the surrounding world: walk around dynamic obstacles, each other, use the terrain (path and smart objects) in various ways, be able to drive vehicles, fly the helicopter, etc. There are many things they cannot do for various reasons, but in the future iterations of our game we will try to gradually add the missing elements.

Alex Champandard: What about the lessons learned? If you had thirty seconds to give advice to other game developers out there, what would it be?

Dmitriy Iassenev:

  1. Do not reinvent the wheel. Use the Internet to find solutions to your problems.
  2. Have a clear idea of the game you are making, of which features it will have and which it will not, use prototyping to avoid implementing features that do not make it into the release.
  3. Debug characters with comfortable tools: each component should have its debug draw/mode/screen. Draw paths (all the iterations, all the smooth stages and so on), draw visibility checks to find out why character doesn’t see, draw covers information and so on. To debug characters behaviour we created their debug screen with all the data from all the layers, so we can easily pick the character and find out what is wrong with him. Pause and time factor are invaluable for the animation/movement/orientation debug.
  4. Let programmers do programmer work and designers design. Designer is a bad programmer, programmer is a bad designer. This could become a problem when using scripts. Let people do things in which they are strong. Instead of advancing designers in programming, create WYSIWYP editor with many options to tune.
  5. Be aware of all the new developments in your area, read Internet resources, like AiGameDev.com :)
  6. Implement something new in your game. Maybe it is only one thing (since several can be too much for a single project), but necessarily new. We see an excellent implementation of decision trees in Black & White, Jeff Orkin has created an exceptional implementation of a GOAP planner in F.E.A.R., we have tried to make a kind of approximated A-Life in a shooter, maybe someone will make something new tomorrow — this will benefit the players, who will get diverse games, and developers themselves, who will set an example of a good method or approach.

Alex Champandard: Many thanks for your time Dmitriy, and best of luck with the upcoming project!


Implementation of custom physic objects schemes

To implement custom scheme for physical object create new script called

ph_scheme_name

for instance

ph_proximity_bomb

and implement new action class. Such class should contain methods:

  • __init()

    - constructor

  • reset_scheme()

    - called when scheme is activated

  • deactivate()

    - called on scheme deactivation

  • update()

    - update method

  • use_callback()

    - called when actor "use" object

  • hit_callback()

    - called when actor/npc hits object

  • death_callback()

    - called when actor/npc destroys object
-- ph_proximity_bomb.script

-- action class

class "action_proximity_bomb"

-- constructor
function action_proximity_bomb:__init(object, storage)
    self.object = object
    self.stor = storage
end

-- called on scheme deactivation
function action_proximity_bomb:deactivate(obj)
    self.object:set_tip_text("")
end

-- called on scheme activation
function action_proximity_bomb:reset_scheme()
    self.object:set_tip_text(self.stor.tips)
end

-- scheme update
function action_proximity_bomb:update(delta)
    if db.actor then
        -- handle all the common switch conditions, like 'on_info', 'on_actor_inside', etc
        if xr_logic.try_switch_to_another_section(self.object, self.stor, db.actor)
        then
            return
        end
    end
end

-- object usage
function action_proximity_bomb:use_callback(obj, actor)
    if self.stor.on_use then
        -- object was used by actor, find section to switch
        local section = xr_logic.pick_section_from_condlist(db.actor,
            self.object, self.stor.on_use.condlist)
        -- switch to section
        if xr_logic.switch_to_section(self.object, self.stor, section) then
            return true
        end
    end
end

-- object hit
function action_proximity_bomb:hit_callback(obj, amount, local_direction, who, bone_index)
    if who:id() == db.actor:id() then
        print("object '%s' was hit", self.object:name())
    end
end

-- object death
function action_proximity_bomb:death_callback(obj, who)
    if who:id() == db.actor:id() then
        print("object '%s' destroyed", self.object:name())
    end
end
To set and unset the scheme we need to define functions:

  • set_scheme()

    - loads scheme's data and sets scheme for object

  • disable_scheme()

    - unsets the scheme

  • add_to_binder()

    - binds object with action (scheme)
In function

set_scheme

we call function

assign_storage_and_bind

which creates

storage

array for our scheme and calls function

add_to_binder

from our script which sets action (scheme) for object. Then we set

logic

field in

storage

array using function

cfg_get_switch_conditions

Aftear that we can use several common switch conditions like

on_info, on_signal, ...

Next, we read all data used by our scheme and we save it in

storage

array, thanks to that we can use this data in action class.
-- ph_proximity_bomb.script

-- function sets new action for object
function add_to_binder(object, ini, scheme, section, storage)
    local new_action = action_proximity_bomb(object, storage)
    xr_logic.subscribe_action_for_events(object, storage, new_action)
end

-- function loads scheme data
function set_scheme(object, ini, scheme, section, gulag_name)
    local st = xr_logic.assign_storage_and_bind(object, ini, scheme, section)
    st.logic = xr_logic.cfg_get_switch_conditions(ini, section, object)

    -- read scheme settings from object's custom_data field
    st.on_use = xr_logic.cfg_get_condlist(ini, section, "on_use", object)
    st.tips = utils.cfg_get_string(ini, section, "tips", object, false, "", "")
end

-- function unsets object's action
function disable_scheme(object, scheme)
    local st = db.storage[object:id()][scheme]
    if st then
        xr_logic.unsubscribe_action_from_events(object, st, st.action)
    end
end
To disable our scheme, we also need to edit

xr_logic.script

-- xr_logic.script

function disable_generic_schemes(npc, stype)
  if stype == modules.stype_stalker then
    ...
  elseif stype == modules.stype_item then
    ph_proximity_bomb.disable_scheme(npc, "ph_proximity_bomb")
    ...
  end
end
To enable scheme, edit

modules.script

:
--           script_name,         scheme_name,        scheme_type
load_scheme("ph_proximity_bomb", "ph_proximity_bomb", stype_item)
Spawn object in all.spawn:

- to use hit and death callbacks we spawn object as

physic_destroyable_object

- in

custom_data

field we add section for our scheme and we set variables which will be used by our scheme
[189]
; cse_abstract properties
section_name = physic_destroyable_object
name = esc_proximity_bomb1
position = -141.698059082031,-22.8454513549805,-353.901885986328
direction = 0.0872493088245392,3.1415901184082,-9.62032299867133e-006

; cse_alife_object properties
game_vertex_id = 0
distance = 0
level_vertex_id = 594270
object_flags = 0xffffff3a
custom_data = «END
[logic]
active = ph_proximity_bomb@before_blow
on_hit = hit

[ph_proximity_bomb@before_blow]
tips = "Don't touch it, it's gonna blow ..."
on_actor_dist_le = 10 | ph_proximity_bomb@detonate
on_use = ph_proximity_bomb@detonate
on_info = {+detonate_now} ph_proximity_bomb@detonate

[ph_proximity_bomb@detonate]
tips = "Run ..."
on_timer = 5000 | nil %+detonate()%

[hit]
on_info = %+detonate_now%
END

; cse_visual properties
visual_name = physics\box\expl_dinamit

; cse_ph_skeleton properties

; cse_alife_object_physic properties
physic_type = 0x3
mass = 10
Finally, add

detonate()

function to

xr_effects.script

function detonate(actor, obj)
  local snd = xr_sound.get_safe_sound_object("weapons\\f1_explode")
  snd:play_no_feedback(obj, sound_object.s3d, 0, obj:position(), 1.0)

  local p = particles_object("explosions\\explosion_fuelcan")
  p:play_at_pos(obj:position())

  if actor:position():distance_to(obj:position()) <= 2 then
    actor:kill(actor)
  end

  p:stop()
  p = nil
end


Implementation of custom space restrictors schemes

Space restrictor schemes keeps tracking actor's position, if actor is inside zone then certain action is performed. To implement custom scheme for space restrictor create new script called

sr_scheme_name

for instance

sr_dead_zone.script

and implement new action class. Such class should contain methods:

  • __init()

    - constructor

  • reset_scheme()

    - called when scheme is activated

  • deactivate()

    - called on scheme deactivation

  • update()

    - update method
local state_inside = 1
local state_outside = 2

class "action_kill_actor"

-- constructor
function action_kill_actor:__init(obj, storage)
  self.object = obj
  self.st = storage
end

-- scheme activation
function action_kill_actor:reset_scheme()
  self.time_to_die = self.st.time_to_die
  self:switch_state()
end

-- scheme deactivation
function action_kill_actor:deactivate()
end

-- scheme update
function action_kill_actor:update()
  if not db.actor:alive() then
    return
  end

  if self.state == state_inside then
    -- actor is inside zone more then X minutes
    if game.get_game_time() > self.timer then
      db.actor:kill(db.actor)
      return
    end
  end

  if xr_logic.try_switch_to_another_section(self.object, self.st, db.actor) then
    return
  end

  self:switch_state()
end

-- keep tracking actor's position
function action_kill_actor:switch_state()
  if self.object:inside(db.actor:center()) then
    if self.state ~= state_inside then
      self:zone_enter()
    end
  elseif self.state ~= state_outside then
    self:zone_leave()
  end
end

-- actor enters zone
function action_kill_actor:zone_enter()
  self.state = state_inside

  local message = "You have " .. self.time_to_die .. " minutes to leave this area!"
  news_manager.send_tip(db.actor, message)

  self.timer = game.CTime()
  self.timer:setHMSms(0, self.time_to_die, 0, 0)
  self.timer = self.timer + game.get_game_time()
end

-- actor left zone
function action_kill_actor:zone_leave()
  self.state = state_outside
  self.timer = nil
end
To set the scheme we need to define functions:

  • set_scheme()

    - loads scheme's data and sets scheme for object

  • add_to_binder()

    - binds object with action (scheme)
In function

set_scheme

we call function

assign_storage_and_bind

which creates

storage

array for our scheme and calls function

add_to_binder

from our script which sets action (scheme) for object. Then we set

logic

field in

storage

array using function

cfg_get_switch_conditions

Aftear that we can use several common switch conditions like

on_info, on_signal, ...

Next, we read all data used by our scheme and we save it in

storage

array, thanks to that we can use this data in action class.
function add_to_binder(npc, ini, scheme, section, storage)
  local new_action = action_kill_actor(npc, storage)
  xr_logic.subscribe_action_for_events(npc, storage, new_action)
end

function set_scheme(npc, ini, scheme, section, gulag_name)
  local st = xr_logic.assign_storage_and_bind(npc, ini, scheme, section)
  st.logic = xr_logic.cfg_get_switch_conditions(ini, section, npc)
  st.time_to_die = utils.cfg_get_number(ini, section, "time_to_die", npc, false, 5)
end
To enable scheme, edit

modules.script

--           script_name,    scheme_name,  scheme_type
load_scheme("sr_dead_zone", "dead_zone",   stype_restrictor)
Spawn object in all.spawn:
[108]
; cse_abstract properties
section_name = space_restrictor
name = esc_temp_dead_zone
position = -131.080963134766,-3.83071899414063,-29.8998317718506
direction = 0.111189842224121,-0.00129711860790849,0.011689349077642

; cse_alife_object properties
game_vertex_id = 0
distance = 0
level_vertex_id = 594270
object_flags = 0xffffff3e
custom_data = «END
[logic]
active = dead_zone

[dead_zone]
time_to_die = 8 ;-- minutes
END

; cse_shape properties
shapes = shape0
shape0:type = sphere
shape0:offset = 0,0,0
shape0:radius = 15.5

; cse_alife_space_restrictor properties
restrictor_type = 3


Implementation of custom stalker scheme

Ai schemes which controls behaviour of stalkers differs from other types of schemes. Each stalker scheme contains at least one evaluator (property) and at least one action (operator). Evaluator evaluates (calculates) conditions which have to be met to perform action. If all the conditions are met then the action will be performed. All stalkers have special object called planner. Planner runs in background and checks all evaluators. If certain evaluator returns

true

then assigned action is performed. For instance if we are going to implement scheme which gives stalkers ability to throw grenades, then:

evaluator - conditions to throw a grenade:
  • stalker have to see an enemy
  • enemy have to be localized in certain distance (for instance: less then 30 meters)
  • stalker should have a grenade in his backpack
  • throw probability would be a good idea, to avoid "grenade spamming"
action - throw a grenade, this involves:
  • calculating grenade fly-path (including code for avoiding obstacles)
  • time to explode the grenade
What is calculated in evaluator and what action is performed depends on scheme purpose. Evaluator inherits from

property_evaluator

class. Each evaluator should include

evaluate()

method which calculates all the conditions. If

evaluate()

method returns

true

(conditions are met) then an action is performed.
class "evaluator_my_evaluator" (property_evaluator)

-- constructor
function evaluator_my_evaluator:__init(storage, evaluator_name) super(nil, evaluator_name)
  self.stor = storage
end

function evaluator_my_evaluator:evaluate()
  -- function to evaluate conditions; function is_active()
  -- returns true if scheme is active, false otherwise

  return xr_logic.is_active(self.object, self.stor)
end
To define action we inherit from

action_base

class. When scheme is activated then method

activate_scheme()

is called, then method

initialize()

and then planner periodically invokes method

execute()

class "my_action" (action_base)

-- constructor
function my_action:__init(action_name, storage) super(nil, action_name)
  self.stor = storage
end

function my_action:initialize()
  action_base.initialize(self)

  -- this method is called only once,
  -- right before "execute" method
end

function my_action:activate_scheme(loading, npc)
  -- function is called once,
  -- when scheme is activated
end

function my_action:execute()
  action_base.execute(self)

  -- this method works as an update function
  -- here you should add code to perform actions
end

function my_action:finalize()
  -- this function is called when action
  -- was performed and evaluator fails

  action_base.finalize(self)
end
To set scheme we need to bind it to npc and set scheme settings (if any). We gain access to planner object using

motivation_action_manager()

function, then we add evaluators (properties) to actions (operators) and finally, we add actions to planner. Actions and evaluators are numbered, that is you have to set unique numbers for your evaluators and actions. To do this, you can use tables:
local operators	= {}
local properties = {}

properties["my_evaluator"] = 6450
operators["my_action"] = 6451
or constants:
local MY_PROPERTY = 6450
local MY_ACTION = 6451
It doesn't really matter which way you choose (constants or tables), it is important to use unique numbers for evaluators and actions. To make sure you are using unique numbers, check scripts:

xr_actions_id

and

xr_evaluators_id

. Now we add our evaluator to planner:
planner:add_evaluator(properties["my_evaluator"], evaluator_my_evaluator(storage, "my_evaluator"))
To add evaluators to action we use method

add_precondition()

. There are several basic preconditions which have to be met to perform the action:

  • property_alive

    - npc is alive

  • property_danger

    - no danger near by

  • property_enemy

    - no enemies in sight

  • property_anomaly

    - no anomalies around
We use

world_property

object to point what value should be returned by certain evaluator. For instance:
world_property(stalker_ids.property_alive, true)
means that npc have to be alive (

property_alive

evaluator returns

true

) but:
world_property(stalker_ids.property_enemy, false)
there must be no enemies around. Despite basic evaluators we also add our evaluator as precondition (we point what value our evaluator should return to perform action) and effect (opposite value which will stop action):
add_precondition(world_property(properties["my_evaluator"], true))
add_effect(world_property(properties["my_evaluator"], false))
We can also add common preconditions:
  • actor is not going to talk to the npc
  • npc is not wounded
  • npc is not going to hunt helicopters
  • npc is not punching actor
xr_motivator.addCommonPrecondition(new_action)
When we finally add our action to planner, we should add alife actions:
new_action = planner:action(xr_actions_id.alife)
new_action:add_precondition(world_property(properties["my_evaluator"], false))
in other words, if our evaluator returns

false

value then our action won't be executed that is - npc should perform basic actions (like walking on map, etc.). Using

subscribe_action_for_events()

we add our action for npc.
function add_to_binder(npc, ini, scheme, section, storage)
  local operators	= {}
  local properties = {}

  local planner = npc:motivation_action_manager()

  properties["my_evaluator"] = 6450
  operators["my_action"] = 6451

  planner:add_evaluator(properties["my_evaluator"],
    evaluator_my_evaluator(storage, "my_evaluator"))

  local new_action = my_action("my_action", storage)
  new_action:add_precondition(world_property(stalker_ids.property_alive, true))
  new_action:add_precondition(world_property(stalker_ids.property_danger, false))
  new_action:add_precondition(world_property(stalker_ids.property_enemy, false))
  new_action:add_precondition(world_property(stalker_ids.property_anomaly, false))
  new_action:add_precondition(world_property(properties["my_evaluator"], true))
  xr_motivator.addCommonPrecondition(new_action)
  new_action:add_effect(world_property(properties["my_evaluator"], false))
  planner:add_action(operators["my_action"], new_action)

  xr_logic.subscribe_action_for_events(npc, storage, new_action)

  new_action = planner:action(xr_actions_id.alife)
  new_action:add_precondition(world_property(properties["my_evaluator"], false))
end

function set_scheme(npc, ini, scheme, section, gulag_name)
  local stor = xr_logic.assign_storage_and_bind(npc, ini, scheme, secttion)

  -- here load scheme settings (if your scheme supports any)
  -- for instance:

  stor.tips = utils.cfg_get_string(ini, section, "tips", npc, false, gulag_name)
  stor.show = utils.cfg_get_bool(ini, section, "show", npc, false, true)
  stor.show_time = utils.cfg_get_number(ini, section, "show_time", npc, false, 15000)
end

Stalker salute scheme

As an example we implement ai scheme for npcs which force them to salute actor. The scheme have to work in background. Assumptions:
  • only soldiers with rank less than master can salute
  • wounded npcs won't salute
  • npcs with active camper, kamp or sleeper schemes won't salute
  • npc have to see actor
  • actor have to be near by the npc
  • no enemies, anomalies and danger around
Sometimes, if we have several conditions to check in evaluator, it is more convenient to implement special class which will handle all the conditions. We will implement

SaluteManager

class to test all conditions:
local salute_npc_rank =
{
  novice = true,
  stalker = true,
  veteran = true,
  master = false
}

local SALUTE_STATE_ACTIVE = 0
local SALUTE_STATE_SUSPENDED = 1

class "SaluteManager"

function SaluteManager:__init(npc, storage)
  self.npc = npc
  self.storage = storage
  self.distance = 10
  self.state = SALUTE_STATE_ACTIVE
  self.expire_time = 80 -- 80 sec to suspend scheme
  self.salute_time = nil
end

-- if npc has active scheme: camper, kamp or sleeper
-- then this npc won't salute
function SaluteManager:DoNotInterruptScheme()
  local active_scheme = db.storage[self.npc:id()].active_scheme
  if active_scheme == "camper" or active_scheme == "kamp"
    or xr_sleeper.is_npc_asleep(self.npc)
  then
    return true
  end
  return false
end

-- basic preconditions
function SaluteManager:basic_precond()
  if not db.actor then
    return false
  end

  if not db.actor:alive() then
    return false
  end

  if self:DoNotInterruptScheme() then
    self:suspend_scheme(nil)
  end

  return self:scheme_enabled()
end

-- returns true if scheme is enabled
-- (active), false otherwise
function SaluteManager:scheme_enabled()
  if self.state == SALUTE_STATE_ACTIVE then
    return true
  end
  return false
end

-- function suspends scheme (action)
function SaluteManager:suspend_scheme(timer)
  if timer == nil then
    self.state = SALUTE_STATE_SUSPENDED
    return
  end

  if timer:diffSec(self.salute_time) > self.expire_time then
    self.state = SALUTE_STATE_SUSPENDED
  end
end

-- update method, we will use this in evaluator
function SaluteManager:update()
  if self:basic_precond() and salute_npc_rank[ranks.get_obj_rank_name(self.npc)] and
    self.npc:position():distance_to(db.actor:position()) <= self.distance
  then

    if self.salute_time == nil then
      self.salute_time = game.get_game_time()
    end

    return true
  end

  return false
end
Now we implement evaluator:
class "evaluator_salute" (property_evaluator)

function evaluator_salute:__init(evaluator_name, storage) super(nil, evaluator_name)
  self.storage = storage
end

function evaluator_salute:evaluate()

  -- we use value returned by update method of SaluteManager object
  -- we set SaluteManager object in add_to_binder function

  -- we add SaluteManager object to storage object which we
  -- pass as an argument to our evaluator

  return self.storage.salute_manager:update()
end
Now it's time to implement salute action. We perform action in

initialize()

method (to force npc to salute only once) and in

execute()

method we will wait until evaluator return

false

that is when salute animation ends:
class "action_salute_priboi" (action_base)

function action_salute_priboi:__init(action_name, storage) super(nil, action_name)
  self.storage = storage
end

function action_salute_priboi:initialize()
  action_base.initialize(self)
  self.object:set_desired_position()
  self.object:set_desired_direction()
  self:perform_action()
end

function action_salute_priboi:perform_action()
  local anim = nil

  if math.random(103, 1324) % 2 == 0 then
    anim = "salut"
  else
    anim = "salut_free"
  end

  state_mgr.set_state(self.object, anim, nil, nil,
  {look_object = db.actor}, {animation = true})
end

function action_salute_priboi:execute()
  action_base.execute(self)

  local stor = db.storage[self.object:id()]["salute"]
  if stor then
    if stor.salute_manager:scheme_enabled() then
      stor.salute_manager:suspend_scheme(game.get_game_time())
    end
  end
end

function action_salute_priboi:finalize()
  self.object:clear_animations()
  action_base.finalize(self)
end
The scheme is ready, now we have to bind it to npcs (enable salute action for npcs)
function enable_scheme(npc, ini, scheme, section)
  -- only soldier can salute
  if npc:character_community() == "military" then
    xr_logic.assign_storage_and_bind(npc, ini, scheme, section)
  end
end
However we can also bind action for all npcs and then in

add_to_binder

function we can set our evaluator to always return

false

value for npcs which won't salute at all:
function enable_scheme(npc, ini, scheme, section)
  xr_logic.assign_storage_and_bind(npc, ini, scheme, section)
end

local SALUTE_PROPERTY = 6432
local SALUTE_OPERATOR = 6435

function add_to_binder(object, ini, scheme, section, storage)
  -- gain access to planner
  local planner = object:motivation_action_manager()

  -- add evaluator to planner:
  if npc:character_community() ~= "military" then
    -- npc is not a soldier, set evaluator to always return false value
    -- thanks to that, an action won't be performed
    planner:add_evaluator(SALUTE_PROPERTY, property_evaluator_const(false))
    return
  else
    -- npc is soldier, so to perform action planner will evaluate conditions
    -- if conditions are met then evaluator will return true and action will be performed
    planner:add_evaluator(SALUTE_PROPERTY, evaluator_salute("evaluator_salute", storage))
    end

  local newAction = action_salute_priboi("action_salute_priboi", storage)

  -- add basic preconditions
  newAction:add_precondition(world_property(stalker_ids.property_alive, true))
  newAction:add_precondition(world_property(stalker_ids.property_danger, false))
  newAction:add_precondition(world_property(stalker_ids.property_enemy, false))
  newAction:add_precondition(world_property(stalker_ids.property_anomaly, false))
  newAction:add_precondition(world_property(xr_evaluators_id.sidor_wounded_base, false))

  -- add our evaluator as precondition and effect
  newAction:add_precondition(world_property(SALUTE_PROPERTY, true))
  newAction:add_effect(world_property(SALUTE_PROPERTY, false))

  -- add salute action to planner
  planner:add_action(SALUTE_OPERATOR, newAction)

  -- add alife actions
  newAction = planner:action(xr_actions_id.alife)
  newAction:add_precondition(world_property(SALUTE_PROPERTY, false))

  -- add to storage object of SaluteManager class
  storage.salute_manager = SaluteManager(object, storage)
end
Also we will add function to disable scheme:
function disable_scheme(npc, scheme)
  local stor = db.storage[npc:id()][scheme]
  if stor then
    stor.salute_manager:suspend_scheme(nil)
  end
end
To enable salute scheme we have to:
  • add scheme to

    modules.script

    • if xr_salute then
  load_scheme("xr_salute", "salute", stype_stalker)
end
  • edit

    enable_generic_schemes

    function (

    xr_logic.script

    )
    • function enable_generic_schemes(ini, npc, stype, section)
  if stype == modules.stype_stalker then
    ...
    if xr_salute then
      xr_salute.enable_scheme(npc, ini, "salute", section)
    end
    ...
  end
end
  • edit

    disable_generic_schemes

    function (

    xr_logic.script

    )
    • function disable_generic_schemes(npc, stype)
  if stype == modules.stype_stalker then
    ...
    if xr_salute then
      xr_salute.disable_scheme(npc, "salute")
    end
    ...
  end
end


camper

— campter stands at a spot and looks in the given direction or moves along patrol paths
— campers switch to all-purpose combat when they see an enemy closer than 30 meters away. If a camper survives, he returns to camper mode
— campers do not move and attack. If they see an enemy, they stop, shoot, then continue movement
— in all other situations camper acts according to scheme:

- if an enemy is in sight - shoot,
- if danger is heard - look in its direction
- if a grenade is spotted - run away
- if an enemy was in sight, but disappeard, look in the direction of last sighting of the enemy.
  • related files:
    • all.spawn
    • modules.script
    • xr_camper.script
    • xr_danger.script
path_walk = <patrol>
set of points which npc should reach
path_look = <patrol>
points to look at
sniper = <boolean> { default: false }
if set to

true

then camper shoots only single shots and is not looking along patrol points, but rather scanning space between them. Scan speed between two points is fixed at 20 seconds. Note: set only two look points for a sniper.
no_retreat = <boolean> { default: false }
if

true

then the character won't go towards the nearest

path_walk

point upon enemy sighting, but starts attacking him immediately. Needed in case you want to create a scene with some guys hitting on some others. Set this flag for the campers. They walk along their patrol points and take out the enemies.
shoot = <const> { default: "always" }
type of shooting:
none
no shooting at all
always
shoot whenever possible
terminal
shoot only at the last path point
radius = <number> { default: 20 }
distance in meters; if the distance between the camper and the enemy is less, camper switches to all-purpose combat mode
def_state_standing = <string> { default: "" }
state in which camper stays in camper point
def_state_moving = <string> { default: "" }
state in which camper move to the nearest point upon an enemy sighting
def_state_moving_fire = <string> { default: "sneak_fire" }
state in which camper shoot the enemy while moving towards the nearest path point
def_state_campering = <string> { default: "hide" }
state in which camper ambush an enemy while remaining on the path (waiting for enemy)
def_state_campering_fire = <string> { default: "hide_fire" }
state in which camper shoot an enemy while moving along the path
attack_sound = <string> { default: "fight_attack" }
ability to overwrite attack sound for snipers/campers; to disable use

attack_sound = false

scantime_free = <number> { default: 60000 }
obsolete
enemy_idle = <number> { default: 60000 }
obsolete
Note: you can use standard conditions to switch sections as described in Scheme sections switching
The camper has a large drawback - when he is hit and he does not know where the hit comes from (because he does not see the enemy or does not hear the shot), he daftly remains standing at the old spot and wait for the next bullet. In sight of this, you should not set campers to guard a position if there are several directions from which the player or other stalkers can attack the camper. Use walkers in such cases; while setting campers to attack along paths or using them as snipers.


draining

Scheme was designed to drain health from actor with specific particle and sound. Also has finalising signal

draining_complete

with which you can switch section to another scheme.
  • related files:
    • all.spawn
    • modules.script
    • xr_draining.script
Example:
[logic]
active = draining@test

[draining@test]
combat_ignore_cond = always
on_signal = draining_complete | %+some_info% walker@go


follower

NPC follows the leader. If the leader is closer than 5 meters, npc walks, if 5 to 20 — runs, if more than 20 — sprints. Paths are not set.
  • related files:
    • all.spawn
    • modules.script
    • xr_attendant.script
leader = <string> story_id | actor { default: "" }

Now actor also supported!

If npc follows actor better to set

target = actor

too.
story id of the leader; if follower scheme is set in a gulag, then story_id of the leader should be replaced by his logic sections in the script file. Example:
t =
{
    section = "logic@bar_arena_follower_2",
    idle = 0, prior = 7, state = {0},
    squad = squad, group = groups[0],
    in_rest = "", out_rest = "",
    dependent = "logic@bar_arena_leader",
    predicate = function(obj)
        return obj:character_community() == "dolg"
    end
}
anim_walk = <string> { default: "patrol" }
state for the follower to walk behind the leader
anim_run = <string> { default: "run" }
state for the follower to run after the leader
anim_sprint = <string> { default: "sprint" }
state for the follower to sprint after the leader
formation_line = <boolean> { default: "false" }
try to stay to the side of the leader, otherwise walk behind
distance = <number> { default: 1.5 or 5 }
distance in meters to keep away from the leader at; 1.5 meters by default, if walks in a chain, then 5 meters
snd = <string> { default: "state" }
sound theme; to disable, set

snd = "silent"

state_if_leader_in_meet = <string> { default: "idle" }
state for follower if the leader is in the meet state
stub_for_corpse = <boolean> { default: false }
whether the follower should stay near leader corpse
dont_change_job = <boolean> { default: false }
wheter reset job for followers


heli_hunter

NPC hunts helicopters. Usually combined with walker scheme.
  • related files:
    • all.spawn
    • modules.script
    • xr_heli_hunter.script
dist = <number>
if distance between heli and npc is equal or less then

dist

then npc will attack heli
actor_dist = <number>
if distance between actor and npc is less then

actor_dist

then npc will attack actor
Example:
[logic]
active = walker1
heli_hunter = heli

[heli]
dist = 40

[walker1]
path_walk = aes_monolit_heli_hunter_walk1
path_look = aes_monolit_heli_hunter_look1
heli_hunter = true


kamp

Scheme for a stalker sitting in a given radius from a given point (by a campfire) and facing this point.
  • related files:
    • all.spawn
    • modules.script
    • xr_kamp.script
center_point = <patrol>
center point of camp (usually it is a center of fire)
path_walk = <patrol>
to avoid stalker deaths inside fire - read below
radius = <number> { default: 2 }
how far from the

center_point

the npc will sit
def_state_moving = <string> { default: "walk" }
default state (animation) in which the stalker will go towards

center_point

Example:
[kamp@around_fire]
center_point = esc_kamp_around_fire_center
path_walk = esc_kamp_around_fire_task
radius = 2
def_state_moving = walk
If the camp point is inside a campfire, then in offline mode stalkers will approach it and in online mode they will spawn inside the fire. To avoid this, in the camp section set path_walk from a point called <path_camp_name>_task. If the camp point is located in an open field, then there is no need for adding path_walk patrol.

Implementation of kamp scheme allow only 8 npcs per kamp - more than 8 npcs around one camp = CTD !!!


meet

Scheme defines behaviour of stalker during talking with actor.
  • related files:
    • all.spawn
    • modules.script
    • xr_meet.script
Syntax
[logic]
meet = meet

[walker]
meet = meet

[meet]
meet_state     = 30| state@sound| 20| state@sound| 10| state@sound
meet_state_wpn = 30| state@sound| 20| state@sound| 10| state@sound
victim         = 30| nil| 20| actor
victim_wpn     = 30| nil| 20| actor
use            = self
use_wpn        = false
zone           = name| state@sound
meet_dialog    = dialog_id
synpairs       = state@sound|state@sound
abuse          = true/false
meet_state, meet_state_wpn = <list: distance|animation@sound>
sets animation and voicing for the character, depending on distance to the actor and whether the actor is unarmed or armed, respectively. Format:
field = distance | animation
field = distance | animation @ sound
field = distance | condition_list animation @ condition_list sound
victim, victim_wpn = <list: distance|value>
sets the object at which the character will look:
nil
does not look anywhere
actor
looks at the player
story_id
story id of object to look at
field = distance | actor
field = distance | nil
field = distance | story_id
field = distance1 | actor | distance2 | nil | distance3 | story_id
use, use_wpn = <condition_list>
describes if the character will talk with actor when actor "use" npc. Value

self

means that npc will "use" actor that is the dialog will be displayed even when actor did not "use" the npc. Format:
field = true
field = false
field = self
field = condition_list self, condition_list true, false
meet_dialog = <condition_list>
starting dialogs for the NPC. Format:
meet_dialog = dialog_id
meet_dialog = condition_list dialog_id1, dialog_id2, ...
zone = <list: restrictor|anim@sound>
contains a set of restrictor names, as well as animations and sounds the NPC will play, if the player is sighted within a restrictor. Format:
zone = restrictor_name | animation
zone = restrictor_name | animation @ sound
zone = restrictor_name | condition_list animation @ condition_list sound
synpairs = <list: animation@sound>
contains a set of pairs animation@sound_theme to synchronize with. Format:
synpairs = animation @ sound | animation @ sound | ...
abuse = <boolean>
if

true

then character will punch actor when actor abuse the character (when actor will point his gun towards character and try to "use" the character)
precond = <const> : visibility | usability
meet precondition - describes when animations and sounds are played:
visibility
when npc see actor
usability
when actor "use" npc
init_meet = <string>
sound name; will be played when actor "use" npc
To disable meet scheme in certain section use meet = no_meet, for instance:
[walker]
meet = no_meet
To use default meet settings you can use meet = default_meet (or you can skipt it - default settings will be used anyway), for instance:
[walker]
meet = default_meet


patrol

Several npcs walks together in formation. One of them is leader, the rest follow him. When the commander moves, he works like a regular walker, and the others follow his actions. That is, if leader rush forward with his weapon at ready, the others does the same. If the commander dies, another one is chosen automatically (the first who enter the scheme). When the leader is in

meet

state, the rest will stop and wait.
  • related files:
    • all.spawn
    • modules.script
    • move_mgr.script
    • xr_patrol.script
path_walk = <patrol>
set of points which npc should reach
path_look = <patrol>
points to look at
formation = <const> : back | line | around { default: "back" }
formation type
move_type = <string> { default: "patrol" }
sets the initial movement mode (animation)
def_state_standing = <string> { default: "guard" }
state in which the npc stands at the point and looks
def_state_moving1 = <string> { default: "patrol" }
state in which the npc moves towards the first point if it is nearby
def_state_moving2 = <string> { default: "rush" }
state in which the npc moves towards the first point if it is further
def_state_moving3 = <string> { default: "sprint" }
state in which the npc moves towards the first point if it is far away
animation = <string> { default: "" }
animation
commander = <boolean> { default: false }
sets the commander; only one is allowed
Note:

- you can use standard conditions to switch sections as described in Scheme sections switching
- only max 7 npcs allowed in one patrol
You may add formation to waypoints, using

ret

flag:
p0:name = wp00|ret = 0 ; formation = line
p0:name = wp00|ret = 1 ; formation = around
p0:name = wp00|ret = 2 ; formation = back
The scheme is not fully implemented:
  • there is no way to modify formation
  • everyone is quiet while they should tell stories
  • commander does not give commands
  • it is not recommended to use sprinting


remark

Scheme is used for synchronization with other schemes, usually to play sound and animation and then switch to other scheme.
  • related files:
    • all.spawn
    • modules.script
    • xr_remark.script
snd_anim_sync = <boolean> { default: false }
whether synchronize animation with sound
snd = <string> { default: nil }
sound to play
anim = <string> { default: "wait" }
npc animation;

idle

animation is forbidden
tips = <string> { default: "" }
name of (storyline) sound to play -

news_manager.script

(table:

news

)
tips_sender = <string> { default: "" }
used only if

tips

field is specified; however this parameter is obsolete
anim_reset = <boolean> { default: true }
whether reset the animation or not
target = <const> { default: "nil" }
object to look at; possible values:
nil
position calculated by engine
actor
player
story_id
object's story id
<job_name>, <gulag_name>
look at a stalker that has a certain job in a certain gulag (the second parameter is not required, if omitted, the gulag of the stalker to which the given section applies is used). Example:
target = logic@cit_killers_base_guard, cit_killers
<patrol_name>, <point_number>
look at a vertex of given patrol
Note: you can use standard conditions to switch sections as described in Scheme sections switching
Signals:

  • sound_end

    - when the sound (

    snd

    ) finish playing

  • anim_end

    - when the animations finish playing

  • action_end

    - when both finish, if synchronized
Example:
[remark]
anim = animation
snd = sound
snd_anim_sync = true
on_signal = action_end | next scheme


sleeper

Scheme for a sleeping npc. It must have a path consisting of at least one waypoint. The sleeper will sit down at the zeroth point of the path and face in the direction of the first point.
  • related files:
    • all.spawn
    • modules.script
    • move_mgr.script
    • xr_sleeper.script
path_main = <patrol>
waypoint consisting of at least one point; if the path constsists of two points, there should be a link from the first to the zeroth (or a bidirectional one).
wakeable = <boolean> { default: false }
whether the character can wake up
Note: you can use standard conditions to switch sections as described in Scheme sections switching
Example:
[sleeper@house]
path_main = esc_sleeper_house
wakeable = true


walker

Basic scheme that moves a character along waypoints (

path_walk

), stops at certain points, executes certain actions.
  • related files:
    • all.spawn
    • modules.script
    • move_mgr.script
    • xr_walker.script
path_walk = <patrol>
set of points which npc should reach
path_look = <patrol> { default: nil }
points to look at; this parameter is optional however if

path_walk

contains only one point then it is good idea to add

path_look

otherwise stalker will look into random direction
team = <string> { default: nil }
team to synchronize with; all walkers in a team must have the same team. It is desirable to specify level name and location in team; for instance: escape_bridge, escape_factory — this will reduce the chance of error of giving different teams the same name.
sound_idle = <string_list> { default: "" }
comma-separated list of idle npc sounds; use sound themes from

sound_theme.script

(table

theme

)
def_state_standing = <string> { default: "guard" }
state in which the npc stands at the point and looks
def_state_moving1 = <string> { default: "patrol" }
state in which the npc moves towards the first point if it is nearby
def_state_moving2 = <string> { default: "rush" }
state in which the npc moves towards the first point if it is further
def_state_moving3 = <string> { default: "sprint" }
state in which the npc moves towards the first point if it is far away
Note: you can use standard conditions to switch sections as described in Scheme sections switching
Example:
[walker@esc_loners]
path_walk = loner1_walk
path_look = loner1_look
def_state_moving1 = patrol
def_state_moving2 = patrol
def_state_moving3 = patrol
def_state_standing = binocular
team = killers
sound_idle = state


wounded

Scheme describe wounded stalker behaviour (animations, sounds, dialogs, ability to use medkit, etc)
  • related files:
    • all.spawn
    • modules.script
    • xr_wounded.script
Syntax
[logic]
wounded = wounded

[walker]
wounded = wounded

[wounded]
hp_state          = coef|condlist anim@condlist sound|coef|condlist anim@condlist sound
hp_state_see      = coef|condlist anim@condlist sound|coef|condlist anim@condlist sound
psy_state         = coef|condlist anim@condlist sound|coef|condlist anim@condlist sound
hp_victim         = coef|condlist victim|coef|condlist victim
hp_cover          = coef|condlist bool|coef|condlist bool
hp_fight          = coef|condlist bool|coef|condlist bool
syndata           = anim@sound|anim@sound
help_dialog       = dialog_id
help_start_dialog = dialog_id
hp_state = <format#1>
animation and sound when npc is wounded and actor isn't visible
hp_state_see = <format#1>
animation and sound when npc is wounded and npc see actor
psy_state = <format#1>
animation and sound for psychic (mental) state
hp_victim = <list: coef|value>
point (which depends on coefficient value) to look at. Format:
hp_victim = coefficient | actor
hp_victim = coefficient | nil
hp_victim = coefficient | story_id
hp_cover = <coef|value>
go to cover or not. Format:
hp_cover = coefficient | true
hp_cover = coefficient | false
hp_fight = <coef|value>
combat allowed or not. Format:
hp_fight = coefficient | true
hp_fight = coefficient | false
syndata = <list: animation@sound>
synchronization of animation and sound. Format:
syndata = animation @ sound | animation @ sound | ...
help_dialog = <string>
dialog started by npc, sth like: "Stalker please, give me a medkit"
help_start_dialog = <string>
dialog started by actor, sth like: "Stalker may i help you? I'll give you my medkit"
use_medkit = <boolean>
whether the npc can use medkit (only once), if he is wounded - only if npc contains medkit in his backpack
format#1

Fields marked with format#1 are using following formats:
field = coefficient | animation
field = coefficient | animation @ sound
field = coefficient | condition_list animation @ condition_list sound
Coefficients are calculated like this:
health_coef = current_npc_health * 100
psy_coef = current_npc_psy_health * 100
for instance: (the whole field is long, to make it more readable i splitted it up - in psy_state field replace "fifty" and "twenty" with proper content)
psy_state = 50 | fifty | 20 | twenty

whre:

fifty = {=best_pistol} psy_armed, psy_pain @ wounded_psy
twenty = {=best_pistol} psy_shoot, psy_pain @ {=best_pistol} wounded_psy_shoot, wounded_psy
- if 50 >= psy_coef (current npc psy health * 100) then:
- if npc has activated pistol then psy_armed animation will be set
- no pistol - animation psy_pain + wounded_psy sound

- if 20 >= psy_coef then:
- if npc has activated pistol then animation psy_shoot
- no pistol - animation psy_pain + sound:
- wounded_psy_shoot if npc activated pistol
- wounded_psy otherwise


zoneguard

There are two zones for the NPC (or one). He walks along paths, but when the player enters the first zone, drops everything, runs up to the player, points a weapon at him (might also yell or talk), if the player enters the second zone — attacks him.
  • related files:
    • all.spawn
    • modules.script
    • xr_zoneguard.script
path_walk = <patrol>
set of points which npc should reach
path_look = <patrol>
points to look at
team = <string>
name of a team of synchronized zoneguards (only one from the team will react to the player)
zone_guard = <string>
the name of the zone in which the player will be attacked
zone_warn = <string>
the name of the zone in which to start a conversation with the player
walker_team = <string>
team to set it in walker mode (will be the same as

team

if not set)
no_move = <boolean> { default: false }
will yell to the player from the spot, and not run towards him
snd_greet = <string> { default: "" }
name of the sound scheme from which to play a sound on finding the player
enable_dialog = <boolean> { default: false }
whether enable the dialog
ignore_friends = <boolean> { default: false }
if

true

, will ignore friendly characters
ignore_cond = <condition_list>
conditions for ignoring the player
no_danger = <boolean> { default: false }
if

true

, will not play the threat animation to neutrals
anim = <string> { default: "" }
animation to play if not hostile to the player
snd_anim_sync = <boolean> { default: false }
if

true

, will synchronize sound with animation


AI salkers behaviour schemes

on_death

Section activated when npc dies. You may use this section to give/take away infoportions and call functions.
  • related files:
    • all.spawn
    • modules.script
    • xr_death.script
[logic]
on_death = death

[death]
on_info  = %+info -info =func()%
on_info2 = %+info -info =func()%

on_hit

Section activated when actor shot npc/mutant.
  • related files:
    • all.spawn
    • modules.script
    • xr_hit.script
[logic]
on_hit = hit

[hit]
on_info  = %+info -info =func()%

actor_dialogs

Shows which dialogs will be available to the player when speaking with this NPC. Can be used in almost any scheme.
  • related files:
    • all.spawn
    • xr_meet.script
    • xr_logic.script
id
comma-separated list of available dialogs
disable
comma-separated list of disbled dialogs
[logic]
actor_dialogs = actor_dialogs

[actor_dialogs]
id = dialog_id1, dialog_id2, ...
disable = dialog_id3, dialog_id4, ...

on_use

This section shows what will happen if the player try to "use" the NPC.
  • related files:
    • all.spawn
    • modules.script
    • xr_use.script
    • xr_motivator.script
Note: scheme is obsolete, it was used for (probably) mutants and physic objects. Currently only function notify_on_use() is used to play sound appropriate to situation (for instance when you abuse npc trying to talk to him with your weapon unholstered)
[walker]
on_use = use

[use]
on_info = %+info -info =func()%

dont_spawn_loot

If you want to prevent spawning items in corpse of dead stalker then add this section to character's custom_data field.
  • related files:
    • all.spawn
    • death_manager.script
[dont_spawn_loot]

dont_spawn_character_supplies

If this section is set, then the standard set of loot specified in character's profile (xml) won't span inside him.
[dont_spawn_character_supplies]

spawn

All the items specified in this section will be spawned in character's backpack. However all the items from character's profile (xml) will be spawned too.
[spawn]
item_name1 = quantity
item_name2 = quantity
...

game_info

Camps do not tell stories automatically. You have to "teach" certain stalkers a few stories. To do this, add the following section to custom data:
[game_info]
stories = story_01, legend_01
There are currently the following stories and legends:
  • story_01 - edge of the Zone and grenade in one move
  • story_02 - about the anomaly and the rocks
  • story_03 - about Vilnov's group's return
  • story_04 - about the time Kostia Fedorin encountered an artifact and disappeared from the radar
  • story_05 - about the fight with a controller
  • story_06 - about the door, the vodka, and the house
  • legend_01 - about the alien experiment in the Zone
  • legend_02 - about secret laboratories in the Zone
  • legend_03 - legend of the guide
  • legend_04 - legend of the Dark Stalker
  • legend_05 - legend about not sleeping deep in the Zone

known_info

Section with infoportion which certain stalker "knows". If you search corpse of stalker who have

known_info

section in custom_data field, then all the infoportions from this section will be given.
  • related files:
    • all.spawn
    • xr_info.script
    • xr_motivator.script
[known_info]
infoportion1
infoportion2
...

info

Section defines infoportion which should be given to npc or taken away.
  • related files:
    • all.spawn
    • stalker_generic.script
    • xr_logic.script
[info]
in = {+info1 -info2} give_info1 | {=func(123:456) give_info2 | give_info3
out = {!func +info3} take_info1 | {+info4 -info5} take_info2 | take_info3
In function

get_infos_from_data

the pattern seems to be broken. I would use this one instead:
for name in string.gfind(s, "%s*([%+%-%~%=%!%{%}%_]*[^%|]*)%p*") do
    ...
end

function set_npc_info(npc, ini, scheme, section)
    local in_info	= get_infos_from_data(npc, utils.cfg_get_string(ini,
        section, "in", npc, false, ""))

    local out_info	= get_infos_from_data(npc, utils.cfg_get_string(ini,
        section, "out", npc, false, ""))

    for k,v in pairs(in_info) do
        npc:give_info_portion(v)
    end

    for k,v in pairs(out_info) do
        npc:disable_info_portion(v)
    end
end

function get_infos_from_data(npc, s)
    local condlist
    local t = {}

    if s then
        --for name in string.gfind(s, "(%|*[^%|]+%|*)%p*") do
        for name in string.gfind(s, "%s*([%+%-%~%=%!%{%}%_]*[^%|]*)%p*") do
            condlist = xr_logic.parse_condlist(npc, "in", name, name)
            if condlist then
                table.insert(t, xr_logic.pick_section_from_condlist(db.actor, npc, condlist))
            end
        end
    end

    return t
end

no_smart

If this section is specified in custom_data field then NPC cannot be controlled by the smart terrain, even if it fits all the parameters. Such npc will be controlled by engine and schemes set in custom_data.
[no_smart]

show_spot

You may hide stalker's mapspot (remove it from minimap) using this section.
  • related files:
    • all.spawn
    • stalker_generic.script
    • xr_logic.script
show_spot = <condition_list> { default: true }
whether to show the mapspot or not. For zombified stalkers this field has always

false

value (not visible on map). For other stalkers

condition_list

is evaluated thus

true

(show mapspot) or

false

(remove mapspot) value is set.
[logic]
show_spot = true

[camper@not_on_map]
show_spot = false ; hide mapspot in this section

[camper@on_map]
show_spot = true ; show mapspot in this section

[walker]
show_spot = {+info1} false ; disable mapspot when info1 is set

spawner

This section, which both NPCs and monsters have, spawns them (switch them online) with a given codition. After the object is spawned, it is taken under control by the logic script. To make them appear in a given point, they should have the flag

no_move_in_offline

in the level editor and

can_switch_offline

should be off. The spawner is added to custom_data field before the logic section. It works like this:
cond = <condition_list>
condition which have to be met to spawn; field is evaluated for mutants. stalkers and vehicles
check_distance = <boolean>
whether the spawner should check the distance before spawn; field evaluated only for mutants
min_distance = <number> { default: 150 m }
if the actor is closer to the spawn point then value specified in this field - mutant won't be spawned; field evaluated only for mutants when

check_distance = true

[spawner]
cond = {+info -info =func() !func()}
Note: if the spawn condition is not met, the object won't be spawned. However if it has been spawned already and now the condition stops being met, the object will be switched offline by spawner. This can be used to simulate "day-night cycle" for instance for monsters: spawn monster at night, move offline during day:
[spawner]
cond = {!is_day}

danger

This scheme describes how stalkers react on different types of dangers. The algorithm works like this: first check if distance to the source of danger is not matched by

ignore_danger

. If the danger is closer, then its type is analyzed and is matched against the corresponding distance. If the danger is closer, then reaction to it is permitted.
  • related files:
    • all.spawn
    • modules.script
    • xr_danger.script
danger_expiration_time = <number> { default: 5000 }
the amount of time after which danger can be disregarded
danger_inertion_time = <number> { default: 10000 }
the amount of time after which a character can forget about a danger he has reacted to
Ignore danger if it is further than specified distance (in meters):
ignore_distance = <number> { default: 150 }
basic distance, if danger (no matter what type it is) is matched by value specified by this field then stalker won't react on danger
ignore_distance_grenade = <number> { default: 15 }
ignore grenade if it is further than specified value
ignore_distance_corpse = <number> { default: 10 }
ignore corpse if it is further than specified value
ignore_distance_hit = <number> { default: 150 }
ignore hit if enemy was shooting from further distance than specified value
ignore_distance_sound = <number> { default: 50 }
ignore danger sounf if it is further than specified value
ignore_types = <string_list> { default: "" } possible values: grenade | corpse | hit | sound
comma-separated list of danger types which will be ignored by npc
Wait specified amount of time before reaction on danger:
danger_inertion_time_grenade = <number> { default: 20000 }
danger_inertion_time_corpse = <number> { default: 10000 }
danger_inertion_time_hit = <number> { default: 60000 }
danger_inertion_time_sound = <number> { default: 15000 }
danger_inertion_time_ricochet = <number> { default: 30000 }
[walker@ignore_corpse]
danger = danger_condition@ignore_corpse

[danger_condition@ignore_corpse]
ignore_distance_corpse = 0

;------------------------------------------

[walker@nades]
danger = danger_condition@nades

[danger_condition@nades]
ignore_distance_grenade = 10
danger_inertion_time_grenade = 10000

;------------------------------------------

[walker@ignore_some_danger]
danger = danger@ignore

[danger@ignore]
ignore_types = corpse, sound
ignore_distance = 0
Note! Danger settings also apply to the camper scheme. That is, radius does not work in camper settings. The data is taken from the danger section.

treshold

There is a way to change stalkers parameters that determine how they attack monsters. There are two such patameters:
  • related files:
    • all.spawn
    • stalker_generic.script
    • xr_logic.script
max_ignore_distance = <number> { default: 15 m }
the stalker will always attack monsters within a given radius
ignore_monster = <number> range: [0 ... 1] { default: 0 m }
If the monster is evaluated at below this parameter, and the monster is outside the given radius, it will be attacked. Currently, stalkers never attack anything outside the 15-meter radius.
[logic]
threshold = threshold@tratata

[walker]
threshold = threshold@tratata

[threshold@tratata]
max_ignore_distance = number
ignore_monster      = number

on_combat

Shows what happens when the NPC goes into battle.
  • related files:
    • all.spawn
    • modules.script
    • xr_combat.script
    • xr_combat_monolith.script
    • xr_combat_camper.script
    • xr_combat_zombied.script
[logic]
on_combat = combat

[combat]
on_info = %+info -info =func% ; affects for each round of combat
To set up the various types of scripted battles use the parameter

combat_type (camper | monolith | zombied)

. In the following example, the stalker fights:
- as a

campter

, if enemy = actor and is further than X meters
- as

monolith

, if any enemy is further than Y meters
- engine-controlled otherwise
[logic]
active    = walker
on_combat = combat

[walker]
path_walk = ...

[combat]
combat_type = {=fighting_actor =fighting_ge_X_meters} camper,
    {=fighting_ge_Y_meters} monolith
The stalker moves under the simulation, but his combat is not engine-controlled, but always zombie-style.
[logic]
active = nil
on_combat = combat

[combat]
combat_type = zombied
If different character sections require different types of combat or different conditions, you can use the override combat_type. Remember: the override will always take priority over the combat section settings. That is, if you have logic for five sections and you need camper combat, but monolith combat in fifth, you can do it like this:
[logic]
active = walker1
on_combat = combat

[walker1]
...
[walker2]
...
[walker3]
...
[walker4]
...
[walker5]
...
combat_type = monolith

[combat]
combat_type = camper
You may use in scripts function

disable_combat_handler

to disable the combat section.

combat_ignore

Under certain conditions npc won't fight against enemies (will ignore combat).
  • related files:
    • all.spawn
    • modules.script
    • xr_combat_ignore.script
combat_ignore_cond = <condition_list> { default: "never" }
conditions which have to be met to ignore combat. If you set

combat_ignore_cond = always

then the character will always ignore combat in current scheme, untill switching to a scheme that does not ignore the combat
combat_ignore
empty section
[walker]
combat_ignore_cond = {+info –info =func !func}

[combat_ignore]
Functions used for combat ignore condition list:
fighting_dist_ge_20()
to check if current enemy is at distance equal to or greater than 20 m
fighting_dist_ge(distance in meters)
to check if current enemy is at distance equal to or greater then given distance
fighting_actor()
to check if actor is current enemy
check_fighting(story_id)
to check if npc with given story_id is current enemy
see_actor()
to check if npc see actor

Overrides

Settings that modify behavior of general schemes, depending on the currently active scheme (all are optional):
meet_enabled = true
enables meet scheme. Obsolete!
meet_talk_enabled = true
adds a possibility of a dialog to the current scheme. Obsolete!
meet_dialog = <dialog name>
dialog to fire on use. Obsolete! (now it is part of meet scheme)
meet_state = <state name>
sets the character's state for when a dialog opens. Obsolete! (now it is part of meet scheme)
wounded_enabled = true
enables the NPC to use the wounded scheme. Obsolete!
heli_hunter = true
heli_hunter
combat_ignore_cond
combat_ignore_cond
combat_ignore_keep_when_attacked = true
the character ignores combat, even if the actor shot him
combat_type = {condition} type
combat_type
on_combat
on_combat
companion_enabled = true
free stalkers will join you as companions (will also charge for it in future) Obsolete!


mob_camp

  1. Mutant crouches, looks at a point.
  2. Runs between positions.
  3. Switches to an all-purpose scheme (combat, panic) at the sight of an enemy.
  4. Can set several positions and times of changing position.
  5. Minimum and maximum distances from the enemy to the current camp position are set.
  6. If the enemy walks away too far, the monster returns to position.
  • related files:
    • all.spawn
    • modules.script
    • mob_camp.script
    • mob_state_mgr.script
path_look = <patrol>
path consisting of locations for the monster to look at
path_home = <patrol>
path consisting of the monster's locations
time_change_point = <number> { default: 10000 }
time in milliseconds to change the current camp point (

path_home

)
home_min_radius = <number> { default: 30 }
minimum radius in meters from the enemy to the camp point
home_max_radius = <number> { default: 40 }
maximum radius in meters from the enemy to the camp point
state = <const> vis | invis
controls visibility of mutants.

Only for bloodsuckers!

skip_transfer_enemy = <boolean> { default: false }
if this is added to custom_data, the monster will not get an enemy from other monsters, if sees him (monsters have to be in different groups for this)
Minimum and maximum radius are necessary for ignoring enemies that run too far away to return to the current position. The enemy's distance to the current position is considered. If the distance is greater than

home_min_radius

, attack the enemy until the enemy disappears or distance exceeds

home_max_radius

. Two distances are needed to avoid the situation in which the player stands on the border of the radius and enters/exits the zone, and makes the monster run off and back.
  • the current position is chosen randomly

  • home_min_radius

    have to be less then

    home_max_radius

  • the only required parameter is

    path_look

    . If

    path_home

    is not set, then the current node and the object's spawn node are concidered.

  • path_home

    and

    path_look

    patrols indices must coincide, that is if both fields are specified then both have the same number of waypoints (because: monster sits at a point of

    path_home

    and looks at a point of

    path_look

    )
  • to make the monster look in different points at a camp position,

    path_look

    can consist of several points
Signals

-

enemy

when mutant noticed enemy
Note: you can use standard conditions to switch sections as described in Scheme sections switching
[logic@esc_flesh_1_camp]
active = mob_camp@esc_flesh_1_camp

[mob_camp@esc_flesh_1_camp]
path_look = look
path_home = walk
time_change_point = 60000
home_min_radius = 15
home_max_radius = 30


mob_death

Scheme is used for setting infoportions and calling functions when mutant dies.
  • related files:
    • all.spawn
    • modules.script
    • mob_death.script
[logic]
on_death = death

[death]
on_info = %+info -info =func()%


mob_dogmeat

Also was known as mob_follower. Scheme which was supposed to make monsters friendly and also give them possibility to recieve orders from actor. Was tested with dog.

Still not implented!

  • related files:
    • all.spawn
    • modules.script
    • mob_dogmeat.script
    • bind_monster.script
    • bind_stalker.script

Last version changes:

- new faction for Dogmeat (relations same as actor, friend of actor, actor is friend with Dogmeat)
- disable combat, enable only on actor enemy sight
- disable traiding
- turn hostile against actor if actor attacked and there's no enemies around
- on level changer check level name saved in level_weathers.script; if level was changed - remove old Dogmeat, spawn new one near actor
- Dogmeat walks/runs near actor (maintain 2-3 meters distance)
- enable talk only when actor near Dogmeat (2-3 meters)
- talk options:
  • Good boy
  • Bad boy
  • Wait here
  • Go away
  • Stay at Cordon and wait for me
    • speed_walk = <number> { default: "2.0" }
    walking speed of our friendly animal
    speed_run = <number> { default: "5.0" }
    running speed of our friendly animal
    show_tip_text = <boolean> { default: "false" }
    textual hint when monster is targeted


mob_eluder

Mutant moves along the patrol waypoints (without considering connections between points), keeping at a distance from the player, keeping its course, exiting the scheme upon getting too close to the player and returning back when the distance increases.
  • related files:
    • all.spawn
    • modules.script
    • mob_eluder.script
path = <patrol>
set of points to walk
time_capture = <number> { default: 10 }
time in seconds the monster spends (is captured) in this scheme
time_release = <number> { default: 10 }
time in seconds the monster spends in the all-purpose scheme
min_dist = <number> { default: 5 }
distance in meters under which the monster switches to the all-purpose scheme
max_dist = <number> { default: 10 }
distance in meters over which the monster switches to the eluder scheme


mob_fake_death

Scheme is designed only for zombies, to fall down/rise up depending on conditions.
  • related files:
    • all.spawn
    • modules.script
    • mob_fake_death.script
Example:
[logic]
active = mob_fake_death

[mob_fake_death]
; fall down and lie until actor is closer than 5
on_actor_dist_le = 5 | nil


mob_home

Monsters keep the

path_home points

. They attack an enemy if the enemy is within the

home_min_radius

, otherwise they hide. This implies that

home_min_radius

should be made such that there would be enough covers inside path. In idle state, mutants typically also go to cover.
  • related files:
    • all.spawn
    • modules.script
    • mob_home.script
path_home = <patrol>
this patrol should contains only one point (p0) which is a position of home (center point)
home_min_radius = <number> { default: 20 }
monster will attack enemy only if he is closer than value specified in this field
home_max_radius = <number> { default: 40 }
stop chasing enemy if he is further then value specified in this field
aggressive_home = <boolean> { default: false }
if this line exist in scheme settings then monster run, not walk towards the designated

path_home

You can set min and max radius as flags in

path_home

:
minr = <number>
home_min_radius
maxr = <number>
home_max_radius
Note: you can use standard conditions to switch sections as described in Scheme sections switching
[logic@esc_dogs_to_fox_home]
active = mob_home@esc_dogs_to_fox_home

[mob_home@esc_dogs_to_fox_home]
path_home = path1
home_min_radius = 15
home_max_radius = 30
aggressive_home = true
panic = 0


mob_jump

Serves to make monsters jump without any checks and limitations (such as distance, angle). Position is set with a patrol path, offset and the physical jump factor.
  • related files:
    • all.spawn
    • modules.script
    • mob_jump.script
path_jump = <patrol>
path, with which we set 1st target jump point (with zero index). The real point accounts for the position path_jump[0] + offset
ph_jump_factor = <number> { default: 1.8 }
affects jump timing. Visually, it sets the curve of the jump. The higher it is, the sharper and faster the jump. This scheme allows creating a jump from one roof to another, jumping out a window, jumping over a barrier, etc. 1.8 by default
offset = <string> { default: "" }
displacement along axes x, y, z, with which the real point in space is set (might not be located at an AI node)
Note: you can use standard conditions to switch sections as described in Scheme sections switching
Signals

-

jumped

when mutant jumped

Example:
[logic]
active = mob_jump

[mob_jump]
path_jump      = path
ph_jump_factor = 2.8
offset         = 0, 10, 0
on_signal      = jumped | nil


mob_kicker

Scheme used for mutants to kick corpses. You can see it on below video when bloodsucker is kicking lying npc's corpse.
  • related files:
    • all.spawn
    • modules.script
    • mob_kicker.script
    • mob_alife_mgr.script
Note:
- you can use standard conditions to switch sections as described in Scheme sections switching
- you can also set mutant behaviour using Mutants scheme overrides

"Beautiful day"


mob_panic

Scheme sets custom panic treshold for monsters.
  • related files:
    • all.spawn
    • modules.script
    • mob_panic.script
    • xr_logic.script
panic = <number> { default: nil }
panic treshold; if not specified then

panic_threshold

value from monster ltx file will be used
Example:
[logic]
active = mob_walker

[mob_walker]
path_look = mil_monster_look
path_walk = mil_monster_walk1
panic = 0
on_actor_dist_le_nvis = 20 | mob_home@2 %=bar_arena_hit%

[mob_home@2]
path_home = mil_monster_walk1
home_min_radius = 50
home_max_radius = 70
panic = 0


mob_remark

Scheme for playing animations and sounds.
  • related files:
    • all.spawn
    • modules.script
    • mob_remark.script
    • mob_state_mgr.script
    • mob_sound.script
dialog_cond = <condition_list>
condition to display dialog window
anim = <string> { default: "" }
comma-separated list of monster's animations
anim_head = <string> { default: "" }
comma-separated list of head animations
tip = <string> { default: "" }
tip to display on hud
snd = <string> { default: "" }
comma-separated list of sound themes
time = <string> { default: "" }
animation playback time, used only for debugging (comma-separated list of times)
Note: you can use standard conditions to switch sections as described in Scheme sections switching
[logic]
active = mob_remark@hold

[mob_remark@hold]
anim = stand_idle_0
time = 50000
on_signal = action_end | mob_remark@hold
on_info = {+dar_codelock2_unlock} nil


mob_trade

Scheme designed for trading with mutants. Mutant has to be spawned as

m_trader

. To enable trade section we use field

on_trade

where trader section is defined, also path to ltx file with items to trade have to be specified in

trade

field. In vanilla game this scheme is used only for Sidorovitch in conjuction with

mob_trader

scheme which plays animations and sounds allowing Sidorovitch to react on trade transaction (

mob_trader@new_trade_good

and

mob_trader@new_trade_bad

in following example).
  • related files:
    • all.spawn
    • modules.script
    • mob_trade.script
    • xr_logic.script
Example:

[logic]
active = mob_trader@whatever
on_trade = mob_trade@new
trade = misc\trade_trader.ltx

[mob_trader@whatever]
...

[mob_trade@new]
on_info = {=trading =trade_exchanged} mob_trader@new_trade_exchanged
on_info2 = {!trading =trade_exchanged} mob_trader@new_trade_good
on_info3 = {!trading !trade_exchanged} mob_trader@new_trade_bad
-

trading()

function returns true if player trades with mutant
-

trade_exchanged()

function returns true if player bought and/or sold items

Creating mutant trader

- character profile for our new trader (

config\gameplay\character_desc_escape.xml

)
<specific_character id="tmp_mutant_trader" no_random = "1">
  <name>dogmeat_name</name>
  <icon>ui_npc_monster_dog</icon>
  <bio>escape_trader_bio</bio>

  <class>tmp_mutant_trader</class>
  <community>trader</community>
  <visual>monsters\dog\dog</visual>

  <rank>330</rank>
  <reputation>23</reputation>
  <money min="100000" max="100000" infinitive="1"/>
  <supplies>
    [spawn] \n
  </supplies>

  <start_dialog>dogmeat_hello_dlg</start_dialog>
</specific_character>
- bind profile with character class (

config\gameplay\npc_profile.xml

)
<character id="tmp_mutant_trader">
  <class>tmp_mutant_trader</class>
  <specific_character>tmp_mutant_trader</specific_character>
</character>
- character dialog (

config\gameplay\dialogs_escape.xml

)
<dialog id="dogmeat_hello_dlg">
  <phrase_list>
    <phrase id="0">
      <text>dogmeat_hello_dlg_0</text>
      <next>1</next>
    </phrase>
    <phrase id="1">
      <text>dogmeat_hello_dlg_1</text>
      <action>dialogs.break_dialog</action>
    </phrase>
  </phrase_list>
</dialog>
- character dialog text (

config\text\eng\stable_dialogs_escape.xml

)
<string id="dogmeat_hello_dlg_0">
  <text><barking></text>
</string>
<string id="dogmeat_hello_dlg_1">
  <text>Good Dogmeat, good boy.</text>
</string>
- character name (

config\text\eng\stable_bio_name.xml

)
<string id="dogmeat_name">
  <text>Dogmeat</text>
</string>
- character logic (

config\scripts\dogmeat.ltx

)
;-- several remark sections are used
;-- to change animations for dogmeat

[logic]
active = mob_remark@idle_0
trade = misc\trade_trader.ltx

[mob_remark@idle_0]
dialog_cond = always
anim = stand_idle_0
on_signal = action_end | mob_remark@idle_1

[mob_remark@idle_1]
dialog_cond = always
anim = stand_idle_1
on_signal = action_end | mob_remark@idle_2

[mob_remark@idle_2]
dialog_cond = always
anim = stand_scared_0
on_signal = action_end | mob_remark@idle_0
- spawning new trader:
[8623]
; cse_abstract properties
section_name = m_trader
name = dogmeat
position = -153.209762573242,-29.5977191925049,-372.008026123047
direction = 0,0,0

; cse_alife_object properties
game_vertex_id = 15
distance = 0
level_vertex_id = 96324
object_flags = 0xffffff7b
custom_data = «END
[logic]
cfg = scripts\dogmeat.ltx
END

; cse_visual properties
visual_name = monsters\dog\dog

; cse_alife_trader_abstract properties
money = 1000000
trader_flags = 0
character_profile = tmp_mutant_trader
trader
dialog
trade


mob_trader

Originally this scheme is used for Sidorovitch only. Its main purpose is to play animations and sounds, enable/disable ability to talk with mutant.
  • related files:
    • all.spawn
    • modules.script
    • mob_trader.script
    • mob_sound.script
anim_global = <const> { default: "" }
one of the animations specified in

global_animations

table
can_talk = <boolean> { default: true }
enable/disable ability to talk with mutant (dialogs)
tip_text = <string> { default: "character_use" }
textual hint when monster is targeted
sound_phrase = <string> { default: "" }
sound phrase specified in

mob_sound.script

anim_head = <const> { default: "" }
one of the animations specified in

head_animations

table
on_signal = <condition_list>
sets

animation_end

signal
Note: you can use standard conditions to switch sections as described in Scheme sections switching
Signals:

-

animation_end

- when mob_trader finish animation
-

sound_phrase_end

- when mob_trader stops talking (external sound)

Example:

-

config\scripts\esc_trader.ltx


mob_walker

Scheme controls mutants movement.
  • related files:
    • all.spawn
    • modules.script
    • mob_walker.script
path_walk = <patrol>
path to walk
path_look = <patrol>
points to look at (optional)
no_reset = <boolean> { default: false }
whether reset the action of the previous scheme (sound, for instance)
state = <const> vis | invis
controls visibility of mutants.

Only for bloodsuckers!

Note:
- you can use standard conditions to switch sections as described in Scheme sections switching
- you can also set mutant behaviour using Mutants scheme overrides
You may use flags for patrols:

path_walk

:
s = sound_theme
sound theme (idle, eat, attack, attack_hit, take_damage, die, threaten, steal, panic, growling)
c
crouch
r
run
sig = signal_name
signal name
b = vis|invis
visibility (

Only for bloodsuckers

)

path_look

:
t = time
time to wait in milliseconds while looking at a point
a = anim_name
animation (stand_idle, sit_idle, lie_idle, eat, sleep, rest, attack, look_around, turn)
[logic]
active = mob_walker

[mob_walker]
braindead = true
path_walk = esc_tut_flesh_bush_walk
path_look = esc_tut_flesh_bush_look


Mutants scheme overrides

actor_friendly = <boolean>
if

true

, mutant does not attack the player unless player attacked first
npc_friendly = <boolean>
if

true

, mutant does not attack the player or other mutants unless someone attacked first
friendly = <boolean>
if

true

, mutant does not attack anyone unless someone attacked the mutant first
braindead = <boolean>
if

true

, mutant ignores all attacks


sr_aes_deadzone

Scheme used for displaying postprocess effector.
  • related files:
    • all.spawn
    • modules.script
    • sr_aes_deadzone.script
intensity = <number>
base postprocess intensity
intensity_speed = <number>
percentage increase/decrease of base intensity
hit_intensity = <number>
percentage increase/decrease of hits relative to base intensity
Note: you can use standard conditions to switch sections as described in Scheme sections switching
Example:
[logic]
active = sr_aes_deadzone

[sr_aes_deadzone]
intensity = 100
intensity_speed = 73
hit_intensity = 21


sr_cutscene

This scheme is designed for playing camera (flyby) animation with certain effects. Actor is teleported to point from where animation begins playing. The player looses control until cam_effector finishes.
  • related files:
    • all.spawn
    • modules.script
    • sr_cutscene.script
point = <patrol>
point where actor is moved before starting flyby animation
look = <patrol>
point where actor is looking (direction of point where flyby animation is taking place)
pp_effector = <string> { default: "blink.ppe" }
file located in folder

gamedata\anims

and containing an effect (filename is written without the extension). This effect is started right before flyby animation.

Fixed!

cam_effector = <string_list> { default: "" }
files located in the folder

gamedata\anims\camera_effects

and containing a camera animation (filename is written without the extension)

Now multiple! You can write them through commas.

Note: you can use standard conditions to switch sections as described in Scheme sections switching
Signals:

-

cameff_end

- set when camera effect finished playing

Example:
[logic]
active = sr_idle

[sr_idle]
on_actor_inside = sr_cutscene

[sr_cutscene]
point = rostok_cam_way
look = rostok_cam_look
cam_effector = rostok_camera
on_signal = cameff_end | nil


sr_blocker

Scheme used for blocking actor from going to maps where we don't want to see him yet... turns around actor from this zone.
  • related files:
    • all.spawn
    • modules.script
    • sr_blocker.script
path_walk = <patrol>
waypoint to teleport actor back
path_look = <patrol>
point to look at when teleporting actor back
cond = <condition_list>
condlist (when zone should be active)


sr_idle

Purpose of this scheme is to sustain schemes execution and to handle section switching.
  • related files:
    • all.spawn
    • modules.script
    • sr_idle.script
Example:
[logic]
active = sr_idle@wait

[sr_idle@wait]
on_info = {+spawn_zombies} nil %=spawn_zombies(4)%


sr_light

Zone in which NPCs turn on|off their flashlights no matter what time of the day it is. Usually it is used in dark places like for instance bunkers or hangars to force npcs to use flashlight.
  • related files:
    • all.spawn
    • modules.script
    • sr_light.script
light_on = <boolean> { default: false }
whether torch light should be on
Example:
[logic]
active = sr_light@lights_on

[sr_light@lights_on]
light_on = true
on_info = {=dist_to_actor_le(5)} sr_light@lights_off

[sr_light@lights_off]
light_on = false
on_info = {=dist_to_actor_ge(5)} sr_light@lights_on


sr_mapspot

When actor enter mapspot restrictor, then restrictor will be marked on map.
  • related files:
    • all.spawn
    • modules.script
    • sr_mapspot.script
hint = <string> { default: "" }
hint id in string table (required)
location = <string> { default: "crlc_small" }
mapspot type name
Example:
[logic]
active = sr_mapspot

[sr_mapspot]
hint     = gar_swamp
location = crcl_big


sr_no_weapon

This scheme removes the player's weapon when actor enters zone.
  • related files:
    • all.spawn
    • modules.script
    • sr_no_weapon.script
[logic]
active = sr_no_weapon

[sr_no_weapon]


sr_particle

This scheme shows particles, static or dynamic (moving in the given place at the given time).
  • related files:
    • all.spawn
    • modules.script
    • sr_particle.script
name = <string>
particle name
path = <patrol>
camera path
mode = <const> : 1 | 2
mode = 1
used for static particles
mode = 2
used for dynamic particles
loop = <boolean>
whether the partcle playback should loop
You can set sound and delay flags at waypoints. A sound will be rendomly chosen from the specified scheme:
[particle_way]
points = p0
p0:name = name00|s=agro_test|d=1000
p0:position = -82.9337844848633,-5.55924224853516,-70.8113632202148
p0:game_vertex_id = 717
p0:level_vertex_id = 2626
Note: you can use standard conditions to switch sections as described in Scheme sections switching
Examples:
  • lopped particle:
    • [logic]
active = {-bar_deactivate_radar_done} sr_particle, nil

[sr_particle]
name = anomaly2\psi_antenna_00
path = rad_antenna_particle1
mode = 2
looped = true
on_info = {+bar_deactivate_radar_done} nil
  • you can use signal to switch between sections:
    • [logic]
active = sr_idle

[sr_idle]
on_info = {+sar_mon_poltergeist_1_spawn} sr_particle

[sr_particle]
name = anomaly2\gravity_blast_03
path = sar_poltergeist_1_way
mode = 2
looped = false
on_signal = particle_end | nil


sr_postprocess

Scheme used for displaying postprocess effector.
  • related files:
    • all.spawn
    • modules.script
    • sr_postprocess.script
intensity = <number>
base postprocess intensity
intensity_speed = <number>
percentage increase/decrease of base intensity
hit_intensity = <number>
percentage increase/decrease of hits relative to base intensity
Note: you can use standard conditions to switch sections as described in Scheme sections switching
Example:
[logic]
active = sr_idle

[sr_idle]
on_actor_inside = sr_postprocess

[sr_postprocess]
on_actor_outside = sr_idle
intensity = 0.5
intensity_speed = 0.1
hit_intensity = 0.3


sr_psy_antenna

Scheme defines "psy zones". Zones can be nested inside each other and intersect each other.
  • related files:
    • all.spawn
    • modules.script
    • sr_psy_antenna.script
eff_intensity = <number>
percentage increase/decrease of radiation relative to base intensity
hit_intensity = <number>
percentage increase/decrease of hits relative to base intensity
postprocess = <string> { default: "psy_antenna.ppe" }
postprocess file name
phantom_prob = <number> { default: 0 }
probability to spawn phantoms
mute_sound_threshold = <number> { default: 0 }
how much the sound should be muted when actor is inside restrictor
Note: you can use standard conditions to switch sections as described in Scheme sections switching
Examples:
  • mute_sound_threshold example:
    • [logic]
active = sr_psy_antenna

[sr_psy_antenna]
eff_intensity = 100
hit_intensity = 0
mute_sound_threshold = 1
  • switching sections depending on psi helmet:
    • [logic]
active = sr_psy_antenna@no_helmet

[sr_psy_antenna@no_helmet]
eff_intensity = 40
hit_intensity = 10
on_info = {=actor_has_item(good_psy_helmet)} sr_psy_antenna@good_helmet, {=actor_has_item(bad_psy_helmet)} sr_psy_antenna@bad_helmet
on_info2 = {+bar_deactivate_radar_done} nil
phantom_prob = 70

[sr_psy_antenna@bad_helmet]
eff_intensity = 20
hit_intensity = 0
on_info = {=actor_has_item(good_psy_helmet)} sr_psy_antenna@good_helmet, {=actor_has_item(bad_psy_helmet)}, sr_psy_antenna@no_helmet
on_info2 = {+bar_deactivate_radar_done} nil
phantom_prob = 60

[sr_psy_antenna@good_helmet]
eff_intensity = 10
hit_intensity = 0
on_info = {=actor_has_item(good_psy_helmet)}, {=actor_has_item(bad_psy_helmet)} sr_psy_antenna@bad_helmet, sr_psy_antenna@no_helmet
on_info2 = {+bar_deactivate_radar_done} nil
phantom_prob = 45


sr_play_anim

Scheme for creating cutscenes automaticly by logic.

Not finished!

  • related files:
    • all.spawn
    • modules.script
    • sr_play_anim.script
cam_effector = <anm> {default: nil}
name of anm file in anims\camera_effects folder which will determine camera to play
npc_spawn_point = <patrol>
where we will spawn fake actor
npc_section = <string>
defined in creatures\spawn_sections.ltx


sr_generator_waves

Scheme for deadly waves on generator level with particles and sound(zone_torrid).
  • related files:
    • all.spawn
    • modules.script
    • sr_generator_waves.script
path = <anm> {default: nil}
name of anm file in anims folder which will determine path of particle


sr_sleep

Scheme defines sleep zones.
  • related files:
    • all.spawn
    • modules.script
    • sr_sleep.script
    • dream.script
    • dream.ltx
type = <const> : nightmare | normal | happy | all { default: "all" }
Sets the type of dream
dream_prob = <number> : [0, 100]
probability of non-storyline dreams in the given zone
cond = <condition_list>
used for switching sections, giving infoportions, calling functions, etc
Example:
[logic]
active = sr_sleep

[sr_sleep]
cond = {+mil_dolg_dead}
type = nightmare


sr_sound

Scheme used for playing sounds.
  • related files:
    • all.spawn
    • modules.script
    • sr_sound.script
snd = <string_list>
comma-separated list of paths to sound files
type = <const> : floor_wooden | rats_panic | random
for convenience - some commonly used sound files were defined in table

sound_types

delay = <number> { default: 0 }
delay before playing the sounds in seconds of real time
idle = <number> { default: 0 }
duration of time to ignore entry into the zone after the last sound begins. For instance, to make sure howling is no more frequent than once every few minutes. In seconds of real time.
rnd = <number> { default: 100 }
percent probability of playing the sound
position = <patrol>
name of the waypoint which points will be used to play the sound at. Could be random, meaning a random place in the radius of 15-50 meters of the player. If this parameter is not set, then the player's position is assumed
slide_velocity = <number> { default: 3 }
speed of sound movement along the path
slide_sound_once = <boolean> { default: false }

true

- play the sound once, even if it does not reach the last point;

false

- if the sound has ended, but not reached the last point, play it again
play_at_actor = <boolean>
forces the sound play continuosly at actor's location. If this is true and the path is set (or is random) the game will crash
Note: you can use standard conditions to switch sections as described in Scheme sections switching
Examples:
  • using slide sound:
    • [logic]
active = sr_sound@random_sounds

[sr_sound@random_sounds]
type = random
position = dar_way_explorer
slide_velocity = 12
slide_sound_once = false

; ---------------------------------------------------
; ---------------------------------------------------

[logic]
active = sr_sound

[sr_idle]
on_actor_inside = {+bar_deactivate_radar_done} sr_sound

[sr_sound]
snd = ambient\gunfire_1
position = rad_freedom_vs_duty_way_look
slide_sound_once = true
on_timer = 7000 | nil
  • play_at_actor example:
    • [logic]
active = sr_idle

[sr_idle]
on_actor_inside = {-garbage_meetstalker_die} sr_idle@shooting %+gar_distance_shooting%

[sr_idle@shooting]
on_info = {+gar_seryi_actor_enter} nil
on_timer = 5000| sr_sound@shooting

[sr_sound@shooting]
snd = characters_voice\scenario\garbage\grey_grey_1
play_at_actor = true
on_signal = sound_end| nil
on_timer = 20000 | nil

; -----------------------------------------------------------
; -----------------------------------------------------------

[logic]
active = sr_idle

[sr_idle]
on_info = {+sar_start_decoding} sr_sound

[sr_sound]
snd = device\decoder
play_at_actor = true
on_signal = sound_end | sr_idle
on_info = {+sar_finish_decoding} nil


sr_sound_act

Scheme used for playing sounds in "actor's head". Can be used for instance for radio/pda messages.
  • related files:
    • all.spawn
    • modules.script
    • sr_sound2d.script
    • sound_theme.script
snd = <string> { default: nil }
path to sound file
theme = <string> { default: nil }
name of sound theme

gamedata\scripts\sound_theme.script

delay = <number> { default: 0 }
delay before playback
delay_max = <number> { default: 0 }
a random interval between

delay

and

delay_max

will be used
stereo = <boolean> { default: false }
if

true

and

snd

parameter is specified then the sound scheme will be automatically appended with suffixes _r and _l for right and left channels, respectively
Note: you can use standard conditions to switch sections as described in Scheme sections switching
Examples:
  • using single sound:
    • [logic]
active = sr_idle

[sr_idle]
on_info = {+aes_first_sound} sr_sound_act

[sr_sound_act]
snd = characters_voice\scenario\aes\aes_chopers_begin_1
on_signal = sound_end | nil
  • using sound theme:
    • [logic]
active = sr_idle

[sr_idle]
on_actor_inside = sr_sound_act

[sr_sound_act]
theme = sar_monolith_call
delay = 10000
delay_max = 15000
stereo = true
on_info = {+sar2_monolith_miracle} sr_idle@end

[sr_idle@end]


sr_teleport

Scheme used for teleporting actor to previously defined places. Current implementation of teleport scheme can handle 10 different spawn points. Spawn point is choosen by calculating its probability.
  • related files:
    • all.spawn
    • modules.script
    • sr_teleport.script
timeout = <number> { default: 900 }
delay in milliseconds before the teleporter activates
point = <patrol> { default: "none" }
one-point patrol path to teleport into
look = <patrol> { default: "none" }
one-point patrol path to turn towards
prob = <number> { default: 100 }
point choose probability
Example:
[logic]
active = sr_teleport

[sr_teleport]
timeout = 0

point1 = point1
look1  = look1
prob1  = 10

point2 = point2
look2  = look2
prob2  = 20


sr_territory

Catches stalker hits and deaths within a restrictor. Only events (stalker hits and deaths) caused by actor are counted.
  • related files:
    • all.spawn
    • modules.script
    • sr_territory.script
territory_hit = <condition_list>
logic for stalker hits within a restrictor
territory_death = <condition_list>
logic for stalker deaths within a restrictor
Example:
[logic]
active = sr_territory@outside

[sr_territory@outside]
on_actor_inside = sr_territory@inside

[sr_territory@inside]
on_actor_outside = sr_territory@outside
territory_hit = {-bar_dolg_territory_1_hit -bar_arena_fight_begin -bar_arena_fight} %+bar_dolg_territory_1_hit =bar_territory_logic%, {-bar_dolg_territory_2_hit -bar_arena_fight_begin -bar_arena_fight} %+bar_dolg_territory_2_hit =bar_territory_logic%, {-bar_dolg_territory_3_hit -bar_arena_fight_begin -bar_arena_fight} %+bar_dolg_territory_3_hit =bar_territory_logic%
territory_death = {-bar_dolg_territory_kill -bar_arena_fight_begin -bar_arena_fight} %+bar_dolg_territory_kill =bar_territory_logic%


sr_timer

Scheme used for hud timers. When timer expires then certain action is performed.
  • related files:
    • all.spawn
    • modules.script
    • sr_timer.script
type = <const> : inc | dec { default: "inc" }
type of timer (counter): incrementing (

inc

) or decrementing (

dec

)
start_value = <number>
timer starting value measured in milliseconds. For decrementing counters (

type = dec

) this parameter is required. For incrementing counters (

type = inc

) it is set to

0

by default.
on_value = <list: number|condition_list => timer_value|action>
used for performing some actions based on the counter's state
timer_id = <string> { default: "hud_timer" }
type of hud timer
string = <string> { default: "" }
hud message
Note: you can use standard conditions to switch sections as described in Scheme sections switching
scheme drawback: when actor enters restrictor, timer starts counting from start_value. However when actor leave restrictor and then enters it again - timer will start counting from start_value not from previously counted value!
Example:
[logic]
active = sr_idle

[sr_idle]
on_actor_inside  = sr_timer %=play_snd(characters_voice\scenario\radar\rad_hat_1)%

[sr_timer]
type             = dec
start_value      = 2400000 ; 240 seconds => 4 minutes
on_value         = 5 | %=play_snd(characters_voice\scenario\radar\rad_hat_2)% | 0 | nil %=aes_kill_actor%
on_info          = {+bar_deactivate_radar_done} nil
string           = st_helmet_countdown
on_actor_outside = sr_idle


sr_tip

The purpose of this scheme is to give the player a tip on entry into a restrictor.
  • related files:
    • all.spawn
    • modules.script
    • sr_tip.script
    • news_manager.script
name = <string> { default: "" }
message id
type = <string> { default: "news" }
type of message; obsolete
sender = <string> { default: nil }
icon of sender defined in

news_manager.script

(table

tips_icons

)
timeout = <number> { default: nil }
delay in milliseconds before the message will be displayed
showtime = <number> { default: nil }
how long message should be displayed on hud
single = <boolean> { default: false }
whether the message should be displayed only once
cond = <condition_list>
condition for displaying message
Note: you can use standard conditions to switch sections as described in Scheme sections switching
Signals:

-

tip_sended

- set when message was displayed; signal is set only if field

single

is set to

true

Examples:
  • disable scheme when actor is inside restrictor (show message then disable scheme):
    • [logic]
active = sr_tip

[sr_tip]
name            = gar_dolg_blokpost_warning
type            = tips
sender          = dolg
showtime        = 5000
on_actor_inside = nil
  • to disable scheme when message was sent (works only if

    single = true

    then

    tip_sended

    signal is set):
    • [logic]
active = sr_tip

[sr_tip]
name      = tips_esc_trader_about_anomalies
cond      = {-tips_esc_trader_about_anomalies}
type      = tips
single    = true
on_signal = tip_sended | nil %+tips_esc_trader_about_anomalies%
  • with condition:
    • [logic]
active = sr_tip

[sr_tip]
name            = rad_barman_spam
type            = tips
cond            = {+bar_deactivate_radar_done}
sender          = barman
on_actor_inside = nil
showtime        = 25000


sr_weather

Weather changer

Scheme can be used to fix weather on undeground areas and change it to another one when you need it.
  • related files:
    • all.spawn
    • modules.script
    • sr_weather.script
The idea behind this scheme is to fix weather problems in underground areas (partial laboratory complexes, bunkers, caves, etc). Basically, you spawn weather changer, which is a space restrictor and then you define weather preset for this zone. If actor enters this zone, weather will be changed to the one you specified in logic. In that way, we can define weather effects for special places, for instance, in case of Sin base at Darkscape, we can define weather preset with some wind, sliding rocks and draining water sound effects with some dust particles etc. This also helps to exclude weather effects, like for instance rain, in such places.

The major drawback is that the whole underground area have to be included in weather changer space restrictor. You can however spawn several weather changers which sticks to each other and together cover the whole area – in that way weather will be changed each time player enters new restrictor, but it happens fast so no visual problems should be spotted.

Also remember that weather changer restrictor needs to be high enough to include whole area – to fix situation when player jumps and in that way – exits weather changer (so weather is reset back to level weather) and then when actor is falling down – weather is set by weather changer. In such situation odd visual effects can be seen.

Another problem to consider – weather changer restrictor which covers some underground area should not overflow to outdoor areas. This is important because if weather changer overflows to outdoor area it is possible that weather will be changed accidentally when actor walks above (outside) underground area. For instance when cave or bunker is placed under road, and if weather changer restrictor which is covering that underground area is overflowing to that road – weather will be changed if actor walks on that road.

So in short: weather changer restrictor must cover the whole underground area, but it cannot overflow it (unless area above/around that underground is not accessible, but it’s just better not to overflow it).

To spawn weather changer, simply spawn space restrictor and add logic. Weather changer scheme accepts only one parameter (

weather

), which is name of weather preset to change once actor enters this restrictor.
Default value is

indoor

but I think we can define better presets (indoor has some sounds which fits only to underground labs).

Here’s an example of weather changer space restrictor – logic is the most important part (highlighted below):
[852]
; cse_abstract properties
section_name = space_restrictor
name = esc_sr_weather_changer
position = -148.328994750977,-30.0799999237061,-360.151000976563
direction = 0,0,0
; cse_alife_object properties
game_vertex_id = 17
distance = 0
level_vertex_id = 101513
object_flags = 0xffffff3e
custom_data = «END
[logic]
active = sr_weather
[sr_weather]
weather = indoor
END
; cse_shape properties
shapes = shape0
shape0:type = sphere
shape0:offset = 0,0,0
shape0:radius = 10
; cse_alife_space_restrictor properties
restrictor_type = 3


ph_button

Pressing a button switches sections and eventually handles infoportions.
  • related files:
    • all.spawn
    • modules.script
    • ph_button.script
on_press = <condition_list>
describes what happens when the button is pressed
anim = <string> { default: "" }
animation to play when the button is pressed
anim_blend = <boolean> { default: true }
whether the animation should be smooth (blended)
tooltip = <string> { default: "" }
textual hint when the button is targeted. The hint is needed to at least explain that it can be presseed.
Examples:
  • button
    • [logic]
active = ph_button@locked

[ph_button@locked]
anim_blend  = false
anim        = button_false
on_press    = ph_button@unlocked %+prisoner_door_unlocked%

[ph_button@unlocked]
anim_blend  = false
anim        = button_true
on_press    = ph_button@locked %-val_prisoner_door_unlocked%
  • switch
    • [logic]
active = ph_button@active

[ph_button@active]
anim     = lab_switcher_idle
tooltip  = tips_labx16switcher_press
on_press = ph_button@deactivated

[ph_button@deactivated]
anim = lab_switcher_off


ph_camera

Scheme designed for moving cameras.
  • related files:
    • all.spawn
    • modules.script
    • ph_camera.script
state = <const> : path_tracking | off { default: "path_tracking" }
camera state, can be either

off

(camera is disabled or destroyed) or

path_tracking

(rotating); other states are evaluated only via script (such as enemy tracking, waiting on end points, etc). If you set

state

to

path_tracking

, you must specify a way point for camera which defines at least 2 points (end points) between which camera will be rotating – typically one point on left camera’s side and another on right. You can define more points (middle points) at which camera will be looking at while rotating – this depends on how wide camera zone is
path_look = <patrol> { default: "" }
camera tracking path, way point name consisting of at least 2 points (end points) between which camera will be rotating. This is needed only if camera

state

is set to

path_tracking

. Way point is not requiring any flags or signals, however do not place way point to close to camera – that would force camera to look down underneath which can look odd. Here’s an example of way point with 2 end points (left, right):

[esc_camera_look_2points]
points = p0,p1
p0:name = name00
p0:position = -148.761138916016,-30.1068019866943,-358.168518066406
p0:game_vertex_id = 17
p0:level_vertex_id = 100794
p0:links = p1(1)
p1:name = name02
p1:position = -147.65705871582,-29.7977733612061,-371.756530761719
p1:game_vertex_id = 15
p1:level_vertex_id = 102976
start_wp = <number> { default: 0 }
start point of camera tracking path, number (index) of first way point at which camera will look; by default camera will look first at point 0; this is needed only if camera state is set to

path_tracking

stop_at_end_points = <boolean> {default : true }
this is Boolean value (either

true

or

false

) which decides whether camera will stop and wait at end points (both – left and right). This gives nice effect that camera is paused for some time before rotating back to previous point. Needed only if camera

state

is set to

path_tracking

; by default it is set to

true

, so camera will stop and wait at end points; pause time is set to ca 3 seconds
speed_h = <number> { default: 0.2 }
horizontal camera rotation speed; this decides how fast camera rotates in left – right direction; It goes actually pretty fast, so value should be lower then 0.5 – for instance value 0.2 (which is default) is good enough ?
speed_v = <number> { default: 0.2 }
vertical camera rotation speed; this decides how fast camera rotates in up – down direction; Just like with horizontal speed, value should be lower then 0.5 – again, value 0.2 (default) seems to be enough
fov_angle_min = <number> { default: 5 }
minimum FOV angle which depends on place where camera was spawned and is used for enemy detection; rather needs to be set with trial & error approach; default value is 5
fov_angle_max = <number> { default: 30 }
same as above, except this defines maximum FOV angle; default value is 30
fov_zones = <string_list>
comma separated list of camera zones names – space restrictors where enemies can be spotted; if you won’t specify any camera zones then enemies will not be tracked at all
track_mutants = <boolean> {default : false }
Boolean value which decides whether camera will track mutants which enter camera’s camera zones specified in

fov_zones

field
enemies = <string_list>
comma separated list of communities to track; for instance you can set camera enemies to

bandit

and camera will track only bandits; if you want camera to track user, just add

actor

as enemies

Parametres below are deprecated! Don't use them!

speed_p = <number> { default: 1 }
vertical camera rotation speed;
focus_type = <const> : cone | rect { default: "cone" }
camera focus type
focus_angle_h = <number> { default: 90 }
horizontal visibility angle; used only if

focus_type = rect

focus_angle_p = <number> { default: 90 }
vertical visibility angle; used only if

focus_type = rect

focus_angle = <number> { default: 135 }
cone angle visibility; used only if

focus_type = cone

warn_distance = <number> { default: 10 }
the distance at which camera can recognize enemies
pause_after_lost_target = <number> { default: 5000 }
pause after the target went out of sight (tracking delay)
If camera cover received hit (actor shoot camera) then signal

camera_destroyed

is issued and it should be used to switch scheme section (camera goes to

off

state). However, it is better to exit logic: on_signal = camera_destroyed | nil – destroyed camera it not usable anymore so no need to idle in

off

state (unless we decide that in certain situations camera should be “repaired” after some time and becomes usable again – this can be implemented fairly easy, just give me a shout and I’ll alter camera script, but for now, exit from scheme when signal was triggered).

Here’s an example of camera logic:
[logic]
active = ph_camera@on

[ph_camera@on]
state = path_tracking
path_look = esc_camera_look_2points
start_wp = 0
speed_h = 0.2
speed_p = 0.2
fov_angle_min = 5
fov_angle_max = 30
fov_zones = esc_cam_zone_01
enemies = actor
track_mutants = true
stop_at_end_points = true
on_signal = camera_destroyed | nil ;--ph_camera@off

[ph_camera@off]
state = off
ph_camera.script is not used in vanilla thus it wasn't fully tested. That means it may not work at all. However you can use projector scheme: spawn camera object with section_name = search_light, then use ph_projector scheme in logic - camera will work using projector scheme.


ph_car

Scheme designed to control behaviour of vehicles.
  • related files:
    • all.spawn
    • modules.script
    • ph_car.script
usable = <condition_list>
if

usable ~= nil

then vehicle can be used by actor (not tested)
show_tips = <boolean> { default: true }
whether show textual messages (

tip_use, tip_locked

)
tip_use = <string> { default: "tip_car_use" }
textual hint when the vehicle is targeted but isn't locked; used only when

usable ~= nil

tip_locked = <string> { default: "tip_car_locked" }
textual hint when the vehicle is targeted and it's locked; used only when

usable ~= nil

path_walk = <patrol> { default: nil }
vehicle path; used only when

usable = nil

target = <const> : actor | points | story_id | gulag_name { default: "points" }
vehicle target; used only when vehicle has weapon and

usable = nil

.

Gulags now supported too! Use it with fire_gulag.

fire_gulag = true
vehicle will fire to specificly setted gulag, works only if to target setted

gulag_name

track_target = <boolean> { default: false }
whether the vehicle should track the target;

false

by default; used only when

usable = nil

on_target_vis = <list: string|condition_list>
if target (

string

) is visible then

condition_list

is evaluated; used only when

usable = nil

on_target_nvis = <list: string|condition_list>
if target (

string

) isn't visible then

condition_list

is evaluated; used only when

usable = nil

path_fire = <patrol> { default: nil }
vehicle fire points; used only if

target = points

and

usable = nil

auto_fire = <boolean> { default: false }
whether shoot on sight;

false

by default; used only if

usable = nil

fire_time = <number> { default: 0 }
before this time expires vehicle can shoot; used only if

usable = nil

fire_repeat = <const> : inf | number { default: nil }
how many times repeat the fire; used only if

usable = nil

fire_range = <number> { default: 50 }
vehicle file range; used only if

usable = nil

invulnerable = <boolean> { default: false }
whether the car is invulnerable
headlights = <const> : on | off { default: "off" }
whether vehicle's lights are turned on
on_death_info = <string> { default: nil }
infoportion which will be given when vehicle will be destroyed
Examples:
[logic]
active = ph_car1

[ph_car1]
path_walk      = aes_btr_walk5
target         = actor
on_signal      = arrive | ph_car2

[ph_car2]
path_fire      = aes_btr_look5
fire_repeat    = 50
auto_fire      = true
on_target_vis  = actor | ph_car3

[ph_car3]
target         = actor
track_target   = true
on_target_nvis = actor | ph_car2

;------------------------------------------------------------------------
;------------------------------------------------------------------------

[logic]
active = ph_idle@wait

[ph_idle@wait]
on_death_info   = pri_wave3_btr_dead
on_info         = {+pri_wave3_start} ph_car@start

[ph_car@start]
path_walk       = pri_wave3_btr_walk
target          = actor
on_death_info   = pri_wave3_btr_dead
on_signal       = arrived | ph_car@hunt_actor %+pri_wave3_btr_arrived%

[ph_car@hunt_actor]
target          = actor
track_target    = true
on_death_info   = pri_wave3_btr_dead
on_info         = {+pri_wave3_bomber_on_position} ph_car@wait_bomber

[ph_car@wait_bomber]
target          = actor
on_death_info   = pri_wave3_btr_dead
on_timer        = 1000 | ph_car@kill_bomber

[ph_car@kill_bomber]
target          = 806
on_death_info   = pri_wave3_btr_dead
on_info         = {+pri_bomber_dead} ph_car@point_fire

[ph_car@point_fire]
path_fire       = pri_wave3_btr_look
fire_repeat     = inf
auto_fire       = true
on_death_info   = pri_wave3_btr_dead
on_timer        = 4000 | ph_car@fight_actor2

[ph_car@fight_actor2]
target          = actor
track_target    = true
on_death_info   = pri_wave3_btr_dead
on_timer        = 5000 | ph_car@fire

[ph_car@fire]
path_fire       = pri_wave3_btr_look
fire_repeat     = inf
auto_fire       = true
on_target_vis   = actor | ph_car@fight_actor2
on_death_info   = pri_wave3_btr_dead
on_timer        = 5000 | ph_car@fight_actor2


ph_code

When a proper code is entered, scheme gives out an infoportion. Scheme is used with ph_door scheme, to unlock the doors.
  • related files:
    • all.spawn
    • modules.script
    • ph_code.script
    • ui_numpad.script
tips = <string> { default: "st_codelock" }
textual hint when the combination lock is targeted.
code = <number> { default: nil }
code to unlock the lock
on_code = <condition_list>
used for switching sections, giving infoportions, calling functions, etc; used only if

code

is specified
on_check_code = <list: string, condition_list>
same as

on_code

however it is used only if

code

is not provided
Examples:

  • on_code

    • [logic]
active = ph_code

[ph_code]
code = 1287975
on_code = nil %+rad_code_door_unlocked%

;-------------------------------------------------------

[logic]
active = ph_code@lock

[ph_code@lock]
code = 1243
on_code = {+dar_password_info1} %+dar_codelock1_unlock%

  • on_check_code

    • [logic]
active = ph_code@lock

[ph_code@lock]
on_check_code1 = 2011533 | %+bun_codelock_open%
on_check_code2 = 342089 | %+bun_codelock_open%


ph_computer

Scheme for computers. Now also support games.
  • related files:
    • all.spawn
    • modules.script
    • ph_computer.script
    • pc*****.script
availability = <const> : accessible | locked | malfunction
computer's availability

* locked

will display info that this pc is locked is controlled by infoportion so we can use it and thus make sure that user won't use it before he finds infoportion

* malfunction

- will display that pc is bsoded

* accessible

means that you can use it
pc_type = <const> : desktop | laptop (not finished)
type of pc we will make
user_name = <string> {default: anything}
login for created pc
password = <string>
{default: anything}
password to unlock pc, can be infoportion too; used only if

availability = locked,

useless if availability is accessible or malfunction
password_info = <string>
infoportion name, will be given when you type password (that was just to prevent situation when you have to type password everytime you use pc)
icons_alignment = <const> : horizontal | vertical | mess
alignment of icons on desktop
wallpaper = <string_list> : {default: nil}
wallpaper we use on desktop, like this for example: wallpaper1, wallpaper2, wallpaper3..., wallpaper7
other wallpapers: wallpaper_iaea (atomic sign), wallpaper_water, wallpaper_dso (un military police sign as on niva)
show_tips = <boolean> true | false
whether show text tips when you target monitor with your crosshair ...text like: turn on/off pc (if availability is available), this pc is not operational (malfunction) or you can't use this pc now (locked)
tetris = <boolean> true | false
whether pc should contain tetris game
puzzle = <boolean> true | false
whether pc should contain puzzle game
solitare = <boolean> true | false
whether pc should contain solit. game
known_info = <string_list>
comma-separated list with text files which computer holds (those are actually section names from config\lost_alpha\pc_data\known_info.ltx)
known_code = <string_list>
comma-separated list to encrypted files, logs, codes, ini or anything which computer holds
known_image = <string_list>
infoportions of image files which computer holds (test_pc_known_image_001, test_pc_known_image_002.. etc)
known_audio = <string_list>
infoportions of sound files which computer holds (test_pc_known_audio_001, test_pc_known_audio_002)
known_video = <string_list>
section names to video files config\lost_alpha\pc_data\known_video.ltx
known_hex = <number> {default: X}
Where

X

=number 1-5, so this just a number of random files with some hex dump in it which will be added to the pc
on_use = <condition_list>
called when

use

callback was called
Example
[logic]
active = ph_computer@unlocked

[ph_computer@unlocked]
availability = accessible
pc_type = desktop
user_name = ;ui_pc_user_name
password = top_secret
password_info = pc_on_use_callback
icons_alignment = horizontal
wallpaper = wallpaper_dso
show_tips = true
tetris = true
puzzle = true
solitare = true
known_info = esc_tut_pc_to_all, esc_tut_pc_about_anoms
known_code = pc_test_code_1, pc_test_code_2, pc_test_code_3, pc_test_code_4, pc_test_code_5, pc_test_code_6
known_image = test_pc_known_image_001, test_pc_known_image_002
known_galleries = test_pc_known_gallery_001, test_pc_known_gallery_002, test_pc_known_gallery_003
known_audio = test_pc_known_audio_001, test_pc_known_audio_002
known_video = test_pc_known_video_001, test_pc_known_video_002
known_hex = 5
on_info = {+pc_lock_now} ph_computer@locked, {+pc_bsod_now} ph_computer@bsod
on_use = ;%+pc_on_use_callback%

[ph_computer@locked]
availability = locked
pc_type = desktop
icons_alignment = horizontal
on_info = {+pc_unlock_now} ph_computer@unlocked

[ph_computer@bsod]
availability = malfunction
pc_type = desktop
icons_alignment = horizontal
on_info = {+pc_fix_now} ph_computer@unlocked
Implementation
-- tip_computer_accessible = "To turn the computer on press ($$ACTION_USE$$)"
-- tip_computer_locked = "This computer is locked. You need password."
-- tip_computer_bsod = "This computer isn't operational."

class "action_computer"

function action_computer:__init(object, storage)
    self.object = obj
    self.st = storage
end

function action_computer:reset_scheme()
    if self.st.show_tips then
        if self.st.availability == "accessible" then
            self.object:set_tip_text("tip_computer_accessible")
        elseif self.st.availability == "locked" then
            self.object:set_tip_text("tip_computer_locked")
        else
            self.object:set_tip_text("tip_computer_bsod")
        end
    end
end

function action_computer:update()
    if xr_logic.try_switch_to_another_section(self.object, self.st, db.actor) then
        return
    end
end

function action_computer:is_accessible()
    if self.st.availability == "accessible" then
        return true
    elseif self.st.availability == "locked" then
        return has_alife_info(self.st.password)
    end

    return false
end

function action_computer:use_callback(computer, actor)
    if self:is_accessible() then
        level.start_stop_menu(ui_computer.computer(self, st.known_infos, st.known_images, st.known_sounds), true)
    end

    if self.st.on_use then
        if xr_logic.switch_to_section(self.object, self.st,
            xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.on_use.condlist))
        then
            return
        end
    end
end

function action_computer:deactivate()
    self.object:set_tip_text("")
end

-- ****************************************************** --

function add_to_binder(obj, ini, scheme, section, storage)
    xr_logic.subscribe_action_for_events(obj, storage, action_computer(obj, storage))
end

function set_scheme(obj, ini, scheme, section, gulag_name)
    local st = xr_logic.assign_storage_and_bind(obj, ini, scheme, section)
    st.logic = xr_logic.cfg_get_switch_conditions(ini, section, obj)

    st.availability = utils.cfg_get_string(ini, section, "availability", obj, false, "", "accessible")
    st.password = utils.cfg_get_string(ini, section, "password", obj, false, "", "")
    st.known_infos = parse_names(utils.cfg_get_string(ini, section, "known_infos", obj, false, "", ""))
    st.known_images = parse_names(utils.cfg_get_string(ini, section, "known_images", obj, false, "", ""))
    st.known_sounds = parse_names(utils.cfg_get_string(ini, section, "known_sounds", obj, false, "", ""))
    st.show_tips = utils.cfg_get_bool(ini, section, "show_tips", obj, false, true)
    st.on_use = xr_logic.cfg_get_condlist(ini, section, "on_use", obj)
end


ph_door

Scheme designed to handling doors.
  • related files:
    • all.spawn
    • modules.script
    • ph_door.script
closed = <boolean> { default: true }
whether doors are closed or not
locked = <boolean>
whether doors are locked or not;

false

(unlocked) by default
no_force = <boolean> { default: false }
whether script will use force to open the doors or not
show_tips = <boolean> { default: true }
whether display textual hint when the doors are targeted or not
tip_open = <string> { default: "tip_door_open" }
textual hint when the doors are targeted (doors are closed but not locked)
tip_unlock = <string> { default: "tip_door_locked" }
textual hint when the doors are targeted (doors locked)
tip_close = <string> { default: "tip_door_close" }
textual hint when the doors are targeted (doors opened)
snd_open_start = <string> { default: "trader_door_open_start" }
the sound that will be played on attempts to open the door
snd_close_start = <string> { default: "trader_door_close_start" }
the sound that will be played on attempts to close the door
snd_close_stop = <string> { default: "trader_door_close_stop" }
the sound that will be played when the door is closed shut
on_use = <condition_list>
occurs when actor "use" doors; used for switching sections, giving infoportions, calling functions, etc.
hit_on_bone = <list: number|condition_list>
list: bone_index|condition_list used for hit_callback
Examples:
[logic]
active = ph_door@locked

[ph_door@locked]
locked = true
closed = true
on_info = {+dar_codelock1_unlock} ph_door@open %+dar_open_door1 =play_snd(device\door_servomotor)%
tip_open =
tip_close =
on_use = %+dar_door1_closed%

[ph_door@open]
locked = false
closed = false
tip_open =
tip_close =

; ------------------------------------------------------------------------------------------------
; ------------------------------------------------------------------------------------------------

[logic]
active = ph_door@locked

[ph_door@locked]
locked = true
closed = true
snd_open_start = trader_door_open_start
snd_close_start = trader_door_close_start
snd_close_stop = trader_door_close_stop
hit_on_bone = 2|ph_door@close

[ph_door@close]
locked = false
closed = true
snd_open_start = trader_door_open_start
snd_close_start = trader_door_close_start
snd_close_stop = trader_door_close_stop
on_use = ph_door@open
hit_on_bone = 1|ph_door@free|2|ph_door@free

[ph_door@open]
locked = false
closed = false
snd_open_start = trader_door_open_start
snd_close_start = trader_door_close_start
snd_close_stop = trader_door_close_stop
on_use = ph_door@close
hit_on_bone = 1|ph_door@free|2|ph_door@free

[ph_door@free]
locked = false
closed = true
no_force = true
on_use = ph_door@open


ph_force

This scheme allows to kick an object (apply force) in a given direction.
  • related files:
    • all.spawn
    • modules.script
    • ph_appforce.script

  • remarks:
    • object can be hitted only once
force = <number> { default: 0 }
force applied to the object
time = <number> { default: 0 }
duration of time the force is applied to the object, measured in seconds
delay = <number> { default: 0 }
delay in seconds before the force is applied
point = <patrol> { default: "" }
name of a patrol path which will be used as the target (where the object will point)
point_index = <number> { default: 0 }
index of a point of the patrol path towards which the object will fly
Example:
[logic]
active = sr_idle

[sr_idle]
on_info = {+rad_here_i_come} ph_force

[ph_force]
force = 1500
time = 500
delay = 0
point = rad_barrel_drop
point_index = 0


ph_gate

Scheme designed for handling gates.
  • related files:
    • all.spawn
    • modules.script
    • ph_gate.script
state = <const> : open | closed | none { default: "none" }
gate state after initialization
locking = <const> : stick | soft | hard | none { default: "none" }
soft
the gate is blocked by force, so it can be forced (pushed) or broken through
hard
the gate is blocked with boundaries; the gate can only be broken
stick
doors are closed but can be opened
breakable = <boolean> { default: true }
determines whether the gate can be broken
left_limit = <number> { default: 100 }
sets the angle [0-180] for opening left door
right_limit = <number> { default: 100 }
sets the angle [0-180] for opening right door
tip_open = <string> { default: "tip_gate_open" or "tip_gate_locked" }
textual hint when the gate is targeted (gate closed)
tip_close = <string> { default: "tip_gate_close" }
textual hint when the gate is targeted (gate opened)
snd_init = <string> { default: "" }
the sound that will be played when the scheme is initiated
snd_open_start = <string> { default: "" }
the sound that will be played on attempts to open the gate
snd_close_start = <string> { default: "" }
the sound that will be played on attempts to close the gate
snd_close_stop = <string> { default: "" }
the sound that will be played when the gate is closed shut
on_use = <condition_list>
occurs when actor "use" gate; used for switching sections, giving infoportions, calling functions, etc.
Examples:
[logic]
active = ph_gate@locked

[ph_gate@locked]
state = closed
locking = hard
on_info = {+val_chase_start} ph_gate@unlocked

[ph_gate@unlocked]
state = closed
locking = stick

; ----------------------------------------------
; ----------------------------------------------

[ph_gate@locked]
state      = opened
locking    = soft
left_limit = 130
rigt_limit = 60
breakable  = false

[ph_gate@opened]
state   = opened
locking = stick

[ph_gate@closed]
state = closeded


ph_heavy

Is set for physical objects, which are forbidden for throwing by burers and poltergeists. For instance, they should lay in a certain place (such as storyline-related documents) or are too massive to be thrown nicely.

This scheme is deprecated!

[ph_heavy]


ph_hit

Scheme designed to hit object. This scheme is deprecated, use ph_force instead
  • related files:
    • all.spawn
    • modules.script
    • ph_hit.script
power = <number> { default: 0 }
hit power
impulse = <number> { default: 1000 }
hit impulse
bone = <string>
which bone should be hit
dir_path = <patrol>
hit direction
Example:
[logic]
active = ph_idle

[ph_idle]
on_info = {+hit_object} ph_hit

[ph_hit]
power = 1
impulse = 10
bone = provod
dir_path = hit_waypoint


ph_idle

Scheme for all physical objects, used for sustain scheme execution and switching between scheme sections.
  • related files:
    • all.spawn
    • modules.script
    • ph_idle.script
hit_on_bone = <list: number|condition_list>
list: bone_index|condition_list used for hit_callback
nonscript_usable = <boolean> { default: false }
if set to

true

then object will be controlled by engine (not by script) - used usually when scheme sections are beeing switched or to execute scheme once and then sustain its execution in current state (like for stashes)
on_use = <condition_list>
used for switching sections, giving infoportions, calling functions, etc.
tips = <string> { default: "" }
textual hint when the object is targeted.
Examples:
  • used for stashes objects - treasure_inventory_box.ltx (

    nonscript_usable

    example) - that is: scheme is sustained in this section, engine controls items in stashes
    • [logic]
active = ph_idle@enable

[ph_idle@enable]
nonscript_usable = true
tips = st_search_treasure
  • used for wish granter (

    on_use

    example)
    • [logic]
active = ph_idle

[ph_idle]
tips = tip_monolith
on_use = nil %+sar2_monolith_miracle =sar_monolith_miracle%

  • hit_on_bone

    example - if certain bone will be hitted (bone_index - here: 1, 2, 3, or 4) then certain infoportion will be given
    • [logic]
active = ph_idle

[ph_idle]
hit_on_bone = 1|%+agroprom_u_light_5%|2|%+agroprom_u_light_5%|3|%+agroprom_u_light_5%|4|%+agroprom_u_light_5%
on_info = {+agroprom_u_light_5} nil %=turn_off_object%
  • sustain scheme execution example
    • [logic]
active = ph_idle@wait

[ph_idle@wait]
on_info = {+sar_finish_decoding} ph_idle@nil
on_actor_inside = ph_idle@nil %+sar_finish_decoding%

[ph_idle@nil]


ph_impulse

This scheme was used for hitting physical objects.
  • related files:
    • all.spawn
    • modules.script
    • ph_impulse.script
force = <number> { default: nil }
hit force; if

force

variable isn't provided then scheme expect viariables:
force_min = <number> { default: 10 }
force_max = <number> { default: 20 }
velocity = <number> { default: nil }
hit velocity; if

velocity

variable isn't provided then scheme expect viariables:
velocity_x = <number> { default: 0.1 }
velocity_y = <number> { default: 0.0 }
velocity_z = <number> { default: 0.1 }
bone = <string> { default: "provod" }
object bone name (that bone will be hitted)
duration = <number> { default: 500 }
hit duration
interval = <number> { default: 300 }
interval between hits


ph_light_mgr

Scheme for handling hanging lamps.

Hanging lamps manager is handling behavior of hanging lamps even during blowout period. If you want to enable hanging lamp manager, simply spawn hanging lamp and add logic with parameters described below.
  • related files:
    • all.spawn
    • modules.script
    • ph_light_mgr.script
lights_on = <const> : day | night | always_on | always_off | blink { default: "night" }
defines when lamps should be turned on/off. Available values are:

* night

– light will be turned on during nights and turned off during day;

* day

– lights turned on only during day;

* always_on

- lights are turned on no matter whether it is night or day; Default value is set to night;

* always_off

- lights turned off
on_blowout = <const> : blink | on | off | random { default: "blink" }
describes what will happen with light during blowout; available values are:

* on

– light stays turned on;

* off

– light is turned off during blowout and if light was previously on – it is possible that bulb will explode with particle and sound effects (this happens randomly);

* blink

– light will be turned on/off at specified (via script) timer;

* random

– one of previously mentioned values – either

on, off

or

blink

value picked randomly
oscillator = <boolean> { default: false }
whether use oscillator or not; if it is set to true then lamp will oscillate to simulate wind effect; default value is

false

(oscillator disabled). During blowout, if oscillator is enabled, oscillator force is greater then normally so lamp will oscillate faster; using oscillator makes sense only in outdoor areas, however using it in underground may add nice moving lights effect
Example logic for light manager:
[ph_light_mgr]
lights_on = night
on_blowout = blink
oscillator = true
Implementation

Source here


ph_on_death

Scheme for tracking destruction of physical objects and producing various effects.
  • related files:
    • all.spawn
    • modules.script
    • ph_death.script

  • remarks:
    • scheme can be used only if object was spawned as

      physic_destroyable_object

Examples:
[logic]
active = ph_on_death
particle = anomaly2\plasma_generator

[ph_on_death]
on_info = %=inc_counter(mon_destroy_generator) =x18_gluk%


on_hit

Scheme called when object was hit. Can be use for handling infoportions or switching sections.
  • related files:
    • all.spawn
    • modules.script
    • ph_hit.script

  • remarks:
    • scheme can be used only if object was spawned as

      physic_destroyable_object

Example:
[logic]
active = ph_sound@humor
on_hit = hit

[hit]
on_info = %+esc_matugalnik_off%

[ph_sound@humor]
snd = humor
on_info = {+esc_matugalnik_off} nil


ph_oscillate

This scheme is meant for gentle rocking of physical objects (lamps, hanging zombies, crane ropes, etc). The force is applied to a bone of the object with linear increase. That is, during a period of time the force grows from 0 to the specified magnitude. After this a pause is made (force is not applied) for the time period/2. After the pause ends, the force is applied at the same place, but in opposite direction.
  • related files:
    • all.spawn
    • modules.script
    • ph_oscillate.script
joint = <string> { default: nil }
name of the bone to which force is applied
period = <number> { default: 0 }
time at which force is applied
force = <number> { default: 0 }
force (in newtons)
correct_angle = <number> { default: 0 }
angle correction
Examples:
  • hanging lamp:
    • [logic]
active = ph_oscillate

[ph_oscillate]
joint = provod
force = 1.5
period = 500
  • crane rope:
    • [logic]
active = ph_oscillate

[ph_oscillate]
joint = bone05
period = 3000
force = 500
correct_angle = 5


ph_projector

Scheme designed to control projectors. Automatic projector is looking at specified points in either select random point to look at way or follow the path method. To spawn automatic projector, select

spawns\physics\search light

and add

ph_projector

logic. Automatic projector is disabled during day (no light) because to be honest it doesn’t fit and looks odd. So projector is enabled (lights on) during nights only. There is also some interaction with player, who can turn on/off projector (update: disabled, can’t see anything good in letting player switch the projector …)
  • related files:
    • all.spawn
    • modules.script
    • ph_projector.script

  • remarks:
    • projector section_name in all.spawn have to be

      search_light

path_look = <patrol> { default: "" }
name of the waypoint which contains points at which projector will look at
rnd_wp = <boolean> { default: false }
Boolean value which decides whether projector will randomly select points to look at (

true

value) from given

path_look

. In other case (

false

value) projector will follow path (

path_look

) from point 0 to the last point and then backward (just like camera). Default value is set to

true

, so projector will randomly select points and that actually looks pretty cool
duration = <number> { default: 15000 }
time in milliseconds for projector to look at each point; after that time projector will select next point; default value is set to

15000

(ca. 3 game minutes)
rot_hspeed = <number> { default: 0.52 }
horizontal rotation speed; default value is set to

0.52

and that’s actually fast enough
rot_vspeed = <number> { default: 0.22 }
vertical rotation speed; default value is set to

0.22

which is good enough
Example:
  • Here's example for projector logic:
    • [logic]
active = ph_projector

[ph_projector]
path_look = esc_projector_look
rnd_wp = true
duration = 15000
rot_hspeed = 0.52
rot_vspeed = 0.22
    For synchronization with NPC use

    light_guard

    scheme for NPC. This scheme is for guarding NPC which basically is standing next to projector and looking at the same points as projector. It requires the same parameters as automatic projector, plus:
    • proj_name – projector name; so use some unique names for spawned projectors!
    • anim – NPC animation; default value is guard

    In this case, spawn projector without any logic, give it some unique name and add logic for NPC:

  • projector section
    • [8623]
; cse_abstract properties
section_name = search_light
name = priboy_esc_projector
position = -162.704177856445,-24.1463871002197,-349.41064453125
direction = 0,0,0
; cse_alife_object properties
game_vertex_id = 31
distance = 0
level_vertex_id = 86409
object_flags = 0xffffff3a
custom_data = «END
[collide]
ignore_static
END
; cse_visual properties
visual_name = equipments\projector
  • NPC logic
    • [logic]
active = walker

[walker]
path_walk = esc_tmp_soldier_walk
on_signal = ready | light_guard

[light_guard]
path_look = esc_projector_look
anim = guard
rnd_wp = true
duration = 15000
rot_hspeed = 0.52
rot_vspeed = 0.22
proj_name = priboy_esc_projector
on_timer = 20000|kamp

[kamp]
center_point = esc_blokpost_kamp
path_walk = esc_blokpost_kamp_task
def_state_moving = run
on_timer = 20000|walker
    Using signal in other scheme is a good idea (here in walker scheme). Basically, signal ready is set when NPC reached waypoint where projector is so NPC can now be synchronized with projector.

    Bugs and problems
  • Projector is not receiving any hit/death callbacks, so player can’t destroy the projector; it is possible to disable/enable projector via script (turning light on/off, that is) but I cannot find a way to detect when player shoot at projector; that also removes the possibility to add scheme for NPC who would shoot projectors to destroy them (when attacking enemy base for instance); for light guard I can eventually add condition – when guard was killed, projector light goes off
  • For some LOL reason, when projector is projecting light on mine field … mines which are highlighted will explode.
  • projector definition (all.spawn)
    • [SECTION_NUMBER]
; cse_abstract properties
section_name = search_light
name = esc_projector_0001
position = -162.704177856445,-24.1463871002197,-349.41064453125
direction = -0.0882880091667175,-0.302475899457932,-0.0493709892034531

; cse_alife_object properties
game_vertex_id = 31
distance = 0
level_vertex_id = 86409
object_flags = 0xffffff3a
custom_data = «END
[ph_projector]
path_look = esc_projector_look
rnd_wp = false
duration = 15000
rot_hspeed = 0.52
rot_vspeed = 0.22
END

; cse_visual properties
visual_name = equipments\projector

    [8623]
; cse_abstract properties
section_name = search_light
name = priboy_esc_projector
position = -162.704177856445,-24.1463871002197,-349.41064453125
direction = 0,0,0
; cse_alife_object properties
game_vertex_id = 31
distance = 0
level_vertex_id = 86409
object_flags = 0xffffff3a
custom_data = «END
[collide]
ignore_static
[logic]
cfg = scripts\projector.ltx
END
; cse_visual properties
visual_name = equipments\projector
  • projector look points (all.spawn)
    • [esc_projector_look]
points = p0,p1,p2,p3,p4,p5
p0:name = name00
p0:position = -149.43032836914,-30.275472640991,-353.27014160156
p0:game_vertex_id = 17
p0:level_vertex_id = 100801
p0:links = p1(1)

p1:name = name01
p1:position = -149.5237121582,-30.545835494995,-343.30355834961
p1:game_vertex_id = 34
p1:level_vertex_id = 100079
p1:links = p2(1)

p2:name = name02
p2:position = -151.0556640625,-30.367727279663,-335.43637084961
p2:game_vertex_id = 34
p2:level_vertex_id = 98606
p2:links = p3(1)

p3:name = name03
p3:position = -156.27381896973,-30.305505752563,-333.00823974609
p3:game_vertex_id = 34
p3:level_vertex_id = 93393
p3:links = p4(1)

p4:name = name04
p4:position = -163.82290649414,-30.35138130188,-334.50473022461
p4:game_vertex_id = 34
p4:level_vertex_id = 85751
p4:links = p5(1)

p5:name = name05
p5:position = -171.99630737305,-30.637647628784,-336.41754150391
p5:game_vertex_id = 34
p5:level_vertex_id = 77758
Implementation:

Source here


ph_radio

This basically gives more interaction with environment. You can turn on/off radio or even use it to switch channels.
  • related files:
    • all.spawn
    • modules.script
    • ph_sound.script
    • ph_radio.script
status = <const> on | off { default: "off" }
radio operational status; can be either

on

(radio turned on) or

off

(default value); another value is

destroyed

but it is handled via script only
on_use = <condition_list>
condition list which is utilized when actor use radio (to turn it on/off); this can be used to switch channels – see example below
theme = <string> { default: "" }
name of the sound theme; you can define sound themes in

gamedata\scripts\sound_theme.script

looped = <boolean> { default: false }
whether script should loop the sound (

true

) or not (

false

). By default sound will not be looped
random = <boolean> { default: true }
if set to

true

then script will randomly pick sounds from given

theme

; useful only if

theme

has more then one sound
pause_min = <number> { default: 0 }
minimum idle time (in milliseconds) after which next sound from

theme

will be played;

pause_min

cannot be greater then

pause_max

pause_max = <number> { default: 0 }
maximum idle time; time to play next sound from

theme

is counted as value between

pause_min

and

pause_max

; zero value for both -

pause_min

and

pause_max

means that script won't make any pause between picking sounds from

theme

no_hit = <boolean> { default: false }
if set to

false

, radio can be destroyed by shooting at it – sound will stop and radio won't be usable anymore
To spawn radio, use

physic_destroyable_object

section. This is important because this section allows an object to receive hit callbacks which is necessary to destroy radio (if

no_hit

option is set to

false

).
[851]
; cse_abstract properties
section_name = physic_destroyable_object
name = barin_radio
position = -137.344650268555,-24.3393001556396,-359.960021972656
direction = 0.00358727108687162,3.10139989852905,0.000144259523949586

; cse_alife_object properties
game_vertex_id = 0
distance = 0
level_vertex_id = 594270
object_flags = 0xffffff3a
custom_data = «END
[collide]
ignore_static
[logic]
cfg = scripts\radio.ltx
END

; cse_visual properties
visual_name = physics\decor\priemnik_gorizont

; cse_ph_skeleton properties

; cse_alife_object_physic properties
physic_type = 0x3
mass = 10
fixed_bones = link
Here’s an example of radio logic:
  • first radio is disabled (turned off) so if actor use it then radio will be turned on and music will start playing. This happens automatically, that is - script detects that actor used radio and then switch states based on previous state value - so using

    on_use

    here is not needed at all. Then if actor use the radio again - script will turn it off.
    • [logic]
active = ph_radio@dj_barin

[ph_radio@dj_barin]
snd = dj_barin_station
status = off
  • To switch between different sound themes (channels) you can use

    on_use

    condition. This however requires intermediate states because each time actor use radio - it is either turned on or off via script. To counteract this, we can use timer to immediately switch to another channel.

    First we start with

    ph_radio@start

    section which is an idle section - radio state is off. If actor use radio now it will be turned on via script but since we are using

    on_use

    condition - section will be switched immediately to intermediate (timer) section, that is

    ph_radio@switch_channel_1

    . This section will switch to

    ph_radio@channel_1

    section after 100 milliseconds and that is our first channel. In the same manner, we can switch to next channel and so on. The drawback is that to turn the radio off, you actually need to switch through all available channels.
    • [logic]
active = ph_radio@start

[ph_radio@start]
snd = whatever_snd_theme
on_use = ph_radio@switch_channel_1

[ph_radio@switch_channel_1]
snd = whatever_snd_theme
state = off
on_timer = 100 | ph_radio@channel_1

[ph_radio@channel_1]
snd = channel_1_snd_theme
status = on
random = true
on_use = ph_radio@switch_channel_2

[ph_radio@switch_channel_2]
snd = whatever_snd_theme
state = off
on_timer = 100 | ph_radio@channel_2

[ph_radio@channel_2]
snd = channel_2_snd_theme
status = on
random = true
on_use = ph_radio@start


ph_sound

Scheme handles sound sources; scheme can be set for objects (like radio, speaker) or as sound source on the terrain.
  • related files:
    • all.spawn
    • modules.script
    • ph_sound.script
    • sound_theme.script
snd = <string> { default: "" }
name of a theme from file

gamedata\scripts\sound_theme.script

looped = <boolean> { default: false }
if set to

true

then script will loop the sound
random = <boolean> { default: true }
if set to

true

then random sounds will be picked from the theme
min_idle = <number> { default: 0 }
minimum idle time before the sound is turned on, in ms
max_idle = <number> { default: 0 }
maximum idle time before the sound is turned on, in ms
no_hit = <boolean>
if set to

true

(default) then scheme will ignore object hits thus sound won't be stopped when object was hit
Note: looping sound may continue playing, even if the object becomes nil. To avoid this situation it's better to use section which will stop sound (you can also create section with single short - might be muted - sound); you may also set no_hit to false and then the sound should be stopped when object is hit:
[logic]
active = ph_sound@enabled
on_hit = hit

[hit]
on_info = %+disable_sound%

[ph_sound@enabled]
snd       = sound_theme_name
looped    = true
on_info   = {+disable_sound} ph_sound@disabled, {=dist_to_actor_le(2)} ph_sound@disabled

[ph_sound@disabled]
snd       = sound_theme_name
looped    = false
on_signal = sound_end | nil

; ------------------------------------------------------------------------------------
; ------------------------------------------------------------------------------------

[logic]
active = ph_sound@on

[ph_sound@on]
snd       = radar_meh_on
looped    = true
on_info   = {+bar_deactivate_radar_done} ph_sound@off

[ph_sound@off]
snd       = radar_meh_off
looped    = false
on_signal = sound_end | nil
Example:
[logic]
active = ph_sound

[ph_sound]
snd              = gar_seryi_shooting
looped           = true
max_idle         = 5000
on_actor_in_zone = gar_seryi_factory | ph_sound@end

[ph_sound@end]
snd       = gar_seryi_shooting_2
looped    = false
on_signal = sound_end | nil

gar_seryi_shooting

is defined in

sound_theme.script

ph_snd_themes["gar_seryi_shooting"] = {[[characters_voice\scenario\garbage\distance_shooting]]}
Note: ph_sound scheme can be applied with space restrictors:
[logic]
active = sr_idle

[sr_idle]
on_info = {+gar_seryi_actor_enter} nil, {+gar_distance_shooting} ph_sound

[ph_sound]
on_info = {+gar_seryi_actor_enter} nil
snd = gar_seryi_shooting
on_actor_in_zone = gar_seryi_factory_0000 | ph_sound@end

[ph_sound@end]
snd = gar_seryi_shooting_2
looped = false
on_signal = sound_end | nil


Heli behaviour schemes

Helicopter schemes

  • the helicopter has "logic" set in custom_data section
  • the helicopter does not react to anomalies
  • the helicopter cannot collide with level geometry until shot down
  • hits in the area of the cabin in which the first pilot sits are dozens of times more damaging for the helicopter
  • like stalkers, the helicopter has an all-purpose combat scheme
  • helicopter pilots have replicas for the following events: hit, enemy spotted, damaged (smoking), falling.

heli_move

Allows the helicopter to fly along a patrol path, control speed, hover, shoot at various targets. The scheme must have

path_move

set — the path along which the helicopter will fly. It may contain just one point, if the helicopter should hover. It is allowed, but not necessary to set

path_look

- the path into the points of which the helicopter should look. The vertices of these points can be anywhere within the boundaries of the level's bounding box. They do not depend on AI nodes.

The helicopter will fly along points without respect of connections between them. It will fly from vertex to vertex in order of increasing number (that is, in the order they were set in the level). The helicopter will try to follow path vertices exactly. If needed, it can fly precisely under a bridge.

The helicopter will fly as fast as possible. That is, if you set its speed at the next vertex at 10 m/s, while its maximum speed is set to 30 m/s, then it will not start flying 10 m/s right away. It will first speed up to 30 m/s, then on approach of the vertex it will slow down such that it gets to the vertex at 10 m/s.

If a

path_move

vertex has some flags, the helicopter will look into any of the

path_look

vertices for which the same set of flags is specified. It will start turning towards the point starting at the previous point of the path. The helicopter cannot currently look towards several

path_look

points while hovering.
  • related files:
    • all.spawn
    • modules.script
    • heli_move.script
path_move = <patrol> { default: "" }
path along which the helicopter will fly
path_look = <patrol> { default: "" }
path into the points of which the helicopter should look
rocket_delay = <number> { default: nil }
delay (in msec) before firing rockets
default_velocity = <number> { default: 30 }
helicopter speed in meters per seconds (m/sec)
engine_sound = <boolean> { default: true }
enable/disable heli engine sound
invulnerable = <boolean> { default: false }
enable/disable capability of being wounded
immortal = <boolean> { default: false }
if

true

the helicopter will calculate damage, but not recieve it
mute = <boolean> { default: false }
enable/disable default helicopter (pilot's) phrases (sounds)
You may use patrols to set flags:

» path_move

e
sets enemy. The helicopter will start firing at this target starting at the previous vertex. If the value is not set, it will fire at the point form

path_look

corresponding to the given vertex. If

e = actor

(or

e = a

) is set, then fire will target the actor. If

e = number

fire will target the object with the given story id.
w
which weapon to use. The possible values are:

w = 1

to use the machine gun only;

w = 2

to use rockets only. Shoots with everything by default.
v
maximum speed in m/s on the path from the given vertex to the next. If this parameter is not set, the value from

helicopter.ltx

is taken by default.
dv
destination velocity in m/s that the helicopter should have on arriving at the given vertex
die
kill the helicopter
flame
set the helicopter on fire, as if it was damaged

» path_look

e
works the same way as in

path_move

. The difference is that the helicopter will only start firing at it when it gets to the point of

path_move

that corresponds to the given vertex.
w
works the same way as in

path_move

t
duration of time during which the helicopter will look at the given point. If this parameter is not set, then the helicopter will fly by without stopping, but will try to face the vertex while moving.
All-purpose combat scheme

In the general combat scheme, a helicopter is not tied to a path, does not see anyone, can only find someone upon damaged receive or from a parameter in custom_data. The helicopter shoots at an enemy upon detection. Until then, looks for him, circling the points of last sighting. If it does not see the enemy for a long time — forgets him. If the enemy is specified in the current behavior section, he will not be forgotten while the section is in effect.

Settings

There is no separate section for this behavior scheme. Therefore, settings are given in the current behavior scheme:
combat_ignore = <condition_list>
if

true

, the helicopter will not try to "take revenge" on whoever hits it
combat_enemy = <const> : nil | actor | story_id
this parameter could be used to give the helicopter a specific enemy:
nil
no enemy
actor
player
story_id
id number of the enemy
combat_use_rocket = <boolean> { default: true }
whether the helicopter is authorized to use rockets
combat_use_mgun = <boolean> { default: true }
whether the helicopter is authorized to use the machine gun
combat_velocity = <number> { default: 30 }
speed in m/s at which the helicopter will circle
combat_safe_altitude = <number> { default: 3.0 }
height, relative to the highest point of the geometry of the level, below which the helicopter will not decend (can be negative)
The helicopter has the

xr_hit

scheme. It works the same way as for stalkers. There is a group of functions (

xr_effects

) for working with a helicopter and its custom_data:
heli_set_enemy_actor()
make the actor the helicopter's enemy
heli_start_flame()
set the helicopter on fire
heli_die()
destroy the helicopter


xr_logic

configure_schemes(npc, ini, ini_filename, stype, section_logic, gulag_name)

purpose:
function prepares storage (setup ini file with object scheme sections) to activate ai scheme; previous scheme is removed (if any was set previously); after this function was called, scheme is activated by function

determine_section_to_activate

arguments:

  • npc <game_object>

    - object awaiting for scheme activation (npc, mutant, physic object, etc)

  • ini <ini_file>

    -

    custom_data

    field

  • ini_filename <string>

    - path to ltx file

  • stype <const>

    - scheme type:
    • stype_stalker
    • stype_mobile
    • stype_item
    • stype_heli
    • stype_restrictor

  • section_logic

    - name of section

  • gulag_name

    - name of smart terrain (gulag) - only for npcs and mutants
returns:
ini file
remarks:
  • all generic schemes (combat, meet, danger, etc) are reset
  • if object is trader npc then function initialize

    trade_manager

    for that npc
  • to disable previous scheme (if any was set), function calls

    deactivate

    function for previous scheme

determine_section_to_activate(npc, ini, section_logic, actor)

purpose:
search for a section to activate (used only for

logic

section)
arguments:

  • npc <game_object>

    - object

  • ini <ini_file>

    -

    custom_data

    field

  • section_logic <string>

    - literaly string

    logic

  • actor <game_object>

    - db.actor
returns:
active section name
remarks:
  • function is used only to determine active section in

    logic

    section (

    active

    field)

disable_generic_schemes(npc, stype)

purpose:
function disables generic schemes
  • stype_stalker
    • combat - xr_combat
    • use - xr_use
    • hit - xr_hit
    • actor_dialogs - xr_meet
    • heli_hunter - xr_heli_hunter
    • combat_ignore - xr_combat_ignore
  • stype_mobile
    • mob_combat
    • mob_trade
  • stype_item
    • ph_on_hit
  • stype_heli
    • hit - xr_hit
arguments:

  • npc <game_object>

    - object

  • stype <const>

    - scheme type
    • stype_stalker
    • stype_mobile
    • stype_item
    • stype_heli

enable_generic_schemes(ini, npc, stype, section)

purpose:
function enables generic schemes
  • stype_stalker
    • danger - xr_danger
    • on_combat - xr_combat
    • on_use - xr_use
    • info - stalker_generic
    • on_hit - xr_hit
    • actor_dialogs - xr_meet
    • wounded - xr_wounded
    • abuse - xr_abuse
    • meet - xr_meet
    • on_death - xr_death
    • heli_hunter - xr_heli_hunter
    • combat_ignore - xr_combat_ignore
  • stype_mobile
    • on_combat - mob_combat
    • on_death - mob_death
    • on_trade - mob_trade
    • on_hit - xr_hit
    • mob_panic
  • stype_item
    • on_hit - ph_on_hit
  • stype_heli
    • on_hit - xr_hit
arguments:

  • ini <ini_file>

    - object's custom_data

  • npc <game_object>

    - object

  • stype <const>

    - scheme type
    • stype_stalker
    • stype_mobile
    • stype_item
    • stype_heli

  • section <string>

    - scheme section name

activate_by_section(npc, ini, section, loading)

purpose:
function activates scheme by section (scheme) name
arguments:

  • npc <game_object>

    - object

  • ini <ini_file>

    - object's custom_data

  • section <string>

    - section to activate (obtained by function

    determine_section_to_activate()

    )

  • loading <boolean>

    -

    true

    if object loaded data from packet
remarks:
  • only one scheme can be activated at once, so if there is already any scheme activated then function reports error
  • if section doesn't exist in custom_data then function reports error
  • if function determines scheme name and script file then function calls

    set_scheme

    function (for this scheme) to activate the scheme
  • if

    npc

    argument is stalker then function calls

    activate_scheme

    function,

    reset_scheme

    otherwise

reset_generic_schemes_on_scheme_switch(npc, scheme, section)

purpose:
function resets generic schemes when schemes are beeing switched. Works only for schemes:
  • stype_stalker
  • stype_mobile
  • stype_item
arguments:

  • npc <game_object>

    - object

  • scheme <string>

    - scheme name

  • section <string>

    - section name

assign_storage_and_bind(npc, ini, scheme, section)

purpose:
this function is called by scheme's

set_scheme

function and binds scheme to object using scheme's

add_to_binder

function
arguments:

  • npc <game_object>

    - object

  • ini <ini_file>

    - object's custom_data

  • scheme <string>

    - scheme name

  • section <string>

    - section name
returns:
storage object

subscribe_action_for_events(npc, storage, new_action)

purpose:
function registers new action to receive event notifications
arguments:

  • npc <game_object>

    - object

  • storage <table>

    - storage

  • new_action <object>

    - object of class which is controlling scheme action

unsubscribe_action_from_events(npc, storage, new_action)

purpose:
function disables given action (action won't receive events)
arguments:

  • npc <game_object>

    - object

  • storage <table>

    - storage

  • new_action <object>

    - object of class which is controlling scheme action

issue_event(npc, st, event_fn, ...)

purpose:
function is used for calling active scheme functions
arguments:

  • npc <game_object>

    - object

  • st <table>

    - storage array for active scheme

  • event_fn <string>

    - function name

  • ...

    - parameters for

    event_fn

remarks:
  • function calls

    event_fn

    only for active scheme

  • event_fn

    has to be set by

    subscribe_action_for_events()

    function

pick_section_from_condlist(actor, npc, condlist)

purpose:
function checks all sections in given condition list. For each section all the switch conditions are checked, first section which conditions are met is returned unless it is

never

section - then

nil

is returned
arguments:

  • actor <game_object>

    - object

  • npc <game_object>

    - object

  • condlist <condlist>

    - parsed condition list
returns:
  • section name - first section which conditions are met (section can be =

    nil

    )

  • nil

    if conditions are met but section is

    never

  • nil

    if all the conditions for all sections failed
remarks:

  • condlist

    argument is condition list parsed by function

    parse_condlist()

switch_to_section(npc, st, section)

purpose:
function switch one section to another
arguments:

  • npc <game_object>

    - object

  • st <table>

    - storage

  • section <string>

    - section to activate
returns:

true

if section is switched,

false

otherwise
remarks:
  • section is switched only if it is different then the active section
  • before switching, function calls

    deactivate

    function for active scheme

see_actor(npc)

purpose:
function checks whether object see player
arguments:

  • npc <game_object>

    - object
returns:

true

if object see player,

false

otherwise

try_switch_to_another_section(npc, st, actor)

purpose:
function checks conditions (listed below), if any condition is met then function is trying to switch to that section
  • on_actor_dist_le
  • on_actor_dist_le_nvis
  • on_actor_dist_ge
  • on_actor_dist_ge_nvis
  • on_signal
  • on_info
  • on_timer
  • on_game_timer
  • on_actor_in_zone
  • on_actor_not_in_zone
  • on_npc_in_zone
  • on_npc_not_in_zone
  • on_actor_inside
  • on_actor_outside
arguments:

  • npc <game_object>

    - object

  • st <table>

    - storage

  • actor <game_object>

    - object
returns:

true

if section can be switched,

false

otherwise
remarks:
  • to use this function in ai schemes you have to set

    storage.logic

    field by calling function

    cfg_get_switch_conditions()

function set_scheme(obj, ini, scheme, section, gulag_name)
  local st = xr_logic.assign_storage_and_bind(obj, ini, scheme, section)
  st.logic = xr_logic.cfg_get_switch_conditions(ini, section, obj)
  ...
end

-- and then in update function of your scheme:
function action_name:update(delta)
  if xr_logic.try_switch_to_another_section(self.object, self.st, actor) then
    return
  end
end

is_active(npc, st)

purpose:
function is called to verify that the scheme is active
arguments:

  • npc <game_object>

    - object

  • st <table>

    - storage
returns:

true

if scheme is active,

false

otherwise
remarks:
  • this function is mainly used in evaluators

parse_infop1(rslt, str)

purpose:
function parse given string and search for infoportions and functions
arguments:

rslt

- table where function will store parsed infoportions/functions

str

- string with infoportions/functions
remarks:
function saves different values depending on infoportion type:
  • +infoportion
    • name = infoportion name
    • required = true
  • -infoportion
    • name = infoportion name
    • required = false
  • ~number
    • prob = probability value (number)
  • =function_name
    • func = function name
    • expected = true
  • !function_name
    • func = function name
    • expected = false

parse_func_params(str)

purpose:
function returns table with parsed arguments
arguments:

str

- string formated as: a:b:c:d:...
returns:
table with arguments given as

str

remarks:
  • function is used for parsing arguments of function saved in condition list
local tbl = parse_func_params("a:b:c:d:e")
tbl[1] = "a"
tbl[2] = "b"
tbl[3] = "c"
tbl[4] = "d"
tbl[5] = "e"

parse_infop(rslt, str)

purpose:
function parse given string and search for infoportions and functions
arguments:

rslt

- table where function will store parsed infoportions/functions

str

- string with infoportions/functions
remarks:
function saves different values depending on infoportion type:
  • +infoportion
    • name = infoportion name
    • required = true
  • -infoportion
    • name infoportion name
    • required = false
  • ~number
    • prob = probability value (number)
  • =function_name
    • func = function name
    • expected = true
    • params = function params
  • !function_name
    • func = function name
    • expected = false
    • params = function params

parse_condlist(npc, section, field, src)

purpose:
function to parse given condition list
arguments:

  • npc <game_object>

    - object

  • section <string>

    - section name

  • field <string>

    - field name

  • src <condlist>

    - condition list
returns:
table with parsed condition list
remarks:
  • take a look at functions

    parse_infop

    and

    parse_func_params

local cond_list = "{=my_func(arg1:arg2:arg3) +info1 -info2} walker"
local tbl = parse_condlist(nil, nil, nil, cond_list)

-- tbl will contain data:

tbl[1].section = "walker"

tbl[1]["infop_check"][1].func = "my_func"
tbl[1]["infop_check"][1].expected = true
tbl[1]["infop_check"][1].params = {"arg1", "arg2", "arg3"}

tbl[1]["infop_check"][2].name = "info1"
tbl[1]["infop_check"][2].required = true

tbl[1]["infop_check"][3].name = "info2"
tbl[1]["infop_check"][3].required = false

-- ****************************************************** --

cond_list = "{=my_func(46:name)} walker %+give_this_info -take_this_info_away%, {!my_another_func +info} sleeper %=spawn_item(wpn_abakan)%, nil"

-- tbl will contain data:

tbl[1].section = "walker"

tbl[1]["infop_check"][1].func = "my_func"
tbl[1]["infop_check"][1].expected = true
tbl[1]["infop_check"][1].params = {46, "name"}

tbl[1]["infop_set"][1].name = "give_this_info"
tbl[1]["infop_set"][1].required = true

tbl[1]["infop_set"][2].name = "take_this_info_away"
tbl[1]["infop_set"][2].required = false

tbl[2].section = "sleeper"
tbl[2]["infop_check"][1].func = "my_another_func"
tbl[2]["infop_check"][1].expected = false

tbl[2]["infop_check"][2].name = "info"
tbl[2]["infop_check"][2].required = true

tbl[2]["infop_set"][1].func = "spawn_item"
tbl[2]["infop_set"][1].expected = true
tbl[2]["infop_set"][1].params = {"wpn_abakan"}

tbl[3].section = nil

cfg_get_number_and_condlist(ini, section, field, npc)

purpose:
function parse given field for number and condition list
arguments:

  • ini <ini_file>

    - object's custom_data

  • section <string>

    - section name

  • field <string>

    - field name

  • npc <game_object>

    - object
returns:
table with number, condition list and field name if there was no problems during parsing,

nil

otherwise

cfg_get_string_and_condlist(ini, section, field, npc)

purpose:
function parse given field for string and condition list
arguments:

  • ini <ini_file>

    - object's custom_data

  • section <string>

    - section name

  • field <string>

    - field name

  • npc <game_object>

    - object
returns:
table with string, condition list and field name if there was no problems during parsing,

nil

otherwise

cfg_get_two_strings_and_condlist(ini, section, field, npc)

purpose:
function parse given field for two strings and condition list
arguments:

  • ini <ini_file>

    - object's custom_data

  • section <string>

    - section name

  • field <string>

    - field name

  • npc <game_object>

    - object
returns:
table with two strings, condition list and field name if there was no problems during parsing,

nil

otherwise

cfg_get_condlist(ini, section, field, npc)

purpose:
function parse given field for condition list
arguments:

  • ini <ini_file>

    - object's custom_data

  • section <string>

    - section name

  • field <string>

    - field name

  • npc <game_object>

    - object
returns:
table with condition list and field name if there was no problems during parsing,

nil

otherwise

cfg_get_switch_conditions(ini, section, npc)

purpose:
function search for all common conditions in given section, evaluates existing conditions by executing proper functions and saves the results in table which then returns. Common conditions which function is searching for are:
  • on_actor_dist_le
  • on_actor_dist_le_nvis
  • on_actor_dist_ge
  • on_actor_dist_ge_nvis
  • on_signal
  • on_info
  • on_timer
  • on_game_timer
  • on_actor_in_zone
  • on_actor_not_in_zone
  • on_actor_inside
  • on_actor_outside
  • on_npc_in_zone
  • on_npc_not_in_zone
arguments:

  • ini <ini_file>

    - object's custom_data

  • section <string>

    - section name

  • npc <game_object>

    - object
returns:
table with evaluated common conditions in given section
remarks:
  • function is used for storage argument passed for

    try_switch_to_another_section()

    function

cfg_get_overrides(ini, section, npc)

purpose:
function returns table with schemes overrides
arguments:

  • ini <ini_file>

    - object's custom_data

  • section <string>

    - section name

  • npc <game_object>

    - object
returns:
table with schemes overrides
remarks:
returned table contains fields:
  • heli_hunter (parsed condition list) - if heli_hunter override is present
  • combat_ignore (condition list)
  • combat_ignore_keep_when_attacked (boolean)
  • combat_type (condition list)
  • on_combat (condition list)
  • soundgroup (string) - or center_point (for kamp)
  • companion_enabled (boolean) -

    obsolete

  • meet_enabled (boolean) -

    obsolete

  • meet_talk_enabled (boolean) -

    obsolete

  • meet_dialog (string) -

    obsolete

  • meet_state (string) -

    obsolete

  • reactions (table) -

    obsolete

  • wounded_enabled (boolean) -

    obsolete

cfg_get_npc_and_zone(ini, section, field, npc)

purpose:
function is used for parsing conditions:
  • on_npc_in_zone
  • on_npc_not_in_zone
arguments:

  • ini <ini_file>

    - object's custom_data

  • section <string>

    - section name

  • field <string>

    - field name

  • npc <game_object>

    - object
returns:
table with two strings, condition list and field name if there was no problems during parsing,

nil

otherwise

generic_scheme_overrides(npc)

purpose:
function returns table with schemes overrides for given object
arguments:

  • npc <game_object>

    - object
returns:
table with schemes overrides
remarks:
returned table contains fields:
  • heli_hunter (parsed condition list)
  • combat_ignore (condition list)
  • combat_ignore_keep_when_attacked (boolean)
  • combat_type (condition list)
  • on_combat (condition list)
  • companion_enabled (boolean)
  • soundgroup (string)

mob_release(mob)

purpose:
removes script control from

mob

object
arguments:

  • mob <game_object>

    - object

mob_capture(mob, reset_actions)

purpose:
function set script control over object
arguments:

  • mob <game_object>

    - object

  • reset_actions <boolean>

    - whether previous action should be reset or not

mob_captured(mob)

purpose:
funtion checks whether object is controlled by script
arguments:

  • mob <game_object>

    - object
returns:

true

if object is controlled by script;

false

otherwise

save_logic(obj, packet)

purpose:
function saves scheme's

activation_time

and

activation_game_time

for given object
arguments:

  • obj <game_object>

    - object

  • packet <net_packet>

    - packet

load_logic(obj, reader)

purpose:
function loads scheme's

activation_time

and

activation_game_time

for given object
arguments:

  • obj <game_object>

    - object

  • reader <net_packet>

    - packet

pstor_is_registered_type(tv)

purpose:
function checks whether variable type is allowed to be saved in pstor
arguments:

  • tv <string>

    - variable type
returns:

true

if variable type is allowed to be saved in pstor,

false

otherwise
remarks:
allowed variables - only of type:
  • boolean
  • string
  • number

pstor_store(obj, varname, val)

purpose:
function saves (in pstor) variable name and value
arguments:

  • obj <game_object>

    - object

  • varname <string>

    - variable name

  • val

    - value
remarks:
function can save only values of type:
  • boolean
  • string
  • number

pstor_retrieve(obj, varname, defval)

purpose:
function loads value (using its name) from pstor
arguments:

  • obj <game_object>

    - object

  • varname <string>

    - variable name

  • defval

    - default value
returns:
value of given variable,

defval

or

nil

remarks:
function can load only values of type:
  • boolean
  • string
  • number

pstor_save_all(obj, packet)

purpose:
function saves all variables from pstor of given object
arguments:

  • obj <game_object>

    - object

  • packet <net_packet>

    - packet

pstor_load_all(obj, reader)

purpose:
function loads all variables from pstor of given object
arguments:

  • obj <game_object>

    - object

  • reader <net_packet>

    - packet

save_obj(obj, packet)

purpose:
function saves given object's data:
  • storage.ini_filename - name of ini file with custom_data field
  • storage.section_logic - logic section (custom_data)
  • storage.active_section - active scheme section
  • storage.gulag_name - gulag name (for stalker/mutant only, if was controlled by gulag)
  • storage.activation_time - when active_section was activated
  • storage.activation_game_time - when active_section was activated (game time)
  • custom variables saved in scheme's

    save

    function
  • all variables from pstor
arguments:

  • obj <game_object>

    - object

  • packet <net_packet>

    - packet

load_obj(obj, reader)

purpose:
function loads given object's data:
  • storage.ini_filename - name of ini file with custom_data field
  • storage.section_logic - logic section (custom_data)
  • storage.active_section - active scheme section
  • storage.gulag_name - gulag name (for stalker/mutant only, if was controlled by gulag)
  • storage.activation_time - when active_section was activated
  • storage.activation_game_time - when active_section was activated (game time)
  • all variables from pstor
arguments:

  • obj <game_object>

    - object

  • reader <net_packet>

    - packet

get_customdata_or_ini_file(npc, filename)

purpose:
function returns ini file with object's custom_data field
arguments:

  • npc <game_object>

    - object

  • filename <table>

    - name of file which contains custom_data field
returns:
returns ini file with custom_data field, it can be:

  • npc:spawn_ini()

    - if

    filename = "<customdata>"

  • config\scripts\dummy.ltx

    - if

    filename = "<customdata>"

    and object doesn't have custom_data field
  • dynamic ltx from

    gulag_tasks.loadLtx

  • custom ltx if

    filename

    is a path to ltx file

initialize_obj(obj, st, loaded, actor, stype)

purpose:
function configures ai schemes (load last section, activates scheme)
arguments:

  • obj <game_object>

    - object

  • st <table>

    - storage

  • loaded <boolean>

    - whether object loaded data from packet

  • actor <game_object>

    - object

  • stype <const>

    - scheme type
remarks:
  • function is using

    configure_schemes

    function to setup the scheme


xr_effects

camera_got_hit(actor, cam_cover, p)

purpose:
executed when camera is hit
arguments:

  • actor <game_object>

    - db.actor

  • cam_cover <game_object>

    - camera cover object

  • p <table>

    - parameters array; p[1] must be camera's name

disable_ui_elements(actor, npc, p)

purpose:
disables hud elements (mini map, health progressbar, etc), hides actor weapon, disables actor input
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:
    • all - to disable hud elements, actor input and hide actor weapon
    • weapon - to hide actor weapon
    • input - to disable actor input
    • hud - to disable hud elements

enable_ui_elements(actor, npc, p)

purpose:
enables hud elements (mini map, health progressbar, etc), restores actor weapon, enables actor input
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:
    • all - to enable hud elements, actor input and restore actor weapon
    • weapon - to restore actor weapon
    • input - to enables actor input
    • hud - to enables hud elements

disable_ui(actor, npc, p)

purpose:
same as

disable_ui_elements(actor, npc, {"all"})

arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array; not used

enable_ui(actor, npc, p)

purpose:
same as

enable_ui_elements(actor, npc, {"all"})

arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array; not used

run_cam_effector(actor, npc, p)

purpose:
starts camera effect
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:

    • p[1]

      - camera effect name without extension (required)

    • p[2]

      - effector id (optional, default - random)

    • p[3]

      - whether loop camera effect (optional, default - false)

stop_cam_effector(actor, npc, p)

purpose:
disables camera effect
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:

    • p[1]

      - camera effect id (required)

run_postprocess(actor, npc, p)

purpose:
starts postprocess effector
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:

    • p[1]

      - postprocess effect name without extension (required)

    • p[2]

      - effector id (optional, default - random)

    • p[3]

      - whether loop postprocess effector (optional, default - false)

stop_postprocess(actor, npc, p)

purpose:
stops postprocess effector
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:

    • p[1]

      - postprocess effect id (required)

run_tutorial(actor, npc, p)

purpose:
starts tutorial video
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:

    • p[1]

      - tutorial name (required)

run_tutorial_if_newbie(actor, npc, p)

purpose:
starts tutorial video (if actor decided to ask Sid to describe PDA etc)
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:

    • p[1]

      - tutorial name (required)

teleport_actor(actor, npc, p)

purpose:
teleports actor to given destination
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:

    • p[1]

      - position waypoint name (required)

    • p[2]

      - look waypoint name (required)

drop_actor_inventory(actor, npc, p)

purpose:
teleports actor items to specified position
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:

    • p[1]

      - drop position waypoint name (required)

drop_npc_inventory(actor, npc, p)

purpose:
teleports npc's items to specified position
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

  • p <table>

    - parameters array:

    • p[1]

      - drop position waypoint name (required)

drop_npc_item(actor, npc, p)

purpose:
force npc to drop specified item
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

  • p <table>

    - parameters array:

    • p[1]

      - item (section name) to drop (required)

drop_npc_items(actor, npc, p)

purpose:
force npc to drop specified items
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

  • p <table>

    - parameters array; items (section names) to drop

give_item(actor, npc, p)

purpose:
npc receives specified item
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

  • p <table>

    - parameters array:

    • p[1]

      - item section name (required)

give_items(actor, npc, p)

purpose:
npc receives specified items
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

  • p <table>

    - parameters array; items (section names)

respawner_spawn(actor, npc, p)

purpose:
force specified respawner to spawn items
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:

    • p[1]

      - respawner name (required)

play_particle_on_path(actor, npc, p)

purpose:
plays particle effect on specified path
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:

    • p[1]

      - particle effect name (required)

    • p[2]

      - path waypoint name (required)

    • p[3]

      - point probability - path points at which particle effect will be played depends on point probability; default (100%) means that particle effect will be played on each point (optional, default = 100%)

monster_berserk(actor, npc, p)

purpose:
makes the mutant evil ;)
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be mutant object

  • p <table>

    - parameters array; not used

activate_bolt(actor, npc, p)

purpose:
activates actor's bolt slot
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array; not used

send_tip(actor, npc, p)

purpose:
displays hud message
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - object which is currently executing scheme where this function was called (can be npc, mutant, vehicle, helicopter, space restrictor, physic object, etc)

  • p <table>

    - parameters array:

    • p[1]

      - news id (required)

    • p[2]

      - sender - icon name from news_manager.tips_icons array (optional)

    • p[3]

      - sender story id (optional)

hit_npc(actor, npc, p)

purpose:
something hits npc (or npc hits something)
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

  • p <table>

    - parameters array:

    • p[1]

      - direction - if number then it is considered story id (if reverse is true then object with this story id will hit npc, otherwise npc will hit that object); if it's not a number then it is considered a waypoint name (if reverse is true then npc will be hit from zero point of this waypoint, otherwise - npc will hit in zero point direction of that waypoint)

    • p[2]

      - bone name

    • p[3]

      - power

    • p[4]

      - impulse

    • p[5]

      - reverse (default: false) - decides who hits: if reverse is true then something will hit npc, if reverse is false then npc will hit that something

hit_obj(actor, npc, p)

purpose:
function to hit object
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

  • p <table>

    - parameters array:

    • p[1]

      - story id of object which should be hit (required)

    • p[2]

      - hit bone name (required)

    • p[3]

      - hit power (required)

    • p[4]

      - hit impulse (required)

    • p[5]

      - story id of object which will hit object given by story id in param

      p[1]

      or waypoint name (optional); if none given, then

      npc

      will hit

      p[1]

      object

hit_by_killer(actor, npc, p)

purpose:
killer hits killed npc
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - killed npc object

  • p <table>

    - parameters array:

    • p[1]

      - hit bone name

    • p[2]

      - hit power

    • p[3]

      - hit impulse

set_friends(actor, npc, p)

purpose:
function sets friendly relation between

npc

and objects given by story ids (in parameters array)
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

  • p <table>

    - parameters array - list of story ids

set_enemies(actor, npc, p)

purpose:
function sets foe relation between

npc

and objects given by story ids (in parameters array)
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

  • p <table>

    - parameters array - list of story ids

play_snd(actor, npc, p)

purpose:
function to play sound around actor ("in actor's head")
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - not used

  • p <table>

    - parameters array:

    • p[1]

      - path to sound file (required)

    • p[2]

      - delay (optional)

play_snd_from_obj(actor, npc, p)

purpose:
function to play sound from given source
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - not used

  • p <table>

    - parameters array:

    • p[1]

      - story id - sound source (required)

    • p[2]

      - path to sound file (required)

play_snd_now(actor, npc, p)

purpose:
sets sound for npc (npc "speaks" given phrase)
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

  • p <table>

    - parameters array:

    • p[1]

      - sound preset name defined in

      sound_theme.script

      (required)

inc_counter(actor, npc, p)

purpose:
function increments variable by 1; useful in case you want to count how many npcs actor should kill, etc.
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - not used

  • p <table>

    - parameters array:

    • p[1]

      - pstor variable name (required)

dec_counter(actor, npc, p)

purpose:
function decrements variable by 1; useful in case you want to count how many npcs actor should kill, etc.
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - not used

  • p <table>

    - parameters array:

    • p[1]

      - pstor variable name (required)

alien_control(actor, npc, p)

purpose:
when bloodsucker is close to actor, using this function actor can see himself like from bloodsuckers eyes
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be bloodsucker object

  • p <table>

    - parameters array:

    • p[1]

      -

      "true"

      to set alien control,

      "false"

      otherwise

set_gulag_enemy_actor(actor, npc, p)

purpose:
sets given gulag foe relation to actor
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - not used

  • p <table>

    - parameters array:

    • p[1]

      - gulag name or story_id (required)

smart_terrain_force_update(actor, npc, p)

purpose:
force smart terrain update
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - not used

  • p <table>

    - parameters array:

    • p[1]

      - smart terrain name or story_id (required)

actor_punch(npc)

purpose:
force npc to hit actor
arguments:

  • npc <game_object>

    - stalker object

clearAbuse(npc)

purpose:
resets abuse for given npc
arguments:

  • npc <game_object>

    - stalker object

turn_off(actor, npc, p)

purpose:
function to turn off the hanging lamp
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - not used

  • p <table>

    - parameters array:

    • p[1]

      - story_id of hanging lamp (required)

turn_off_object(actor, npc)

purpose:
function to turn off the hanging lamp
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be hanging lamp

turn_on(actor, npc, p)

purpose:
function to turn on the hanging lamp
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - not used

  • p <table>

    - parameters array:

    • p[1]

      - story_id of hanging lamp (required)

turn_on_object(actor, npc)

purpose:
function to turn on the hanging lamp
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be hanging lamp

actor_friend(actor, npc)

purpose:
sets friendly relation between actor and npc
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

actor_neutral(actor, npc)

purpose:
sets neutral relation between actor and npc
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

actor_enemy(actor, npc)

purpose:
sets foe relation between actor and npc
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be npc object

disable_combat_handler(actor, npc)

purpose:
disables [combat] section for given npc
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be stalker or mutant object

disable_combat_ignore_handler(actor, npc)

purpose:
disables [combat_ignore] section for given npc
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be stalker object

search_gulag_job(actor, npc)

purpose:
resets object's job in gulag
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be stalker or mutant object

actor_hits_npc(actor, npc)

purpose:
function to hit npc (by actor)
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be stalker object

actor_items_to_stash(actor, npc, p)

purpose:
transfers actor items to stash
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - not used

  • p <table>

    - parameters array:

    • p[1]

      - stash story id (required)

heli_set_enemy_actor(actor, npc)

purpose:
sets actor as helicopter's enemy
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be helicopter

heli_set_enemy(actor, npc, p)

purpose:
sets object as helicopter's enemy
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be helicopter

  • p <table>

    - parameters array:

    • p[1]

      - object (target) story_id (required)

heli_clear_enemy(actor, npc)

purpose:
resets helicopter's enemy
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be helicopter

heli_start_flame(actor, npc)

purpose:
sets helicopter on fire
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be helicopter object

heli_die(actor, npc)

purpose:
function to destroy helicopter
arguments:

  • actor <game_object>

    - db.actor

  • npc <game_object>

    - should be helicopter object


Artefacts activation

To enable artefact activation open

config\misc\artefacts.lx

file and:
  • add all artefacts to section

    artefact_spawn_zones

    • :
    [artefact_spawn_zones]
; artefact_name      = anomaly_type,            radius, power
af_medusa            = zone_mosquito_bald_weak, 2.0,     30.0
af_cristall_flower   = zone_mosquito_bald_weak, 3.0,     50.0
af_night_star        = zone_mosquito_bald_weak, 5.0,    100.0
af_vyvert            = zone_gravi_zone_weak,    2.0,     30.0
af_gravi             = zone_gravi_zone_weak,    3.0,     50.0
af_gold_fish         = zone_gravi_zone_weak,    5.0,    100.0
af_cristall          = torrid_zone,             3.0,     50.0
af_blood             = zone_mincer_weak,        2.0,     30.0
af_mincer_meat       = zone_mincer_weak,        3.0,     50.0
af_soul              = zone_mincer_weak,        5.0,    100.0
af_electra_sparkler  = zone_witches_galantine,  2.0,     30.0
af_electra_flash     = zone_witches_galantine,  3.0,     50.0
af_electra_moonlight = zone_witches_galantine,  5.0,    100.0
mp_af_electra_flash  = zone_witches_galantine,  3.0,     50.0
af_electra_pepe      = zone_witches_galantine,  5.0,    100.0
af_ameba_slime       = zone_buzz,               3.0,     50.0
af_ameba_slug        = zone_buzz,               4.0,     75.0
af_ameba_mica        = zone_buzz,               5.0,    100.0
af_dummy_pellicle    = zone_death,              3.0,     75.0
af_fuzz_kolobok      = zone_no_gravity,         5.0,    150.0
af_rusty_thorn       = zone_radioactive_weak,   2.0,     30.0
af_rusty_sea-urchin  = zone_radioactive_weak,   3.0,     50.0
af_rusty_kristall    = zone_radioactive_weak,   4.0,    100.0
af_drops             = zone_zharka_static_weak, 3.0,     50.0
af_fireball          = zone_zharka_static_weak, 4.0,     75.0
af_dummy_battery     = zone_radioactive_weak,   4.0,    100.0
af_dummy_dummy       = zone_radioactive_weak,   4.0,    100.0
af_dummy_glassbeads  = zone_radioactive_weak,   4.0,    100.0
af_dummy_spring      = zone_radioactive_weak,   4.0,    100.0
  • make sure that each artefact has variable

    artefact_activation_seq

    - link to artefact activation section. Activation section describes all the states artefacts goes through before it activates (changes into anomaly). Each state has defined time, sound, light color, color range, particle and animation, for instance:
    • [af_electra_pepe]:af_base
...
artefact_activation_seq = af_activation_electra
...

  • in section

    af_base

    uncomment

    slot

    variable and set it to 1:
    • [af_base]:identity_immunities
hud             = artefact_hud
animation_slot  = 11
allow_inertion  = true
slot            = 1
...

Credits

Author: XiaNi


Artefacts on belt

To set how many artefacts can be attached to belt:
  • in

    config\system.ltx

    edit line:
    • max_belt = 5 ; how many artefacts can be attached to belt
  • in

    config\ui\inwentory_new.xml

    edit belt window:
    • <dragdrop_belt x="645" y="136" width="410" height="60"
    cell_width = "60" cell_height="60" rows_num="1" cols_num="5" />
x, y
window position (upper left corner)
width, height
window width and height
cell_width, cell_height
artefact cell width and height
rows_num
how many rows with artefacts
cols_num
how many artefacts (columns for artefacts)


Editing artefacts

Artefacts are defined in file

config\misc\artefacts.lx

. Each artefact contains variables:

cform

- collision for models; always:

skeleton

class

- for artefacts:

SCRPTART

visual

- path to artefact mesh (*.ogf)

description

- artefact description

inv_name

- artefact name

inv_name_short

- short (hud) artefact name

inv_weight

- artefact weight

inv_grid_x, inv_grid_y

- position of artefact icon

ui_icon_equipment.dds file

inv_grid_width, inv_grid_height

- width and height of artefact icon

ui_icon_equipment.dds file

cost

- artefact price

jump_height

- how high artefact can jump

particles

- artefact particle name

lights_enabled

- whether the light for artefact should be enabled

trail_light_color

- color (R, G, B) of artefact trail (when moving)

trail_light_range

- light range for artefact trail

artefact_activation_seq

- link to artefact activation section. Activation section describes all the states artefacts goes through before it activates (changes into anomaly). Each state has defined time, sound, light color, color range, particle and animation, for instance:

additional restoration includes:

  • health_restore_speed

  • radiation_restore_speed

  • satiety_restore_speed

  • power_restore_speed

  • bleeding_restore_speed

All the additional restorations are counted as follows:
negative value - takes away restoration speed
positive value - gives extra additional restoration

hit_absorbation_sect

- link to immunity section which includes:

  • burn_immunity

    - burn immunity

  • strike_immunity

    - strike immunity

  • shock_immunity

    - shock immunity

  • wound_immunity

    - wound immunity

  • radiation_immunity

    - radiation immunity

  • telepatic_immunity

    - telepatic immunity

  • chemical_burn_immunity

    - chemical immunity

  • explosion_immunity

    - explosion immunity

  • fire_wound_immunity

    - bulletproof cap immunity
All the immunity types are counted as follows:
- value 0.0 means +100%
- value 1.0 means 0% (normal, no extra immunity)
- value 2.0 means -100%
- values between 0.0 and 1.0 means +x extra immunity
- values between 1.0 and 2.0 means -x extra immunity

Adding new artefact

new visual
inventory

[af_electra_pepe]:af_base
GroupControlSection = spawn_group
$spawn              = "artifacts\electra"
$prefetch           = 64
cform               = skeleton
class               = SCRPTART
visual              = physics\anomaly\artefact_electra2.ogf

description         = "Very rare artifact. Attach it to your belt and you will be immortal"
inv_name            = "Electra"
inv_name_short      = "Electra"
inv_weight          = 0.5
inv_grid_x          = 12
inv_grid_y          = 9
cost                = 5000

jump_height         = .1
particles           = anomaly2\artefact\artefact_electra
lights_enabled      = true
trail_light_color   = 0.6,0.7,0.9
trail_light_range   = 1.0

artefact_activation_seq = af_activation_electra
hit_absorbation_sect    = af_electra_pepe_absorbation

health_restore_speed    = 0.0   ; no extra helth restore
radiation_restore_speed = 0.0   ; no extra radiation restore
satiety_restore_speed   = 0.0   ; no extra satiety restore
power_restore_speed     = 0.006 ; extra endurance restore
bleeding_restore_speed  = 0.0   ; no extra bleeding restore

[af_electra_pepe_absorbation]
burn_immunity          = 0.0 ; burn +100%
strike_immunity        = 0.0 ; impact +100%
shock_immunity         = 0.0 ; electric shock +100%
wound_immunity         = 0.0 ; rupture +100%
radiation_immunity     = 0.0 ; radiation +100%
telepatic_immunity     = 0.0 ; telepathy +100%
chemical_burn_immunity = 0.0 ; chemical burn +100%
explosion_immunity     = 0.0 ; explosion +100%
fire_wound_immunity    = 0.0 ; bulletproof cap +100%


Stashes

Stashes are described in file

config\misc\treasure_manager.ltx

. Each stash definition contains several informations, like:
target
story_id of stash (box spawned via all.spawn)
name
stash name (

config\text\eng\stable_treasure_manager.xml

)
description
stash description (

config\text\eng\stable_treasure_manager.xml

)
items
list of items to spawn, format: item_name, item_quantity
condlist
condition (infoportion, number, functions) which have to be met to spawn items in stash
community
defines npc's community which can gives us stash info
[esc_secret_bridge_vagon]
target = 5000
name = esc_secret_0000_name
description = esc_secret_0000_descr
items = ammo_5.45x39_ap,30,medkit_army,2
condlist = {=npc_rank(experienced) =actor_on_level(l01_escape)} 10
community = military


Editing traders

What items certain trader can buy/sell and how much the items costs is defined in ltx files. Each trader has its own ltx file:

  • config\misc\trade_barman.ltx

    - Barkeep

  • config\misc\trade_dolg.ltx

    - Petrenko

  • config\misc\trade_ecolog.ltx

    - Sakharov

  • config\misc\trade_freedom.ltx

    - Skinflint

  • config\misc\trade_trader.ltx

    - Sidorovitch

  • config\misc\trade_generic.ltx

    - for all npcs
Each trader file includes

trader

section which contains variables:

  • buy_condition

    - what the trader buys from you and how much he pays

  • sell_condition

    - how much will you pay for stuff

  • buy_supplies

    - items selling by trader
Because each variable has

condition_list

value, you can use infoportions and logic functions to define several sections:
- make sure that items from both sections match; for instance if you add item to section sell_condition but you won't add the same item to buy_supplies then the game may crash; that said, use the same conditions for each section
- always add some generic (basic) section in case all of the conditions fail
- make sure there are no duplicated items
[trader]
buy_condition  = ecolog_generic_buy
sell_condition = {+yan_kill_brain_done} ecolog_after_brain_sell, ecolog_start_sell
buy_supplies   = {+yan_kill_brain_done} supplies_after_brain, supplies_start
If your trader has several sections, then storing them in one file isn't convenient. You can split trader sections into several files:
; config\misc\trade_barman.ltx

#include "traders\barman_buy.ltx" ; contains section: barman_buy
#include "traders\barman_stock.ltx" ; contains sections: barman_stock_aes and barman_stock
#include "traders\barman_sell.ltx" ; contains sections: barman_sell_aes, barman_sell

[trader]
buy_condition	= barman_buy
sell_condition	= {+aes_arrive_to} barman_sell_aes, barman_sell
buy_supplies	= {+aes_arrive_to} barman_stock_aes, barman_stock

buy_condition

Describes what the trader buys from you and how much he pays:
[section_name]
item_name = number, number

; for instance:

[barman_buy]
af_medusa       = 0.7, 0.3
ammo_9x18_fmj   = 0.7, 0.1
wpn_pm          = 0.4, 0.1
wpn_addon_scope = 0.7, 0.2
wpn_walther_m1  = 1.0, 0.3
novice_outfit   = 0.8, 0.3
medkit_army     = 1.0, 0.5
To calculate how much trader will pay for certain item:
[section_name]
item_name = x, y
price = {[(x + y) / 2] * item_cost} * item_condition

where:

item_cost - is a value of variable cost in item section (config\misc\items.ltx if item section is defined there)
item_condition - current condition of item

For example:
[barman_buy]
medkit_army = 1.0, 0.5 ; cost = 220 (items.ltx)
x = 1.0, y = 0.5, item_cost = 220, item_condition = 1.0
price = {[(1.0 + 0.5) / 2] * 220} * 1.0
trader will pay for medkit 75% of base medkit price, i.e 165 RU
Item price may vary because of item_condition factor

sell_condition

Defines how much will you pay for stuff bought from trader. This section works exactly the same as

buy_condition

section, prices for items are calculated in the same way, using the formula defined before.

buy_supplies

Defines items which trader will sell. You can define how many items trader will have in his stock along with probability:
[section_name]
item_name = item_quantity, item_stock_probability

; for instance:

[barman_stock]
af_medusa       = 1, 0.3
ammo_9x18_fmj   = 5, 1.0
wpn_pm          = 3, 1.0
wpn_addon_scope = 2, 0.5
medkit_army     = 3, 1.0

Disabling items

You can disable certain items by simply listing them inside section. For instance:
[barman_stock]
af_medusa       = 1, 0.3
ammo_9x18_fmj   = 5, 1.0
wpn_pm
wpn_addon_scope
medkit_army     = 3, 1.0

[barman_buy]
af_medusa       = 0.7, 0.3
ammo_9x18_fmj   = 0.7, 0.1
wpn_pm
wpn_addon_scope
medkit_army     = 1.0, 0.5
thanks to that trader won't sell/buy items:

wpn_pm

and

wpn_addon_scope

For quest items, add to item section (ltx):
quest_item = true

Adding new treader

To add new trader:
  • prepare

    config\misc\trader_file.ltx

    with all needed sections (

    buy_condition, sell_condition, buy_supplies

    )
  • extend trader logic by adding path to trader file:
    • [logic]
active = walker
trade = misc\trader_file.ltx

[walker]
...
  • edit trader character profile (should be unique) to make sure he will have enough money:
    • <money min="100000" max="100000" infinitive="1"/>
  • open

    config\creatures\m_stalker.ltx

    file and scroll down to section

    stalker_trader

    and add

    max_item_mass

    to ensure that trader will be able to carry all the items you defined in section

    buy_supplies

    :
    • [stalker_trader]:stalker
$spawn               = "stalkers\stalker_trader"
use_single_item_rule = off
can_select_items     = off
max_item_mass        = 10000.0
inv_max_weight       = 10000.0
  • use

    section_name = stalker_trader

    in all.spawn for your trader
  • if you are going to edit stalkers supplies or you want to use

    section_name = stalker

    instead of

    section_name = stalker_trader

    then open

    config\creatures\m_stalker.ltx

    file and in section

    stalker

    :
    • ; change:
use_single_item_rule = on

; to:
use_single_item_rule = false


Editing weapons

Weapons are defined in ltx files included in folder

config\weapons\

. Each weapon contains variables:

cform

- collision for models; always:

skeleton

class

- weapon class

visual

- path to weapon mesh (*.ogf)

description

- weapon description

inv_name

- weapon name

inv_name_short

- short (hud) weapon name

inv_weight

- weapon weight

inv_grid_x, inv_grid_y

- position of weapon icon

ui_icon_equipment.dds file

inv_grid_width, inv_grid_height

- width and height of weapon icon

ui_icon_equipment.dds file

cost

- weapon price

weapon_class

- weapon's class:

  • sniper_rifle

  • assault_rifle

  • shotgun

  • heavy_weapon

  • pistols don't have a class!

fire_modes

- comma-separated list of fireing mode; there are 4 values (NOT FOR ALL WEAPONS):

  • -1

    - full auto

  • 1

    - single shot

  • 2

    - double burst

  • 3

    - triple burst
weapon addons:

  • scope_status

  • silencer_status

  • grenade_launcher_status

each addon can have one of below values:

  • 0

    - no addon

  • 1

    - permanent

  • 2

    - attachable

zoom_enabled

- whether the zoom is enabled

zoom_hide_crosshair

- whether hide the crosshair in zoom

scope_zoom_factor

- zoom factor when you are using scope; lower values - you can see further.

scope_texture

- you can assign different textures

ammo_class

- list of ammo classes which can be used by weapon

ammo_mag_size

- clip (magazine) size

condition_shot_dec

- how much weapon condition is lost per round fired

misfire_probability

- chance that your weapon will jam

sprint_allowed

- whether you can sprint with weapon (

true

) or not (

false

)

rpm

- how many rounds per minute your weapon can fire

hit_power

- weapon damages; the higher the numbers the more damage

hit_impulse

- amount of impact the bullet has

hit_type

- type of the wound

fire_distance

- maximum fire distance that bullet can fly

bullet_speed

- how fast is the bullet

slot

- weapon slot:

  • 1

    - primary slot (pistols slot)

  • 2

    - secondary slot (assault rifles slot)

ammo_limit

- obsolete

ammo_elapsed

- obsolete
weapon sounds (

sounds\weapons\

):

  • snd_draw

    - sound when you grab weapon from holster

  • snd_holster

    - sound when hiding weapon to holster

  • snd_shoot

    - standard shot sound

  • snd_shoot1

    - shot sound 1

  • snd_shoot2

    - shot sound 2

  • snd_shoot3

    - shot sound 3

  • snd_empty

    - empty clip sound

  • snd_reload

    - reload sound

  • snd_shoot_grenade

    - grenade shot sound

  • snd_reload_grenade

    - loading grenade sound
If you created new weapon, or weapon inherited from some basic weapon type, then don't forget to include it in config\mp\mp_ranks.ltx


Creating factions

To create a new faction called

wind

:
  1. in

    game_relations.ltx

    add to section

    communities_relations

    relations with other factions
  2. in

    game_relations.ltx

    edit field

    communities

    :
    3. communities = actor, 0, ..., wind, 18
  3. in

    game_relations.ltx

    edit section

    communities_sympathy

    :
    4. [communities_sympathy]
...
wind = 0.0
  4. edit

    death_manager.script

    :
    5. function init_drop_settings()
    local community_list =
    {
        "stalker", "dolg", "freedom",
        "bandit", "military", "zombied",
        "ecolog", "killer", "monolith",
        "arena_enemy", "actor_dolg", "wind"
    }
    ...
end
  5. edit

    death_items_by_communities.ltx

    and add

    wind

    section with items
  6. edit

    xr_statistic.script

    :
    7. local killCountProps =
{
    ...
    wind_novice = 1, wind_experienced = 2, wind_veteran = 3, wind_master = 4
    ...
}

function getNpcType(npc)
    if IsStalker(npc) then
        if npc:character_community() == "wind" then
            community = "wind"
        else
            ...
        end
    end
end
  7. create several character profiles for

    wind

    faction and set proper community:
    8. ...
<community>wind</community>
...
  8. add help dialogs to xml and ltx files
    9. ; config\misc\dialog_manager.ltx

[list]
...
dm_intro_help_15
...

[dm_intro_help_15]
wounded = true
npc_community = wind
category = intro

    <!-- config\text\eng\stable_dialog_manager.xml -->

<string id="dm_intro_help_15">
    <text>Help friend!</text>
</string>
  9. edit

    string_table_general.xml

    10. <string_table>
    ...
    <string id="wind">
        <text>Wind</text>
    </string>
    ...
<string_table>
  10. you may add stashes for

    wind

    community
    11. ; config\misc\treasure_manager.ltx

[esc_secret_stalker_things]
target = 5008
name = esc_secret_0008_name
description = esc_secret_0008_descr
items = af_rusty_thorn
condlist = {=actor_on_level(l01_escape)} 10
community = wind

Credits

Author: BAC9-FLCL, Arhet, SenseyVit


Custom scripted postprocess

-- custom scripted ppe

local scripted_ppe = pp_eff.scr_pp("fallout_light")
scripted_ppe:start()
scripted_ppe:stop()

-- ******************************************************** --

-- ppe from file

local ppe_from_file = pp_eff.file_pp("yantar_underground_psi.ppe")
ppe_from_file:start()
ppe_from_file:set_factor(5.0)
ppe_from_file:stop()

Implementation

Source here
Tool for creating new custom ppe


Online vs Offline objects

In order to manipulate game objects, there are two types of objects: offline and online. The object is always offline (server object), it appears online (level, client object) when distance between actor and object is less than alife-zone radius set in alife.ltx file. It turns out that changes done in online object are not immediately updated for offline object and vice versa.

Offline (server) objects

Offline objects are represented by classes which depends on object type, for instance

cse_alife_human_stalker

for stalkers. Scripts

se_*.script

controls offline objects (se stands for server entity). To get server object you can use function

alife():object(id_or_name)

if you know object's name or id, or you can iterate throught level objects:
function iterate_level_objects()
  -- actor_id = 0; invalid_id = 65535
  for id = 1, 65534 do
    obj = alife():object(id)
    if obj then
      -- obj is valid offline (server) object
    end
  end
end

Online (client, level) objects

Online objects are represented as

game_object

class. To get online object you can use function

level.object_by_id(object_id)

which returns

game_object

if you know object's id, or you can iterate through level objects, like this:
function iterate_level_objects()
  -- actor_id = 0; invalid_id = 65535
  for id = 1, 65534 do
    obj = level.object_by_id(id)
    if obj then
      -- obj is valid online (level) object
    end
  end
end

Comparison

 | offline object | online object
instance of | alife():object(id_or_name) | level.object_by_id(id)
id | obj.id | obj:id()
name | obj:name() | obj:name()
section | obj:section_name() | obj:section()
position | obj.position | obj:position()
level vertex id | obj.m_level_vertex_id | obj:level_vertex_id()
game vertex id | obj.m_game_vertex_id | obj:game_vertex_id()
parent id | obj.parent_id | p = obj:parent() if p then return p:id() end


Adding diaries to pda

To add new diaries to pda and receive them when looting npc:
  • function which will notify us that we got someone's diary:
    • -- scripts\news_manager.script

function send_pda_info(npc)
  local task_texture, task_rect = get_texture_info("ui_iconsTotal_tutorial_help_wounded")
  local news_text = "%c[255,0,0,255]Found PDA: ".."\\n"..npc:character_name()

  db.actor:give_game_news(news_text, task_texture, task_rect, 0, 4000)
  pda_news:play(db.actor, 0, sound_object.s2d)
end
  • function which will give us a pda's diary:
    • -- scripts\treasure_manager.script

local npcs_with_diary =
{
  wolf = "encyclopedy_pda_diary_wolf",
  tolik = "encyclopedy_pda_diary_tolik",
  petruha = "encyclopedy_pda_diary_petruha",
  nimble = "encyclopedy_pda_diary_nimble",
  skull = "encyclopedy_pda_diary_skull"
}

function CTreasure:use(npc)
  printf("TREASURE USE")

  -- EDIT START
  local diary = npcs_with_diary[string.lower(npc:character_name())]
  if diary ~= nil and not has_alife_info(diary) then
    db.actor:give_info_portion(diary)
    news_manager.send_pda_info(npc)
  end
  -- EDIT END

  local avail = {}
  -- rest of use() function without changes
  ...
end
  • needed infoportions:
    • <!-- config\gameplay\info_found_pdas.xml -->

<?xml version='1.0' encoding="windows-1251"?>

<game_information_portions>
  <article>pda_diary_wolf</article>
  <article>pda_diary_tolik</article>
  <article>pda_diary_petruha</article>
  <article>pda_diary_nimble</article>
  <article>pda_diary_skull</article>

  <info_portion id="encyclopedy_pda_diary_wolf">
    <article>pda_diary_wolf</article>
  </info_portion>

  <info_portion id="encyclopedy_pda_diary_tolik">
    <article>pda_diary_tolik</article>
  </info_portion>

  <info_portion id="encyclopedy_pda_diary_petruha">
    <article>pda_diary_petruha</article>
  </info_portion>

  <info_portion id="encyclopedy_pda_diary_nimble">
    <article>pda_diary_nimble</article>
  </info_portion>

  <info_portion id="encyclopedy_pda_diary_skull">
    <article>pda_diary_skull</article>
  </info_portion>
</game_information_portions>
  • articles links:
    • <!-- config\gameplay\info_found_pdas.xml -->

<?xml version='1.0' encoding="UTF-8"?>
<encyclopedia>

  <article id="pda_diary_wolf" name="Wolf's Diary" article_type="Journal"
        group="Found Diaries">
    <texture x="1664" y="383" width="128" height="128">ui\ui_icons_npc</texture>
    <text>enc_pda_diary_wolf</text>
  </article>

  <article id="pda_diary_tolik" name="Tolik's Diary" article_type="Journal"
        group="Found Diaries">
    <texture x="511" y="1" width="128" height="128">ui\ui_icons_npc</texture>
    <text>enc_pda_diary_tolik</text>
  </article>

  <article id="pda_diary_petruha" name="Petruha's Diary" article_type="Journal"
        group="Found PDAs">
    <texture x="1407" y="128" width="128" height="128">ui\ui_icons_npc</texture>
    <text>enc_pda_diary_petruha</text>
  </article>

  <article id="pda_diary_nimble" name="Nimble's Diary" article_type="Journal"
        group="Found PDAs">
    <texture x="1920" y="128" width="128" height="128">ui\ui_icons_npc</texture>
    <text>enc_pda_diary_nimble</text>
  </article>

  <article id="pda_diary_skull" name="Skull's Diary" article_type="Journal"
        group="Found PDAs">
    <texture x="128" y="128" width="128" height="128">ui\ui_icons_npc</texture>
    <text>enc_pda_diary_skull</text>
  </article>

</encyclopedia>
  • diaries texts:
    • <!-- config\text\eng\string_table_enc_found_pdas.xml -->

<?xml version="1.0" encoding="windows-1251" ?>
<string_table>

  <string id="enc_pda_diary_wolf">
    <text>One Duty guy asked me for dynamit. Hehe i wonder what he want to blow.
        Anyway the guy, called Skull seems to be in Army Warehouses. Damn it's so far
        away from me, but he said that he will pay me well. So i will wait untill
        Fanatic arive and then i will go to Army Warehouses</text>
  </string>

  <string id="enc_pda_diary_tolik">
    <text>We were comming back from a scouting raid. Came across some gangsters.
        One of our guys got killed right there. They grabbed Nimble 'couse he made up
        nice story about a cache full of artifacts. I played dead and that saved
        my ass. I feel shame ...</text>
  </string>

  <string id="enc_pda_diary_petruha">
    <text>Another shitty day in the Zone. Otter died, i don't know where
        is Tolik and now bandits captured Nimble.</text>
  </string>

  <string id="enc_pda_diary_nimble">
    <text>bla bla bla ...</text>
  </string>

  <string id="enc_pda_diary_skull">
    <text>Damn, how can i kill this sniper?! I can't attack Freedomers
        and since we disobeyed Voronin's orders we can't come back to the
        Bar\n\nToday i woke up with a great idea! I will blow out the wall
        with dynamit. Now i have to wait for Wolf. He said that he have explosives.</text>
  </string>

</string_table>
  • we've created 3 completely new files, so we have to "tell" engine where he should find them:
    • ; config\system.ltx

[info_portions]
files = (...), info_found_pdas

[encyclopedia]
files = (...), encyclopedia_found_pdas

; ---------------------------------------------
; ---------------------------------------------

; config\localization.ltx

[string_table]
language    = eng
font_prefix = ;_cent ;_west

files = (...), string_table_enc_found_pdas
Wolf's diary
Tolik's diary
Petruha's PDA
Nimble's PDA

Method described above can be used to add some random diaries for not unique npcs (only when they have pda object):
local npcs_with_diary =
{
  wolf = "encyclopedy_pda_diary_wolf",
  tolik = "encyclopedy_pda_diary_tolik",
  petruha = "encyclopedy_pda_diary_petruha",
  nimble = "encyclopedy_pda_diary_nimble",
  skull = "encyclopedy_pda_diary_skull"
}

local random_diaries =
{
  "encyclopedy_pda_diary_rnd1", "encyclopedy_pda_diary_rnd2",
  "encyclopedy_pda_diary_rnd3", "encyclopedy_pda_diary_rnd4",
  "encyclopedy_pda_diary_rnd5", "encyclopedy_pda_diary_rnd6",
}

function CTreasure:use(npc)
  local unique_npc_diary = npcs_with_diary[string.lower(npc:character_name())]
  local random_npc_diary = random_diaries[math.random(1, #random_diaries)]

  -- random npc
  if unique_npc_diary == nil then
    if npc:object("device_pda") ~= nil and not has_alife_info(random_npc_diary) then
      db.actor:give_info_portion(random_npc_diary)
      news_manager.send_pda_info(npc)
    end

  -- unique npc
  elseif not has_alife_info(unique_npc_diary) then
    if npc:object("device_pda") == nil then
      alife():create("device_pda", npc:position(), npc:level_vertex_id(), npc:game_vertex_id(), npc:id())
    end
    db.actor:give_info_portion(unique_npc_diary)
    news_manager.send_pda_info(npc)
  end

  -- rest of use() function without changes
  ...
end
Random diaries


NPC on PDA's map

To mark npcs on PDA's map in a similar way as it is in Clear Sky:

1. Define mapspots types for each faction (in vanilla there are 9 factions so you will need 9 mapspots) in

config\ui\mapspots.xml

<faction_stalker>
  <level_map spot="faction_stalker_mapspot" pointer="quest_pointer" />
  <mini_map spot="faction_stalker_mapspot" pointer="quest_pointer" />
</faction_stalker>

<faction_stalker_mapspot width="5" height="5" stretch="1" alignment="c" xform_anim="map_spot_rel_xform" xform_anim_cyclic="0">
  <texture r="255" g="200" b="0">ui_minimap_point</texture> <!-- orange or yellow dot -->
</faction_stalker_mapspot>
Now,

level_map_spot

is for pda map, while

mini_map_spot

is for mini map (on hud). You can define different colors for mapspots by blending colors using RGB values in texture tag, for instance for mercenaries faction we can use blue dot:
<faction_killer>
  <level_map spot="faction_killer_mapspot" pointer="quest_pointer" />
  <mini_map spot="faction_killer_mapspot" pointer="quest_pointer" />
</faction_killer>

<faction_killer_mapspot width="5" height="5" stretch="1" alignment="c" xform_anim="map_spot_rel_xform" xform_anim_cyclic="0">
  <texture r="0" g="0" b="255">ui_minimap_point</texture> <!-- blue dot -->
</faction_killer_mapspot>
2. If you added all needed mapspots types, then you can extend

xr_motivator.script

(npc's binder) to display mapspots. First we define an array with mapspots for all factions:
local mapspots_by_community =
{
  stalker = "faction_stalker",
  bandit = "faction_bandit",
  killer = "faction_killer",
  ecolog = "faction_ecolog",
  military = "faction_military",
  dolg = "faction_dolg",
  freedom = "faction_freedom",
  zombied = "faction_zombied",
  monolith = "faction_monolith"
}
Now extend binder class by adding method to add/remove mapspots:
function motivator_binder:handle_mapspot(action)
   if action ~= "add" and action ~= "remove" then
      return
   end

   local hint = self.object:character_community()
   local mapspot = mapspots_by_community[hint]

   if not mapspot then
      return
   end

   if action == "add" then
      if level.map_has_object_spot(self.object:id(), mapspot) ~= 0 then
         return -- mapspot already exist
      else
         level.map_add_object_spot(self.object:id(), mapspot, hint)
      end
   elseif action == "remove" then
      for  index = 1, level.map_has_object_spot(self.object:id(), mapspot) do
         level.map_remove_object_spot(self.object:id(), mapspot)
      end
   end
end
To add mapspot we will extend method net_spawn():
function motivator_binder:net_spawn(sobject)
   ... -- origi code from this method
   self:handle_mapspot("add") -- add mapspot
   return true
end
3. Finally, we should remove mapspots when game is beeing destroyed or when npc is dead:
function motivator_binder:net_destroy()
   ... -- origi code from this method
   self:handle_mapspot("remove") -- remove mapspot when game is beeing  destroyed
   object_binder.net_destroy(self)
end

function motivator_binder:death_callback(victim, who)
   ... -- origi code from this method
   self:handle_mapspot("remove") -- remove mapspot when stalker  died
end


Scripted timer

There are situations when you would like to perform an action, when certain amount of time expires. For instance, say you are working on a task in which you have to occupy certain position for a period of 15 minutes, or you want to spawn more npcs/mutants after each 24 hours, or you want to implement a blowout script which has 3 phases - each phase remain different amount of time. To do this in a convenient way, you can implement a timer. In this article I would like to show you a simple timer class. It defines two different types of timer, both timers counts backward starting from a given value (expiration_time):
  • silent timer - its main purpose is to perform previously defined action (when the timer expire)
  • counterclockwise timer - same as silent timer, but with text presentation on hud (useful for debugging purposes)

Useful variables

First let's define some variables with default values, i.e. time after the timer expires (def_expiration_time), type of timer (def_timer_type) and function which will be called when timer expires (def_action):
local def_expiration_time = {h = 0, m = 5, s = 0} -- default time - 5 minutes
local def_timer_type = "counterclockwise" -- default type of timer
local def_action = "no_action" -- default function called when timer expires

Timer class constructor

Class constructor is called when you create a new timer object. With arguments passed to constructor we can choose timer type, timer expiration time and action. Variable timer_expired indicates whether the timer expired or not. Function time_global() returns a number of seconds passed since you start the game. Function script_name() returns name of the current script (without extension), so if you named your script my_timer.script then function script_name() returns my_timer. Variable action holds name (string) of the function which will be called when the timer expired. There are two ways you can call functions in Stalker:
  • using name of the script where the function you want to call is defined, like this:
    • my_script_name.my_function_name(arguments)
  • or you can use global variable (array) _G
    • _G["my_script_name"]["my_function_name"](arguments)
We will define the action function in the same script as timer class, so we will use script_name() function, but of course you can use different script for action functions only. Variable timer_wnd holds a pointer to hud window where you can see the text presentation of counterclockwise timer (upper right corner). We will use the window called hud_timer_text, defined in config\ui\ui_custom_msgs.xml (you can define your own "window" there if you want, for example change the font of the timer text etc)
class "Timer"

function Timer:__init(timer_type, expiration_time, action)
    if timer_type ~= "counterclockwise" and
        timer_type ~= "silent"
    then
        timer_type = def_timer_type
    end

    self.timer_type = timer_type
    self.expiration_time = self:normalize_expiration_time(expiration_time)
    self.activation_time = time_global()
    self.timer_expired = false
    self.timer_wnd = nil

    if not action or not _G[script_name()][action] then
        self.action = def_action
    else
        self.action = action
    end

    if self.timer_type ~= "silent" then
        get_hud():AddCustomStatic("hud_timer_text", true)
        self.timer_wnd = get_hud():GetCustomStatic("hud_timer_text"):wnd()
    end
end
For variable expiration_time we will use a string data type, so we can pass the time in a convinient way. Here is the format we will use: hours:minutes:seconds, for instance:
expiration_time = "15:34:34"
Function normalize_expiration_time() parse the expiration_time we passed as a constructor argument and returns the number of seconds. We will use 1000 multiplier and ... math backgrounds ;)
  • 1 minute = 60 seconds
  • 1 hour = 60 minutes
  • 1 hour = 60 * 60 = 3600 seconds
so to convert expiration_time to seconds (because function time_global() is using seconds) we will use this formula:
local multiplier = 1000

local hours = expiration_time_hours * 60 * 60 * multiplier
local minutes = expiration_time_minutes * 60 * multiplier
local seconds = expiration_time_seconds * multiplier

-- for instance:
local expiration_time = "10:15:02"

local hours = 10 * 60 * 60 * 1000 -- 36 000 000 seconds
local minutes = 15 * 60 * 1000 -- 900 000 seconds
local seconds = 2 * 1000 -- 2000 seconds

local total_num_of_secs = hours + minutes + seconds -- 36 902 000 seconds

function Timer:normalize_expiration_time(exp_time)
    if exp_time == nil then
        exp_time = ""
    end

    local t = {}
    for word in string.gmatch(exp_time, "[%d]+") do
        table.insert(t, word)
    end

    local hours = tonumber(t[1] or def_expiration_time.h) * 3600000
    local minutes = tonumber(t[2] or def_expiration_time.m) * 60000
    local seconds = tonumber(t[3] or def_expiration_time.s) * 1000

    return hours + minutes + seconds
end

Destructor

Destructor is used to release any resources allocated by the object. Since our timer does not consume much resources, we can use an empty destructor:
function Timer:__finalize()
end
or if you want, you can help a bit lua interpreter releasing the variables on your own:
function Timer:__finalize()
    self.timer_type = nil
    self.activation_time = nil
    self.expiration_time = nil
    self.timer_wnd = nil
    self.action = nil
    self.timer_expired = nil
end

Update method (the core of our timer)

Update method should be instantly called when we create our timer object. Its main purpose is to count the time and if expiration_time expire - perform an action (call a function with certain action)
function Timer:Update()
    if self.timer_expired then
        return
    end

    local new_time = self.expiration_time - (time_global() - self.activation_time)
    if new_time <= 0 then
        self.timer_expired = true
        new_time = 0
    end

    -- if we are using the counterclockwise timer
    if self.timer_type ~= "silent" then
        -- convert seconds to: hours, minutes and seconds
        local hours = math.floor(new_time / 3600000)
        local minutes = math.floor(new_time / 60000 - hours * 60)
        local seconds = math.floor(new_time / 1000 - hours * 3600 - minutes * 60)

        -- update hud window (6 digit display)
        self.timer_wnd:SetTextST(self:TimeToString(hours) .. ":" ..
            self:TimeToString(minutes) .. ":" .. self:TimeToString(seconds))
    end

    if self.timer_expired then
        -- timer expired:
        self:Expired()
    end
end

function Timer:TimeToString(t)
    if t >= 10 then
        return tostring(t)
    else
        return "0" .. tostring(t)
    end
end

When the time expires

We perform the action using special function:
function Timer:Expired()
    -- if we are using counterclockwise timer then remove hud display
    if self.timer_type ~= "silent" then
        get_hud():RemoveCustomStatic("hud_timer_text")
    end

    -- perform action
    _G[script_name()][self.action]()
end

Creating timer object and using its methods

Let's create a local variable to hold our timer object. Function get_timer() will be our "handle" to timer object.
local pTimer = nil

function get_timer(timer_type, expiration_time, action)
    if pTimer == nil then
        -- create timer object
        pTimer = Timer(timer_type, expiration_time, action)
    end
    return pTimer
end
If you want to use more then one timer at the same time - create an array of objects (timers):
local my_timers = {}

local timer_type, expiration_time = nil, nil
local h, m, s = nil, nil, nil

-- create 10 timers
for index = 1, 10 do
    if index % 2 == 0 then
        -- 5x silent timers
        timer_type = "silent"
    else
        -- 5x counterclockwise timers
        timer_type = "counterclockwise"
    end

    -- just an exaple:
    h = tostring(math.random(100) % 24)
    m = tostring(math.random(100) % 60)
    s = tostring(math.random(100) % 60)
    expiration_time = h .. ":" .. m .. ":" .. s

    table.insert(my_timers, Timer(timer_type, expiration_time, "no_action"))
end

-- and let's call an update method for each created timer
for index, timer in pairs(my_timers) do
    timer:Update()
end

Action when timer expires

As a default action we used no_action function. Now it's time to define it, we will do that in the same script where we implemented our timer class:
function no_action()
    -- default action
end
Obviously, the empty action function is not very useful, however it is only a default function, in case we forgot to pass an action function to timer constructor. What action function should contain depends on the purpose of timer. For instance:
-- in the same script where timer class is defined, add:
local spawn_vectors =
{
    {pos = vector():set(1, 2, 3), lvid = 123, gvid = 456},
    {pos = vector():set(4, 5, 6), lvid = 789, gvid = 1011},
    {pos = vector():set(7, 8, 9), lvid = 1213, gvid = 1415},
    {pos = vector():set(8, 7, 6), lvid = 1617, gvid = 1819},
    {pos = vector():set(5, 4, 3), lvid = 2021, gvid = 2223},
}

-- action for our timer
function spawn_mutants()
    local spawn_data = nil
    for index = 1, 5 do
        spawn_data = spawn_vectors[math.random(1, #spawn_vectors)]
        alife():create("bloodsucker_strong", spawn_data.pos, spawn_data.lvid, spawn_data.gvid)

        spawn_data = spawn_vectors[math.random(1, #spawn_vectors)]
        alife():create("tushkano_normal", spawn_data.pos, spawn_data.lvid, spawn_data.gvid)
    end
end

-- create and update timer, for example in bind_stalker.script:
local my_timer = nil

function actor_binder:update(delta)
    ...
    if my_timer == nil then
        -- timer class is defined in timer.script
        my_timer = timer.Timer("silent", "0:30:0", "spawn_mutants")
    else
        -- update timer, if it expires then spawn_mutants() function
        -- will be called and after that, my_timer will be suspended
        my_timer:Update()
    end
    ...
end
As you can see the timer will be suspended when the expiration_time expires and then the action function will be called. We can also add a method to resume our timer:
function Timer:isSuspended()
    return self.timer_expired
end

function Timer:Resume()
    if self.timer_type ~= "silent" then
        get_hud():AddCustomStatic("hud_timer_text", true)
        self.timer_wnd = get_hud():GetCustomStatic("hud_timer_text"):wnd()
    end
    self.activation_time = time_global()
    self.timer_expired = false
end
and resume the timer when it expired:
function actor_binder:update(delta)
    ...
    if my_timer == nil then
        my_timer = timer.Timer("silent", "0:30:0", "spawn_mutants")
    elseif my_timer:isSuspended() then
        my_timer:Resume()
    else
        my_timer:Update()
    end
    ...
end
We can also stop the timer (suspend it) if the certain conditions are meet
function Timer:Suspend()
    self.timer_expired = true
end
for instance:
function actor_binder:update(delta)
    ...
    if my_timer == nil then
        my_timer = timer.Timer("silent", "0:30:0", "spawn_mutants")
    else
        if not xr_conditions.is_day() then
            if my_timer:isSuspended() then
                my_timer:Resume()
            end
            -- update only during nights
            my_timer:Update()
        elseif not my_time:isSuspended() then
            -- suspend if not suspended yet
            my_timer:Suspend()
        end
    end
    ...
end


Inheritance

Lua and OOP

Lua is not an object oriented language, it doesn't provide class system. However you can simulate classes in Lua using metatables. Luckily for us developers already enabled class system so we can use classes in our scripts. In this article I would like to show you how to use inheritance. I assume you are familiar with Lua and object oriented programming. Let's start by defining some useful functions for debuging purposes:
function ToString(arg)
    local t = type(arg)
    if t == "string" then
        return arg
    elseif t == "number" or t == "boolean" then
        return tostring(arg)
    elseif t == "nil" then
        return "<nil>"
    elseif t == "table" then
        return "<table>"
    else
        return "<user_data>"
    end
end

function debug_print(...)
    local args = {...}
    if args[1] ~= nil then
        local con, str = get_console(), ""
        for k, v in pairs(args) do
            str = str .. ToString(v) .. " "
            if string.len(str) >= 190 then
                con:execute("load *** " .. str)
                str = ""
            end
        end
        if str ~= "" then
            con:execute("load *** " .. str)
        end
    end
end

Base class

class "BaseClass"

function BaseClass:__init(a, b)
   self.a = a
   self.b = b
   debug_print("BaseClass init")
end

function BaseClass:__finalize()
   self.a = nil
   self.b = nil
   debug_print("BaseClass finalize")
end

function BaseClass:Print()
   debug_print("BaseClass a =", self.a)
   debug_print("BaseClass b =", self.b)
end
As you can see our BaseClass has two variables (fields) and one method (function). Now let's create a new object and call function Print:
local bc = BaseClass(5, 20)
-- output: BaseClass init

bc:Print()
-- output: BaseClass a = 5
-- output: BaseClass b = 20

bc = nil
-- output: BaseClass finalize
First we created a new object of BaseClass, the constructor of that class was called, then we called function Print and we destroyed our object by assigning a nil value to variable bc thus the destructor of BaseClass was called. Now let's create a new class which derives from BaseClass.

Derived class

class "DerivedClass" (BaseClass)

function DerivedClass:__init(a, b, c) super(a, b)
   self.c = c
   debug_print("DerivedClass init")
end

function DerivedClass:__finalize()
   self.c = nil
   debug_print("DerivedClass finalize")
end

function DerivedClass:Print()
   BaseClass.Print(self)
   debug_print("DerivedClass c =", self.c)
end
Right after the name of our new class, we have to write (in parentheses) name of the base class we are deriving from (BaseClass in this case). In the constructor definition we call a constructor of the base class. Inside function Print we call function Print from the base class. As an argument we have to provide a "pointer" to derived class (self). If we create a new object from derived class then the constructor of the base class will be called first and then constructor of the derived class. When the object of derived class is destroyed then its destructor is called first and then destructor of the base class (if there are no more objects derived from that class).
local dc = DerivedClass(5, 20, 100)
-- output: BaseClass init
-- output: DerivedClass init

dc:Print()
-- output: BaseClass a = 5
-- output: BaseClass b = 20
-- output: DerivedClass c = 100

dc = nil
-- output: DerivedClass finalize
-- output: BaseClass finalize
When a class is inherited then all the functions and variables are inherited:
function DerivedClass:Print()
   --BaseClass.Print(self)
   debug_print("DerivedClass a =", self.a)
   debug_print("DerivedClass b =", self.b)
   debug_print("DerivedClass c =", self.c)
end

local dc = DerivedClass(5, 20, 100)
-- output: BaseClass init
-- output: DerivedClass init

dc:Print()
-- output: DerivedClass a = 5
-- output: DerivedClass b = 20
-- output: DerivedClass c = 100

dc = nil
-- output: DerivedClass finalize
-- output: BaseClass finalize

Practical example

This example is based on Scripted timer article. This time the Timer class defines only silent timer. HudTimer class derives from Timer class and defines the timer with text displayed on hud.

Timer class

local def_expiration_time = {h = 0, m = 5, s = 0}

class "Timer"

function Timer:__init(expiration_time, action)
    self.expiration_time = self:normalize_expiration_time(expiration_time)
    self.activation_time = time_global()
    self.current_time = nil
    self.scriptName, self.functionName = this.get_action(action)
    self.timer_expired = false
end

function Timer:__finalize()
    self.scriptName = nil
    self.functionName = nil
    self.timer_expired = nil
    self.current_time = nil
    self.activation_time = nil
    self.expiration_time = nil
end

function Timer:Update()
    if self.timer_expired then
        return
    end

    self.current_time = self.expiration_time - (time_global() - self.activation_time)
    if self.current_time <= 0 then
        self.current_time = 0
        self:Expired()
    end
end

function Timer:Expired()
    self.timer_expired = true
    _G[self.scriptName][self.functionName]()
end

function Timer:isSuspended()
    return self.timer_expired
end

function Timer:Suspend()
    self.timer_expired = true
end

function Timer:Resume()
    self.activation_time = time_global()
    self.timer_expired = false
end

function Timer:Resume(expiration_time)
    self.expiration_time = self:normalize_expiration_time(expiration_time)
    self.activation_time = time_global()
    self.timer_expired = false
end

function Timer:normalize_expiration_time(exp_time)
    if exp_time == nil then
        exp_time = ""
    end

    local t = {}
    for word in string.gmatch(exp_time, "[%d]+") do
        table.insert(t, word)
    end

    local hours = tonumber(t[1] or def_expiration_time.h) * 3600000
    local minutes = tonumber(t[2] or def_expiration_time.m) * 60000
    local seconds = tonumber(t[3] or def_expiration_time.s) * 1000

    return hours + minutes + seconds
end

function no_action()
end

function get_action(str)
    if not str then
        return script_name(), "no_action"
    end

    local scriptName, functionName = nil, nil
    local dot = string.find(str, "%.")

    if dot then
        scriptName = string.sub(str, 0, dot - 1)
        functionName = string.sub(str, dot + 1)
    end

    if not scriptName then
        scriptName = script_name()
    end

    if not functionName then
        functionName = "no_action"
    end

    if not _G[scriptName] then
        scriptName = script_name()
    end

    if not _G[scriptName][functionName] then
        return scriptName, "no_action"
    end

    return scriptName, functionName
end

HudTimer class

HudTimer class only display the text on the hud while base class (Timer) is responsible for counting time. However Timer class can be used separately for silent timers. As for the display you can use hud_timer or hud_timer_text:

hud_timer
hud_timer_text

local def_display = "hud_timer" -- "hud_timer_text"

class "HudTimer" (Timer)

function HudTimer:__init(expiration_time, action, display_type) super(expiration_time, action)
    if not display_type then
        self.display_type = def_display
    else
        self.display_type = display_type
    end

    get_hud():AddCustomStatic(self.display_type, true)
    self.timer_wnd = get_hud():GetCustomStatic(self.display_type):wnd()
end

function HudTimer:__finalize()
    self.display_type = nil
    self.timer_wnd = nil
end

function HudTimer:Update()
    Timer.Update(self)

    local hours = math.floor(self.current_time / 3600000)
    local minutes = math.floor(self.current_time / 60000 - hours * 60)
    local seconds = math.floor(self.current_time / 1000 - hours * 3600 - minutes * 60)
    self.timer_wnd:SetTextST(self:TimeToString(hours) .. ":" .. self:TimeToString(minutes) .. ":" .. self:TimeToString(seconds))

    if self.timer_expired then
        get_hud():RemoveCustomStatic(self.display_type)
    end
end

function HudTimer:isSuspended()
    return self.timer_expired
end

function HudTimer:Suspend()
    self.timer_expired = true
end

function HudTimer:Resume()
    Timer.Resume(self)
    get_hud():AddCustomStatic(self.display_type, true)
    self.timer_wnd = get_hud():GetCustomStatic(self.display_type):wnd()
end

function HudTimer:Resume(expiration_time)
    Timer.Resume(self, expiration_time)
    get_hud():AddCustomStatic(self.display_type, true)
    self.timer_wnd = get_hud():GetCustomStatic(self.display_type):wnd()
end

function HudTimer:TimeToString(t)
    if t >= 10 then
        return tostring(t)
    else
        return "0" .. tostring(t)
    end
end


Items removal

Removing items like corpses and weapons can improve performance a bit but also prevents situations when there are a lot of items lying on a ground which looks odd. To remove corpses:
  • open

    config\creatures\stalkers.ltx

    file and add:
    • [stalkers_common]
; value of those variables describes how
; long (game hours) corpses will stay on map

corpse_remove_game_time_interval = 3
stay_after_death_time_interval   = 3
  • open

    config\creatures\monsters.ltx

    file and add:
    • [monsters_common]
; value of those variables describes how
; long (game hours) corpses will stay on map

corpse_remove_game_time_interval = 1
stay_after_death_time_interval   = 1
However we can remove corpses and other items using script. First, let's make some assumptions:
remove weapons only if:
  • actor is at least 70 meters away from weapon
  • weapon doesn't have parent - in case someone picked the weapon up
  • weapon wasn't spawned via all.spawn
  • eventually: if weapon doesn't have story_id
remove corpses only if:
  • actor is at least 70 meters away from weapon
  • actor looted corpse (only for npcs)
  • corpse doesn't have any special items, possibly storyline related (only for npcs)
  • corpse doesn't have story_id
  • corpse doesn't have any game infos (infoportions, articles, etc.)
Let's start by defining two arrays with objects class ids. We will use them to recognise item type:
local wpn_clsids =
{
  [clsid.wpn_vintorez_s] = true,
  [clsid.wpn_ak74_s] = true,
  [clsid.wpn_lr300_s] = true,
  [clsid.wpn_hpsa_s] = true,
  [clsid.wpn_pm_s] = true,
  [clsid.wpn_shotgun_s] = true,
  [clsid.wpn_bm16_s] = true,
  [clsid.wpn_svd_s] = true,
  [clsid.wpn_svu_s] = true,
  [clsid.wpn_rpg7_s] = true,
  [clsid.wpn_val_s] = true,
  [clsid.wpn_walther_s] = true,
  [clsid.wpn_usp45_s] = true,
  [clsid.wpn_groza_s] = true,
  [clsid.wpn_knife_s] = true,
}

local mtn_clsids =
{
  [clsid.boar_s] = true,
  [clsid.bloodsucker_s] = true,
  [clsid.dog_s] = true,
  [clsid.flesh_s] = true,
  [clsid.pseudodog_s] = true,
  [clsid.cat_s] = true,
  [clsid.psy_dog_s] = true,
  [clsid.burer_s] = true,
  [clsid.chimera_s] = true,
  [clsid.controller_s] = true,
  [clsid.fracture_s] = true,
  [clsid.poltergeist_s] = true,
  [clsid.gigant_s] = true,
  [clsid.zombie_s] = true,
  [clsid.tushkano_s] = true,
  [clsid.snork_s] = true,
}
also we need an array with weapons spawned via all.spawn:
local all_spawn_weapons =
{
  "esc_wpn_ak74u" = true,
  "esc_wpn_pm" = true,
  "esc_wpn_bm16" = true,
  "esc_wpn_walther" = true,
  "kat_wpn_ak74_m1" = true,
  "kat_wpn_ak74u" = true,
  "val_wpn_ak74u" = true,
  "val_wpn_ak74u_0000" = true,
  "val_wpn_ak74u_0001" = true,
  "val_wpn_mp5" = true,
  "val_wpn_mp5_0000" = true,
  "val_wpn_rpg_0000" = true,
  "val_wpn_abakan" = true,
  "dar_wpn_ak74" = true,
  "dar_wpn_rpg7" = true,
  "mil_wpn_rg-6" = true,
  "mil_wpn_lr_0000" = true,
  "mil_wpn_pm_0000" = true,
  "mil_wpn_ak74u" = true,
  "mil_wpn_ak0001" = true,
  "mil_wpn_ak0002" = true,
  "mil_wpn_bm16" = true,
  "mil_wpn_pm" = true,
  "mil_wpn_abakan" = true,
  "mil_wpn_lr_0002" = true,
  "mil_wpn_lr_0003" = true,
  "mil_wpn_lr_0004" = true,
  "mil_wpn_vintorez" = true,
  "mil_wpn_lr300_m1" = true,
  "level_prefix_wpn_groza" = true,
  "aes_wpn_abakan" = true,
  "aes_wpn_rpg7" = true
}
and finally, let's add code for removing items:
class "ItemsRemoval"

function ItemsRemoval:__init()
  self.to_remove = {}
  self.state = "idle"
  self.upd_time = 50000
  self.distance = 70
  self.timer = time_global() + self.upd_time
end

function ItemsRemoval:__finalize()
end

function ItemsRemoval:update()
  if self.state == "idle" then
    self.state = "upd_suspended"
    self:execute()
    if self.timer <= time_global() then
      self:collector()
      self.timer = time_global() + self.upd_time
    end
    self.state = "idle"
  end
end

function ItemsRemoval:isWeapon(obj)
  if all_spawn_weapons[obj:name()] ~= nil then
    return false
  end

  if wpn_clsids[get_clsid(obj)] ~= nil then
    return not self:has_parent(obj:parent())
  end
  return false
end

function ItemsRemoval:isStalker(obj)
  return get_clsid(obj) == clsid.script_stalker
end

function ItemsRemoval:isMutant(obj)
  if obj:section() == "m_crow" then
    return true
  end

  return mtn_clsids[get_clsid(obj)] ~= nil
end

function ItemsRemoval:contains(id)
  return self.to_remove[id] ~= nil
end

function ItemsRemoval:has_parent(parent)
  if not parent then
    return false
  end

  return parent:id() ~= 65535
end

function ItemsRemoval:has_story_id(obj)
  return obj:story_id() ~= 4294967296 -- 2^32 no story_id
end

function ItemsRemoval:has_story_id_alternative(id)
  local obj = alife():object(id)
  if obj then
    if obj.m_story_id ~= nil then
      if obj.m_story_id ~= 4294967296 then -- 2^32 no story_id
        return true -- has story_id
      end
    end
  end
  return false
end

function ItemsRemoval:has_game_info(id)
  local obj = alife():object(id)
  if obj then
    if obj:spawn_ini() then
      return obj:spawn_ini():section_exist("game_info")
    end
  end
  return false
end

function ItemsRemoval:add_alive(obj, id)
  local iType = nil

  if self:isMutant(obj) then
    iType = "mutant"
  elseif self:isStalker(obj) then
    iType = "stalker"
  end

  if iType == "stalker" or iType == "mutant" then
    if obj:alive() then
      return false
    end
    if not self:has_story_id(obj) and not self:has_game_info(id) then
      self.to_remove[id] = {item_type = iType}
      if iType == "stalker" then
        self.to_remove[id].looted = false
      end
      return true
    end
  end

  return false
end

function ItemsRemoval:add_item(obj, id)
  if self:contains(id) then
    return false
  end

  if self:isWeapon(obj) then
    self.to_remove[id] = {item_type = "weapon"}
    return true
  end

  return self:add_alive(obj, id)
end

function ItemsRemoval:collector()
  local obj = nil
  for index = 1, 65535 do
    obj = level.object_by_id(index)
    if obj then
      self:add_item(obj, index)
    end
  end
end

function ItemsRemoval:mark_looted(id)
  if self:contains(id) then
    self.to_remove[id].looted = true
  end
end

function ItemsRemoval:exclude_stalker(id)
  self.to_remove[id] = nil
end

function ItemsRemoval:execute()
  local item, can_remove = nil, false

  for id, data in pairs(self.to_remove) do
    item = level.object_by_id(id)

    if item and db.actor then
      if item:position():distance_to(db.actor:position()) >= self.distance then
        if data.item_type == "weapon" then
          if not self:has_parent(item:parent()) then
            can_remove = true
          else
            can_remove = false
            self.to_remove[id] = nil
          end
        elseif data.item_type == "stalker" then
          if data.looted then
            can_remove = true
          else
            can_remove = false
          end
        else
          can_remove = true
        end

        if can_remove then
          --self:remove_mapspot(id, item)
          alife():release(alife():object(id), true)
          self.to_remove[id] = nil
        end
      end
    end
  end
end

function ItemsRemoval:remove_mapspot(id, item)
  item = alife():object(item.id())
  if item then
    if item:visible_for_map() then
      item:visible_for_map(false)
    end

    for index = 1, level.map_has_object_spot(id) do
      level.map_remove_object_spot(id)
    end
  end
end

-- ********************************************** --

local items_rem = nil

function getIR()
  if items_rem == nil then
    items_rem = ItemsRemoval()
  end
  return items_rem
end
We need to add code to

death_manager.script

to exclude npcs corpses which contains special items:
function keep_item(npc, item)
  local section = item:section()

  if section == "bolt" then
    return false
  end

  if always_keep_item[section] == true then
    item_removal.getIR():exclude_stalker(npc:id())
    return true
  end

...

end
And the last thing - we have to add update code to

bind_stalker.script

:
function actor_binder:update(delta)
  object_binder.update(self, delta)
  local time = time_global()
  game_stats.update(delta, self.object)

  item_removal.getIR():update()

...

end


Item usage

There are situations in which you would like to execute function when actor use certain inventory item, for instance sleeping bag, antidote or some device. Edit

bind_stalker.script

function actor_binder:on_item_drop (obj)
  item_usage.on_item_drop(obj)
  ...
end

function actor_binder:update(delta)
  ...
  item_usage.check_dropped_item()
  if self.bCheckStart then
    ...
  end
end
Create new file called

item_usage.script

local dropped_item = nil

function on_item_drop(obj)
  if not has_alife_info("ui_inventory") then
    return
  end

  if obj:section() == "antirad" then
    dropped_item = obj:id()
  end
end

function check_dropped_item()
  if dropped_item ~= nil then
    local se_obj = alife():object(dropped_item)
    if se_obj == nil then
      -- item used, call your script here
    else
      -- item dropped
    end
    dropped_item = nil
  end
end


Scripted actions

Using scripted actions you can force an object to perform certain action. To perform actions we use class

entity_action

C++ class entity_action {
  entity_action ();
  entity_action (const entity_action*);

  function set_action(move&);
  function set_action(look&);
  function set_action(anim&);
  function set_action(sound&);
  function set_action(particle&);
  function set_action(object&);
  function set_action(cond&);
  function set_action(act&);
  function move() const;
  function particle() const;
  function completed();
  function object() const;
  function all();
  function time();
  function look() const;
  function sound() const;
  function anim() const;
};
You can set actions for:
  • npcs
  • mutants
  • physical objects
  • anomalies
What kind of action object can perform depends on object type (for instance physical object cannot jump, but mutant can, etc.). Class

entity_action

allows you to perform actions:
  • object movement
  • object sight (look)
  • play animations
  • play sounds
  • play particles
  • interact with another object
  • set conditions for actions like for instance time
To set action for object:
  • we have to mark that object is under script control:
    -- script_name() returns name of the current script
obj:script(true, script_name())
    however object should be controlled by only one script, thus it is recommended to use function

    xr_logic.mob_capture(object, boolean)

    which set script control for object if object isn't yet controlled by script;

    reset_actions

    argument decides whether previous action (if any) should be reset
    • function mob_capture(mob, reset_actions)
  if reset_actions == nil then
    abort("mob_capture: reset_actions parameter's value is not specified")
  end

  if reset_actions then
    reset_action(mob, script_name())
  else
    if not mob:get_script() then
      mob:script(true, script_name())
    end
  end
end
  • set action for object using

    entity_action

    class (read below)
  • release script control:
    • obj:script(false, script_name())

-- or you can use function (xr_logic.script)
function mob_release(mob)
  if mob:get_script() then
    mob:script(false, script_name())
  end
end
  • to check whether object is controlled by script, you can use function
    • -- xr_logic.script
function mob_captured(mob)
  return mob:get_script()
end
  • you can also check if object is performing action
    • if obj:action() then
  -- object is performing action
end
  • in most cases object can perform only one action at the same time, so if you want to set another action you need to wait untill object finish current action (for instance using

    action()

    function) or reset current action and then set new action. Some of the actions can be set simultaneously, for instance we can stop vehicle and turn the engine off:
    • vehicle:script(true, script_name()) -- set script control
action(vehicle, move(move.off + move.handbrake), cond(cond.time_end, 1000)) -- engine_off + vehicle_stop
vehicle:script(false, script_name()) -- reset script control

Setting actions

To set action for object we use

entity_action

class providing all needed settings, for instance to force object to look at certain point:
function do_action(obj)
  -- set script control and reset previous actions
  xr_logic.mob_capture(obj, true)

  -- set new action
  local act = entity_action()
  act:set_action(look(patrol("patrol_name"):point(0), 1, 1))
  act:set_action(cond(cond.look_end))

  -- perform action
  object:command(act, false)

  -- reset script control
  xr_logic.mob_release(obj)
end
To make things faster, we can use function

action()

from

_g.script

function do_action(obj)
  -- set script control and reset previous actions
  xr_logic.mob_capture(obj, true)

  -- set and perform new action
  action(obj, look(patrol("patrol_name"):point(0), 1, 1), cond(cond.look_end))

  -- reset script control
  xr_logic.mob_release(obj)
end

Action types

If you take a look at

entity_action

class, you will notice that

set_action

method is overloaded expecting object of several classes as argument. Those classes are used for actions. Refer to

lua_help.script

to get yourself familiar with classes:

  • cond

    - class to set conditions for actions (define when action ends)

  • look

    - class to set look position
    • action(dog, look(look.point, db.actor), cond(cond.look_end))

  • move

    - class to set movement type
    • -- walk toward corpse
action(dog, move(move.walk_fwd, corpse), cond(cond.move_end))

-- run using patrol
action(dog, move(move.run_fwd, patrol("way1")), cond(cond.move_end))

  • anim

    - class to set animations
    • action(obj, anim(anim.sit_idle, 0), cond(cond.time_end, 3000))
action(obj, anim(anim.attack, 0), cond(cond.time_end, 50000))
action(obj, anim("vstuplenie"), cond(cond.anim_end))

  • act

    - class to set stalkers/mutants actions
    • action(obj, act(act.rest), cond(cond.time_end, 5000) -- rest
action(obj, act(act.eat, nil), cond(cond.time_end, 2000) -- eat
action(obj, act(act.panic), cond(cond.time_end, 3000) -- panic
action(obj, act(act.attack, db.actor), cond(cond.time_end, 10000)) -- attack actor

  • sound

    - class to set sounds
    • local snd = [[characters_voice\scenario\trader\trader_monolog2]]
action(obj, anim("stand_idle_0"), sound(snd, "bip01_head"), cond(cond.sound_end))

  • object

    - class to interact with object's elements
    • -- turn off left light
action(self.object, object("left_light",  object.deactivate), cond(cond.time_end, time_infinite))

-- turn on right light
action(self.object, object("right_light", object.activate), cond(cond.time_end, time_infinite))

  • particle

    and

    particle_params

    - classes for paticle effects
    • local v1 = vector():set(0, 0, 0.3)
local v2 = vector():set(0, 0, 0)
local v3 = vector():set(1, 0, 0)

local params = particle_params(v1, v2, v3)
local part = particle("nature\\fog_stormy", "bip01_head", params, false)

-- set particle effect on actor's head
action(db.actor, part, cond(cond.time_end, 2000000))

  • patrol

    - class handling patrols (waypoints)
    • local p = patrol("way1", patrol.start, patrol.continue)
action(obj, move(move.walk_fwd, p:point(0)), cond(cond.move_end))


Saving data

There are situations in which you need to save data to saved game file and then read them when actor load saved game. To save/load data you can use actor binder (

bind_stalker.script

):
function actor_binder:save(packet)
  ...
  st_binder.save(packet)
end

function actor_binder:load(reader)
  ...
  st_binder.load(reader)
end
For instance:
-- st_binder.script

function get_string()
  local str = ""
  for index = 1, 10 do
    str = str .. string.char(math.random(97, 122))
  end
  return str
end

function save(writer)
  local n = nil

  for index = 1, 50 do
    n = math.random(0, 255)
    parser.dbg_print("saving : %d [unsigned 8 bits]", n)
    writer:w_u8(n)

    n = math.random(256, 65535)
    parser.dbg_print("saving : %d [unsigned 16 bits]", n)
    writer:w_u16(n)

    n = math.random(65536, 429496672)
    parser.dbg_print("saving : %d [unsigned 32 bits]", n)
    writer:w_u32(n)

    n = tonumber(string.format("%.5f", math.random()))
    parser.dbg_print("saving : %.5f [float]", n)
    writer:w_float(n)

    n = get_string()
    parser.dbg_print("saving : %s [string]", n)
    writer:w_stringZ(n)

    n = if_then_else(math.random() > 0.5, true, false)
    parser.dbg_print("saving : %s [boolean]", tostring(n))
    writer:w_bool(n)
  end
end

function load(reader)
  for index = 1, 50 do
    parser.dbg_print("loading : %d [unsigned 8 bits]", reader:r_u8())
    parser.dbg_print("loading : %d [unsigned 16 bits]", reader:r_u16())
    parser.dbg_print("loading : %d [unsigned 32 bits]", reader:r_u32())
    parser.dbg_print("loading : %.5f [float]", reader:r_float())
    parser.dbg_print("loading : %s [string]", reader:r_stringZ())
    parser.dbg_print("loading : %s [boolean]", tostring(reader:r_bool()))
  end
end


Packets

Packets gives you ability to read/change object's settings without modifying all.spawn entities, for instance you can change

custom_data

object's field, object visual, actually everything what you normally would add in all.spawn entity.

To read object's data using packets:
  • create

    net_packet

    object
  • read object's data using

    STATE_Read

    method and save it in new

    net_packet

  • change object's data (if needed)
  • save data to object (update object's data) using

    STATE_Write

    method
local se_obj = alife():object(all_spawn_obj_name_or_id) -- get server object
local packet = net_packet() -- create packet (object's data storage)

se_obj:STATE_Write(packet) -- fill packet with object's data
se_obj:r_seek(0) -- set reader position on first block
... -- read/edit object's data
se_obj:STATE_Read(packet, packet:w_tell()) -- save object's data
To read and save data we use

net_packet

class which provides several methods to read and save data. Most common methods are:
C++ class net_packet
{
  net_packet ();

  -- stream reader:
  function r_advance(number);
  function r_begin(number&);
  function r_seek(number);
  function r_eof(net_packet*);
  function r_tell();
  function r_elapsed();

  function r_u8();
  function r_s8();
  function r_u16();
  function r_s16();
  function r_s32();
  function r_u32();
  function r_u64();
  function r_s64();
  function r_float();
  function r_stringZ();
  function r_bool();
  function r_vec3();

  -- stream writer
  function w_begin(number);
  function w_tell();

  function w_u8(number);
  function w_u16(number);
  function w_s16(number);
  function w_s32(number);
  function w_u32(number);
  function w_float(number);
  function w_stringZ(string);
  function w_bool(boolean);
  function w_vec3(vector);
};

Using packets

To read data from packet we read fields as they appear in all.spawn entity, using proper data types. Eventually you can refer to acdc.pl to confirm fields and data types they represent. Take a look at following example - level changer:
[285]
; cse_abstract properties
section_name = level_changer
name = exit_to_garbage_01
position = 29.34641456604,16.5561962127686,686.241638183594
direction = 0,0,0

; cse_alife_object properties
game_vertex_id = 1
distance = 7
level_vertex_id = 309788
object_flags = 0xffffff3e
custom_data = «END
[pt_move_if_reject]
path = esc_way_if_reject
END
story_id = 91

; cse_shape properties
shapes = shape0
shape0:type = box
shape0:axis_x = 2.22939944267273,0,0
shape0:axis_y = 0,4.83859968185425,0
shape0:axis_z = 0,0,2.22939944267273
shape0:offset = 0,0,0

; cse_alife_space_restrictor properties
restrictor_type = 3

; cse_alife_level_changer properties
dest_game_vertex_id = 253
dest_level_vertex_id = 209876
dest_position = 36.4117431640625,0.481237411499023,-305.824523925781
dest_direction = 0.0111562451347709,-0.000250374461757019,0.0224392339587212
dest_level_name = L02_Garbage
dest_graph_point = start_actor_01
silent_mode = 0
To read data from such object:
function get_shape(p)
  local shape_count = p:r_u8()
  local shape_type = nil
  local shapes = {}

  for i = 1, shape_count do
    shape_type = p:r_u8()
    shapes[i] = {}
    shapes[i].stype = shape_type
    if shape_type == 0 then
      -- sphere
      shapes[i].center = p:r_vec3()
      shapes[i].radius = p:r_float()
    else
      -- box
      shapes[i].v1 = p:r_vec3()
      shapes[i].v2 = p:r_vec3()
      shapes[i].v3 = p:r_vec3()
      shapes[i].offset = p:r_vec3()
  end

  return shapes
end

function read_level_changer_data(obj)
  local packet = net_packet() -- create new packet
  obj:STATE_Write(packet) -- save current object's data in packet

  -- now we have access to object's data (saved in packet)
  -- so we can read them and save them in array
  local lc_data = {}
  lc_data.game_vertex_id = packet:r_u16()
  lc_data.distance = packet:r_float()
  lc_data.direct_control = packet:r_s32()
  lc_data.level_vertex_id = packet:r_s32()
  lc_data.object_flags = packer:r_s32()
  lc_data.custom_data = packet:r_stringZ()
  lc_data.story_id = packet:r_s32()
  lc_data.spawn_story_id = packet:r_s32()

  lc_data.shapes = get_shape(packet)
  lc_data.restrictor_type = packet:r_u8()

  lc_data.dest_game_vertex_id = packet:r_u16()
  lc_data.dest_level_vertex_id = packet:r_s32()
  lc_data.dest_position = packet:r_vec3()
  lc_data.dest_direction = packet:r_vec3()
  lc_data.dest_level_name = packet:r_stringZ()
  lc_data.dest_graph_point = packet:r_stringZ()
  lc_data.silent_mode = packet:r_u8()

  return lc_data
end
To save data:
function save_shape(shapes, p)
  p:w_u8(#shapes)
  for i = 1, #shapes do
    p:w_u8(shapes[i].stype)
    if shapes[i].stype == 0 then
      -- sphere
      p:w_vec3(shapes[i].center)
      p:w_float(shapes[i].radius)
    else
      -- box
      p:w_vec3(shapes[i].v1)
      p:w_vec3(shapes[i].v2)
      p:w_vec3(shapes[i].v3)
      p:w_vec3(shapes[i].offset)
    end
  end
end

function save_level_changer_data(obj, lc_data)
  local packet = net_packet()
  obj:STATE_Write(packet)

  packet:w_begin(lc_data.game_vertex_id)
  packet:w_float(lc_data.distance)
  packet:w_s32(lc_data.direct_control)
  packet:w_s32(lc_data.level_vertex_id)
  packet:w_s32(lc_data.object_flags)
  packet:w_stringZ(lc_data.custom_data)
  packet:w_s32(lc_data.story_id)
  packet:w_s32(lc_data.spawn_story_id)

  save_shape(lc_data.shapes, packet)

  packet:w_u8(lc_data.restrictor_type)
  packet:w_u16(lc_data.dest_game_vertex_id)
  packet:w_s32(lc_data.dest_level_vertex_id)
  packet:w_vec3(lc_data.dest_position)
  packet:w_vec3(lc_data.dest_direction)
  packet:w_stringZ(lc_data.dest_level_name)
  packet:w_stringZ(lc_data.dest_graph_point)
  packet:w_u8(lc_data.silent_mode)

  packet:r_seek(0)
  obj:STATE_Read(packet, packet:w_tell())
end
Usage example:
-- spawn new level changer:
local pos = vector():set(29.34641456604,16.5561962127686,686.241638183594)
local obj = alife():create("level_changer", pos, 309788, 1)

-- set mapspot for level changer:
level.map_add_object_spot_ser(obj.id, "level_changer", "To Garbage")

-- get spawned level changer data:
local lc_data = read_level_changer_data(obj)

-- because we just spawned level changer, we need to fill some fields:
local dest_pos = vector():set(36.4117431640625,0.481237411499023,-305.824523925781)
local dest_dir = vector():set(0.0111562451347709,-0.000250374461757019,0.0224392339587212)
lc_data.distance = 7
lc_data.custom_data = "[pt_move_if_reject]\npath = esc_way_if_reject\n"
lc_data.story_id = 91
lc_data.restrictor_type = 3
lc_data.dest_game_vertex_id = 253
lc_data.dest_level_vertex_id = 209876
lc_data.dest_position = dest_pos
lc_data.dest_direction = dest_dir
lc_data.dest_level_name = "L02_Garbage"
lc_data.dest_graph_point = "start_actor_01"
lc_data.silent_mode = 0

-- save (update) data:
save_level_changer_data(obj, lc_data)

Packet wrapper

To work with packets you can use packer wrapper - set of functions designed for reading and saving data.

packet wrapper (author: xStream)


packet::cse_abstract

cse_abstract properties

function state_read(packet, data)
  data.dummy16 = packet:r_u16()
  data.section_name = packet:r_stringZ()
  data.name = packet:r_stringZ()
  data.s_gameid = packet:r_u8()
  data.s_rp = packet:r_u8()
  data.position = packet:r_vec3()
  data.direction = packet:r_vec3()
  data.respawn_time = packet:r_u16()
  data.unknown_id = packet:r_u16()
  data.parent_id = packet:r_u16()
  data.phantom_id = packet:r_u16()
  data.s_flags = packet:r_u16()
  data.version = packet:r_u16()
  if is_cs or is_cop then
    -- only for Clear Sky and Call of Pripyat
    data.cse_abstract__unk1_u16 = packet:r_u16()
  end
  data.script_version = packet:r_u16()
  data.unused = packet:r_u16()
  data.spawn_id = packet:r_u16()
  data.extended_size = packet:r_u16()
end

function state_write(packet, data)
  packet:w_u16(data.dummy16)
  packet:w_stringZ(data.section_name)
  packet:w_stringZ(data.name)
  packet:w_u8(data.s_gameid)
  packet:w_u8(data.s_rp)
  packet:w_vec3(data.position)
  packet:w_vec3(data.direction)
  packet:w_u16(data.respawn_time)
  packet:w_u16(data.unknown_id)
  packet:w_u16(data.parent_id)
  packet:w_u16(data.phantom_id)
  packet:w_u16(data.s_flags)
  packet:w_u16(data.version)
  if is_cs or is_cop then
    -- only for Clear Sky and Call of Pripyat
    packet:w_u16(data.cse_abstract__unk1_u16)
  end
  packet:w_u16(data.script_version)
  packet:w_u16(data.unused)
  packet:w_u16(data.spawn_id)
  packet:w_u16(data.extended_size)
end

function update_read(packet, data)
  data.upd_cse_abstract__unk1_u16 = packet:r_u16()
  if data.upd_cse_abstract__unk1_u16 ~= 0 then
    abort("unexpected size [%d] in cse_abstract_properties::update_read",
      data.upd_cse_abstract__unk1_u16)
  end
end

function update_write(packet, data)
  packet:w_u16(data.upd_cse_abstract__unk1_u16) --packet:w_u16(0)
end


packet::cse_alife_car

cse_alife_car properties

function state_read(packet, data)
  -- cse_alife_object properties
  data.game_vertex_id = packet:r_u16()
  data.distance = packet:r_float()
  data.direct_control = packet:r_s32() --packet:r_u32()
  data.level_vertex_id = packet:r_s32() --packet:r_u32()
  data.object_flags = packet:r_s32()
  data.custom_data = packet:r_stringZ()
  data.story_id = packet:r_s32()
  data.spawn_story_id = packet:r_s32()

  -- cse_visual properties
  data.visual_name = packet:r_stringZ()
  data.visual_flags = packet:r_u8()

  -- cse_ph_skeleton properties
  data.skeleton_name = packet:r_stringZ()
  data.skeleton_flags = packet:r_u8()
  data.source_id = packet:r_u16()

  -- cse_alife_car properties
  data.health = packet:r_float()
end

function state_write(packet, data)
  -- cse_alife_object properties
  packet:w_u16(data.game_vertex_id)
  packet:w_float(data.distance)
  packet:w_s32(data.direct_control) --packet:w_u32(data.direct_control)
  packet:w_s32(data.level_vertex_id) --packet:w_u32(data.level_vertex_id)
  packet:w_s32(data.object_flags)
  packet:w_stringZ(data.custom_data)
  packet:w_s32(data.story_id)
  packet:w_s32(data.spawn_story_id)

  -- cse_visual properties
  packet:w_stringZ(data.visual_name)
  packet:w_u8(data.visual_flags)

  -- cse_ph_skeleton properties
  packet:w_stringZ(data.skeleton_name)
  packet:w_u8(data.skeleton_flags)
  packet:w_u16(data.source_id)

  -- cse_alife_car properties
  packet:w_float(data.health)
end


packet::cse_alife_dynamic_object_visual

cse_alife_dynamic_object_visual properties

function state_read(packet, data)
  -- cse_alife_object properties
  data.game_vertex_id = packet:r_u16()
  data.distance = packet:r_float()
  data.direct_control = packet:r_s32() --packet:r_u32()
  data.level_vertex_id = packet:r_s32() --packet:r_u32()
  data.object_flags = packet:r_s32()
  data.custom_data = packet:r_stringZ()
  data.story_id = packet:r_s32()
  data.spawn_story_id = packet:r_s32()

  -- cse_visual properties
  data.visual_name = packet:r_stringZ()
  data.visual_flags = packet:r_u8()
end

function state_write(packet, data)
  -- cse_alife_object properties
  packet:w_u16(data.game_vertex_id)
  packet:w_float(data.distance)
  packet:w_s32(data.direct_control) --packet:w_u32(data.direct_control)
  packet:w_s32(data.level_vertex_id) --packet:w_u32(data.level_vertex_id)
  packet:w_s32(data.object_flags)
  packet:w_stringZ(data.custom_data)
  packet:w_s32(data.story_id)
  packet:w_s32(data.spawn_story_id)

  -- cse_visual properties
  packet:w_stringZ(data.visual_name)
  packet:w_u8(data.visual_flags)
end


packet::cse_alife_graph_point

cse_alife_graph_point properties

function state_read(packet, data)
  data.connection_point_name = packet:r_stringZ()
  data.connection_level_name = packet:r_stringZ()
  data.location0 = packet:r_u8()
  data.location1 = packet:r_u8()
  data.location2 = packet:r_u8()
  data.location3 = packet:r_u8()
end

function state_write(packet, data)
  packet:w_stringZ(data.connection_point_name)
  packet:w_stringZ(data.connection_level_name)
  packet:w_u8(data.location0)
  packet:w_u8(data.location1)
  packet:w_u8(data.location2)
  packet:w_u8(data.location3)
end


packet::cse_alife_helicopter

cse_alife_helicopter properties

function state_read(packet, data)
  -- cse_alife_object properties
  data.game_vertex_id = packet:r_u16()
  data.distance = packet:r_float()
  data.direct_control = packet:r_s32() --packet:r_u32()
  data.level_vertex_id = packet:r_s32() --packet:r_u32()
  data.object_flags = packet:r_s32()
  data.custom_data = packet:r_stringZ()
  data.story_id = packet:r_s32()
  data.spawn_story_id = packet:r_s32()

  -- cse_visual properties
  data.visual_name = packet:r_stringZ()
  data.visual_flags = packet:r_u8()

  -- cse_motion properties
  data.motion_name = packet:r_stringZ()

  -- cse_ph_skeleton properties
  data.skeleton_name = packet:r_stringZ()
  data.skeleton_flags = packet:r_u8()
  data.source_id = packet:r_u16()

  -- cse_alife_helicopter properties
  data.startup_animation = packet:r_stringZ()
  data.engine_sound = packet:r_stringZ()
end

function state_write(packet, data)
  -- cse_alife_object properties
  packet:w_u16(data.game_vertex_id)
  packet:w_float(data.distance)
  packet:w_s32(data.direct_control) --packet:w_u32(data.direct_control)
  packet:w_s32(data.level_vertex_id) --packet:w_u32(data.level_vertex_id)
  packet:w_s32(data.object_flags)
  packet:w_stringZ(data.custom_data)
  packet:w_s32(data.story_id)
  packet:w_s32(data.spawn_story_id)

  -- cse_visual properties
  packet:w_stringZ(data.visual_name)
  packet:w_u8(data.visual_flags)

  -- cse_motion properties
  packet:w_stringZ(data.motion_name)

  -- cse_ph_skeleton properties
  packet:w_stringZ(data.skeleton_name)
  packet:w_u8(data.skeleton_flags)
  packet:w_u16(data.source_id)

  -- cse_alife_helicopter properties
  packet:w_stringZ(data.startup_animation)
  packet:w_stringZ(data.engine_sound)
end


packet::cse_alife_inventory_box

cse_alife_inventory_box properties

function state_read(packet, data)
  -- cse_alife_object properties
  data.game_vertex_id = packet:r_u16()
  data.distance = packet:r_float()
  data.direct_control = packet:r_s32() --packet:r_u32()
  data.level_vertex_id = packet:r_s32() --packet:r_u32()
  data.object_flags = packet:r_s32()
  data.custom_data = packet:r_stringZ()
  data.story_id = packet:r_s32()
  data.spawn_story_id = packet:r_s32()

  -- cse_visual properties
  data.visual_name = packet:r_stringZ()
  data.visual_flags = packet:r_u8()

  -- cse_alife_inventory_box properties
  if is_cop then
    -- for Call of Pripyat only
    data.cse_alive_inventory_box__unk1_u8 = packet:r_u8()
    data.cse_alive_inventory_box__unk2_u8 = packet:r_u8()
    data.tip = packet:r_stringZ()
  end
end

function state_write(packet, data)
  -- cse_alife_object properties
  packet:w_u16(data.game_vertex_id)
  packet:w_float(data.distance)
  packet:w_s32(data.direct_control) --packet:w_u32(data.direct_control)
  packet:w_s32(data.level_vertex_id) --packet:w_u32(data.level_vertex_id)
  packet:w_s32(data.object_flags)
  packet:w_stringZ(data.custom_data)
  packet:w_s32(data.story_id)
  packet:w_s32(data.spawn_story_id)

  -- cse_visual properties
  packet:w_stringZ(data.visual_name)
  packet:w_u8(data.visual_flags)

  -- cse_alife_inventory_box properties
  if is_cop then
    -- for Call of Pripyat only
    packet:w_u8(data.cse_alive_inventory_box__unk1_u8)
    packet:w_u8(data.cse_alive_inventory_box__unk2_u8)
    packet:w_stringZ(data.tip)
  end
end


packet::cse_alife_level_changer

cse_alife_level_changer properties

function state_read(packet, data)
  -- cse_alife_object properties
  data.game_vertex_id = packet:r_u16()
  data.distance = packet:r_float()
  data.direct_control = packet:r_s32() --packet:r_u32()
  data.level_vertex_id = packet:r_s32() --packet:r_u32()
  data.object_flags = packet:r_s32()
  data.custom_data = packet:r_stringZ()
  data.story_id = packet:r_s32()
  data.spawn_story_id = packet:r_s32()

  -- cse_shape properties
  local shape_count = packet:r_u8()
  data.shapes = {}
  for i = 1, shape_count do
    local shape_type = packet:r_u8()
    data.shapes[i] = {}
    data.shapes[i].shtype = shape_type
    if shape_type == 0 then
      -- sphere
      data.shapes[i].offset = packet:r_vec3()
      data.shapes[i].radius = packet:r_float()
    else
      -- box
      data.shapes[i].v1 = packet:r_vec3()
      data.shapes[i].v2 = packet:r_vec3()
      data.shapes[i].v3 = packet:r_vec3()
      data.shapes[i].offset = packet:r_vec3()
    end
  end

  -- cse_alife_space_restrictor properties
  data.restrictor_type = packet:r_u8()

  -- cse_alife_level_changer properties
  data.dest_game_vertex_id = packet:r_u16()
  data.dest_level_vertex_id = packet:r_s32()
  data.dest_position = packet:r_vec3()
  data.dest_direction = packet:r_vec3()
  data.dest_level_name = packet:r_stringZ()
  data.dest_graph_point = packet:r_stringZ()
  data.silent_mode = packet:r_u8()
end

function state_write(packet, data)
  -- cse_alife_object properties
  packet:w_u16(data.game_vertex_id)
  packet:w_float(data.distance)
  packet:w_s32(data.direct_control) --packet:w_u32(data.direct_control)
  packet:w_s32(data.level_vertex_id) --packet:w_u32(data.level_vertex_id)
  packet:w_s32(data.object_flags)
  packet:w_stringZ(data.custom_data)
  packet:w_s32(data.story_id)
  packet:w_s32(data.spawn_story_id)

  -- cse_shape properties
  packet:w_u8(table.getn(data.shapes))
  for i = 1, table.getn(data.shapes) do
    packet:w_u8(data.shapes[i].shtype)
    if data.shapes[i].shtype == 0 then
      -- sphere
      packet:w_vec3(data.shapes[i].offset)
      packet:w_float(data.shapes[i].radius)
    else
      -- box
      packet:w_vec3(data.shapes[i].v1)
      packet:w_vec3(data.shapes[i].v2)
      packet:w_vec3(data.shapes[i].v3)
      packet:w_vec3(data.shapes[i].offset)
    end
  end

  -- cse_alife_space_restrictor properties
  packet:w_u8(data.restrictor_type)

  -- cse_alife_level_changer properties
  packet:w_u16(data.dest_game_vertex_id)
  packet:w_s32(data.dest_level_vertex_id)
  packet:w_vec3(data.dest_position)
  packet:w_vec3(data.dest_direction)
  packet:w_stringZ(data.dest_level_name)
  packet:w_stringZ(data.dest_graph_point)
  packet:w_u8(data.silent_mode)
end


packet::cse_alife_object

cse_alife_object properties

function state_read(packet, data)
  -- cse_alife_object properties
  data.game_vertex_id = packet:r_u16()
  data.distance = packet:r_float()
  data.direct_control = packet:r_s32() --packet:r_u32()
  data.level_vertex_id = packet:r_s32() --packet:r_u32()
  data.object_flags = packet:r_s32()
  data.custom_data = packet:r_stringZ()
  data.story_id = packet:r_s32()
  data.spawn_story_id = packet:r_s32()
end

function state_write(packet, data)
  -- cse_alife_object properties
  packet:w_u16(data.game_vertex_id)
  packet:w_float(data.distance)
  packet:w_s32(data.direct_control) --packet:w_u32(data.direct_control)
  packet:w_s32(data.level_vertex_id) --packet:w_u32(data.level_vertex_id)
  packet:w_s32(data.object_flags)
  packet:w_stringZ(data.custom_data)
  packet:w_s32(data.story_id)
  packet:w_s32(data.spawn_story_id)
end


packet::cse_alife_object_breakable

cse_alife_object_breakable properties

function state_read(packet, data)
  -- cse_alife_object properties
  data.game_vertex_id = packet:r_u16()
  data.distance = packet:r_float()
  data.direct_control = packet:r_s32() --packet:r_u32()
  data.level_vertex_id = packet:r_s32() --packet:r_u32()
  data.object_flags = packet:r_s32()
  data.custom_data = packet:r_stringZ()
  data.story_id = packet:r_s32()
  data.spawn_story_id = packet:r_s32()

  -- cse_visual properties
  data.visual_name = packet:r_stringZ()
  data.visual_flags = packet:r_u8()

  -- cse_alife_object_breakable properties
  data.health = packet:r_float()
end

function state_write(packet, data)
  -- cse_alife_object properties
  packet:w_u16(data.game_vertex_id)
  packet:w_float(data.distance)
  packet:w_s32(data.direct_control) --packet:w_u32(data.direct_control)
  packet:w_s32(data.level_vertex_id) --packet:w_u32(data.level_vertex_id)
  packet:w_s32(data.object_flags)
  packet:w_stringZ(data.custom_data)
  packet:w_s32(data.story_id)
  packet:w_s32(data.spawn_story_id)

  -- cse_visual properties
  packet:w_stringZ(data.visual_name)
  packet:w_u8(data.visual_flags)

  -- cse_alife_object_breakable properties
  packet:w_float(data.health)
end


packet::cse_alife_object_climable

cse_alife_object_climable properties

function state_read(packet, data)
  -- cse_alife_object properties
  data.game_vertex_id = packet:r_u16()
  data.distance = packet:r_float()
  data.direct_control = packet:r_s32() --packet:r_u32()
  data.level_vertex_id = packet:r_s32() --packet:r_u32()
  data.object_flags = packet:r_s32()
  data.custom_data = packet:r_stringZ()
  data.story_id = packet:r_s32()
  data.spawn_story_id = packet:r_s32()

  -- cse_shape properties
  local shape_count = packet:r_u8()
  data.shapes = {}
  for i = 1, shape_count do
    local shape_type = packet:r_u8()
    data.shapes[i] = {}
    data.shapes[i].shtype = shape_type
    if shape_type == 0 then
      -- sphere
      data.shapes[i].offset = packet:r_vec3()
      data.shapes[i].radius = packet:r_float()
    else
      -- box
      data.shapes[i].v1 = packet:r_vec3()
      data.shapes[i].v2 = packet:r_vec3()
      data.shapes[i].v3 = packet:r_vec3()
      data.shapes[i].offset = packet:r_vec3()
    end
  end

  -- cse_alife_object_climable properties
  if is_cop then
    -- only for Call of Pripyat
    data.game_material = packet:r_stringZ()
  end
end

function state_write(packet, data)
  -- cse_alife_object properties
  packet:w_u16(data.game_vertex_id)
  packet:w_float(data.distance)
  packet:w_s32(data.direct_control) --packet:w_u32(data.direct_control)
  packet:w_s32(data.level_vertex_id) --packet:w_u32(data.level_vertex_id)
  packet:w_s32(data.object_flags)
  packet:w_stringZ(data.custom_data)
  packet:w_s32(data.story_id)
  packet:w_s32(data.spawn_story_id)

  -- cse_shape properties
  packet:w_u8(table.getn(data.shapes))
  for i = 1, table.getn(data.shapes) do
    packet:w_u8(data.shapes[i].shtype)
    if data.shapes[i].shtype == 0 then
      -- sphere
      packet:w_vec3(data.shapes[i].offset)
      packet:w_float(data.shapes[i].radius)
    else
      -- box
      packet:w_vec3(data.shapes[i].v1)
      packet:w_vec3(data.shapes[i].v2)
      packet:w_vec3(data.shapes[i].v3)
      packet:w_vec3(data.shapes[i].offset)
    end
  end

  -- cse_alife_object_climable properties
  if is_cop then
    -- only for Call of Pripyat
    packet:w_stringZ(data.game_material)
  end
end


packet::cse_alife_object_hanging_lamp

cse_alife_object_hanging_lamp properties

function state_read(packet, data)
  -- cse_alife_object properties
  data.game_vertex_id = packet:r_u16()
  data.distance = packet:r_float()
  data.direct_control = packet:r_s32() --packet:r_u32()
  data.level_vertex_id = packet:r_s32() --packet:r_u32()
  data.object_flags = packet:r_s32()
  data.custom_data = packet:r_stringZ()
  data.story_id = packet:r_s32()
  data.spawn_story_id = packet:r_s32()

  -- cse_visual properties
  data.visual_name = packet:r_stringZ()
  data.visual_flags = packet:r_u8()

  -- cse_ph_skeleton properties
  data.skeleton_name = packet:r_stringZ()
  data.skeleton_flags = packet:r_u8()
  data.source_id = packet:r_u16()

  -- cse_alife_object_hanging_lamp properties
  data.main_color = packet:r_s32()
  data.main_brightness = packet:r_float()
  data.main_color_animator = packet:r_stringZ()
  data.main_range = packet:r_float()
  data.light_flags = packet:r_u16()
  data.startup_animation = packet:r_stringZ()
  data.lamp_fixed_bones = packet:r_stringZ()
  data.health = packet:r_float()
  data.main_virtual_size = packet:r_float()
  data.ambient_radius = packet:r_float()
  data.ambient_power = packet:r_float()
  data.ambient_texture = packet:r_stringZ()
  data.main_texture = packet:r_stringZ()
  data.main_bone = packet:r_stringZ()
  data.main_cone_angle = packet:r_float()
  data.glow_texture = packet:r_stringZ()
  data.glow_radius = packet:r_float()
  data.ambient_bone = packet:r_stringZ()

  if is_cs or is_cop then
    -- only for Call of Pripyat and Clear Sky
    data.cse_alife_object_hanging_lamp__unk1_f32 = packet:r_float()
    data.cse_alife_object_hanging_lamp__unk2_f32 = packet:r_float()
    data.cse_alife_object_hanging_lamp__unk3_f32 = packet:r_float()
  end
end

function state_write(packet, data)
  -- cse_alife_object properties
  packet:w_u16(data.game_vertex_id)
  packet:w_float(data.distance)
  packet:w_s32(data.direct_control) --packet:w_u32(data.direct_control)
  packet:w_s32(data.level_vertex_id) --packet:w_u32(data.level_vertex_id)
  packet:w_s32(data.object_flags)
  packet:w_stringZ(data.custom_data)
  packet:w_s32(data.story_id)
  packet:w_s32(data.spawn_story_id)

  -- cse_visual properties
  packet:w_stringZ(data.visual_name)
  packet:w_u8(data.visual_flags)

  -- cse_ph_skeleton properties
  packet:w_stringZ(data.skeleton_name)
  packet:w_u8(data.skeleton_flags)
  packet:w_u16(data.source_id)

  -- cse_alife_object_hanging_lamp properties
  packet:w_s32(data.main_color)
  packet:w_float(data.main_brightness)
  packet:w_stringZ(data.main_color_animator)
  packet:w_float(data.main_range)
  packet:w_u16(data.light_flags)
  packet:w_stringZ(data.startup_animation)
  packet:w_stringZ(data.lamp_fixed_bones)
  packet:w_float(data.health)
  packet:w_float(data.main_virtual_size)
  packet:w_float(data.ambient_radius)
  packet:w_float(data.ambient_power)
  packet:w_stringZ(data.ambient_texture)
  packet:w_stringZ(data.main_texture)
  packet:w_stringZ(data.main_bone)
  packet:w_float(data.main_cone_angle)
  packet:w_stringZ(data.glow_texture)
  packet:w_float(data.glow_radius)
  packet:w_stringZ(data.ambient_bone)

  if is_cs or is_cop then
    -- only for Call of Pripyat and Clear Sky
    packet:w_float(data.cse_alife_object_hanging_lamp__unk1_f32)
    packet:w_float(data.cse_alife_object_hanging_lamp__unk2_f32)
    packet:w_float(data.cse_alife_object_hanging_lamp__unk3_f32)
  end
end


packet::cse_alife_object_physic

cse_alife_object_physic properties

function state_read(packet, data)
  -- cse_alife_object properties
  data.game_vertex_id = packet:r_u16()
  data.distance = packet:r_float()
  data.direct_control = packet:r_s32() --packet:r_u32()
  data.level_vertex_id = packet:r_s32() --packet:r_u32()
  data.object_flags = packet:r_s32()
  data.custom_data = packet:r_stringZ()
  data.story_id = packet:r_s32()
  data.spawn_story_id = packet:r_s32()

  -- cse_visual properties
  data.visual_name = packet:r_stringZ()
  data.visual_flags = packet:r_u8()

  -- cse_ph_skeleton properties
  data.skeleton_name = packet:r_stringZ()
  data.skeleton_flags = packet:r_u8()
  data.source_id = packet:r_u16()

  -- cse_alife_object_physic properties
  data.physic_type = packet:r_s32()
  data.mass = packet:r_float()
  data.fixed_bones = packet:r_stringZ()
end

function state_write(packet, data)
  -- cse_alife_object properties
  packet:w_u16(data.game_vertex_id)
  packet:w_float(data.distance)
  packet:w_s32(data.direct_control) --packet:w_u32(data.direct_control)
  packet:w_s32(data.level_vertex_id) --packet:w_u32(data.level_vertex_id)
  packet:w_s32(data.object_flags)
  packet:w_stringZ(data.custom_data)
  packet:w_s32(data.story_id)
  packet:w_s32(data.spawn_story_id)

  -- cse_visual properties
  packet:w_stringZ(data.visual_name)
  packet:w_u8(data.visual_flags)

  -- cse_ph_skeleton properties
  packet:w_stringZ(data.skeleton_name)
  packet:w_u8(data.skeleton_flags)
  packet:w_u16(data.source_id)

  -- cse_alife_object_physic properties
  packet:w_s32(data.physic_type)
  packet:w_float(data.mass)
  packet:w_stringZ(data.fixed_bones)
end

function read_chunk(packet, length)
  local tab = {}
  for i = 1, length do
    tab[i] = packet:r_s32() --packet:r_float()
  end
  return tab
end

function update_read(packet, data)
  if is_soc or packet:r_elapsed() == 0 then
    return
  end

  data.upd_num_items = packet:r_u8()

  if data.upd_num_items ~= 0 and data_left(packet) then
    if is_cs then
      -- only for Clear Sky
      data.upd_position = read_chunk(packet, 3, "s32") -- "float"
      data.upd_cse_alife_object_physic__unk1 = read_chunk(packet, 3)
      data.upd_cse_alife_object_physic__unk2 = read_chunk(packet, 3)
      data.upd_cse_alife_object_physic__unk3 = read_chunk(packet, 4)
      if bit_and(data.upd_num_items, 64) == 0 then
        if packet:r_elapsed() >= 12 then
          data.upd_cse_alife_object_physic__unk4 = read_chunk(packet, 3)
        else
          abort("cse_alife_object_physic[CS]: cannot read __unk4!")
          return
        end
      end
      if bit_and(data.upd_num_items, 128) == 0 then
        if packet:r_elapsed() >= 12 then
          data.upd_cse_alife_object_physic__unk5 = read_chunk(packet, 3)
        else
          abort("cse_alife_object_physic[CS]: cannot read __unk5!")
          return
        end
      end
      if data_left(packet) then
        data.upd_cse_alife_object_physic__marker_one = packet:r_u8()
        if data.upd_cse_alife_object_physic__marker_one ~= 1 then
          abort("upd:cse_alife_object_physic__marker_one ~= 1 [CS]")
        end
      end
    elseif is_cop then
      -- only for Call of Pripyat
      data.upd_ph_force = read_chunk(packet, 3)
      data.upd_ph_torque = read_chunk(packet, 3)
      data.upd_ph_position = read_chunk(packet, 3)
      data.upd_ph_rotation = read_chunk(packet, 4)
      if bit_and(data.upd_num_items, 64) == 0 then
        if packet:r_elapsed() >= 12 then
          data.upd_ph_angular_vel = read_chunk(packet, 3)
        else
          abort("cse_alife_object_physic[COP]: cannot read upd:ph_angular_vel!")
          return
        end
      end
      if bit_and(data.upd_num_items, 128) == 0 then
        if packet:r_elapsed() >= 12 then
          data.upd_ph_linear_vel = read_chunk(packet, 3)
        else
          abort("cse_alife_object_physic[COP]: cannot read upd:ph_linear_vel!")
          return
        end
      end
      if data_left(packet) then
        data.upd_ph_unknown = packet:r_u8()
      end
    end
  end

  if data_left(packet) then
    abort("cse_alife_object_physic: data left [%d] in update packet", packet:r_elapsed())
  end
end

function write_chunk(packet, tab)
  if tab == nil then
    return
  end
  for k, v in ipairs(tab) do
    packet:w_s32(v) --packet:w_float(v)
  end
end

function update_write(packet, data)
  if is_soc then
    return
  end
  if data.upd_num_items ~= nil then
    packet:w_u8(data.upd_num_items)
    if data.upd_num_items ~= 0 then
      if is_cs then
        -- only for Clear Sky
        write_chunk(packet, data.upd_position)
        write_chunk(packet, data.upd_cse_alife_object_physic__unk1)
        write_chunk(packet, data.upd_cse_alife_object_physic__unk2)
        write_chunk(packet, data.upd_cse_alife_object_physic__unk3)
        write_chunk(packet, data.upd_cse_alife_object_physic__unk4)
        write_chunk(packet, data.upd_cse_alife_object_physic__unk5)
        packet:w_u8(data.upd_cse_alife_object_physic__marker_one)
      elseif is_cop then
        -- only for Call of Pripyat
        write_chunk(packet, data.upd_ph_force)
        write_chunk(packet, data.upd_ph_torque)
        write_chunk(packet, data.upd_ph_position)
        write_chunk(packet, data.upd_ph_rotation)
        write_chunk(packet, data.upd_ph_angular_vel)
        write_chunk(packet, data.upd_ph_linear_vel)
        packet:w_u8(data.upd_ph_unknown)
      end
    end
  end
end


packet::cse_alife_object_projector

cse_alife_object_projector properties

function state_read(packet, data)
  -- cse_alife_object properties
  data.game_vertex_id = packet:r_u16()
  data.distance = packet:r_float()
  data.direct_control = packet:r_s32() --packet:r_u32()
  data.level_vertex_id = packet:r_s32() --packet:r_u32()
  data.object_flags = packet:r_s32()
  data.custom_data = packet:r_stringZ()
  data.story_id = packet:r_s32()
  data.spawn_story_id = packet:r_s32()

  -- cse_visual properties
  data.visual_name = packet:r_stringZ()
  data.visual_flags = packet:r_u8()
end

function state_write(packet, data)
  -- cse_alife_object properties
  packet:w_u16(data.game_vertex_id)
  packet:w_float(data.distance)
  packet:w_s32(data.direct_control) --packet:w_u32(data.direct_control)
  packet:w_s32(data.level_vertex_id) --packet:w_u32(data.level_vertex_id)
  packet:w_s32(data.object_flags)
  packet:w_stringZ(data.custom_data)
  packet:w_s32(data.story_id)
  packet:w_s32(data.spawn_story_id)

  -- cse_visual properties
  packet:w_stringZ(data.visual_name)
  packet:w_u8(data.visual_flags)
end


packet::cse_alife_space_restrictor

cse_alife_space_restrictor properties

function state_read(packet, data)
  -- cse_alife_object properties
  data.game_vertex_id = packet:r_u16()
  data.distance = packet:r_float()
  data.direct_control = packet:r_s32() --packet:r_u32()
  data.level_vertex_id = packet:r_s32() --packet:r_u32()
  data.object_flags = packet:r_s32()
  data.custom_data = packet:r_stringZ()
  data.story_id = packet:r_s32()
  data.spawn_story_id = packet:r_s32()

  -- cse_shape properties
  local shape_count = packet:r_u8()
  data.shapes = {}
  for i = 1, shape_count do
    local shape_type = packet:r_u8()
    data.shapes[i] = {}
    data.shapes[i].shtype = shape_type
    if shape_type == 0 then
      -- sphere
      data.shapes[i].offset = packet:r_vec3()
      data.shapes[i].radius = packet:r_float()
    else
      -- box
      data.shapes[i].v1 = packet:r_vec3()
      data.shapes[i].v2 = packet:r_vec3()
      data.shapes[i].v3 = packet:r_vec3()
      data.shapes[i].offset = packet:r_vec3()
    end
  end

  -- cse_alife_space_restrictor properties
  data.restrictor_type = packet:r_u8()
end

function state_write(packet, data)
  -- cse_alife_object properties
  packet:w_u16(data.game_vertex_id)
  packet:w_float(data.distance)
  packet:w_s32(data.direct_control) --packet:w_u32(data.direct_control)
  packet:w_s32(data.level_vertex_id) --packet:w_u32(data.level_vertex_id)
  packet:w_s32(data.object_flags)
  packet:w_stringZ(data.custom_data)
  packet:w_s32(data.story_id)
  packet:w_s32(data.spawn_story_id)

  -- cse_shape properties
  packet:w_u8(table.getn(data.shapes))
  for i = 1, table.getn(data.shapes) do
    packet:w_u8(data.shapes[i].shtype)
    if data.shapes[i].shtype == 0 then
      -- sphere
      packet:w_vec3(data.shapes[i].offset)
      packet:w_float(data.shapes[i].radius)
    else
      -- box
      packet:w_vec3(data.shapes[i].v1)
      packet:w_vec3(data.shapes[i].v2)
      packet:w_vec3(data.shapes[i].v3)
      packet:w_vec3(data.shapes[i].offset)
    end
  end

  -- cse_alife_space_restrictor properties
  packet:w_u8(data.restrictor_type)
end


packet::cse_motion

cse_motion properties

function state_read(packet, data)
  -- cse_motion properties
  data.motion_name = packet:r_stringZ()
end

function state_write(packet, data)
  -- cse_motion properties
  packet:w_stringZ(data.motion_name)
end


packet::cse_ph_skeleton

cse_ph_skeleton properties

function state_read(packet, data)
  -- cse_ph_skeleton properties
  data.skeleton_name = packet:r_stringZ()
  data.skeleton_flags = packet:r_u8()
  data.source_id = packet:r_u16()
end

function state_write(packet, data)
  -- cse_ph_skeleton properties
  packet:w_stringZ(data.skeleton_name)
  packet:w_u8(data.skeleton_flags)
  packet:w_u16(data.source_id)
end


packet::cse_shape

cse_shape properties

function state_read(packet, data)
  -- cse_shape properties
  local shape_count = packet:r_u8()
  data.shapes = {}
  for i = 1, shape_count do
    local shape_type = packet:r_u8()
    data.shapes[i] = {}
    data.shapes[i].shtype = shape_type
    if shape_type == 0 then
      -- sphere
      data.shapes[i].offset = packet:r_vec3()
      data.shapes[i].radius = packet:r_float()
    else
      -- box
      data.shapes[i].v1 = packet:r_vec3()
      data.shapes[i].v2 = packet:r_vec3()
      data.shapes[i].v3 = packet:r_vec3()
      data.shapes[i].offset = packet:r_vec3()
    end
  end
end

function state_write(packet, data)
  -- cse_shape properties
  packet:w_u8(table.getn(data.shapes))
  for i = 1, table.getn(data.shapes) do
    packet:w_u8(data.shapes[i].shtype)
    if data.shapes[i].shtype == 0 then
      -- sphere
      packet:w_vec3(data.shapes[i].offset)
      packet:w_float(data.shapes[i].radius)
    else
      -- box
      packet:w_vec3(data.shapes[i].v1)
      packet:w_vec3(data.shapes[i].v2)
      packet:w_vec3(data.shapes[i].v3)
      packet:w_vec3(data.shapes[i].offset)
    end
  end
end


packet::cse_visual

cse_visual properties

function state_read(packet, data)
  -- cse_visual properties
  data.visual_name = packet:r_stringZ()
  data.visual_flags = packet:r_u8()
end

function state_write(packet, data)
  -- cse_visual properties
  packet:w_stringZ(data.visual_name)
  packet:w_u8(data.visual_flags)
end


Reading LTX file through script

Instead of creating more and more variables in scripts, you can use ltx files to store your variables (for instance some script initial values) and then load them in a convenient way. This article is about creating/editing ltx files and reading its contents via script.

ltx file format

Ltx files are actually simple text files with an INI file structure. Each file is divided into sections (with unique names). Sections contains pairs: field and its value. Each field name within one section should be unique, otherwise later value will be assigned, for instance:
[my_section]
af_blood = 1
af_blood = 5

;-- equivalent:
[my_section]
af_blood = 5
Inheritance between sections is allowed which means you can define one base section (or more) and then derive from it, for instance:
[section_a]
af_blood = 2

[section_b]
af_medusa = 3

;-------------------------------------------

[section_c]:section_a
af_night_star = 1

;-- section_c equivalent:
[section_c]
af_blood = 2 ;-- derived from section_a
af_night_star = 1

;-------------------------------------------

[section_d]:section_a, section_b
af_mincer_meat = 5

;-- section_d equivalent:
[section_d]
af_medusa = 3 ;-- derived from section_b
af_blood = 2 ;-- derived from section_a
af_mincer_meat = 5
To read certain value, you need to know which field holds it and in which section such a field is defined. Here is a simple example of ltx file we will use throughout this article:
; config\my_ltx\spawn_data.ltx

[artefacts]
af_blood = 0
af_medusa = 0
af_vyvert = 0
af_dummy_pellicle = 0
af_fuzz_kolobok = 0
af_night_star = 0

[cheap_artefacts]:artefacts
af_blood = 3
af_medusa = 5
af_vyvert = 12
af_mincer_meat = 6
af_rusty_thorn = 8

[expensive_artefacts]:artefacts
af_fuzz_kolobok = 1
af_night_star = 2
af_electra_moonlight = 3

[spawn_vectors]
spawn_vector_1 = 334.4873046875, 15.932071685791, -17.644674301147, 582487, 2
spawn_vector_2 = 287.0705871582, 4.5406813621521, 108.36920166016, 556184, 145
spawn_vector_3 = 241.57223510742, 15.355321884155, 125.87966156006, 525993, 149
spawn_vector_4 = 245.78199768066, 8.2552852630615, 186.07391357422, 528854, 148
spawn_vector_5 = 174.6911315918, 10.57382774353, 155.11390686035, 477423, 150

[blowout]
enabled = true
show_blowout_timer = false
show_blowout_warning = true
spawn_artefacts = true
spawn_mutants = false
radiation_strength = 0.65
blowout_type = outdoor
As you can see our ltx file defines 5 sections, 2 of them derive from section artefacts (trivial example for the sake of clarity). Also values of fields inside sections are using basic data types: numbers, boolean values, strings, however by default values are treated as strings.

Reading ltx fields

To read fields and its values from ltx file, Stalker provides class (exported to lua) called ini_file. Most of methods has two strings arguments. In that case the first argument is a section name, the second - field name. Constructor of ini_file class takes only one argument - path to ltx file. The main folder where Stalker looks for ltx files is

gamedata\config\

as it is defined in fsgame.ltx file (variable

$game_config$

). To read the ltx file, we need to create an object of ini_file class providing the path to our ltx file:
local path = "my_ltx\\spawn_data.ltx" -- gamedata\config\my_ltx\spawn_data.ltx
local ltx = ini_file(path)
Now we can access our fields using methods from ini_file class:
local ltx = ini_file("my_ltx\\spawn_data.ltx")

if ltx then
  -- read value of field 'enabled' from section 'blowout'
  local is_blowout_enabled = ltx:r_bool("blowout", "enabled") -- true

  -- section 'blowout', field 'radiation_strength'
  local rad_strength = ltx:r_float("blowout", "radiation_strength") -- 0.65

  -- section 'blowout', field 'blowout_type'
  local blow_type = ltx:r_string("blowout", "blowout_type") -- outdoor

  -- number of fields (lines) in section 'spawn_vectors'
  local num_lines = ltx:line_count("spawn_vectors") -- 5

  -- read the 5th vector: v.x = 174.6911315918, v.y = 10.57382774353, v.z = 155.11390686035
  local v = ltx:r_vector("spawn_vectors", "spawn_vector_5")

  -- safe reading
  if ltx:section_exist("expensive_artefacts") then
    if ltx:line_exist("expensive_artefacts", "af_electra_moonlight") then
      local moonlights = ltx:r_string("expensive_artefacts",
      "af_electra_moonlight") .. "x moonlights" -- 3x moonlights
    end
  end
end

Reading section

Above examples are useful if you only want to read one or two fields. If you want to read all the fields from certain section, you can use loop, for instance:
-- read the whole section, line by line
-- load fields and values to array
function read_section(ltx_path, section_name)
  local ltx = ini_file(ltx_path)
  local array = {}

  if not ltx then
    return array
  end

  if ltx:section_exist(section_name) then
    local field, value, lines = "", "", ltx:line_count(section_name)
    for current_line = 0, lines - 1 do
      result, field, value = ltx:r_line(section_name, current_line, "", "")
      array[field] = value
    end
  end

  return array
end

-- function call:
local tbl = read_section("my_ltx\\spawn_data.ltx", "cheap_artefacts")

for artefact_name, quantity in pairs(tbl) do
  for counter = 1, quantity do
    alife():create(artefact_name, vector():set(1, 2, 3), 12345, 67890)
  end
end

Reading more complex data

Function

read_section()

reads the given section line by line, each line is read as a string value. Our ltx file contains section spawn_vectors. As the name implies, this section holds vectors to spawn objects, for example mutants or npcs. For such objects we need the position to spawn (vector) but also level vertex id and game vertex id. We can read the section using method r_vector(), but this function will return only spawn position (vector - first 3 values). However we can read the whole section using function

read_section()

and then parse the values:
function parse_spawn_data(str)
  local t = {}
  str = string.gsub(str, "(%a)", "")
  for value in string.gfind(str, "([%-?%d+%.%d+]+)[%p%s]?") do
    table.insert(t, tonumber(value))
  end

  for index = 1, #t do
    if t[index] == nil then
      return nil
    end
  end

  return {position = vector():set(t[1], t[2], t[3]),
  level_vertex = t[4], game_vertex = t[5]}
end

local string_table = read_section("my_ltx\\spawn_data.ltx", "spawn_vectors")
local parsed_data = {}

for k, v in pairs(string_table) do
  table.insert(parsed_data, parse_spawn_data(v))
end

local artefacts = read_section("my_ltx\\spawn_data.ltx", "cheap_artefacts")
local temp = nil

for artefact_name, quantity in pairs(artefacts) do
  for counter = 1, quantity do
    temp = parsed_data[math.random(1, #parsed_data)]
    alife():create(artefact_name, temp.position, temp.level_vertex, temp.game_vertex)
  end
end

Dynamic ltx

You can also create ltx file in memory and then use methods of ini_file class, just like you would read data from loaded file:
local content = "[sect]\n"
content = content .. "bool = true \n"
content = content .. "float = 1.5 \n"
content = content .. "string = tekst \n"
content = content .. "uint = 100 \n"

local ltx = create_ini_file(content)

if ltx:section_exist("sect") then
  printf("sect::bool = %s", tostring(ltx:r_bool("sect", "bool")))
  printf("sect::float = %.3f", ltx:r_float("sect", "float"))
  printf("sect::string = %s", ltx:r_string("sect", "string"))
  printf("sect::uint = %d", ltx:r_u32("sect", "uint"))
end


Custom UI Windows

Creating window class

To create window class, we inherit from

CUIScriptWnd

class. Here's the basic stub:
class "uiCustomWindow" (CUIScriptWnd)

-- constructor
function uiCustomWindow:__init(parent) super()
  self.owner = parent -- window parent

  self:InitControls() -- init window controls
  self:InitCallBacks() -- set callbacks for controls
end

-- destructor
function uiCustomWindow:__finalize()
end

-- window update function
function uiCustomWindow:Update()
  CUIScriptWnd.Update(self)
end

-- init window controls
function uiCustomWindow:InitControls()
  -- set window position (x, y, width, height)
  self:Init(0, 0, device().width, device().height)
end

-- set callbacks for controls
function uiCustomWindow:InitCallBacks()
end

-- handle window events
function uiCustomWindow:OnKeyboard(dik, keyboard_action)
  CUIScriptWnd.OnKeyboard(self, dik, keyboard_action)

  if keyboard_action == ui_events.WINDOW_KEY_PRESSED then
    if dik == DIK_keys.DIK_ESCAPE then
      -- close window when ESC button has been pressed
      self:GetHolder():start_stop_menu(self, true)
    end
  end

  return true
end

-- ************************************************************ --
-- ************************************************************ --

function display_window()
  local wnd_parent = get_hud()
  local custom_wnd = uiCustomWindow(wnd_parent)
  level.start_stop_menu(custom_wnd, true)
end

Adding window controls and callbacks

You can add controls dynamically:
-- init window controls
function uiCustomWindow:InitControls()
  -- set window position (x, y, width, height)
  self:Init(0, 0, device().width, device().height)

  local label = CUIStatic()
  label:SetAutoDelete(true)
  label:SetWindowName("label_1")
  label:SetText("click the button!")
  label:SetWndPos(50, 50)
  self:AttachChild(label)

  local button = CUI3tButton()
  button:SetAutoDelete(true)
  button:SetWindowName("button_1")
  button:SetText("click me ...")
  button:SetWndPos(50, 80)
  button:InitTexture("ui_button_main01")
  self:AttachChild(button)
end

-- set callbacks for controls
function uiCustomWindow:InitCallBacks()
  self:AddCallback("button_1", ui_events.BUTTON_CLICKED, self.OnButtonClicked, self)
end

-- BUTTON_CLICKED event handler
function uiCustomWindow:OnButtonClicked()
  local label = self:GetStatic("label_1")
  if label then
    label:SetText("button clicked!")
  end
end
If you are adding controls dynamically, always use functions SetAutoDelete(true) to release control in window's destructor and AttachChild(window_control) to attach control to main window.
You can also add static controls, previously defined in xml file:
<!-- gamedata\config\ui\uiCustomWindow.xml -->

<?xml version="1.0" encoding="utf-8"?>
<window>
  <label x="50" y="50" width="100" height="23">
    <text font="letterica18" r="238" g="155" b="23">click the button!</text>
  </label>
  <button x="50" y="80" width="157" height="48">
    <texture>ui_button_main01</texture>
    <text font="graffiti22">click me ...</text>
    <text_color>
      <e r="227" g="199" b="178"/>
      <t r="180" g="153" b="155"/>
      <d r="106" g="95" b="91"/>
      <h r="0" g="0" b="0"/>
    </text_color>
  </button>
</window>

-- init window controls
function uiCustomWindow:InitControls()
  -- set window position (x, y, width, height)
  self:Init(0, 0, device().width, device().height)

  local xml = CScriptXmlInit()
  xml:ParseFile("uiCustomWindow.xml")

  -- Register function is equivalent to SetAutoDelete and AttachChild
  self:Register(xml:InitStatic("label", self), "label_1")
  self:Register(xml:Init3tButton("button", self), "button_1")
end

-- set callbacks for controls
function uiCustomWindow:InitCallBacks()
  self:AddCallback("button_1", ui_events.BUTTON_CLICKED, self.OnButtonClicked, self)
end

-- BUTTON_CLICKED event handler
function uiCustomWindow:OnButtonClicked()
  local label = self:GetStatic("label_1")
  if label then
    label:SetText("button clicked!")
  end
end

CUIScriptWnd methods

main methods

CUIScriptWnd()

- constructor

SetAutoDelete(boolean)

- whether window should be released when it is not used anymore

IsAutoDelete()

- returns

true

if window is marked as "auto delete";

false

otherwise

Init(number, number, number, number) | Init(Frect*)

- sets window's position: x, y, width and height

Load(string)

- sets window's texture

SetWndPos(number, number)

- sets window's position: x, y only

SetHeight(number)

- sets window's height

GetHeight() const

- returns window's height

SetWidth(number)

- sets window's width

GetWidth() const

- returns window's width

SetWndRect(number, number, number, number) | SetWndRect(Frect)

- sets window's rectangle (user area)

Update()

- window's update function

OnKeyboard(number, enum EUIMessages)

- window's keyboard event handler

SetWndSize(number, number)

- sets window's size

SetWindowName(string)

- sets window's name

WindowName()

- returns window's name

Dispatch(number, number)

- used for dispatching window's messages

Enable(boolean)

- whether the window is enabled

IsEnabled()

- returns

true

if window is enabled;

false

otherwise

Show(boolean)

- whether the window should be displayed

IsShown()

- returns

true

if window is shown;

false

otherwise

SetPPMode()

- to enable postprocessing mode

ResetPPMode()

- to disable postprocessing mode

SetHolder(CDialogHolder*)

-

GetHolder()

-

SetFont(CGameFont*)

- sets current window's font
local font = GetFontSmall()
self:SetFont(font)
you can use this fonts:

  • GetFontSmall()

  • GetFontMedium()

  • GetFontDI()

  • GetFontLetterica25()

  • GetFontLetterica16Russian()

  • GetFontLetterica18Russian()

  • GetFontGraffiti19Russian()

  • GetFontGraffiti22Russian()

  • GetFontGraffiti32Russian()

  • GetFontGraffiti50Russian()

GetFont()

- returns current window's font

methods used for window's controls

Register(CUIWindow*) | Register(CUIWindow*, string)

- used for registering window's controls

AttachChild(CUIWindow*)

- used for attaching window's controls

DetachChild(CUIWindow*)

- used for releasing window's controls

AddCallback(string, number, const function&) | AddCallback(string, number, const function&, object)

- adds events callbacks to window's controls

GetFrameLineWnd(string)

- returns pointer to

CUIFrameLineWnd

control attached to window or

nil

GetDialogWnd(string)

- returns pointer to

CUIDialogWnd

control attached to window or

nil

GetCheckButton(string)

- returns pointer to

CUICheckButton

control attached to window or

nil

GetMessageBox(string)

- returns pointer to

CUIMessageBox

control attached to window or

nil

GetProgressBar(string)

- returns pointer to

CUIProgressBar

control attached to window

or nil

GetEditBox(string)

- returns pointer to

CUIEditBox

control attached to window or

nil

GetListWnd(string)

- returns pointer to

CUIListWnd

control attached to window or

nil

GetPropertiesBox(string)

- returns pointer to

CUIPropertiesBox

control attached to window or

nil

GetButton(string)

- returns pointer to

CUIButton

control attached to window or

nil

GetStatic(string)

- returns pointer to

CUIStatic

control attached to window or

nil

GetTabControl(string)

- returns pointer to

CUITabControl

control attached to window or

nil

GetFrameWindow(string)

- returns pointer to

CUIFrameWindow

control attached to window or

nil


Hud dialogs

Hud dialogs are ui windows displayed on hud, however such windows are not handling input events (keyboard, mouse), thus they are designed rather to present some informations. To display hud dialog, create window based on

CUIScriptWnd

class and use functions from

level

namespace:
class "HudDialog" (CUIScriptWnd)

function HudDialog:__init() super()
end

...

local hud_dlg = HudDialog()

level.add_dialog_to_render(hud_dlg) -- display window
level.remove_dialog_to_render(hud_dlg) -- remove window

Adding input

Hud dialogs are not handling input events however we can use a trick to at least handle keyboard (limited though). We display hud dialog and then capture certain key-press event using game tutorial. Open

config\ui\game_tutorials.xml

and add new tutorial, for instance:
<barin_wnd_test>
  <global_wnd/>
  <item>

    <pause_state>off</pause_state>
    <grab_input>0</grab_input>

    <guard_key>use</guard_key>
    <sound>device\pda\pda_tip</sound>

    <function_on_start>wnd.display_dlg</function_on_start>
    <function_on_stop>wnd.hide_dlg</function_on_stop>

    <length_sec>4</length_sec>
    <cursor_pos/>
    <pda_section/>

    <main_wnd>
      <auto_static start_time="3" length_sec="10000" x="350"
        y="660" width="300" height="60" light_anim="ui_pda_contacts"
        la_cyclic="1" la_text="1" la_alpha="1">
      <window_name>w1</window_name>
      <text font="graffiti22" r="255" g="0" b="0" a="255" align="c">
        press ESCAPE or ENTER to close window and finish tutorial</text>
      </auto_static>
    </main_wnd>
  </item>

</barin_wnd_test>
Now create

wnd.script

and add window class and functions:
class "HudDialog" (CUIScriptWnd)

function HudDialog:__init() super()
end

...

local hud_dlg = nil

function display_dlg()
  local hud_dlg = HudDialog()
  level.add_dialog_to_render(hud_dlg)
end

function hide_dlg()
  level.remove_dialog_to_render(hud_dlg)
end
When we start tutorial
game.start_tutorial("barin_wnd_test")
window will be displayed, now we can press ESCAPE or ENTER to close window. You can set tutorial length value or use some timer - if user won't press certain button then window will be closed automatically.


Object binders

As the name implies, binder binds object with script which control object's state. Usually binders handle events (such as object usage, hit, death, etc.) however binders can be used for other purposes (this depends on object type). In stalker you can find several binders, such as:
  • bind_stalker - actor
  • bind_restrictor - space restrictors
  • bind_smart_terrain - smart terrains
  • bind_respawn - respawns
  • bind_physic_object - physics objects
  • bind_heli - helicopters
  • bind_monster - mutants
  • xr_motivator - stalkers
  • xr_zones - scripted zones (in vanilla - only arena)
To bind object with script, add variable

script_binding

to object section along with function name which will be used for binding, for instance:
[physic_destroyable_object]
GroupControlSection = spawn_group
$spawn              = "physics\destroyable_object"
class               = P_DSTRBL
remove_time         = 60
script_binding      = bind_physic_object.init

-- bind_physic_object.script

function init(obj)
  ...
  obj:bind_object(generic_physics_binder(obj))
end
To implement object binder (such as

generic_physics_binder

in previous example) we have to define class which inherits from

object_binder

class:
C++ class object_binder
{
  property object

  object_binder (game_object*)

  function update(number)
  function reload(string)
  function reinit()
  function save(net_packet*)
  function load(reader*)
  function net_save_relevant()
  function net_export(net_packet*)
  function net_import(net_packet*)
  function net_spawn(cse_alife_object*)
  function net_destroy()
  function net_Relcase(game_object*)
}

object_binder

- constructor; takes only one argument which is the object we want to bind. From now on, to access our object we can use property

object

(which points to our object)

update

- object update function

reload

- reloads object data

reinit

- reinitializes all the variables

save

- saves object variables

load

- loads all previously saved variables

net_save_relevant

- saves all the relevant informations for object (???)

net_export

- ???

net_import

- ???

net_spawn

- called when object is spawned (moved online)

net_destroy

- called when object is moved offline

net_Relcase

- ???
Binder functions are called in specific order relatively to events (there is only one exception:

update

function which is called all the time when object is online):
  • when you load game -

    reload(), reinit(), load(), net_spawn(), update()

  • when you save game -

    save()

  • when you quit game -

    net_destroy()

  • when object is switched offline -

    net_destroy()

  • when object is switched online -

    reload(), reinit(), net_spawn(), update()

  • callback functions are called relatively to proper events (use_callback is called when object is beeing used, etc)
Note: Binders are working only for online objects. That said, methods save() and load() save/load information only for online objects. If your binder save data for online object, you change level and then come back - binder will loose saved data. In such situation use server classes (se_*.script)

Binder and ai schemes - issue events

If an object has some scheme assigned in logic section (custom_data) and this scheme is active, then you can call certain scheme's functions from binder. To do this, you call function

issue_event(obj, stor, fname, args)

which has several arguments:
  • obj - binded object
  • stor - storage of active scheme
  • fname - function we want to call
  • args - all arguments for function we want to call
For instance, to update active scheme:
if self.st.active_section ~= nil then
  xr_logic.issue_event(self.object, self.st[self.st.active_scheme], "update", delta)
end

Binder implementation

  • binding function: this function binds object with binder (script); here you can add all necessary conditions for binding - for instance if certain object doesn't have any logic data (custom_data) then you might not want to bind it:
    • function bind_object(obj)
  local custom_data = obj:spawn_ini()
  if custom_data and custom_data:section_exist("logic") then
    obj:bind_object(my_binder(obj)) -- bind object
  else
    abort("object %s doesn't have logic section!", obj:name()) -- object is not binded
  end
end
  • binder constructor - here you can set all necessary properties; as an argument we pass object we want to bind; you can of course pass other arguments (if needed) in binding function
    • class "my_binder" (object_binder)

-- binder constructor
function my_binder:__init(obj) super(obj)
  self.was_used_by_actor = false
end

function my_binder:reload(section)
  object_binder.reload(self, section)
end
  • reinit - that's a good place to add object to storage script (db.script), init all callbacks, etc
    • function my_binder:reinit()
  object_binder.reinit(self)

  db.storage[self.object:id()] = { } -- add object to storage table
  self.st = db.storage[self.object:id()] -- if you need storage data

  -- set callbacks
  self.object:set_callback(callback.hit, my_binder.hit_callback, self) -- only for npcs/mutants
  self.object:set_callback(callback.death, my_binder.death_callback, self) -- only for npcs/mutants
  self.object:set_callback(callback.use_object, my_binder.use_callback, self)
end
  • net_spawn - called each time when object is switched online; as an argument, function receives server object which you can use to call any functions from se_*.script of your object
    • function my_binder:net_spawn(server_object)
  if not object_binder.net_spawn(self, server_object) then
    return false
  end

    --local se_script = alife():object(server_object.id)

  return true
end
  • net_destroy - called each time when object is switched offline; here you should remove object from storage (if you added it), remove all callbacks (if you set any)
    • function my_binder:net_destroy()
  db.storage[self.object:id()] = nil -- remove object from storage

  -- clear callbacks (if any)
  self.object:set_callback(callback.hit, nil)
  self.object:set_callback(callback.death, nil)
  self.object:set_callback(callback.use_object, nil)

  object_binder.net_destroy(self)
end
  • update - here you add all updates; this depends on object and its purpose
    • function my_binder:update(delta)
  object_binder.update(self, delta)

  -- if object has active scheme, then update it:
  if self.st.active_section ~= nil then
    xr_logic.issue_event(self.object, self.st[self.st.active_scheme], "update", delta)
  end
end
  • save - here you save all needed data to packet
    • function my_binder:save(writer)
  object_binder.save(self, writer)
  xr_logic.save_obj(self.object, writer)
  writer:w_bool(self.was_used_by_actor)
end
  • load - here you load all data from packet
    • function my_binder:load(reader)
  object_binder.load(self, reader)
  xr_logic.load_obj(self.object, reader)
  self.was_used_by_actor = reader:r_bool()
end
  • finally, add all callbacks you registered before:
    • function my_binder:hit_callback(obj, amount, local_direction, who, bone_index)
  if self.st.hit then
    xr_logic.issue_event(self.object, self.st.hit, "hit_callback", obj, amount, local_direction, who, bone_index)
  end
end

function my_binder:death_callback(victim, who)
  if self.st.active_section then
    xr_logic.issue_event(self.object, self.st[self.st.active_scheme], "death_callback", victim, who)
  end
end

function my_binder:use_callback(obj, who)
  if self.object:alive() then
    if self.st.active_section then
      xr_logic.issue_event(self.object, self.st[self.st.active_scheme], "use_callback", obj, who)
    end
  end

  if not self.was_used_by_actor then
    self.was_used_by_actor = true
  end
end


Anomaly fields

One of the most annoying part of Stalker Shadow of Chernobyl are artefacts scattered all over the Zone. Basically artefacts are just jumping in the grass if spawned in all.spawn or near anomalies when spawned by anomaly. This definitely makes it too easy to simply pick up the artefact ... Clear Sky from the other hand presents a new approach to anomalies and artefacts. This article explains how to implement anomalies zones (fields) similar to those from Clear Sky.

Main ideas:
  • anomalies are scattered close to each other, together they create a zone or simply - field of anomalies
  • artefacts are jamping inside anomaly zone using predefined paths
  • when actor is inside anomaly field, special effector is run along with bleeding or radiation hit to make it harder to pick up the artefact

All.spawn

To create anomaly zone we spawn it via all.spawn. First we need to extend

system.ltx

by providing anomaly zone section. As you can see anomaly zone is actually a space restrictor (

SPC_RS_S

) and we will implement binder to control its behaviour:
[anomaly_zone]
GroupControlSection	= spawn_group_restrictor
$spawn              = "ai\space_restrictor"
class               = SPC_RS_S
script_binding      = bind_anom_field.bind
To spawn anomaly zone in all.spawn:
[SECTION_NUMBER]
; cse_abstract properties
section_name = anomaly_zone
name = esc_temp_anom_zone
position = -260.755340576172,-9.49868297576904,-50.1563873291016
direction = -0.89111453294754,1.68149662017822,1.80764615535736

; cse_alife_object properties
game_vertex_id = 0
distance = 0
level_vertex_id = 594270
object_flags = 0xffffffbe

; cse_shape properties
shapes = shape0
shape0:type = sphere
shape0:offset = 0,0,0
shape0:radius = 10

; cse_alife_space_restrictor properties
restrictor_type = 3
Because each anomaly zone need some data, like for instance how many anomalies are included inside zone, what types of artefacts certain zone can spawn and so on, we will store such data in ltx file. Each anomaly field data is stored in section named the same as spawned restrictor in all.spawn:
[esc_temp_anom_zone]
postprocess = fallout_light
hit_type = shock
idle_time = 600
artefacts = af_medusa, 6, af_cristall_flower, 5
af_paths = esc_training_anom_zone_af_path
anom_pos = esc_training_anom_zone_anomalies
anoms = zone_gravi_zone_weak_noart, 8
af_on_zone_spawn = true
cond = {+priboy_training_find_af_done} 100, 0
where:

postprocess

- ppe file name or custom scripted postprocess; used when actor enters anomaly field

hit type

- type of hit when actor is inside zone

idle_time

- time (in minutes) to spawn artefacts

artefacts

- list of pairs: artefact_section, spawn_probability

af_paths

- list of patrols for artefacts (artefact will jamp along this paths)

anom_pos

- positions of anomalies (patrol); to make it convenient, binder will spawn anomalies using positions (waypoints) from

anom_pos

patrol

anoms

- list of pairs: anomaly_section, quantity; this will be used when binder will spawn anomalies

af_on_zone_spawn

- setting this field to

true

means that an artefact will be spawned when zone is created (only once - when new game is created)

cond

- condition list for spawning artefacts

Implementation

Source here


Object binders

Teleport binder

author: Imp
source: here

  • create new sections for teleports:
    • ; config\misc\zone_teleport.ltx

; poz_x, poz_y, poz_z - actor position after teleportation
; rotate - actor direction after teleportation
; radius - teleport radius

[m_teleport]:zone_teleport
teleport       = standart
script_binding = bind_mteleport.bind
radius         = 1
poz_x          = -222.32
poz_y          = -12.27
poz_z          = -165.67
rotate         = 2

[m_teleport_1]:zone_teleport
teleport       = standart
script_binding = bind_mteleport.bind
radius         = 2
poz_x          = 22.78
poz_y          = 20.35
poz_z          = 659.24
rotate         = 1.5
  • implement binder:
    • -- scripts\bind_mteleport.script

local teleport_binders = {}

function bind(obj)
  obj:bind_object(restrictor_teleport(obj))
end

class "restrictor_teleport" (object_binder)

function restrictor_teleport:__init(obj, char_ini) super(obj)
end

function restrictor_teleport:net_spawn(data)
  local char_ini = system_ini()

  if self.teleport == true then
    teleport_binders[self.object:id()] = self
    self["parametrs"] = {}

    -- teleport radius
    if char_ini:line_exist(self.section, "radius") then
      self.parametrs["radius"] = tonumber(char_ini:r_string(self.section, "radius"))
    else
      self.parametrs["radius"] = 2
    end

    -- current teleport position
    local s_obj = alife():object(self.object:id())
    self.parametrs["x"] = tonumber(s_obj.position.x)
    self.parametrs["y"] = tonumber(s_obj.position.y)
    self.parametrs["z"] = tonumber(s_obj.position.z)

    -- actor spawn position (after teleportation)
    self.parametrs["poz_x"] = tonumber(char_ini:r_string(self.section, "poz_x"))
    self.parametrs["poz_y"] = tonumber(char_ini:r_string(self.section, "poz_y"))
    self.parametrs["poz_z"] = tonumber(char_ini:r_string(self.section, "poz_z"))

    -- actor direction (after teleportation)
    if char_ini:line_exist(self.section, "rotate") then
      self.parametrs["rotate"] = tonumber(char_ini:r_string(self.section, "rotate"))
    end
  end
  return true
end

function restrictor_teleport:net_destroy()
  teleport_binders[self.object:id()] = nil
  self.parametrs = nil
  object_binder.net_destroy(self)
end

function restrictor_teleport:reload(section)
  local char_ini = system_ini()

  self.section = section
  if char_ini ~= nil then
    if char_ini:line_exist(self.section, "teleport") then
      self["teleport"] = true
    end
  end
end
  • add actor update function to check if actor is inside teleport:
    • -- scripts\bind_mteleport.script

function abs_comp(a,b)
  if(a < b) then
    return (b - a)
  else
    return (a - b)
  end
end

function teleportate(x, y, z)
  db.actor:set_actor_position(vector():set(x, y, z))
  local snd_obj = xr_sound.get_safe_sound_object([[affects\tinnitus3a]])
  snd_obj:play_no_feedback(db.actor, sound_object.s2d, 0, vector(), 1.0)
end

function actor_update(delta)
  local i, v, actor_poz, s
  actor_poz = db.actor:position()

  for i, v in pairs(teleport_binders) do
    s = v.parametrs
    local obj = level.object_by_id(i)
    if obj ~= nil then
      if s.teleporte ~= nil and s.teleporte ~= false then
        if (time_global() <= s.time) then
          teleportate(s.poz_x, s.poz_y, s.poz_z)
          if s.rotate ~= nil then
            db.actor:set_actor_direction(s.rotate)
          end
          s.teleporte = false
        end
        return
      end

      -- if actor inside teleporter's radius
      if (abs_comp(s.x, actor_poz.x) < v.parametrs.radius and
        abs_comp(s.z, actor_poz.z) < v.parametrs.radius and
        abs_comp(s.y, actor_poz.y) < v.parametrs.z_radius)
      then
        s["teleporte"] = true
        s["time"] = time_global() + 500
        level.add_pp_effector ("teleport.ppe", 2006, false)
      end
    end
  end
end
  • add actor update function call:
    • -- scripts\bind_stalker.script

function actor_binder:update(delta)
  ...
  if self.next_restrictors_update_time < time then
    bind_mteleport.actor_update(delta)
  end
  ...
end
  • usage:
    • local a = vector():set(-244.55, -19.46, -125.42)
local obj = alife():create("m_teleport_1", a, 12829, 8, 65535)


Crow binder

Binding crows might be useful for crow hunting game, for blowout when you want to kill some birds during blowout, for changing crow into eatable meat, etc. You will need edited ogf file and texture for crows from amk mod. To enable binding, add to

config\creatures\m_crow.ltx

line:
script_binding = bind_crow.bind
To be able to eat crow meat, add to

config\misc\items.ltx

:
[dead_crow]:identity_immunities
GroupControlSection  = spawn_group
discovery_dependency =
class                = II_FOOD
cform                = skeleton
visual               = monsters\crow\crow.ogf
description          = dead_crow_desc
inv_name             = dead_crow_name
inv_name_short       = dead_crow_name
inv_weight           = 0.5
inv_grid_width       = 1
inv_grid_height      = 1
inv_grid_x           = 8
inv_grid_y           = 14
cost                 = 20
eat_health           = 0
eat_satiety          = 0.2
eat_power            = 0
eat_radiation        = 0
eat_alcohol          = 0
wounds_heal_perc     = 0
eat_portions_num     = -1
animation_slot       = 4
slot                 = 4
hud                  = wpn_vodka_hud
Then create file

scripts\bind_crow.script

and add code:
function bind(obj)
  db.storage[obj:id()] = {}
  obj:bind_object(crow_binder(obj))
end

class "crow_binder" (object_binder)

function crow_binder:__init(object) super(object)
  self.loaded = false
  self.init_done = false
  self.update = 0
  self.killed = false
end

function crow_binder:reload(section)
  object_binder.reload(self, section)
end

function crow_binder:reinit()
  object_binder.reinit(self)
  if db.storage[self.object:id()] == nil then
    db.storage[self.object:id()] = {}
  end
  self.storage = db.storage[self.object:id()]
  self.init_done = false
end

function crow_binder:net_spawn(sobj)
  if not object_binder.net_spawn(self, sobj) then
    return false
  end
  db.add_obj(self.object)
  return true
end

function crow_binder:net_destroy()
  db.storage[self.object:id()] = nil
  db.del_obj(self.object)
  object_binder.net_destroy(self)
end

function crow_binder:save(packet)
  object_binder.save(self, packet)
  xr_logic.save_obj(self.object, packet)
end

function crow_binder:load(reader)
  self.loaded = true
  object_binder.load(self, reader)
  xr_logic.load_obj(self.object, reader)
end

function crow_binder:update(delta)
  if not self.init_done then
    self.init_done = true
    self.blow_hit = false
    self.object:set_callback(callback.use_object, crow_binder.use_callback, self)
    end

  if not self.killed and not self.blow_hit then
    if xr_blowout.GetBlowout().config.kill_birds then
      if xr_blowout.GetBlowout():GetPhase() == "surge" then
        if math.random() > 0.5 then
          local h = hit()
          h.type = hit.strike
          h.power = 1000
          h.impulse = 1000
          h.direction = self.object:direction()
          h.draftsman = self.object
          self.object:hit(h)
        end
        self.blow_hit = true
      end
    end
  end

  local is_dead = not self.object:alive()

  if not self.killed and is_dead then
    self.object:set_tip_text("inventory_item_use")
    self:death_callback()
    self.killed = true
  end

  if self.update <= time_global() then
    if is_dead and not db.actor:see(self.object) then
      alife():release(alife():object(self.object:id()), true)
    end
    self.update = time_global() + 300000
  end
end

function crow_binder:hit_callback(obj, amount, local_direction, who, bone_index)
end

function crow_binder:death_callback()
  -- add custom code here
end

function crow_binder:use_callback(who)
  self.update = time_global()
  priboy_utils.spawn_inv_items("dead_crow", 1)
end


Actor dialogs

Actor dialogs are optional dialogs which actor can choose during conversation with npc, usually it is:
  • cool info dialog
  • give medkit dialog
  • task manager dialog
  • custom dialog
To attach actor dialog for a certain npc, add dialog's id to npc's character profile:
<specific_character id="character_name">

  ...

  <actor_dialog>dm_cool_info_dialog</actor_dialog>
  <actor_dialog>dm_help_wounded_medkit_dialog</actor_dialog>
  <actor_dialog>tm_wolf_dialog</actor_dialog>
  <actor_dialog>tm_wolf_reward</actor_dialog>

</specific_character>


Start dialogs

Start dialogs are dialogs displayed automatically when actor start talking with npc, usually it is hello dialog or some special dialog related to storyline. If talking is enabled for certain npc, then such npc must have at least one start dialog attached to his character profile. To attach start dialog for a certain npc, add dialog's id to npc's character profile:
<specific_character id="character_name">

  ...

  <start_dialog>escape_lager_volk_talk</start_dialog>
  <start_dialog>dm_hello_dialog</start_dialog>

</specific_character>
You can also set start dialogs in meet section, using conditions:
[walker@esc_blokpost_cmdr]
path_walk = cmdr_walk
path_look = cmdr_look
meet = meet@esc_blokpost_cmdr
danger = danger@esc_blokpost

[meet@esc_blokpost_cmdr]
meet_state = 3|wait@wait
meet_state_wpn = 3|wait@wait
victim = 3|actor
victim_wpn = 3|actor
use = true
use_wpn = true
abuse = false
precond = usability
meet_dialog = {-priboy_training_end} commander_before_training_dialog,
commander_after_training_dialog


Static dialogs

Static dialogs are text conversations between actor and npc. Dialogs are constructed as xml trees, branch with id = 0 is executed first:
<dialog id="unique_dialog_id">
  <!-- eventually: dialog conditions -->
  <phrase_list>
    <phrase id="0">
      <text>text_id_0</text>
      <next>1</next>
    </phrase>
    <phrase id="1">
      <text>text_id_1</text>
    </phrase>
  </phrase_list>
</dialog>
Each dialog phrase contains:
- conditions (optional) - which have to be met to display further dialog's phrases:

  • precondition

    - boolean function; if function returns

    true

    then condition is met
    • -- dialogs.script

function has_actor_enough_money()
  return db.actor:money() >= 5000
end

    <dialog id="unique_dialog_id">
  <phrase_list>
    <phrase id="0">
      <precondition>dialogs.has_actor_enough_money</precondition>
    </phrase>
  </phrase_list>
</dialog>

  • has_info

    - returns

    true

    only if actor has certain infoportion
    • <dialog id="unique_dialog_id">
  <phrase_list>
    <phrase id="0">
      <has_info>bandits_killed</has_info>
    </phrase>
  </phrase_list>
</dialog>
    you can use

    precondition

    to achieve the same effect:
    -- dialogs.script

function has_info()
  return has_alife_info("bandits_killed")
end

    <dialog id="unique_dialog_id">
  <phrase_list>
    <phrase id="0">
      <precondition>dialogs.has_info</precondition>
    </phrase>
  </phrase_list>
</dialog>

  • dont_has_info

    - returns

    true

    only if actor doesn't have certain infoportion
    • <dialog id="unique_dialog_id">
  <phrase_list>
    <phrase id="0">
      <dont_has_info>prisoner_dead</dont_has_info>
    </phrase>
  </phrase_list>
</dialog>
    you can use

    precondition

    to achieve the same effect:
    -- dialogs.script

function dont_has_info()
  return not has_alife_info("prisoner_dead")
end

    <dialog id="unique_dialog_id">
  <phrase_list>
    <phrase id="0">
      <precondition>dialogs.dont_has_info</precondition>
    </phrase>
  </phrase_list>
</dialog>
All the above conditions can be also applied to the whole dialog:
<dialog id="unique_dialog_id">
  <precondition>dialogs.has_enough_money</precondition>
  <has_info>bandits_killed</has_info>
  <dont_has_info>prisoner_dead</dont_has_info>
  <phrase_list>
    ...
  </phrase_list>
</dialog>
If your dialog have several conditions, it might be convenient to use only

precondition

:
-- dialogs.script

function my_precond()
  if has_alife_info("bandits_killed") and not
    has_alife_info("prisoner_dead") and
    db.actor:money() >= 5000
  then
    return true
  end
  return false
end

<dialog id="unique_dialog_id">
  <precondition>dialogs.my_precond</precondition>
  <phrase_list>
    ...
  </phrase_list>
</dialog>
You can use preconditions inside dialog branches, like this:
<dialog id="unique_dialog_id">
  <phrase_list>
    <phrase id="0">
      <text />
      <next>1</next>
      <next>2</next>
      <next>3</next>
    </phrase>
    <phrase id="1">
      <text />
      <precondition>dialogs.has_enough_ammo</precondition>
      <next>11</next>
    </phrase>
    <phrase id="2">
      <text />
      <precondition>dialogs.has_enough_money</precondition>
      <next>22</next>
    </phrase>
    <phrase id="3">
      <text />
      <next>33</next>
    </phrase>
  </phrase_list>
</dialog>
- actions (optional) - which will be performed when certain dialog phrase is activated

  • action

    - calls certain function
    • -- dialogs.script

function give_money()
  relocate_money(db.actor, 5000, "in")
end

    <dialog id="unique_dialog_id">
  <phrase_list>
    <phrase id="0">
      <action>dialogs.give_money</action>
    </phrase>
  </phrase_list>
</dialog>

  • give_info

    - gives certain infoportion and

    disable_info

    - takes the infoportion away
    • <dialog id="unique_dialog_id">
  <phrase_list>
    <phrase id="0">
      <give_info>task_done</give_info>
      <disable_info>task_in_progress</disable_info>
    </phrase>
  </phrase_list>
</dialog>
    you can use

    action

    to achieve the same effect:
    -- dialogs.script

function give_info()
  db.actor:give_info_portion("task_done")
  db.actor:disable_info_portion("task_in_progress")
end

    <dialog id="unique_dialog_id">
  <phrase_list>
    <phrase id="0">
      <action>dialogs.give_info</action>
    </phrase>
  </phrase_list>
</dialog>
- text (required) - textual information displayed in dialog window
<dialog id="unique_dialog_id">
  <phrase_list>
    <phrase id="0">
      <text>text_id_0</text>
    </phrase>
  </phrase_list>
</dialog>
You can use empty

<text>

tags if you need to check certain conditions:
<dialog id="unique_dialog_id">
  <!-- eventually: dialog conditions -->
  <phrase_list>
    <phrase id="0">
      <text />
      <!-- conditions check -->
      <next>1</next>
    </phrase>
    <phrase id="1">
      <text>text_id_1</text>
    </phrase>
  </phrase_list>
</dialog>
- link (required, unless it is the last phrase) to next part (phrase) of dialog
<dialog id="unique_dialog_id">
  <phrase_list>
    <phrase id="0">
      <text />
      <next>1</next> <!-- link to next phrase -->
    </phrase>
    <phrase id="1">
      <text>text_id_1</text>
    </phrase>
  </phrase_list>
</dialog>


Dynamic dialogs

Dynamic dialogs can control the way dialog is displayed. Such dialogs are useful in situations when certain branches of dialog can change in time, depending on new circumstances. We add dynamic dialogs as

actor_dialog

or

start_dialog

to character profile, for instance:
<actor_dialog>tm_trader_dialog</actor_dialog>
<actor_dialog>tm_trader_reward</actor_dialog>
Then we need to add dialogs definitions (for instance in

dialogs.xml)

<dialog id="tm_trader_dialog" priority="-1">
  <init_func>task_manager.init_trader_task_dialog</init_func>
  <has_info>esc_serious_talk</has_info>
</dialog>

<dialog id="tm_trader_reward" priority="0">
  <init_func>task_manager.init_trader_reward_dialog</init_func>
  <precondition>task_manager.has_active_vendor_task</precondition>
  <has_info>esc_serious_talk</has_info>
</dialog>
As you can see we can use preconditions inside dialogs,

priority

sets the order in which dialgs will be displayed on dialog list - dialogs with lower priority will be displayed first;

init_func

constructs the dialog. We pass dialog object as an argument for

init_func

then we add dialog phrases.

To add new phrase to dialog, we use method

AddPhrase()

then we can add preconditions and actions:
C++ class CPhraseDialog
{
  function AddPhrase("text", phrase_id, parent_phrase_id, -10000);
};

C++ class CPhraseScript
{
  function AddHasInfo(string);
  function AddGiveInfo(string);
  function AddDisableInfo(string);
  function AddDontHasInfo(string);
  function AddAction(string);
  function AddPrecondition(string);
};
For instance, consider this static dialog:
Actor: Hello, any news?
NPC: Yep, it seems that Sidorovitch has a new job for you.
NPC: Someone killed bandits in carpark.
NPC: Wolf is gone, he went to Army Warehouses. Here, he left this for you.
NPC: Nope, nothing new.
Actor: Ok, thanks.
NPC: Anytime, you know where to find me.

<dialog id="my_dialog">
  <phrase_list>
    <phrase id="0">
      <text>Hello, any news?</text>
      <next>1</next>
      <next>2</next>
      <next>3</next>
      <next>4</next>
    </phrase>
    <phrase id="1">
      <precondition>dialogs.dark_valley_done</precondition>
      <text>Yep, it seems Sid has a new job for you.</text>
      <next>5</next>
    </phrase>
    <phrase id="2">
      <has_info>esc_carpark_bandits_dead</has_info>
      <dont_has_info>esc_wolf_is_gone</dont_has_info>
      <text>Someone killed bandits in carpark.</text>
      <next>5</next>
    </phrase>
    <phrase id="3">
      <has_info>esc_wolf_is_gone</has_info>
      <text>Wolf is gone, he went to Army Warehouses. Here, he left this for you.</text>
      <action>dialogs.give_money</action>
      <give_info>wolf_goodies_given</give_info>
      <disable_info>esc_wolf_is_gone<disable_info>
      <next>5</next>
    </phrase>
    <phrase id="4">
      <text>Nope, nothing new.</text>
      <next>5</next>
    </phrase>
    <phrase id="5">
      <text>Ok, thanks.</text>
      <next>6</next>
    </phrase>
    <phrase id="6">
      <text>Anytime, you know where to find me.</text>
    </phrase>
  </phrase_list>
</dialog>
We can change above static dialog to dynamic dialog:
function add_last_branch(dlg)
  dlg:AddPhrase("Ok, thanks.", 5, 1, -10000)
  dlg:AddPhrase("Anytime, you know where to find me.", 6, 5, -10000)
end

function init_news_dialog(dlg)
  local phrase, script = nil, nil

  dlg:AddPhrase("Hello, any news?", 0, -1, -10000)

  phrase = dlg:AddPhrase("Yep, it seems that Sid ...", 1, 0, -10000)
  script = phrase:GetPhraseScript()
  script:AddPrecondition("dialogs.dark_valley_done")
  add_last_branch(dlg)

  phrase = dlg:AddPhrase("Someone killed bandits in carpark.", 2, 0, -10000)
  script = phrase:GetPhraseScript()
  script:AddHasInfo("esc_carpark_bandits_dead")
  script:AddDontHasInfo("esc_wolf_is_gone")
  add_last_branch(dlg)

  phrase = dlg:AddPhrase("Wolf is gone, he went to Army Warehouses ...", 3, 0, -10000)
  script = phrase:GetPhraseScript()
  script:AddAction("dialogs.give_money")
  script:AddGiveInfo("wolf_goodies_given")
  script:AddDisableInfo("esc_wolf_is_gone")
  add_last_branch(dlg)

  dlg:AddPhrase("Nope, nothing new.", 4, 0, -10000)
  add_last_branch(dlg)
end


Editing tasks

«Side tasks»

Side are those which you can obtain through dialog from certain (unique) npcs (kill npc, find artefact/item, protect camp, eliminate camp and come back for reward). Such tasks are described in several files:

config\text\eng\stable_task_manager.xml

- contains tasks titles and descriptions

config\gameplay\storyline_info_taskmanager.xml

- tasks articles (how and where add tasks informations to pda)

scripts\task_manager.script

- script which controls tasks

config\misc\task_manager.ltx

- contains tasks definitions

«Side tasks» definitions

; config\misc\task_manager.ltx

; -------------
; random task:
; -------------

[barmen_kill_stalker_3]
type = kill_stalker
community = actor
init_condition = {=actor_on_level(l10_radar) +yan_kill_brain_done}
need_return = false
text = barmen_kill_stalker_3_text
description = barmen_kill_stalker_3_descr
parent = barman
target = rad_monolith_master
reward_money = 10000
reward_reputation = +200
reward_rank = 20
time = 86400

; --------------------------------------------------------------------
; --------------------------------------------------------------------

; -----------------
; non-random task
; -----------------

[barmen_kill_stalker_2]
type = kill_stalker
community = actor
condlist = {+bar_darklab_document_done}
text = barmen_kill_stalker_2_text
description = barmen_kill_stalker_2_descr
parent = barman
target = sim_stalker_veteran
reward_reputation = -45
reward_rank = 6
reward_item = af_soul
time = 86400
prior = 3

type

- task type:

  • artefact

    - find the artifact

  • find_item

    - find specific item

  • monster_part

    - bring mutant's body part

  • kill_stalker

    - kill npc

  • defend_lager

    - defend camp

  • eliminate_lager

    - eliminate camp

  • storyline

    - storyline

community

- always

actor

which means that only actor can get that task (in the old times also npcs could get tasks from traders)

init_condition

- list with conditions which have to be met to enable the task. This variable is only for random tasks (which you get when you are in the certain place at the certain time)

condlist

- list with conditions which have to be met to enable the task. This variable is only for non-random tasks

need_return

- by default

true

which means you have to come back to the task giver to collect your reward. If you set this variable to

false

then you won't get reward

text

- task title (short text in pda)

description

- task description

parent

- task giver:

  • trader

    - Sidorovich

  • barmen

    - Barkeep

  • ecolog

    - Professor Sakharov

  • dolg

    - General Voronin

  • freedom

    - Lukash

  • zastava_commander

    - Sergeant Kitsenko

  • wolf

    - Wolf

  • petrenko

    - Colonel Petrenko

  • hunter

    - Hunter

  • lisiy

    - Baldy

  • drunk_dolg

    - Brome

  • shustriy

    - Nimble

  • mercenary

    - Snitch

target

- depends on task

type

:

  • type = artefact

    then

    target =

    artefact section name

  • type = find_item

    then

    target =

    item section name

  • type = monster_part

    then

    target =

    mutant body part section name

  • type = kill_stalker

    then

    target =

    npc character profile name (can be unique profile - unique npc or it can be a general profile like for instance:

    sim_stalker_novice

    )

  • type = defend_lager

    then

    target =

    smart terrain name

  • type = eliminate_lager

    then

    target =

    smart terrain name

reward_item

- list with reward items, each item name is splitted by comma:
reward_item = grenade_rgd5, grenade_rgd5 ; correct
reward_item = grenade_rgd5, 2 ; WRONG !!!

reward_money

- how much money will you get when finish the task

reward_reputation

- how much your reputation will grow after finishing the job

reward_rank

- how much your runk will grow

time

- how much time you have to accomplish the task; time is counted in seconds: 1 h = 3600 s, so one day = 3600s x 24h = 86400 seconds; to disable time limit just remove

time

variable from task definition (or comment it with comma)

prior

- there are some tasks which you have to do in specific order (for example artefacts tasks for Sidorowich: Jellyfish -> Stone Flower -> Meat Chunk -> Night Star), so value of this variable controls tasks order. It is useful to add priorities if task giver has more than one task with the same type. For instance if task giver has 3 tasks with type

kill_stalker

then it's a good idea to use priorities


Editing tasks

Creating custom «side task»

  • task definition:
    • [wolf_find_item_1]
type = find_item
community = actor
text = wolf_find_item_1_text
description = wolf_find_item_1_descr
parent = wolf
target = wpn_bm16
reward_money = 100000
reward_reputation = +15
reward_rank = 3
reward_item = wpn_svd_m1, ammo_7.62x54_ap, ammo_7.62x54_ap,
ammo_7.62x54_ap, ammo_7.62x54_ap
  • task description and title:
    • <!-- config\text\eng\stable_task_manager.xml -->

<string id="wolf_find_item_1">
  <text>Find Sawn-off dbl-barrel</text>
</string>

<string id="wolf_find_item_1_descr">
  <text>Wolf wants you to find Sawn-off dbl-barrel bacause he want to go to Army Warehouses for a boars hunt</text>
</string>

<string id="wolf_find_item_1_text">
  <text>Marked One bring me Sawn-off dbl-barrel and i will give you a real nice sniper rifle.</text>
</string>
  • article:
    • <!-- config\gameplay\storyline_info_taskmanager.xml -->

<article id="wolf_find_item_1_descr" name="find_item" article_type="task">
  <text>wolf_find_item_1_descr</text>
</article>
Obtaining the task
Task target
Task description
Task completed

Remember to set unique target for tasks with find_item type, otherwise each time you obtain the task, location of task target might be completely random. This affects also kill_stalker task type if you set general (not unique) character profile as target - however this might be desirable effect.

Now after obtaining the task again,
location of task target is somewhere else ...


Editing tasks

Creating new task giver

  • adding tasks descriptions:
    • <!-- config\gameplay\game_tasks_by_vendor.xml -->

<game_task id="eliminate_lager_barin">
  <title>title</title>
  <objective>
    <text>objective</text>
    <icon>ui_iconsTotal_eliminate_lager</icon>
    <function_complete>task_manager.task_complete</function_complete>
    <function_fail>task_manager.task_fail</function_fail>
  </objective>
</game_task>

<game_task id="defend_lager_barin">
  <title>title</title>
  <objective>
    <text>objective</text>
    <icon>ui_iconsTotal_defend_lager</icon>
    <function_complete>task_manager.task_complete</function_complete>
    <function_fail>task_manager.task_fail</function_fail>
  </objective>
</game_task>

<game_task id="artefact_barin">
  <title>title</title>
  <objective>
    <text>objective</text>
    <icon>ui_iconsTotal_artefact</icon>
    <function_complete>task_manager.task_complete</function_complete>
    <function_fail>task_manager.task_fail</function_fail>
  </objective>
</game_task>

<game_task id="kill_stalker_barin">
  <title>title</title>
  <objective>
    <text>objective</text>
    <icon>ui_iconsTotal_kill_stalker</icon>
    <function_complete>task_manager.task_complete</function_complete>
    <function_fail>task_manager.task_fail</function_fail>
  </objective>
</game_task>

<game_task id="monster_part_barin">
  <title>title</title>
  <objective>
    <text>objective</text>
    <icon>ui_iconsTotal_monster_part</icon>
    <function_complete>task_manager.task_complete</function_complete>
    <function_fail>task_manager.task_fail</function_fail>
  </objective>
</game_task>

<game_task id="find_item_barin">
  <title>title</title>
  <objective>
    <text>objective</text>
    <icon>ui_iconsTotal_find_item</icon>
    <function_complete>task_manager.task_complete</function_complete>
    <function_fail>task_manager.task_fail</function_fail>
  </objective>
</game_task>
  • adding task dialogs - dialogs (thus tasks) will be available when actor has

    val_prisoner_free

    infoportion (if actor helped dutyer escape from bandits prison):
    • <!-- config\gameplay\dialogs.xml -->

<dialog id="tm_barin_dialog" priority="-1">
  <init_func>task_manager.init_barin_task_dialog</init_func>
  <has_info>val_prisoner_free</has_info>
</dialog>

<dialog id="tm_barin_reward" priority="0">
  <init_func>task_manager.init_barin_reward_dialog</init_func>
  <precondition>task_manager.has_active_vendor_task</precondition>
  <has_info>val_prisoner_free</has_info>
</dialog>
  • enabling task dialogs:
    • <!-- config\gameplay\character_desc_simulation.xml -->

;----------------------Barin------------------------------

<specific_character id="sim_dolg_veteran_barin" team_default = "1">
  <name>sim_dolg_veteran_barin_name</name>
  <icon>ui_npc_u_stalker_do_antigas</icon>
  <map_icon x="4" y="0"></map_icon>
  <bio>sim_dolg_veteran_barin_bio</bio>

  <class>sim_dolg_veteran_barin</class>
  <community>dolg</community>
  <terrain_sect>stalker_dolg_terrain</terrain_sect>

  <rank>651</rank>
  <reputation>-35</reputation>

  <visual>actors\dolg\stalker_do_antigas</visual>
  <snd_config>characters_voice\human_03\dolg\</snd_config>
  <crouch_type>0</crouch_type>

  <supplies>
    [spawn] \n
    wpn_usp  \n
    ammo_11.43x23_hydro = 1 \n
    wpn_groza_m1 \n
    ammo_5.45x39_ap = 1 \n
    ammo_vog-25 = 2 \n
    #include "gameplay\character_items.xml"	\n
    #include "gameplay\character_food.xml" \n
    #include "gameplay\character_drugs.xml"
  </supplies>

  #include "gameplay\character_criticals_6.xml"
  #include "gameplay\character_dialogs.xml"

  <actor_dialog>tm_barin_dialog</actor_dialog>
  <actor_dialog>tm_barin_reward</actor_dialog>

</specific_character>
  • adding story_id:
    • ; config\game_story_ids.ltx

5353 = "barin_the_trader"

    ; all.spawn :: alife_l05_bar.ltx

[3498]
; cse_abstract properties
section_name = stalker
name = bar_stalker_0001
position = 198.025970458984,-2.47955322265625e-005,106.330772399902
direction = 0,0,0

; cse_alife_trader_abstract properties
money = 5000
character_profile = sim_dolg_veteran_barin

; cse_alife_object properties
game_vertex_id = 1189
distance = 6.29999971389771
level_vertex_id = 47225
object_flags = 0xffffffbf
story_id = 5353

custom_data = «END
; stalker_custom_data.ltx
[game_info]
END

; cse_visual properties
visual_name = actors\dolg\stalker_do_antigas

; cse_alife_creature_abstract properties
g_team = 0
g_squad = 0
g_group = 0
health = 1
dynamic_out_restrictions =
dynamic_in_restrictions =

upd:health = 1
upd:timestamp = 0
upd:creature_flags = 0
upd:position = 198.025970458984,-2.47955322265625e-005,106.330772399902
upd:o_model = 0
upd:o_torso = 0,0,0
upd:g_team = 0
upd:g_squad = 0
upd:g_group = 0

; cse_alife_monster_abstract properties

upd:next_game_vertex_id = 65535
upd:prev_game_vertex_id = 65535
upd:distance_from_point = 0
upd:distance_to_point = 0

; cse_alife_human_abstract properties
predicate5 = 2,2,1,1,2
predicate4 = 2,2,2,0

; cse_ph_skeleton properties

upd:start_dialog =

; se_stalker properties
  • extending the script:
    • -- scripts\task_manager.script

local parent_by_story =
{
  [003]  = "trader",
  [500]  = "barman",
  [902]  = "ecolog",
  [507]  = "dolg",
  [707]  = "freedom",
  [006]  = "wolf",
  [004]  = "shustriy",
  [510]  = "drunk_dolg",
  [504]  = "hunter",
  [518]  = "zastava_commander",
  [506]  = "petrenko",
  [607]  = "lisiy",
  [515]  = "mercenary",
  [5353] = "barin"
}

local story_by_parent =
{
  trader            = 003,
  barman            = 500,
  ecolog            = 902,
  dolg              = 507,
  freedom           = 707,
  wolf              = 006,
  shustriy          = 004,
  drunk_dolg        = 510,
  hunter            = 504,
  zastava_commander = 518,
  petrenko          = 506,
  lisiy             = 607,
  mercenary         = 515,
  barin             = 5353
}

function init_barin_task_dialog(dlg)
  get_random_task():init_task_dialog(dlg, "barin")
end

function init_barin_reward_dialog(dlg)
  get_random_task():init_reward_dialog(dlg, "barin")
end
  • adding tasks definitions:
    • ; config\misc\task_manager.ltx

[barin_find_artefact_1]
type = artefact
community = actor
text = barin_find_artefact_1_text
description = barin_find_artefact_1_descr
parent = barin
target = af_night_star
reward_money = 5000
reward_reputation = 10
reward_rank = 4
reward_item = wpn_groza_m1, ammo_5.45x39_ap, ammo_5.45x39_ap, ammo_vog-25

[barin_monster_part_1]
type = monster_part
community = actor
condlist = {-rad_after_antenna_turned_off}
text = barin_monster_part_1_text
description = barin_monster_part_1_descr
parent = barin
target = mutant_krovosos_jaw
reward_money = 3000
reward_reputation = +20
reward_rank = 5
time = 86400
reward_item = ammo_vog-25, ammo_vog-25, bandage, bandage, bandage

    <!-- config\text\eng\stable_task_manager.xml -->

<string id="barin_find_artefact_1">
  <text>Find the Night Star artifact</text>
</string>
<string id="barin_find_artefact_1_text">
  <text>I need the Night Star. It's quite a rare artifact, but it provides pretty
  good protection from bullets... So will you do it? I'll pay you well!</text>
</string>
<string id="barin_find_artefact_1_descr">
  <text>Find the Night Star, a rare and expensive artifact.</text>
</string>

<string id="barin_monster_part_1">
  <text>Bring the jaw of the bloodsucker</text>
</string>
<string id="barin_monster_part_1_text">
  <text>I heard a superstition that the tooth of a bloodsucker brings good luck.
  As if the one who wears that tooth around his neck gets past the anomalies, and
  artifacts fall right into his hands. If you bring the jaw of a bloodsucker, I'll
  pay you well.</text>
</string>
<string id="barin_monster_part_1_descr">
  <text>So the tooth of the bloodsucker brings good luck? Let's see if it'll bring
  luck to the bloodsucker</text>
</string>

    <!-- config\gameplay\storyline_info_taskmanager.xml -->

<article id="barin_find_artefact_1_descr" name="artefact" article_type="task">
  <text>barin_find_artefact_1_descr</text>
</article>

<article id="barin_monster_part_1_descr" name="monster_part" article_type="task">
  <text>barin_monster_part_1_descr</text>
</article>


Editing tasks

«Storyline tasks»

The main difference between «side tasks» and «storyline tasks» is that in «side tasks» task target have to exist (have to be registered) before you get the task. In case of «storyline tasks» it is not needed because «storyline tasks» are controlled by infoportions/functions. That means you have to carefully think about all possible problems. For instance if your task can be completed only if actor enters certain space restrictor then you must ensure that actor won't miss that restrictor.

«Storyline task» definition

«Storyline tasks» are defined in xml files. Each task has unique id (name), title and contains numbered (starting from 0) objectives.
<game_task id="unique_task_id">
  <title>task_title</title>
  <objective></objective>
  <objective></objective>
  <objective></objective>

  ...

</game_task>
You can use

key

attribute for

<objective>

tag, otherwise objectives will be numbered in order they appear in task definition:
<game_task id="unique_task_id">
  <title>task_title</title>
  <objective key="0"></objective>
  <objective key="1"></objective>
  <objective key="2"></objective>

  ...

</game_task>
First objective (0) contains task's icon (icons are defined in

config\ui\ui_iconstotal.xml

:: texture:

textures\ui\ui_iconsTotal.dds

) and article, other objectives contains titles (

<text>

tag):
<game_task id="unique_task_id">
  <title>task_title</title>
  <objective key="0">
    <icon>ui_iconsTotal_icon_name</icon>
    <article>unique_task_id_article_id</article>
  </objective>

  <objective key="1">
    <text>objective_1_title</text>
  </objective>

  <objective key="2">
    <text>objective_2_title</text>
  </objective>
</game_task>
Each objective may contain map spot, if we want to mark an object on map:
  • story ids :

    config\game_story_ids.ltx

  • map spots types :

    config\ui\map_spots.xml

  • map spots hints :

    config\text\eng\string_table_mapspots_*.xml

<game_task id="unique_task_id">
  <title>task_title</title>
  <objective key="0">
    <icon>ui_iconsTotal_icon_name</icon>
    <article>unique_task_id_article_id</article>
  </objective>

  <objective key="1">
    <text>objective_1_title</text>
    <map_location_type hint="Sidorovitch">green_location</map_location_type>
    <object_story_id>escape_trader</object_story_id>
  </objective>

  <objective key="2">
    <text>objective_2_title</text>
    <map_location_type hint="Barkeep">blue_location</map_location_type>
    <object_story_id>bar_Barman</object_story_id>
  </objective>
</game_task>
To recognize whether the task can be completed or failed we use infoportions and/or boolean functions. Objective 0 always decides whether the whole task is completed or failed unlike other objectives which only controls themselfs:
<!-- config\gameplay\info_portions.xml -->

<info_portion id="whole_task_completed" />
<info_portion id="whole_task_failed" />

<info_portion id="objective_1_completed" />
<info_portion id="objective_1_failed" />

<info_portion id="objective_2_completed" />
<info_portion id="objective_2_failed" />

<!-- config\gameplay\game_tasks.xml -->

<game_task id="unique_task_id">
  <title>task_title</title>
  <objective key="0">
    <icon>ui_iconsTotal_icon_name</icon>
    <article>unique_task_id_article_id</article>
    <infoportion_complete>whole_task_completed</infoportion_complete>
    <infoportion_fail>whole_task_failed</infoportion_fail>
  </objective>

  <objective key="1">
    <text>objective_1_title</text>
    <map_location_type hint="Sidorovitch">green_location</map_location_type>
    <object_story_id>escape_trader</object_story_id>
    <infoportion_complete>objective_1_completed</infoportion_complete>
    <infoportion_fail>objective_1_failed</infoportion_fail>
  </objective>

  <objective key="2">
    <text>objective_2_title</text>
    <map_location_type hint="Barkeep">blue_location</map_location_type>
    <object_story_id>bar_Barman</object_story_id>
    <infoportion_complete>objective_2_completed</infoportion_complete>
    <infoportion_fail>objective_2_failed</infoportion_fail>
  </objective>
</game_task>
We can also use boolean functions:

  • <function_complete>

  • <function_fail>

<!-- config\gameplay\info_portions.xml -->

<info_portion id="whole_task_completed" />
<info_portion id="whole_task_failed" />

<info_portion id="objective_1_completed" />
<info_portion id="objective_1_failed" />

<info_portion id="objective_2_completed" />
<info_portion id="objective_2_failed" />

<!-- config\gameplay\game_tasks.xml -->

<game_task id="unique_task_id">
  <title>task_title</title>
  <objective key="0">
    <icon>ui_iconsTotal_icon_name</icon>
    <article>unique_task_id_article_id</article>
    <function_complete>my_tasks.is_task_completed</function_complete>
    <infoportion_fail>whole_task_failed</infoportion_fail>
  </objective>

  <objective key="1">
    <text>objective_1_title</text>
    <map_location_type hint="Sidorovitch">green_location</map_location_type>
    <object_story_id>escape_trader</object_story_id>
    <infoportion_complete>objective_1_completed</infoportion_complete>
    <infoportion_fail>objective_1_failed</infoportion_fail>
  </objective>

  <objective key="2">
    <text>objective_2_title</text>
    <map_location_type hint="Barkeep">blue_location</map_location_type>
    <object_story_id>bar_Barman</object_story_id>
    <infoportion_complete>objective_2_completed</infoportion_complete>
    <infoportion_fail>objective_2_failed</infoportion_fail>
  </objective>
</game_task>

-- scripts\my_tasks.script

function is_task_completed()
  return has_alive_info("whole_task_completed")
end
If function

is_task_completed()

returns

true

then task will be completed, otherwise nothing will happen unless we receive

whole_task_failed

infoportion which means that our task failed. Notice that function

is_task_completed()

doesn't care about other objectives. That means if we receive

whole_task_completed

infoportion faster that we complete/fail all objectives - not finished objectives won't be updated. To fix this:
-- scripts\my_tasks.script

function set_objectives(value)
  for index = 1, 2 do
    if not (has_alive_info("objective_" .. index .. "_completed")
      or has_alive_info("objective_".. index .. "_failed"))
    then
      db.actor:give_info_portion("objective_" .. index .. value)
    end
  end
end

function is_task_completed()
  if has_alive_info("whole_task_completed") then
    set_objectives("completed")
    return true -- task completed
  end

  if has_alive_info("whole_task_failed") then
    set_objectives("failed")
  end

  return false
end
If you use boolean functions in task definition then two hidden arguments are passed to your function:

  • string

    - task name (task id)

  • number

    - objective number
-- scripts\my_tasks.script

function is_task_completed(task_name, objective)
  -- task_name = unique_task_id
  -- objective = 0
end
Those arguments can be useful if you have several similar tasks/objectives, for instance:
<!-- config\gameplay\info_portions.xml -->

<info_portion id="task_giver_dead" />
<info_portion id="medkit_and_bandage_delivered" />
<info_portion id="bread_delivered" />
<info_portion id="nades_delivered" />

<!-- config\gameplay\game_tasks.xml -->

<game_task id="bring_medkit_and_bandage">
  <title>Bring medkit and bandage</title>
  <objective key="0">
    <infoportion_complete>medkit_and_bandage_delivered</infoportion_complete>
    <infoportion_fail>task_giver_dead</infoportion_fail>
  </objective>

  <objective key="1">
    <text>Find medkit</text>
    <function_complete>my_tasks.has_item</function_complete>
    <infoportion_fail>task_giver_dead</infoportion_fail>
  </objective>

  <objective key="2">
    <text>Find bandage</text>
    <function_complete>my_tasks.has_item</function_complete>
    <infoportion_fail>task_giver_dead</infoportion_fail>
  </objective>
</game_task>

<game_task id="bring_bread">
  <title>Bring 3 loaves of bread</title>
  <objective key="0">
    <infoportion_complete>bread_delivered</infoportion_complete>
    <infoportion_fail>task_giver_dead</infoportion_fail>
  </objective>

  <objective key="1">
    <text>Find bread</text>
    <function_complete>my_tasks.has_item</function_complete>
    <infoportion_fail>task_giver_dead</infoportion_fail>
  </objective>
</game_task>

<game_task id="bring_10_grenades">
  <title>Bring 10 grenades</title>
  <objective key="0">
    <infoportion_complete>nades_delivered</infoportion_complete>
    <infoportion_fail>task_giver_dead</infoportion_fail>
  </objective>

  <objective key="1">
    <text>Find 10 grenades</text>
    <function_complete>my_tasks.has_item</function_complete>
    <infoportion_fail>task_giver_dead</infoportion_fail>
  </objective>
</game_task>

-- scripts\my_tasks.script

local tasks_ids =
{
  bring_medkit_and_bandage = {target_1 = "medkit", target_2 = "bandage"},
  bring_bread = {target_1 = "bread", quantity = 3},
  bring_10_grenades = {target_1 = "grenade_f1", quantity = 10},
}

function get_item_count(section)
  local counter, inv_item = 0, nil

  for index = 0, db.actor:object_count() - 1 do
    inv_item = db.actor:object(index)
    if inv_item:section() == section then
      counter = counter + 1
    end
  end

  return counter
end

function has_item(task_name, objective)
  if tasks_ids[task_name] ~= nil then
    local target = "target_" .. objective
    if tasks_ids[task_name][target] then
      if tasks_ids[task_name].quantity then
        return get_item_count(tasks_ids[task_name][target]) >= tasks_ids[task_name].quantity
      else
        return db.actor:object(tasks_ids[task_name][target]) ~= nil
      end
    end
  end
  return false
end
Notice that we used one function to test objectives from different tasks.

We can also get information about current state of certain objective or set objective state:

  • db.actor:get_task_state(task_name, objective)

    - returns current task state:

    • task.completed

    - objective completed

    task.fail

    - objective failed

    task.in_progress

    - task in progress

    task.task_dummy

    - task not given yet

  • db.actor:set_task_state(task_state, task_name, objective)

    - sets task state
db.actor:get_task_state("bring_medkit_and_bandage", 0) -- main objective - the whole task state
db.actor:get_task_state("bring_medkit_and_bandage", 1) -- second objective (find medkit) state
db.actor:get_task_state("bring_medkit_and_bandage", 2) -- third objective (find bandage) state

-- let's fail "find medkit" objective:
db.actor:set_task_state(task.fail, "bring_medkit_and_bandage", 1)

-- and complete "find bandage" objective:
db.actor:set_task_state(task.completed, "bring_medkit_and_bandage", 2)

-- and finally the whole task will be failed:
db.actor:set_task_state(task.fail, "bring_medkit_and_bandage", 0)
Finally to give task you can use:
  • infoportion - using

    <task>task_id</task>

    tag:
    • <info_portion id="start_task_info">
  <task>bring_medkit_and_bandage</task>
</info_portion>

    db.actor:give_info_portion("start_task_info")
  • script to give task dynamically:
    • local task = CGameTask()
task:load("bring_medkit_and_bandage") -- load from xml using task_id
db.actor:give_task(task, 0, false)

-- eventually to ensure that certain task wasn't given already
-- (storyline tasks are rather given only once):
local task_state = db.actor:get_task_state("bring_medkit_and_bandage", 0)
if task_state ~= task.completed and task_state ~= task.fail and
    task_state ~= task.in_progress
then
  local task = CGameTask()
  task:load("bring_medkit_and_bandage")
  db.actor:give_task(task, 0, false)
end


Editing tasks

Creating «storyline task»

As an example of « storyline task » we will add task which suppose to be included in vanilla Stalker. Luckily for us, all the needed infoportions, restrictors and npcs are already in all.spawn. All we have to do is to recreate task definition.

  • val_sos_wounded

    - wounded stalker (all.spawn :: alife_l04_darkvalley.ltx);

    story_id = 422

  • val_sos_dead

    - infoportion received when

    val_sos_wounded

    dies

  • val_sos_got_medkit

    - infoportion received (via dialog) when

    val_sos_wounded

    get medkit

  • val_sos_actor_near_wounded

    - infoportion received when actor enter

    val_sos_bs_zone

    space restrictor

  • val_sos_start_task

    - infoportion received when actor enter

    val_sos_bs_zone

    space restrictor

  • send_tip(val_sos_pda:stalker:422)

    - function called by

    val_sos_bs_zone

    space restrictor; displays SOS hud message
<!-- config\gameplay\tasks_darkvalley.xml -->

<game_task id="val_help_wounded">
  <title>val_help_wounded_title</title>
  <objective key="0">
    <icon>ui_iconsTotal_val_sos_help</icon>
    <article>val_help_wounded_article_id</article>
    <infoportion_complete>val_sos_got_medkit</infoportion_complete>
    <infoportion_fail>val_sos_dead</infoportion_fail>
  </objective>

  <objective key="1">
    <text>val_help_wounded_reach</text>
    <map_location_type hint="val_sos_help_main_hint">green_location</map_location_type>
    <object_story_id>val_sos_stalker</object_story_id>
    <infoportion_complete>val_sos_actor_near_wounded</infoportion_complete>
  </objective>

  <objective key="2">
    <text>val_help_wounded_give_medkit</text>
    <!--
          val_sos_help_main_hint is defined in:
          config\text\eng\string_table_mapspots_darkvalley.xml
    -->
    <map_location_type hint="val_sos_help_main_hint">green_location</map_location_type>
    <object_story_id>val_sos_stalker</object_story_id>
   </objective>
</game_task>

<!-- config\text\eng\string_table_tasks_darkvalley.xml -->

<string id="val_help_wounded_title">
  <text>Help wounded stalker</text>
</string>
<string id="val_help_wounded_reach">
  <text>Reach wounded stalker</text>
</string>
<string id="val_help_wounded_give_medkit">
  <text>Give medkit to wounded stalker</text>
</string>

<!-- config\gameplay\storyline_info_darkvalley.xml -->

<article id="val_help_wounded_article_id" name="val_help_wounded"
    article_type="task" group="val_help_wounded">
  <text>val_help_wounded_article</text>
</article>

<!-- config\text\eng\stable_storyline_info_darkvalley.xml -->

<string id="val_help_wounded_article">
  <text>STAT, help the wounded stalker before it's to late ...</text>
</string>

<!-- config\gameplay\info_l05dark_valley.xml -->

<info_portion id="val_sos_start_task">
  <action>darkvalley_tasks.send_sos_message</action>
  <task>val_help_wounded</task>
</info_portion>

-- scripts\darkvalley_tasks.script

function send_sos_message()
  news_manager.send_tip(db.actor, "Hurry up!", 0, "stalker", 5000, nil)
end

; config\misc\task_manager.ltx

[list]
...
val_help_wounded
...

[val_help_wounded]
type = storyline
reward_rank = 4
reward_reputation = +10
;reward_relation = dolg, +100


Useful snippets

Npc smart terrain id

function get_smart_id(npc_id)
    local obj = alife():object(npc_id)
    local strn_id = obj:smart_terrain_id()

    -- strn_id == 65535 means: no smart

    return strn_id
end

Display icon on hud

function display_hud_icon(icon, action)
  local hud_icon = get_hud():GetCustomStatic(icon)
  if action == "show" then
    if not hud_icon then
      get_hud():AddCustomStatic(icon, true)
    end
  elseif action == "remove" then
    if hud_icon then
      get_hud():RemoveCustomStatic(icon)
    end
  end
end

Giving infoportions

function GiveInfoportion(info, npc)
    if not npc then
        npc = db.actor
    end
  if not has_alife_info(info)	then
    npc:give_info_portion(info)
    return true -- info given
  end
  return false
end

Disabling infoportions

function DisableInfoportion(info, npc)
    if not npc then
        npc = db.actor
    end
  if has_alife_info(info) then
    npc:disable_info_portion(info)
    return true -- info disabled
  end
  return false
end

Distance between objects

function distance_between_objects(source_object, destination_object)
  if source_object and destination_object then
    return source_object:position():distance_to(destination_object:position())
  end
  return (-1)
end

Patch version

function get_patch_version()
  local version = script_server_object_version()
  if version == 6 then
    return "1.0004"
  elseif version == 7 then
    return "1.0005" -- or 1.0006
  else
    return "unknown"
  end
end

Actor has item

function has_actor_item(section)
  return db.actor:object(section)
end

Actor how many items by section

function get_item_count(section)
  local counter, inv_item = 0, nil
  for index = 0, db.actor:object_count() - 1 do
    inv_item = db.actor:object(index)
    if inv_item:section() == section then
      counter = counter + 1
    end
  end
  return counter
end

Actor item in slot

local slots =
{
  knife = 0,
  pistol = 1,
  assault_rifle = 2,
  grenade = 3,
  binocular = 4,
  bolt = 5,
  outfit = 6
}

-- get_item_in_slot(slots.outfit)

function get_item_in_slot(slot)
  return db.actor:item_in_slot(slot)
end

Actor activate item (by slot)

local slots =
{
  knife = 0,
  pistol = 1,
  assault_rifle = 2,
  grenade = 3,
  binocular = 4,
  bolt = 5,
  outfit = 6
}

-- activate_item(slots.bolt)

function activate_item(slot)
  db.actor:activate_slot(slot)
end

Actor transfer items to stash

function can_transfer(section)
  if section == "wpn_binoc" or section == "wpn_knife" or section == "bolt"
    section == "device_torch" or section == "device_pda"
  then
    return false
  end
  return true
end

function transfer_items_to_stash(stash_sid)
  local inv_box = level_object_by_sid(stash_sid)
  if inv_box then
    local inv_item = nil
    for index = 0, db.actor:object_count() - 1 do
      inv_item = db.actor:object(index)
      if inv_item then
        if can_transfer(inv_item:section()) then
          db.actor:transfer_item(level.object_by_id(inv_item:id()), inv_box)
        end
      end
    end
  end
end

Has infoportion

function actor_has_infoportion(infoportion_name)
  return has_alife_info(infoportion_name)
end

Actor receive money

function actor_receive_money(amount)
  dialogs.relocate_money(db.actor, amount, "in")
end

Actor lost money

function actor_lost_money(amount)
  dialogs.relocate_money(db.actor, amount, "out")
end

Actor teleport

function teleport_actor(dest_position, dest_look)
    local point = patrol(dest_position)
    local look = patrol(dest_look)

    db.actor:set_actor_position(point:point(0))
    local dir = look:point(0):sub(point:point(0))
    db.actor:set_actor_direction(-dir:getH())
end

Set task state

-- set_task_state(task.fail, "esc_kill_bandits_task", 0)

function set_task_state(status, task_name, objective)
  db.actor:set_task_state(status, task_name, task_objective)
end

Mark items on map

function set_mapspot(id, name)
  level.map_add_object_spot(id, "waypoint1", name)
end

-- mark_on_map({"surprise_box"}, "by_name")
-- mark_on_map({"zone_radioactive", "af_night_star"}, "by_section")

function mark_on_map(tab_section, by)
  local object = nil
  for obj_id = 1, 65534 do
    object = level.object_by_id(obj_id)
    if object then
      for k, v in pairs(tab_section) do
        if by == "by_section" then
          if string.find(object:section(), v) then
            set_mapspot(obj_id, object:name())
          end
        elseif by == "by_name" then
          if string.find(object:name(), v) then
            set_mapspot(obj_id, object:name())
          end
        end
      end
    end
  end
end


lua_help TODO

TODO

This is a list of objects, classes, functions/methods, properties etc.
which I couldn't test or don't know what they are used for,
I'm not sure about functionality, etc:

actor_stats.add_points_str()

alife_simulator::valid_object_id()
alife_simulator::add_out_restriction()
alife_simulator::remove_out_restriction()
alife_simulator::add_in_restriction()
alife_simulator::remove_in_restriction()
alife_simulator::remove_all_restrictions()
alife_simulator::create(number)
alife_simulator::release() [flag]
alife_simulator::kill_entity()

object_binder::reload()
object_binder::net_export()
object_binder::net_save_relevant()
object_binder::load()
object_binder::reinit()
object_binder::net_Relcase()
object_binder::net_import()

CGameTask

client_classes

CMainMenu

entity_action::all()
entity_action::time()

render_device::cam_right
render_device::cam_top
render_device::f_time_delta
render_device::frame
render_device::precache_frame
render_device::time_delta
render_device::pause() [TEST]

level.start_stop_menu() [flag]
level.is_wfx_playing()
level.add_cam_effector() [last arg]
level.set_pp_effector_factor() [last arg]
level.cover_in_direction() [not sure]
level.prefetch_sound()
level.iterate_sounds()
level.add_complex_effector()
level.remove_complex_effector()
level.environment()
level.physics_world()

ini_file::r_line() [last args]
ini_file::r_clsid()

profile_timer::tostring()

SGameTaskObjective::set_article_key()

CUIWindow::ResetPPMode()
CUIWindow::SetPPMode()

TODO: DIK_keys
TODO: key_bindings
TODO: CUIGameCustom
TODO: FS
TODO: ui_events
TODO: CUIListWnd
TODO: CUIPropertiesBox
TODO: CUIMessageBox
TODO: CUITextWnd

buy_condition()
ef_storage()
game_ini()
GetTextureInfo()
GetTextureName()
GetTextureRect()
sell_condition()
show_condition()
render_get_dx_level()
IsImportantSave()


lua_help lua

_G

A global variable (table) that holds the global environment (that is, _G._G = _G). Lua itself does not use this variable; changing its value does not affect any environment, nor vice-versa. (Use setfenv to change environments.) In case of Stalker, _G contains all data (functions, global variables) from _g.script and from all scripts (saved as _G[script_name]). Thas is, you can create global functions just by adding them to _g.script
function script_valid(s_name)
  if _G[s_name] ~= nil then
    -- script <s_name> exists and is valid
    -- (was parsed properly without any errors)
    return true
  end
  return false
end

function func_exist(s_name, f_name)
  if script_valid(s_name) then
    if _G[s_name][f_name] ~= nil then
      -- function <f_name> exist in script <s_name>
      return true
    end
  end
  return false
end

function call_func(s_name, f_name, ...)
  if func_exist(s_name, f_name) then
    return _G[s_name][f_name](...)
  end
end

_VERSION

A global variable that holds a string containing the current interpreter version. For Stalker Shadow of Chernobyl 1.0004 this variable is equal to "lua 5.1 (alpha) + luajit 1.0.3".

assert(v [, message])

Issues an error when the value of its argument v is false (i.e., nil or false); otherwise, returns all its arguments. message is an error message; when absent, it defaults to "assertion failed!". For instance:
-- temp.script
function do_crash()
  assert(1 == 2, "buggy ...")
end

printf("CTD imminent :)")
do_crash()

Expression    : fatal error
Function      : CScriptEngine::lua_error
File          : E:\stalker\patch_1_0004\xr_3da\xrGame\script_engine.cpp
Line          : 73
Description   : <no expression>
Arguments     : LUA error: .... - shadow of chernobyl\gamedata\scripts\temp.script:20: buggy ...

collectgarbage(opt [, arg])

This function is a generic interface to the garbage collector. It performs different functions according to its first argument, opt:
  • "stop" - stops the garbage collector.
  • "restart" - restarts the garbage collector.
  • "collect" - performs a full garbage-collection cycle. This is the default action if opt is not specified
  • "count" - returns the total memory in use by Lua (in Kbytes). see gcinfo
  • "step" - performs a garbage-collection step. The step "size" is controlled by arg (larger values mean more steps) in a non-specified way. If you want to control the step size you must experimentally tune the value of arg. Returns true if the step finished a collection cycle.
  • "setpause" - sets arg as the new value for the pause of the collector. Returns the previous value for pause.
  • "setstepmul" - sets arg as the new value for the step multiplier of the collector. Returns the previous value for step.

dofile(filename)

Opens the named file and executes its contents as a Lua chunk. When called without arguments, dofile executes the contents of the standard input (stdin). Returns all values returned by the chunk. In case of errors, dofile propagates the error to its caller (that is, dofile does not run in protected mode).

error(message [, level])

Terminates the last protected function called and returns message as the error message. Function error never returns. Usually, error adds some information about the error position at the beginning of the message. The level argument specifies how to get the error position. With level 1 (the default), the error position is where the error function was called. Level 2 points the error to where the function that called error was called; and so on. Passing a level 0 avoids the addition of error position information to the message.

gcinfo()

Returns the amount of memory in use by Lua (in kilobytes)
printf("currently Lua is using %d Kb", gcinfo())
printf("garbage collector says %d Kb", collectgarbage("count"))

getfenv([f])

Returns the current environment in use by the function. f can be a Lua function or a number that specifies the function at that stack level: Level 1 is the function calling getfenv. If the given function is not a Lua function, or if f is 0, getfenv returns the global environment. The default for f is 1.

getmetatable(object)

If object does not have a metatable, returns nil. Otherwise, if the object's metatable has a "__metatable" field, returns the associated value. Otherwise, returns the metatable of the given object.

ipairs(t)

Returns three values: an iterator function, the table t, and 0, so that the construction
for i, v in ipairs(t) do body end
will iterate over the pairs (1,t[1]), (2,t[2]), ..., up to the first integer key absent from the table.

jit

Just-In-Time Compiler for Lua
LuaJIT Documentation

load(func [, chunkname])

Loads a chunk using function func to get its pieces. Each call to func must return a string that concatenates with previous results. A return of an empty string, nil, or no value signals the end of the chunk. If there are no errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message. The environment of the returned function is the global environment. chunkname is used as the chunk name for error messages and debug information. When absent, it defaults to "=(load)".

loadfile([filename])

Loads a file as a Lua chunk (without running it). If there are no errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message. The environment of the returned function is the global environment.

loadstring(string [, chunkname])

Loads a string as a Lua chunk (without running it). If there are no errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message. The environment of the returned function is the global environment. The optional parameter chunkname is the name to be used in error messages and debug information.
local func, error_msg = loadstring("return 15 + 32")
printf("15 + 32 = %d", func())

newproxy()

newproxy is an unsupported and undocumented function in the Lua base library. From Lua code, the setmetatable function may only be used on objects of table type. The newproxy function circumvents that limitation by creating a zero-size userdata and setting either a new, empty metatable on it or using the metatable of another newproxy instance. We are then free to modify the metatable from Lua. This is the only way to create a proxy object from Lua which honors certain metamethods, such as __len.
do
  local a = newproxy(true)  -- create proxy object with new metatable
  assert(type(a) == 'userdata')
  getmetatable(a).__len = function() return 5 end
  assert(#a == 5)
  local b = newproxy(a)     -- create proxy object with same metatable as another proxy
  assert(b ~= a)
  assert(getmetatable(b) == getmetatable(a))
  assert(#b == 5)
  local c = newproxy(false) -- create proxy object with no metatable
  assert(not getmetatable(c))

  local is_collected = false
  local o = newproxy(true)
  getmetatable(o).__gc = function() is_collected = true end  -- finalizer
  o = nil; collectgarbage()  -- clear all references to object and ensure finalizer called
  assert(is_collected)
end

next(table [, index])

Allows a program to traverse all fields of a table. Its first argument is a table and its second argument is an index in this table. next returns the next index of the table and its associated value. When called with nil as its second argument, next returns an initial index and its associated value. When called with the last index, or with nil in an empty table, next returns nil. If the second argument is absent, then it is interpreted as nil. In particular, you can use next(t) to check whether a table is empty. The order in which the indices are enumerated is not specified, even for numeric indices. (To traverse a table in numeric order, use a numerical for or the ipairs function.) The behavior of next is undefined if, during the traversal, you assign any value to a non-existent field in the table. You may however modify existing fields. In particular, you may clear existing fields.
function is_tab_empty(t)
  return next(t) == nil
end

printf("empty table = %s", tostring(is_tab_empty({})))

local tab = {"a", "b", "c"}
local index, value = next(tab) => 1 "a"
local index, value = next(tab, 2) => 3 "c"

-- as an iterator:
for k, v in next, tab, nil do
  printf("%s = %s", k, v)
end

pairs(t)

Returns three values: the next function, the table t, and nil, so that the construction
for k, v in pairs(t) do body end
will iterate over all key–value pairs of table t. See function next for the caveats of modifying the table during its traversal.

pcall(f, arg1, ...)

Calls function f with the given arguments in protected mode. This means that any error inside f is not propagated; instead, pcall catches the error and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In such case, pcall also returns all results from the call, after this first result. In case of any error, pcall returns false plus the error message.
function vector_to_string(v)
  return string.format("%f,%f,%f", v.x, v.y, v.z)
end

function test(v)
  local result, data = pcall(vector_to_string, v)
  if result then
    printf("there were no errors: %s", data)
  else
    printf("error = %s", data)
  end
end

test(vector():set(1.23, 4.56, 7.89))
-- there were no errors: 1.230000,4.560000,7.890000

test("blablabla")
-- error = bad argument #2 to '?' (number expected, got nil)

print(...)

Receives any number of arguments, and prints their values to stdout, using the tostring function to convert them to strings. print is not intended for formatted output, but only as a quick way to show a value, typically for debugging. For formatted output, use string.format.

rawequal(v1, v2)

Checks whether v1 is equal to v2, without invoking any metamethod. Returns a boolean.

rawget(table, index)

Gets the real value of table[index], without invoking any metamethod. table must be a table; index may be any value.

rawset(table, index, value)

Sets the real value of table[index] to value, without invoking any metamethod. table must be a table, index any value different from nil, and value any Lua value. This function returns table.

select(selector, list)

If selector is equal to "#" function returns number of arguments passed to select as list. If selector is any integer number (except 0) then function returns all arguments from list starting from selector to the last argument from list. Usually, function is utilised in functions which expect not specified number of arguments (...)
function print_info(...)
  local arg_num = select("#", ...)
  if arg_num == 3 then
    local age = select(1, ...)
    local name, faction = select(2, ...)
    printf("%s from %s age %d", name, faction, age)
  else
    printf("expected 3 arguments, got %d", arg_num)
  end
end

print_info(31, "wolf", "loners") => wolf from loners age 31
print_info() => expected 3 arguments, got 0

setfenv(f, table)

Sets the environment to be used by the given function. f can be a Lua function or a number that specifies the function at that stack level: Level 1 is the function calling setfenv. setfenv returns the given function. As a special case, when f is 0 setfenv changes the environment of the running thread. In this case, setfenv returns no values.

setmetatable(table, metatable)

Sets the metatable for the given table. (You cannot change the metatable of other types from Lua, only from C.) If metatable is nil, removes the metatable of the given table. If the original metatable has a "__metatable" field, raises an error. This function returns table.

tonumber(e [, base])

Tries to convert its argument to a number. If the argument is already a number or a string convertible to a number, then tonumber returns this number; otherwise, it returns nil. An optional argument specifies the base to interpret the numeral. The base may be any integer between 2 and 36, inclusive. In bases above 10, the letter 'A' (in either upper or lower case) represents 10, 'B' represents 11, and so forth, with 'Z' representing 35. In base 10 (the default), the number can have a decimal part, as well as an optional exponent part. In other bases, only unsigned integers are accepted.

tostring(e)

Receives an argument of any type and converts it to a string in a reasonable format. For complete control of how numbers are converted, use string.format. If the metatable of e has a "__tostring" field, then tostring calls the corresponding value with e as argument, and uses the result of the call as its result.

type(v)

Returns the type (coded as a string) of its only argument. The possible results of this function are "nil" (a string, not the value nil), "number", "string", "boolean", "table", "function", "thread", and "userdata".
type(nil) => "nil"
type(5) => "number"
type(1.234) => "number"
type("some text") => "string"
type(false) => "boolean"
type(true) => "boolean"
type({}) => "table"
type(function() end) => "function"
type(db.actor) => "userdata"

unpack(list [, i [, j]])

Returns the elements from the given table (list). This function is equivalent to
return list[i], list[i+1], ..., list[j]
except that the above code can be written only for a fixed number of elements. By default, i is 1 and j is the length of the list, as defined by the length operator.
local tab = {1, "two", 3, "four", 5}
unpack(tab) => 1, "two", 3, "four", 5
unpack(tab, 1, 5) => 1, "two", 3, "four", 5
unpack(tab, 1, 2) =>  => 1, "two"
unpack(tab, 4, 7) =>  => "four", 5, nil, nil

xpcall(f, err)

This function is similar to pcall, except that you can set a new error handler. xpcall calls function f in protected mode, using err as the error handler. Any error inside f is not propagated; instead, xpcall catches the error, calls the err function with the original error object, and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In this case, xpcall also returns all results from the call, after this first result. In case of any error, xpcall returns false plus the result from err.
function vector_to_string(v)
  return string.format("%f,%f,%f", v.x, v.y, v.z)
end

function error_handler(err_msg)
  printf("got error: %s", err_msg)
  return true
end

local result, data = xpcall(vector_to_string, error_handler)
if result then
  printf("there were no errors")
else
  printf("error handled = %s", tostring(data))
end

-- got error: .... - shadow of chernobyl\gamedata\scripts\temp.script:3: attempt to index local 'v' (a nil value)
-- error handled = true

coroutine.coco

This field is set to true if CoCo (True C Coroutines for Lua) is present; nil otherwise
CoCo Documentation

coroutine.create(func)

Creates a new coroutine, with body func. func must be a Lua function. Returns this new coroutine, an object with type "thread".

coroutine.cstacksize([newdefault])

Sets the default C stack size.

coroutine.resume(co [, val1, ...])

Starts or continues the execution of coroutine co. The first time you resume a coroutine, it starts running its body. The values val1, ... are passed as the arguments to the body function. If the coroutine has yielded, resume restarts it; the values val1, ... are passed as the results from the yield. If the coroutine runs without any errors, resume returns true plus any values passed to yield (if the coroutine yields) or any values returned by the body function (if the coroutine terminates). If there is any error, resume returns false plus the error message.

coroutine.running()

Returns the running coroutine, or nil when called by the main thread.

coroutine.status(co)

Returns the status of coroutine co, as a string: "running", if the coroutine is running (that is, it called status); "suspended", if the coroutine is suspended in a call to yield, or if it has not started running yet; "normal" if the coroutine is active but not running (that is, it has resumed another coroutine); and "dead" if the coroutine has finished its body function, or if it has stopped with an error.

coroutine.wrap(func)

Creates a new coroutine, with body func. func must be a Lua function. Returns a function that resumes the coroutine each time it is called. Any arguments passed to the function behave as the extra arguments to resume. Returns the same values returned by resume, except the first boolean. In case of error, propagates the error.

coroutine.yield(...)

Suspends the execution of the calling coroutine. The coroutine cannot be running a C function, a metamethod, or an iterator. Any arguments to yield are passed as extra results to resume.

math.abs(x)

Returns the absolute value of x.

math.acos(x)

Returns the arc cosine of x (in radians).

math.asin(x)

Returns the arc sine of x (in radians).

math.atan(x)

Returns the arc tangent of x (in radians).

math.atan2(y, x)

Returns the arc tangent of y/x (in radians), but uses the signs of both parameters to find the quadrant of the result. (It also handles correctly the case of x being zero.)

math.ceil(x)

Returns the smallest integer larger than or equal to x.

math.cos(x)

Returns the cosine of x (assumed to be in radians).

math.cosh(x)

Returns the hyperbolic cosine of x.

math.deg(x)

Returns the angle x (given in radians) in degrees.

math.exp(x)

Returns the value ex.

math.floor(x)

Returns the largest integer smaller than or equal to x.

math.fmod(x, y)

Returns the remainder of the division of x by y. You can also use the expression x % y to compute this value. Modulo is defined as:
x % y = x - math.floor(x / y) * y

math.frexp(x)

Returns m and e such that x = m2e, e is an integer and the absolute value of m is in the range [0.5, 1) (or zero when x is zero).

math.huge

Value larger than or equal to any other numerical value (infinity).

math.ldexp(m, e)

Returns m2e (e should be an integer).

math.log(x)

Returns the natural logarithm of x.

math.log10(x)

Returns the base-10 logarithm of x.

math.max(x, ...)

Returns the maximum value among its arguments.

math.min(x, ...)

Returns the minimum value among its arguments.

math.mod(x, y)

see math.fmod

math.modf(x)

Returns two numbers, the integral part of x and the fractional part of x.

math.pi

The value of pi (3.1415926535898).

math.pow(x, y)

Returns xy. (You can also use the expression x ^ y to compute this value.)

math.rad(x)

Returns the angle x (given in degrees) in radians.

math.random([m [,n]])

This function is an interface to the simple pseudo-random generator function rand provided by ANSI C. (No guarantees can be given for its statistical properties.) When called without arguments, returns a uniform pseudo-random real number in the range [0,1). When called with an integer number m, math.random returns a uniform pseudo-random integer in the range [1, m]. When called with two integer numbers m and n, math.random returns a uniform pseudo-random integer in the range [m, n].

math.randomseed(x)

Sets x as the "seed" for the pseudo-random generator: equal seeds produce equal sequences of numbers.

math.sin(x)

Returns the sine of x (assumed to be in radians).

math.sinh(x)

Returns the hyperbolic sine of x.

math.sqrt(x)

Returns the square root of x. (You can also use the expression x ^ 0.5 to compute this value.)

math.tan(x)

Returns the tangent of x (assumed to be in radians).

math.tanh(x)

Returns the hyperbolic tangent of x.

string.byte(s [, i [, j]])

Returns the internal numerical codes of the characters s[i], s[i+1], ..., s[j]. The default value for i is 1; the default value for j is i.
local a = string.byte("abc") -- 97
local ab = string.byte("abc", 1, 2) -- 97 98
local c = string.byte("abc", -1) -- 99
local abc = string.byte("abc", 1, -1) -- 97 98 99

string.char(...)

Generate a string representing the character codes passed as arguments.
local abc = string.char(97, 98, 99) -- abc

string.dump(function)

Returns a string containing a binary representation of the given function, so that a later loadstring on this string returns a copy of the function. function must be a Lua function without upvalues.
local bin = string.dump(function(msg) printf("hello %s", msg) end)
loadstring(bin)("world") -- hello world

string.find(s, pattern [, init [, plain]])

Looks for the first match of pattern in the string s. If it finds one, then find returns the indices of s where this occurrence starts and ends; otherwise, it returns nil. A third, optional numerical argument init specifies where to start the search; its default value is 1 and can be negative (searching backward). A value of true as a fourth, optional argument plain turns off the pattern matching facilities, so the function does a plain "find substring" operation, with no characters in pattern being considered "magic". Note that if plain is given, then init must be given as well. If the pattern has captures, then in a successful match the captured values are also returned, after the two indices.
string.find("hello big word", "big") -- 7 9
string.find("hello big word", "%sbig%s") -- 6 10
string.find("hello big word", "%sbig%s", 1, true) -- nil
string.find("hello%sbig%sword", "%sbig%s", 1, true) -- 6 12

string.format(frm, ...)

Function for formatting strings, typically for output. Returns a formatted version of given string (frm). The format string (frm) is composed of regular text and directives, which control where and how each argument must be placed in the formatted string. A simple directive is the character % plus a letter that tells how to format the argument:
  • d or i - signed decimal number
  • u - unsigned decimal number
  • x or X - hexadecimal using small/capital letters
  • o - octal
  • f - floating-point number
  • e or E - floating-point number in exponential notation (small/capital letters)
  • c - single character
  • s - strings
  • q - puts quots around string
Between the % and the letter, a directive can include other options, which control the details of the format, such as the number of decimal digits of a floating-point number, etc.
string.format("%.4f", math.pi) -- 3.1416
string.format("0x%.x", -30) -- 0xffffffe2
string.format("%.02d", 5) -- 05
string.format("%c", 97) -- a

gfind(s, pat)

Returns an iterator function that, each time it is called, returns the next captures from pattern pat over string s. If pat specifies no captures, then the whole match is produced in each call.
-- pattern %a specifies no captures; gfind iterates over all
-- the words from string s, printing each word per line
local s = "hello world from Lua"
for w in string.gfind(s, "%a+") do
  print(w)
end

-- pattern (%w+)=(%w+) specifies two captures
-- which collects and prints pairs key=value
local s = "from=world, to=Lua"
for k, v in string.gfind(s, "(%w+)=(%w+)") do
  printf("%s = %s", k, v)
end

string.gmatch(s, pattern)

Returns an iterator function that, each time it is called, returns the next captures from pattern over string s. If pattern specifies no captures, then the whole match is produced in each call.

string.gsub(s, pat, repl [, n])

Returns a copy of s in which all occurrences of the pattern pat have been replaced by a replacement string specified by repl. gsub also returns, as a second value, the total number of substitutions made. If repl is a string, then its value is used for replacement. Any sequence in repl of the form %n, with n between 1 and 9, stands for the value of the n-th captured substring. If repl is a function, then this function is called every time a match occurs, with all captured substrings passed as arguments, in order; if the pattern specifies no captures, then the whole match is passed as a sole argument. If the value returned by this function is a string, then it is used as the replacement string; otherwise, the replacement string is the empty string. The optional last parameter n limits the maximum number of substitutions to occur. For instance, when n is 1 only the first occurrence of pat is replaced.
string.gsub("hello world", "(%w+)", "%1 %1") -- hello hello world world
string.gsub("hello world", "(%w+)", "%1 %1", 1) -- hello hello world
string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1") -- world hello Lua from
string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function(s) return loadstring(s)() end) --> 4+5 = 9

string.len(s)

Receives a string and returns its length. The empty string has length 0. Embedded zeros are counted, so "a\000b\000c" has length 5.

string.lower(s)

Receives a string and returns a copy of that string with all uppercase letters changed to lowercase. All other characters are left unchanged.

string.match(s, pattern [, init])

Looks for the first match of pattern in the string s. If it finds one, then match returns the captures from the pattern; otherwise it returns nil. If pattern specifies no captures, then the whole match is returned. A third, optional numerical argument init specifies where to start the search; its default value is 1 and can be negative.

string.rep(s, n)

Returns a string that is the concatenation of n copies of the string s.

string.reverse(s)

Returns a string that is the string s reversed.

string.sub(s, i [, j])

Returns the substring of s that starts at i and continues until j; i and j can be negative. If j is absent, then it is assumed to be equal to -1 (which is the same as the string length). In particular, the call string.sub(s, 1, j) returns a prefix of s with length j, and string.sub(s, -i) returns a suffix of s with length i.

string.upper(s)

Receives a string and returns a copy of that string with all lowercase letters changed to uppercase. All other characters are left unchanged.

table.concat(table [, sep [, i [, j]]])

Given an array where all elements are strings or numbers, returns table[i]..sep..table[i+1] ... sep..table[j]. The default value for sep is the empty string, the default for i is 1, and the default for j is the length of the table. If i is greater than j, returns the empty string.
local tab = {1, 2, 3, 4, 5}

table.concat(tab) => 12345
table.concat(tab, ", ") => 1, 2, 3, 4, 5

table.foreach(table, func)

Note: this function is deprecated in Lua 5.1, but it can still be useful for printing out a table. You should use the pairs() operator instead.

Executes the given func over all elements of table. For each element, the function is called with the index and respective value as arguments. If the function returns any non-nil value, then the loop is broken, and this value is returned as the final value of foreach. The behavior of foreach is undefined if you change the table during the traversal. This function could be defined in Lua:
function foreach(t, f)
  for i, v in t do
    local res = f(i, v)
    if res then
      return res
    end
  end
end

function print_tab_elem(index, value)
  printf("[%d]:%s ", index, value)
  return nil -- optional in this case
end

local tab = {"a", "b", "c"}
table.foreach(tab, print_tab_elem) => [1]:a [2]:b [3]:c

table.foreachi(table, func)

Note: this function is deprecated in Lua 5.1, but it can still be useful for printing out a table. You should use the ipairs() operator instead.

Executes the given func over the numerical indices of table. For each index, the function is called with the index and respective value as arguments. Indices are visited in sequential order, from 1 to n, where n is the result of getn(table). If the function returns any non-nil value, then the loop is broken, and this value is returned as the final value of foreachi. This function could be defined in Lua:
function foreachi(t, f)
  for i = 1, getn(t) do
    local res = f(i, t[i])
    if res then
      return res
    end
  end
end

table.getn(table)

Returns the size of a table that is the largest numerical index with a non-nil value in the table. For simple arrays you can use symbol #tab, for associative arrays count pairs key-value
-- "simle" array
local tab = {10, 9, 8, 7}

printf("table size = %d", table.getn(tab)) -- 4
printf("table size = %d", #tab) -- 4

-- associative array
local tab = {a = 1, b = 2, c = 3}

function getn(t)
  local c = 0
  for k, v in pairs(t) do
    c = c + 1
  end
  return c
end

printf("table size = %d", table.getn(tab)) -- 0
printf("table size = %d", #tab) -- 0
printf("table size = %d", getn(tab)) -- 3

table.insert(table, [pos,] value)

Inserts element value at position pos in table, shifting up other elements to open space, if necessary. The default value for pos is n + 1, where n is the length of the table, so that a call table.insert(t, x) inserts x at the end of table t.

table.maxn(table)

Returns the largest positive numerical index of the given table, or zero if the table has no positive numerical indices. (To do its job this function does a linear traversal of the whole table.)

table.remove(table [, pos])

Removes from table the element at position pos, shifting down other elements to close the space, if necessary. Returns the value of the removed element. The default value for pos is n, where n is the length of the table, so that a call table.remove(t) removes the last element of table t.

table.setn(table, n)

Function to explicitly set the size n of a table. This function is obsolete in Lua 5.1 however it still works in Stalker Shadow of Chernobyl 1.0004
-- in stalker:
local tab = {}
table.setn(tab, 10)
printf("tab size = %d", table.getn(tab)) -- 10

-- lua interpreter:

> tab = {}
> table.setn(tab, 10)
stdin:1: 'setn' is obsolete
stack traceback:
  [C]: in function 'setn'
  stdin:1: in main chunk
  [C]: ?

table.sort(table [, comp])

Sorts table elements in a given order, in-place, from table[1] to table[n], where n is the length of the table. If comp is given, then it must be a function that receives two table elements, and returns boolean value. If comp is not given, then the standard Lua operator < is used instead.
local t = {5, 1, 3, 2, 4}
-- ascending by default
table.sort(t)
-- t = {1, 2, 3, 4, 5}

-- descending
table.sort(t, function(a, b) return a > b end)
-- t = {5, 4, 3, 2, 1}


lua_help act

class act

Specifies action for mutants. See: scripted actions
C++ class act
{
  -- EScriptMonsterGlobalAction:
  const rest = 0;
  const eat = 1;
  const attack = 2;
  const panic = 3;

  act();
  act(enum act::EScriptMonsterGlobalAction);
  act(enum act::EScriptMonsterGlobalAction, game_object*);
};

act::act(t_act, gobj)

act::act(t_act)

act::act()

purpose:
object constructor
arguments:

  • t_act <const>

    -

    EScriptMonsterGlobalAction

    flag

  • gobj <game_object>

    - object
returns:

  • <act>

    - object
example:
action(dog, act(act.attack, db.actor), cond(cond.time_end, 10000))
action(dog, act(act.eat, nil), cond(cond.time_end, 2000))


lua_help actor_stats

actor_stats.add_to_ranking(object_id)

purpose:
function adds stalker to ranking system (system of points given for tasks, killed enemies, etc.)
arguments:

  • object_id <number>

    - stalker's id

actor_stats.remove_from_ranking(object_id)

purpose:
function removes stalker from ranking system
arguments:

  • object_id <number>

    - stalker's id

actor_stats.get_actor_ranking()

purpose:
function returns current actor position in ranking list (PDA -> Ranking)
returns:

  • <number>

    - current position in ranking system
remarks:
  • ranking list in PDA (PDA -> Ranking) counts from 561 (last position, bottom) to 0 (first on the list)
  • if you start new game, actor is set at the bottom position (561) on the list
  • function utilised only for actor
example:
-- 561 in new game (last place on ranking list)
local curr_pos = actor_stats.get_actor_ranking()

-- set rank to master
db.actor:set_character_rank(db.actor:character_rank() + 1000)

-- 0 (first place on ranking list)
local curr_pos = actor_stats.get_actor_ranking()

actor_stats.get_points(points_type)

purpose:
function returns total points of given type
arguments:

  • points_type <const> stalkerkills|monsterkills|quests

    - type of points
remarks:
  • function utilised only for actor
example:
local q = actor_stats.get_points("quests")
printf("points for completed quests = %d", q)

local s = actor_stats.get_points("stalkerkills")
printf("points for killed stalkers = %d", s)

local m = actor_stats.get_points("monsterkills")
printf("points for killed mutants = %d", m)

actor_stats.add_points(points_type, id, coefficient, quantity)

purpose:
function add points
arguments:

  • points_type <const> stalkerkills|monsterkills|quests

    - type of points

  • id <string>

    - this is for pda (actor stats); can be task id (quests) or stalker/monster id (stalkerkills/monsterkills)

  • coefficient <number>

    - points multiplier

  • quantity <number>

    - how many points
remarks:
  • points are calculated as coefficient2 * quantity (?)
  • function utilised only for actor
example:
-- add 4 points for killing zombified stalker (master)
actor_stats.add_points("stalkerkills", "zombied_master", 1, 4)

-- add 3 points for killing bloodsucker (strong)
actor_stats.add_points("monsterkills", "bloodsucker_strong", 1, 3)

-- add 5 points for completing task
actor_stats.add_points("quests", "tm_eliminate_camp_1", 1, 5)

-- add 45 points for completing task
actor_stats.add_points("quests", "tm_eliminate_camp_2", 3, 5)

actor_stats.add_points_str(string, string, string)

purpose:
function add points
arguments:

  • <string>

    - ???

  • <string>

    - ???

  • <string>

    - ???


lua_help alife_simulator

class alife_simulator

Performs basic operations on server entities. To get instance of

alife_simulator

use function

alife()

C++ class alife_simulator
{
  function level_name(const alife_simulator*, number);
  function level_id(alife_simulator*);

  function add_out_restriction(alife_simulator*, cse_alife_monster_abstract*, number);
  function remove_out_restriction(alife_simulator*, cse_alife_monster_abstract*, number);
  function add_in_restriction(alife_simulator*, cse_alife_monster_abstract*, number);
  function remove_in_restriction(alife_simulator*, cse_alife_monster_abstract*, number);
  function remove_all_restrictions(number, const enum RestrictionSpace::ERestrictorTypes&);

  function dont_has_info(const alife_simulator*, const number&, string);
  function has_info(const alife_simulator*, const number&, string);

  function set_interactive(number, boolean);
  function switch_distance() const;
  function switch_distance(number);
  function set_switch_online(number, boolean);
  function set_switch_offline(number, boolean);

  function object(const alife_simulator*, number);
  function object(const alife_simulator*, string);
  function object(const alife_simulator*, number, boolean);

  function valid_object_id(const alife_simulator*, number);
  function actor(const alife_simulator*);
  function story_object(const alife_simulator*, number);
  function spawn_id(alife_simulator*, number);

  function release(alife_simulator*, cse_abstract*, boolean);
  function kill_entity(cse_alife_monster_abstract*, const number&, cse_alife_schedulable*);
  function kill_entity(alife_simulator*, cse_alife_monster_abstract*, const number&);
  function kill_entity(alife_simulator*, cse_alife_monster_abstract*);

  function create(alife_simulator*, number);
  function create(alife_simulator*, string, const vector&, number, number, number);
  function create(alife_simulator*, string, const vector&, number, number);
  function create_ammo(alife_simulator*, string, const vector&, number, number, number, number);
};

---------------------------------------------------------------------------------------------------------

local a_sim = alife()

alife_simulator::actor()

purpose:
returns actor entity (server object)
returns:

  • cse_alife_creature_actor*

    - actor server entity

alife_simulator::level_id()

purpose:
returns current level id
returns:

  • <number>

    - current level id

alife_simulator::level_name(level_id)

purpose:
returns level name; this method is usually utilised to figure out where certain object currently is (at which level)
arguments:

  • level_id

    - level id
returns:

  • <string>

    - level name
example:
local gvid = alife():actor().m_game_vertex_id

-- to get current level name
local lname = alife():level_name(game_graph():vertex(gvid):level_id())
local lname = alife():level_name(alife():level_id())

-- or in this case, simply:
local lname = level.name()

alife_simulator::has_info(npc_id, info_name)

purpose:
checks whether npc has infoportion
arguments:

  • npc_id <number>

    - npc id

  • info_name <string>

    - infoportion name
returns:

  • true

    - npc has info

  • false

    - otherwise
remarks:
  • if you are interested in giving infoportions for npcs, use known_info
  • use function has_alife_info() from

    _g.script

    instead
example:
alife():has_info(db.actor:id(), "storyline_actor_start")

-- or in this case:
has_alife_info("storyline_actor_start")

alife_simulator::dont_has_info(npc_id, info_name)

purpose:
checks whether npc doesn't have infoportion
arguments:

  • npc_id <number>

    - npc id

  • info_name <string>

    - infoportion name
returns:

  • true

    - npc doesn't have infoportion

  • false

    - otherwise
remarks:
  • if you are interested in giving infoportions for npcs, use known_info
  • use function has_alife_info() from

    _g.script

    instead
example:
alife():dont_has_info(db.actor:id(), "storyline_actor_start")

-- or:
not alife():has_info(db.actor:id(), "storyline_actor_start")

-- or in this case:
not has_alife_info("storyline_actor_start")

alife_simulator::story_object(story_id)

purpose:
returns server entity
arguments:

  • story_id <number>

    - object's story id
returns:

  • server object

    - if object with given story id exists

  • nil

    - otherwise

alife_simulator::object(obj_id)

alife_simulator::object(obj_name)

available only in SOC (?)

alife_simulator::object(obj_id, noidea)

purpose:
returns server entity
arguments:

  • obj_id <number>

    - object id

  • obj_name <string>

    - object name

  • noidea <boolean>

    - ???
returns:

  • server object

    - if object exists

  • nil

    - otherwise

alife_simulator::spawn_id(obj_id)

purpose:
returns object spawn id
arguments:

  • obj_id <number>

    - object id
returns:

  • <number>

    - object spawn id

alife_simulator::valid_object_id(id)

purpose:
checks whether given id is valid (being utilised by game)
arguments:

  • id <number>

    - id
returns:

  • true

    - id is valid

  • false

    - otherwise
example:
printf("valid[0] = %s", tostring(alife():valid_object_id(0))) -- true
printf("valid[65530] = %s", tostring(alife():valid_object_id(65530))) -- true
printf("valid[65535] = %s", tostring(alife():valid_object_id(65535))) -- false
printf("valid[-1] = %s", tostring(alife():valid_object_id(-1))) -- false
printf("valid[-10] = %s", tostring(alife():valid_object_id(-10))) -- true (???)
printf("valid[2.3] = %s", tostring(alife():valid_object_id(2.3))) -- true (???)

alife_simulator::set_interactive(obj_id, flag)

purpose:
sets/unsets object interactive flag
arguments:

  • obj_id <number>

    - object id

  • flag <boolean>

    - set (

    true

    ) / unset (

    false

    ) interactive flag

alife_simulator::switch_distance()

alife_simulator::switch_distance(dist)

purpose:
returns/sets alife switch distance (radius)
arguments:

  • dist <number>

    - switch distance
remarks:
  • setting new switch distance causes CTD (tested in SOC)
  • returns value of field switch_distance in section alife (config\alife.ltx)
example:
local curr_dist = alife():switch_distance() -- 150 m (vanilla)
alife():switch_distance(200) -- set to 200 m -> CTD

alife_simulator::set_switch_online(obj_id, flag)

purpose:
moves object online
arguments:

  • obj_id <number>

    - object id

  • flag <boolean>

    - set/unset online flag
remarks:
  • this method is used in conjuction with set_switch_offline
example:
-- move object online
alife():set_switch_online(1234, true)
alife():set_switch_offline(1234, false)

alife_simulator::set_switch_offline(obj_id, flag)

purpose:
moves object offline
arguments:

  • obj_id <number>

    - object id

  • flag <boolean>

    - set/unset offline flag
remarks:
  • some objects can be switched offline only when certain distance between object and actor is preserved (for npcs - 15 m)
  • this method is used in conjuction with set_switch_online
example:
-- move object offline
alife():set_switch_offline(1234, true)
alife():set_switch_online(1234, false)

alife_simulator::add_out_restriction(obj, rest_id)

purpose:
adds OUT restrictor for given object
arguments:

  • obj <cse_alife_monster_abstract*>

    - object (mutant only ???)

  • rest_id <number>

    - restrictor id (???)
remarks:
  • restrictions are controlled by smart terrains
  • use add_restrictions instead

alife_simulator::remove_out_restriction(obj, rest_id)

purpose:
removes OUT restrictor for given object
arguments:

  • obj <cse_alife_monster_abstract*>

    - object (mutant only ???)

  • rest_id <number>

    - restrictor id (???)
remarks:
  • restrictions are controlled by smart terrains
  • use remove_restrictions instead

alife_simulator::add_in_restriction(obj, rest_id)

purpose:
adds IN restrictor for given object
arguments:

  • obj <cse_alife_monster_abstract*>

    - object (mutant only ???)

  • rest_id <number>

    - restrictor id (???)
remarks:
  • restrictions are controlled by smart terrains
  • use add_restrictions instead

alife_simulator::remove_in_restriction(obj, rest_id)

purpose:
removes IN restrictor for given object
arguments:

  • obj <cse_alife_monster_abstract*>

    - object (mutant only ???)

  • rest_id <number>

    - restrictor id (???)
remarks:
  • restrictions are controlled by smart terrains
  • use remove_restrictions instead

alife_simulator::remove_all_restrictions(obj_id, rest_type)

purpose:
removes restrictors for given object
arguments:

  • obj_id <number>

    - object id

  • rest_type <number>

    - type of restrictor to remove: 0[NONE], 1[OUT], 2[IN], 3[NOT A] (?)
remarks:
  • restrictions are controlled by smart terrains
  • use game_object::remove_all_restrictions instead

alife_simulator::create(section_name, pos, lvid, gvid, pid)

alife_simulator::create(section_name, pos, lvid, gvid)

alife_simulator::create(number)

purpose:
creates (spawns) object
arguments:

  • section_name <string>

    - section name

  • pos <vector>

    - spawn position

  • lvid <number>

    - level vertex id

  • gvid <number>

    - game vertex id

  • pid <number>

    - parent (owner) id
returns:

  • se_obj

    - server entity of spawned object

  • nil

    - if object could not be spawned
example:
-- spawn in actor's backpack:
local pos = db.actor:position()
local lvid = db.actor:level_vertex_id()
local gvid = db.actor:game_vertex_id()
local section = "bandage"
local se_obj = alife():create(section, pos, lvid, gvid, db.actor:id())
if se_obj then
  printf("%s spawned in actor inventory", section)
end

-- spawn at given position:
local pos = vector():set(1.23, 4.56, 7.89)
local lvid, gvid = 12345, 678
local section = "bandage"
alife():create(section, pos, lvid, gvid)

alife_simulator::create_ammo(section_name, pos, lvid, gvid, pid, quantity)

purpose:
creates (spawns) ammo
arguments:

  • section_name <string>

    - ammo section name

  • pos <vector>

    - spawn position

  • lvid <number>

    - level vertex id

  • gvid <number>

    - game vertex id

  • pid <number>

    - parent (owner) id

  • quantity <number>

    - how many rounds to spawn
returns:

  • se_obj

    - server entity of spawned object

  • nil

    - if object could not be spawned
example:
-- se_respawn.script
function create_ammo(section, position, lvi, gvi, pid, num)
  local num_in_box = system_ini():r_u32(section, "box_size")
  while num > num_in_box do
    alife():create_ammo(section, position, lvi,	gvi, pid, num_in_box)
    num = num - num_in_box
  end
  alife():create_ammo(section, position, lvi,	gvi, pid, num)
end

alife_simulator::release(obj, flag)

purpose:
removes given object
arguments:

  • obj <cse_abstract*>

    - object to remove

  • flag <boolean>

    - ???

alife_simulator::kill_entity(obj, killer_id, noidea)

alife_simulator::kill_entity(obj, killer_id)

alife_simulator::kill_entity(obj)

purpose:
kills offline (server) object
arguments:

  • obj <cse_alife_monster_abstract*>

    - object (mutant only ???) to kill

  • killer_id <number>

    - killer id (???)

  • noidea <cse_alife_schedulable*>

    - ???


lua_help anim

class anim

Creates animation action for object. See: scripted actions
C++ class anim
{
  -- EMentalState:
  const danger = 0;
  const free = 1;
  const panic = 2;

  -- EScriptMonsterAnimAction (SOC):
  const stand_idle = 0;
  const sit_idle = 1;
  const lie_idle = 2;
  const eat = 3;
  const sleep = 4;
  const rest = 5;
  const attack = 6;
  const look_around = 7;
  const turn = 8;

  -- EScriptMonsterAnimAction (COP):
  const stand_idle = 0;
  const capture_prepare = 1;
  const sit_idle = 2;
  const lie_idle = 3;
  const eat = 4;
  const sleep = 5;
  const rest = 6;
  const attack = 7;
  const look_around = 8;
  const turn = 9;

  anim();
  anim(string);
  anim(string, boolean);
  anim(enum anim::EMentalState);
  anim(enum anim::EScriptMonsterAnimAction, number);

  function completed();
  function type(enum anim::EMentalState);
};

anim::anim(a_act, noidea1)

anim::anim(a_name, noidea2)

anim::anim(a_type)

anim::anim(a_name)

anim::anim()

purpose:
object constructor
arguments:

  • a_act <const>

    -

    EScriptMonsterAnimAction

    flag

  • noidea1 <number>

    - probably it is index of animation, if object has several animations, for instance: sit_idle_0, sit_idle_1

  • a_name <string>

    - animation name

  • noidea2 <boolean>

    - looped (?)

  • a_type <const>

    -

    EMentalState

    flag
returns:

  • <anim>

    - object
example:
action(dog, anim(anim.attack, 0), cond(cond.time_end, 50000))

action(dog, anim(anim.stand_idle, 0), look(look.point, db.actor:position()), cond(cond.look_end))
action(dog, anim("sit_idle_0"), look(look.point, db.actor:position()), cond(cond.look_end))

anim::completed()

purpose:
checks whether animation was completed (finished); probably called by

entity_action:completed()

(see: scripted actions)
returns:

  • true

    - animation completed

  • false

    - otherwise

anim::type(a_type)

purpose:
sets animation type (

EMentalState

)
arguments:

  • a_type <const>

    -

    EMentalState

    flag


lua_help CConsole

class CConsole

Executes console commands. To get pointer to

CConsole

use

get_console()

function.
C++ class CConsole
{
  function execute_script(string);
  function get_string(string);
  function execute(string);
  function execute_deferred(string);
  function get_bool(CConsole*, string);
  function get_float(CConsole*, string);
  function get_integer(CConsole*, string);
  function get_token(string);
  function show();
  function hide();
};

-------------------------------------------

local c = get_console()

CConsole::show()

purpose:
shows console window

CConsole::hide()

purpose:
hides console window

CConsole::execute(cmd)

purpose:
executes console command
arguments:

  • cmd <string>

    - console command
example:
local c = get_console()
c:execute("g_game_difficulty gd_master")

CConsole::execute_deferred(cmd)

purpose:
executes (with some delay) console command; available only in COP
arguments:

  • cmd <string>

    - console command
example:
local c = get_console()
c:execute_deferred("main_menu off")

CConsole::execute_script(fname)

purpose:
executes ltx file which contains console commands (for instance: user.ltx)
arguments:

  • fname <string>

    - file name
example:
-- file: C:\Documents and Settings\All Users\Dokumenty\STALKER-SHOC\hide.ltx
hud_crosshair off
hud_crosshair_dist off
hud_info off
hud_weapon off

-- file: C:\Documents and Settings\All Users\Dokumenty\STALKER-SHOC\show.ltx
hud_crosshair on
hud_crosshair_dist on
hud_info on
hud_weapon on

local c = get_console()
c:execute_script("hide.ltx") -- hide hud indicators
c:execute("screenshot 1") -- make screenshot
c:execute_script("hide.ltx") -- show hud indicators

-- or:
local c = get_console()
-- hide hud indicators
c:execute("hud_crosshair off")
c:execute("hud_crosshair_dist off")
c:execute("hud_info off")
c:execute("hud_weapon off")
-- make screenshot
c:execute("screenshot 1")
-- show hud indicators
c:execute("hud_crosshair on")
c:execute("hud_crosshair_dist on")
c:execute("hud_info on")
c:execute("hud_weapon on")

CConsole::get_string(cmd)

purpose:
returns console command value
arguments:

  • cmd <string>

    - console command
returns:

  • <string>

    - console command value
example:
local v = get_console():get_string("rs_stats")
printf("rs_stats is %s", v) -- off

get_console():execute("rs_stats on")
v = get_console():get_string("rs_stats")
printf("rs_stats is %s", v) -- on

CConsole::get_float(cmd)

purpose:
returns console command value
arguments:

  • cmd <string>

    - console command
returns:

  • <float>

    - console command value
example:
local v = get_console():get_float("mouse_sens")
printf("mouse_sens = %f", v) -- for instance: 0.32

CConsole::get_integer(cmd)

purpose:
returns console command value
arguments:

  • cmd <string>

    - console command
returns:

  • <int>

    - console command value
example:
local v = get_console():get_integer("mm_net_srv_maxplayers")
printf("max players = %d", v) -- 32

CConsole::get_bool(cmd)

purpose:
returns console command value
arguments:

  • cmd <string>

    - console command
returns:

  • <boolean>

    - console command value
example:
local v = get_console():get_bool("rs_stats")
printf("rs_stats = %s", tostring(v)) -- nil

get_console():execute("rs_stats on")
v = get_console():get_bool("rs_stats")
printf("rs_stats = %s", tostring(v)) -- true

CConsole::get_token(cmd)

purpose:
returns console command value
arguments:

  • cmd <string>

    - console command
returns:

  • <string>

    - console command value
example:
local v = get_console():get_token("rs_stats")
printf("rs_stats is %s", v) -- off


lua_help CGameFont

class CGameFont

Specifies constants for text aligning.
C++ class CGameFont
{
  const alLeft = 0;
  const alRight = 1;
  const alCenter = 2;
};
List of available fonts (in SOC):
  • GetFontGraffiti19Russian()
  • GetFontGraffiti22Russian()
  • GetFontGraffiti32Russian()
  • GetFontGraffiti50Russian()
  • GetFontLetterica16Russian()
  • GetFontLetterica18Russian()
  • GetFontLetterica25()
  • GetFontMedium()
  • GetFontSmall()
  • GetFontDI()
local label = CUIStatic()
label:SetAutoDelete(true)
label:SetWindowName("label")
label:Init(20, 50, 300, 16)
label:SetFont(GetFontLetterica16Russian())
label:SetTextAlign(CGameFont.alLeft)
label:SetTextColor(255, 255, 255, 255)
label:SetText("some text")

wnd:AttachChild(label)


lua_help CGameGraph GameGraph__CVertex GameGraph__LEVEL_MAP__value_type

class GameGraph__CVertex

Stores info about graph point. To get object of this class use:

game_graph():vertex(gvid)

where gvid is game vertex id (graph point id).
C++ class GameGraph__CVertex
{
  function level_vertex_id() const;
  function game_point(const GameGraph__CVertex*);
  function level_id() const;
  function level_point(const GameGraph__CVertex*);
};

GameGraph__CVertex::level_vertex_id()

purpose:
returns level vertex id
returns:

  • <number>

    - level vertex id
local gvid = db.actor:game_vertex_id()
local gg_cvertex = game_graph():vertex(gvid)

local lvid = gg_cvertex:level_vertex_id()

GameGraph__CVertex::game_point()

purpose:
returns game vertex (graph point) position
returns:

  • <vector>

    - game vertex position
function vector_to_string(v)
  return string.format("%f,%f,%f", v.x, v.y, v.z)
end

local gvid = db.actor:game_vertex_id()
local gg_cvertex = game_graph():vertex(gvid)
local gp = gg_cvertex:game_point()

printf("gp = %s", vector_to_string(gp))

GameGraph__CVertex::level_id()

purpose:
returns level id (where this graph point exist)
returns:

  • <number>

    - level id
example:
local gvid = db.actor:game_vertex_id()
local gg_cvertex = game_graph():vertex(gvid)
local level_id = gg_cvertex:level_id()

local lname = alife():level_name(level_id)
printf("actor is on %s level", lname)

GameGraph__CVertex::level_point()

purpose:
returns position of level vertex id
returns:

  • <vector>

    - level vertex position
example:
function vector_to_string(v)
  return string.format("%f,%f,%f", v.x, v.y, v.z)
end

local gvid = db.actor:game_vertex_id()
local gg_cvertex = game_graph():vertex(gvid)
local lp = gg_cvertex:level_point()

printf("lp = %s", vector_to_string(lp))

-- or:
local gg_lvid = level.vertex_position(gg_cvertex:level_vertex_id())
printf("gg_lvid = %s", vector_to_string(lp))

-- difference between lp and gg_lvid: ~ <0.1, 0.1, 0.1>

class CGameGraph

Represents graph point. To get graph point use

game_graph()

function.
C++ class CGameGraph
{
  function vertex_id(const GameGraph__CVertex*) const;
  function accessible(const CGameGraph*, const number&);
  function accessible(const CGameGraph*, const number&, boolean);
  function vertex(const number&) const;
  function valid_vertex_id(const number&) const;
  function levels(const CGameGraph*);
};

CGameGraph::vertex_id(gg_cvertex)

purpose:
extracts graph point id (game vertex id) from given GameGraph__CVertex
arguments:

  • gg_cvertex <GameGraph__CVertex>

    - GameGraph__CVertex
returns:

  • <number>

    - graph point id (game vertex id)
example:
local gvid = db.actor:game_vertex_id()
local gg_cvertex = game_graph():vertex(gvid)
local gg_gvid = game_graph():vertex_id(gg_cvertex)

if gvid == gg_gvid then
  printf("the same ...") -- the same ...
end

CGameGraph::accessible(gvid, status)

CGameGraph::accessible(gvid)

purpose:
enables/disables graph point; checks whether graph point is accessible
arguments:

  • gvid <number>

    - graph point id (game vertex id)

  • status <boolean>

    - enable (

    true

    ) or disable (

    false

    )
returns:

  • true

    - graph point is accessible (enabled)

  • false

    - otherwise
example:
local gp_name = "mil_graph_zabor_changer_1"
local gp_gvid = patrol(gp_name):game_vertex_id(0)

-- disable graph point
game_graph():accessible(gp_gvid, false)

if game_graph():accessible(gp_gvid) == false then
  -- enable graph point
  game_graph():accessible(gp_gvid, true)
end

CGameGraph::vertex(gvid)

purpose:
returns GameGraph__CVertex
arguments:

  • gvid <number>

    - graph point id (game vertex id)
returns:

  • <GameGraph__CVertex>

    - GameGraph__CVertex
example:
local gvid = db.actor:game_vertex_id()
local gg_cvertex = game_graph():vertex(gvid)

CGameGraph::valid_vertex_id(gvid)

purpose:
checks whether given number (gvid) is being used by game as graph point id (game vertex id)
arguments:

  • gvid <number>

    - possible graph point id (game vertex id)
returns:

  • true

    - game vertex id is valid (being used by game), or simply: graph point with gvid id exist

  • false

    - otherwise
example:
for i = 0, 5000 do
  if not game_graph():valid_vertex_id(i) then
    printf("there are %d graph points in soc", i - 1) -- 3511
    return
  end
end

CGameGraph::levels()

purpose:
iterator, returns collection of GameGraph__LEVEL_MAP__value_type; available only in COP
returns:

  • <GameGraph__LEVEL_MAP__value_type>

    - GameGraph__LEVEL_MAP__value_type
example:
local name = nil
for level in game_graph():levels() do
  name = alife():level_name(level.id)
  printf("id = %d, name = %s", level.id, name)
end

class GameGraph__LEVEL_MAP__value_type

Represents level (map); available only in COP. See: CGameGraph::levels
C++ class GameGraph__LEVEL_MAP__value_type
{
  property id;
  property level;
};


lua_help gg_scan

 Finds last (top) level vertex id and game vertex id for each level.
Author: bardak (?)
local top_game_vertex_ids = nil
local top_lvid = nil
local top_gvid = nil

local levels =
{
  "l01_escape", "l02_garbage", "l03_agroprom", "l03u_agr_underground",
  "l04_darkvalley", "l04u_LabX18", "l05_bar", "l06_rostok", "l07_military",
  "l08_yantar", "l10_radar", "l11_pripyat", "l12_stancia", "l12_stancia_2",
  "l12u_control_monolith", "l12u_sarcofag", "l08u_brainlab"
}

local function gg_backward_scan(gg, lid, gvid)
  while true do
    gvid = gvid - 1
    if gvid < 0 or gg:vertex(gvid):level_id() ~= lid then
      return gvid + 1
    end
  end
  abort("gg_backward_scan")
  return nil
end

local function fill_top_game_vertex_ids(gg)
  top_game_vertex_ids = {}
  local sim = alife()
  local lid, gvid = -1, 0
  while gg:valid_vertex_id(gvid) == true do
    if gg:vertex(gvid):level_id() ~= lid then
      lid = gg:vertex(gvid):level_id()
      top_game_vertex_ids[sim:level_name(lid)] = gg_backward_scan(gg, lid, gvid)
    end
    gvid = gvid + 10
  end
end

function get_top_graph_ids(level_name)
  if level_name ~= nil then
    local gg = game_graph()
    if top_game_vertex_ids == nil then
      fill_top_game_vertex_ids(gg)
    end
    local gvid = top_game_vertex_ids[level_name]
    return gg:vertex(gvid):level_vertex_id(), gvid
  end
  if top_gvid == nil then
    local gg = game_graph()
    local gvid = db.actor:game_vertex_id()
    top_gvid = gg_backward_scan(gg, gg:vertex(gvid):level_id(), gvid)
    top_lvid = gg:vertex(top_gvid):level_vertex_id()
  end
  return top_lvid, top_gvid
end

function test()
  local lvid, gvid = nil, nil
  for k, v in pairs(levels) do
    lvid, gvid = get_top_graph_ids(v)
    printf("%s : lvid[%d] gvid[%d]", v, lvid, gvid)
  end
end


lua_help task CGameTask SGameTaskObjective

class CGameTask

Creates new quest.
C++ class CGameTask
{
  CGameTask();

  function get_objective(number);
  function get_title();
  function get_id();
  function add_objective(SGameTaskObjective*);
  function set_title(string);
  function load(string);
  function get_objectives_cnt();
};

CGameTask::get_objective(index)

purpose:
returns task objective
arguments:

  • index <number>

    - objective index
returns:

  • <SGameTaskObjective>

    - task objective; see: SGameTaskObjective

CGameTask::get_title()

purpose:
returns task title
returns:

  • <string>

    - task title

CGameTask::get_id()

purpose:
returns task name (id)
returns:

  • <string>

    - task name (id)

CGameTask::add_objective(objective)

purpose:
adds new task objective
arguments:

  • objective <SGameTaskObjective>

    - new objective; see: SGameTaskObjective

CGameTask::set_title(text)

purpose:
sets task title
arguments:

  • text <string>

    - task title

CGameTask::load(id_name)

purpose:
loads task
arguments:

  • id_name <string>

    - task name (id) from config\gameplay\game_tasks_by_vendor.xml (game_tasks.xml)

CGameTask::get_objectives_cnt()

purpose:
returns number of task objectives
returns:

  • <number>

    - number of task objectives

class task

Task type/progress constants.
-- Shadow of Chernobyl
C++ class task
{
  const fail = 0;
  const in_progress = 1;
  const completed = 2;

  const task_dummy = -1;
};

-- Call of Pripyat
C++ class task
{
  const storyline = 0;
  const additional = 1;

  const fail = 0;
  const in_progress = 1;
  const completed = 2;

  const task_dummy = 65535;
};

class SGameTaskObjective

Creates task objective.
C++ class SGameTaskObjective
{
  SGameTaskObjective(CGameTask*, number);

  function add_fail_func(string);
  function add_on_fail_func(string);
  function add_fail_info(string);
  function add_on_fail_info(string);

  function add_complete_func(string);
  function add_on_complete_func(string);
  function add_complete_info(string);
  function add_on_complete_info(string);

  function set_article_key(string);
  function set_article_id(string);

  function set_description(string);
  function get_description();

  function get_state();
  function get_idx();

  function set_object_id(number);
  function set_map_hint(string);
  function set_map_location(string);
};

SGameTaskObjective::add_fail_func(script_func)

purpose:
adds objective fail predicate (boolean function which determines if objective was failed)
arguments:

  • script_func <string>

    - objective fail predicate; format: script_name.function_name

SGameTaskObjective::add_on_fail_func(script_func)

purpose:
adds objective fail callback (function called when objective was failed)
arguments:

  • script_func <string>

    - objective fail callback; format: script_name.function_name

SGameTaskObjective::add_fail_info(info)

purpose:
adds fail infoportion (objective failes if actor received this infoportion)
arguments:

  • info <string>

    - infoportion

SGameTaskObjective::add_on_fail_info(info)

purpose:
adds infoportion which will be given when objective failes
arguments:

  • info <string>

    - infoportion

SGameTaskObjective::add_complete_func(script_func)

purpose:
adds objective complete predicate (boolean function which determines if objective was completed)
arguments:

  • script_func <string>

    - objective complete predicate; format: script_name.function_name

SGameTaskObjective::add_on_complete_func(script_func)

purpose:
adds objective complete callback (function called when objective was completed)
arguments:

  • script_func <string>

    - objective complete callback; format: script_name.function_name

SGameTaskObjective::add_complete_info(info)

purpose:
adds complete infoportion (objective is completed if actor received this infoportion)
arguments:

  • info <string>

    - infoportion

SGameTaskObjective::add_on_complete_info(info)

purpose:
adds infoportion which will be given when objective was completed
arguments:

  • info <string>

    - infoportion

SGameTaskObjective::set_article_key(string)

purpose:
???
arguments:

  • <string>

    -

SGameTaskObjective::set_article_id(descr)

purpose:
sets objective description
arguments:

  • descr <string>

    - objective description

SGameTaskObjective::set_description(descr)

purpose:
sets objective title
arguments:

  • descr <string>

    - objective title

SGameTaskObjective::get_description()

purpose:
returns objective title
returns:

  • <string>

    - objective title

SGameTaskObjective::get_state()

purpose:
returns objective state
returns:

  • <task>

    - objective state (see: task)

SGameTaskObjective::get_idx()

purpose:
returns objective index
returns:

  • <number>

    - objective index

SGameTaskObjective::set_object_id(id)

purpose:
sets objective target
arguments:

  • id <number>

    - target id

SGameTaskObjective::set_map_hint(text)

purpose:
sets mapspot caption (label)
arguments:

  • text <string>

    - mapspot caption

SGameTaskObjective::set_map_location(mtype)

purpose:
sets mapspot type
arguments:

  • mtype <string>

    - mapspot type


lua_help client_spawn_manager

class client_spawn_manager

Stores information about spawned objects. To get instance of

client_spawn_manager

use level.client_spawn_manager()
C++ class client_spawn_manager
{
  function remove(number, number);
  function add(number, number, const function<void>&, object);
  function add(number, number, const function<void>&);
};

-----------------------------------------------------------------------

local csm = level.client_spawn_manager()

client_spawn_manager::add(obj_id, parent_id, callback, object)

client_spawn_manager::add(obj_id, parent_id, callback)

purpose:
adds object to

client_spawn_manager

arguments:

  • obj_id <number>

    - object id

  • parent_id <number>

    - object's parent id

  • callback <function>

    - callback function called on adding object to

    client_spawn_manager

  • object

    - object's parent
example:
function callback(id, obj)
  local str = ""
  if id ~= nil then
    str = string.format("added obj: id = %d", id)
  end
  if obj ~= nil then
    str = string.format("%s name = %s", str, obj:name())
  end
  if str ~= "" then
    printf("%s", str)
  end
end

local pos = db.actor:position()
local lvid = db.actor:level_vertex_id()
local gvid = db.actor:game_vertex_id()

local obj = alife():create("af_ameba_mica", pos, lvid, gvid, db.actor:id())

local c_spawn = level.client_spawn_manager()
if c_spawn then
  c_spawn:add(obj.id, db.actor:id(), callback)
end

client_spawn_manager::remove(obj_id, parent_id)

purpose:
removes object from

client_spawn_manager

arguments:

  • obj_id <number>

    - object id

  • parent_id <number>

    - object's parent id


lua_help client object classes

class CGameObject

C++ class DLL_Pure
{
  DLL_Pure();

  function _construct();
};

C++ class ISheduled { };

C++ class ICollidable
{
  ICollidable();
};

C++ class IRenderable { };

C++ class CGameObject : DLL_Pure, ISheduled, ICollidable, IRenderable
{
  CGameObject();

  function Visual();
  function getEnabled() const;
  function _construct();
  function net_Import(net_packet&);
  function getVisible() const;
  function net_Export(net_packet&);
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CAI_Bloodsucker

C++ class CAI_Bloodsucker : CGameObject
{
  CAI_Bloodsucker();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CAI_Boar

C++ class CAI_Boar : CGameObject
{
  CAI_Boar();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CAI_Dog

C++ class CAI_Dog : CGameObject
{
  CAI_Dog();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CAI_Flesh

C++ class CAI_Flesh : CGameObject
{
  CAI_Flesh();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CAI_PseudoDog

C++ class CAI_PseudoDog : CGameObject
{
  CAI_PseudoDog();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CAI_Stalker

C++ class CAI_Stalker : CGameObject
{
  CAI_Stalker();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CAI_Trader

C++ class CAI_Trader : CGameObject
{
  CAI_Trader();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CBastArtefact

C++ class CBastArtefact : CGameObject
{
  CBastArtefact();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CBlackDrops

C++ class CBlackDrops : CGameObject
{
  CBlackDrops();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CBlackGraviArtefact

C++ class CBlackGraviArtefact : CGameObject
{
  CBlackGraviArtefact();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CBurer

C++ class CBurer : CGameObject
{
  CBurer();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CCat

C++ class CCat : CGameObject
{
  CCat();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CChimera

C++ class CChimera : CGameObject
{
  CChimera();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CController

C++ class CController : CGameObject
{
  CController();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CDummyArtefact

C++ class CDummyArtefact : CGameObject
{
  CDummyArtefact();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CElectricBall

C++ class CElectricBall : CGameObject
{
  CElectricBall();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CF1

C++ class CF1 : CGameObject
{
  CF1();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CFadedBall

C++ class CFadedBall : CGameObject
{
  CFadedBall();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CFracture

C++ class CFracture : CGameObject
{
  CFracture();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CGalantineArtefact

C++ class CGalantineArtefact : CGameObject
{
  CGalantineArtefact();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CGraviArtefact

C++ class CGraviArtefact : CGameObject
{
  CGraviArtefact ();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CHairsZone

C++ class CHairsZone : CGameObject
{
  CHairsZone();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class hanging_lamp

C++ class hanging_lamp : CGameObject
{
  hanging_lamp();

  function Visual();
  function getEnabled() const;
  function net_Import(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function turn_on();
  function turn_off();
  function net_Export(net_packet&);
  function _construct();
  function use(CGameObject*);
};

class CMercuryBall

C++ class CMercuryBall : CGameObject
{
  CMercuryBall();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CMincer

C++ class CMincer : CGameObject
{
  CMincer();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CMosquitoBald

C++ class CMosquitoBald : CGameObject
{
  CMosquitoBald();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CPoltergeist

C++ class CPoltergeist : CGameObject
{
  CPoltergeist();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CPseudoGigant

C++ class CPseudoGigant : CGameObject
{
  CPseudoGigant();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CPsyDog

C++ class CPsyDog : CGameObject
{
  CPsyDog();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CPsyDogPhantom

C++ class CPsyDogPhantom : CGameObject
{
  CPsyDogPhantom();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CRGD5

C++ class CRGD5 : CGameObject
{
  CRGD5();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CRustyHairArtefact

C++ class CRustyHairArtefact : CGameObject
{
  CRustyHairArtefact();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CScope

C++ class CScope : CGameObject
{
  CScope();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CSnork

C++ class CSnork : CGameObject
{
  CSnork();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CSpaceRestrictor

C++ class CSpaceRestrictor : CGameObject
{
  CSpaceRestrictor();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CStalkerOutfit

C++ class CStalkerOutfit : CGameObject
{
  CStalkerOutfit();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CThornArtefact

C++ class CThornArtefact : CGameObject
{
  CThornArtefact();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CTorch

C++ class CTorch : CGameObject
{
  CTorch();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CTushkano

C++ class CTushkano : CGameObject
{
  CTushkano();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponAK74

C++ class CWeaponAK74 : CGameObject
{
  CWeaponAK74();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponBM16

C++ class CWeaponBM16 : CGameObject
{
  CWeaponBM16();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponBinoculars

C++ class CWeaponBinoculars : CGameObject
{
  CWeaponBinoculars();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponFN2000

C++ class CWeaponFN2000 : CGameObject
{
  CWeaponFN2000();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponFORT

C++ class CWeaponFORT : CGameObject
{
  CWeaponFORT();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponGroza

C++ class CWeaponGroza : CGameObject
{
  CWeaponGroza();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponHPSA

C++ class CWeaponHPSA : CGameObject
{
  CWeaponHPSA();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponKnife

C++ class CWeaponKnife : CGameObject
{
  CWeaponKnife();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponLR300

C++ class CWeaponLR300 : CGameObject
{
  CWeaponLR300();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponPM

C++ class CWeaponPM : CGameObject
{
  CWeaponPM();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponRG6

C++ class CWeaponRG6 : CGameObject
{
  CWeaponRG6();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponRPG7

C++ class CWeaponRPG7 : CGameObject
{
  CWeaponRPG7();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponSVD

C++ class CWeaponSVD : CGameObject
{
  CWeaponSVD();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponSVU

C++ class CWeaponSVU : CGameObject
{
  CWeaponSVU();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponShotgun

C++ class CWeaponShotgun : CGameObject
{
  CWeaponShotgun();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponUSP45

C++ class CWeaponUSP45 : CGameObject
{
  CWeaponUSP45();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponVal

C++ class CWeaponVal : CGameObject
{
  CWeaponVal();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponVintorez

C++ class CWeaponVintorez : CGameObject
{
  CWeaponVintorez();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CWeaponWalther

C++ class CWeaponWalther : CGameObject
{
  CWeaponWalther();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CZombie

C++ class CZombie : CGameObject
{
  CZombie();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class CZudaArtefact

C++ class CZudaArtefact : CGameObject
{
  CZudaArtefact();

  function Visual();
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};

class smart_cover_object

C++ class smart_cover_object : CGameObject
{
  smart_cover_object();

  function Visual() const;
  function _construct();
  function getEnabled() const;
  function net_Import(net_packet&);
  function net_Export(net_packet&);
  function getVisible() const;
  function net_Spawn(cse_abstract*);
  function use(CGameObject*);
};


lua_help main_menu cMainMenu

main_menu.get_main_menu()

purpose:
returns pointer to main_menu
returns:

  • CMainMenu*

    - pointer to main_menu

class CMainMenu

To get pointer to CMainMenu use main_manu (see above)
C++ class CMainMenu
{
  function GetGSVer();
  function ValidateCDKey();
  function GetPatchProgress();
  function CancelDownload();
  function GetAccountMngr();
  function GetDemoInfo(string);
  function GetProfileStore();
  function GetLoginMngr();
  function GetCDKey();
  function GetPlayerName();
};

-------------------------------------

local mm = main_menu.get_main_menu()

CMainMenu::GetGSVer()

purpose:
returns game server version
returns:

  • <string>

    - game version
example:
local mm = main_menu.get_main_menu()
printf("ver = %s", mm:GetGSVer())

CMainMenu::ValidateCDKey()

purpose:
function validates cd key
returns:

  • true

    - cd key is valid

  • false

    - otherwise

CMainMenu::GetPatchProgress()

purpose:
function checks patch download progress
returns:

  • Patch_Dawnload_Progress*

CMainMenu::CancelDownload()

purpose:
stops downloading patch

CMainMenu::GetAccountMngr() obsolete (removed in SOC)

CMainMenu::GetDemoInfo(string) obsolete (removed in SOC)

CMainMenu::GetProfileStore() obsolete (removed in SOC)

CMainMenu::GetLoginMngr() obsolete (removed in SOC)

CMainMenu::GetCDKey() obsolete (removed in SOC)

CMainMenu::GetPlayerName() obsolete (removed in SOC)


lua_help color

class color

Represents RGB color model. See also fcolor.
C++ class color
{
  property b;
  property g;
  property r;

  color();
  color(number, number, number);

  function set(number, number, number);
};

--------------------------------------------

local ca = color(0.1, 0.2, 0.3)

local cb = color()
cb.r = 0.1
cb.g = 0.2
cb.b = 0.3

local cc = color()
cc:set(0.1, 0.2, 0.3)

color::r

purpose:
holds red color value
property:

  • <number>

    - red color value

color::g

purpose:
holds green color value
property:

  • <number>

    - green color value

color::b

purpose:
holds blue color value
property:

  • <number>

    - blue color value

color::set(r, g, b)

purpose:
sets color values
arguments:

  • r <number>

    - red color value

  • g <number>

    - green color value

  • b <number>

    - blue color value


lua_help cond

class cond

Creates condition for scripted actions.

  • cond.move_end

    - condition for move action

  • cond.look_end

    - condition for look action

  • cond.anim_end

    - condition for anim action

  • cond.sound_end

    - condition for sound action

  • cond.object_end

    - condition for object action

  • cond.time_end

    - universal condition, can be applied to any action

  • cond.act_end

    - condition for act action
C++ class cond
{
  const move_end = 1;
  const look_end = 2;
  const anim_end = 4;
  const sound_end = 8;
  const object_end = 32;
  const time_end = 64;
  const act_end = 128;

  cond();
  cond(number);
  cond(number, double);
};

cond::cond(flag, time)

cond::cond(flag)

cond::cond()

purpose:
object constructor
arguments:

  • flag <const>

    - one of the flags from

    cond

    class

  • time <number>

    - time (only with

    cond.time_end

    flag)
returns:

  • <cond>

    - object


lua_help CPhrase CPhraseDialog CPhraseScript

class CPhraseScript

Dialog phrase conditions and actions.
C++ class CPhraseScript
{
  function AddHasInfo(string);
  function AddGiveInfo(string);
  function AddDisableInfo(string);
  function AddDontHasInfo(string);
  function AddAction(string);
  function AddPrecondition(string);
  function SetScriptText(string);
};

CPhraseScript::AddHasInfo(info)

purpose:
adds has_info condition to dialog phrase
arguments:

  • info <string>

    - infoportion

CPhraseScript::AddGiveInfo(info)

purpose:
adds give_info action to dialog phrase
arguments:

  • info <string>

    - infoportion

CPhraseScript::AddDisableInfo(info)

purpose:
adds disable_info action to dialog phrase
arguments:

  • info <string>

    - infoportion

CPhraseScript::AddDontHasInfo(info)

purpose:
adds dont_has_info condition to dialog phrase
arguments:

  • info <string>

    - infoportion

CPhraseScript::AddAction(script_func)

purpose:
adds action callback to dialog phrase
arguments:

  • script_func <string>

    - action callback; format script_name.function_name

CPhraseScript::AddPrecondition(script_func)

purpose:
adds precondition to dialog phrase
arguments:

  • script_func <string>

    - predicate; format script_name.function_name

CPhraseScript::SetScriptText(script_func)

purpose:
adds text to phrase; this is useful if phrase text depends on some factor or condition, for instance name of the place where guide npc can take actor, or how much will cost repairing weapon, etc; available only in COP
arguments:

  • script_func <string>

    - function which returns phrase text (

    string

    ); format script_name.function_name

class CPhrase

Dialog phrase.
C++ class CPhrase
{
  function GetPhraseScript();
};

CPhrase::GetPhraseScript()

purpose:
returns dialog phrase conditions and actions
returns:

  • <CPhraseScript>

    - dialog phrase conditions and actions; see: CPhraseScript

class CPhraseDialog

Represents dialog tree (list of phrases).
C++ class CPhraseDialog
{
  function AddPhrase(string, number, number, number);
};

CPhraseDialog::AddPhrase(text, phr_id, p_phr_id, noidea)

purpose:
adds dialog phrase
arguments:

  • text <string>

    - phrase text

  • phr_id <number>

    - this phrase id

  • p_phr_id <number>

    - parent phrase id

  • noidea <number>

    - constant (?) -10000
returns:

  • <CPhrase>

    - dialog phrase; see: CPhrase


lua_help CSavedGameWrapper

class CSavedGameWrapper

Extracts actor data from saved game file.
C++ class CSavedGameWrapper
{
  CSavedGameWrapper(string);

  function level_name(const CSavedGameWrapper*);
  function level_id() const;
  function game_time(const CSavedGameWrapper*);
  function actor_health() const;
};

--------------------------------------------------

local save_name = "barin_quicksave"
local sgw = CSavedGameWrapper(save_name)

CSavedGameWrapper::level_name()

purpose:
returns level name
returns:

  • <string>

    - level name

CSavedGameWrapper::level_id()

purpose:
returns level id
returns:

  • <number>

    - level id

CSavedGameWrapper::game_time()

purpose:
returns game time
returns:

  • <CTime>

    - game time; see: CTime

CSavedGameWrapper::actor_health()

purpose:
returns actor health
returns:

  • <float>

    - actor health


lua_help CScriptXmlInit

class CScriptXmlInit

Set of methods to load ui controls from xml file.
C++ class CScriptXmlInit
{
  CScriptXmlInit();

  function ParseFile(string);
  function InitAnimStatic(string, CUIWindow*);
  function InitAutoStaticGroup(string, CUIWindow*);
  function InitStatic(string, CUIWindow*);
  function InitButton(string, CUIWindow*);
  function Init3tButton(string, CUIWindow*);
  function InitFrameLine(string, CUIWindow*);
  function InitFrame(string, CUIWindow*);
  function InitTrackBar(string, CUIWindow*);
  function InitTab(string, CUIWindow*);
  function InitLabel(string, CUIWindow*);
  function InitProgressBar(string, CUIWindow*);
  function InitScrollView(string, CUIWindow*);
  function InitEditBox(string, CUIWindow*);
  function InitCheck(string, CUIWindow*);
  function InitList(string, CUIWindow*);

  function InitSpinText(string, CUIWindow*);
  function InitSpinFlt(string, CUIWindow*);
  function ParseShTexInfo(string);
  function InitSpinNum(string, CUIWindow*);
  function InitMMShniaga(string, CUIWindow*);
  function InitWindow(string, number, CUIWindow*);
  function InitComboBox(string, CUIWindow*);

  function InitMapInfo(string, CUIWindow*);
  function InitServerList(string, CUIWindow*);
  function InitMapList(string, CUIWindow*);
  function InitCDkey(string, CUIWindow*);
  function InitKeyBinding(string, CUIWindow*);

  -- COP only ...
  function InitSleepStatic(string, CUIWindow*);
  function InitTextWnd(string, CUIWindow*);
  function InitListBox(string, CUIWindow*);
  function InitMPPlayerName(string, CUIWindow*);
};

CScriptXmlInit::ParseFile(path)

purpose:
parse xml file which contains window (ui controls) definition; default path is set to

gamedata\config\ui\

arguments:

  • path <string>

    - path to xml file
example:
local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

CScriptXmlInit::InitAnimStatic(name, wnd)

purpose:
loads static animation (like those in PDA)
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach static anim)
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - width (of one animation frame)

  • height

    - height (of one animation frame)

  • frames

    - number of animation frames (2 rows * 8 columns = 16 for ui_pda_anims_04)

  • duration

    - animation time

  • columns

    - number of columns (in dds)

  • frame_width

    - width of one animation frame

  • frame_height

    - height of one animation frame

  • cyclic

    - whether loop animation

  • autoplay

    - whether start playing automatically once anim is loaded
<!-- config\ui\my_wnd.xml -->
<my_anim x="250" y="14" width="32" height="32" frames="16" duration="1000"
  columns="8" frame_width="32" frame_height="32" cyclic="1" autoplay="1">
    <texture>ui\ui_pda_anims_04</texture>
</my_anim>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

xml:InitAnimStatic("my_anim", self)

CScriptXmlInit::InitAutoStaticGroup(name, wnd)

SOC only
purpose:
loads group of static textures or text
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach static group)
xml attributes:

  • x

    - position x (relatively to parent - your ui window); optional

  • y

    - position y (relatively to parent - your ui window); optional

  • width

    - width (optional)

  • height

    - height (optional)
remarks:
  • you can also add (max 3) static textures in any control using tag auto_static - see my_texture example below
<!-- config\ui\my_wnd.xml -->
<contacts_list_header x="0" y="0" width="391" height="20">
  <auto_static x="24" y="40" width="130" height="20">
    <text align="l" font="letterica16" r="238" g="153" b="26" a="255">ui_pda_base_contact_name</text>
  </auto_static>
  <auto_static x="158" y="40" width="70" height="20">
    <text align="l" font="letterica16" r="238" g="153" b="26" a="255">ui_pda_base_contact_group</text>
  </auto_static>
  <auto_static x="232" y="40" width="191" height="20">
    <text align="l" font="letterica16" r="238" g="153" b="26" a="255">ui_pda_base_contact_stat</text>
  </auto_static>
</contacts_list_header>

<my_texture x="135" y="603" width="760" height="32">
  <texture>ui_string_01</texture>
    <auto_static x="24" y="40" width="130" height="20">
      <text align="l" font="letterica16" r="238" g="153" b="26" a="255">some text</text>
    </auto_static>
</my_texture>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

xml:InitAutoStaticGroup("contacts_list_header", self)

-- will automatically load "some text" as well
xml:InitStatic("my_texture", self)

CScriptXmlInit::InitStatic(name, wnd)

purpose:
loads static texture or text; see: CUIStatic
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach static control)
returns:

  • <CUIStatic>

    - CUIStatic

CScriptXmlInit::InitButton(name, wnd)

SOC only
purpose:
loads button; see: CUIButton
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach button)
returns:

  • <CUIButton>

    - CUIButton

CScriptXmlInit::Init3tButton(name, wnd)

purpose:
loads button; see: CUI3tButton
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach button)
returns:

  • <CUI3tButton>

    - CUI3tButton

CScriptXmlInit::InitFrameLine(name, wnd)

purpose:
loads frame line control; see: CUIFrameLineWnd
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach frame line control)
returns:

  • <CUIFrameLineWnd>

    - CUIFrameLineWnd

CScriptXmlInit::InitFrame(name, wnd)

purpose:
loads frame window; see: CUIFrameWindow
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach frame control)
returns:

  • <CUIFrameWindow>

    - CUIFrameWindow

CScriptXmlInit::InitTrackBar(name, wnd)

purpose:
loads trackbar control; see: CUITrackBar
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach trackbar)
returns:

  • <CUITrackBar>

    - CUITrackBar

CScriptXmlInit::InitTab(name, wnd)

purpose:
loads tab control; see: CUITabControl
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach tab control)
returns:

  • <CUITabControl>

    - CUITabControl

CScriptXmlInit::InitLabel(name, wnd)

SOC only
purpose:
loads label control; see: CUILabel
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach label control)
returns:

  • <CUILabel>

    - CUILabel

CScriptXmlInit::InitProgressBar(name, wnd)

purpose:
loads progress bar control; see: CUIProgressBar
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach progress bar)
returns:

  • <CUIProgressBar>

    - CUIProgressBar

CScriptXmlInit::InitScrollView(name, wnd)

purpose:
loads ScrollBar control; see: CUIScrollView
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach progress bar)
returns:

  • <CUIScrollView>

    - CUIScrollView

CScriptXmlInit::InitEditBox(name, wnd)

purpose:
loads EditBox (or: TextBox) control; see: CUIEditBox
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach EditBox)
returns:

  • <CUIEditBox>

    - CUIEditBox

CScriptXmlInit::InitCheck(name, wnd)

purpose:
loads CheckBox control; see: CUICheckButton
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach CheckBox)
returns:

  • <CUICheckButton>

    - CUICheckButton

CScriptXmlInit::InitList(name, wnd)

SOC only
purpose:
loads ListView control; see: CUIListWnd
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach ListView)
returns:

  • <CUIListWnd>

    - CUIListWnd

CScriptXmlInit::InitListBox(name, wnd)

COP only
purpose:
loads ListView control; see: CUIListBox
arguments:

  • name <string>

    - control name (xml)

  • wnd <CUIWindow>

    - your ui window (to attach ListView)
returns:

  • <CUIListBox>

    - CUIListBox


lua_help CTime

class CTime

Provides convenient way to store time and perform basic operations. To get instance of

CTime

use function

game.CTime()

C++ class CTime
{
  const DateToDay = 0;
  const DateToMonth = 1;
  const DateToYear = 2;

  const TimeToHours = 0;
  const TimeToMinutes = 1;
  const TimeToSeconds = 2;
  const TimeToMilisecs = 3;

  CTime();
  CTime(const CTime&);

  function set(number, number, number, number, number, number, number);
  function setHMS(number, number, number);
  function setHMSms(number, number, number, number);

  function get(number&, number&, number&, number&, number&, number&, number&);

  function add(CTime*);
  function sub(CTime*);
  function diffSec(CTime*);

  function timeToString(number);
  function dateToString(number);

  operator ==(const CTime&, CTime);
  operator <(const CTime&, CTime);
  operator +(CTime&, CTime);
  operator >=(const CTime&, CTime);
  operator >(const CTime&, CTime);
  operator -(CTime&, CTime);
  operator <=(const CTime&, CTime);
};

--------------------------------------------------

local ta = game.CTime()
local tb = game.CTime(ta)

CTime::set(Y, M, D, h, m, s, ms)

CTime::setHMSms(h, m, s, ms)

CTime::setHMS(h, m, s)

purpose:
sets desired time
arguments:

  • Y <number>

    - years

  • M <number>

    - months [1, depends on year]

  • D <number>

    - days [1, depends on year]

  • h <number>

    - hours [0, 23]

  • m <number>

    - minutes [0, 59]

  • s <number>

    - seconds [0, 59]

  • ms <number>

    - miliseconds [0, 999]

CTime::get(Y, M, D, h, m, s, ms)

purpose:
returns stored time
arguments/returns:

  • Y <number>

    - years

  • M <number>

    - months

  • D <number>

    - days

  • h <number>

    - hours

  • m <number>

    - minutes

  • s <number>

    - seconds

  • ms <number>

    - miliseconds
example:
local t = game.CTime()
local Y, M, D, h, m, s, ms = 0, 0, 0, 0, 0, 0, 0
Y, M, D, h, m, s, ms = t:get(Y, M, D, h, m, s, ms)

CTime::add(CTime*)

purpose:
adds time
arguments:

  • CTime*

    - time to add
example:
local a = game.CTime()
local b = game.CTime()

a:set(2010, 3, 25, 4, 35, 23, 0)
b:set(1, 1, 1, 5, 2, 5, 0)

a:add(b)

-- or simply (addition operator has been overloaded)
local c = a + b

CTime::sub(CTime*)

purpose:
subtract time
arguments:

  • CTime*

    - time to subtract
example:
local a = game.CTime()
local b = game.CTime()

a:set(2010, 3, 25, 4, 35, 23, 0)
b:set(1, 1, 1, 5, 2, 5, 0)

a:sub(b)

-- or simply (subtraction operator has been overloaded)
local c = a - b

CTime::diffSec(CTime*)

purpose:
calculates difference
arguments:

  • CTime*

    - time to compare
returns:

  • <number>

    - difference (in seconds)
example:
local a = game.CTime()
local b = game.CTime()

a:set(2010, 3, 25, 5, 35, 23, 0)
b:set(2010, 3, 25, 4, 35, 23, 0)

printf("one hour difference = %d", a:diffSec(b)) -- 3600 s (1 hour)

CTime::timeToString(flag)

purpose:
extracts time using given format
arguments:

  • flag <const:0|1|2|3>

    - extraction format

    • game.CTime.TimeToHours

      - hh

    • game.CTime.TimeToMinutes

      - hh:mm

    • game.CTime.TimeToSeconds

      - hh:mm:ss

    • game.CTime.TimeToMilisecs

      - hh:mm:ss:ms
example:
local t = game.CTime()
t:set(2012, 5, 1, 5, 30, 0, 0)

print(t:timeToString(game.CTime.TimeToHours)) -- 05
print(t:timeToString(game.CTime.TimeToMinutes)) -- 05:30
print(t:timeToString(game.CTime.TimeToSeconds)) -- 05:30:00
print(t:timeToString(game.CTime.TimeToMilisecs)) -- 05:30:00:00

CTime::dateToString(flag)

purpose:
extracts date using given format
arguments:

  • flag <const:0|1|2>

    - extraction format

    • game.CTime.DateToDay

      - dd/mm/yyyy

    • game.CTime.DateToMonth

      - mm/yyyy

    • game.CTime.DateToYear

      - yyyy
example:
local t = game.CTime()
t:set(2012, 5, 1, 5, 30, 0, 0)

print(t:dateToString(game.CTime.DateToDay)) -- 01/05/2012
print(t:dateToString(game.CTime.DateToMonth)) -- 05/2012
print(t:dateToString(game.CTime.DateToYear)) -- 2012


lua_help CUIButton CUI3tButton

class CUIButton

Implements button ui control. Load from xml file with InitButton
events:

  • ui_events.BUTTON_CLICKED

    - on button clicked
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - button width

  • height

    - button height

  • hint

    - text hint displayed when you point button with mouse pointer (hover)
<!-- config\ui\my_wnd.xml -->
<my_button x="10" y="10" width="117" height="29" hint="click me ...">
  <window_name>my_button</window_name>
  <text align="l" x="0" y="0" font="letterica18" a="255" r="255" g="255" b="255">some text ...</text>
</my_button>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local btn = xml:InitButton("my_button", self)

-- or:
local btn = CUIButton()
btn:SetAutoDelete(true)
btn:SetWindowName("my_button")
btn:Init(10, 10, 117, 29)
btn:SetTextAlign(CGameFont.alLeft)
btn:SetTextX(0)
btn:SetTextY(0)
btn:SetFont(GetFontLetterica18Russian())
btn:SetTextColor(255, 255, 255, 255)
btn:SetText("some text ...")
wnd:AttachChild(btn)

C++ class CUIButton : CUIStatic
{
  CUIButton();

  function SetHighlightColor(number);
  function GetTextY();
  function Enable(boolean);
  function SetHeight(number);
  function GetText();
  function GetColor() const;
  function GetWidth() const;
  function GetClipperState();
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function SetFont(CGameFont*);
  function IsShown();
  function Show(boolean);
  function GetHeight() const;
  function SetWidth(number);
  function GetTextX();
  function IsEnabled();
  function ResetPPMode();
  function GetHeading();
  function GetFont();
  function SetStretchTexture(boolean);
  function EnableTextHighlighting(boolean);
  function ClipperOn();
  function IsAutoDelete();
  function SetAutoDelete(boolean);
  function SetTextX(number);
  function SetTextColor(number, number, number, number);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function AttachChild(CUIWindow*);
  function SetWindowName(string);
  function GetStretchTexture();
  function Init(number, number, number, number);
  function Init(Frect*);
  function Init(string, number, number, number, number);
  function GetTextAlign();
  function SetTextST(string);
  function SetText(string);
  function WindowName();
  function ClipperOff();
  function SetTextY(number);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function SetTextureOffset(number, number);
  function SetOriginalRect(number, number, number, number);
  function InitTexture(string);
  function SetHeading(number);
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetTextAlign(number);
  function SetColor(number);
  function GetWndPos(CUIWindow*);
  function TextControl();
  function GetTextureRect();
  function SetTextureRect(Frect*);
};

<CUIButton>::SetHighlightColor(argb)

SOC only
purpose:
sets highlighting color
arguments:

  • argb <int>

    - color packed as int number (see: GetARGB)
example:
btn:SetTextColor(255, 238, 155, 23) -- yellow
btn::SetHighlightColor(GetARGB(255, 255, 255, 255)) -- white

<CUIButton>::EnableTextHighlighting(b_status)

SOC only
purpose:
enables/disables text highlighting; text highlighting is enabled by default
arguments:

  • b_status <boolean>

    - enables (

    true

    ) or disables (

    false

    ) text highlighting

class CUI3tButton

Implements button ui control with 3 textures describing button states (activated, deactivated, hovered). Load from xml file with Init3tButton
events:

  • ui_events.BUTTON_CLICKED

    - on button clicked
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - button width

  • height

    - button height

  • hint

    - text hint displayed when you point button with mouse pointer (hover)

  • texture

    - either id of texture defined in xml (first example) or explicitely listed (second example)

  • text_color

    - text color (definition for each button state: enabled, deactivated, hovered, normal)
<!-- config\ui\my_wnd.xml -->
<my_button x="10" y="10" width="117" height="29" hint="click me ...">
  <window_name>my_button</window_name>
  <text align="l" x="0" y="0" font="letterica18" a="255" r="255" g="255" b="255">some text ...</text>
  <texture>ui_button_ordinary</texture>
  <text_color>
    <t r="231" g="168" b="12" />
    <d r="181" g="170" b="148" />
    <e r="127" g="127" b="127" />
    <h r="255" g="255" b="255" />
  </text_color>
</my_button>

<btn_prev x="0" y="0" width="44" height="29">
  <texture_e>ui_button_arrow_left_e</texture_e>
  <texture_h>ui_button_arrow_left_h</texture_h>
  <texture_t>ui_button_arrow_left_t</texture_t>
  <texture_d>ui_button_arrow_left_d</texture_d>
</btn_prev>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local btn = xml:Init3tButton("my_button", self)

-- or:
local btn = CUI3tButton()
btn:SetAutoDelete(true)
btn:SetWindowName("my_button")
btn:Init("ui_button_ordinary", 10, 10, 117, 29)
btn:SetTextAlign(CGameFont.alLeft)
btn:SetTextX(0)
btn:SetTextY(0)
btn:SetFont(GetFontLetterica18Russian())
btn:SetTextColor(255, 255, 255, 255)
btn:SetText("some text ...")
wnd:AttachChild(btn)

C++ class CUI3tButton : CUIButton
{
  CUI3tButton();

  function SetHighlightColor(number);
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function GetText();
  function GetColor() const;
  function GetWidth() const;
  function SetTextX(number);
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function SetFont(CGameFont*);
  function IsShown();
  function Show(boolean);
  function GetHeight() const;
  function SetWidth(number);
  function GetTextX();
  function IsEnabled();
  function ResetPPMode();
  function GetHeading();
  function EnableTextHighlighting(boolean);
  function SetStretchTexture(boolean);
  function SetColor(number);
  function ClipperOn();
  function GetFont();
  function SetAutoDelete(boolean);
  function GetClipperState();
  function SetTextColor(number, number, number, number);
  function SetHeading(number);
  function AttachChild(CUIWindow*);
  function GetTextY();
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function SetOriginalRect(number, number, number, number);
  function GetTextAlign();
  function Init(number, number, number, number);
  function Init(Frect*);
  function Init(string, number, number, number, number);
  function SetTextureOffset(number, number);
  function WindowName();
  function ClipperOff();
  function SetTextY(number);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function SetText(string);
  function SetTextST(string);
  function InitTexture(string);
  function GetStretchTexture();
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetTextAlign(number);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
  function TextControl();
  function GetTextureRect();
  function SetTextureRect(Frect*);
};


lua_help CUIStatic

class CUIStatic

Implements static texture and text label. Load from xml file with InitStatic
xml attributes for static and animated text:

  • x

    - position x (relatively to parent control)

  • y

    - position y (relatively to parent control)

  • width

    - width

  • height

    - height

  • complex_mode

    - whether move some text to next line if it doesn't fit (text longer then

    width

    )

  • align

    - text alignment:

    • align="l"

      - align left

    • align="c"

      - align center

    • align="r"

      - align right

  • font

    - font type; available fonts and script functions

    • font="small"

      - GetFontSmall()

    • font="medium"

      - GetFontMedium()

    • font="letterica16"

      - GetFontLetterica16Russian()

    • font="letterica18"

      - GetFontLetterica18Russian()

    • font="letterica25"

      - GetFontLetterica25()

    • font="graffiti19"

      - GetFontGraffiti19Russian()

    • font="graffiti22"

      - GetFontGraffiti22Russian()

    • font="graffiti32"

      - GetFontGraffiti32Russian()

    • font="graffiti50"

      - GetFontGraffiti50Russian()

    • font="header"

      - GetFontDI() (???)

  • a

    - transparency

  • r

    - red color

  • g

    - green color

  • b

    - blue color
  • for animated text:

    • light_anim

      - animation type; blinking animations:

      • light_anim="ui_pda_contacts"

        - very fast

      • light_anim="ui_btn_hint"

        - fast

      • light_anim="ui_slow_blinking_alpha"

        - slow

      • light_anim="ui_too_slow_blinking"

        - very slow

    • la_cyclic

      - whether loop animation

    • la_texture

      - whether animate texture

    • la_text

      - whether animate text

    • la_alpha

      - whether use alpha channel
xml attributes for static texture (texture tag):

  • x

    - position x (in dds)

  • y

    - position y (in dds)

  • width

    - texture width

  • height

    - texture height

  • stretch

    - whether stretch texture
<!-- config\ui\my_wnd.xml -->
<anim_blink_text x="294" y="91" width="250" height="24" light_anim="ui_slow_blinking_alpha"
  la_cyclic="1" la_texture="0" la_text="1" la_alpha="1">
  <text x="0" y="0" font="letterica16" r="240" g="217" b="182" a="255" align="c">some text</text>
</anim_blink_text>

<static_text x="0" y="730" width="1024" height="32">
  <window_name>my_static_text</window_name>
  <text x="0" y="0" font="graffiti32" r="155" g="148" b="148" align="c">some text</text>
</static_text>

<static_texture x="0" y="0" width="100" height="50">
  <texture x="300" y="650" width="100" height="50">ui\ui_icon_equipment</texture>
</static_texture>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local st_text = xml:InitStatic("static_text", self)

-- the same with more detailed code:
local st_text = CUIStatic()
st_text:SetAutoDelete(true)
st_text:SetWindowName("my_static_text")
st_text:Init(0, 730, 1024, 34)
st_text:SetFont(GetFontGraffiti32Russian())
st_text:SetTextColor(155, 148, 148, 255)
st_text:SetTextAlign(CGameFont.alCenter)
st_text:SetTextX(0)
st_text:SetTextY(0)
st_text:SetText("some text")

wnd:AttachChild(st_text)

C++ class CUIStatic : CUIWindow
{
  CUIStatic();

  function GetTextY();
  function Enable(boolean);
  function SetHeight(number);
  function GetText();
  function GetColor() const;
  function GetWidth() const;
  function SetTextX(number);
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function SetFont(CGameFont*);
  function IsShown();
  function Show(boolean);
  function GetHeight() const;
  function SetWidth(number);
  function GetTextX();
  function IsEnabled();
  function ResetPPMode();
  function GetHeading();
  function GetFont();
  function SetStretchTexture(boolean);
  function ClipperOn();
  function GetClipperState();
  function SetAutoDelete(boolean);
  function ClipperOff();
  function SetTextColor(number, number, number, number);
  function SetHeading(number);
  function AttachChild(CUIWindow*);
  function GetStretchTexture();
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function SetTextST(string);
  function GetTextAlign();
  function Init(number, number, number, number);
  function Init(Frect*);
  function Init(string, number, number, number, number);
  function SetOriginalRect(number, number, number, number);
  function WindowName();
  function SetTextureOffset(number, number);
  function SetTextY(number);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function SetColor(number);
  function SetText(string);
  function InitTexture(string);
  function SetWindowName(string);
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetTextAlign(number);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
  function TextControl();
  function GetTextureRect();
  function SetTextureRect(Frect*);
};
Usage example:

You can load texture via xml where you specify texture path, position and size. That's convenient, however, imagine that your script should display texture of some item actor currently has in his backpack. You can define your own xml file and specify icons for all items from

ui_icon_equipment.dds

. That's a lot of typing, not to mention that each time you add new item, you will have to add it to xml as well. Here's an example of xml usage and how to achieve the same effect via script, which in this case would save you a lot of typing.
<!-- config\ui\ui_icons_equipment.xml -->
<ui_texture>
  <file_name>ui\ui_icon_equipment</file_name>
  <texture id="ui_icon_equipment_device_pda" x="300" y="650" width="100" height="50" />
</ui_texture>

<!-- config\ui\my_wnd.xml -->
<?xml version="1.0" encoding="windows-1251" ?>
<window>
  <my_texture x="0" y="0" width="100" height="50">
    <texture>ui_icon_equipment_device_pda</texture>
    <!-- or: <texture x="300" y="650" width="100" height="50">ui\ui_icon_equipment</texture> -->
  </my_texture>
</window>

-- load from xml:
self.texture = CUIStatic()
self.texture:SetAutoDelete(true)
self.texture:SetWindowName("my_texture")
self.texture:Init(0, 0, 100, 50)
self.texture:InitTexture("ui_icon_equipment_device_pda") -- from xml
self:AttachChild(self.texture)

-- or:
-- local xml = CScriptXmlInit()
-- xml:ParseFile("my_wnd.xml")
-- self.texture = xml:InitStatic("my_texture", self)

-- load via script:
function load_texture(wnd, child_name, item_section)
  local child = wnd:GetStatic(child_name)
  if child then
    wnd:DetachChild(child)
  end

  local inv_grid_width = parser.r_uint(system_ini(), item_section, "inv_grid_width", 0) * 50
  local inv_grid_height = parser.r_uint(system_ini(), item_section, "inv_grid_height", 0) * 50
  local inv_grid_x = parser.r_uint(system_ini(), item_section, "inv_grid_x", 0) * 50
  local inv_grid_y = parser.r_uint(system_ini(), item_section, "inv_grid_y", 0) * 50

  child = CUIStatic()
  child:SetAutoDelete(true)
  child:SetWindowName(child_name)
  child:InitTexture("ui\\ui_icon_equipment")
  child:SetWndRect(x, y, inv_grid_width, inv_grid_height)
  child:SetStretchTexture(false)
  child:SetTextureOffset(-inv_grid_x, -inv_grid_y)
  child:ClipperOn()
  wnd:AttachChild(child)
end

self.texture = CUIStatic()
self.texture:SetAutoDelete(true)
self.texture:SetWindowName("my_texture")
self.texture:Init(0, 0, 100, 50)
self:AttachChild(self.texture)

-- wnd is pointer (handle) to your window
load_texture(wnd, "my_texture", "device_pda")

<CUIStatic>::GetTextY()

SOC only
purpose:
gets y position of text
returns:

  • <int>

    - y position of text; 0 if it wasn't explicitely set by SetTextY

<CUIStatic>::GetText()

SOC only
purpose:
gets text
returns:

  • <string>

    - text; empty string if text wasn't set by SetText or SetTextST

<CUIStatic>::GetColor()

SOC only
purpose:
gets texture color
returns:

  • <int>

    - current color packed as int (see: GetARGB)

<CUIStatic>::SetTextX(x_pos)

SOC only
purpose:
sets x position of text
arguments:

  • x_pos <int>

    - x position of text

<CUIStatic>::GetTextX()

SOC only
purpose:
gets x position of text
returns:

  • <int>

    - x position of text; 0 if it wasn't explicitely set by SetTextX

<CUIStatic>::GetHeading

SOC only
purpose:
gets heading (?) value
returns:

  • <int>

    - zero by default

<CUIStatic>::SetStretchTexture(b_status)

purpose:
sets texture stretch status
arguments:

  • b_status <boolean>

    - if set to

    true

    texture will be stretched

<CUIStatic>::ClipperOn()

SOC only
purpose:
enables clipper; useful if texture you want to load is saved in dds file which contains more textures; see example

<CUIStatic>::GetClipperState()

SOC only
purpose:
gets clipper state
returns:

  • true

    - clipper is on

  • false

    - otherwise (default)

<CUIStatic>::ClipperOff()

SOC only
purpose:
disables clipper

<CUIStatic>::SetTextColor(a, r, g, b)

SOC only
purpose:
sets text color; for texture color see SetColor
arguments:

  • a <int [0, 255]>

    - transparency

  • r <int [0, 255]>

    - red

  • g <int [0, 255]>

    - green

  • b <int [0, 255]>

    - blue
example:
label:SetTextColor(255, 255, 255, 255) -- white
label:SetTextColor(255, 0, 0, 255) -- blue

<CUIStatic>::SetHeading(h)

SOC only
purpose:
sets heading (?)
arguments:

  • h <int>

    - heading

<CUIStatic>::GetStretchTexture()

SOC only
purpose:
gets texture stretch status
returns:

  • true

    - texture is stretched

  • false

    - otherwise (default)

<CUIStatic>::SetTextST(text)

SOC only
purpose:
sets text, either explicitely or translates (id); see also: SetText
arguments:

  • text <string>

    - text or string id
example:
label:SetTextST("ui_inv_ok") -- OK (translated)
label:SetTextST("some text") -- some text

<CUIStatic>::GetTextAlign()

SOC only
purpose:
gets current text alignment (

CGameFont.alLeft

by default)
returns:

  • <int [0, 2]>

    - CGameFont flag
example:
label:SetTextAlign(CGameFont.alRight)
local ta = label:GetTextAlign()

if ta == CGameFont.alLeft then
  ta = "alignment = left"
elseif ta == CGameFont.alRight then
  ta = "alignment = right"
else
  ta = "alignment = center"
end

<CUIStatic>::Init(str, x, y, w, h)

SOC only

<CUIStatic>::Init(x, y, w, h)

SOC only

<CUIStatic>::Init(fr)

SOC only
purpose:
displays window and sets its position and size
arguments:

  • str <string>

    - texture

  • x <int>

    - position x

  • y <int>

    - position y

  • w <int>

    - width

  • h <int>

    - height

  • fr <Frect>

    - Frect

<CUIStatic>::SetOriginalRect(x, y, w, h)

SOC only
purpose:
sets rectangle
arguments:

  • x <int>

    - position x

  • y <int>

    - position y

  • w <int>

    - width

  • h <int>

    - height

<CUIStatic>::SetTextureOffset(x, y)

SOC only
purpose:
sets offset for texture; useful if texture you want to load is saved in dds file which contains more textures; see example
arguments:

  • x <int>

    - x offset

  • y <int>

    - y offset

<CUIStatic>::SetTextY(y_pos)

SOC only
purpose:
sets y position of text
arguments:

  • y_pos <int>

    - y position of text

<CUIStatic>::SetColor(argb)

SOC only
purpose:
sets texture color; for text color see SetTextColor
arguments:

  • argb <int>

    - color value (see: GetARGB)
example:
label:Init(20, 60, 128, 128)
label:SetColor(GetARGB(255, 0, 0, 255)) -- blue
label:InitTexture("ui_highlight")

<CUIStatic>::SetText(text)

SOC only
purpose:
sets text; see also: SetTextST
arguments:

  • text <string>

    - text
example:
label:SetText("ui_inv_ok") -- ui_inv_ok
label:SetText(game.translate_string("ui_inv_ok")) -- OK

<CUIStatic>::InitTexture(string)

purpose:
loads texture
arguments:

  • <string>

    - texture
example:
label:InitTexture("ui\\hud_weapon_back") -- path
label:InitTexture("ui_highlight") -- defined in xml

<CUIStatic>::SetTextAlign(flag)

SOC only
purpose:
sets text alignment
arguments:

  • flag <CGameFont>

    - CGameFont flag
example:
label:SetTextAlign(CGameFont.alLeft)
label:SetTextAlign(CGameFont.alCenter)
label:SetTextAlign(CGameFont.alRight)

<CUIStatic>::TextControl()

COP only
purpose:
returns CUITextWnd (text ui control)
returns:

  • <CUITextWnd>

    - CUITextWnd

<CUIStatic>::GetTextureRect()

COP only
purpose:
gets texture rectangle
returns:

  • <Frect>

    - texture rectangle; see: Frect

<CUIStatic>::SetTextureRect(fr)

COP only
purpose:
sets texture rectangle
arguments:

  • fr <Frect>

    - texture rectangle; see: Frect


lua_help CUIWindow

class CUIWindow

Base class for UI controls.
C++ class CUIWindow
{
  CUIWindow();

  function GetFont();
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function SetAutoDelete(boolean);
  function AttachChild(CUIWindow*);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function ResetPPMode();
  function WindowName();
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function SetFont(CGameFont*);
  function IsShown();
  function Init(number, number, number, number);
  function Init(Frect*);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function GetHeight() const;
  function SetWidth(number);
  function Show(boolean);
  function IsEnabled();
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function GetWidth() const;
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
};

<CUIWindow>::GetFont()

SOC only
purpose:
returns current font set for window
returns:

  • <CGameFont>

    - CGameFont

  • nil

    - if font wasn't set

<CUIWindow>::SetWindowName(wnd_name)

purpose:
sets window name
arguments:

  • wnd_name <string>

    - window name
remarks:
  • you can set window name in xml using window_name tag

<CUIWindow>::Enable(b_status)

purpose:
enables/disables window
arguments:

  • b_status <boolean>

    - enable (

    true

    ) or disable (

    false

    ) window

<CUIWindow>::SetHeight(h)

SOC only
purpose:
set window height
arguments:

  • h <int>

    - height

<CUIWindow>::SetAutoDelete(b_status)

purpose:
if b_status is set to true, this control will be automatically removed in parent window destructor
arguments:

  • b_status <boolean>

    - auto-delete status

<CUIWindow>::AttachChild(wnd)

purpose:
adds child window
arguments:

  • wnd <CUIWindow>

    - child window

<CUIWindow>::SetWndPos(x, y)

SOC only

<CUIWindow>::SetWndPos(v2)

COP only
purpose:
sets window position
arguments:

  • x <int>

    - position x

  • y <int>

    - position y

  • v2 <vector2>

    - position; see: vector2

<CUIWindow>::ResetPPMode()

purpose:
disables postprocessing (?) mode

<CUIWindow>::WindowName()

purpose:
returns window name
returns:

  • <string>

    - window name, if it was set (SetWindowName)

  • nil

    - if window name wasn't set

<CUIWindow>::DetachChild(wnd)

purpose:
removes child window
arguments:

  • wnd <CUIWindow>

    - child window

<CUIWindow>::SetPPMode()

purpose:
enables postprocessing (?) mode

<CUIWindow>::SetFont(font)

SOC only
purpose:
sets font
arguments:

  • font <CGameFont>

    - font (see: CGameFont for available fonts)

<CUIWindow>::IsShown()

purpose:
checks whether window is displayed
returns:

  • true

    - window displayed

  • false

    - otherwise

<CUIWindow>::Init(x, y, w, h)

SOC only

<CUIWindow>::Init(fr)

SOC only
purpose:
displays window and sets its position and size
arguments:

  • x <int>

    - position x

  • y <int>

    - position y

  • w <int>

    - width

  • h <int>

    - height

  • fr <Frect>

    - Frect

<CUIWindow>::SetWndRect(x, y, w, h)

SOC only

<CUIWindow>::SetWndRect(fr)

purpose:
sets window rectangle
arguments:

  • x <int>

    - position x

  • y <int>

    - position y

  • w <int>

    - width

  • h <int>

    - height

  • fr <Frect>

    - Frect

<CUIWindow>::GetHeight()

purpose:
gets window height
returns:

  • <int>

    - window height

<CUIWindow>::SetWidth(w)

SOC only
purpose:
sets window width
arguments:

  • w <int>

    - width

<CUIWindow>::Show(b_status)

purpose:
displays/hides window; see also: IsShown
arguments:

  • b_status <boolean>

    - display (

    true

    ) or hide (

    false

    ) window

<CUIWindow>::IsEnabled()

purpose:
checks whether window is enabled; see also: Enable
returns:

  • true

    - window is enabled

  • false

    - otherwise

<CUIWindow>::SetWndSize(w, h)

SOC only

<CUIWindow>::SetWndSize(v2)

COP only
purpose:
sets window size
arguments:

  • w <int>

    - width

  • h <int>

    - height

  • v2 <vector2>

    - window size; see: vector2

<CUIWindow>::GetWidth()

purpose:
gets window width
returns:

  • <int>

    - window width

<CUIWindow>::IsAutoDelete()

purpose:
checks whether window is set as auto-delete; see also: SetAutoDelete
arguments:

  • true

    - set as auto-delete

  • false

    - otherwise

<CUIWindow>::GetWndPos()

COP only
purpose:
gets window position
returns:

  • <vector2>

    - window position; see: vector2


lua_help CUICheckButton

class CUICheckButton

Implements CheckBox ui control. Load from xml file with InitCheck
events:

  • ui_events.BUTTON_CLICKED

    - raised when control was clicked (state of control was changed)
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - CheckBox width

  • height

    - CheckBox height
<!-- config\ui\my_wnd.xml -->
<my_checkbox x="10" y="20" width="50" height="30">
  <window_name>checkbox</window_name>
  <text align="l" font="letterica18" x="0" y="-5" a="255" r="238" g="155" b="23">click me!</text>
</my_checkbox>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local check = xml:InitCheck("my_checkbox", self)

-- or:
local check = CUICheckButton()
check:SetAutoDelete(true)
check:SetWindowName("checkbox")
check:SetFont(GetFontLetterica18Russian())
check:SetTextColor(255, 238, 155, 23)
check:SetTextAlign(CGameFont.alLeft)
check:EnableTextHighlighting(false)
check:SetTextY(-5)
check:Init(10, 20, 50, 30)
check:SetText("click me!")
wnd:AttachChild(check)

C++ class CUICheckButton : CUI3tButton
{
  CUICheckButton();

  function SetHighlightColor(number);
  function GetTextY();
  function Enable(boolean);
  function SetHeight(number);
  function GetText();
  function GetColor() const;
  function GetWidth() const;
  function GetClipperState();
  function GetCheck();
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function SetFont(CGameFont*);
  function IsShown();
  function Show(boolean);
  function GetHeight() const;
  function SetWidth(number);
  function GetTextX();
  function IsEnabled();
  function ResetPPMode();
  function SetDependControl(CUIWindow*);
  function GetHeading();
  function EnableTextHighlighting(boolean);
  function SetStretchTexture(boolean);
  function GetStretchTexture();
  function IsAutoDelete();
  function ClipperOn();
  function GetFont();
  function SetAutoDelete(boolean);
  function SetTextX(number);
  function SetTextColor(number, number, number, number);
  function SetCheck(boolean);
  function AttachChild(CUIWindow*);
  function SetWindowName(string);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function SetTextST(string);
  function GetTextAlign();
  function Init(number, number, number, number);
  function Init(Frect*);
  function Init(string, number, number, number, number);
  function SetText(string);
  function WindowName();
  function ClipperOff();
  function SetTextY(number);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function SetTextureOffset(number, number);
  function SetOriginalRect(number, number, number, number);
  function InitTexture(string);
  function SetHeading(number);
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetTextAlign(number);
  function SetColor(number);
  function GetWndPos(CUIWindow*);
  function TextControl();
  function GetTextureRect();
  function SetTextureRect(Frect*);
};

<CUICheckButton>::GetCheck()

purpose:
returns CheckBox status
returns:

  • true

    - CheckBox is checked

  • false

    - otherwise (unchecked)

<CUICheckButton>::SetDependControl(wnd)

purpose:
sets ui control which will be enabled when CheckBox is unchecked; if CheckBox is checked then depend control (ui window control) is disabled
arguments:

  • wnd <CUIWindow>

    - ui control

<CUICheckButton>::SetCheck(status)

purpose:
sets CheckBox status
arguments:

  • status <boolean>

    - checked (

    true

    ) or unchecked (

    false

    )


lua_help CUICustomEdit CUIEditBox CUIEditBoxEx

class CUICustomEdit

Base class for EditBox (or TextBox) ui control. Use CUIEditBox or CUIEditBoxEx instead.
C++ class CUICustomEdit : CUIWindow
{
  function GetFont();
  function SetNumbersOnly(boolean);
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function SetTextPosX(number);
  function Init(number, number, number, number);
  function Init(Frect*);
  function GetTextAlignment();
  function GetText();
  function SetAutoDelete(boolean);
  function SetTextAlignment(enum CGameFont::EAligment);
  function SetTextColor(number);
  function GetTextColor();
  function AttachChild(CUIWindow*);
  function SetText(string);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function SetTextPosY(number);
  function GetWidth() const;
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function WindowName();
  function IsShown();
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function GetHeight() const;
  function SetWidth(number);
  function Show(boolean);
  function IsEnabled();
  function ResetPPMode();
  function SetFont(CGameFont*);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
  function SetNextFocusCapturer(CUICustomEdit*);
  function CaptureFocus(boolean);
};

<CUICustomEdit>::SetNumbersOnly(b_status)

SOC only
purpose:
sets data type accepted by EditBox: either numbers only or numbers and text
arguments:

  • b_status <boolean>

    - if

    true

    EditBox will accept only numbers (although you can still set text using SetText); if

    false

    EditBox will accept any text (including numbers)

<CUICustomEdit>::SetTextPosX(pos_x)

SOC only
purpose:
sets horizontal text position
arguments:

  • pos_x <int>

    - horizontal text position (x)

<CUICustomEdit>::GetTextAlignment()

SOC only
purpose:
returns text alignment type (

CGameFont.alLeft

by default)
returns:

  • <int [0, 2]>

    - CGameFont flag

<CUICustomEdit>::GetText()

purpose:
gets text
returns:

  • <string>

    - text; empty string if text wasn't set

<CUICustomEdit>::SetTextAlignment(flag)

SOC only
purpose:
sets text alignment
arguments:

  • flag <CGameFont>

    - CGameFont flag

<CUICustomEdit>::SetTextColor(argb)

SOC only
purpose:
sets text color
arguments:

  • argb <int>

    - color value (see: GetARGB)

<CUICustomEdit>::GetTextColor()

SOC only
purpose:
gets text color
returns:

  • <int>

    - current color packed as int (see: GetARGB)

<CUICustomEdit>::SetText(text)

purpose:
sets text
arguments:

  • text <string>

    - text

<CUICustomEdit>::SetTextPosY(pos_y)

SOC only
purpose:
sets vertical text position
arguments:

  • pos_y <int>

    - vertical text position (y)

<CUICustomEdit>::SetNextFocusCapturer(ed_box)

COP only
purpose:
sets EditBox control which will receive focus once this EditBox lost it; useful for switching EditBox controls with TAB key
arguments:

  • ed_box <CUICustomEdit>

    - EditBox control

<CUICustomEdit>::CaptureFocus(status)

COP only
purpose:
sets/unsets focus control for EditBox
arguments:

  • status <boolean>

    - whether receive focus (

    true

    ) or not (

    false

    )

class CUIEditBox

Implements EditBox (or TextBox) ui control for one-line text. Texture consists of 3 parts: begin (_b), back, end (_e), for instance ui_linetext_e. Load from xml file with InitEditBox
events:

  • ui_events.EDIT_TEXT_CHANGED

    - raised when content of control has been changed

  • ui_events.EDIT_TEXT_COMMIT

    - raised when ENTER was pressed (content of control has been accepted)
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - edit box width

  • height

    - edit box height

  • max_symb_count

    - number of input characters

  • password

    - display text (0) or asterisks (1)
<!-- config\ui\my_wnd.xml -->
<edit_box_passwd x="294" y="91" width="250" height="24" password="1" max_symb_count="26">
  <window_name>ed_passwd</window_name>
  <text font="letterica18" r="207" g="155" b="80" />
  <texture>ui_linetext_e</texture>
</edit_box_passwd>

<edit_box_letters x="294" y="91" width="250" height="24" password="0" max_symb_count="26">
  <window_name>ed_letters</window_name>
  <text font="letterica18" align="l" r="238" g="155" b="23">some text ...</text>
  <texture>ui_linetext_e</texture>
</edit_box_letters>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local edit_box = xml:InitEditBox("edit_box_letters", self)

-- or:
local ed_box = CUIEditBox()
ed_box:SetAutoDelete(true)
ed_box:SetWindowName("ed_letters")
ed_box:Init(294, 91, 250, 24)
ed_box:InitTexture("ui_linetext_e")
ed_box:SetFont(GetFontLetterica18Russian())
ed_box:SetTextAlignment(CGameFont.alLeft)
ed_box:SetTextColor(GetARGB(255, 238, 155, 23))
ed_box:SetTextPosY(-5) -- a bit up
ed_box:SetText("some text ...")
ed_box:SetNumbersOnly(false)
wnd:AttachChild(ed_box)

C++ class CUIEditBox : CUICustomEdit
{
  CUIEditBox();

  function GetWidth() const;
  function GetTextAlignment();
  function AttachChild(CUIWindow*);
  function SetWindowName(string);
  function Enable(boolean);
  function SetText(string);
  function SetTextPosX(number);
  function SetFont(CGameFont*);
  function ResetPPMode();
  function GetText();
  function SetAutoDelete(boolean);
  function GetTextColor();
  function SetTextColor(number);
  function Show(boolean);
  function SetNumbersOnly(boolean);
  function DetachChild(CUIWindow*);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function SetTextPosY(number);
  function WindowName();
  function Init(number, number, number, number);
  function Init(Frect*);
  function SetPPMode();
  function SetTextAlignment(enum CGameFont::EAligment);
  function IsShown();
  function GetFont();
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function GetHeight() const;
  function SetWidth(number);
  function InitTexture(string);
  function IsEnabled();
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetHeight(number);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
  function SetNextFocusCapturer(CUICustomEdit*);
  function CaptureFocus(boolean);
};

<CUIEditBox>::InitTexture(string)

purpose:
loads texture
arguments:

  • <string>

    - texture

class CUIEditBoxEx

Implements EditBox (or TextBox) ui control for multiline text. Texture - like for FrameWindow control (consists of 9 parts)
C++ class CUIEditBoxEx : CUICustomEdit
{
  CUIEditBoxEx();

  function GetWidth() const;
  function GetTextAlignment();
  function AttachChild(CUIWindow*);
  function SetWindowName(string);
  function Enable(boolean);
  function SetText(string);
  function SetTextPosX(number);
  function SetFont(CGameFont*);
  function ResetPPMode();
  function GetText();
  function SetAutoDelete(boolean);
  function GetTextColor();
  function SetTextColor(number);
  function Show(boolean);
  function SetNumbersOnly(boolean);
  function DetachChild(CUIWindow*);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function SetTextPosY(number);
  function WindowName();
  function Init(number, number, number, number);
  function Init(Frect*);
  function SetPPMode();
  function SetTextAlignment(enum CGameFont::EAligment);
  function IsShown();
  function GetFont();
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function GetHeight() const;
  function SetWidth(number);
  function InitTexture(string);
  function IsEnabled();
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetHeight(number);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
  function SetNextFocusCapturer(CUICustomEdit*);
  function CaptureFocus(boolean);
};

<CUIEditBoxEx>::InitTexture(string)

purpose:
loads texture
arguments:

  • <string>

    - texture


lua_help CUIDialogWnd

class CUIDialogWnd

Base class for UI dialog windows.
C++ class CUIDialogWnd : CUIWindow
{
  function GetFont();
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function SetAutoDelete(boolean);
  function SetHolder(CDialogHolder*);
  function AttachChild(CUIWindow*);
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function Init(number, number, number, number);
  function Init(Frect*);
  function GetWidth() const;
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function WindowName();
  function IsShown();
  function GetHolder();
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function GetHeight() const;
  function SetWidth(number);
  function Show(boolean);
  function IsEnabled();
  function ResetPPMode();
  function SetFont(CGameFont*);
  function IsAutoDelete();
  function ShowDialog(boolean);
  function HideDialog();
  function GetWndPos(CUIWindow*);
};

<CUIDialogWnd>::SetHolder(parent)

purpose:
???
aguments:

  • parent <CDialogHolder>

    - ???

<CUIDialogWnd>::GetHolder()

purpose:
???
returns:

  • <CDialogHolder>

    - ???

<CUIDialogWnd>::ShowDialog(noidea)

COP only
purpose:
displays dialog window
aguments:

  • noidea <boolean>

    - ???

<CUIDialogWnd>::HideDialog()

COP only
purpose:
hides dialog window


lua_help CUIFrameLineWnd

class CUIFrameLineWnd

Implements frame line control. Load from xml file with InitFrameLine.
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - frame line width

  • height

    - frame line height

  • title

    - tag is optional and defines frame line caption text
<!-- config\ui\my_wnd.xml -->
<my_frame_line x="10" y="20" width="434" height="21">
  <window_name>frm_line</window_name>
  <texture>ui_string_03</texture>
  <title x="10" y="1" width="434" height="18">
    <text font="letterica16" r="255" g="255" b="255" complex_mode="0">frame line caption text</text>
  </title>
</my_frame_line>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local frame_line = xml:InitFrameLine("my_frame_line", self)

-- or:
local frame_line = CUIFrameLineWnd()
frame_line:SetAutoDelete(true)
frame_line:SetWindowName("frm_line")
frame_line:SetColor(GetARGB(255, 0, 255, 0)) -- green
frame_line:Init("ui_string_03", 10, 20, 434, 21, true)
wnd:AttachChild(frame_line)
frame_line:GetTitleStatic():SetFont(GetFontLetterica16Russian())
frame_line:GetTitleStatic():SetTextColor(255, 255, 255, 255)
frame_line:GetTitleStatic():SetText("frame line caption text")
frame_line:GetTitleStatic():SetTextX(10)
frame_line:GetTitleStatic():SetTextY(7)

C++ class CUIFrameLineWnd : CUIWindow
{
  CUIFrameLineWnd();

  function GetFont();
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function SetAutoDelete(boolean);
  function SetOrientation(boolean);
  function GetTitleStatic();
  function AttachChild(CUIWindow*);
  function SetColor(number);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function Init(number, number, number, number);
  function Init(Frect*);
  function Init(string, number, number, number, number, boolean);
  function GetWidth() const;
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function WindowName();
  function IsShown();
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function GetHeight() const;
  function SetWidth(number);
  function Show(boolean);
  function IsEnabled();
  function ResetPPMode();
  function SetFont(CGameFont*);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
};

<CUIFrameLineWnd>::SetOrientation(b_type)

SOC only
purpose:
sets texture orientation (horizontal/vertical); only texture will be affected (caption stays horizontal)
arguments:

  • b_type <boolean>

    - texture orientation: horizontal (

    true

    ) or vertical (

    false

    )

<CUIFrameLineWnd>::GetTitleStatic()

SOC only
purpose:
returns handle to caption window
returns:

  • <CUIStatic>

    - caption (

    title

    tag in xml) window

<CUIFrameLineWnd>::Init(str, x, y, w, h, o)

SOC only

<CUIFrameLineWnd>::Init(x, y, w, h)

SOC only

<CUIFrameLineWnd>::Init(fr)

SOC only
purpose:
displays window and sets its position and size
arguments:

  • str <string>

    - texture

  • x <int>

    - position x

  • y <int>

    - position y

  • w <int>

    - width

  • h <int>

    - height

  • o <boolean>

    - texture orientation - see: SetOrientation

  • fr <Frect>

    - Frect

<CUIFrameLineWnd>::SetHeight(h)

purpose:
set window height
arguments:

  • h <int>

    - height

<CUIFrameLineWnd>::SetWidth(w)

purpose:
sets window width
arguments:

  • w <int>

    - width

<CUIFrameLineWnd>::SetColor(argb)

purpose:
sets texture color
arguments:

  • argb <int>

    - color value (see: GetARGB)


lua_help CUIFrameWindow

class CUIFrameWindow

Implements frame window.
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - frame width

  • height

    - frame height

  • title

    - tag is optional and defines frame caption text
<!-- config\ui\my_wnd.xml -->
<frame_wnd x="0" y="0" width="600" height="600">
  <window_name>my_frame_wnd</window_name>
  <texture>ui_stroketextbox</texture>
  <title x="10" y="10" width="434" height="18">
    <text font="letterica16" r="255" g="255" b="255" complex_mode="0">frame caption text
  </title>
</frame_wnd>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local frame_line = xml:InitFrame("frame_wnd", self)

-- or:
local frame = CUIFrameWindow()
frame:SetAutoDelete(true)
frame:SetWindowName("my_frame_wnd")
frame:Init("ui_stroketextbox", 0, 0, 600, 600)
wnd:AttachChild(frame)
frame:GetTitleStatic():SetFont(GetFontLetterica16Russian())
frame:GetTitleStatic():SetTextColor(255, 255, 255, 255)
frame:GetTitleStatic():SetText("frame caption text")
frame:GetTitleStatic():SetTextX(10)
frame:GetTitleStatic():SetTextY(10)

C++ class CUIFrameWindow : CUIWindow
{
  CUIFrameWindow();

  function GetFont();
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function SetAutoDelete(boolean);
  function GetTitleStatic();
  function AttachChild(CUIWindow*);
  function SetColor(number);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function Init(number, number, number, number);
  function Init(Frect*);
  function Init(string, number, number, number, number);
  function GetWidth() const;
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function WindowName();
  function IsShown();
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function GetHeight() const;
  function SetWidth(number);
  function Show(boolean);
  function IsEnabled();
  function ResetPPMode();
  function SetFont(CGameFont*);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
};

<CUIFrameWindow>::GetTitleStatic()

SOC only
purpose:
returns handle to caption window
returns:

  • <CUIStatic>

    - caption (

    title

    tag in xml) window

<CUIFrameWindow>::Init(str, x, y, w, h)

SOC only

<CUIFrameWindow>::Init(x, y, w, h)

SOC only

<CUIFrameWindow>::Init(fr)

SOC only
purpose:
displays window and sets its position and size
arguments:

  • str <string>

    - texture

  • x <int>

    - position x

  • y <int>

    - position y

  • w <int>

    - width

  • h <int>

    - height

  • fr <Frect>

    - Frect

<CUIFrameLineWnd>::SetHeight(h)

purpose:
set window height
arguments:

  • h <int>

    - height

<CUIFrameLineWnd>::SetWidth(w)

purpose:
sets window width
arguments:

  • w <int>

    - width

<CUIFrameLineWnd>::SetColor(argb)

purpose:
sets texture color
arguments:

  • argb <int>

    - color value (see: GetARGB)


lua_help CUILabel

class CUILabel

Implements label control (for static text or texture). Load from xml file with InitLabel. Available only in SOC.
label = CUILabel()
label:SetAutoDelete(true)
label:SetWindowName("my_label")
label:Init(10, 10, 200, 20)
label:SetText("label text")
wnd:AttachChild(label)

C++ class CUILabel : CUIFrameLineWnd
{
  CUILabel();

  function GetFont();
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function GetText();
  function SetAutoDelete(boolean);
  function SetOrientation(boolean);
  function SetText(string);
  function IsAutoDelete();
  function AttachChild(CUIWindow*);
  function SetFont(CGameFont*);
  function SetWndPos(number, number);
  function ResetPPMode();
  function DetachChild(CUIWindow*);
  function Init(number, number, number, number);
  function Init(Frect*);
  function Init(string, number, number, number, number, boolean);
  function SetPPMode();
  function WindowName();
  function IsShown();
  function Show(boolean);
  function GetTitleStatic();
  function GetHeight() const;
  function SetWidth(number);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function IsEnabled();
  function SetWndSize(number, number);
  function GetWidth() const;
  function SetColor(number);
};


lua_help CUIListBox

class CUIListBox

Implements ListView ui control. Load from xml file with InitListBox. Available only in COP.
events:

  • ui_events.LIST_ITEM_CLICKED

    - raised if list item was selected (clicked)

  • ui_events.WINDOW_LBUTTON_DB_CLICK

    - list item double-clicked with left mouse button
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - list width

  • height

    - list height

  • item_height

    - list items height

  • always_show_scroll

    - if set to 0 (default) scrollbar will be displayed only when needed; 1 - displays scrollbar always

  • scroll_profile

    - sets scrollbar style:

    • default

      - sets default scrollbar style

    • pda_logs

      - sets PDA scrollbar style
remarks:
  • to add items to ListView control, create new class and derive from CUIListBoxItem
  • then if you want to add item, create instance of your class and use method AddExistingItem
<!-- config\ui\my_wnd.xml -->
<listview x="10" y="10" width="400" height="300" item_height="18"
  always_show_scroll="1" scroll_profile="default" />

class "list_item" (CUIListBoxItem)
function list_item:__init(height) super(height)
  self.text = self:GetTextItem()
end

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local list = xml:InitListBox("listview", self)
list:ShowSelectedItem(true)

local item = nil
for i = 1, 30 do
  item = list_item(18)
  item:SetText("item " .. i)
  list:AddExistingItem(item)
end

C++ class CUIListBox : CUIScrollView
{
  CUIListBox();

  function SetWindowName(string);
  function Enable(boolean);
  function RemoveWindow(CUIWindow*);
  function ScrollToBegin();
  function GetMinScrollPos();
  function AddExistingItem(CUIListBoxItem*);
  function AddWindow(CUIWindow*, boolean);
  function GetWidth() const;
  function Clear();
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function IsShown();
  function Show(boolean);
  function GetHeight() const;
  function IsEnabled();
  function ResetPPMode();
  function GetWndPos(CUIWindow*);
  function GetCurrentScrollPos();
  function SetAutoDelete(boolean);
  function AttachChild(CUIWindow*);
  function AddTextItem(string);
  function SetWndPos(vector2);
  function ScrollToEnd();
  function RemoveItem(CUIWindow*);
  function GetMaxScrollPos();
  function GetItemByIndex(number);
  function WindowName();
  function GetSelectedIndex();
  function GetSelectedItem();
  function SetWndRect(Frect);
  function SetScrollPos(number);
  function GetSize();
  function RemoveAll();
  function ShowSelectedItem(boolean);
  function SetWndSize(vector2);
  function GetItem(number);
  function IsAutoDelete();
};

<CUIListBox>::AddExistingItem(item)

COP only
purpose:
adds item to list
arguments:

  • item <CUIListBoxItem>

    - item (CUIListBoxItem)

<CUIListBox>::AddTextItem(txt)

COP only
purpose:
adds text (as item) ?
arguments:

  • txt <string>

    - text ?

<CUIListBox>::RemoveItem(wnd)

COP only
purpose:
removes item from list
arguments:

  • wnd <CUIWindow>

    - item (CUIListBoxItem)

<CUIListBox>::GetItemByIndex(index)

COP only
purpose:
returns item
arguments:

  • index <uint> [0, <CUIListBox>::GetSize() - 1]

    - item position
returns:

  • <CUIListBoxItem>

    - item (CUIListBoxItem)

  • nil

    - item not found

<CUIListBox>::GetSelectedIndex()

COP only
purpose:
returns selected item index (position on list)
returns:

  • <uint> [0, <CUIListBox>::GetSize() - 1]

    - index of selected item

  • -1

    - item not selected

<CUIListBox>::GetSelectedItem()

COP only
purpose:
returns selected item
returns:

  • <CUIListBoxItem>

    - selected item (CUIListBoxItem)

  • nil

    - item not selected

<CUIListBox>::GetSize()

COP only
purpose:
returns number of items
returns:

  • <uint>

    - number of items

<CUIListBox>::RemoveAll()

COP only
purpose:
removes all items from list

<CUIListBox>::ShowSelectedItem(status)

COP only
purpose:
enables/disables visual selection of items; by default item selection is not visible
arguments:

  • status <boolean>

    - enable (

    true

    ) or disable (

    false

    )

<CUIListBox>::GetItem(index)

COP only
purpose:
returns item
arguments:

  • index <uint> [0, <CUIListBox>::GetSize() - 1]

    - item position
returns:

  • <CUIListBoxItem>

    - item (CUIListBoxItem)

  • nil

    - if item not found


lua_help CUIScrollView

class CUIScrollView

Implements ScrollView control. Load from xml file with InitScrollView.
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - scroll bar window width

  • height

    - scroll bar window height

  • always_show_scroll

    - whether display vertical scrollbar (0 = only when scrollbar's child windows overfilled scrollbar window area)
<!-- config\ui\my_wnd.xml -->
<scroll x="10" y="20" width="400" height="100" always_show_scroll="1">
  <window_name>scroll</window_name>
</scroll>
<scroll_item x="0" y="0" width="100" height="20">
  <text align="l" x="0" y="0" font="letterica18">some text ...</text>
</scroll_item>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local scroll = xml:InitScrollView("scroll", self)
for i = 1, 20 do
  local itm = nil
  if i == 1 then
    itm = xml:InitButton("scroll_item", nil)
    itm:SetWindowName("btn_1")
    self:Register(itm, "btn_1")
  else
    itm = xml:InitStatic("scroll_item", nil)
  end
  itm:SetText("This is " .. i .. " line ...")
  scroll:AddWindow(itm, true)
end

function base_wnd:InitCallBacks()
  self:AddCallback("btn_1", ui_events.BUTTON_CLICKED, self.OnExit, self)
end

function base_wnd:OnExit()
  self:GetHolder():start_stop_menu(self, true)
end

C++ class CUIScrollView : CUIWindow
{
  CUIScrollView();

  function GetCurrentScrollPos();
  function GetMinScrollPos();
  function GetMaxScrollPos();
  function SetScrollPos(number);
  function ScrollToBegin();
  function ScrollToEnd();
  function AddWindow(CUIWindow*, boolean);
  function RemoveWindow(CUIWindow*);
  function Clear();
  function GetFont();
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function SetAutoDelete(boolean);
  function Init(number, number, number, number);
  function Init(Frect*);
  function AttachChild(CUIWindow*);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function GetWidth() const;
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function WindowName();
  function IsShown();
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function GetHeight() const;
  function SetWidth(number);
  function Show(boolean);
  function IsEnabled();
  function ResetPPMode();
  function SetFont(CGameFont*);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
};

<CUIScrollView>::GetCurrentScrollPos()

purpose:
returns current scrollbar position
returns:

  • <number>

    - current scrollbar position

<CUIScrollView>::GetMinScrollPos()

purpose:
returns min scrollbar position
returns:

  • <number>

    - min scrollbar position

<CUIScrollView>::GetMaxScrollPos()

purpose:
returns max scrollbar position
returns:

  • <number>

    - max scrollbar position

<CUIScrollView>::SetScrollPos(pos)

purpose:
sets scrollbar position
arguments:

  • pos <number>

    - scrollbar position

<CUIScrollView>::ScrollToBegin()

purpose:
scrolls to begin

<CUIScrollView>::ScrollToEnd()

purpose:
scrolls to end

<CUIScrollView>::AddWindow(wnd, noidea)

purpose:
adds window (ui control) to scrollbar
arguments:

  • wnd <CUIWindow>

    - window (ui control)

  • noidea <boolean>

    - ???

<CUIScrollView>::RemoveWindow(wnd)

purpose:
removes window (child window) from scrollbar
arguments:

  • wnd <CUIWindow>

    - window to remove

<CUIScrollView>::Clear()

purpose:
removes all items (child windows) from scrollbar control


lua_help CUIListItem CUIListItemEx CUIListBoxItem

class CUIListItem

Base class for ListView (CUIListWnd) item. Available only in SOC.
C++ class CUIListItem : CUIButton
{
  CUIListItem();

  function SetHighlightColor(number);
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function GetText();
  function GetColor() const;
  function GetWidth() const;
  function SetTextX(number);
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function SetFont(CGameFont*);
  function IsShown();
  function Show(boolean);
  function GetHeight() const;
  function SetWidth(number);
  function GetTextX();
  function IsEnabled();
  function ResetPPMode();
  function GetHeading();
  function EnableTextHighlighting(boolean);
  function SetStretchTexture(boolean);
  function SetColor(number);
  function ClipperOn();
  function GetFont();
  function SetAutoDelete(boolean);
  function GetClipperState();
  function SetTextColor(number, number, number, number);
  function SetHeading(number);
  function AttachChild(CUIWindow*);
  function GetTextY();
  function SetWndPos(number, number);
  function SetOriginalRect(number, number, number, number);
  function GetTextAlign();
  function Init(number, number, number, number);
  function Init(Frect*);
  function Init(string, number, number, number, number);
  function SetTextureOffset(number, number);
  function WindowName();
  function ClipperOff();
  function SetTextY(number);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function SetText(string);
  function SetTextST(string);
  function InitTexture(string);
  function GetStretchTexture();
  function SetWndSize(number, number);
  function SetTextAlign(number);
  function IsAutoDelete();
};

class CUIListItemEx

Base class for ListView (CUIListWnd) item. Available only in SOC.
C++ class CUIListItemEx : CUIListItem
{
  CUIListItemEx();

  function SetSelectionColor(number);
  function SetHighlightColor(number);
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function GetText();
  function GetColor() const;
  function GetWidth() const;
  function SetTextX(number);
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function SetFont(CGameFont*);
  function IsShown();
  function Show(boolean);
  function GetHeight() const;
  function SetWidth(number);
  function GetTextX();
  function IsEnabled();
  function ResetPPMode();
  function GetHeading();
  function EnableTextHighlighting(boolean);
  function SetStretchTexture(boolean);
  function SetColor(number);
  function ClipperOn();
  function GetFont();
  function SetAutoDelete(boolean);
  function GetClipperState();
  function SetTextColor(number, number, number, number);
  function SetHeading(number);
  function AttachChild(CUIWindow*);
  function GetTextY();
  function SetWndPos(number, number);
  function SetOriginalRect(number, number, number, number);
  function GetTextAlign();
  function Init(number, number, number, number);
  function Init(Frect*);
  function Init(string, number, number, number, number);
  function SetTextureOffset(number, number);
  function WindowName();
  function ClipperOff();
  function SetTextY(number);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function SetText(string);
  function SetTextST(string);
  function InitTexture(string);
  function GetStretchTexture();
  function SetWndSize(number, number);
  function SetTextAlign(number);
  function IsAutoDelete();
};

<CUIListItemEx>::SetSelectionColor(argb)

SOC only
purpose:
sets selection color
arguments:

  • argb <uint>

    - color packed as int number (see: GetARGB)

class CUIListItemPair

obsolete (removed)
C++ class CUIListItemPair : CUIListItemEx
{
  CUIListItemPair();

  function SetHighlightColor(number);
  function SetSelectionColor(number);
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function GetText();
  function GetColor() const;
  function GetWidth() const;
  function SetTextX(number);
  function SetTextFileName(string);
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function SetFont(CGameFont*);
  function IsShown();
  function SetBorder(number);
  function Show(boolean);
  function GetHeight() const;
  function SetWidth(number);
  function GetTextX();
  function IsEnabled();
  function ResetPPMode();
  function GetBorder();
  function SetFontDateTime(CGameFont*);
  function GetHeading();
  function EnableTextHighlighting(boolean);
  function SetFontFileName(CGameFont*);
  function SetOriginalRect(number, number, number, number);
  function SetStretchTexture(boolean);
  function SetTextDateTime(string);
  function GetTextFileName();
  function SetColor(number);
  function ClipperOn();
  function GetFont();
  function SetAutoDelete(boolean);
  function GetClipperState();
  function SetTextColor(number, number, number, number);
  function SetHeading(number);
  function AttachChild(CUIWindow*);
  function GetTextY();
  function GetStretchTexture();
  function GetTextDateTime();
  function GetTextAlign();
  function SetTextST(string);
  function SetTextureOffset(number, number);
  function WindowName();
  function ClipperOff();
  function SetTextY(number);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function SetText(string);
  function Init(number, number, number, number);
  function Init(Frect*);
  function Init(string, number, number, number, number);
  function InitTexture(string);
  function SetWndPos(number, number);
  function SetWndSize(number, number);
  function SetTextAlign(number);
  function IsAutoDelete();
};

class CUIListBoxItem

Base class for ListView (CUIListBox) item. Available only in COP.
C++ class CUIListBoxItem : CUIFrameLineWnd
{
  CUIListBoxItem(number);

  function SetWindowName(string);
  function GetWndPos(CUIWindow*);
  function SetHeight(number);
  function AddIconField(number);
  function SetAutoDelete(boolean);
  function SetTextColor(number);
  function AddTextField(string, number);
  function AttachChild(CUIWindow*);
  function GetTextItem();
  function SetWndPos(vector2);
  function IsAutoDelete();
  function Enable(boolean);
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function WindowName();
  function IsShown();
  function ResetPPMode();
  function SetWndRect(Frect);
  function GetHeight() const;
  function SetWidth(number);
  function Show(boolean);
  function IsEnabled();
  function SetWndSize(vector2);
  function GetWidth() const;
  function SetColor(number);
};

<CUIListBoxItem>::CUIListBoxItem(h)

COP only
purpose:
object constructor
arguments:

  • h <uint>

    - item height
returns:

  • <CUIListBoxItem>

    - object

<CUIListBoxItem>::AddIconField(w)

COP only
purpose:
adds small image (icon)
arguments:

  • w <uint>

    - width

<CUIListBoxItem>::SetTextColor(argb)

COP only
purpose:
sets text color
arguments:

  • argb <int>

    - color value (see: GetARGB)

<CUIListBoxItem>::AddTextField(txt, w)

COP only
purpose:
adds text
arguments:

  • txt <string>

    - text

  • w <uint>

    - width

<CUIListBoxItem>::GetTextItem()

COP only
purpose:
gets text window
returns:

  • <CUITextWnd>

    - CUITextWnd


lua_help CUIListWnd

class CUIListWnd

Implements ListView ui control. Load from xml file with InitList. Available only in SOC.
events:

  • ui_events.LIST_ITEM_CLICKED

    - raised if list item was selected (clicked)

  • ui_events.WINDOW_LBUTTON_DB_CLICK

    - list item double-clicked with left mouse button
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - list width

  • height

    - list height

  • item_height

    - list items height

  • always_show_scroll

    - if set to 0 (default) scrollbar will be displayed only when needed; 1 - displays scrollbar always

  • flip_vert

    - if set to 1 (default is 0) items will be flipped

  • scroll_profile

    - sets scrollbar style:

    • default

      - sets default scrollbar style

    • pda

      - sets PDA scrollbar style
remarks:
  • to add items to ListView control, create new class and derive from either CUIListItem or CUIListItemEx
  • then if you want to add item, create instance of your class and use method AddItem
<!-- config\ui\my_wnd.xml -->
<listview x="10" y="10" width="400" height="300" item_height="18"
  always_show_scroll="1" flip_vert="0" scroll_profile="pda" />

class "list_item" (CUIListItemEx)
function list_item:__init(txt) super()
  self.text = CUIStatic()
  self.text:SetAutoDelete(true)
  self.text:SetText(txt)
  self:AttachChild(self.text)
end

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local list = xml:InitList("listview", self)
list:ShowSelectedItem(true)

for i = 1, 30 do
  list:AddItem(list_item("item " .. i))
end

C++ class CUIListWnd : CUIWindow
{
  CUIListWnd();

  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function ActivateList(boolean);
  function ScrollToBegin();
  function GetVertFlip();
  function GetWidth() const;
  function GetSize();
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function SetFont(CGameFont*);
  function IsShown();
  function Show(boolean);
  function GetHeight() const;
  function SetWidth(number);
  function IsEnabled();
  function ResetPPMode();
  function SetItemHeight(number);
  function GetFont();
  function EnableScrollBar(boolean);
  function GetItemPos(CUIListItem*);
  function ResetFocusCapture();
  function GetSelectedItem();
  function SetAutoDelete(boolean);
  function ShowSelectedItem(boolean);
  function SetTextColor(number);
  function GetFocusedItem();
  function AttachChild(CUIWindow*);
  function SetFocusedItem(number);
  function SetWndPos(number, number);
  function ScrollToEnd();
  function IsListActive();
  function Init(number, number, number, number);
  function Init(Frect*);
  function IsScrollBarEnabled();
  function WindowName();
  function SetVertFlip(boolean);
  function GetLongestSignWidth();
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function ScrollToPos(number);
  function RemoveAll();
  function RemoveItem(number);
  function AddItem(CUIListItem*);
  function SetWndSize(number, number);
  function GetItem(number);
  function IsAutoDelete();
};

<CUIListWnd>::ActivateList(status)

SOC only
purpose:
activates/deactivates list window
arguments:

  • status <boolean>

    - activate (

    true

    ) or deactivate (

    false

    ) list window

<CUIListWnd>::ScrollToBegin()

SOC only
purpose:
scrolls to begin

<CUIListWnd>::GetVertFlip()

SOC only
purpose:
checks whether items are flipped
returns:

  • true

    - items are flipped

  • false

    - otherwise

<CUIListWnd>::GetSize()

SOC only
purpose:
returns number of items
returns:

  • <uint>

    - number of items

<CUIListWnd>::SetItemHeight(h)

SOC only
purpose:
sets items height; you can also set items height with item_height attribute in xml
arguments:

  • h <uint>

    - height

<CUIListWnd>::EnableScrollBar(status)

SOC only
purpose:
enables/disables scrollbars
arguments:

  • status <boolean>

    - enable (

    true

    ) or disable (

    false

    ) scrollbar

<CUIListWnd>::GetItemPos(item)

SOC only
purpose:
returns item index (position on list)
arguments:

  • item <CUIListItem> or <CUIListItemEx>

    - item (CUIListItem or CUIListItemEx)
returns:

  • <uint> [0, <CUIListWnd>::GetSize() - 1]

    - item position

  • -1

    - item not found
example:
local index = self.list:GetSelectedItem()
if index ~= -1 then
  local item = self.list:GetItem(index)
  if item ~= nil then
    local pos = self.list:GetItemPos(item)
    if index == pos then
      printf("equal") -- equal
    end
  end
end

<CUIListWnd>::ResetFocusCapture()

SOC only
purpose:
resets capture; you can use it to highlight items - see example below
example:
-- highlight item trick:
self.list:SetFocusedItem(0)
self.list:ResetFocusCapture()

<CUIListWnd>::GetSelectedItem()

SOC only
purpose:
returns index (postion on list) of selected item
returns:

  • <uint> [0, <CUIListWnd>::GetSize() - 1]

    - index of selected item

  • -1

    - item not selected

<CUIListWnd>::ShowSelectedItem(status)

SOC only
purpose:
enables/disables visual selection of items; by default item selection is not visible
arguments:

  • status <boolean>

    - enable (

    true

    ) or disable (

    false

    )

<CUIListWnd>::SetTextColor(argb)

SOC only
purpose:
sets text color (for items); doesn't work, set text color for each item separately (CUIListItem or CUIListItemEx)
arguments:

  • argb <uint>

    - color value packet as uint number; see: GetARGB

<CUIListWnd>::GetFocusedItem()

SOC only
purpose:
returns index (position on list) of focused item; only if it was set by SetFocusedItem otherwise returns -1
arguments:

  • <uint> [0, <CUIListWnd>::GetSize() - 1]

    - index of focused item

  • -1

    - item not focused

<CUIListWnd>::SetFocusedItem(index)

SOC only
purpose:
sets focus for item (selects item but without raising callback)
arguments:

  • index <uint> [0, <CUIListWnd>::GetSize() - 1]

    - item position

<CUIListWnd>::ScrollToEnd()

SOC only
purpose:
scrolls to end

<CUIListWnd>::IsListActive()

SOC only
purpose:
checks whether list window is activated (focused)
returns:

  • true

    - list window is active

  • false

    - otherwise (inactive)

<CUIListWnd>::IsScrollBarEnabled()

SOC only
purpose:
checks whether scrollbar is enabled
returns:

  • true

    - scrollbar is enabled

  • false

    - otherwise

<CUIListWnd>::SetVertFlip(status)

SOC only
purpose:
sets/unsets items vertical flip; you can also use flip_vert attribute in xml; by default flip is off (0, false)
arguments:

  • status <boolean>

    - set (

    true

    ) or unset (

    false

    ) items vertical flip

<CUIListWnd>::GetLongestSignWidth()

obsolete (removed)

<CUIListWnd>::ScrollToPos(index)

SOC only
purpose:
scrolls to item
arguments:

  • index <uint> [0, <CUIListWnd>::GetSize() - 1]

    - item position

<CUIListWnd>::RemoveAll()

SOC only
purpose:
removes all items from list

<CUIListWnd>::RemoveItem(index)

SOC only
purpose:
removes item
arguments:

  • index <uint> [0, <CUIListWnd>::GetSize() - 1]

    - item position

<CUIListWnd>::AddItem(item)

SOC only
purpose:
adds item to list
arguments:

  • item <CUIListItem> or <CUIListItemEx>

    - item (CUIListItem or CUIListItemEx)

<CUIListWnd>::GetItem(index)

SOC only
purpose:
returns item
arguments:

  • index <uint> [0, <CUIListWnd>::GetSize() - 1]

    - item position
returns:

  • <CUIListItem> or <CUIListItemEx>

    - item (CUIListItem or CUIListItemEx)

  • nil

    - if item not found


lua_help CUIProgressBar

class CUIProgressBar

Implements progress bar control. Load from xml file with InitProgressBar.
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - progress bar width

  • height

    - progress bar height

  • horz

    - whether progress bar should be displayed horizontally or vertically

  • min

    - min progress bar position

  • max

    - max progress bar position

  • pos

    - progress bar starting position
xml tags:

  • min_color

    - progress bar color when min value reached (optional)

  • max_color

    - progress bar color when max value reached (optional)

  • progress

    - progress bar texture

  • background

    - progress bar background texture (slot); optional - you can set background texture as separated static texture, or not use background at all
<!-- config\ui\my_wnd.xml -->
<pbar x="20" y="200" width="120" height="20" horz="1" min="0" max="100" pos="100">
  <window_name>my_pbar</window_name>
  <min_color r="255" g="0" b="0" />
  <max_color r="0" g="255" b="0" />
  <progress>
    <texture>ui_mg_progress_efficiency_full</texture>
  </progress>
  <background>
    <texture>ui_mg_progress_efficiency_empty</texture>
  </background>
</pbar>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local pbar = xml:InitProgressBar("pbar", self)

C++ class CUIProgressBar : CUIWindow
{
  CUIProgressBar();

  function GetRange_min();
  function GetRange_max();
  function SetRange(number, number);
  function SetProgressPos(number);
  function GetProgressPos();
  function GetFont();
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function SetAutoDelete(boolean);
  function AttachChild(CUIWindow*);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function Init(number, number, number, number);
  function Init(Frect*);
  function GetWidth() const;
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function WindowName();
  function IsShown();
  function SetWndSize(number, number);
  function SetWndPos(vector2);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function GetHeight() const;
  function SetWidth(number);
  function Show(boolean);
  function IsEnabled();
  function ResetPPMode();
  function SetFont(CGameFont*);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
};

<CUIProgressBar>::GetRange_min()

purpose:
gets min progress bar value (

min

attribute in xml definition)
returns:

  • <number>

    - min progress bar value

<CUIProgressBar>::GetRange_max()

purpose:
gets max progress bar value (

max

attribute in xml definition)
returns:

  • <number>

    - max progress bar value

<CUIProgressBar>::SetRange(min, max) obsolete (removed)

<CUIProgressBar>::SetProgressPos(pos)

purpose:
sets progress bar value
arguments:

  • pos <number [min, max]>

    - progress bar value

<CUIProgressBar>::GetProgressPos()

purpose:
gets current progress bar value
returns:

  • <number [min, max]>

    - current progress bar value


lua_help CUIScriptWnd

class CUIScriptWnd

Base class for scripted ui window.
C++ class CUIScriptWnd : CUIDialogWnd, DLL_Pure
{
  CUIScriptWnd();

  function _construct();
  function Register(CUIWindow*);
  function Register(CUIWindow*, string);
  function Enable(boolean);
  function SetHeight(number);
  function SetHolder(CDialogHolder*);
  function GetWidth() const;
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function SetFont(CGameFont*);
  function IsShown();
  function Show(boolean);
  function GetHeight() const;
  function SetWidth(number);
  function IsEnabled();
  function ResetPPMode();
  function GetFont();
  function Update();
  function AddCallback(string, number, const function<void>&);
  function AddCallback(string, number, const function<void>&, object);
  function SetAutoDelete(boolean);
  function OnKeyboard(number, enum EUIMessages);
  function Dispatch(number, number);
  function AttachChild(CUIWindow*);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function Init(number, number, number, number);
  function Init(Frect*);
  function WindowName();
  function GetHolder();
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function SetWindowName(string);
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function Load(string);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
  function ShowDialog(boolean);
  function HideDialog();
  function GetFrameLineWnd(string);
  function GetCheckButton(string);
  function GetListWndEx(string);
  function GetPropertiesBox(string);
  function GetButton(string);
  function GetListWnd(string);
  function GetStatic(string);
  function GetTabControl(string);
  function GetRadioButton(string);
  function GetFrameWindow(string);
  function GetDialogWnd(string);
  function GetEditBox(string);
  function GetProgressBar(string);
  function GetMessageBox(string);
};

<CUIScriptWnd>::_construct()

purpose:
???

<CUIScriptWnd>::Register(wnd, w_name)

<CUIScriptWnd>::Register(wnd)

SOC only
purpose:
registers child window; only registered window can raise callbacks (ui events)
arguments:

  • wnd <CUIWindow>

    - pointer to child window

  • w_name <string>

    - child window name; optional if you set window name with window_name (xml) or via SetWindowName

<CUIScriptWnd>::Update()

purpose:
window update method

<CUIScriptWnd>::AddCallback(wnd_name, event, callback, object)

<CUIScriptWnd>::AddCallback(wnd_name, event, callback)

SOC only
purpose:
sets callback for child window
arguments:

  • wnd_name <string>

    - child window name

  • event <number>

    - event type (ui_events); depends on child window type

  • callback <function>

    - callback function (event handler)

  • object

    - callback receiver; usually - parent window of control which raised event

<CUIScriptWnd>::OnKeyboard(key, act)

purpose:
handles keyboard events (key pressed, key released)
arguments:

  • key <number>

    - pressed/released key constant (DIK_keys)

  • act <const>

    - keyboard action: either

    ui_events.WINDOW_KEY_PRESSED

    or

    ui_events.WINDOW_KEY_RELEASED

<CUIScriptWnd>::Dispatch(number, number)

purpose:
dispatch window messages
arguments:

  • ? <number>

    - ?

  • ? <number>

    - ?

<CUIScriptWnd>::Load(path)

purpose:
sets window texture
arguments:

  • path <string>

    - texture path

<CUIScriptWnd>::GetFrameLineWnd(wnd_name)

SOC only
purpose:
returns child window (frame line)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUIFrameLineWnd>

    - frame line (CUIFrameLineWnd)

  • nil

    - child window doesn't exist

<CUIScriptWnd>GetCheckButton::(wnd_name)

SOC only
purpose:
returns child window (check box)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUICheckButton>

    - check box (CUICheckButton)

  • nil

    - child window doesn't exist

<CUIScriptWnd>::GetListWndEx(wnd_name)

SOC only
purpose:
returns child window (list window)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUIListWnd>

    - list window (CUIListWnd)

  • nil

    - child window doesn't exist

<CUIScriptWnd>::GetPropertiesBox(wnd_name)

SOC only
purpose:
returns child window (properties box)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUIPropertiesBox>

    - properties box (CUIPropertiesBox)

  • nil

    - child window doesn't exist

<CUIScriptWnd>::GetButton(wnd_name)

SOC only
purpose:
returns child window (button)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUIButton> or <CUI3tButton>

    - button (CUIButton or CUI3tButton)

  • nil

    - child window doesn't exist

<CUIScriptWnd>::GetListWnd(wnd_name)

SOC only
purpose:
returns child window (list window)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <?>

    - list window (CUIListWnd)

  • nil

    - child window doesn't exist

<CUIScriptWnd>::GetStatic(wnd_name)

SOC only
purpose:
returns child window (static control)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUIStatic>

    - static control (CUIStatic)

  • nil

    - child window doesn't exist

<CUIScriptWnd>::GetTabControl(wnd_name)

SOC only
purpose:
returns child window (tab control)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUITabControl>

    - tab control (CUITabControl)

  • nil

    - child window doesn't exist

<CUIScriptWnd>::GetRadioButton(wnd_name)

SOC only
purpose:
returns child window (radio button)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUITabControl>

    - radio button (CUITabControl with radio tag set to 1)

  • nil

    - child window doesn't exist

<CUIScriptWnd>::GetFrameWindow(wnd_name)

SOC only
purpose:
returns child window (frame window)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUIFrameWindow>

    - frame window (CUIFrameWindow)

  • nil

    - child window doesn't exist

<CUIScriptWnd>::GetDialogWnd(wnd_name)

SOC only
purpose:
returns child window (dialog window)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUIDialogWnd>

    - dialog window (CUIDialogWnd)

  • nil

    - child window doesn't exist

<CUIScriptWnd>::GetEditBox(wnd_name)

SOC only
purpose:
returns child window (edit box)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUIEditBox>

    - edit box (CUIEditBox)

  • nil

    - child window doesn't exist

<CUIScriptWnd>::GetProgressBar(wnd_name)

SOC only
purpose:
returns child window (progress bar)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUIProgressBar>

    - progress bar (CUIProgressBar)

  • nil

    - child window doesn't exist

<CUIScriptWnd>::GetMessageBox(wnd_name)

SOC only
purpose:
returns child window (message box)
arguments:

  • wnd_name <string>

    - child window name
returns:

  • <CUIMessageBox>

    - message box (CUIMessageBox)

  • nil

    - child window doesn't exist


lua_help CUITabButton

class CUITabButton

Implements button for CUITabControl
C++ class CUITabButton : CUIButton
{
  CUITabButton();

  function SetHighlightColor(number);
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function GetText();
  function GetColor() const;
  function GetWidth() const;
  function SetTextX(number);
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function SetFont(CGameFont*);
  function IsShown();
  function Show(boolean);
  function GetHeight() const;
  function SetWidth(number);
  function GetTextX();
  function IsEnabled();
  function ResetPPMode();
  function GetHeading();
  function EnableTextHighlighting(boolean);
  function SetStretchTexture(boolean);
  function SetColor(number);
  function ClipperOn();
  function GetFont();
  function SetAutoDelete(boolean);
  function GetClipperState();
  function SetTextColor(number, number, number, number);
  function SetHeading(number);
  function AttachChild(CUIWindow*);
  function GetTextY();
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function SetOriginalRect(number, number, number, number);
  function GetTextAlign();
  function Init(number, number, number, number);
  function Init(Frect*);
  function Init(string, number, number, number, number);
  function SetTextureOffset(number, number);
  function WindowName();
  function ClipperOff();
  function SetTextY(number);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function SetText(string);
  function SetTextST(string);
  function InitTexture(string);
  function GetStretchTexture();
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetTextAlign(number);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
  function TextControl();
  function GetTextureRect();
  function SetTextureRect(Frect*);
};


lua_help CUITabControl

class CUITabControl

Implements TabControl. Load from xml file with InitTab.
events:

  • ui_events.TAB_CHANGED

    - active tab was changed
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - tab control width

  • height

    - tab control height

  • radio

    - whether treat control as RadioButton (1) or TabControl (0)
<!-- config\ui\my_wnd.xml -->
<tab_control x="5" y="5" width="590" height="50">
  <window_name>my_tab_control</window_name>
  <button x="0" y="7" width="117" height="29">
    <window_name>t_bnt0</window_name>
    <texture>ui_button_ordinary</texture>
    <text align="l" x="0" y="0" font="letterica18" r="255" g="255" b="255">button[0]</text>
    <text_color>
      <t r="231" g="168" b="12" />
      <d r="181" g="170" b="148" />
      <e r="127" g="127" b="127" />
      <h r="255" g="255" b="255" />
    </text_color>
  </button>
  <button x="125" y="7" width="117" height="29">
    <window_name>t_bnt1</window_name>
    <texture>ui_button_ordinary</texture>
    <text align="l" x="0" y="0" font="letterica18" r="255" g="255" b="255">button[1]</text>
    <text_color>
      <t r="231" g="168" b="12" />
      <d r="181" g="170" b="148" />
      <e r="127" g="127" b="127" />
      <h r="255" g="255" b="255" />
    </text_color>
  </button>
  <button x="250" y="7" width="117" height="29">
    <window_name>t_bnt2</window_name>
    <texture>ui_button_ordinary</texture>
    <text align="l" x="0" y="0" font="letterica18" r="255" g="255" b="255">button[2]</text>
    <text_color>
      <t r="231" g="168" b="12" />
      <d r="181" g="170" b="148" />
      <e r="127" g="127" b="127" />
      <h r="255" g="255" b="255" />
    </text_color>
  </button>
  <button x="375" y="7" width="117" height="29">
    <window_name>t_bnt3</window_name>
    <texture>ui_button_ordinary</texture>
    <text align="l" x="0" y="0" font="letterica18" r="255" g="255" b="255">button[3]</text>
    <text_color>
      <t r="231" g="168" b="12" />
      <d r="181" g="170" b="148" />
      <e r="127" g="127" b="127" />
      <h r="255" g="255" b="255" />
    </text_color>
  </button>
</tab_control>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local tab_ctrl = xml:InitTab("tab_control", self)

-- or:
local tab_ctrl = CUITabControl()
tab_ctrl:SetAutoDelete(true)
tab_ctrl:SetWindowName("my_tab_control")
tab_ctrl:Init(5, 5, 590, 50)

local t_btn = nil
for i = 0, 3 do
  t_btn = CUITabButton()
  t_btn:SetAutoDelete(true)
  t_btn:SetWindowName("t_btn" .. i)
  t_btn:Init("ui_button_ordinary", 125 * i, 7, 117, 29)
  t_btn:SetText("button[" .. i .. "]")
  tab_ctrl:AddItem(t_btn)
end

self:AttachChild(self.tab_ctrl)

C++ class CUITabControl : CUIWindow
{
  CUITabControl();

  function GetFont();
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function GetButtonByIndex(number);
  function SetNewActiveTab(number);
  function GetTabsCount() const;
  function SetAutoDelete(boolean);
  function RemoveAll();
  function GetActiveIndex();
  function RemoveItem(number);
  function AttachChild(CUIWindow*);
  function AddItem(CUITabButton*);
  function AddItem(string, string, number, number, number, number);
  function AddItem(string, string, vector2, vector2);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function Init(number, number, number, number);
  function Init(Frect*);
  function GetWidth() const;
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function WindowName();
  function IsShown();
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function GetHeight() const;
  function SetWidth(number);
  function Show(boolean);
  function IsEnabled();
  function ResetPPMode();
  function SetFont(CGameFont*);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
  function GetActiveId();
  function SetActiveTab(string);
  function GetButtonById(string);
};

CUITabControl::GetButtonByIndex(index)

SOC only
purpose:
returns pointer to button (CUITabButton) with given index
arguments:

  • index [0, CUITabControl::GetTabsCount() - 1]

    - index of CUITabButton
returns:

  • <CUITabButton>

    - CUITabButton

CUITabControl::SetNewActiveTab(index)

SOC only
purpose:
activates button (CUITabButton) with given index
arguments:

  • index [0, CUITabControl::GetTabsCount() - 1]

    - index of CUITabButton

CUITabControl::GetTabsCount()

purpose:
returns number of buttons
returns:

  • <int>

    - number of buttons

CUITabControl::RemoveAll()

purpose:
removes all buttons

CUITabControl::GetActiveIndex()

SOC only
purpose:
gets index of active button (CUITabButton)
returns:

  • [0, CUITabControl::GetTabsCount() - 1]

    - index of active button

CUITabControl::RemoveItem(index)

SOC only
purpose:
removes button (CUITabButton) with given index
arguments:

  • index [0, CUITabControl::GetTabsCount() - 1]

    - index of button (CUITabButton) to remove

CUITabControl::AddItem(text, texture, x, y, w, h)

SOC only

CUITabControl::AddItem(text, texture, pos, size)

COP only

CUITabControl::AddItem(btn)

purpose:
adds button to TabControl
arguments:

  • text <string>

    - text to display on button

  • texture <string>

    - button texture

  • x <int>

    - position x

  • y <int>

    - position y

  • w <int>

    - width

  • h <int>

    - height

  • pos <vector2>

    - position; see: vector2

  • size <vector2>

    - size; see: vector2

  • btn <CUITabButton>

    - CUITabButton
example:
self.tab_ctrl = CUITabControl()
self.tab_ctrl:SetAutoDelete(true)
self.tab_ctrl:SetWindowName("tab_control")
self.tab_ctrl:Init(5, 5, 590, 50)

for i = 0, 3 do
  self.tab_ctrl:AddItem("button[" .. i .. "]", "ui_button_ordinary", 125 * i, 7, 117, 29)
end

self:AttachChild(self.tab_ctrl)

-- or:
local t_btn = nil
for i = 0, 3 do
  t_btn = CUITabButton()
  t_btn:SetAutoDelete(true)
  t_btn:SetWindowName("t_btn" .. i)
  t_btn:Init("ui_button_ordinary", 125 * i, 7, 117, 29)
  t_btn:SetText("button[" .. i .. "]")
  self.tab_ctrl:AddItem(t_btn)
end

self:AttachChild(self.tab_ctrl)

CUITabControl::GetActiveId()

COP only
purpose:
returns active button id
returns:

  • <string>

    - active button id

CUITabControl::SetActiveTab(id)

COP only
purpose:
activates button (tab) with given id
arguments:

  • id <string>

    - button id

CUITabControl::GetButtonById(id)

COP only
purpose:
returns button (CUITabButton) with given id
arguments:

  • id <string>

    - button id
returns:

  • <CUITabButton>

    - CUITabButton


lua_help CUITrackBar

class CUITrackBar

Implements TrackBar UI control. Load from xml file with InitTrackBar
events:

  • ui_events.BUTTON_CLICKED

    - trackbar clicked
remarks:
  • no idea how to get current trackbar value in SOC
  • current trackbar value is printed in console each time you change it (SOC)
  • in other words - not usable in SOC other then displaying visual control
xml attributes:

  • x

    - position x (relatively to parent - your ui window)

  • y

    - position y (relatively to parent - your ui window)

  • width

    - trackbar width

  • height

    - trackbar height
<!-- config\ui\my_wnd.xml -->
<track_bar x="20" y="55" width="300" height="21">
  <window_name>my_track_bar</window_name>
</track_bar>

local xml = CScriptXmlInit()
xml:ParseFile("my_wnd.xml")

local track_bar = xml:InitTrackBar("track_bar", self)

-- or:
local track_bar = CUITrackBar()
track_bar:SetAutoDelete(true)
track_bar:SetWindowName("my_track_bar")
track_bar:Init(20, 55, 300, 21)
wnd:AttachChild(track_bar)

C++ class CUITrackBar : CUIWindow
{
  CUITrackBar();

  function GetFont();
  function SetWindowName(string);
  function Enable(boolean);
  function SetHeight(number);
  function SetAutoDelete(boolean);
  function SetCheck(boolean);
  function AttachChild(CUIWindow*);
  function Init(number, number, number, number);
  function Init(Frect*);
  function SetWndPos(number, number);
  function SetWndPos(vector2);
  function GetCheck();
  function GetWidth() const;
  function DetachChild(CUIWindow*);
  function SetPPMode();
  function WindowName();
  function IsShown();
  function SetWndSize(number, number);
  function SetWndSize(vector2);
  function SetWndRect(Frect);
  function SetWndRect(number, number, number, number);
  function GetHeight() const;
  function SetWidth(number);
  function Show(boolean);
  function IsEnabled();
  function ResetPPMode();
  function SetFont(CGameFont*);
  function IsAutoDelete();
  function GetWndPos(CUIWindow*);
  function SetCurrentValue();
  function GetIValue();
  function GetFValue();
};

CUITrackBar::SetCheck(b_status)

purpose:
enables/disables trackbar
arguments:

  • b_status <boolean>

    - enable (

    true

    ) or disable (

    false

    ) trackbar

CUITrackBar::GetCheck()

purpose:
gets trackbar status
returns:

  • true

    - trackbar enabled

  • false

    - otherwise

CUITrackBar::SetCurrentValue()

COP only
purpose:
sets current trackbar value

CUITrackBar::GetIValue()

COP only
purpose:
returns current trackbar value
returns:

  • <int>

    - current trackbar value

CUITrackBar::GetFValue()

COP only
purpose:
returns current trackbar value
returns:

  • <float>

    - current trackbar value


lua_help duality

class duality

Creates duality effect (for post-process effector)
C++ class duality
{
  property h;
  property v;

  duality();
  duality(number, number);

  function set(number, number);
};

----------------------------------

local dual_a = duality(0.5, 0.3)

local dual_b = duality()
b:set(0.5, 0.3)

local dual_c = duality()
dual_c.h = 0.5
dual_c.v = 0.3

duality::h

purpose:
holds horizontal duality
property:

  • <float>

    - horizontal duality

duality::v

purpose:
holds vertical duality
property:

  • <float>

    - vertical duality

duality::set(h, v)

purpose:
sets duality parameters
arguments:

  • h <float> [0.0, 1.0]

    - horizontal

  • v <float> [0.0, 1.0]

    - vertical


lua_help effector effector_params

class effector_params

Holds post-process effector parameters.
C++ class effector_params
{
  property blur;
  property color_add;
  property color_base;
  property color_gray;
  property dual;
  property gray;
  property noise;

  effector_params();

  function assign(effector_params*, effector_params*);
};

effector_params::blur

purpose:
holds blur value
property:

  • <float [0.0, 1.0]>

    - blur value

effector_params::color_add

purpose:
holds additional color value
property:

  • <color>

    - additional color

effector_params::color_base

purpose:
holds base color value
property:

  • <color>

    - base color

effector_params::color_gray

purpose:
holds gray color value
property:

  • <color>

    - gray color

effector_params::dual

purpose:
holds duality value
property:

  • <duality>

    - duality

effector_params::gray

purpose:
holds gray saturation value
property:

  • <float [0.0, 1.0]>

    - gray saturation

effector_params::noise

purpose:
holds noise value
property:

  • <noise>

    - noise

effector_params::assign(ep)

purpose:
assigns (copy) all properties to ep (???)
arguments:

  • ep <effector_params>

    - effector_params

class effector

Creates post-process effector.
C++ class effector
{
  effector(number, number);

  function start(effector*);
  function finish(effector*);
  function process(effector_params*);
};

effector::effector(id, noidea)

purpose:
object constructor
arguments:

  • id <number>

    - effector id (should be unique)

  • noidea <number>

    - seed? const? 10000000
returns:

  • <effector>

    - object

effector::start()

purpose:
starts effector

effector::finish()

purpose:
stops effector

effector::process(ep)

purpose:
process effector parameters
arguments:

  • ep <effector_params>

    - effector_params


lua_help entity_action

class entity_action

Sets scripted actions for objects. See: scripted actions
C++ class entity_action
{
  entity_action();
  entity_action(const entity_action*);

  function set_action(act&);
  function set_action(anim&);
  function set_action(cond&);
  function set_action(look&);
  function set_action(move&);
  function set_action(object&);
  function set_action(particle&);
  function set_action(sound&);

  function anim() const;
  function look() const;
  function move() const;
  function object() const;
  function particle() const;
  function sound() const;

  function completed();
  function all();
  function time();
};

entity_action::set_action(action)

purpose:
sets action for object
arguments:

  • action

    - action type:
    • act
    • anim
    • cond
    • look
    • move
    • object
    • particle
    • sound

entity_action::anim()

purpose:
returns anim object, if it was set by set_action
returns:

  • <anim>

    - anim object (if set)

entity_action::look()

purpose:
returns look object, if it was set by set_action
returns:

  • <look>

    - look object (if set)

entity_action::move()

purpose:
returns move object, if it was set by set_action
returns:

  • <move>

    - move object (if set)

entity_action::object()

purpose:
returns object, if it was set by set_action
returns:

  • <object>

    - object (if set)

entity_action::particle()

purpose:
returns particle object, if it was set by set_action
returns:

  • <particle>

    - particle object (if set)

entity_action::sound()

purpose:
returns sound object, if it was set by set_action
returns:

  • <sound>

    - sound object (if set)

entity_action::completed()

purpose:
checks whether action was completed - only for actions which specify

completed()

method (?)
returns:

  • true

    - action completed

  • false

    - otherwise

entity_action::all()

purpose:
???
returns:

  • <number>

    - ???

entity_action::time()

purpose:
returns action start time or time set by cond ???
returns:

  • <number>

    - action start time or time set by cond ???


lua_help fbox

class Fbox

Stores min and max vectors. For instance, class is utilised for level boundaries - lowest (min) and highest (max) point of level geometry. See: level.get_bounding_volume()
C++ class Fbox
{
  property max;
  property min;

  Fbox();
};

------------------

local fbox_a = level.get_bounding_volume()
local fbox_b = Fbox()

Fbox::min

purpose:
holds min vector
property:

  • <vector>

    - min vector

Fbox::max

purpose:
holds max vector
property:

  • <vector>

    - max vector


lua_help fcolor

class fcolor

Represents ARGB color model. See also color.
C++ class fcolor
{
  property a;
  property b;
  property g;
  property r;

  fcolor();

  function set(number, number, number, number);
  function set(const fcolor&);
  function set(number);
};

--------------------------------------------------

local ca = fcolor()
ca.r = 0.1
ca.g = 0.2
ca.b = 0.3
ca.a = 0.5

fcolor::r

purpose:
holds red color value
property:

  • <number>

    - red color value

fcolor::g

purpose:
holds green color value
property:

  • <number>

    - green color value

fcolor::b

purpose:
holds blue color value
property:

  • <number>

    - blue color value

fcolor::a

purpose:
holds transparency value
property:

  • <number>

    - transparency value

fcolor::set(r, g, b, a)

fcolor::set(rgba)

purpose:
sets color values
arguments:

  • r <number>

    - red color value

  • g <number>

    - green color value

  • b <number>

    - blue color value

  • a <number>

    - transparency value

  • rgba <number>

    - color value; use GetARGB
example:
local a = fcolor()
-- r = 0.1; g = 0.2; b = 0.3; a = 0.5
a:set(0.1, 0.2, 0.3, 0.5)

local b = fcolor()
-- r = 0.1; g = 0.2; b = 0.3; a = 0.5
b:set(a)

local c = fcolor()
-- r = 1.0; g = 1.0; b = 1.0; a = 1.0
c:set(4294967295) -- 2^32 - 1


lua_help Frect

class Frect

Creates rectangle frame.
C++ class Frect
{
  property lt; -- obsolete ?
  property rb; -- obsolete ?
  property x1;
  property x2;
  property y1;
  property y2;

  Frect();

  function set(number, number, number, number);
};

Frect::set(x1, y1, x2, y2)

purpose:
sets position, width and height
arguments:

  • x1 <int>

    - position x

  • y1 <int>

    - position y

  • x2 <int>

    - width

  • y2 <int>

    - height
example:
local fr = Frect()
fr:set(10, 20, 800, 600)

-- fr.x1 = 10
-- fr.x2 = 800
-- fr.y1 = 20
-- fr.y2 = 600


lua_help game_difficulty

class game_difficulty

Game difficulty level constants. See also:
  • level.get_game_difficulty
  • level.set_game_difficulty
C++ class game_difficulty
{
  -- ESingleGameDifficulty:
  const novice = 0;
  const stalker = 1;
  const veteran = 2;
  const master = 3;
};


lua_help game

game.translate_string(id)

purpose:
Using given id, function returns text translated in current game language
arguments:

  • id <string>

    - id of text
returns:

  • <string>

    - translated text
example:
-- config\text\eng\string_table_tutorial.xml
local rad_tutor = game.translate_string("radiation_tutorial")
printf("radiation tutorial = %s", rad_tutor)

game.start_tutorial(tutorial)

purpose:
function starts game tutorial
arguments:

  • tutorial <string>

    - tutorial id (config\ui\game_tutorials.xml)

game.stop_tutorial()

purpose:
function stops currently playing tutorial; available only in COP.

game.has_active_tutorial()

purpose:
function checks whether game tutorial is on
returns:

  • true

    - tutorial is on

  • false

    - otherwise

game.get_game_time()

purpose:
function returns current game time (see: CTime)
returns:

  • <CTime>

    - game time

game.time()

purpose:
function returns current game time
returns:

  • <number>

    - current game time in miliseconds


lua_help hit

class hit

Sets hit data.
C++ class hit
{
  -- SOC:
  const burn = 0;
  const shock = 1;
  const strike = 2;
  const wound = 3;
  const radiation = 4;
  const telepatic = 5;
  const chemical_burn = 6;
  const explosion = 7;
  const fire_wound = 8;
  const dummy = 10;

  -- COP:
  const burn = 0;
  const shock = 1;
  const chemical_burn = 2;
  const radiation = 3;
  const telepatic = 4;
  const strike = 5;
  const wound = 6;
  const explosion = 7;
  const fire_wound = 8;
  const light_burn = 11;
  const dummy = 12;

  property direction;
  property draftsman;
  property impulse;
  property power;
  property type;

  hit();
  hit(const hit*);

  function bone(string);
};

-----------------------------

local h = hit()
h.direction = db.actor:position():sub(mutant:position())
h.draftsman = mutant
h.impulse = 10
h.power = 1
h.type = hit.fire_wound
h:bone("pelvis")

mutant:hit(h)

hit::direction

purpose:
hit direction
property:

  • <vector>

    - hit direction

hit::draftsman

purpose:
object which hits
property:

  • <game_object>

    - object which hits

hit::impulse

purpose:
hit impulse
property:

  • <number>

    - hit impulse

hit::power

purpose:
hit power
property:

  • <number>

    - hit power

hit::type

purpose:
hit type
property:

  • <const [0, 8]>

    - hit type: flag from

    hit

    class

hit::bone(name)

purpose:
sets bone which should be hit
arguments:

  • name <string>

    - bone name


lua_help ini_file

class ini_file

INI (*.ltx) file reader. See article: Reading LTX file through script
C++ class ini_file
{
  ini_file(string);

  function section_exist(string);
  function line_exist(string, string);
  function line_count(string);
  function r_line(ini_file*, string, number, string&, string&);
  function r_bool(string, string);
  function r_float(string, string);
  function r_clsid(string, string);
  function r_u32(string, string);
  function r_s32(string, string);
  function r_token(string, string, const token_list&);
  function r_vector(string, string);
  function r_string_wq(string, string);
  function r_string(string, string);
};

------------------------------------------------------------------------

-- gamedata\config\misc\my_file.ltx
local path = "misc\\my_file.ltx"
local ltx = ini_file(path)

ini_file::section_exist(sect_name)

purpose:
checks whether ini file contains section
arguments:

  • sect_name <string>

    - section name
returns:

  • true

    - section exist

  • false

    - otherwise

ini_file::line_exist(sect_name, field_name)

purpose:
checks whether field exist in given section
arguments:

  • sect_name <string>

    - section name

  • field_name <string>

    - field name
returns:

  • true

    - field exist

  • false

    - otherwise

ini_file::line_count(sect_name)

purpose:
returns number of lines (fields) in given section
arguments:

  • sect_name <string>

    - section name
returns:

  • <number>

    - number of lines (fields)

ini_file::r_line(sect_name, line_num, "", "")

purpose:
reads line (field and its value) from given section
arguments:

  • sect_name <string>

    - section name

  • line_num <number>

    - line number
returns:

  • <boolean>

    - result

  • <string>

    - field name

  • <string>

    - value
example:
[sect]
a = text
b = 10

-- a_name = a; a_val = text
local result, a_name, a_val = ltx:r_line("sect", 0, "", "")

-- b_name = b; b_val = 10
local result, b_name, b_val = ltx:r_line("sect", 1, "", "")

ini_file::r_bool(sect_name, field_name)

purpose:
reads boolean value
arguments:

  • sect_name <string>

    - section name

  • field_name <string>

    - field name
returns:

  • <boolean>

    - field value
remarks:
  • CTD if field value is empty
example:
[sect]
a = true
b = 1
c = false
d = 0
e = text
f = 1234

ltx:r_bool("sect", "a") -- true
ltx:r_bool("sect", "b") -- true
ltx:r_bool("sect", "c") -- false
ltx:r_bool("sect", "d") -- false
ltx:r_bool("sect", "e") -- false
ltx:r_bool("sect", "f") -- false

ini_file::r_float(sect_name, field_name)

purpose:
reads floating point number
arguments:

  • sect_name <string>

    - section name

  • field_name <string>

    - field name
returns:

  • <float>

    - field value
remarks:
  • CTD if field value is empty
example:
[sect]
a = 1.5
b = -1.5
c = text
d = true
e = false

ltx:r_float("sect", "a") -- 1.5
ltx:r_float("sect", "b") -- -1.5
ltx:r_float("sect", "c") -- 0
ltx:r_float("sect", "d") -- 0
ltx:r_float("sect", "e") -- 0

ini_file::r_clsid(sect_name, field_name)

purpose:
reads class id
arguments:

  • sect_name <string>

    - section name

  • field_name <string>

    - field name
returns:

  • <number>

    - 188 or 0 (???)

ini_file::r_u32(sect_name, field_name)

purpose:
reads unsigned integer
arguments:

  • sect_name <string>

    - section name

  • field_name <string>

    - field name
returns:

  • <number>

    - field value
remarks:
  • CTD if field value is empty
example:
[sect]
a = 32
b = -32
c = true
d = false
e = text

ltx:r_u32("sect", "a") -- 32
ltx:r_u32("sect", "b") -- 4294967296
ltx:r_u32("sect", "c") -- 0
ltx:r_u32("sect", "d") -- 0
ltx:r_u32("sect", "e") -- 0

ini_file::r_s32(sect_name, field_name)

purpose:
reads signed integer
arguments:

  • sect_name <string>

    - section name

  • field_name <string>

    - field name
returns:

  • <number>

    - field value
remarks:
  • CTD if field value is empty
example:
[sect]
a = 32
b = -32
c = true
d = false
e = text

ltx:r_s32("sect", "a") -- 32
ltx:r_s32("sect", "b") -- -32
ltx:r_s32("sect", "c") -- 0
ltx:r_s32("sect", "d") -- 0
ltx:r_s32("sect", "e") -- 0

ini_file::r_token(sect_name, field_name, tk_list)

purpose:
reads token and returns its value
arguments:

  • sect_name <string>

    - section name

  • field_name <string>

    - field name

  • tk_list <token_list>

    - token list
returns:

  • <number>

    - token value

  • 0

    - token not found in tk_list argument
remarks:
  • CTD if field value is empty
example:
[sect]
en = enemy
ne = neutral
fr = friend

local tokens = token_list()
tokens:add("enemy", 0)
tokens:add("neutral", 1)
tokens:add("friend", 2)

ltx:r_token("sect", "en", tokens) -- 0 (enemy)
ltx:r_token("sect", "ne", tokens) -- 1 (neutral)
ltx:r_token("sect", "fr", tokens) -- 2 (friend)

ini_file::r_vector(sect_name, field_name)

purpose:
reads vector
arguments:

  • sect_name <string>

    - section name

  • field_name <string>

    - field name
returns:

  • <vector>

    - field value
example:
[sect]
a = 241.57223510742, 15.355321884155, 125.87966156006

ltx:r_vector("sect", "a")

ini_file::r_string_wq(sect_name, field_name)

purpose:
reads string (without quotes)
arguments:

  • sect_name <string>

    - section name

  • field_name <string>

    - field name
returns:

  • <string>

    - field value

  • nil

    - field value was empty
example:
[sect]
a = text
b = "text"
c = 'text'

ltx:r_string_wq("sect", "a") -- text
ltx:r_string_wq("sect", "b") -- text
ltx:r_string_wq("sect", "c") -- 'text'

ini_file::r_string(string, string)

purpose:
reads string
arguments:

  • sect_name <string>

    - section name

  • field_name <string>

    - field name
returns:

  • <string>

    - field value

  • nil

    - field value was empty
example:
[sect]
a = text
b = "text"
c = 'text'

ltx:r_string_wq("sect", "a") -- text
ltx:r_string_wq("sect", "b") -- "text"
ltx:r_string_wq("sect", "c") -- 'text'


lua_help level

level.present()

purpose:
function indicates whether level was rendered
returns:

  • true

    - if level was rendered

  • false

    - otherwise
example:
if level.present() and (db.actor ~= nil) and db.actor:alive() then
  -- game is on
end

level.name()

purpose:
function returns current level's name
example:
if level.name() == "l02_garbage" then
  -- current level is garbage
end

level.hide_indicators()

purpose:
function removes (hides) hud elements

level.show_indicators()

purpose:
function displays hud elements

level.disable_input()

purpose:
function disables player's input (keyboard, mouse)

level.enable_input()

purpose:
function enables player's input (keyboard, mouse)

level.get_time_hours()

purpose:
function returns current in-game hour
returns:

  • <number>

    - current in-game hour
example:
if level.get_time_hours() == 0 then
  -- midnight
end

level.get_time_minutes()

purpose:
function returns current in-game minutes
returns:

  • <number>

    - current in-game minutes

level.get_time_days()

purpose:
function returns a number of in-game days
returns:

  • <number>

    - current in-game days

level.get_time_factor()

purpose:
function returns time coefficient; field time_factor in section alife (config\alife.ltx)
returns:

  • <number>

    - time factor (10 in vanilla game)

level.set_time_factor(coeff)

purpose:
function sets time coefficient
arguments:

  • coeff <number>

    - time coefficient

level.get_game_difficulty()

purpose:
function returns player current game difficulty level
returns:

  • <game_difficulty>

    - current game difficuly level (see: game_difficulty)

level.set_game_difficulty(difficulty_level)

purpose:
function sets game difficulty level
arguments:

  • difficulty_level <game_difficulty>

    - difficulty level (see: game_difficulty)
remarks:
game difficulty level can be also set via console command:
get_console():execute("g_game_difficulty gd_novice")
get_console():execute("g_game_difficulty gd_stalker")
get_console():execute("g_game_difficulty gd_veteran")
get_console():execute("g_game_difficulty gd_master")

level.set_game_difficulty(game_difficulty.master)

level.map_remove_object_spot(object_id, mapspot_type)

purpose:
function removes mapspot from object
arguments:

  • object_id <number>

    - id of object which has assigned mapspot

  • mapspot_type <string>

    - type of mapspot (config\ui\map_spots.xml)

level.map_has_object_spot(object_id, mapspot_type)

purpose:
checks whether object has assigned certain mapspot
arguments:

  • object_id <number>

    - id of object which has assigned mapspot

  • mapspot_type <string>

    - type of mapspot (config\ui\map_spots.xml)
returns:

  • true

    - object has assigned given mapspot

  • false

    - otherwise
example:
if level.map_has_object_spot(624, "blue_location") ~= 0 then
  level.map_remove_object_spot(624, "blue_location")
end

level.map_add_object_spot_ser(object_id, mapspot_type, hint)

purpose:
function adds mapspot for server object
arguments:

  • object_id <number>

    - id of object to assign mapspot

  • mapspot_type <string>

    - type of mapspot (config\ui\map_spots.xml)

  • hint <string>

    - textual hint

level.map_add_object_spot(object_id, mapspot_type, hint)

purpose:
function adds mapspot for level object
arguments:

  • object_id <number>

    - id of object to assign mapspot

  • mapspot_type <string>

    - type of mapspot (config\ui\map_spots.xml)

  • hint <string>

    - textual hint

level.map_change_spot_hint(object_id, mapspot_type, hint)

purpose:
function change mapspot hint
arguments:

  • object_id <number>

    - object's id

  • mapspot_type <string>

    - type of mapspot (config\ui\map_spots.xml)

  • hint <string>

    - textual hint

level.add_dialog_to_render(dlg)

purpose:
function renders dialog (displays dialog) window
arguments:

  • dlg <CUIDialogWnd *>

    - ui window object
remarks:
  • displayed window is not handling input events (keyboard, mouse)

level.remove_dialog_to_render(dlg)

purpose:
function removes dialog (ui window)
arguments:

  • dlg <CUIDialogWnd *>

    - dialog object

level.start_stop_menu(dlg, b_val)

purpose:
function displays dialog (ui window); not available in COP
arguments:

  • dlg <CUIDialogWnd *>

    - dialog object (ui window)

  • b_val <boolean>

    -

    true

    to display window;

    false

    - ?

level.vertex_position(vertex_id)

purpose:
function returns position of given vertex
arguments:

  • vertex_id <number>

    - vertex id
returns:

  • <vector>

    - position of given vertex

level.vertex_in_direction(vertex_id, dir, radius)

purpose:
function search for vertex in given direction
arguments:

  • vertex_id <number>

    - current vertex id

  • dir <vector>

    - direction towards which search for vertex

  • radius <number>

    - searching radius
returns:

  • <number>

    - found vertex id

level.patrol_path_exists(patrol)

purpose:
function check whether given patrol exists in all.spawn
arguments:

  • patrol <string>

    - patrol name
returns:

  • true

    - given patrol exists in all.spawn

  • false

    - otherwise

level.object_by_id(object_id)

purpose:
function returns level (client) object
arguments:

  • object_id <number>

    - object id
returns:

  • <game_object>

    - found object

  • nil

    - otherwise

level.spawn_phantom(position)

purpose:
function spawns phantom
arguments:

  • position <vector>

    - phantom spawn position
example:
local phantom_spawn_radius = 30.0
local phantom_fov = 45
local phantom_max = 8

if phantom_manager:phantom_count() < phantom_max then
  local radius = phantom_spawn_radius * (math.random() / 2.0 + 0.5)
  local ang = phantom_fov * math.random() - phantom_fov * 0.5
  local dir = vector_rotate_y(db.actor:direction(), ang)
  phantom_manager.spawn_phantom(db.actor:position():add(dir:mul(radius)))
end

level.rain_factor()

purpose:
function returns rain coefficient
returns:

  • <float [0.0, ... 1.0]>

    rain factor

level.get_weather()

purpose:
function returns current weather (preset name)
returns:

  • <string>

    current weather name

level.set_weather(weather, switch_type)

purpose:
function sets new weather
arguments:

  • weather <string>

    - name of weather to set

  • switch_type <boolean>

    - how to set new weather:

    • true

      - immediately

    • false

      - morphing slowly

level.set_weather_fx(weather)

purpose:
function sets new weather
arguments:

  • weather <string>

    - name of weather to set
remarks:
  • weather is set on top of current weather, that is current weather is gently morphed into weather you want to set
  • you can use this function to set temporary weather, such as blowout weather

level.is_wfx_playing()

purpose:
function check if weather effect is displayed
returns:

  • true

    - weather effect is on

  • false

    - otherwise
example:
printf("weather effect = %s", tostring(level.is_wfx_playing())) -- false
level.set_weather_fx("surge_day")
printf("weather effect = %s", tostring(level.is_wfx_playing())) -- still false ???

level.get_snd_volume()

purpose:
function returns volume of level sounds
returns:

  • <float [0.0, ..., 1.0]>

    - sounds volume

level.set_snd_volume(volume)

purpose:
function sets volume for level sounds
arguments:

  • volume <float [0.0, ..., 1.0]>

    - sounds volume

level.add_cam_effector(anim, id, looped, "")

purpose:
function adds camera effector (animation)
arguments:

  • anim <string>

    - path to camera animation

  • id <number>

    - effector id (should be unique)

  • looped <boolean>

    - whether animation should be looped

level.add_cam_effector2(anim, id, looped, callback)

level.add_cam_effector2(anim, id, looped, callback, fov)

available in COP only
purpose:
function adds camera effector (animation)
arguments:

  • anim <string>

    - path to camera animation

  • id <number>

    - effector id (should be unique)

  • looped <boolean>

    - whether animation should be looped

  • callback <string>

    - function to call when anim ends; format: script_name.function_name

  • fov <float>

    - field of view; either custom or use device().fov
example:
function display_anim()
  local anim = "camera_effects\\arena.anm"
  local callback = script_name() .. ".effector_callback"
  level.add_cam_effector2(anim, 123, false, callback)
end

function effector_callback
  print("anim stopped!")
end

level.remove_cam_effector(id)

purpose:
function removes camera animation
arguments:

  • id <number>

    - effector id

level.add_pp_effector(effector, id, looped)

purpose:
function displays postprocess effector
arguments:

  • effector <string>

    - path to effector

  • id <number>

    - effector id (should be unique)

  • looped <boolean>

    - whether effector should be looped

level.set_pp_effector_factor(id, intensity)

level.set_pp_effector_factor(id, intensity, no_idea)

purpose:
function sets postprocess effector intensity
arguments:

  • id <number>

    - effector id

  • intensity <float>

    - effector intensity

  • no_idea <float>

    - ???
example:
level.add_pp_effector("effector_controller_aura.ppe", 999, true)
level.set_pp_effector_factor(999, 1.0)

level.add_pp_effector("yantar_underground_psi.ppe", 123, true)
level.set_pp_effector_factor(123, 5.0, 0.3)

level.remove_pp_effector(id)

purpose:
function removes postprocess effector
arguments:

  • id <number>

    - effector id

level.add_call(object, predicate, callback)

level.add_call(predicate, callback)

purpose:
function set checker (predicate) for given object
arguments:

  • object

    - object to set checker for

  • predicate

    - determines when to stop checking object; function or string with format: script_name.function_name

  • callback

    - function called when predicate returns true; function or string with format: script_name.function_name
remarks:
For given object function

add_call()

set checker (predicate). Predicate is called periodically, if it returns true then callback is called and checker is suspended. Both predicate and callback have one argument - object which we set checker for.
example:
local pos = vector():set(123, 456, 789)
local lvid = 1234
local gvid = 5678

-- spawn weapon
local wpn = alife():create("wpn_abakan", pos, lvid, gvid)

-- set "checker": if weapon is online (predicate)
-- then transfer it to actor's backpack (callback)
if wpn ~= nil then
  level.add_call(wpn,
    -- predicate ("checker")
    function(se_obj)
      if level.object_by_id(se_obj.id) ~= nil then
        -- weapon is online: stop checking object
        -- and call callback function
        return true
      end
      -- weapon is still offline,
      -- keep checking object
      return false
    end,
    -- callback
    function(se_obj)
      local w = level.object_by_id(se_obj.id)
      -- weapon is online, move to actor
      w:transfer_item(w, db.actor)
    end)
end

level.remove_call(object, predicate, callback)

level.remove_call(predicate, callback)

purpose:
function removes added checker for given object - see level.add_call()
arguments:

  • object

    - object with checker

  • predicate

    - determines when to stop checking object; function or string with format: script_name.function_name

  • callback

    - function called when predicate returns true; function or string with format: script_name.function_name

level.remove_calls_for_object(object)

purpose:
function removes all added checkers for given object - see level.add_call()
arguments:

  • object

    - object with checkers

level.main_input_receiver()

purpose:
function returns handle to current displayed ui window (such as: inventory window, pda, etc.); not available in COP
returns:

  • <CUIDialogWnd*>

    - handle to displayed ui window

  • nil

    - if window is not displayed
remarks:

  • CUIDialogWnd

    is not handling user input (keyboard, mouse)
example:
-- if inventory window (backpack) is displayed
-- get handle to window, add label
function show_label_in_inventory()
  if has_alife_info("ui_inventory") then
    local inventory_wnd = level.main_input_receiver()
    if inventory_wnd then
      local label = CUIStatic()
      label:SetWindowName("my_test_label")
      label:SetAutoDelete(true)
      label:SetWndPos(300, 700)
      label:SetFont(GetFontLetterica25())
      label:SetTextColor(255, 0, 255, 0)
      label:SetText("Some text ...")
      inventory_wnd:AttachChild(label)
    end
  end
end

level.get_bounding_volume()

purpose:
function returns pointer to Fbox structure which contains information on current level boundaries - lowest (min) and highest (max) point of current level geometry
returns:

  • <Fbox*>

    - pointer to Fbox structure
example:
function vector_to_string(v)
  return string.format("%f,%f,%f", v.x, v.y, v.z)
end

local f_box = level.get_bounding_volume()

printf("%s boundaries:", level.name())
printf("lowest point: %s", vector_to_string(f_box.min))
printf("highest point: %s", vector_to_string(f_box.max))

level.game_id()

purpose:
function returns game id
returns:

  • <number>

    - game id (1 for single player)

level.cover_in_direction(number, dir)

purpose:
function returns possible cover in given direction; not available in COP
arguments:

  • number <number>

    - object id

  • dir <vector>

    - direction
returns:

  • <vector>

    - cover position

  • nil

    - if cover not found

level.client_spawn_manager()

purpose:
function returns instance of client_spawn_manager

level.prefetch_sound(name)

purpose:
function to prefetch sound files
arguments:

  • name <string>

    - sound name
remarks:
  • obsolete function (?)
  • more details in sound_prefetch.script

level.iterate_sounds(path, nstl, object, callback)

level.iterate_sounds(path, nstl, callback)

purpose:
function iterates sound files in given path
arguments:

  • path <string>

    - path to sound files

  • nstl <number>

    - sound_theme.nstl (64)

  • object <game_object>

    - object to prefetch sounds for

  • callback <function>

    - function called for each sound in given path
remarks:
  • obsolete function (?)
  • more details in sound_prefetch.script

level.add_complex_effector(path, id)

purpose:
function displays postprocess effector
arguments:

  • path <string>

    - path to effector

  • id <number>

    - effector id
remarks:
  • obsolete function (?)

level.remove_complex_effector(id)

purpose:
function removes postprocess effector
arguments:

  • id <number>

    - effector id
remarks:
  • obsolete function (?)

level.environment() obsolete

level.physics_world() obsolete

level.check_object(game_object*) obsolete (removed in SOC)

level.debug_object(string) obsolete (removed in SOC)

level.debug_actor() obsolete (removed in SOC)


lua_help look

class look

Creates look action. See: scripted actions
C++ class look
{
  -- ESightType:
  const cur_dir = 0;
  const path_dir = 1;
  const direction = 2;
  const point = 3;
  const danger = 5;
  const search = 6;
  const fire_point = 10;

  look();
  look(enum SightManager::ESightType);
  look(enum SightManager::ESightType, vector&);
  look(enum SightManager::ESightType, game_object*);
  look(enum SightManager::ESightType, game_object*, string);
  look(const vector&, number, number);
  look(game_object*, number, number);

  function completed();
  function type(enum SightManager::ESightType);
  function object(game_object*);
  function bone(string);
  function direct(const vector&);
};

look:look(gobj, speed_h, speed_v)

look:look(pos, speed_h, speed_v)

look:look(flag, gobj, bname)

look:look(flag, gobj)

look:look(flag, pos)

look:look(flag)

look:look()

purpose:
object constructor
arguments:

  • gobj <game_object>

    - object to look at

  • speed_h <number>

    - horizontal speed

  • speed_v <number>

    - vertical speed

  • pos <vector>

    - position to look at

  • flag <const>

    -

    ESightType

    flag

  • bname <string>

    - bone name (?)
returns:

  • <look>

    - object
example:
action(obj, anim(anim.turn, 0), look(look.point, db.actor), cond(cond.look_end))
action(obj, anim(anim.turn, 0), look(look.point, db.actor:position()), cond(cond.time_end, 3000))
action(obj, look(db.actor:position(), 0.22, 0.35), cond(cond.look_end))
action(obj, look(look.path_dir), cond(cond.time_end, 2000))

look::completed()

purpose:
checks whether look action was completed; probably called by

entity_action:completed()

(see: scripted actions)
returns:

  • true

    - action finished

  • false

    - otherwise

look::type(l_type)

purpose:
sets look type
arguments:

  • l_type <const>

    -

    ESightType

    flag

look::object(gobj)

purpose:
sets object to look at
arguments:

  • gobj <game_object>

    - object to look at

look::bone(bname)

purpose:
sets bone
arguments:

  • bname <string>

    - bone name

look::direct(vec)

purpose:
sets look position (?)
arguments:

  • vec <vector>

    - look position (?)


lua_help MonsterSpace

class MonsterSpace

Sets of flags for sound.
C++ class MonsterSpace
{
  -- EMonsterHeadAnimType:
  const head_anim_normal = 0;
  const head_anim_angry = 1;
  const head_anim_glad = 2;
  const head_anim_kind = 3;
  const sound_script = 128;
};


lua_help move

class move

Creates move action. See: scripted actions
C++ class move
{
  -- EScriptMonsterMoveAction:
  const walk_fwd = 0;
  const walk_bkwd = 1;
  const run_fwd = 2;
  const drag = 3;
  const jump = 4;
  const steal = 5;
  const walk_with_leader = 6; -- COP only
  const run_with_leader = 7; -- COP only

  -- no idea:
  const default = 0;
  const curve = 0;
  const force = 1;
  const dodge = 1;
  const criteria = 2;
  const curve_criteria = 2;

  -- EDetailPathType:
  const line = 0;

  -- EBodyState:
  const crouch = 0;
  const standing = 1;

  -- EMovementType:
  const walk = 0;
  const run = 1;
  const stand = 2;

  -- EInputKeys:
  const none = 1;
  const fwd = 2;
  const back = 4;
  const left = 8;
  const right = 16;
  const up = 32;
  const down = 64;
  const handbrake = 128;
  const on = 256;
  const off = 512;

  move();
  move(enum move::EInputKeys);
  move(enum move::EInputKeys, number);
  move(enum move::EBodyState, enum move::EMovementType, enum move::EDetailPathType, game_object*);
  move(enum move::EBodyState, enum move::EMovementType, enum move::EDetailPathType, game_object*, number);
  move(enum move::EBodyState, enum move::EMovementType, enum move::EDetailPathType, const patrol&);
  move(enum move::EBodyState, enum move::EMovementType, enum move::EDetailPathType, const patrol&, number);
  move(enum move::EBodyState, enum move::EMovementType, enum move::EDetailPathType, const vector&);
  move(enum move::EBodyState, enum move::EMovementType, enum move::EDetailPathType, const vector&, number);
  move(const vector&, number);
  move(enum EMovementType);
  move(enum move::EScriptMonsterMoveAction, vector&);
  move(enum move::EScriptMonsterMoveAction, patrol&);
  move(enum move::EScriptMonsterMoveAction, game_object*);
  move(enum move::EScriptMonsterMoveAction, vector&, number);
  move(enum move::EScriptMonsterMoveAction, number, vector&);
  move(enum move::EScriptMonsterMoveAction, number, vector&, number);
  move(enum move::EScriptMonsterMoveAction, patrol&, number);
  move(enum move::EScriptMonsterMoveAction, game_object*, number);
  move(enum move::EScriptMonsterMoveAction, vector&, number, enum EScriptMonsterSpeedParam);
  move(enum move::EScriptMonsterMoveAction, patrol&, number, enum EScriptMonsterSpeedParam);
  move(enum move::EScriptMonsterMoveAction, game_object*, number, enum EScriptMonsterSpeedParam);

  function completed();
  function path(enum EDetailPathType);
  function position(const vector&);
  function input(enum CScriptMovementAction::EInputKeys);
  function patrol(const class CPatrolPath*, class shared_str);
  function object(game_object*);
  function body(enum EBodyState);
};

move::move(bstate, mtype, ptype, gobj, dist)

move::move(bstate, mtype, ptype, pos, dist)

move::move(bstate, mtype, ptype, ptr, dist)

move::move(bstate, mtype, ptype, gobj)

move::move(bstate, mtype, ptype, ptr)

move::move(bstate, mtype, ptype, pos)

move::move(mact, lvid, pos, dist)

move::move(mact, pos, speed)

move::move(mact, gobj, dist)

move::move(mact, lvid, pos)

move::move(mact, ptr, dist)

move::move(inkey, speed)

move::move(mact, gobj)

move::move(pos, dist)

move::move(mact, ptr)

move::move(mact, pos)

move::move(mtype)

move::move(inkey)

move::move()

purpose:
object constructor
arguments:

  • bstate <const>

    -

    EBodyState

    flag

  • mtype <const>

    -

    EMovementType

    flag

  • ptype <const>

    -

    EDetailPathType

    flag

  • gobj <game_object>

    - target object

  • dist <number>

    - distance to point/object (or speed ?)

  • pos <vector>

    - target position

  • ptr <patrol>

    - patrol

  • mact <const>

    -

    EScriptMonsterMoveAction

    flag

  • lvid <number>

    - level vertex id

  • speed <number>

    - speed

  • inkey <const>

    -

    EInputKeys

    flag
returns:

  • <move>

    - object
example:
action(car, move(move.on), cond(cond.time_end, 1000)) -- start engine
action(car, move(move.back, 3), cond(cond.time_end, 4000)) -- move back
action(car, move(move.fwd + move.left, 6), cond(cond.time_end, 4000)) -- move forward and left
action(car, move(move.fwd + move.right, 5), cond(cond.time_end, 4000)) -- move forward and right
action(car, move(move.off + move.handbrake), cond(cond.time_end, 1000)) -- engine_off + vehicle_stop

action(dog, move(move.run_fwd, patrol("way1", patrol.start, patrol.continue)), cond(cond.time_end, 400000))
action(dog, move(move.walk_fwd, db.actor:position()), cond(cond.move_end))
action(dog, move(move.walk_fwd, db.actor), cond(cond.move_end))

move::completed()

purpose:
checks whether move action was completed; probably called by

entity_action:completed()

(see: scripted actions)
returns:

  • true

    - action finished

  • false

    - otherwise

move::path(p_type)

purpose:
sets path type
arguments:

  • p_type <const>

    -

    EDetailPathType

    flag

move::position(pos)

purpose:
sets target position
arguments:

  • pos <vector>

    - position

move::input(m_type)

purpose:
sets move action (for vehicles)
arguments:

  • m_type <const>

    -

    EInputKeys

    flag

move::patrol(const class CPatrolPath*, class shared_str)

purpose:
sets patrol (obsolete ?)
arguments:

  • patrol <CPatrolPath>

    - patrol ?

  • shared_str <string>

    - ???

move::object(gobj)

purpose:
sets target object
arguments:

  • gobj <game_object>

    - target object

move::body(b_state)

purpose:
sets body state
arguments:

  • b_state <const>

    -

    EBodyState

    flag


lua_help net_packet

class net_packet

Set of methods to save/read to/from packet.
C++ class net_packet
{
  net_packet();

  function r_seek(number);
  function r_advance(number);
  function r_eof(net_packet*);
  function r_elapsed();
  function r_begin(number&);
  function r_tell();
  function r_stringZ(net_packet*);
  function r_vec3(vector&);
  function r_matrix(matrix&);
  function r_bool(net_packet*);
  function r_float();
  function r_float_q8(number&, number, number);
  function r_float_q16(number&, number, number);
  function r_u8();
  function r_s8();
  function r_u16();
  function r_s16();
  function r_u24();
  function r_u32();
  function r_s32();
  function r_u64();
  function r_s64();
  function r_angle8(number&);
  function r_angle16(number&);
  function r_dir(vector&);
  function r_sdir(vector&);
  function r_clientID(net_packet*);

  function w_begin(number);
  function w_tell();
  function w_stringZ(string);
  function w_vec3(const vector&);
  function w_matrix(matrix&);
  function w_bool(net_packet*, boolean);
  function w_float(number);
  function w_float_q8(number, number, number);
  function w_float_q16(number, number, number);
  function w_u8(number);
  function w_u16(number);
  function w_s16(number);
  function w_u24(number);
  function w_u32(number);
  function w_s32(number);
  function w_u64(unsigned __int64);
  function w_s64(__int64);
  function w_angle8(number);
  function w_angle16(number);
  function w_dir(const vector&);
  function w_sdir(const vector&);
  function w_clientID(ClientID&);
  function w_chunk_open8(number&);
  function w_chunk_close8(number);
  function w_chunk_open16(number&);
  function w_chunk_close16(number);
};

net_packet::r_seek(pos)

purpose:
sets position of packet pointer
arguments:

  • pos <number>

    - position (0 - beginning)
example:
local p = net_packet()
p:w_u8(255) -- save 1 byte
p:r_seek(0) -- back to the beginning
p:r_u8() -- 255

net_packet::r_advance(nb)

purpose:
moves packet pointer (starting from current position) about nb bytes
arguments:

  • nb <number>

    - number of bytes to read (skip)
example:
local p = net_packet()

p:w_u16(65535) -- write 2 bytes
p:w_u8(255) -- write 1 byte

p:w_stringZ("some text")

p:r_seek(0) -- back to the beginning
p:r_advance(3) -- skip 3 bytes

printf("str = %s", p:r_stringZ()) -- some text

net_packet::r_eof()

purpose:
checks if current packet pointer position is at the end of packet
returns:

  • true

    - packet pointer position is at the end of packet

  • false

    - otherwise
example:
local p = net_packet()
p:w_u16(65535)
p:r_seek(0) -- back to beginning
printf("eof = %s", tostring(p:r_eof())) -- false
p:r_advance(2)
printf("eof = %s", tostring(p:r_eof())) -- true

-- or:
if p:r_elapsed() == 0 then
  -- nothing left to read = eof
end

net_packet::r_elapsed()

purpose:
returns number of bytes left in packet
returns:

  • <number>

    - number of bytes left in packet

net_packet::r_begin() obsolete

net_packet::r_tell()

purpose:
returns number of bytes read
returns:

  • <number>

    - number of bytes read

net_packet::r_stringZ()

purpose:
reads string
returns:

  • <string>

    - read from packet

net_packet::r_vec3()

purpose:
reads vector
returns:

  • <vector>

    - read from packet
example:
function vector_to_string(v)
  return string.format("%f,%f,%f", v.x, v.y, v.z)
end

local p = net_packet()
p:w_vec3(vector():set(1.23, 4.56, 7.89))

p:r_seek(0)
local tmp = p:r_vec3()
printf("vec = %s", vector_to_string(tmp))

-- or:
p:r_seek(0)
local x = p:r_float()
local y = p:r_float()
local z = p:r_float()
tmp = vector():set(x, y, z)
printf("vec = %s", vector_to_string(tmp))

net_packet::r_matrix()

purpose:
reads matrix
returns:

  • <matrix>

    - read from packet

net_packet::r_bool()

purpose:
reads boolean value
returns:

  • <boolean>

    - read from packet

net_packet::r_float()

purpose:
reads float value
returns:

  • <float>

    - read from packet

net_packet::r_float_q8() obsolete

net_packet::r_float_q16() obsolete

net_packet::r_u8()

purpose:
reads 1 byte (unsigned)
returns:

  • <number>

    - read from packet

net_packet::r_s8()

purpose:
reads 1 byte (signed)
returns:

  • <number>

    - read from packet

net_packet::r_u16()

purpose:
reads 2 bytes (unsigned)
returns:

  • <number>

    - read from packet

net_packet::r_s16()

purpose:
reads 2 bytes (signed)
returns:

  • <number>

    - read from packet

net_packet::r_u24()

purpose:
reads 3 bytes (unsigned)
returns:

  • <number>

    - read from packet

net_packet::r_u32()

purpose:
reads 4 bytes (signed - see remarks and example)
returns:

  • <number>

    - read from packet
remarks:
  • actually it reads signed int - see example below
example:
local p = net_packet()
p:w_u32(4294967295)

p:r_seek(0)
p:r_u32() -- -2147483648 (!)

net_packet::r_s32()

purpose:
reads 4 bytes (signed)
returns:

  • <number>

    - read from packet

net_packet::r_u64() obsolete

net_packet::r_s64() obsolete

net_packet::r_angle8() obsolete

net_packet::r_angle16() obsolete

net_packet::r_dir() obsolete

net_packet::r_sdir() obsolete

net_packet::r_clientID() obsolete

net_packet::w_begin(num)

purpose:
writes 2 bytes
arguments:

  • num <number [0, 65535]>

    - number
remarks:
  • usually it is used when you want to be sure that you are saving data in the beginning of the packet
  • saves 2 bytes
example:
local p = net_packet()
-- start writing data from beginning
p:w_begin(0)
p:w_u8(90)

p:r_seek(2) -- because w_begin() saves 2 bytes
p:r_u8() -- 90

net_packet::w_tell()

purpose:
returns number of bytes written to packet
returns:

  • <number>

    - number of bytes written to packet

net_packet::w_stringZ(str)

purpose:
writes string to packet
arguments:

  • str <string>

    - string to save
remarks:
  • saves 1 byte per character + 1 byte for terminator

net_packet::w_vec3(vec)

purpose:
writes vector to packet
arguments:

  • vec <vector>

    - vector to save
remarks:
  • same as saving each vector property (x, y, z) as float
  • saves 12 bytes (3 x 4)

net_packet::w_matrix(m)

purpose:
writes matrix to packet
arguments:

  • m <matrix>

    - matrix to save

net_packet::w_bool(b)

purpose:
writes boolean value to packet
arguments:

  • b <boolean>

    - value to save
remarks:
  • you can also save 1 (as true) or 0 (false)
  • saves 1 byte

net_packet::w_float(num)

purpose:
writes floating point number to packet
arguments:

  • num <float>

    - value to save
remarks:
  • saves 4 bytes

net_packet::w_float_q8(number, number, number) obsolete

net_packet::w_float_q16(number, number, number) obsolete

net_packet::w_u8(num)

purpose:
writes unsigned number (unsigned char)
arguments:

  • num <number [0, 255]>

    - value to save
  • or:

    num <number [-128, 127]>

    - value to save
remarks:
  • you can save signed number and then read it as s8()
  • saves 1 byte
example:
local p = net_packet()
p:w_u8(255) -- save 255
p:w_u8(-128) -- save 128 (without sign)

p:r_seek(0)
p:r_u8() -- 255
p:r_s8() -- -128

net_packet::w_u16(num)

purpose:
writes unsigned number (unsigned short int)
arguments:

  • num <number [0, 65535]>

    - value to save
remarks:
  • saves 2 bytes

net_packet::w_s16(num)

purpose:
writes signed number (signed short int)
arguments:

  • num <number [–32 768, 32 767]>

    - value to save
remarks:
  • saves 2 bytes

net_packet::w_u24(num)

purpose:
writes unsigned number
arguments:

  • num <number [0, 16 777 215]>

    - value to save
remarks:
  • saves 3 bytes

net_packet::w_u32(num)

purpose:
writes unsigned number (unsigned int)
arguments:

  • num <number [0, 4 294 967 295]>

    - value to save
remarks:
  • see remarks: net_packet::r_u32
  • saves 4 bytes

net_packet::w_s32(num)

purpose:
writes signed number (int)
arguments:

  • num <number [–2 147 483 648, 2 147 483 647]>

    - value to save
remarks:
  • saves 4 bytes

net_packet::w_u64(num) obsolete

net_packet::w_s64(num) obsolete

net_packet::w_angle8(num) obsolete

net_packet::w_angle16(num) obsolete

net_packet::w_dir(vector) obsolete

net_packet::w_sdir(vector) obsolete

net_packet::w_clientID(cid) obsolete

net_packet::w_chunk_open8(num) obsolete

net_packet::w_chunk_close8(num) obsolete

net_packet::w_chunk_open16(num) obsolete

net_packet::w_chunk_close16(num) obsolete


lua_help noise

class noise

Creates noise effect (for post-process effector)
C++ class noise
{
  property fps;
  property grain;
  property intensity;

  noise();
  noise(number, number, number);

  function set(number, number, number);
};

-----------------------------------------

local noise_a = noise(0.9, 0.5, 30)

local noise_b = noise()
noise_b:set(0.9, 0.5, 30)

local noise_c = noise()
noise_c.intensity = 0.9
noise_c.grain = 0.5
noise_c.fps = 30

noise::fps

purpose:
holds noise frames per second
property:

  • <int>

    - noise fps

noise::grain

purpose:
holds noise grain
property:

  • <int>

    - noise grain

noise::intensity

purpose:
holds noise intensity
property:

  • <int>

    - noise intensity

noise::set(intensity, grain, fps)

purpose:
sets noise parameters
arguments:

  • intensity <float> [0.0, 1.0]

    - noise intensity

  • grain <float> [0.0, 1.0]

    - noise grain

  • fps <int> [1, 100]

    - noise fps


lua_help object

class object

Creates object action. See: scripted actions
C++ class object
{
  -- EObjectAction (SOC):
  const dummy = -1;
  const switch1 = 0;
  const switch2 = 1;
  const reload = 2;
  const reload1 = 2;
  const reload2 = 3;
  const aim1 = 4;
  const aim2 = 5;
  const fire1 = 6;
  const fire2 = 7;
  const idle = 8;
  const strap = 9;
  const drop = 10;
  const activate = 15;
  const deactivate = 16;
  const use = 17;
  const turn_on = 18;
  const turn_off = 19;
  const show = 20;
  const hide = 21;
  const take = 22;

  -- EObjectAction (COP):
  const dummy = -1;
  const switch1 = 0;
  const switch2 = 1;
  const reload = 2;
  const reload1 = 2;
  const reload2 = 3;
  const aim1 = 4;
  const aim2 = 5;
  const fire1 = 6;
  const fire2 = 8;
  const idle = 9;
  const strap = 10;
  const drop = 11;
  const activate = 16;
  const deactivate = 17;
  const use = 18;
  const turn_on = 19;
  const turn_off = 20;
  const show = 21;
  const hide = 22;
  const take = 23;

  object();
  object(string);
  object(game_object*);
  object(game_object*, enum object::EObjectAction);
  object(game_object*, enum object::EObjectAction, number);
  object(enum object::EObjectAction);
  object(string, enum object::EObjectAction);

  function completed();
  function action(enum object::EObjectAction);
};

object::object(gobj, flag, noidea)

object::object(bname, flag)

object::object(gobj, flag)

object::object(bname)

object::object(gobj)

object::object(flag)

object::object()

purpose:
object constructor
arguments:

  • gobj <game_object>

    - object

  • flag <const>

    -

    EObjectAction

    flag

  • noidea <number>

    - ???

  • bname <string>

    - bone name
returns:

  • <object>

    - object
example:
action(npc, object(npc:best_weapon(), object.fire1), look(look.fire_point, db.actor), cond(cond.time_end, 1500))
action(car, object("left_light",  object.activate), cond(cond.time_end, time_infinite))
action(car, object("right_light",  object.deactivate), cond(cond.time_end, time_infinite))

object::completed()

purpose:
checks whether object action was completed; probably called by

entity_action:completed()

(see: scripted actions)
returns:

  • true

    - action completed

  • false

    - otherwise

object::action(flag)

purpose:
sets object action type (?)
arguments:

  • flag <const>

    -

    EObjectAction

    flag


lua_help object_binder

class object_binder

Base class for object binding. Class derived from

object_binder

controls online entity (client object). See: Object binders
C++ class object_binder
{
  property object;

  object_binder(game_object*);

  function save(net_packet*);
  function update(number);
  function reload(string);
  function net_export(net_packet*);
  function net_save_relevant();
  function load(reader*);
  function net_destroy();
  function reinit();
  function net_Relcase(game_object*);
  function net_spawn(cse_alife_object*);
  function net_import(net_packet*);
};

object_binder::object

purpose:
holds pointer to binded object
property:

  • <game_object>

    - online (client) object

object_binder::save(np)

purpose:
saves object state
arguments:

  • np <net_packet>

    - packet (see: net_packet)

object_binder::update(delta)

purpose:
updates object state
arguments:

  • delta <number>

    - update time

object_binder::reload(sect_name)

purpose:
???
arguments:

  • sect_name <string>

    - section name

object_binder::net_export(np)

purpose:
???
arguments:

  • np <net_packet>

    - packet (see: net_packet)

object_binder::net_save_relevant()

purpose:
???
returns:

  • true

    - ???

  • false

    - ???

object_binder::load(r)

purpose:
loads object state
arguments:

  • r <reader>

    - ??? (why different class for reading from packet ?)

object_binder::net_destroy()

purpose:
called when object is switched offline

object_binder::reinit()

purpose:
???

object_binder::net_Relcase(obj)

purpose:
???
arguments:

  • obj <game_object>

    - binded object (?)

object_binder::net_spawn(se_obj)

purpose:
called each time binded object should be switched online
arguments:

  • se_obj <cse_alife_object>

    - server object of binded (

    object_binder::object

    property) object
returns:

  • true

    - object switched online properly

  • false

    - otherwise

object_binder::net_import(np)

purpose:
???
arguments:

  • np <net_packet>

    - packet (see: net_packet)


lua_help object_factory

class object_factory

Registers new class for object. Pointer to

object_factory

is accessible in function

class_registrator.register()

C++ class object_factory
{
  function register(string, string, string, string);
  function register(string, string, string);
};

object_factory::register(coc, soc, clsid, script_clsid)

object_factory::register(coc, clsid, script_clsid)

purpose:
registers new class for object
arguments:

  • coc <string>

    - client object class

  • soc <string>

    - server object class

  • clsid <string>

    - object class id

  • script_clsid <string>

    - script class id


lua_help particle particle_params particles_object

class particle_params

Sets particle parameters.
C++ class particle_params
{
  particle_params();
  particle_params(const vector&);
  particle_params(const vector&, const vector&);
  particle_params(const vector&, const vector&, const vector&);
};

particle_params::particle_params(a, b, c)

particle_params::particle_params(a, b)

particle_params::particle_params(a)

particle_params::particle_params()

purpose:
object constructor
arguments:

  • a <vector>

    - velocity, position, angles ???

  • b <vector>

    - velocity, position, angles ???

  • c <vector>

    - velocity, position, angles ???
returns:

  • <particle_params>

    - object

class particle

Plays particle effects. Utilised in scripted actions
C++ class particle
{
  particle();
  particle(string, string);
  particle(string, string, const particle_params&);
  particle(string, string, const particle_params&, boolean);
  particle(string, const particle_params&);
  particle(string, const particle_params&, boolean);

  function set_velocity(const vector&);
  function set_position(const vector&);
  function set_bone(string);
  function set_angles(const vector&);
  function completed();
  function set_particle(string, boolean);
};

particle::particle(p_name, b_name, pp, looped)

particle::particle(p_name, b_name, pp)

particle::particle(p_name, pp, looped)

particle::particle(p_name, b_name)

particle::particle(p_name, pp)

particle::particle()

purpose:
object constructor
arguments:

  • p_name <string>

    - particle name (path)

  • b_name <string>

    - bone name

  • pp <particle_params>

    - particle_params

  • looped <boolean>

    - looped
returns:

  • <particle>

    - object

particle::set_velocity(v)

purpose:
sets velocity for particle effect
arguments:

  • v <vector>

    - velocity

particle::set_position(pos)

purpose:
sets particle effect position
arguments:

  • pos <vector>

    - position

particle::set_bone(bname)

purpose:
sets bone
arguments:

  • bname <string>

    - bone name

particle::set_angles(v)

purpose:
sets angles
arguments:

  • v <vector>

    - angles

particle::completed()

purpose:
checks whether particle action (effect) was completed; probably called by

entity_action:completed()

(see: scripted actions)
returns:

  • true

    - particle effect is not playing

  • false

    - otherwise

particle::set_particle(str, b)

purpose:
sets particle
arguments:

  • str <string>

    - particle name (path)

  • b <boolean>

    - looped (?)

class particles_object

Plays particle effect on specified path or given position.
C++ class particles_object
{
  particles_object(string);

  function load_path(string);
  function start_path(boolean);
  function pause_path(boolean);
  function stop_path();
  function play_at_pos(const vector&);
  function move_to(const vector&, const vector&);
  function playing() const;
  function play();
  function looped() const;
  function stop();
  function stop_deffered();
};

particles_object::particles_object(path)

purpose:
object constructor
arguments:

  • path <string>

    - particle name (path)
returns:

  • <particles_object>

    - object

particles_object::load_path(p)

purpose:
loads particle path
arguments:

  • p <string>

    - patrol name
example:
local p = particles_object("anomaly2\\psi_antenna_00")
p:load_path("rad_antenna_particle1")

particles_object::start_path(looped)

purpose:
loads particle path
arguments:

  • looped <boolean>

    - whether particle effect should be looped
example:
local p = particles_object("anomaly2\\psi_antenna_00")
p:load_path("rad_antenna_particle1")
p:start_path(false) -- play once (not looped)
p:play() -- play particle effect on path

particles_object::pause_path(b)

purpose:
pause/unpause particle path
arguments:

  • b <boolean>

    -

    true

    (pause),

    false

    (unpause)

particles_object::stop_path()

purpose:
stops particle path

particles_object::play_at_pos(pos)

purpose:
starts playing particle effect at given position
arguments:

  • pos <vector>

    - position

particles_object::move_to(a, b)

purpose:
moves particle effect to given position
arguments:

  • a <vector>

    - position

  • b <vector>

    - ???

particles_object::playing()

purpose:
checks whether particle effect is playing
returns:

  • true

    - particle effect is playing

  • false

    - otherwise

particles_object::play()

purpose:
starts playing particle effect (only if path was set, otherwise use play_at_pos)
example:
local p = particles_object("anomaly2\\psi_antenna_00")
p:load_path("rad_antenna_particle1")
p:start_path(false) -- play once (not looped)
p:play() -- play particle effect on path

particles_object::looped()

purpose:
checks whether particle effect is looped
returns:

  • true

    - particle effect is looped

  • false

    - otherwise

particles_object::stop()

purpose:
stops playing particle effect (immediately)

particles_object::stop_deffered()

purpose:
stops playing particle effect but not immediately (stop is slightly delayed)


lua_help patrol

class patrol

Creates patrol action. See: scripted actions
C++ class patrol
{
  const dummy = -1;

  -- EPatrolStartType:
  const start = 0;
  const nearest = 2;
  const custom = 3;
  const next = 4;

  -- EPatrolRouteType:
  const stop = 0;
  const continue = 1;

  patrol(string);
  patrol(string, enum patrol::EPatrolStartType);
  patrol(string, enum patrol::EPatrolStartType, enum patrol::EPatrolRouteType);
  patrol(string, enum patrol::EPatrolStartType, enum patrol::EPatrolRouteType, boolean);
  patrol(string, enum patrol::EPatrolStartType, enum patrol::EPatrolRouteType, boolean, number);

  function level_vertex_id(number) const;
  function point(const patrol*, number);
  function flag(number, number) const;
  function game_vertex_id(number) const;
  function flags(number) const;
  function name(number) const;
  function index(string) const;
  function terminal(number) const;
  function count() const;
  function get_nearest(const vector&) const;
};

patrol::patrol(p_name, s_type, r_type, noidea, wp_index)

patrol::patrol(p_name, s_type, r_type, noidea)

patrol::patrol(p_name, s_type, r_type)

patrol::patrol(p_name, s_type)

patrol::patrol(p_name)

purpose:
object constructor
arguments:

  • p_name <string>

    - patrol name

  • s_type <const>

    -

    EPatrolStartType

    flag

  • r_type <const>

    -

    EPatrolRouteType

    flag

  • noidea <boolean>

    - looped ???

  • wp_index <number>

    - waypoint (p_name) index
returns:

  • <patrol>

    - object
example:
action(npc, move(move.walk_fwd, patrol("way", patrol.start, patrol.stop)), cond(cond.move_end))
action(dog, move(move.walk_fwd, patrol("way", patrol.start, patrol.continue):point(0)), cond(cond.move_end))
action(dog, move(move.walk_fwd, patrol("way", patrol.start, patrol.continue)), cond(cond.time_end, 400000))
action(dog, move(move.run_fwd, patrol("way", patrol.start, patrol.continue)), cond(cond.time_end, 10000))

patrol::level_vertex_id(wp_index)

purpose:
returns waypoint level vertex id
arguments:

  • wp_index <number>

    - waypoint index
returns:

  • <number>

    - level vertex id

patrol::point(wp_index)

purpose:
returns waypoint position
arguments:

  • wp_index <number>

    - waypoint index
returns:

  • <vector>

    - waypoint position

patrol::flag(wp_index, f_index)

purpose:
checks whether waypoint has flag
arguments:

  • wp_index <number>

    - waypoint index

  • f_index <number>

    - flag index
returns:

  • true

    - flag is set

  • false

    - otherwise

patrol::game_vertex_id(wp_index)

purpose:
returns waypoint game vertex id
arguments:

  • wp_index <number>

    - waypoint index
returns:

  • <number>

    - game vertex id

patrol::flags(wp_index)

purpose:
returns waypoint flags
arguments:

  • wp_index <number>

    - waypoint index
returns:

  • <flags32>

    - waypoint flags

patrol::name(wp_index)

purpose:
returns waypoint name
arguments:

  • wp_index <number>

    - waypoint index
returns:

  • <string>

    - waypoint name

patrol::index(wp_name)

purpose:
returns waypoint index
arguments:

  • wp_name <string>

    - waypoint name
returns:

  • <number>

    - waypoint index

patrol::terminal(wp_index)

purpose:
checks whether waypoint is terminal (last waypoint in patrol)
arguments:

  • wp_index <number>

    - waypoint index
returns:

  • true

    - terminal waypoint

  • false

    - otherwise

patrol::count()

purpose:
returns number of waypoints in patrol
returns:

  • <number>

    - number of waypoints in patrol

patrol::get_nearest(pos)

purpose:
returns index of nearest waypoint to given position
arguments:

  • pos <vector>

    - position
returns:

  • <number>

    - nearest waypoint index


lua_help profile_timer

class profile_timer

Creates timer to measure time needed to execute certain chunk of code
C++ class profile_timer
{
  profile_timer();
  profile_timer(profile_timer&);

  function start();
  function stop();
  function time() const;
  function __tostring(profile_timer&);

  operator +(const profile_timer&, profile_timer);
  operator <(const profile_timer&, profile_timer);
};

----------------------------------------------------------

local pt_a = profile_timer()
local pt_b = profile_timer(pt_a)

profile_timer::start()

purpose:
starts timer
example:
local pt = profile_timer()
pt:start()
...
pt:stop()

profile_timer::stop()

purpose:
stops timer
example:
local pt = profile_timer()
pt:start()
...
pt:stop()

profile_timer::time()

purpose:
returns timer value
returns:

  • <float>

    - timer value (in miliseconds)
example:
local pt = profile_timer()

pt:start()
for i = 1, 1000000 do
  -- nothing
end
pt:stop()

printf("time = %f [ms]", pt:time())

profile_timer::__tostring()

purpose:
arguments:

  • <number>

    -
returns:

  • <number>

    -


lua_help relation_registry

relation_registry.community_goodwill(faction, object_id)

purpose:
function returns current relation between given faction and object
arguments:

  • faction <string>

    - faction name

  • object_id <number>

    - id of object for which we want to check relation
returns:

  • <number>

    - faction goodwill (relation)
remarks:

Community relation values can be set in config\creatures\game_relations.ltx:
  • goodwill_enemy = -1000
  • goodwill_neutal = 0
  • goodwill_friend = 1000
example:
if relation_registry.community_goodwill("freedom", db.actor:id()) > 1000 then
  ...
end

relation_registry.set_community_goodwill(faction, object_id, relation)

purpose:
function sets relation between given faction and object
arguments:

  • faction <string>

    - faction name

  • object_id <number>

    - id of object for which we want to set relation

  • relation <number>

    - relation to set
remarks:

Community relation values can be set in config\creatures\game_relations.ltx:
  • goodwill_enemy = -1000
  • goodwill_neutal = 0
  • goodwill_friend = 1000
example:
relation_registry.set_community_goodwill("freedom", db.actor:id(), -5000)

relation_registry.change_community_goodwill(faction, object_id, relation)

purpose:
function updates relation between given faction and object
arguments:

  • faction <string>

    - faction name

  • object_id <number>

    - id of object for which we want to change relation

  • relation <number>

    - relation to update
example:
relation_registry.change_community_goodwill("freedom", db.actor:id(), 100)

relation_registry.community_relation(a, b)

COP only
purpose:
returns relation between given factions
arguments:

  • a <string>

    - faction name

  • b <string>

    - faction name
returns:

  • <number [-1000, 1000]>

    - factions relation

relation_registry.set_community_relation(a, b, rel)

COP only
purpose:
sets communities relation
arguments:

  • a <string>

    - faction name

  • b <string>

    - faction name

  • rel <number>

    - relation


lua_help render_device

class render_device

Stores information about rendering device. To get

render_device

object use

device()

function.
C++ class render_device
{
  property aspect_ratio;
  property cam_dir;
  property cam_pos;
  property cam_right;
  property cam_top;
  property f_time_delta;
  property fov;
  property frame;
  property height;
  property precache_frame;
  property time_delta;
  property width;

  function time_global(const render_device*);
  function is_paused(render_device*);
  function pause(render_device*, boolean);
};

-------------------------------------------------

local rd = device()

render_device::aspect_ratio

purpose:
holds current aspect ratio (4:3 => 0.750; 16:9 [widescreen] => 0.625)
property:

  • <float>

    - current aspect ratio
example:
local ar = device().aspect_ratio
-- or:
local ar = device().height / device().width

render_device::cam_dir

purpose:
holds current camera direction
property:

  • <vector>

    - current camera direction

render_device::cam_pos

purpose:
holds current camera position
property:

  • <vector>

    - current camera position

render_device::cam_right

purpose:
???
property:

  • <vector>

    - ???

render_device::cam_top

purpose:
???
property:

  • <vector>

    - ???

render_device::f_time_delta

purpose:
???
property:

  • <number>

    - ???

render_device::fov

purpose:
holds field of view
property:

  • <float>

    - field of view (67.5)

render_device::frame

purpose:
???
property:

  • <number>

    - ???

render_device::height

purpose:
holds current screen height
property:

  • <number>

    - current screen height

render_device::precache_frame

purpose:
???
property:

  • <number>

    - ???

render_device::time_delta

purpose:
???
property:

  • <number>

    - ???

render_device::width

purpose:
holds current screen width
property:

  • <number>

    - current screen width

render_device::time_global()

purpose:
holds number of miliseconds passed since starting render
returns:

  • <number>

    - number of miliseconds passed since starting render

render_device::is_paused()

purpose:
checks whether game is paused
returns:

  • true

    - game is paused (for instance main menu is displayed)

  • false

    - otherwise

render_device::pause(flag)

purpose:
pause/unpause game
arguments:

  • flag <boolean>

    - pause (

    true

    ) / unpause (

    false

    ) game


lua_help rtoken_list

class rtoken_list

Creates token list. Similar to token_list except tokens ids are automatically numbered (ascending, starting from 0)

See also:
  • token_list
  • token
C++ class rtoken_list
{
  rtoken_list();

  function clear();
  function remove(number);
  function count();
  function get(number);
  function add(string);
};

--------------------------

local t = rtoken_list()
t.add("enemy") -- id = 0
t.add("friend") -- id = 1
t.add("neutral") -- id = 2

rtoken_list::clear()

purpose:
removes all tokens from list

rtoken_list::remove(id)

purpose:
removes token with given id
arguments:

  • id <number>

    - token id

rtoken_list::count()

purpose:
returns number of tokens in list
returns:

  • <number>

    - number of tokens in list
example:
function iter_tokens(t_list)
  for id = 0, t_list:count() - 1 do
    printf("%s = %d", t_list:get(id), id)
  end
end

local t = rtoken_list()
t.add("enemy")
t.add("friend")
t.add("neutral")

iter_tokens(t)

rtoken_list::get(id)

purpose:
returns token name
arguments:

  • id <number>

    - token id
returns:

  • <string>

    - token name

  • nil

    - token not found
example:
local t = rtoken_list()
t.add("enemy")
t.add("friend")
t.add("neutral")

-- first token (enemy)
t.get(0)

-- last token (neutral)
t.get(t:count() - 1)

rtoken_list::add(name)

purpose:
adds token to list
arguments:

  • name <string>

    - token name


lua_help sound

class sound

Creates sound action (sets sound for object). See: scripted actions
C++ class sound
{
  -- ESoundTypes:
  const idle = 1;
  const eat = 2;
  const attack = 3;
  const attack_hit = 4;
  const take_damage = 5;
  const die = 7;
  const threaten = 9;
  const steal = 10;
  const panic = 11;

  sound();
  sound(string, string);
  sound(string, string, const vector&);
  sound(string, string, const vector&, const vector&);
  sound(string, string, const vector&, const vector&, boolean);
  sound(string, const vector&);
  sound(string, const vector&, const vector&);
  sound(string, const vector&, const vector&, boolean);
  sound(sound_object&, string, const vector&);
  sound(sound_object&, string, const vector&, const vector&);
  sound(sound_object&, string, const vector&, const vector&, boolean);
  sound(sound_object&, const vector&);
  sound(sound_object&, const vector&, const vector&);
  sound(sound_object&, const vector&, const vector&, boolean);
  sound(enum MonsterSound::EType);
  sound(enum MonsterSound::EType, number);
  sound(string, string, enum MonsterSpace::EMonsterHeadAnimType);

  function set_sound(string);
  function set_sound(const sound_object&);
  function set_position(const vector&);
  function set_bone(string);
  function set_angles(const vector&);
  function set_sound_type(enum ESoundTypes);
  function completed();
};

sound::sound(path, bone)

sound::sound(path, bone, ms)

sound::sound(flag)

sound::sound()

purpose:
object constructor
arguments:

  • path <string>

    - sound file path

  • bone <string>

    - bone name

  • ms <const:MonsterSpace>

    - if you specified head as bone argument; see: MonsterSpace

  • flag <const>

    -

    ESoundTypes

    flag
returns:

  • <sound>

    - object
example:
action(obj, sound(sound.idle), cond(cond.time_end, 3000))

local path = [[characters_voice\scenario\trader\trader3c]]
action(obj, sound(path, "bip01_head"), cond(cond.sound_end))
action(obj, sound(path, "bip01_head", MonsterSpace.head_anim_normal), cond(cond.time_end, 2000))

action(obj, look(look.point, db.actor:position()), sound("monsters\\boar\\attack_0",
  "bip01_head", vector(), vector(), true), cond(cond.look_end))

sound::set_sound(snd_obj)

sound::set_sound(path)

purpose:
sets sound
arguments:

  • snd_obj <sound_object>

    - sound_object

  • path <string>

    - sound file path

sound::set_position(pos)

purpose:
sets sound position
arguments:

  • pos <vector>

    - position

sound::set_bone(b_name)

purpose:
sets bone as sound source
arguments:

  • b_name <string>

    - bone name

sound::set_angles(vec)

purpose:
sets angles
arguments:

  • vec <vector>

    - angles

sound::set_sound_type(flag)

purpose:
sets sound type
arguments:

  • flag <const>

    -

    ESoundTypes

    flag

sound::completed()

purpose:
checks whether sound action was completed (finished playing); probably called by

entity_action:completed()

(see: scripted actions)
returns:

  • true

    - sound is not playing

  • false

    - otherwise


lua_help sound_object

class sound_object

Plays sounds.
C++ class sound_object
{
  const s3d = 0;
  const looped = 1;
  const s2d = 2;

  property frequency;
  property max_distance;
  property min_distance;
  property volume;

  sound_object(string);
  sound_object(string, enum ESoundTypes);

  function set_position(const vector&);
  function get_position() const;
  function play_no_feedback(game_object*, number, number, vector, number);
  function play_at_pos(game_object*, const vector&);
  function play_at_pos(game_object*, const vector&, number);
  function play_at_pos(game_object*, const vector&, number, number);
  function play(game_object*);
  function play(game_object*, number);
  function play(game_object*, number, number);
  function length();
  function playing() const;
  function stop();
  function stop_deffered();
  function attach_tail(string);
};

----------------------------------------------------------------------------

local path = "characters_voice\\scenario\\agroprom\\krot_help_pda_1"
local snd = sound_object(path)
local snd = xr_sound.get_safe_sound_object(path)

sound_object::sound_object(path)

purpose:
object constructor; in SOC you can also use

xr_sound.get_safe_sound_object(path)

arguments:

  • path <string>

    - sound path (default path: gamedata\sounds)
returns:

  • <sound_object>

    - object

sound_object::frequency

purpose:
sound frequency
property:

  • <number>

    - sound frequency (returns 1 ???)
remarks:
  • property is set when you start playing sound

sound_object::max_distance

purpose:
max distance at which sound can be heard
property:

  • <int>

    - max distance
remarks:
  • property is set when you start playing sound

sound_object::min_distance

purpose:
min distance at which sound can be heard
property:

  • <int>

    - min distance
remarks:
  • property is set when you start playing sound

sound_object::volume

purpose:
sound volume
property:

  • <float [0.0, 1.0]>

    - sound volume
remarks:
  • property is set when you start playing sound
example:
local snd = sound_object("tmp\\mplayer_15")
printf("vol = %f", snd.volume) -- CTD
snd:play(db.actor)
printf("vol = %f", snd.volume) -- 1.0 (default)

sound_object::set_position(pos)

purpose:
sets sound position
arguments:

  • pos <vector>

    - sound position
remarks:
  • set position only after you started playing sound
example:
local snd = sound_object("tmp\\mplayer_15")
snd:play(db.actor, 0, sound_object.s2d)
snd:set_position(db.actor:bone_position("bip01_head"))

sound_object::get_position()

purpose:
returns sound position
returns:

  • <vector>

    - sound position

sound_object::play_no_feedback(obj, p_type, delay, pos, vol)

purpose:
plays sound without any feedback, that is, once started playing, you cannot stop it, you can't also use any methods/properties from

sound_object

class to querry sound (any attempt will fail = CTD); use only for rather short sounds; keep in mind that when you start sound and reload saved game the sound will still keep playing untill it finish (unless it finished playing before you reloaded)
arguments:

  • obj <game_object>

    - object

  • p_type <const 0, 1, 2>

    - play type: (s3d | s2d | looped)

  • delay <number>

    - delay (in seconds) before playing sound

  • pos <vector>

    - sound position

  • vol <float [0.0, 1.0]>

    - sound volume

sound_object::play_at_pos(obj, pos, delay, p_type)

sound_object::play_at_pos(obj, pos, delay)

sound_object::play_at_pos(obj, pos)

purpose:
plays sound at specified position
arguments:

  • obj <game_object>

    - object

  • pos <vector>

    - sound position

  • delay <number>

    - delay (in seconds) before playing sound

  • p_type <const 0, 1, 2>

    - play type: (s3d | s2d | looped)
example:
local snd = sound_object("tmp\\mplayer_15")
-- delay is set to 10 seconds (that is: 10 * 10 = 100 in-game seconds
snd:play_at_pos(db.actor, vector():set(0, 0, 0), 10, sound_object.s2d)

sound_object::play(obj, delay, p_type)

sound_object::play(obj, delay)

sound_object::play(obj)

purpose:
plays sound
arguments:

  • obj <game_object>

    - object

  • delay <number>

    - delay (in seconds) before playing sound

  • p_type <const 0, 1, 2>

    - play type: (s3d | s2d | looped)

sound_object::length()

purpose:
returns sound length
returns:

  • <number>

    - sound length (in miliseconds)
example:
function sound_len_to_str(len)
  local h = math.floor(len / 3600000)
  local m = math.floor(len / 60000 - h * 60)
  local s = len / 1000 - m * 60 - h * 36000

  local i, f = math.modf(s)
  if f > 0.5 then
    i = i + 1
  end

  s = i

  if s >= 60 then
    s = s % 60
    m = m + 1
    if m >= 60 then
      m = m % 60
      h = h + 1
    end
  end

  return string.format("%s%02d:%02d", if_then_else(h > 0, string.format("%02d:", h), ""), m, s)
end

local snd = sound_object("tmp\\mplayer_15")
snd:play(db.actor)
printf("len = %d [%s]", len, sound_len_to_str(len))

sound_object::playing()

purpose:
checks if sound is playing
returns:

  • true

    - sound is playing

  • false

    - otherwise

sound_object::stop()

purpose:
stops sound (immediately)

sound_object::stop_deffered()

purpose:
stops sound but not immediately (stop is slightly delayed)

sound_object::attach_tail(path)

purpose:
utilised in dynamic music; given sound is attached at the end of current sound, that is, attached sound will be played once current sound stops playing; available in COP only.
arguments:

  • path <string>

    - sound path (default path: gamedata\sounds)


lua_help token

class token

Creates token

See also:
  • token_list
  • rtoken_list
C++ class token
{
  property id;
  property name;

  token();
};

-------------------

local t = token()
-- age = 25
t.name = "age"
t.id = 25

token::name

purpose:
holds token name
property:

  • <string>

    - token name

token::id

purpose:
holds token id
property:

  • <number>

    - token id


lua_help token_list

class token_list

Creates token list (list of constants).

See also:
  • token
  • rtoken_list
C++ class token_list
{
  token_list ();

  function clear();
  function remove(string);
  function name(number);
  function id(string);
  function add(string, number);
};

-------------------------------

local t = token_list()
t.add("neutral", 0)
t.add("friend", 1)
t.add("enemy", 2)

token_list::clear()

purpose:
removes all tokens from list

token_list::remove(name)

purpose:
removes token with given name
arguments:

  • name <string>

    - token name

token_list::name(id)

purpose:
returns token name
arguments:

  • id <number>

    - token id
returns:

  • <string>

    - token name

  • nil

    - token not found

token_list::id(name)

purpose:
returns token id
arguments:

  • name <number>

    - token name
returns:

  • <number>

    - token id

  • nil

    - token not found

token_list::add(name, id)

purpose:
adds token to list
arguments:

  • name <string>

    - token name

  • id <number>

    - token id
remarks:
  • token name should be unique
  • token id should be unique


lua_help vector2

class vector2

Represents vector 2D. Available only in COP.
C++ class vector2
{
  property x;
  property y;

  vector2();

  function set(number, number);
  function set(const vector2&);
};

vector2

SOC implementation
function is_vector2(v2)
  if type(v2) ~= "userdata" then
    assert(nil)
  end
  return (v2.x ~= nil and v2.y ~= nil)
end

class "vector2"

function vector2:__init()
  self.x = 0.0
  self.y = 0.0
end

function vector2:__finalize()
end

function vector2:set(x, y)
  if type(x) == "number" then
    self.x = x
  else
    local str = string.format("vector2::set() accepts only float values, got %s", type(x))
    assert(nil, str)
  end

  if type(y) == "number" then
    self.y = y
  else
    local str = string.format("vector2::set() accepts only float values, got %s", type(y))
    assert(nil, str)
  end
end

function vector2:Set(v2)
  local res, ret = pcall(is_vector2, v2)
  if res then
    if ret then
      self.x = v2.x
      self.y = v2.y
    else
      abort("vector properties are not set!")
    end
  else
    abort("not a vector2 object!")
  end
end

-------------------------------------------------------

local a = vector2()
v2.x = 0.1
v2.y = 0.2

local b = vector2()
b:set(0.1, 0.2)

local c = vector2()
c:Set(b)

vector2::set(x, y)

vector2::set(v2)

purpose:
sets x and y values (coordinates)
arguments:

  • x <float>

    - x value

  • y <float>

    - y value

  • v2 <vector2>

    -

    vector2

    object


lua_help main namespace

alife()

purpose:
returns instance of alife_simulator
returns:

  • <alife_simulator>

    - alife_simulator

app_ready()

purpose:
checks whether game is working properly
returns:

  • true

    - game is working properly

  • false

    - otherwise

bit_not(a)

purpose:
performs bitwise NOT (complement)
arguments:

  • a <number>

    - number
returns:

  • <number>

    - ~a (a with inverted bits)

bit_or(a, b)

purpose:
performs bitwise OR
arguments:

  • a <number>

    - number

  • b <number>

    - number
returns:

  • <number>

    - a | b

bit_and(a, b)

purpose:
performs bitwise AND
arguments:

  • a <number>

    - number

  • b <number>

    - number
returns:

  • <number>

    - a & b

bit_xor(a, b)

purpose:
performs bitwise XOR
arguments:

  • a <number>

    - number

  • b <number>

    - number
returns:

  • <number>

    - a ^ b

buy_condition(ini_file*, string)

buy_condition(number, number)

purpose:
???
arguments:

  • ini_file <ini_file>

    - ini_file

  • string <string>

    - ???

  • number <number>

    - ???

  • number <number>

    - ???

command_line()

purpose:
returns path to xr_3da.exe with additional parameters (if any)
returns:

  • <string>

    - path with parameters
command line

example:
local s = command_line()
-- for instance:
-- "d:\s.t.a.l.k.e.r. - shadow of chernobyl\bin\xr_3da.exe" -nointro

create_ini_file(content)

purpose:
creates ini file (in memory); see: Dynamic ltx
arguments:

  • content <string>

    - ini file content

device()

purpose:
returns pointer to render_device
returns:

  • <render_device>

    - render_device

dik_to_bind(key)

purpose:
returns numerical value of keyboard key; utilised in <CUIScriptWnd>::OnKeyboard()
arguments:

  • key <DIK_keys>

    - DIK_keys
returns:

  • <const>

    - key_bindings
example:
local key = DIK_keys.DIK_ESCAPE -- 1 (ESCAPE)
local b = dik_to_bind(key) -- 47 (ESCAPE)

if b == key_bindings.kQUIT then
  printf("equal") -- equal
end

editor()

purpose:
checks whether game is running in SDK
returns:

  • true

    - SDK mode

  • false

    - otherwise

ef_storage()

purpose:
???
returns:

  • <???>

    - ???

error_log(text)

purpose:
prints error message to game log (console); blocked in SOC
arguments:

  • text <string>

    - text to print

flush()

purpose:
prints text from buffer to game log (console); used in conjuction with log; blocked in SOC

game_graph()

purpose:
returns pointer to CGameGraph
returns:

  • <CGameGraph>

    - CGameGraph

game_ini()

purpose:
???
returns:

  • <???>

    - ???

get_console()

purpose:
returns pointer to CConsole
returns:

  • <CConsole>

    - CConsole

get_hud()

purpose:
returns pointer to CUIGameCustom
returns:

  • <CUIGameCustom>

    - CUIGameCustom

GetARGB(a, r, g, b)

purpose:
returns color value
arguments:

  • a <int [0, 255]>

    - transparency

  • r <int [0, 255]>

    - red

  • g <int [0, 255]>

    - green

  • b <int [0, 255]>

    - blue
returns:

  • <int>

    - color value packed as int number; order: argb where a is most significant byte

GetFontGraffiti19Russian()

purpose:
returns graffiti19 font type; see CGameFont for more available fonts

GetFontGraffiti22Russian()

purpose:
returns graffiti22 font type; see CGameFont for more available fonts

GetFontGraffiti32Russian()

purpose:
returns graffiti32 font type; see CGameFont for more available fonts

GetFontGraffiti50Russian()

purpose:
returns graffiti50 font type; see CGameFont for more available fonts

GetFontLetterica16Russian()

purpose:
returns letterica16 font type; see CGameFont for more available fonts

GetFontLetterica18Russian()

purpose:
returns letterica18 font type; see CGameFont for more available fonts

GetFontLetterica25()

purpose:
returns letterica25 font type; see CGameFont for more available fonts

GetFontMedium()

purpose:
returns medium font type; see CGameFont for more available fonts

GetFontSmall()

purpose:
returns small font type; see CGameFont for more available fonts

GetFontDI()

purpose:
returns header font type; see CGameFont for more available fonts

getFS()

purpose:
returns pointer to FS
returns:

  • <FS>

    - FS

GetTextureInfo(string, string)

purpose:
???
arguments:

  • string <string>

    - ???

  • string <string>

    - ???
returns:

  • <???>

    - ???

GetTextureName(string)

purpose:
arguments:

  • string <string>

    - ???
returns:

  • <???>

    - ???

GetTextureRect(string)

purpose:
arguments:

  • string <string>

    - ???
returns:

  • <???>

    - ???

IsGameTypeSingle()

purpose:
checks game type (single/multi player)
returns:

  • true

    - single player game

  • false

    - multiplayer (?)

log(text)

purpose:
prints given text in game log (console); used in conjuction with flush; blocked in SOC
arguments:

  • text <string>

    - text to print

prefetch(script_name)

purpose:
prefetch script
arguments:

  • script_name <string>

    - script name

script_server_object_version()

purpose:
returns server (game) version; field current_server_entity_version from section script (config\system.ltx)
returns:

  • <number>

    - game version; for SOC:
    • 5 => 1.0000
    • 6 => 1.0001, 1.0003, 1.0004
    • 7 => 1.0005, 1.0006, 1.0007

sell_condition(ini_file*, string)

sell_condition(number, number)

purpose:
???
arguments:

  • ini_file <ini_file>

    - ???

  • string <string>

    - ???

  • number <number>

    - ???

  • number <number>

    - ???

show_condition(ini_file*, string)

purpose:
???
arguments:

  • ini_file <ini_file>

    - ???

  • string <string>

    - ???

system_ini()

purpose:
returns ini_file with parsed config\system.ltx
returns:

  • <ini_file>

    - ini_file (config\system.ltx)

time_global()

purpose:
defined in _g.script; see render_device::time_global
returns:

  • <number>

    - number of miliseconds passed since starting render

has_alife_info(info_id)

purpose:
defined in _g.script; checks whether actor has infoportion
arguments:

  • info_id <string>

    - infoportion id
returns:

  • true

    - actor has infoportion

  • false

    - otherwise

user_name()

purpose:
returns user name
returns:

  • <string>

    - user name
example:
local un = user_name()
printf("%s", un) -- barin

valid_saved_game(path)

purpose:
checks whether given saved game file is valid
arguments:

  • path <string>

    - path to saved game file
returns:

  • true

    - saved game file is valid

  • false

    - otherwise

verify_if_thread_is_running()

purpose:
checks whether coroutine (thread) is running; returns

nil

in SOC

xrRender_test_r2_hw()

purpose:
tests whether your rig can handle dx9 renderer in pure hardware mode; available in SOC only
returns:

  • true

    - dx9 hardware support enabled

  • false

    - otherwise

IsDynamicMusic()

purpose:
checks whether dynamic music is playing; available in COP only
returns:

  • true

    - dynamic music is playing

  • false

    - otherwise

render_get_dx_level()

purpose:
???; available in COP only
returns:

  • true

    - ???

  • false

    - otherwise

IsImportantSave()

purpose:
???; available in COP only
returns:

  • true

    - ???

  • false

    - otherwise

cast_alife_object_to_trader_abstract(cse_alife_object*) obsolete (removed)

cast_alife_object_to_creature(cse_alife_object*) obsolete (removed)

cast_planner_to_action(action_planner*) obsolete (removed in SOC)

cast_action_to_planner(action_base*) obsolete (removed)


SDK - STALKER Level Editor Tutorial

STALKER Level Editor Tutorial

This basic tutorial will help you start making maps of your own using the Stalker Leveleditor. First we'll cover the basic concepts of the editor and get familiar with the editor's functions, then we'll cover how to step by step create a simple, playable level. Might seem overwhelming at first, but with enough practice you'll be able to create anything your imagination can come up with.

» Before Opening the Editor

Now, you have to have the game installed and properly configured to be able to make levels for it. You will also need a copy of 3D Studio Max 6 as all level geometry will be created there and exported to the editor using the XrayExportUtility plugin. Also a paint program of choise can be very useful for making your own textures and skins.

» The Editor

Now that we have everything we need to get started, let's open up the editor. If everything is working properly it should start up without any problems. So let's take a look at the initial screen:

When the first screen appears, you are presented with a 3d viewport that you will use to position the objects in your world. The viewport shows both the objects as a textured solid form and in the correct perspective, and closely relates the view as it will appear in the game.

Navigation in the 3d view takes a little getting used to. Holding down SHIFT + left mouse button moves the camera forward, backward, left and right. Holding down SHIFT + right mouse button moves the camera up and down. Holding down SHIFT + left and right mouse buttons will make the camera act like a person standing still and looking around.

» The Top Toolbar

The first section of the toolbar should be pretty self-explanatory.
Undo (Ctrl+Z), Redo (Ctrl+Y) and Select.

The second section of the toolbar is where a lot of what you will use is located.

Add

Move

Rotate

Uniform Scale

Restrict Axis X, Y, Z or ZX

Parent CS Toggle

Non Uniform Scale

In the third section of the toolbar you’ll find different snap settings.

Grid Snap Toggle (G)

Object Snap Toggle (O)

Moving Snap To Object Toggle

Normal Alignment

Vertex Snap Toggle

Angle Snap Toggle

Moving Snap Toggle

In this last section you'll find some very usefull camera tools.

Zoom Extents

Zoom Selected Extents

Aligns the camera view in a specific direction, Front, Back, Left and Right etc.

Free Zoom Extents

Free Zoom Selected Extents

Free Zoom Extents

» The Main Toolbar

The dropdown menu section on the right side of the top toolbar has all of our browsers. Here you will find all the basic tools needed to get started. Most things in these menues should be self-explanatory but let's have a look at them anyway.

File - This is where you will open or save your work.
Scene - Here you will find options for and info about your level.
Compile - All export tools necessary for compile, more about this later.
Objects - Here you will find the Object Library Editor where you import your level geometry / objects.
Images - This is where you import your textures and set their properties in the Image Library.
Sounds - Sound Editor and tools.
Light Anim Editor - Light Animation Library / Editor where you can make or edit lighting effects.
Object List - Lists all objects in your level.
Preferences - Editor Preferences

Edit - Copy / Paste, Undo and Redo.
Selection - Invert selection, Select all, Unselect all.
Visibility - Here you can Hide or Unhide selections.
Locking - Lock or Unlock selections.
Properties - Shows properties of selection.

This is where you choose what to work with or edit in your level.

Obejct - Brings up a list of objects that can be added to, or worked with in your level.
Light - Add / Edit lighting in your level.
Sound Source - Add / Edit sound sources.
Sound Env - Adds a sound volume to your level.
Glow - Glow.
Shape - Creates a shape of box or a shpere.
Spawn Element - Add / Edit spawn points for players, weapons, vehicles etc.
Way Points - AI path tool.
Sector - A tool to divide your level into sectors.
Portal - Generates portals between sector.
Group - Edit groups.
Static Particles - Add / Edit particles.
Detail Objects - Add / Edit detail object layers in your level.
AI Map - Create / Edit a AI map.
Wallmarks - Add / Edit wallmarks.

» Getting Started

Now that you're familiar with the most basic functions of the editor, it's time to actually do some level building. The information above is quite a bit to handle, but as we move into actually building a level, many of the functions and concepts will become clearer.

Let's get started, shall we?

First off, make sure you got the XRayExportUtility plugins for 3D Studio Max 6 installed and working. As all level geometry and objects will be modelled in Max, I suggest we start there...

For this tutroial I'm going to make a simple scene with two houses, a road and the surrounding terrain. Now, objects in the Stalker universe is scaled as it would be in real life using the metric system. So you might want to keep that in mind when you start modelling things like the terrain and buildings, things that has to fit together might not if you rescale them in the editor. Other objects like decorations can always be rescaled in the editor.

I've made this terrain mesh 128x128 meters and cut out for the two houses.
This will do just fine so let's move on to the making of a terrain texture.

» Terrain textures And Shaders

Now, for a terrain of this size a 1024*1024 texture will be more than enought as the texture itself will be used as a colorlayer, then we will use a detail shader to add more depth to the terrain. So when painting the terrain texture try to keep away from any hard edges as it will only look wierd. We also need to make the detail textures for our shaders, in this case two of them, grass and asphalt. The detail textures themselves are simply grayscaled tilable images, usually with 50% grey used as a base colour and darker and lighter greys to make shadows and highlights.

Now that the textures are done we need to name and save them. Now, we want our new textures in a new folder in the Image Editor so we can easily find them. Let's call it "TutorialTex", then the texture should be named "TutorialTex_ texturename.tga". Very easy. So, name the textures, save them as .tga files and copy them to the Import folder in your Xray directory.

Now that the terrain and detail textures are done it's time to import the textures and create the detail shaders. First open up the Stalker Shader Editor and in the dropdown toolbar go to Scene > Images and choose Check New Textures. If everything is good a new window will appear and tell you there are 3 new textures, press Ok. Now go to Scene > Images and choose Image Editor and you will get a list if all textures available including your own new ones. Find the TutorialTex folder and select the terrain texture. Now we are going to change a few settings for this texture as it will be used for terrain only. First uncheck MipMaps > Enabled, wont be needing mipmaps for this texture. Then scroll down to Flags and check Implicit Lighted, this will give the terrain lightmap more detail. Next we will give the terrain a detail texture, go to Details and check Use As Diffuse. Click on the three dots next to Texture and select the grass detail texture you just made. Then we have to set the detail scale, how many times we want the detail texture to tile across our terrain texture, for this texture I use 50. Then press ok and all changes will be saved.

Now the shaders...
In the dropdown toolbar there is a section called tools, select the Engine tab and choose Create. A new window will appear asking you what kind of shader you want to create, choose 'LEVEL:Implicit**detail'. A 'shader00' will appear in the Items list under Tools, highlight the shader and it's properties will show up in the Item Properties. We'll start by giving it a name, for example "tutorial_grass_shader". Scroll down to Detail map and click on $null next to Name, choose Custom and select your grass detail texture. Next click on Custom next to Transform, choose Custom and a Custom Transform field will appear. Change Mode > Programmable to TCM and even more settings will appear. Check Scale enabled and click on [Wave] next to ScaleU, set Offset to 50 and do the same thing for ScaleV.

Now create a new shader, "tutorial_asphalt_shader" with the same settings using the asphalt detail texture.
Then go to File > Save, and that's it for the terrain textures and shaders.

» Exporting The Terrain

Now let's head back to 3d studio max and set up the materials, texture the mesh and export it to the Stalker Level Editor.

Load the terrain and bring up the Material Editor. Select a material and change it's type from Standard to XRayMtl and load the terrain texture as diffuse. Next, pick a new material and do the same thing, using the same texture. Now we are going to set up the first one to use the grass shader we just made and the other one to use the asphalt shader.

Expand X-Ray Params in your first material and you will be able to set the textures shaders and game material. In Engine Shader, pick your "tutorial_grass_shader". In Compile Shader use default_smooth. And in Game Material choose materials\grass. Now pick the other material and do the same thing. In Engine Shader, pick your "tutorial_asphalt_shader". In Compile Shader use default_smooth. And in Game Material choose materials\asphalt.

It's time to map the textures to the mesh, now this is very easy.

Select the terrain mesh and apply the 'grass material' to the whole mesh.
Then select the areas that is asphalt and apply the 'asphalt material' to it.

Before exporting the terrain let's do a Reset XForm, collapse the stack and reset the pivot. Now go to the Utilities tab and you will find the XRay Export tool. Select the terrain mesh and press Export Editor Object, name it to something like "TutorialTerrainMesh" and save it to the import folder in your Xray directory.

» Importing Objects To The Editor

What we are going to do next is to import the terrain to the level editor. This procedure works for any static object you want to import, so let us begin by opening the editor. Go to Toolbar > Scene > Objects and select Library Editor, that will open the Object Library. Right click in the empty space on the left side and choose Create Folder, name it something like "tutorial_objects". Next, select your new folder and press the Import Object buttom, select the terrain mesh and press open. The terrain mesh should now show up in your "tutorial_objects"folder. Now, select the terrain and press the Properties buttom, a new window will appear called Object Properties. Here you can edit all setting for the object, scale the object or edit shaders and material settings. Now we might want to create a thumbnail screenshot of the object, just to make things easier when you get a lot of objects in your list. Just check the box that says preview and the object will show up in the viewport. Now make sure you got a good view of the object and then simply press the Make Thumbnail buttom, and that's it.

» Objects And Shaders

Now that we got the terrain done let's move on to some quick notes about buildings, decorations and other objects.

Objects, like the building in the picture above, are very straight forward to export and import. However there are things to keep in mind when modelling your objects. The Stalker engine uses lightmaps as default, so if every object in your level is set to use lightmaps it will become very very slow. Now, there is a way to deal with this by using vertex lighting on faces or objects that don't actually need to recive shadows. By keeping that in mind when modelling and texturing your objects, you can save up a lot of memory. And also, when modelling buildings or such you should try to keep each room or level a separate mesh, this will make it much easier to optimize.

So now that I have modelled and textured the first building for the level, I need to set the textures shader and material properties. For the building in the picture above I'm going to use vertex lighting for the roof and the railings by the stairs. All the other faces will use lightmaps. Now, bring up the Material Editor, select the brick texture and expand X-Ray Params. This texture will use lightmaps so Engine Shader and Compiler Shader should be left default. But, you might want to set the Game Material to materials\bricks. Next, pick one of the roof textures, one that will use vertex lighting. Now change the Engine Shader and the Compiler Shader so that both reads 'def_shaders\def_vertex', this will make all faces that are mapped with this texture use vertex lighting. And don't forget to set the Game Material. Now go through the settings for all the textures used for this object and make any necessary changes. Then reset X-Form, reset pivot and export the object to the import folder.

Now there are quite a few things that can be done by changing the Engine and Compiler Shaders, most of them should be self-explanatory but here are two examples.

I want the lightbulb on this wall lamp to be lit and the lamp itself to look smooth. Set the lightbulb's Engine Shader to 'def_shaders\def_vertex' and the Compiler Shader to 'def_shaders\def_selflight'. Now, an object like this really dont need to use lightmaps, but if you want it to set the lamp's Engine Shader to 'default' and the Compiler Shader to 'default_smooth'. And if not, just set the lamp texture to use vertex lighting and that will give it a smoother look aswell.

For an alpha texture, like the wirefence above, to show up correctly you must first make sure that the textures properties are all ok. Open the Shader Editor and bring up the Image Editor, find the alpha texture you want to use and make sure Alpha is turned on. Then you can uncheck MipMaps Enabled and save. Now, back in 3d studio max, set the alpha textures Engine Shader to 'def_shaders\def_aref' and the Compiler Shader to 'def_shaders\default_lm01'. If you are using a plane polygon for the texture you might want to make the texture two sided, this is done in the objects properties after the object is imported to the editor.

» Building The Level

It's about time we start working with what we got and build the actuall level, so open up the Level Editor. We'll start by adding the terrain. Go to the Edit Mode menu in the toolbar and select Object, this will put us in object edit mode. Now find and select your terrain in the Objects list, press the Add button in the top toolbar and click in the center of your viewport.

If everything is ok you should see the terrain in your viewport just like
in the picture above. Now, let's add some more stuff to the level...

All objects are added to your level the same way, and as long as you are in object
edit mode you can move around and scale your objects any way you want to.

Next we are going to light to your level but I think now would be a good time to save your work.

» Lighting

For this tutorial level I will only use some basic outdoor lighting. Let's add a lightsource to our scene and go through the settings. Go to the Edit Mode menu in the toolbar and select Light, press the Add button in the top toolbar and click somewhere in the viewport. Now, right-click on it and choose properties.

Hemisphere > Quality - Hemisphere lightmap quality
Hemisphere > Light Control - Hemisphere light controller. $static, $hemi or $sun.

Sun Shadow > Visible - When checked, you will get an approxymate sunlight in the editor viewport. This can be really usefull when it's time to determine the position of the sun.
Sun Shadow > Quality - Sun shadow lightmap quality.
Sun Shadow > Altitude and Longitude - Position of the sun.

Controls - These settings are, as far as I can tell, not in use.

Items > Name - No need to change this one.
Items > Transform > Position, Rotation and Scale
Items > Type - There are two types of light available, Point or Spotlights.
Items > Color - Lightcolor
Items > Brightness
Items > Light Control - $static, $hemi or $sun. If this particular lightsource were to simulate sunlight, it would have been set to $sun. So when the sunlight changes in color or brightness, so would this lightsource.

Usage > Lightmap - Should be checked by default.
Usage > Dynamic - Check for a dynamic lightsource.
Usage > Animated - Check to have the lightsource use an animated light effect.

Range > Set range of lightsource.

Attenuation > Set fadeout / falloff for the lightsource.

Fuzzy > Now this is a really nice feature. If you want a lightsource to be represent as volumetric, and I'm sure that at some point you will, you should check this option. Fuzzy will let you define a volume which will be represented as a number of sub versions of the original lightsource to create a volumetric lighteffect. Each light in the volume will have the energy of the main lightsource's brightness divided by the number of lights used in the volume.
Fuzzy > Count - Set the number of lights used to represent the volume.
Fuzzy > Generate - Generates random placement of lightsources whitin the volume.
Fuzzy > Shape - Defines the shape of the volume, box or sphere.
Fuzzy > Half Dimension / Radius - Set volume dimensions.

Use In D3D > Should be checked by default.

Now that you know what most of the settings do and how they work, let's have a look at the properties for the lightsource I am going to use for the tutorial level.

This lightsource is set up to use some very basic sunlight. Remember,
you wont be able to compile your level unless you have a lightsource
in it. And while tweaking lights, keep down on the lightmap quality.

» Adding Glow

Using glow for a lamp will give it a corona effect. Go to the Edit Mode menu in the toolbar and select Glow, press the Add button in the top toolbar and click somewhere in the viewport. Right-click to bring up the properties. Now, there are only three entries that needs to be set.

Items > Texture - Click in the empty space to set glow texture.
Items > Shader - Click to bring up the Engine Shader list, choose effects \ glow
Items > Radius- Size of corona.

» Detail Objects

Detail Objects are decorations such as grass, leaves and stones that can be added to your level. Adding Detail Objects to your level really makes it come to life, and it's pretty easy to do.

Start by selecting your terrain mesh, then go to the Edit Mode menu and select Detail Objects.Now that we are in Detail Objects mode, go to Snap List > Commands and select Add Selected To List. Your terrain should now show up in the snaplist. Now let's go to the Tools menu and select Properties.

Detail Objects > Objects per square - Number of objects to be drawn per square.
Detail Objects > Base Texture - Click in the empty field and add your terrain texture here. We will, later on, use this texture to pick colors from and associate them with detail objects. Sometimes it's a good idea to use different version of your texture, with more contrast between colors and maybe even have an alpha that will mask away areas where you don't want detail objects at all. This texture wont show up in game, it'll only help you in the process.

Common > Draw objects - When checked, detail objects will be shown in your editor viewport. Check for now.
Common > Draw base texture - Shows your base texture on the terrain. This will make it easier when you pick colors from the texture.
Common > Base texture blended - ???
Common > Draw slot boxes - Shows a boxlike grid on your terrain.

Now with that set, go to DO Commands in the Toolbar and click on First Initialize. Next go to DO Commands and select Object List, a window will appear called Detail objects.

This is where we associate colors from the terrain with the detail objects. Now click on the + in the lower left corner and you will get a list of objects. Select objects by checking them and when done press Ok. Now, back in the Detail objects window you should now see a list of the detail objects you picked from the object list. You are now able to change an objects density and scale by selecting it from your list. Next, click on Append Color Index and you will get your first detail object slot, now click on the color box to open the color picker tool. Pick a color from the terrain using the crosshair, put your mouse pointer on the crosshair, hold down the left mouse button and drag it out on your terrain. Then just click the Append Color Index button to add another color. When you are done picking out colors, it's time to associate them with the objects in your list. This is done by simply dragging an object from the list and dropping it into one of the detail object slots, you can have up to three objects per slot. Now, when you are done with that, close the window and you will be told to reinitialize your level. So, select Reinitialize All in the DO Commands menu and you are done!

» Sectors, Portals And HOM

Time has come to take a look at sectors, portals and HOM. In this section I will try to explain the main techniques for level optimization. Let's start with Sectors and Portals.

Sectors should be your primary means of level optimization, though in maps set mainly outdoors this will not always be possible. Sectors will help the engine by sectioning off the level, any area that the player cannot see at all will not be considered for rendering. To be able to set up sectors properly you need to have your mesh split up into separate objects just like the building in the picture above, one for each sector. This must be done before you export it from 3D Studio Max, but you can still export and import the building as one object by having all objects selected on export.

Now let's have a look at how it is set up in the level editor.

Go to the Edit Mode menu in the toolbar and select Sector. Press the Add button and then Create New from the Edit section in the Toolbar. Now, click somewhere within the object where you intend to have your first sector, if everything is ok it should turn white. Now this is your first sector, if you want to expand your sector to include another part of your object you can use +M or -M to add or remove these areas. Then just click Create New to create your next sector. To make your sectors easier to recognize you can have them use different colors, this is set in the properties of each sector.

When you are finished creating your indoor sectors, your level should look something like in the picture above. Now, we are not quite finished yet, we need to make the rest of the level into a sector aswell. Go to Toolbar > Command and select Create Default. And to make sure everything is ok, go to Toolbar > Command and select Validate Sectors.

Next we are going to create portals between our sectors. Go to the Edit Mode menu in the toolbar and select Portal, then go to Toolbar > Command and select Compute All Portals. Done!

With sectors and portals in place, your level should look something like in the picture above.

Now, let's have a look at HOM.

HOM are the second major source of optimization, and are most useful in outdoor levels. HOM is a very simple mesh that can be hidden in other geometry and hide everything behind it from the player. Some situations where HOM would be best is with terrain. Any outdoor area where there is a large solid barrier, a hill or a large building is a perfect place to put an HOM. Indoor maps can also use HOM in addition to sectors and portals for further optimization of mesh-heavy areas. A HOM mesh is created and exported just like any other object. When importing the mesh to the editor, click on Properties > Flags and check HOM.

» Spawn Elements

Next we are going to add a spawn point, a place for the player to start. Go to the Edit Mode menu in the toolbar and select Spawn Elements. Then select $rpoint in the list of spawn elements, press the Add button and click on the spot in your map where you want the spawn point. The $rpoint is actually a respawn point, so for a multiplayer game you might want to add a bunch of them to your level. Now, in the list of spawn elements you will also find weapons, ammo and other equipment that you can add to your level the very same way you added the spawn point. And most of the spawn objects properties are very easy to understand, there are lots of settings there to play around with.

» Compilling Your Work

Now, finally, we have come to the very last part of this tutorial. We are now going to compile our level and make it playable. First, to be able to compile your level it has to be free from any errors, contain at least one Lightsource, Glow and a spawn point. One way of making sure your level is ok is to run Validate, go to the Scene menu and select Scene > Validate. Now, before we build our level and run the compiller program we need to set a Level path and Make Game, Details and HOM. Start by bringing up the Build options, Scene > Options and select Level options. Here we are going to enter the name of the level that will be used when compilling and when running your level, I'll call this one "Tutorial_001". While in Build options switch to Lightmaps. Here you will find detail settings for the lightmaps in your level. If you set a lower value in Pixels per meter, less details will show up on your lightmaps but it will compile much faster. Which is a very nice feature if you are working on huge levels and want to do a quick compile.

With that set we can move on to exporting our level. Now go to Compile and select Make Game, then Make Details and finally Make HOM. Now, the final step to it in the editor is to build you level. Go to Compile and select Build, click on Continue Build and you are done.

Now, close the level editor and open a Command Prompt window. Start the compiller program with a line like this one:
c:\xray\stalker\xrLC.exe -f tutorial_001
Now, ofcause, this line will only work if you have Stalker installed like I do. Make sure you enter the correct path to your Stalker folder, xrlc.exe -f is optional, and then your levelname.Compilling may take a while depending on the size of your level and your lightmap settings. Now this compile will not include Detail Objects as they are compilled separately. So when your compilation is done, and if you used Detail Objects in you level, we will have to compile them aswell. But don't worry, this compile will not take long. From the Command Prompt, start the detail objects compiller with a line like this:
c:\xray\stalker\xrDO_light.exe -f tutorial_001
And again, make sure you enter the correct path to your Stalker folder.

So, when the compilation is done you got your first playable level and from here, you should have a pretty good jump on creating a level. Now go playtest it...


AI Map Compiling

Basis

Lets start from something easy – lets compile a level with AI and monsters on the map mp_atp. Open the level: first thing to do is to remove all rpoint (those are points of respawn for multyplayer). Moved? Perfect. Creating Spawn elements -> actor and in the Properties lets right the following:
[spawn]
wpn_ak74
Thanks to this the player will have AK74 in weapons after spawn. Next – creating a monster i.e. bloodsucker and put him on the map. Now we need AI map – AI net required for NPCs and monsters in order for them to orientate on the level. Then press Object and click on the earth (now we selected it for following manipulations), next AI Map -> Commands -> Make list from selected, press add in topic AI Map Nodes and add for adding objects on the top-left (kettle) and put nods on the map in order you want (one is enough), press Generate Full. That is all, AI net generated. Must be done: add 1 graph point (Spawns->ai->graph point), you can leave it empty. Save the level, build (build) it. Do not forget to compile the map, (also do not forget to create X: disk with help of the attached program).
  • Windows -> start -> run
  • Write in command line (mp_atp_test is a map name here): xr_lc.bat mp_atp_test

Editing configs

; --------------------------------------------------------------------------
; game_maps_single.ltx
; --------------------------------------------------------------------------

[level_maps_single]
...
testing_ai ; our level name

[testing_ai]
global_rect = -6,1220.0, 248.0,1481.0
texture     = map\map_escape                    ; texture at launch
bound_rect  = -502.148,-412.284,379.976,474,479
weathers    = default                           ; weather

; --------------------------------------------------------------------------
; game_levels.ltx
; --------------------------------------------------------------------------

[levels]
...
level190 ; our level number

[level190]
name    = testing_ai
caption = "testing_ai"
offset  = 2000.0, 800.0, 1000.0 ; our level situated on a global map, X, Y, Z
id      = 193                   ; identification number, must be unique

; --------------------------------------------------------------------------
; game_graphs.ltx
; --------------------------------------------------------------------------

018 = "testing_ai"
Now open AI compiler, specify folder with sdk (.../level_editor, or path to it if it is united with the game). Choose your level:
  1. Pass 1st Stage for draft calculation of AI of the map
  2. or you can choose step 2 if you want to wait longer and calculate hide places for NPCs
  3. building graph and cross-table of the level
  4. graph of the game
  5. file spawn eventually
After all enter gamedata\spawns\ and rename test.spawn to all.spawn.

Important moments 1

While editing config do not forget to specify the real weather there and loading image or it will crash. If you will try to launch the level by following .bat file you will have crash also:
bin\xr_3da.exe -ltx user.ltx -nointro -external -start server(level/single) client(localhost)
Proper .bat file looks like this:
@start bin\XR_3DA.exe -start server(%1/single/alife) client(localhost)
Where 1% is name of the level.

Deep learn

Next step – we will learn how to create several levels and make points of transfer between them. So you have two levels. There is actor on one of them. Lets begin from this level: create ai->level changer, then shape->sphere, enlarge it to needed dimension with help of scale, now switch to editing mode spawn element, select level changer and press commands->attach object, now click on the sphere… This is it: sphere and level changer are linked. Now lets go to bookmark properties, specify the name, i.e. to_atp_2, now we need to write waypoint for return in custom data, in case if a player will refuse to transfer from one location to another (must be). Write this:
[pt_move_if_reject]
path = atp_1_way_if_reject ; go back in case of rejection of transfer
Put the name of the destination level in Level to change, i.e. atp_2. We have to specify a point in Level Point to change where a player will appear, i.e. start_actor_01. We have finished with level changer on this map. Next. Create graph point or use empty if you have one. Write in it start_actor_01 – we need this point in order to transfer from 2nd level atp_2 to 1st level atp_1. Create one more graph point with name exit_atp_1_01, in location choose i.e. escape. In Connection -> Level name specify level that will be linked with atp_1, in our case it is atp_2, in Connection->Point Name write link point from second level, which we will create later (exit_atp_2_01). First level finished. Why do we need graph points exit_xx? exit_xxx is connection_point – in order for compilator to know what levels does it links, or in other words: first graph of the level and then all nods are counted from it.

start_actor_xx – point where actor appears after transfer.

Remember we wrote way point in custom data level changer? Now we need to create it. Select Way Points -> press kettle and put one way point near the level changer (by this we created start point of the way), now press point mode and put second one. Press create 1-link if they didn’t linked with an arrow. Now we have a way which we specified in custom data level changer, go to properties and give it the same name that we put in custom data, as you remember atp_1_way_if_reject. Now we finished the first level.

Now open the second level and go ahead… Create a way from two way points according to instructions above and give it a name atp_2_way_if_reject. Create level changer and give it a name to_atp_1, then we write in custom data the following:
[pt_move_if_reject]
path = atp_2_way_if_reject ; name of the way point
In level to change we specify atp_1 //where we move to with atp_2. In level point to change - start_actor_01 (yes, the one we created in the first level :) )

As well create on atp_2 a graph point with a name start_actor_01. And one more where we write the following:
  • Name: exit_atp_2_01
  • Location: "junkyard" (name of the location)
Connection:
  • Level name: atp_1 (first level)
  • Point name: exit_atp_1_01 (link point from first level)
Do not forget that actor should be only on one location or compiler will show a mistake. Done!... Saving. Recompiling. Start Ai Compiler and choose one by one our both levels and calculate step 1, check the net. Now create text document and write there:
@start /wait bins\ai\1.exe -g atp_1
@start /wait bins\ai\1.exe -g atp_2
Save it as .bat and launch it. As a result we have level.graph (graph and cross-table) for each level.

Create one more text document and write there:
@start /wait bins\ai\1.exe -m
Save it as .bat and launch it. Now we compiled game.graph from level.graphs. And finally create last .bat for compile of all.spawn:
bins\ai\1.exe -s
Compile it, go to gamedata\spawns\, rename existing spawn in all.spawn, and launch new game. As you see you can transfer from one level to another and back without problems.

Almost forgot – “must do” temporary: before compilation started you should replace other levels of the game except yours to another folder, otherwise there probably will be mistakes whil compiling graphs and spawn.

For compiling all single levels from original game use bat file fullgame.bat. If there will be mistakes with alife press continue. After that do procedures mentioned above, compiling of game.graph, and after that all.spawn.

Adding our level to single player. Continue. I succeeded to add my test level to the GSC maps.

To start – we need our test level, properly prepared. If you haven’t red the instructions above you will not be able to continue work. Our level must have connection point – then we need to add in it something suitable for our example, i.e. this:
  • Name: point_test_location_escape (name of connection point)
Connection:
  • Level Name: L01_Escape (the level to be connected with Point)
  • Name: esc_graph_point_0034 (connection point on escape)
explanation how to add it will be ”bellow”.

Done?

Now create one more graph on the level and simply write in its name… i.e. Name: start_actor_01. Then you must add some exclusive thing (i.e. car) on the level. You will know why later. But on the level should not be ACTOR. Save and recompile the level. Compile whole level with help of xrLC, then open AIcompiler and provide steps 1,2 and 3 for our level.

Now we need exceptional tools, in our case we took script from bardak (nickname of VERY good scripter on Russian part of GSC forum – translator’s mark), the script allows us to know at launch of any level level_vertex_id, game_vertex_id, position(coordinates of the player in current moment), direction(coordinates of direction of his “sight”), all these we will need to work with acdc few steps later. Load the game and find appropriate place for a trigger of level transfer and write its parameters on a peace of paper, we will need them later. Properly it should do one more thing: separately get into the level editor , open our level, put ectors directly on the graph start_actor_01, compile the project, pass all steps of compilation in xraicompiler(1,3,4,5,6) do not forget to make backup of game.graph in advance, start new game and write coordinates of level_vertex_id direction but it is too boring, after this you will have to get back to the original game.graph.

So you have to have all the game unpacked, aiwrapper from Bardak installed (read install instruction!!!!) and aicompiler from him. In the folder with aiwrapper create a .bat file with text:

aiwrapper -extract_spawns

or enter the same text in command line in total commander – by using this key we updated level.spawns of all single levels as they were not updated since first patch. Now learn links.sample in notepad. Nothing clear? Explanation: aiwrapper has very useful key –m2.

-m2 <file> - merge AI-graphs of the maps to global game graph including changes for links from specified file.

In other words with its help we can take any existing graph of GSC which is not reserved and give to it needed figures, i.e. make it a connection point, and then all this will be merged to a whole global game.graph. Execute aiwrapper -dump_graph graphs.txt by bat or total commander. This command will give us a list of basis picks of AI-graph to the specified file, in other words list of compiled graphs of all single-levels by GSC in file graphs.txt. Find there point esc_graph_point_0034. As you can see it is empty in terms of links which is not the same as in exit_escape_01,02. That means that we can use it. Originally developers put them for bots’ paths, but we will use it for link with our test level.

Create text file links_fixed.txt
[l01_escape]
; adding transfer for AI from Cordon to test level.
esc_graph_point_0034 = test_level_x, point_test_location_escape
test_level_x – is a name of our level. point_test_location_escape – connection point on our level. So we add these two parameters to graphpoint on escape, and these parameters transform it to connection point linked to the one on our level; save text file.

You’d better move somewhere unused levels (multyplayer also) for any case. Execute by bat or total commander:
aiwrapper -m2 links_fixed.txt
Waiting for game_graph to get compiled – there should be no mistakes.

Important moments 2

There is more simple way to get calculations of game_vertex_id. In folder UTILS (Bardak’s compiler) there is tool ggtool.pl – utility for viewing links between levels in game.graph. Copy compiled game.graph to the utility from gamedata and execute by bat of through total commander:
ggtool.pl game.graph > svyazi.txt
As a result in file svyazi.txt will be specified game_vertex_id for particular level. So you will not need to get dest_game_vertex_id with help of recompiling.

Example: l01_escape (0) --1206.29--> l02_garbage (415)

0 - game_vertex_id, 415 - dest_game_vertex_id, 1206.29 - distance (distance between points), not the one in level_changer, but between connection_points.

Now working on all.spawn

aiwrapper -s
As a result we will have compiled all.spawn of all levels we have in folder levels. This is just a beginning of work. Now we need to edit the received all.spawn and add level_changer on a level of GSC in order to get from it on our level (in our case from escape to test_level_x). Copy received all.spawn to the following place: \utils and create .bat with the following text:
acdc.pl -d all.spawn
As a result we have separated all.spawn in ltxs. We need alife_l01_escape.ltx and in the end of it put:
[unreserved number]
;i.e. 9000
section_name = level_changer
name         = exit_to_new_location_from_esc
;any_name_in_english, (not reserved only)

position =
;here coordinates of level changer placement (situation)
;remember the paper where we have coordinates and parameters?
;see the one where we have coordinates and parameters of escape
;and put them in

direction = 0,0,0
;direction of the sight of the player
;if you want you can put coordinates from the paper

game_vertex_id =
;number of peak of game graph, gives first peak of graph on this location
;from paper about escape

distance = 14.6999998092651
;"distance" between level_changer point and point of appearance
;of actor on another level (if consider the Zone as one whole), maybe and 0.

level_vertex_id =
;peak of level graph, "-1" should work, but better
;to put figures that you have on paper

object_flags = 0xffffff3e
custom_data  = «END
[pt_move_if_reject]
path = esc_way_test_if_reject
END
;custom data, where specified name of waypoint that we will create later
;on the map with help of acdc, and "custom data", as in the beginning
;of the article

story_id =
;put here unreserved ID, i.e. 8435

shapes        = shape0
shape0:type   = box
shape0:axis_x = 5.8284006118774,0,0
shape0:axis_y = 0,5.0005970001221,0
shape0:axis_z = 0,0,5.3902206420898
shape0:offset = 0,0,0
;sphere, in our case cub, here exactly our transfer will activate

restrictor_type = 3
;put here all variables for new level

dest_game_vertex_id =
;number of graph peak of new location, there the player will be tranfered
;remember "unique" object, I asked you to put?
;open "alife_lxx_unknown.ltx" and look for car, take its parameters

dest_level_vertex_id =
;peak of level graph of new level
;should work -1, but better take it from the paper you wrote
;with parameters of new level

dest_position =
;coordinates of appearance of the player
;take them from the paper, or guess, or use corrected coordinates relating
;to an object on our map, i.e. take coordinates of a tree
;with coordinates 0, 1 , 2, correct them relating to the tree position and
;put i.e. 4, 1, 2

dest_direction = 0,-1.12671363353729,0
;direction of sight of the player on new map

dest_level_name =
;name of the level, in our case it is test_level_x

dest_graph_point =
;name of spawn point of the player (of graph, in our case start_actor_01)
One more definition of the two parameters for dummies:

game_vertex_id – put as a connection point (graph_point with appropriate configs) level_vertex_id - graph_point – put as common graph point with unique number without parameters

Put in them unique ID for every level, in some cases just take these two parameters from nearest objects and correct the figures. As a rule level_vertex_id may be -1, but not for all objects, game_vertex_id = level ID.

Got tired? :D But ths is not all. We need to create waypoint, which we had put in custom data level changer. Open way_l01_escape.ltx and write something like:
[esc_way_test_if_reject]

points = p0,p1
;two waypoints

p0:name = name_test1
;waypoint name 1

p0:position = -243.491257,-19.758562,-140.328583
;its coordinates, put them near coordinates of level_changer
;but not in it, correct the coordinates as in example with the tree.

p0:game_vertex_id = 8
p0:level_vertex_id = -1
p0:links = p1(1)

p1:name = name_test2
;name of the 2 waypoints

p1:position = -255.491257,-19.758562,-140.328583
;its coordinates, all the same, but correct them relating to the position of
;waypoint 0, as in this example.

p1:game_vertex_id = 9
p1:level_vertex_id = -1
Got it? Now we need only to compile all.spawn, execute by bat or total commander:
acdc.pl -c all.ltx
Now copy received all.spawn.new to gamedata\spawns\ and rename it in all.spawn. End of tortures. Start new game and enjoy your level transfer.

After all

If you want your transfer point to appear on the map of the player, go to the level_tasks.script and to function add_lchanger_location() section like the one bellow:
-- test_level_x
local obj = sim:story_object(8435)
if obj then
    level.map_add_object_spot(obj.id, "level_changer", "exit_to_new_location_from_esc")
end
In line (obj.id, "level_changer", "exit_to_new_location_from_esc") highlited text – is name of your level_changer, number – its unique story_id on the level, i.e. on escape or your level, list of reserved story_id look in lua_help.script

Credits

author: Loxotron
updated by: Kalinin11
translated by: Gubr


AI Map Creating

Creating AI Map is needed for single player levels only. If you make multiplayer level, you can skip this article. AI Map controls the npc and creatures movement on the level. When you finished the base of your map, you placed every object you wanted to, then:

Step 1:

This is a simple map, just for presentating the AI grids. As you see, it has borders (fence). Also you can see on the right panel, the aimap button, so select it. (it's highlighted with white color)

Step 2:

After you selected it, make sure you clicked on the arrow button at the upper panel (see the image)

Step 3:

When you are done, you can click on the snaps list +/- add button, and start to add the objects to the snaps list (see the image)

Step 4:

When you select an object, it will be added to that list, and will get red color, so you will know, that you already added it. However, adding objects outside the fenced area, is not needed, since you will only spawn npcs inside the level, and of course do not forget to select the fence objects too, so npcs will not try to go through them :) (see the image)

Step 5:

When you selected all needed objects on the level, you need to place at least one ai node by hand on the map. So click on the bucket "add" icon at the upper panel. (see the image)

Step 6:

Click on "add" button again at the ai map nodes panel, and choose an ai map symbol and place it on the map. (see the image)

Step 7:

Now that you placed an AI Node, and you added the needed objects to the Snaps list, you can generate AI Map. Click on "generate full" at the AI Map panel. This can be a long process, if you made a big map, also it depends on your pc. When it is done, you can switch back to "object" mode, so the red colors disappear from the map! (see the image). You can disable ai map after it's generated, because it can be annoying to see the nodes everywhere all the time. To do that, switch back to ai map section, go to properties, and click on show ai grid, and it will turn it off. To turn it on, click on it again.

Step 8:

Now the AI Map is done, you see, it was generated properly, so npcs wont try to go through the walls.

Congratulations! Now you can build your map, and compile it. Later with Neo]['s or Bardak's ai compilers you can build your single player map :)

Extra Tips:

I am sure, you noticed, that stalkers like to go in the fire, maybe the weather is too cold for them? No it's not. The problem is, aimap generation fails at low objects, like fireplaces. To avoid these issues, try this, and it will help to make proper new level:

As you can see in this image, the ai map fails, and npcs can go to the fireplace. To avoid it, you need to put a bigger object to that place where you plan to add fire later. A simple bush, or barrel does the trick. (see the image)

Now the AI Map generation is complete again, and you see, the AI Map won't fail this time. You can simply remove the bush now, it's not needed anymore. (see the image)

Put back the fireplace, and it's done (see the image)


Creating artifact model

For the work we need:
  • X-Ray SDK 0.4
  • A set of plug-ins for export models in the SDK
  • Adobe Photoshop
  • 3ds max 8

General concept

The overall concept - the foundation, the pillars of our work, this segment depends on the entire course of the coming work, so it must be treated with the utmost responsibility. As a result of my creative suffering that's what happened:

Name: Plasma cactus
Characteristics:
- radiation: -68
- bleeding: 15
- bulletproof: -10
- shock +30
Description:
This artifact is generated by anomaly Carousel, is a half-boiled composition of unknown rare plants, one of which reportedly is mutated form of a nettle, in appearance like a cactus, why and got its name. Also you may notice little luminous skin, presumably plasma structure. Artifact is a rare and expensive.

Drawing sketch

Sketch can be done even in Photoshop or in a school notebook, it doesn't really matter.

Modeling and texturing

So, run Max and model similar object to our sketch (to achieve this result, I used modifier MeshSmooth and Nois, and to adjust the shape FreeForm Deformer):

Now create "needles" of the artifact:

Set textures for our objects:

Attach "needles" to our artefact:

Shaders

For my artifact, I used the following parameters:
  • The main piece of artifact: model / selflightl, def_shaders / def_vertex, game_material / default_object
  • Needle: model / model aref, def_shaders / def_vertex, game_material / default_object
  • Plasma sheath: model / lightplanes, def_shaders / def_vertex, game_material / default_object

Export to Actor Editor

Scale model to the size used in Stalker, then use the modifier Physique, and add bone. Then everything is simple: turn in Utilites (it was painted hammer), more / STALKER Export and push Export Skin

Settings for the Actor Editor

After the correct import of textures and geometry should turn out like this:

First shaders test, if something went wrong in 3ds max and now it is too late to correct, then disable Make Progressive, turn into a tab and click Bones the title of the bones ... Now set the following parameters:
  • Bone / Game Material: default_object
  • Mass: at the discretion of
  • Shape / Type: any proposed option, depending on the form
Now click Generate Shape / All, and export model ogf ...

Creating particle for artifact

Using Particle Editor create similar particle:

To new particle effect is now saved in parcticles.xr, so copy the file from the directory SDK into the game directory, and set the appropriate options in the configs artifact.

Conclusion

After the hard work we've created from scratch an artifact model, after adding it to the game:

Sources

  • For 3ds max 8
  • For Actor Editor
  • Texture
  • Ready MOD (artifact can be purchased from the Wolf)

Credits

Author: Alwen


Spawning anomalies

To spawn anomaly in Level Editor, select Edit mode -> Spawn Element and choose for instance mosquito bald average. To add an object choose Add tool (hot key "A"), then select the anomaly and click on the map. Next, we need to define the scope (radius) of anomaly. Return to edit mode: Edit Mode -> Shape and add the scope (Sphere). Using Scale tool set right size of anomaly.

Anomaly on level
Editing the scope

Now switch to edit mode: Spawn Element, choose our anomaly, in the Commands section click on Attach Object. After that, click on our field, the flag should be moved to center of the field. Our anomaly is almost ready, using the Move (T) tool you can put it where you want to.

Binding anomaly
Anomaly position

In Object Inspector set convenient name for anomaly (for instance: zone_mosquito_bald_average_1). Now add anomaly on the list of anomalies card. To do so, edit custom_data for each type of game, enter the following parameters, such as for deathmatch:
[deathmatch_game_anomaly_sets]
permanent = anomaly_name ; in this case: zone_mosquito_bald_average_1
Custom data


Creating particles

This article examines the basis of particles. Start Particle Editor and create a common directory to a folder, to create a new particle effect:

Name the particle and find the Append, in the window that appears, select the source - this is the basis for any particle. Now configure it. Find item Color -> Alpha, set texture transparency.

Next, you need to specify the size of the texture (Size -> Center). Now scroll the list up to the item Max particles - this limit of 50 particles and go down to the line below to step Sprite - put in front of him tick, a sub-item 4. We are still interested in the first paragraph - Texture, choose a pattern for his particle.

Also, choose a shader. Now you can press F5 to run a preview of this particle.

Further, there is a mandatory thing for our effect: push Append and choose Kill Old. This feature removes the old particle. In settings specify the time of life (Age limit). In our particle things are moving, you should also push another item - Move, this item is based on the parameters of Velocity.

Now configure the Domain - a core value of particle. By default, domain - the point (point), when you double click on the paragraph menu, where the choice will be a few basic forms: point, line, sphere, cone, cylinder, disc, etc. I chose the disc. Again Hit F5 to view.

We got that something like fire disc, which slowly rises. Frame rate indicated in paragraph Source -> Rate (greater than this value, so quickly updated our particle). Well now you can add more variety of items and look at the effect.

Copy file particles.xr to gamedata

Credits

Author: Sakharov


Creating a scene in 3dsmax

Configuration (Installing plugins & mapping drive X:)

As of now there are only two 3dsmax plugins avaible, they work for max versions from 6 up to 8 (I personally tested max 7 and max 8 sp3), unfortunately there is no max 9 plugin yet... The plugin is avaible here.

Instalation is pretty easy, You just have to unpack the archive into your 3dsmax root, which will cause "plugins" folder to be overwritten (that is ok). However, in order for plugins to work correctly You have to map drive X:, You can do that by using the .bat file that comes togheter with X-RAY SDK (start->programs->X-Ray SDK->Map X-Drive), if you have deleted it, just type this in notepad and save as .bat
subst X: /D
subst X: "YourDrive:\YourSDKdir\level_editor"
After this is done the plugins will work correctly and You will have new drive in my computer which will point to level_editor folder in x-ray sdk, you have to repeat this each time you turn your pc on.

Creating & Exporting Scene

Ok, we have our STALKER plugins installed, now we can finally start mapping, in this tutorial I won't tell how-to-model-in-3dsmax because that's not what this tutorial is about. I suppose you've read the documents that come togheter with SDK so I don't have to describe basics of how Stalker models work.

The only problem i encountered are 3dsmax Units, if anyone have idea how to set proper units to work with stalker/maya units, let me know. You can always scale your map in editor but it would be nice to have good units in max.

Anyway when You have your models done, you have to use S.T.A.L.K.E.R.Mtl instead of Standard material (to change your material to S.T.A.L.K.E.R.Mtl click on button which says "Standard" in material editor and choose S.T.A.L.K.E.R.Mtl from the list [you won't see it if You have mental ray renderer, it works only with Default scanline renderer]). In your material options there will be new additional stalker material parameters rollout with three possitions where you can choose Shader types for Engine, Compiler and Game. For description of those head over to official SDK docs.

When You have everything modelled, textured and ready for game, select the objects you want to export and head over to utilities tab and click more, select S.T.A.L.K.E.R. Export. Open S.T.A.L.K.E.R. Export rollout that appeared in utilities list and hit Export Editor Objects (You should see a list of selected objects on bottom), plugin will automaticly select import folder of the Level editor.

NOTE: All objects have to use properly set S.T.A.L.K.E.R.Mtl otherwise the exporter will crash when attempting to save .object.

Type your name (ie. mp_mymap_terrain), now if everything goes ok, all objects you had selected will be exported as one .object and you will be able to import it Level Editor without any problems (any possible export errors will appear in the console pop up of stalker plugin, if there is no any, it means You succeed:))

For compiling and running map ingame, take a look into SDK documentation.

Thanks for reading, hopes it was usefull.

Credits

Author: Buddy the Designer
Source: here


Editing Music and Sounds

S.T.A.L.K.E.R. Sound Basics

STALKER uses mono sound files (44.1khz, 16 bit, .ogg) which are separated into two different channels, left and right. However, the separated channel rule only applies to in game music. (Main Menu music, bar music, etc.) Weapons, weather, and everything else only uses one file.

S.T.A.L.K.E.R. Music

Tools required:
  • a program to convert music (Audacity is recommended)
  • MP SDK
First, get a program that can convert your music from its current form to .wav, mono form, 16 bit. Place it in X:\Program Files\X-Ray SDK\level_editor\rawdata\sounds (X for your drive letter). Now launch the level editor using the LevelEditor.bat file and go to the sounds menu, sound editor. You may need to press refresh environment library for your sound to show up. The names are self explanatory, but for game music, select World Ambient. Set the quality to 1.00, make sure the volume is 1.00 (you can adjust to make it higher or lower though) and press OK. It may take a moment for the editor to do its thing, so don't worry if your screen freezes. It will eventually come back, at that point you can properly close the editor.

Your finished piece of sound is now located in level_editor\gamedata\sounds. An extra step is needed if the piece of sound you just made is game music though. Add an _l to the end of the name of your song, so let's say song.ogg, now it's song_l.ogg. Now copy it, and make one called song_r.ogg. Now place them in your STALKER gamedata\sounds\music folder.

Script and LTX Editing

Now we have the music done, but we need to tell the game to use it. If you just made ambient music for a level, all you'll need to do is edit game_levels_music.ltx, located in your gamedata\config folder. However, if you want the music to appear at the bar or over loud speakers, you'll need to edit a script file called sound_theme.script, located in gamedata\scripts.

To make your sound appear as a level ambient, just add the name of your music under the other musics in the specified level (without the _l or _r, so you would add just song to the list if your music name was song)

Example Code:

The numbers shown are the coordinates. The ones highlighted put the music in the center of the level, which can be heard within the range you specified in the level editor.

For script editing, just look around the file and decipher what does what, most of the time the code refers to self-explanatory situations.

Example Code:

In this example I placed two music files, song_l.ogg and song_r.ogg into gamedata\sounds\characters_voice\scenario\bar\radio_music, then referred to it as song in the script. Now, whenever I am in the bar there is a chance that my new music will be randomly selected to be played.

Credits

Source: here


..:: Index ::..

  • All Spawn
    • Common data
    • Stalkers and mutants
    • Space restrictors
    • Anomalies
    • Projectors
    • Lamps
    • Physical objects and destroyable objects
    • Respawners
    • Smart terrains
    • Level changers
  • AI schemes
    • Patrols and waypoints
    • Smart terrains
    • Scheme overview
      • General informations
      • Scheme execution
      • Scheme sections switching
      • An example of fairly complex logic
      • Data types
      • Scheme overrides
      • Custom ai schemes
        • Physical object scheme
        • Stalker scheme
        • Stalker salute scheme
    • NPC schemes
      • walker
      • sleeper
      • kamp
      • camper
      • follower
      • zoneguard
      • patrol
      • wounded
      • heli_hunter
      • meet
      • on_death
      • on_hit
      • actor_dialogs
      • on_use
      • dont_spawn_loot
      • dont_spawn_character_supplies
      • spawn
      • game_info
      • known_info
      • info
      • no_smart
      • show_spot
      • spawner
      • danger
      • treshold
      • on_combat
      • combat_ignore
      • Overrides
        • meet_enabled
        • meet_talk_enabled
        • meet_dialog
        • meet_state
        • wounded_enabled
        • heli_hunter
        • combat_ignore_cond
        • combat_ignore_keep_when_attacked
        • combat_type
        • on_combat
        • companion_enabled
    • Mutants schemes
      • mob_camp
      • mob_death and hit
      • mob_eluder
      • mob_fake_death
      • mob_home
      • mob_jump
      • mob_kicker
      • mob_panic
      • mob_remark
      • mob_trade
      • mob_treader
      • mob_walker
      • spawner
      • Overrides
        • actor_friendly
        • npc_friendly
        • friendly
        • braindead
    • Space restrictors schemes
      • sr_aes_deadzone
      • sr_cutscene
      • sr_idle
      • sr_light
      • sr_mapspot
      • sr_no_weapon
      • sr_particle
      • sr_postprocess
      • sr_psy_antenna
      • sr_sleep
      • sr_sound
      • sr_sound_act
      • sr_teleport
      • sr_territory
      • sr_timer
      • sr_tip
    • Physic objects schemes
      • Implementing custom schemes
      • ph_button
      • ph_camera
      • ph_car
      • ph_code
      • ph_computer
      • ph_door
      • ph_force
      • ph_gate
      • ph_heavy
      • ph_hit
      • ph_idle
      • ph_impulse
      • ph_light_mgr
      • ph_on_death
      • on_hit
      • ph_oscillate
      • ph_projector
      • ph_radio
      • ph_sound
    • Helicopter schemes
      • heli_move
    • xr_logic
      • initialize_obj
      • configure_schemes
      • enable_generic_schemes
      • disable_generic_schemes
      • get_customdata_or_ini_file
      • determine_section_to_activate
      • activate_by_section
      • reset_generic_schemes_on_scheme_switch
      • assign_storage_and_bind
      • subscribe_action_for_events
      • unsubscribe_action_from_events
      • issue_event
      • is_active
      • switch_to_section
      • cfg_get_number_and_condlist
      • cfg_get_string_and_condlist
      • cfg_get_two_strings_and_condlist
      • cfg_get_npc_and_zone
      • cfg_get_condlist
      • parse_func_params
      • parse_infop
      • parse_condlist
      • pick_section_from_condlist
      • cfg_get_switch_conditions
      • try_switch_to_another_section
      • mob_capture
      • mob_captured
      • mob_release
      • pstor_save
      • pstor_retrieve
      • pstor_save_all
      • pstor_load_all
      • save_logic
      • load_logic
      • save_obj
      • load_obj
      • generic_scheme_overrides
      • cfg_get_overrides
  • Configuration files
    • Artefacts activation
    • Artefacts on belt
    • Editing artefacts
    • Editing stashes
    • Editing traders
    • Editing weapons
    • Creating factions
  • Scripting
    • Anomaly fields
    • Crow binder
    • NPC on PDA's map
    • Custom scripted postprocess
    • Custom UI Windows
    • Adding diaries to pda
    • Inheritance
    • Reading LTX file through script
    • Scripted timer
    • Items removal
    • Object binders
    • Dialogs
      • Actor dialogs
      • Start dialogs
      • Static dialogs
      • Dynamic dialogs
    • Editing tasks
      • Side tasks
      • Storyline tasks
    • Useful snippets
  • SDK
    • AI map compiling
    • AI map creating
    • Creating artefact
    • Spawning anomalies
    • Creating particles
    • Creating a scene in 3dsmax
    • Editing Music and Sounds
  • Developer's diaries
    • Shadow of Chernobyl: Legacy of Chernobyl
    • Shadow of Chernobyl: Creating the Zone
    • Shadow of Chernobyl: What Lies Within
    • Shadow of Chernobyl: Factions
    • Shadow of Chernobyl: Last Man Standing
    • Shadow of Chernobyl: Mutants
    • Clear Sky: Developer Diary I
    • Clear Sky: Developer Diary II
    • Clear Sky: Developer Diary III


on_hit

Scheme is used for setting infoportions and calling functions when monster dies.
  • related files:
    • all.spawn
    • modules.script
    • xr_hit.script
[logic]
on_hit = hit

[hit]
on_info = %+info -info =func()%


ph_death

Scheme for tracking destruction of physical objects. This scheme is deprecated, use ph_on_death instead
  • related files:
    • modules.script
    • ph_death.script


gulag_tasks

loadJob(sj, gname, type, squad, groups, gulag)

purpose:
function loads gulags jobs for all levels using

load_job()

function from each

gulag_*.script

arguments:

sj <array>

- array with gulag jobs

gname <string>

- gulag name

type <string>

- gulag type

squad <number>

- smart terrain squad number

groups <array>

- table with groups

gulag <pointer>

- pointer to gulag class
remarks:
table

sj

is filled with gulags jobs

loadStates(gname, type)

purpose:
function returns state function for given smart terrain (

load_states()

function query each

gulag_*.script

)
arguments:

gname <string>

- gulag name

type <string>

- gulag type

loadLtx(name, type)

purpose:
function returns dunamic ltx for given gulag (

load_ltx()

function query each

gulag_*.script

)
arguments:

name <string>

- gulag name

type <string>

- gulag type


sr_danger

This scheme is designed to create danger zones. If enemy is inside danger zone, then it will not be attacked.
  • related files:
    • all.spawn
    • modules.script
    • sr_danger.script
    • xr_danger.script
Example:
[logic]
active = sr_danger

[sr_danger]
on_info = {+val_sacrifice_victim_dead} nil


lua_help

_g.has_alife_info(info_id)

purpose:
checks whether actor has infoportion
arguments:

  • info_id <string>

    - infoportion id
returns:

  • true

    - actor has infoportion

  • false

    - otherwise


Registering custom class

It is possible to register custom class for certain object. Requirements:
  • client class (lua_help.script), which is used to create an object - for instance:

    CGraviArtefact

  • server class (lua_help.script), which is used to control object when it's in offline state - for instance:

    cse_alife_item_artefact

To create a custom class we need to define new class which inherits from server class, for instance we create an artefact which is visible only when actor is 5 (or less) meters away from artefact:
-- se_my_artefact.script
class "se_my_artefact"	(cse_alife_item_artefact)

function se_my_artefact:__init(section) super(section)
end

function se_my_artefact:can_switch_offline()
  return cse_alife_item_artefact.can_switch_offline(self)
end

function se_my_artefact:get_level_name(gvid)
  return alife():level_name(game_graph():vertex(gvid):level_id())
end

function se_my_artefact:can_switch_online()
  if not db.actor then
    return self.online
  end
  if self.parent_id ~= 65535 then
    return true
  end
  if get_level_name(self.m_game_vertex_id) == get_level_name(db.actor:game_vertex_id()) then
    return level.vertex_position(self.m_level_vertex_id):distance_to(db.actor:position()) <= 5
  end
  return cse_alife_item_artefact.can_switch_online(self)
end
Then we add our artefact's definition:
[af_medusa]:af_base
class = MY_CUSTOM_ARTEFACT_CLASS
...
Finally, we have to register new class (

MY_CUSTOM_ARTEFACT_CLASS

)
function register(object_factory)
  ...
  cs_register(object_factory, "CGraviArtefact", "se_my_artefact.se_my_artefact", "MY_CUSTOM_ARTEFACT_CLASS", "script_custom_af")
  ...
end