Left 4 Dead 2/Scripting/Response System
Remember to check for any notes left by the tagger at this article's talk page.
The response system used by Left 4 Dead 2 can be extended through VScript by creating custom decision rules. These rules allow scripts to evaluate criteria and select responses dynamically based on game state, similar to the built-in response rules used by the engine.
VScript response rules use the same general concept as the traditional response rule system, but instead of being defined through KeyValues files, rules are created using Squirrel tables.
A response rule consists of three main parts:
- name - An arbitrary rule name, mainly used for debugging.
- criteria - Conditions that determine whether a rule matches.
- responses - Actions performed when a rule is selected.
Response system structure
A response rule is a Squirrel table containing a name, criteria, and responses:
{
name = "ExampleRule",
criteria =
[
[ "concept", "TLK_REMARK" ],
[ "who", "Coach" ],
[ "subject", "ExampleSubject" ],
[ "distance", 0, 500 ]
],
responses =
[
{
scenename = "scenes/Coach/MoveOn01.vcd"
}
]
}
Multiple rules can be stored in an array:
[
{
name = "Rule1",
criteria = [],
responses = []
},
{
name = "Rule2",
criteria = [],
responses = []
}
]
Criteria
Criteria are used to determine if a rule is valid. Every criterion in a rule must match before the response can be selected.
Criteria can be either static comparisons or functions.
Static criteria
Static criteria compare a fact from the response query against a value.
A two-value criterion checks if a fact equals a value:
[ "concept", "TLK_HELP_ME" ]
This matches when the query contains:
concept = "TLK_HELP_ME"
A three-value criterion checks whether a number is within a range:
[ "distance", 0, 500 ]
This matches values between 0 and 500.
A missing value can be used as an unlimited range:
[ "distance", 500, null ]
Matches values greater than or equal to 500.
Function criteria
For information about what the query contains, see Query facts ↓
Criteria can also be functions that return true or false.
g_rr..
Example:
function IsNotInCombat(query) // query param is IMPORTANT!!! DO NOT REMOVE!!!
{
return query.incombat == 0;
}
// In the rules
criteria =
[
[ IsNotInCombat ]
]
Example 2:
function SomeFunction() {
//...
}
function SomeFunctionABC(query) {
//...
}
MyGlobalVariable <- "ABC";
function IsNotInCombat(query) // query param is IMPORTANT!!! DO NOT REMOVE!!!
{
g_rr.SomeFunction();
g_rr.SomeFunctionABC(query);
print(g_rr.MyGlobalVariable);
return query.incombat == 0;
}
// In the rules
criteria =
[
[ IsNotInCombat ]
]
Functions allow rules to perform custom checks that cannot be represented by simple comparisons.
Another example is using the param query to see if a survior is alive
function IsGamblerAlive(query) // query param is IMPORTANT!!! DO NOT REMOVE!!!
{
return query.isgambleralive == 1;
}
criteria =
[
[ IsGamblerAlive ]
]
Common criteria
concept
The concept represents the type of response being requested.
Examples:
[ "concept", "TLK_HELLO" ]
[ "concept", "TLK_HELP_ME" ]
[ "concept", "TLK_RELOAD" ]
who
who is not an entity lookup. It is a response query fact containing the speaker identifier supplied by the caller.
Built-in survivor responses commonly use internal survivor names:
- Coach
- Gambler (Nick)
- Mechanic (Ellis)
- Producer (Rochelle)
- Biker (Francis)
- Manager (Louis)
- NamVet (Bill)
- TeenGirl (Zoey)
However, custom scripts may provide any string value.
Example:
[ "who", "Virgil" ]
subject
The subject usually refers to the entity the response is directed toward.
Example:
[ "subject", "PlayerRemarkC1M1ThroughThisDoorCoach" ]
Responses
Responses define what happens when a rule matches.
A response can execute a scene, sound, or script function.
Scene responses
A response containing scenename plays a VCD scene:
responses =
[
{ scenename = "scenes/Coach/MoveOn01.vcd" }
]
Script responses
A response can execute a VScript function:
function ExampleResponse(speaker, query)
{
printl("Response executed")
}
responses =
[
{ func = ExampleResponse }
]
The function receives:
speaker- the entity performing the response.query- the response query table.
Response parameters
Responses can contain additional parameters controlling behavior.
Example:
{
scenename = "scenes/Coach/MoveOn01.vcd",
delay = 2,
odds = 50
}
Common parameters:
| Parameter | Description |
|---|---|
| nodelay | Removes the additional speaking delay. |
| defaultdelay | Adds the default response delay. |
| odds | Chance that the response is selected. |
| speakonce | Prevents the response from being used more than once. |
| weight | Changes selection priority between responses. |
| respeakdelay | Prevents the same response from playing again for a period of time. |
| applycontext | |
| applycontexttoworld |
Group parameters
Response groups control how responses are selected and repeated.
group_params = RGroupParams(
{
permitrepeats = false,
sequential = true,
norepeat = false
})
Ususally it can just be group_params = RGroupParams().
Available options:
permitrepeats- Allows responses to repeat before all responses have been used.sequential- Selects responses in order instead of randomly.norepeat- Disables the response group after all responses have been used.matchonce- Disables the rule after it matches once.
Creating a response system
Criteria examples
criteria = [
[ "concept", "onSeeEnemy" ], // arrays of two entries are considered to be fact = value
[ "speaker", "coach" ],
[ "numAllies", 1, 4 ], // arrays of three entries are considered to be fact >= x && fact <= y
[ "enemyType", "smoker" ],
[ @(query) (query.GameTime) < 30 ] // arrays of one entry are expected to be functions (In this entry, its an anonymous function)
],
g_rr is needed in the reponse rules when declaring criteria rules as a variable.
// if you want to share a criterion between many rules, you can declare it as its own variable like this:
CriterionIsNotCoughing <- [ "Coughing", 0 ]
CriterionIsc6m3_port <- [ "map", "c6m3_port" ] //use for the moveon example below to scope the rule change
CriterionIsAwardProtector <- [ "awardname","Protector" ]
// and then include it into the criteria lists as per the examples below. The "g_rr." part
// is a bit of temporary cruft that will go away once we resolve some questions about scope resolution
// in map .nut files.
criteria = [
[ "concept", "PlayerMoveOn" ], // conceptPlayerMoveOn
g_rr.CriterionIsNotCoughing,
[ "Who", "Coach" ], // isCoach
g_rr.CriterionIsc6m3_port
],
Response examples
onFinish work? Might not be implemented as said here: onFinish = eventually, a script function to call when the scene finishes (currently not implemented).responses = [
{ scenename = "coach_see_smoker_1.vcd", // if a 'scenename' key is present, this is expected to be a 'scene' response
soundlevel = 80,
onFinish = @(query, speaker) speaker.smokersSeen += 1 // expected to be a function
},
{
func = ZombieFreakout // if a 'func' key is present, this is expected to be a 'do function' response
},
{
func = @(query,speaker) speaker.PointAt( query.enemy ) // anonymous functions are ok too
},
{
scenename = "coach_see_smoker_2.vcd",
sndlevel = 90,
onFinish = SomeFunc
}
],
responses = [
{
func = SetAwardSpeech
}
],
responses = [
{ scenename = "scenes/Gambler/MoveOn01.vcd",
followup = RThen( "self", "DemonstrateScriptFollowup", {additionalcontext="whatever"}, 1.23 ) // dispatches a "DemonstrateScriptFollowup" concept back to self
func = DemoWritingContextToCharacter
}
],
RThen is a followup event like in the response system in TXT files. All followups need to expose a function and a 'delay' parameter.
The callable gets the following parameters: ( _target, _concept, _contexts, _delay )
RThen(
"coach", // target
"TLK_FOLLOWUP_WHATEVER", // concept
{ foo = 1, bar = "flarb" }, // contexts to add to the followup (may be null instead)
1.5 // delay
)
Query facts
The following table lists the known query facts available when evaluating a response rule. Values shown are examples captured from a live response query.
{
[beingjockeyed] = 0 (float)
[numberinsafespot] = 4 (float)
[health] = 100 (float)
[numberofteamdead] = 0 (float)
[SPITTERsKilled] = 0 (float)
[TankActive] = 0 (float)
[CurrentTemporaryHealth] = 0 (float)
[name] = 0 (float)
[hasfirstaidkit] = 0 (float)
[SMOKERsKilled] = 0 (float)
[disttomanager] = 2456 (float)
[numberofteamalive] = 4 (float)
[worldtalk] = 0 (float)
[worldcxm1intro] = 1 (float)
[concept] = TLK_REMARK (string)
[NORMALsKilled] = 0 (float)
[WITCHsKilled] = 0 (float)
[BOOMERsKilled] = 0 (float)
[isnamvetalive] = 1 (float)
[Chapter] = 0 (float)
[isbikeralive] = 1 (float)
[JOCKEYsKilled] = 0 (float)
[disttoproducer] = 2543.9 (float)
[isteengirlalive] = 1 (float)
[gamemode] = coop (string)
[zombiepresentwitch] = 1 (float)
[ZombiePresentNormal] = 1 (float)
[world_auto_saferoomstart] = 1 (float)
[classname] = player (string)
[disttoclosestsurvivor] = 2456 (float)
[Sneaking] = 0 (float)
[movementspeed] = 94 (float)
[disttocoach] = 2456 (float)
[disttoteengirl] = 2543.9 (float)
[instartarea] = 0 (float)
[pouncevictim] = 0 (float)
[LowViolence] = 0 (float)
[disttogambler] = 0 (float)
[who] = NamVet (string)
[map] = <map name; eg. c1m1_hotel> (string)
[healthfrac] = 1 (float)
[NumberOfTeamIncapacitated] = 0 (float)
[inrescuevehicle] = 0 (float)
[onfire] = 0 (float)
[haspainpills] = 0 (float)
[is_pc] = 1 (float)
[ismanageralive] = 1 (float)
[hasgrenade] = 0 (float)
[worldsaidleavingsafearea] = 1 (float)
[StoreFrontGlass01] = 1 (float)
[team] = Survivor (string)
[speaking] = 0 (float)
[coughing] = 0 (float)
[ZombiesKilledWhileIncapacitated] = 0 (float)
[timesincecombat] = 99999.9 (float)
[is_console] = 0 (float)
[skill.cfg] = 1 (float)
[ClosestSurvivor] = Manager (string)
[IncapacitatedCount] = 0 (float)
[CurrentHealth] = 100 (float)
[hasmolotov] = 0 (float)
[IsIncapacitated] = 0 (float)
[campaignrandomnum] = 25 (float)
[disttomechanic] = 2501.1 (float)
[randomnum] = 80 (float)
[HUNTERsKilled] = 0 (float)
[TANKsKilled] = 0 (float)
[disttobiker] = 2501.1 (float)
[CHARGERsKilled] = 0 (float)
[intensity] = 0.01 (float)
[timesincegroupincombat] = 99999.9 (float)
[hangingfromtongue] = 0 (float)
[insafespot] = 0 (float)
[disttonamvet] = 0 (float)
[incombatmusic] = 0 (float)
[hangingfromledge] = 0 (float)
[incheckpoint] = 0 (float)
[inbattlefield] = 0 (float)
[incapacitated] = 0 (float)
[activeweapon] = SecondPistol (string)
[distance] = 221.494 (float)
[incombat] = 0 (float)
[beinghealed] = 0 (float)
[campaign] = unknown (string)
[numberoutsidesafespot] = 0 (float)
[onthirdstrike] = 0 (float)
[introactor] = Zoey (string)
[subject] = OurSubject (string)
[TimeAveragedIntensity] = 0.01 (float)
} |
{
[beingjockeyed] = 0 (float)
[numberinsafespot] = 0 (float)
[incombat] = 0 (float)
[numberofteamdead] = 0 (float)
[SPITTERsKilled] = 0 (float)
[TankActive] = 1 (float)
[CurrentTemporaryHealth] = 0 (float)
[name] = 0 (float)
[hasfirstaidkit] = 0 (float)
[CHARGERsKilled] = 0 (float)
[numberofteamalive] = 4 (float)
[worldtalk] = 0 (float)
[concept] = TLK_REMARK (string)
[NORMALsKilled] = 0 (float)
[worldsrcgrp_ellisstoryinit] = 1 (float)
[WITCHsKilled] = 0 (float)
[BOOMERsKilled] = 0 (float)
[incapacitated] = 0 (float)
[Chapter] = 0 (float)
[TANKsKilled] = 0 (float)
[JOCKEYsKilled] = 0 (float)
[disttoproducer] = 3958.5 (float)
[gamemode] = coop (string)
[StoreFrontGlass01] = 1 (float)
[ZombiePresentNormal] = 1 (float)
[world_auto_saferoomstart] = 1 (float)
[classname] = player (string)
[disttoclosestsurvivor] = 3958.5 (float)
[Sneaking] = 0 (float)
[movementspeed] = 220 (float)
[disttocoach] = 4017.4 (float)
[isgambleralive] = 1 (float)
[instartarea] = 0 (float)
[pouncevictim] = 0 (float)
[LowViolence] = 0 (float)
[subject] = OurSubject (string)
[who] = Gambler (string)
[map] = <map name; eg. c1m1_hotel> (string)
[healthfrac] = 1 (float)
[health] = 100 (float)
[NumberOfTeamIncapacitated] = 0 (float)
[inrescuevehicle] = 0 (float)
[onfire] = 0 (float)
[haspainpills] = 1 (float)
[is_pc] = 1 (float)
[zombiepresentwitch] = 1 (float)
[hasgrenade] = 0 (float)
[worldsaidleavingsafearea] = 1 (float)
[zombiepresenttank] = 1 (float)
[team] = Survivor (string)
[speaking] = 0 (float)
[coughing] = 0 (float)
[ZombiesKilledWhileIncapacitated] = 0 (float)
[timesincecombat] = 99999.9 (float)
[isproduceralive] = 1 (float)
[is_console] = 0 (float)
[skill.cfg] = 1 (float)
[ClosestSurvivor] = Producer (string)
[CurrentHealth] = 100 (float)
[hasmolotov] = 0 (float)
[IncapacitatedCount] = 0 (float)
[campaignrandomnum] = 82 (float)
[disttomechanic] = 4130.4 (float)
[randomnum] = 27 (float)
[HUNTERsKilled] = 0 (float)
[IsIncapacitated] = 0 (float)
[timesincegroupincombat] = 99999.9 (float)
[world_auto_storywait] = 1 (float)
[intensity] = 0 (float)
[inbattlefield] = 0 (float)
[hangingfromtongue] = 0 (float)
[numberoutsidesafespot] = 4 (float)
[distance] = 323.772 (float)
[incombatmusic] = 0 (float)
[hangingfromledge] = 0 (float)
[incheckpoint] = 0 (float)
[insafespot] = 0 (float)
[disttogambler] = 0 (float)
[activeweapon] = SecondPistol (string)
[world_auto_storygate] = 1 (float)
[ismechanicalive] = 1 (float)
[beinghealed] = 0 (float)
[campaign] = unknown (string)
[iscoachalive] = 1 (float)
[onthirdstrike] = 0 (float)
[introactor] = Francis (string)
[SMOKERsKilled] = 0 (float)
[TimeAveragedIntensity] = 0 (float)
} |
General
| Fact | Type | Example | Description |
|---|---|---|---|
concept |
String | TLK_REMARK |
Response concept. |
who |
String | NamVet |
Speaker identifier. |
subject |
String | OurSubject |
Subject supplied by the caller. |
name |
Float | 0 |
|
classname |
String | player |
|
team |
String | Survivor |
Campaign
| Fact | Type | Example | Description |
|---|---|---|---|
map |
String | c1m1_hotel |
Current map. |
campaign |
String | unknown |
|
gamemode |
String | coop |
|
Chapter |
Float | 0 |
|
LowViolence |
Float | 0 |
|
skill.cfg |
Float | 1 |
Health and status
| Fact | Type | Example | Description |
|---|---|---|---|
health |
Float | 100 |
|
CurrentHealth |
Float | 100 |
|
healthfrac |
Float | 1 |
|
CurrentTemporaryHealth |
Float | 0 |
|
IsIncapacitated |
Float | 0 |
|
incapacitated |
Float | 0 |
|
IncapacitatedCount |
Float | 0 |
|
NumberOfTeamIncapacitated |
Float | 0 |
|
onthirdstrike |
Float | 0 |
|
onfire |
Float | 0 |
|
beinghealed |
Float | 0 |
|
coughing |
Float | 0 |
|
Sneaking |
Float | 0 |
Combat
| Fact | Type | Example | Description |
|---|---|---|---|
incombat |
Float | 0 |
|
incombatmusic |
Float | 0 |
|
inbattlefield |
Float | 0 |
|
timesincecombat |
Float | 99999.9 |
|
timesincegroupincombat |
Float | 99999.9 |
|
intensity |
Float | 0.01 |
|
TimeAveragedIntensity |
Float | 0.01 |
Special infected interactions
| Fact | Type | Example | Description |
|---|---|---|---|
beingjockeyed |
Float | 0 |
Is the current survivor being jockeyed? |
pouncevictim |
Float | 0 |
Is the current survivor being pounce by the hunter? |
hangingfromtongue |
Float | 0 |
Is the current survivor being hanged by the Smoker? |
hangingfromledge |
Float | 0 |
Is the current survivor hanging from a ledge? |
Inventory
| Fact | Type | Example | Description |
|---|---|---|---|
activeweapon |
String | SecondPistol |
Current weapon being held. |
hasfirstaidkit |
Float | 0 |
|
haspainpills |
Float | 0 |
|
hasgrenade |
Float | 0 |
|
hasmolotov |
Float | 0 |
Movement and location
| Fact | Type | Example | Description |
|---|---|---|---|
distance |
Float | 221.494 |
Distance to the seen entity. (eg. info_remarkable) |
movementspeed |
Float | 94 |
Current survivor speed. |
ClosestSurvivor |
String | Manager |
The closest survivor to the seen entity. |
disttoclosestsurvivor |
Float | 2456 |
The closest survivor to the seen entity. |
| Fact | Type | Example | Description |
|---|---|---|---|
disttobiker |
Float | 2501.1 |
Francis |
disttonamvet |
Float | 0 |
Bill |
disttoteengirl |
Float | 2543.9 |
Zoey |
disttomanager |
Float | 2543.9 |
Louis |
| Fact | Type | Example | Description |
|---|---|---|---|
disttocoach |
Float | 2456 |
Coach |
disttogambler |
Float | 0 |
Nick |
disttomechanic |
Float | 2501.1 |
Ellis |
disttoproducer |
Float | 2543.9 |
Rochelle |
Safe room
| Fact | Type | Example | Description |
|---|---|---|---|
insafespot |
Float | 0 |
Is the survivor in the safe area? (Mission start, check point room, etc.) |
numberinsafespot |
Float | 4 |
How many survivors are in a safe area (Mission start, check point room, etc.) |
numberoutsidesafespot |
Float | 0 |
How many survivors are outside a safe area. |
instartarea |
Float | 0 |
Is the survivor in the starting area (nav area with MISSION_START)
|
incheckpoint |
Float | 0 |
Is the survivor in the checkpoint area (nav area with CHECKPOINT)
|
inrescuevehicle |
Float | 0 |
Is the survivor in the rescue vehicle area (nav area with RESCUE_VEHICLE)
|
Director
| Fact | Type | Example | Description |
|---|---|---|---|
TankActive |
Float | 0 |
|
ZombiePresentNormal |
Float | 1 |
|
zombiepresentwitch |
Float | 1 |
|
campaignrandomnum |
Float | 25 |
|
randomnum |
Float | 80 |
Survivor state
| Fact | Type | Example | Description |
|---|---|---|---|
isbikeralive |
Float | 1 |
|
ismanageralive |
Float | 1 |
|
isnamvetalive |
Float | 1 |
|
isteengirlalive |
Float | 1 |
| Fact | Type | Example | Description |
|---|---|---|---|
iscoachalive |
Float | 1 |
|
isgambleralive |
Float | 1 |
|
ismechanicalive |
Float | 1 |
|
isproduceralive |
Float | 1 |
Statistics
| Fact | Type | Example | Description |
|---|---|---|---|
BOOMERsKilled |
Float | 0 |
|
CHARGERsKilled |
Float | 0 |
|
HUNTERsKilled |
Float | 0 |
|
JOCKEYsKilled |
Float | 0 |
|
NORMALsKilled |
Float | 0 |
|
SMOKERsKilled |
Float | 0 |
|
SPITTERsKilled |
Float | 0 |
|
TANKsKilled |
Float | 0 |
|
WITCHsKilled |
Float | 0 |
|
ZombiesKilledWhileIncapacitated |
Float | 0 |
World contexts
| Fact | Type | Example | Description |
|---|---|---|---|
worldtalk |
Float | 0 |
|
worldcxm1intro |
Float | 1 |
|
world_auto_saferoomstart |
Float | 1 |
|
worldsaidleavingsafearea |
Float | 1 |
|
StoreFrontGlass01 |
Float | 1 |
Platform
| Fact | Type | Example | Description |
|---|---|---|---|
is_pc |
Float | 1 |
|
is_console |
Float | 0 |
Examples
// Somewhere in another file
::MyCampaignState <- {
PathA <- false;
PathB <- false;
}
function PickPathA() {
::MyCampaignState.PathA = true;
::MyCampaignState.PathB = false;
}
function PickPathB() {
::MyCampaignState.PathA = false;
::MyCampaignState.PathB = true;
}
// response_testbed_addon.nut
function IsPathA(query) { // query is IMPORTANT!!! DO NOT REMOVE!!!
return ::MyCampaignState.PathA;
}
function IsPathB(query) { // query is IMPORTANT!!! DO NOT REMOVE!!!
return ::MyCampaignState.PathB;
}
local MyCustomResponses = [
//----------------------------------------
// SUBJECT: PathA
//----------------------------------------
{
name = "PathA_NamVet", // Doesnt matter what the name is, as its used for debugging.
criteria = [
["concept", "TLK_REMARK"],
g_rr.IsNamVet,
["subject", "PathA"], // contextsubject
["distance", 0, 500], // min, max
[ IsPathA ] // returns true to say a line or false to stop saying a line
],
responses = [
{ scenename = "scenes/NamVet/WorldFarmHouse0204.vcd" }, //Through here!
{ scenename = "scenes/NamVet/WorldHospital0327.vcd" }, //Through here!
{ scenename = "scenes/NamVet/FollowMe01.vcd" }, //Come on, this way.
{ scenename = "scenes/NamVet/FollowMe04.vcd" }, //This way, people!
{ scenename = "scenes/NamVet/FollowMe05.vcd" } //This way.
],
group_params = RGroupParams()
},
//----------------------------------------
// SUBJECT: PathA_Blocked
//----------------------------------------
{
name = "AlleyABlocked_NamVet",
criteria = [
["concept", "TLK_REMARK"],
g_rr.IsNamVet,
["subject", "PathA_Blocked"], // contextsubject
["distance", 0, 500], // min, max
[ IsPathB ] // returns true to say a line or false to stop saying a line
],
responses = [
{ scenename = "scenes/NamVet/WorldHospital0209.vcd" }, //Gotta find another way.
{ scenename = "scenes/NamVet/WorldHospital0210.vcd" }, //This seemed too easy.
{ scenename = "scenes/NamVet/WorldHospital0211.vcd" } //Going to have to go top side.
],
group_params = RGroupParams()
},
]
rr_ProcessRules(MyCustomResponses); // This is where it creates the response rules for a map / campaign
g_ignoredecisionrules <- [
{
name = "CoachSeeSmoker",
criteria = [
[ "concept", "onSeeEnemy" ], // arrays of two entries are considered to be fact = value
[ "speaker", "coach" ],
[ "numAllies", 1, 4 ], // arrays of three entries are considered to be fact >= x && fact <= y
[ "enemyType", "smoker" ],
[ @(query) (query.GameTime) < 30 ] // arrays of one entry are expected to be functions
],
responses = [
{ scenename = "coach_see_smoker_1.vcd", // if a 'scenename' key is present, this is expected to be a 'scene' response
soundlevel = 80,
onFinish = @(query, speaker) speaker.smokersSeen += 1 // expected to be a function
} , {
func = ZombieFreakout // if a 'func' key is present, this is expected to be a 'do function' response
} , {
func = @(query,speaker) speaker.PointAt( query.enemy ) // anonymous functions are ok too
} , {
scenename = "coach_see_smoker_2.vcd",
sndlevel = 90,
onFinish = @(speaker, query) speaker.smokersSeen += 1
}
],
group_params = RGroupParams({ permitrepeats = false, sequential = true, norepeat = false })
},
{ // another rule to test that I don't inadvertently write state shared between rules
name = "Dummy",
criteria = [
[ "concept", "dummy" ], // arrays of two entries are considered to be fact = value
[ "speaker", "zombie" ]
],
responses = [
{ scenename = "zombie.vcd", // if a 'scenename' key is present, this is expected to be a 'scene' response
sndlevel = 80,
onFinish = @(speaker, query) speaker.smokersSeen += 1 // expected to be a function
}
],
group_params = RGroupParams( ) // default
}
]
// if you want to share a criterion between many rules, you can declare it as its own variable like this:
CriterionIsNotCoughing <- [ "Coughing", 0 ]
CriterionIsc6m3_port <- [ "map", "c6m3_port" ] //use for the moveon example below to scope the rule change
CriterionIsAwardProtector <- [ "awardname","Protector" ]
// and then include it into the criteria lists as per the examples below. The "g_rr." part
// is a bit of temporary cruft that will go away once we resolve some questions about scope resolution
// in map .nut files.
// here's a demo of a function that reads a context out of a character, and sets it again. (you can also read context out of the query.)
function DemoWritingContextToCharacter( speaker, query )
{
// look up a 'bananas' context in the query, add one to it, and write it to the character. write 1 if query has no bananas.
if ( "bananas" in query )
{
speaker.SetContext( "bananas", (query["bananas"] + 1).tostring(), 0 )
}
else
{
speaker.SetContext( "bananas", "1", 0 )
}
}
function SetAwardSpeech (speaker, query)
{
local ProtectedDude = rr_GetResponseTargets()[ query.subject ]
ProtectedDude.SetContext( "AwardSpeech", query.who, 10 )
}
function SubjectAward ( query )
{
if ( "AwardSpeech" in query )
{
if ( query["AwardSpeech"] == query["subject"] )
{
return true
}
else
{
return false
}
}
else
{
return false
}
}
g_decisionrules <- [
{
name = "PlayerMoveOnCoach",
criteria = [
[ "concept", "PlayerMoveOn" ], // conceptPlayerMoveOn
g_rr.CriterionIsNotCoughing,
[ "Who", "Coach" ], // isCoach
g_rr.CriterionIsc6m3_port
],
responses = [
{ scenename = "scenes/Coach/MoveOn01.vcd",
}
],
group_params = RGroupParams({ permitrepeats = true, sequential = false, norepeat = false })
},
{
name = "PlayerMoveOnGambler",
criteria = [
[ "concept", "PlayerMoveOn" ], // conceptPlayerMoveOn
g_rr.CriterionIsNotCoughing,
[ "Who", "Gambler" ], // isgambler
g_rr.CriterionIsc6m3_port
],
responses = [
{ scenename = "scenes/Gambler/MoveOn01.vcd",
followup = RThen( "self", "DemonstrateScriptFollowup", {additionalcontext="whatever"}, 1.23 ) // dispatches a "DemonstrateScriptFollowup" concept back to self
func = DemoWritingContextToCharacter
}
//,{ scenename = "scenes/Gambler/MoveOn02.vcd", // another vcd response
//}
],
group_params = RGroupParams({ permitrepeats = true, sequential = false, norepeat = false })
},
{ // this rule demonstrates how a followup can be an arbitrary script fucntion
name = "TestBogusGambler",
criteria = [
[ "concept", "DemonstrateScriptFollowup" ],
[ "Who", "Gambler" ],
g_rr.CriterionIsc6m3_port
],
responses = [
{ func = g_rr.DemoScriptFollowupFunction }
],
group_params = RGroupParams({ permitrepeats = true, sequential = false, norepeat = false })
},
{
name = "PlayerMoveOnMechanic",
criteria = [
[ "concept", "PlayerMoveOn" ], // conceptPlayerMoveOn
g_rr.CriterionIsNotCoughing,
[ "Who", "Mechanic" ], // isMechanic
g_rr.CriterionIsc6m3_port
],
responses = [
{ scenename = "scenes/Mechanic/MoveOn01.vcd"
}
],
group_params = RGroupParams({ permitrepeats = true, sequential = false, norepeat = false })
},
{
name = "PlayerMoveOnProducer",
criteria = [
[ "concept", "PlayerMoveOn" ], // conceptPlayerMoveOn
g_rr.CriterionIsNotCoughing,
[ "Who", "Producer" ], // isgambler
g_rr.CriterionIsc6m3_port
],
responses = [
{ scenename = "scenes/Producer/MoveOn01.vcd"
}
],
group_params = RGroupParams({ permitrepeats = true, sequential = false, norepeat = false })
},
{
name = "PlayerAwardProtect",
criteria = [
[ "concept", "Award" ], // conceptAward
g_rr.CriterionIsAwardProtector
],
responses = [
{
func = SetAwardSpeech
}
],
group_params = RGroupParams({ permitrepeats = true, sequential = false, norepeat = false })
},
{
name = "ProtectedFriendlyFire",
criteria = [
[ "concept", "PlayerFriendlyFire" ],
[ SubjectAward ]
],
responses = [
{
func = DemoScriptFollowupFunction
}
],
group_params = RGroupParams({ permitrepeats = true, sequential = false, norepeat = false })
}
]
|