XML:BINA/OBJC/TRGV
TRGV : Trigger Volumes (TVs) | ||
---|---|---|
XML
AKEV << Other file types >> CONS TMBD << Other BINA >> ONIE TRIG << Other OBJC >> SNDG |
general information
- BINACJBOTrigger Volume.oni is level specific. (It can be found in AE/AEInstaller/vanilla/levelX_Final.dat)
- The XML code on this page is based on onisplit v0.9.61.0
BSL support
show trigger activity
- debug_triggers = 1 shows tv event and what character enters, stays inside or leaves
Resetting the trigger volume
- trigvolume_reset (string tv_name) resets the TV to its preset state. The primary use of this is to re-enable "entry", "inside" or "exit" calls once the TV has been triggered (only necessary if the respective trigger-only-once flags are set of course). Note that some or all the TV functions are disabled at level load, you'll have to enable them manually after resetting the TV.
Removing corpses
- trigvolume_corpse (integer tv_id) removes all corpses inside the specified trigger volume.
file structure
<?xml version="1.0" encoding="utf-8"?> <Oni> <Objects> [...] </Objects> </Oni>
[...] means at least one trigger volume. Paste all tv data into there (this includes <TRGV Id="..."> and </TRGV> tag).
example
<TRGV Id="7383"> <Header> <Flags>Locked</Flags> <Position>-651.5666 -298 -633.4166</Position> <Rotation>0 0 0</Rotation> </Header> <OSD> <Name>trigger_volume_46</Name> <Scripts> <Entry>train_block2</Entry> <Inside></Inside> <Exit>block_path</Exit> </Scripts> <Teams>255</Teams> <Size>46 31 46</Size> <TriggerVolumeId>46</TriggerVolumeId> <ParentId>0</ParentId> <Notes></Notes> <Flags>PlayerOnly</Flags> </OSD> </TRGV>
start + size = end - 651.5666 + 46 = -605.5666 = x_blue_line_end - 298 + 31 = -267 = y_blue_line_end - 633.4166 + 46 = -587.4166 = z_blue_line_end
tags
tag | type | description |
---|---|---|
<?xml version="1.0" encoding="utf-8"?> | float, flag | Don't change this. |
<Oni> | - | XML root node |
<TRGV Id="..."> | integer | You can also use <TRGV> without the id parameter. |
<Header> | - | |
<Flags> | flag | Used in the past. |
<Position> | float x3 | TV is spawned at this xyz-position |
<Rotation> | float x3 | TV has this xyz-rotation (in degrees) |
<OSD> | - | |
<Name> | string | can have up to 63 characters |
<Scripts> | - | BSL functions; each can have up to 32 characters |
<Entry> | string | called up when character enters the TV |
<Inside> | string | called while character is inside the TV
This one is triggered at every frame (60 times per second) unless the trigger-only-once flag is set. Continuous triggering is typically needed for fire or gas damage. |
<Exit> | string | called up when character leaves the TV |
<Teams> | flags |
Every combination is possible. E.g.:
|
<Size> | float x3 | volume |
<TriggerVolumeId> | integer | used by BSL command "trigvolume_corpse ID" |
<ParentId> | integer | not used ? |
<Notes> | string | space for 128 characters |
<Flags> | flags |
|
BSL bugs and workarounds
String comparison bug
Trigger volumes can call bsl functions. This function can receive a string variable which contains the character name that triggered the trigger volume.
Unfortunately seems there are a bug when comparing this string parameter with another string variable in the BSL. Even if the trigger volume string and the other string are equal when you compare them in BSL it always returns false.
To reproduce the bug you can open "warehouse_level_scripts.bsl" in "EnvWarehouse" folder and replace the t65 function by this one (make a backup of the file first):
func void t65(string ai_name) { var string testVar = "char_0"; dmsg(testVar); dmsg("char_0"); dmsg(ai_name); sleep(100); if(testVar eq "char_0"){ dmsg("regular string comparison works!"); } else{ dmsg("regular string comparison failed!"); } if(ai_name eq "char_0"){ dmsg("trigger volume string comparison worked!"); } else{ dmsg("trigger volume string comparison failed!"); } dprint t65 if(d6 eq 0) { message xdoorislocked dprint door_is_locked } }
Load savepoint 1 in chapter 1 and move the character until the door. This function will be triggered. What is the output that you expect? "trigger volume string comparison worked!" right? Wrong it will print "trigger volume string comparison failed!" even though both string variables have the same value.
(EDIT: Interestingly, ai_name eq ai_name evaluates to true. Apparently, as detailed by Loser HERE, strings are compared by pointer and not by content. --geyser (talk) 18:47, 6 August 2020 (CEST) )
Possible workarounds
If you need to detect a few characters and if all of these are from different teams you can clone the trigger volume one time for each character and call different functions one for each team. This way you make sure you know which character triggered the trigger volume since each character has a unique team and a unique trigger volume function.
On Windows, you can take advantage of the Daodan DLL's custom function d_getindex(string ai_name) and compare by ID.
func void fire_damage(string ai_name) { var int index = d_getindex(ai_name); var int index0 = d_getindex(char_0); var int index1 = d_getindex(LoadingBay_Thug_1); var int index2 = d_getindex(LoadingBay_Thug_2); if(index eq index0) dmsg "Konoko is burning!" if(index eq index1) dmsg "Thug 1 is burning!" if(index eq index2) dmsg "Thug 2 is burning!" } func void main(void) { ai2_spawn LoadingBay_Thug_1 ai2_spawn LoadingBay_Thug_2 chr_location 0 134 -105 1029 }
chr_poison bug
chr_poison is a bsl function which is used in the original game to damage the characters within a specific interval, it is used in fire (example level4) and in gas (level5). It uses trigger volumes to call a function (every frame) which contains chr_poison (using TV "Inside" call). It works as expected except if the trigger volume is trigger many times which in case will trigger the "FATAL SCRIPTING ERROR: exceeded maximum scripting parameter base size of 32768" message in developer console and stop working.
Possible workarounds
Instead of using Inside to call the damage function use Entry to call an intermediate function let's call it "trigger_damage", "trigger_damage" will call another function called "damage_character", this function will be now be responsible for damaging the character every frame. We also need to know when the player leave the TV so we can stop damaging him, for this in the trigger volume xml file we will also use Exit to trigger the function "trigger_stop_damage".
To know which character leaved the damaging trigger volume, we will first give the LSI item to him and then remove it when he leaves the trigger volume, so will damage him only when he has the LSI. You can use another techniques here if you don't want to use the LSI, for instance you can use the multiple TV one explained above to fix the string comparison bug.
The final bsl code will look like this:
# this function now does the work of damage the character each frame # once the character looses lsi it stops damage him func void damage_character(string ai){ if(chr_has_lsi(ai)){ chr_poison(ai, 5, 10); sleep(1); fork damage_character(ai); } }
func void trigger_damage(string ai){ chr_givepowerup(ai,"lsi"); fork damage_character(ai); }
func void trigger_stop_damage(string ai){ chr_inv_reset(ai); # clear lsi }
And the trigger volume XML will look like this:
<TRGV Id="1"> <Header> <Flags>0</Flags> <Position>2005.23 1239.87 -3628.82</Position> <Rotation>0 0 0</Rotation> </Header> <OSD> <Name>BurnCharacter</Name> <Scripts> <Entry>trigger_damage</Entry> <Inside /> <Exit>trigger_stop_damage</Exit> </Scripts> <Teams>4</Teams> <Size>-287.04 1 -179.85</Size> <TriggerVolumeId>1</TriggerVolumeId> <ParentId>0</ParentId> <Notes></Notes> <Flags /> </OSD> </TRGV>
If the technique above is not enough and you are still experiencing the "FATAL SCRIPTING ERROR: exceeded maximum scripting parameter base size of 32768" error after a while, you may try using duplicated trigger volumes, where after a while (and before the error appears) you disable the first one and active the second one (which is a copy of the first).
The duplicated one can look like this:
<TRGV Id="2"> <Header> <Flags>0</Flags> <Position>2005.23 1239.87 -3628.82</Position> <Rotation>0 0 0</Rotation> </Header> <OSD> <Name>BurnCharacterPart2</Name> <Scripts> <Entry>trigger_damage</Entry> <Inside /> <Exit>trigger_stop_damage</Exit> </Scripts> <Teams>4</Teams> <Size>-287.04 1 -179.85</Size> <TriggerVolumeId>2</TriggerVolumeId> <ParentId>0</ParentId> <Notes></Notes> <Flags /> </OSD> </TRGV>
Code to switch between them:
trigvolume_enable("BurnCharacter",0); # disable the first one trigvolume_enable("BurnCharacterPart2",1); # activate the second one
Note that you should have only one of the copies active at a time (so for instance when "BurnCharacter" TV is enabled, "BurnCharacterPart2" TV should be disabled.
Mod Tool addon
With OniTools.xsiaddon you can drag'n'drop BINACJBOTrigger Volume.oni into the viewport.
Add or remove TVs, edit position and rotation as if they were real objects. Change size by moving the polygons.
When ready, export the data as BINACJBOTrigger Volume.xml to a folder of your choice.