XML:BINA/OBJC/TRGV: Difference between revisions

m
copy-edit
m (let's not link to something that's a redirect, since it breaks the chain of links in the nav header)
m (copy-edit)
Line 1: Line 1:
{{XML_OBJC_Header | prev=PWRU | type=TRGV | next=TRIG | name=Trigger Volume}}
{{XML_OBJC_Header | prev=PWRU | type=TRGV | next=TRIG | name=Trigger Volume}}


===general information===
{{TOClimit|3}}
* '''BINACJBOTrigger Volume.oni''' is level specific. (It can be found in AE/AEInstaller/vanilla/level'''X'''_Final.dat)
* The XML code on this page is based on onisplit '''v0.9.61.0'''


==General information==
* "BINACJBOTrigger Volume" is level specific (level''x''_Final.dat).
* The XML on this page is based on OniSplit '''v0.9.61.0'''.


===BSL support===
==BSL support==
'''show trigger activity'''
'''Show trigger activity'''
:'''debug_triggers = 1''' shows tv event and what character enters, stays inside or leaves
:<tt>debug_triggers = 1</tt> shows trigger volume (TV) events and which character enters, stays inside or leaves.


'''Resetting the trigger volume'''
'''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.
:<tt>trigvolume_reset (string tv_name)</tt> resets the TV to its original state. The primary use of this is to re-enable "entry", "inside" or "exit" BSL calls after 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, so you'll have to enable them manually after resetting the TV.


'''Removing corpses'''
'''Removing corpses'''
:'''trigvolume_corpse (integer tv_id)''' removes all corpses inside the specified trigger volume.
:<tt>trigvolume_corpse (integer tv_id)</tt> removes all corpses inside the specified trigger volume.


 
==File structure==
===file structure===
{|width=200px border=0 cellspacing=20 cellpadding=0 style="float:right"
{|width=200px border=0 cellspacing=20 cellpadding=0 style="float:right"
|[[File:Trigger_volume.jpg|200px]]
|[[File:Trigger_volume.jpg|200px]]


The red and blue lines were added to the screenshot in a graphics program, not by debug_triggers.
The red and blue lines were added to the screenshot in a graphics program, not by debug_triggers. But you can now display them with Ctrl-Shift-X if you are using the [[Daodan DLL]] or the Intel Mac build of Oni.
 
If you have an updated Oni Mac engine, the TVs can be made visible.
 
Used BSL commands:
* debug_triggers = 1
* chr_nocollision 0 1
* chr_debug_characters = 1
* chr_location 0 ''x y z''
|}
|}


Line 39: Line 31:
  </Oni>
  </Oni>


'''''[...]''''' means at least one trigger volume. Paste all tv data into there (this includes '''<font color="#0A0"><TRGV Id="..."></font>''' and '''<font color="#0A0"></TRGV></font>''' tag).
'''''[...]''''' means at least one trigger volume. Paste all your TV data in there (this includes the '''<font color="#0A0"><TRGV Id="..."></font>'''/'''<font color="#0A0"></TRGV></font>''' tag).


'''example'''
'''example'''
Line 69: Line 61:
  - 633.4166 + 46  = -587.4166 = z_blue_line_end
  - 633.4166 + 46  = -587.4166 = z_blue_line_end


 
==Tags==
===tags===
 
{| class="wikitable" width="100%"
{| class="wikitable" width="100%"
!width=280px| tag
!width=280px| Tag
!width=60px| type
!width=60px| Type
! description
! Description
|-
| <?xml version="1.0" encoding="utf-8"?>
| float, flag
| Don't change this.
|-
| <Oni>
| -
| XML root node
|-
|-
| <TRGV Id="...">
| <TRGV Id="...">
| integer
| integer
| You can also use <TRGV> without the id parameter.
| You can use <TRGV>s without the ID parameter.
|-
|-
| <Header>
| <Header>
Line 95: Line 77:
| <Flags>
| <Flags>
| flag
| flag
| Used in the past.
| Development relic, unused.
|-
|-
| <Position>
| <Position>
| float x3
| float x3
| TV is spawned at this xyz-position
| TV is spawned at this XYZ position.
|-
|-
| <Rotation>
| <Rotation>
| float x3
| float x3
| TV has this xyz-rotation (in degrees)
| TV has this XYZ rotation (in degrees).
|-
|-
| <OSD>
| <OSD>
Line 111: Line 93:
| <Name>
| <Name>
| string
| string
| can have up to 63 characters
| Can have up to 63 characters.
|-
|-
| <Scripts>
| <Scripts>
| -
| -
| BSL functions; each can have up to 32 characters
| BSL functions to call for each event (below); each function name can have up to 32 characters.
|-
|-
| <Entry>
| <Entry>
| string
| string
| called up when character enters the TV
| Called when character enters the TV.
|-
|-
|valign="top"| <Inside>
|valign="top"| <Inside>
|valign="top"| string
|valign="top"| string
| called while character is inside the TV
| 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.
This one is triggered every frame (60 times per second) unless the trigger-only-once flag is set. Continuous triggering is typically needed for damage by fire or poison gas.
|-
|-
| <Exit>
| <Exit>
| string
| string
| called up when character leaves the TV
| Called when character leaves the TV.
|-
|-
|valign="top"| <Teams>
|valign="top"| <Teams>
Line 148: Line 130:
| <Size>
| <Size>
| float x3
| float x3
| volume
| Volume of TV
|-
|-
| <TriggerVolumeId>
| <TriggerVolumeId>
| integer
| integer
| used by BSL command "trigvolume_corpse ''ID''"
| Used by BSL command <tt>trigvolume_corpse ''ID''</tt>
|-
|-
| <ParentId>
| <ParentId>
| integer
| integer
| not used ?
| Not used?
|-
|-
| <Notes>
| <Notes>
| string
| string
| space for 128 characters
| Space for a note up to 128 characters long.
|-
|-
|valign="top"| <Flags>
|valign="top"| <Flags>
Line 166: Line 148:
|
|
: OneTimeEnter - entry function called only once (otherwise called every time character enters the TV)
: OneTimeEnter - entry function called only once (otherwise called every time character enters the TV)
: OneTimeInside - inside function called only once (otherwise called at every frame while character is in the TV)
: OneTimeInside - inside function called only once (otherwise called every frame while character is in the TV)
: OneTimeExit - exit function called only once (otherwise called every time character leaves the TV)
: OneTimeExit - exit function called only once (otherwise called every time character leaves the TV)
: EnterDisabled - entry function disabled (can be enabled with "trigvolume_enable ''tv_name'' entry 1")
: EnterDisabled - entry function disabled (can be enabled with "trigvolume_enable ''tv_name'' entry 1")
Line 175: Line 157:
|}
|}


===BSL bugs and workarounds===
==BSL bugs and workarounds==
===String comparison bug===
As you've seen, trigger volumes can call BSL functions. Such a function can receive a string variable which contains the name of the character that triggered the trigger volume.


====String comparison bug====
Unfortunately it seems there's a bug in BSL when comparing this string parameter with another string variable. Even if the trigger volume string and the other string are identical, the comparison always returns false.


Trigger volumes can call bsl functions. This function can receive a string variable which contains the character name that triggered the trigger volume.
To reproduce the bug, open "warehouse_level_scripts.bsl" in IGMD/EnvWarehouse/ and replace the '''t65''' function with this version (make a backup of the file first):
 
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)
     func void t65(string ai_name)
Line 195: Line 175:
         sleep(100);
         sleep(100);
          
          
         if(testVar eq "char_0"){
         if (testVar eq "char_0")
             dmsg("regular string comparison works!");
        {
             dmsg("Regular string comparison works!");
         }
         }
         else{
         else
             dmsg("regular string comparison failed!");
        {
             dmsg("Regular string comparison failed!");
         }
         }
          
          
         if(ai_name eq "char_0"){
         if (ai_name eq "char_0")
             dmsg("trigger volume string comparison worked!");
        {
             dmsg("Trigger volume string comparison worked!");
         }
         }
         else{
         else
             dmsg("trigger volume string comparison failed!");
        {
             dmsg("Trigger volume string comparison failed!");
         }
         }
          
          
         dprint t65
         dprint t65
         if(d6 eq 0)
         if (d6 eq 0)
         {
         {
             message xdoorislocked
             message xdoorislocked
Line 217: Line 201:
     }
     }


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.
Load savepoint 1 in Chapter 1 and move the character up to the first closed door. This function will be triggered. What is the output that you expect? "Trigger volume string comparison worked!", since Konoko is char_0? Wrong! It will print "Trigger volume string comparison failed!" even though both string variables have the same value.


[[Image:Trigger_volume_string_comparison_bug.jpg|300px]]
[[Image:Trigger_volume_string_comparison_bug.jpg|300px]]


(EDIT: Interestingly, '''ai_name eq ai_name''' evaluates to '''true'''. Apparently, as detailed by [[User:Loser|Loser]] [[BSL:String_comparison|HERE]], strings are compared by pointer and not by content. --[[User:Geyser|geyser]] ([[User talk:Geyser|talk]]) 18:47, 6 August 2020 (CEST) )
(Update: Apparently, as detailed by [[User:Loser|Loser]] [[BSL:String_comparison|HERE]], strings are compared by pointer and not by content.)
 
=====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.
====Possible workarounds====
If you need to detect a few characters and all of them are on different teams, you can clone the trigger volume one time for each character and call different functions, one for each team. This way you'll know which character triggered the trigger volume, since each team can have a unique trigger volume function thanks to the <Teams> flag.


On Windows, you can take advantage of the [[Daodan DLL]]'s custom function <tt>d_getindex(string ai_name)</tt> and compare by ID.
On Windows, you can take advantage of the [[Daodan DLL]]'s custom function <tt>d_getindex(string ai_name)</tt> and compare by ID.
Line 235: Line 218:
  var int index1 = d_getindex(LoadingBay_Thug_1);
  var int index1 = d_getindex(LoadingBay_Thug_1);
  var int index2 = d_getindex(LoadingBay_Thug_2);
  var int index2 = d_getindex(LoadingBay_Thug_2);
  if(index eq index0)
  if (index eq index0)
  dmsg "Konoko is burning!"
  dmsg "Konoko is burning!"
  if(index eq index1)
  if (index eq index1)
  dmsg "Thug 1 is burning!"
  dmsg "Thug 1 is burning!"
  if(index eq index2)
  if (index eq index2)
  dmsg "Thug 2 is burning!"
  dmsg "Thug 2 is burning!"
  }
  }
Line 251: Line 234:
{{divhide|end}}
{{divhide|end}}


====chr_poison bug====
===chr_poison bug===
chr_poison is a BSL function which is used in the original game to damage characters continuously within a specific interval. For example it is used for fires (e.g. in Chapter 4) and for poison gas (Chapter 5). A trigger volume's <Inside> tag is used to call a function every frame which contains chr_poison. It works as expected, except if the trigger volume is triggered too many times, in which case you'll see the message "FATAL SCRIPTING ERROR: exceeded maximum scripting parameter base size of 32768" in the developer console and the function will stop working.


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", which will be responsible for damaging the character every frame. We also need to know when the player leaves the TV so we can stop damaging him, so in the trigger volume XML file we'll also use <Exit> to call the function "trigger_stop_damage".


=====Possible workarounds=====
To know which character left the damaging TV, we will give an LSI to him upon entry and then remove it when he leaves the trigger volume, and we'll only damage him when he has the LSI. (You can use another technique here if you don't want to use an LSI; for instance you can use the multiple TV approach that was explained above as a string comparison workaround.)


Instead of using <b>Inside</b> to call the damage function use <b>Entry</b> 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 <b>Exit</b> to trigger the function "trigger_stop_damage".
The final BSL code will look like this:


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.
     # This function now does the work of damaging the character each frame
 
     # Once the character loses his LSI, it stops damaging him
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){
     func void damage_character(string ai){
         if(chr_has_lsi(ai)){
         if (chr_has_lsi(ai))
        {
             chr_poison(ai, 5, 10);
             chr_poison(ai, 5, 10);
             sleep(1);
             sleep(1);
Line 273: Line 255:
     }
     }


     func void trigger_damage(string ai){
     func void trigger_damage(string ai)
         chr_givepowerup(ai,"lsi");
    {
         chr_givepowerup(ai, "lsi");
         fork damage_character(ai);
         fork damage_character(ai);
     }
     }


     func void trigger_stop_damage(string ai){
     func void trigger_stop_damage(string ai)
         chr_inv_reset(ai); # clear lsi
    {
         chr_inv_reset(ai); # clear LSI
     }
     }


Line 306: Line 290:
     </TRGV>
     </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).
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 activate the second one (which is a copy of the first).


The duplicated one can look like this:
The duplicated one can look like this:
Line 332: Line 316:
     </TRGV>
     </TRGV>


Code to switch between them:
The 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===
    trigvolume_enable("BurnCharacter", 0); # disable the first one
[[Image:xsi_addon_trigger_volume_manager.jpg|thumb|200px|right|trigger volume manager]]
    trigvolume_enable("BurnCharacterPart2", 1); # activate the second one


With [[Mod_Tool#OniTools_addon|OniTools.xsiaddon]] you can drag'n'drop BINACJBOTrigger Volume'''.oni''' into the viewport.
Note that you should have only one of the copies active at a time.


Add or remove TVs, edit position and rotation as if they were real objects. Change size by moving the polygons.
==Mod Tool addon==
[[Image:xsi_addon_trigger_volume_manager.jpg|thumb|200px|right|Trigger volume manager.]]


When ready, export the data as BINACJBOTrigger Volume.xml to a folder of your choice.
With [[Mod_Tool#OniTools_addon|OniTools.xsiaddon]] you can drag'n'drop BINACJBOTrigger Volume'''.oni''' into the viewport. Add or remove TVs, and edit their position and rotation as if they were real objects. Change their size by moving the points. When ready, export the data as BINACJBOTrigger Volume.xml to a folder of your choice.


{{XML}}
{{XML}}