diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..2f4af840 --- /dev/null +++ b/404.html @@ -0,0 +1,3917 @@ + + + +
+ + + + + + + + + + + + + + +How can I contribute?
+All contributions to ModdingDocs are welcome.
+Including:
+What do I need to know to contribute?
+ModdingDocs uses reStructuredText.
+Cheatsheet for reStructuredText syntax: https://docs.generic-mapping-tools.org/6.2/rst-cheatsheet.html.
+The .rst
files can be found in the docs/source
directory. If you're adding a new file, make sure to link it in index.rst
You don't necessarily need to set up a local build environment.
+To contribute without doing so, you can just edit the files in an editor of your choice and create a GitHub pull request from them.
+There will be a test-build done for each PR, which you can find on your PR as a "Check" by clicking show all checks
and details
.
This should take you to a online version of the docs with your PRs changes.
+You need to have a relatively recent version of Python installed - 3.8 or higher. Download here
+git clone https://github.com/R2Northstar/ModdingDocs/
+cd ModdingDocs
+./run.ps1
+
git clone https://github.com/R2Northstar/ModdingDocs/
+cd ModdingDocs
+./run.sh
+
Warning
+On Windows, if you are not able to run the script by running ./run.ps1
, try running it with: powershell.exe -ExecutionPolicy Bypass -File .\run.ps1
After this you should be able to run with just ./run.ps1
.
If you're using Visual Studio Code, the following extensions might be of interest:
+Note
+To get the ReStructuredText support working, you will likely need to tell VSCode to use the Poetry environment.
+To do so, open one of the .py files, which should make the python version appear in the bottom right of VSCode.
+Click on it, and select the version with (moddingdocs
after it.
Then, when looking at a ReStructuredText file there should be esbonio:
in the bottom right.
Click that to restart the ReStructuredText support. This allows it to see all the dependencies Poetry installed.
+Northstar supports the creation of many user mods. This guide will teach you the basics +of modding to get you started.
+Check out the usage
section for further information, including
+installation
.
This guide assumes you have basic understanding with programming and know how to use +developer environments. Listed below are tools useful for exporting file formats.
+If you'd like a more lengthy set of tutorials covering many topics. Look at: NoSkill modding guide
+To get started with modding for Northstar, we recommend getting yourself some tools.
+Check out the tools
section for more information.
In order to get started with making your mod, create a folder in R2Northstar/mods
.
+While it isn't required, it is best practise by mod authors to follow the naming scheme
+Author.ModName
, such as Northstar.Client
.
After making this folder, inside it add a folder named mod
and a file named
+mod.json
.
Provided is a template mod.json
, for a detailed list of values read the
+cheatsheet
{
+ "Name": "Yourname.Modname",
+ "Description": "Woo yeah wooo!",
+
+ "LoadPriority": 0,
+ "ConVars": [],
+ "Scripts": [],
+ "Localisation": []
+ }
+
Inside the mod
folder, existing files found in the engine's virtual file system will
+be overwritten and new files can be added. If you need to define new Squirrel files
+(.nut/.gnut)
they must be declared in the "Scripts"
array in mod.json
. An
+example for this might be:
"Scripts": [
+ {
+ "Path": "path/to/file.nut",
+ "RunOn": "( CLIENT || SERVER ) && MP"
+ },
+ {
+ "Path": "path/to/another_file.nut",
+ "RunOn": "( CLIENT || SERVER ) && MP",
+ "ClientCallback": {
+ "Before": "ClientPreMapspawnThing",
+ "After": "AfterMapspawnClientThing"
+ },
+ "ServerCallback": {
+ "Before": "ServerPreMapspawncrap",
+ "After": "ServerAfterMapspawnWoo"
+ }
+ }
+ ]
+
"Path"
indicates where the script is, "RunOn"
is the Squirrel VM context (see
+../native/sqvm
) as an expression, and "ClientCallback"
and
+"ServerCallback"
specify a function call that can be "Before"
and/or "After"
+map-spawn.
mod.json
architecture¶Located at your mod's root folder, the mod.json
file is the entrypoint of your mod;
+it contains human-readable information about it, which scripts to load, and a bunch of
+interesting stuff.
This guide will dig into each of the possible mod.json
fields. Please note that
+mod.json
keys must start with an uppercase letter.
This is what a well-formatted mod.json
looks like:
{
+ "Name": "Northstar.CustomServers",
+ "Description": "Attempts to recreate the behaviour of vanilla Titanfall 2 servers, as well as changing some scripts to allow better support for mods",
+ "Version": "1.5.0",
+ "LoadPriority": 0,
+ "ConVars": [
+ {
+ "Name": "ns_private_match_last_mode",
+ "DefaultValue": "tdm"
+ },
+ {
+ "Name": "ns_private_match_last_map",
+ "DefaultValue": "mp_forwardbase_kodai"
+ }
+ ],
+ "Scripts": [
+ {
+ "Path": "sh_northstar_utils.gnut",
+ "RunOn": "CLIENT || SERVER || UI"
+ },
+ {
+ "Path": "mp/_classic_mp_dropship_intro.gnut",
+ "RunOn": "SERVER && MP"
+ }
+ ],
+ "Localisation": [
+ "resource/northstar_custom_%language%.txt"
+ ]
+ }
+
Note
+The real Northstar.CustomServers
mod contains more convars and scripts, some
+have been removed for the readability of the example.
Those ones are pretty self-explanatory. Both fields are used by Northstar itself to
+display in-game information about your mod in the main screen Mods
menu.
Best pratice for your mod's name is to use the Author.ModName
convention.
This field specifies version of your mod using X.Y.Z
scheme; this field must be
+updated each time you release a new version of your mod.
Common use is to increase Z when you publish a fix (e.g. 1.5.0
to 1.5.1
),
+and increase Y when you release new features (e.g. 1.5.1
to 1.6.0
).
Best practise is to follow semantic versioning (https://semver.org/).
+This field defines the order in which all mods will be loaded by Northstar. For example,
+a mod with "LoadPriority": 1
will be loaded after a mod with "LoadPriority": 0
.
If your mod uses code from another mod, make sure to set a greater LoadPriority than the +mod you're using code from.
+This field lists configuration variables, that can be set by servers owners to modify +behaviour of your mod.
+Each configuration variable must have a "Name"
and a "DefaultValue"
. ConVars can
+also have an optional "Flags"
field which specifies special behaviour and an
+optional "HelpString"
field which specifies the usage of the ConVar which can be
+view in-game by running help <convar>
.
You can access configuration variables from squirrel code using GetConVarInt
,
+GetConVarFloat
, GetConVarBool
or GetConVarString
calls.
Warning
+No matter the type of your variables, they have to be JSON strings, otherwise game +won't start!
+If I don't want to wait 15 seconds for matches to start on my server,
+Northstar.CustomServers
mod exposes a ConVar named
+ns_private_match_countdown_length
in its mod.json
manifesto:
"ConVars": [
+ {
+ "Name": "ns_private_match_countdown_length",
+ "DefaultValue": "15"
+ },
+
+ ...
+ ]
+
I can setup the ns_private_match_countdown_length
variable in my
+R2Northstar/mods/Northstar.CustomServers/mod/cfg/autoexec_ns_server.cfg
+configuration file.
When starting a match, Northstar.CustomServers
mod will retrieve the configuration
+variable value, or its default value if it hasn't been specified in configuration file:
// start countdown
+ SetUIVar( level, "gameStartTime", Time() + GetConVarFloat( "ns_private_match_countdown_length" ) )
+
Note
+All Northstar.CustomServers
ConVars are listed here:
+https://r2northstar.gitbook.io/r2northstar-wiki/hosting-a-server-with-northstar/basic-listen-server
You can assign flags to configuration variables; to use several flags at once, just add +their values.
+Name | +Value | +Description | +
---|---|---|
FCVAR_UNREGISTERED | +1 | +If this is set, don't add to linked list, etc. | +
FCVAR_DEVELOPMENTONLY | +2 | +Hidden in released products. Flag is removed automatically if ALLOW_DEVELOPMENT_CVARS is defined. | +
FCVAR_GAMEDLL | +4 | +Defined by the game DLL | +
FCVAR_CLIENTDLL | +8 | +Defined by the client DLL | +
FCVAR_HIDDEN | +16 | +Hidden. Doesn't appear in find or auto complete. Not deterred by ALLOW_DEVELOPMENT_CVARS. | +
FCVAR_PROTECTED | +32 | +It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value. | +
FCVAR_SPONLY | +64 | +This cvar cannot be changed by clients connected to a multiplayer server. | +
FCVAR_ARCHIVE | +128 | +Save this ConVar's value to vars.rc - this works both server and client-side. | +
FCVAR_NOTIFY | +256 | +Notifies players when this ConVar's value was changed. | +
FCVAR_USERINFO | +512 | +Changes the client's info string | +
FCVAR_PRINTABLEONLY | +1024 | +This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). | +
FCVAR_UNLOGGED | +2048 | +If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log | +
FCVAR_NEVER_AS_STRING | +4096 | +never try to print that cvar | +
FCVAR_REPLICATED (AKA FCVAR_SERVER) | +8192 | +This value is set by server and replicated by clients. | +
FCVAR_CHEAT | +16384 | +Do NOT allow changing of this convar by console, unless sv_cheats is 1. | +
FCVAR_SS | +32768 | +causes varnameN where N == 2 through max splitscreen slots for mod to be autogenerated | +
FCVAR_DEMO | +65536 | +Record this cvar in a demo. | +
FCVAR_DONTRECORD | +131072 | +Don't record this. | +
FCVAR_SS_ADDED | +262144 | +This is one of the "added" FCVAR_SS variables for the splitscreen players | +
FCVAR_RELEASE | +524288 | +This value is available to the end user. | +
FCVAR_RELOAD_MATERIALS | +1048576 | +If this cvar changes, it forces a material reload | +
FCVAR_RELOAD_TEXTURES | +2097152 | +If this cvar changes, it forces a texture reload | +
FCVAR_NOT_CONNECTED | +4194304 | +cvar cannot be changed by a client that is connected to a server | +
FCVAR_MATERIAL_SYSTEM_THREAD | +8388608 | +Indicates this cvar is read from the material system thread | +
FCVAR_ARCHIVE_PLAYERPROFILE | +16777216 | +Save this, but to profile.cfg instead - meaning this only works for clients. | +
FCVAR_ACCESSIBLE_FROM_THREADS | +33554432 | +used as a debugging tool necessary to check material system thread convars | +
FCVAR_SERVER_CAN_EXECUTE | +268435456 | +the server is allowed to execute this command on clients via ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd | +
FCVAR_SERVER_CANNOT_QUERY | +536870912 | +If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue). | +
FCVAR_CLIENTCMD_CAN_EXECUTE | +1073741824 | +IVEngineClient::ClientCmd is allowed to execute this command. Note: IVEngineClient::ClientCmd_Unrestricted can run any client command. | +
Note
+Some flags have been skipped due to them being generally useless unless you have +very specific requirements.
+The scripts field lets you declare an array of Squirrel files to import into your mod.
+Each script entry must have a "Path" value and a "RunOn" value.
+ "Scripts": [
+ {
+ "Path": "path/to/file.nut",
+ "RunOn": "( CLIENT || SERVER ) && MP"
+ },
+ {
+ "Path": "path/to/another_file.nut",
+ "RunOn": "( CLIENT || SERVER ) && MP",
+ "ClientCallback": {
+ "Before": "ClientPreMapspawnThing",
+ "After": "AfterMapspawnClientThing"
+ },
+ "ServerCallback": {
+ "Before": "ServerPreMapspawncrap",
+ "After": "ServerAfterMapspawnWoo"
+ }
+ }
+ ]
+
Path of the Squirrel file to import, without mod/scripts/vscripts
prefix (that's
+where your script files should go).
A boolean expression which tells the game when and in which context to compile the script.
+Name | +Description | +
---|---|
SERVER | +Server script VM, recompiles on map change | +
CLIENT | +Client script VM, recompiles on map change | +
UI | +UI script VM, recompiles on when uiscript_reset is ran |
+
SP | +Singleplayer | +
MP | +Multiplayer | +
DEV | +Value of developer convar | +
LOBBY | +True in mp_lobby. (Server and client VMs only) | +
MAP_mp_box | +True if the given map name is being loaded | +
GAMEMODE_at | +True if the given game mode is being loaded | +
CLIENT && !LOBBY
- Compiles on client and not in the lobby. So during actual singeplayer and multiplayer gameplay.
CLIENT && MP && !LOBBY
- Compiles on client, only in multiplayer and not in the lobby.
( CLIENT || SERVER ) && MP
- Compiles on both client and server only in multiplayer.
CLIENT && SP && MAP_sp_boomtown
- Compiles only on client in singleplayer only when the map sp_boomtown
is loaded. ( Here SP
isn't needed as sp_boomtown
is singleplayer only )
CLIENT && GAMEMODE_aitdm
- Compiles on client on both singleplayer and multiplayer only when the aitdm
gamemode is set. ( aitdm
is attrition which is multiplayer only so this script only compiles on multiplayer )
Specify methods that will be called before/after map spawn.
+This field is an array listing localisation files relative paths.
+For more info about localisation works on Northstar, read the localisation
+section.
Note
+This project is under active development.
+Example Mod: +Custom.Crosshairs
+1: Create the following file
+~/Your.Mod/keyvalues/scripts/weapons/mp_weapon_[desired weapon].txt
2: Put the following into the newly created .txt file:
+ WeaponData
+ {
+ RUI_CrosshairData
+ {
+ Crosshair_1
+ {
+ "ui" "ui/crosshair_alternator"
+ }
+ }
+ }
+
3: change "ui/crosshair_alternator" to your desired crosshair
+It is possible to combine crosshairs by modifying the mp_weapon_[Desired +Weapons].txt
+Below is an example of combining the Alternator and R201 crosshairs +into one
+ WeaponData
+ {
+ active_crosshair_count "2" //Amount of crosshairs you want to use
+
+ RUI_CrosshairData
+ {
+ Crosshair_1
+ {
+ "ui" "ui/crosshair_alternator"
+ }
+ Crosshair_2
+ {
+ "ui" "ui/crosshair_tri"
+ }
+ }
+ }
+
To add more crosshairs add another Crosshair_ X following the +formating in the script above.
+Note
+The limit for this seems to be 4 Crosshairs onscreen at once
+Simply add the following line below the "ui" line
+"base_spread" "3.0"
+Below the "ui" line, Like this:
{
+ RUI_CrosshairData
+ {
+ Crosshair_1
+ {
+ "ui" "ui/crosshair_alternator" //THis is the Croshair
+ "base_spread" "3.0" //This is a spread Multiplier, Line doesn't exist by default
+ }
+ }
+ }
+
WeaponData
+ {
+ RUI_CrosshairData
+ {
+ Crosshair_1
+ {
+ "ui" "ui/crosshair_sniper_amped" //This means NO crosshair
+ }
+ }
+ }
+
These are the available crosshairs in-game, along with their in-game +reference:
+ +Crosshair images are taken from the modding guide on +https://noskill.gitbook.io/titanfall2/
+As with any mod, it is recommended to test this out in a private match first. Save any changes you made to the desired weapon's file and type reload
in your console
Keep in mind that some weapons have animated or dynamic crosshairs. Weapons like the Charge Rifle, Cold War, Frag Grenade, etc... have custom animations for their crosshairs. which can cause weirdness or jank when used on other weapons or when using other crosshairs on them.
+ui/crosshair_titan_sniper
Thank you to Cpone#0001
and Nixie#8251
from the Northstar
+Discord for helping me figure this out
Do keep in mind that these are usually changed server-side.
+To actually change the keyvalues of weapons you'd have to name them appropriately inside a mod folder
+For example: R2Northstar\mods\Northstar.Custom\keyvalues\scripts\weapons
+You can find all the weapon keyvalues here
Example for modding crossshairs using keyvalues: crosshairmodding
+Mod Examples:
+Note
+This project is under active development, and this section needs expanding
+For your content to reach as many people as possible, it is important to have it +translated in users' natural language. This guide will help you do that!
+Languages natively supported by Titanfall2 are:
+"tchinese"
)Here's what a translation file looks like:
+ "lang"
+ {
+ "Language" "english"
+ "Tokens"
+ {
+ "MENU_LAUNCH_NORTHSTAR" "Launch Northstar"
+ "MENU_TITLE_MODS" "Mods"
+ "RELOAD_MODS" "Reload Mods"
+ "WARNING" "Warning"
+ "CORE_MOD_DISABLE_WARNING" "Disabling core mods can break your client!"
+ "DISABLE" "Disable"
+ }
+ }
+
It begins with the "lang"
instruction, contains a "Language"
key indicating
+language of current file's translations, and a "Token"
key indexing all
+translations.
Warning
+If the translation file contains any non-ASCII character, it must use "UTF-16
+LE"
encoding.
You'll have to create one file per supported language, and all your files must be named +in a similar fashion.
+For example, Northstar translation files are named
+"northstar_client_localisation_english.txt"
,
+"northstar_client_localisation_french.txt"
,
+"northstar_client_localisation_german.txt"
etc.
You can import them from your mod.json
manifesto this way:
{
+ "Localisation": [
+ "resource/northstar_client_localisation_%language%.txt"
+ ]
+ }
+
Note
+The "%language%"
syntax allows VM to load up translations matching game language
+(e.g. an English client will automatically use
+"northstar_client_localisation_english.txt"
file)
To translate UI elements like menus, you have to insert strings containing your
+translation keys, preceded by a #
.
For example, to translate the "Launch Northstar" button on main menu, instead of +calling:
+ AddComboButton( comboStruct, headerIndex, buttonIndex++, "Launch Northstar" )
+
We'll use:
+ AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_LAUNCH_NORTHSTAR" )
+
You can also use the Localize
method client-side:
Localize( "#MENU_LAUNCH_NORTHSTAR" )
+
Northstar adds new strings to the game which can be localised to match the language you +are using on your Titanfall 2 installation.
+They're all located in "Northstar.Client"
mod: [Northstar localisation files on
+GitHub]
+(https://github.com/R2Northstar/NorthstarMods/blob/main/Northstar.Client/mod/resource)
Note
+To test your modifications, change your game language: with Origin, go to Origin
+(My games library) -> Titanfall 2 (right click) -> Game Properties -> Advanced
+Launch Options
; with Steam, go to Titanfall 2 page -> Manage (cog) -> Properties
+-> Language
.
This document provides a list of various Key Values utilized by weapons and their purposes.
+Note that asset values are identical to strings in Key Value files.
+These can be modified with KeyValue modding, see here: weaponmodding
+The majority of these values are held in eWeaponVar
. Those that are can be modified with attachments (named "Mods" in Key Value files); otherwise, the game will crash with an "Unrecognized entry" error when Key Values are loaded.
Name | +Type | +eWeaponVar | +Purpose | +Notes | +
---|---|---|---|---|
active_crosshair_count |
+int |
+True | +The number of crosshairs to use from the weapon's crosshair list, starting at rui_crosshair_index . |
++ |
activitymodifier |
+string |
+True | +Used for certain weapon animations. | ++ |
ads_fov_zoomfrac_end |
+float |
+True | +The fraction at which FoV should finish zooming in when aiming down sights. | ++ |
ads_fov_zoomfrac_start |
+float |
+True | +The fraction at which FoV should start zooming in when aiming down sights. | ++ |
ads_move_speed_scale |
+float |
+True | +Scalar on movement speed when aiming down sights. Does not affect acceleration. | ++ |
aimassist_adspull_weaponclass |
+string |
+True | +Determines the aim assist class for aim assist pull on aiming down sights. | +none : No aim assist pull.broad , broad_sp : Moderate aim assist pull.precise , precise_sp : High aim assist pull. |
+
aimassist_adspull_zoomEnd |
+float |
+True | +The fraction during aiming down sights at which aim assist pull ends. | ++ |
aimassist_adspull_zoomStart |
+float |
+True | +The fraction during aiming down sights at which aim assist pull starts. | ++ |
aimassist_disable_ads |
+bool |
+True | +Disables aim assist while aiming down sights. | ++ |
aimassist_disable_ads_humansonly |
+bool |
+True | +Disables aim assist on non-heavily armored targets while aiming down sights. | ++ |
aimassist_disable_ads_titansonly |
+bool |
+True | +Disables aim assist on heavily armored targets while aiming down sights. | ++ |
aimassist_disable_hipfire |
+bool |
+True | +Disables aim assist while not aiming down sights. | ++ |
aimassist_disable_hipfire_humansonly |
+bool |
+True | +Disables aim assist on non-heavily armored targets while not aiming down sights. | ++ |
aimassist_disable_hipfire_titansonly |
+bool |
+True | +Disables aim assist on heavily armored targets while not aiming down sights. | ++ |
allow_empty_fire |
+bool |
+True | +Allows the weapon to fire with no ammo remaining. | ++ |
allow_headshots |
+bool |
+True | +Allows the weapon to land headshots on non-heavily armored targets. Does not work on titan weapons. | ++ |
alt_fire_anim_count |
+int |
+True | +Used to alternate firing animations on weapons with dual launchers (Flight Core, Salvo Core, etc.) | ++ |
alwaysShow |
+bool |
+False | +Unknown. | ++ |
ammo_clip_random_loss_on_npc_drop |
+float |
+True | +Random fraction of ammo missing in the clip when dropped by an NPC. | ++ |
ammo_clip_random_loss_on_npc_drop_chunksize |
+int |
+True | +Ensures that the ammo remaining in the weapon when dropped by an NPC is divisible by this value. | ++ |
ammo_clip_reload_max |
+int |
+True | +Unknown. | ++ |
ammo_clip_size |
+int |
+True | +Magazine size. | ++ |
ammo_default_total |
+int |
+True | +The total amount of ammo a weapon spawns with, including the magazine. | ++ |
ammo_display |
+string |
+True | +Unknown. | ++ |
ammo_display_as_clips |
+bool |
+True | +Unknown. | ++ |
ammo_drains_to_empty_on_fire |
+bool |
+True | +Forces the weapon's clip to drain to empty over fire_duration . |
++ |
ammo_min_to_fire |
+int |
+True | +The minimum amount of ammo needed to fire the weapon. | ++ |
ammo_min_to_fire_autoreloads |
+bool |
+True | +Forces a reload when the current ammo is lower than ammo_min_to_fire . |
++ |
ammo_no_remove_from_clip |
+bool |
+True | +Enables/disables consuming ammo in the magazine on attack. | +If true while ammo_no_remove_from_stockpile is false, displays only stockpile ammo on HUD. If both are true, displays infinite ammo.If true and magazine size is greater than 0, ammo is not removed from stockpile. |
+
ammo_no_remove_from_stockpile |
+bool |
+True | +Enables/disables consuming ammo in the stockpile when reloading or on weapons with no magazine. | +If false, displays stockpile ammo on HUD. | +
ammo_per_shot |
+int |
+True | +The amount of ammo consumed per shot. | ++ |
ammo_size_segmented_reload |
+int |
+True | +The amount of ammo reloaded per segment on weapons with segmented reloads. | ++ |
ammo_stockpile_max |
+int |
+True | +The maximum amount of ammo that the stockpile can hold. | ++ |
ammo_suck_behavior |
+string |
+True | +Can be melee_weapons , offhand_weapons , or primary_weapons . Use unknown. |
++ |
anim_alt_idleAttack |
+int |
+True | +Unknown. | ++ |
arc_switch_activator |
+bool |
+False | +Unknown. | ++ |
attack_button_presses_ads |
+bool |
+True | +Enables/disables the attack button triggering aim down sights when held. | ++ |
attack_button_presses_melee |
+bool |
+True | +Enables/disables the attack button triggering melee. | ++ |
battle_chatter_event |
+string |
+False | +Unknown. | ++ |
bob_ |
+Various | +False | +A set of values controlling view model bobbing while moving. | ++ |
body_skin |
+int |
+True | +Unknown. | ++ |
body_type |
+string |
+False | +Unknown. | ++ |
bodygroup1_name |
+string |
+False | +The name that body group 1 is tied to. | ++ |
bodygroup1_set |
+bool |
+True | +Enables/disables body group 1. | +Distinct values exist for bodygroups up to bodygroup10 . |
+
bodygroup_ads_scope_name |
+string |
+False | +The name that the set of aiming down sights scopes is tied to. | ++ |
bodygroup_ads_scope_set |
+int |
+True | +The index of the scope to use from the set while aiming down sights. | ++ |
bodygroup_ammo_index_count |
+int |
+True | +The maximum amount of ammo visible on the viewmodel. | ++ |
bolt_bounce_frac |
+float |
+True | +Maximum angle fraction from parallel that a bolt can still bounce off a surface at. | ++ |
bolt_gravity_enabled |
+bool |
+True | +Enables/disables gravity on bolts fired. | ++ |
bolt_hitsize |
+float |
+True | +The hitbox size of bolts. | +Hitbox size does not affect map collisions. Not 1:1 with other size measurements. |
+
bolt_hitsize_grow1_size |
+float |
+True | +The hitbox size the bolt reaches at the grow1 time. | ++ |
bolt_hitsize_grow1_time |
+float |
+True | +The time in seconds at which grow1 size is reached. | ++ |
bolt_hitsize_grow2_size |
+float |
+True | +The hitbox size the bolt reaches at the grow2 time. | ++ |
bolt_hitsize_grow2_time |
+float |
+True | +The time in seconds at which grow2 size is reached. | ++ |
bolt_hitsize_growfinal_lerptime |
+float |
+True | +The hitbox size the bolt reaches at the growfinal time. | ++ |
bolt_hitsize_growfinal_size |
+float |
+True | +The time in seconds at which growfinal size is reached. | +Time values are absolute from projectile spawn, not cumulative. Later time values must be >= the previous values. | +
bounce_effect_table |
+asset |
+True | +The effect table to use when bouncing. | ++ |
breaks_cloak |
+bool |
+True | +Enables/disables breaking cloak on attack. | +Does not appear to work for all weapons, such as titan weaponry. | +
burst_fire_count |
+int |
+True | +The number of shots fired in a burst. | ++ |
burst_fire_delay |
+float |
+True | +The delay after a burst before another burst can be fired. | +Counts from the moment the last shot in a burst is fired. Also prevents other actions (e.g. holstering, melee) until finished. |
+
burst_or_looping_fire_sound_end |
+string |
+True | +Deprecated. Sound effect played at the end of the burst/loop for the user. | ++ |
burst_or_looping_fire_sound_end_1p |
+string |
+True | +Sound effect played at the end of the burst/loop for the user. | ++ |
burst_or_looping_fire_sound_end_3p |
+string |
+True | +Sound effect played at the end of the burst/loop for others. | ++ |
burst_or_looping_fire_sound_end_npc |
+string |
+True | +Sound effect played at the end of the burst/loop for others when an NPC is using the weapon. | ++ |
burst_or_looping_fire_sound_middle |
+string |
+True | +Deprecated. Sound effect played on every attack in the burst/loop for the user. Ends on burst/loop end. | ++ |
burst_or_looping_fire_sound_middle_1p |
+string |
+True | +Sound effect played on every attack in the burst/loop for the user. Ends on burst/loop end. | ++ |
burst_or_looping_fire_sound_middle_3p |
+string |
+True | +Sound effect played on every attack in the burst/loop for others. Ends on burst/loop end. | ++ |
burst_or_looping_fire_sound_middle_npc |
+string |
+True | +Sound effect played on every attack in the burst/loop for others when an NPC is using the weapon. Ends on burst/loop end. | ++ |
burst_or_looping_fire_sound_start |
+string |
+True | +Deprecated. Sound effect played at the start of the burst/loop for the user. | ++ |
burst_or_looping_fire_sound_start_1p |
+string |
+True | +Sound effect played at the start of the burst/loop for the user. | ++ |
burst_or_looping_fire_sound_start_3p |
+string |
+True | +Sound effect played at the start of the burst/loop for others. | ++ |
burst_or_looping_fire_sound_start_npc |
+string |
+True | +Sound effect played at the start of the burst/loop for others when an NPC is using the weapon. | ++ |
bypass_semiauto_hold_protection |
+bool |
+True | +Unknown. | ++ |
can_attack_when_dead |
+bool |
+True | +Unknown. | ++ |
challeng_req |
+string |
+True | +Unknown. | ++ |
challenge_tier |
+int |
+True | +Unknown. | ++ |
chance_for_bonus_last_shot_in_clip |
+float |
+True | +Chance to refund a shot while on the last shot in the magazine. | ++ |
charge_allow_melee |
+bool |
+True | +Allows the user to melee in the middle of charging the weapon. | ++ |
charge_allow_midway_charge |
+bool |
+True | +Unknown. | ++ |
charge_cooldown_delay |
+float |
+True | +The time in seconds since last charge before charge begins cooling down. | ++ |
charge_cooldown_time |
+float |
+True | +The time in seconds for charge to cooldown to 0 (from full charge). | ++ |
charge_drain_sound_1p |
+string |
+True | +Sound effect played to the user when charge begins cooling down. | ++ |
charge_drain_sound_3p |
+string |
+True | +Sound effect played to others when charge begins cooling down. | ++ |
charge_drain_sound_seek_to_charge_fraction |
+bool |
+True | +Enables/disables setting the starting point of the charge drain sound effect to match the charge fraction. | ++ |
charge_drain_sound_stop_when_empty |
+bool |
+True | +Enables/disables stopping the charge drain sound effect when charge is empty. | ++ |
charge_effect_1p |
+string |
+True | +The particle effect played to the user when charging. | ++ |
charge_effect_3p |
+string |
+True | +The particle effect played to others when charging. | ++ |
charge_effect_attachment |
+string |
+True | +The weapon part to attach the charge effect to. | +An additional charge effect can be used under charge_effect2 . |
+
charge_effect_show_during_drain |
+bool |
+True | +Determines whether to show the charge effect while cooling down. | ++ |
charge_end_forces_fire |
+bool |
+True | +Forces the weapon to fire once charge is full. | +Always forces fire if charge_is_triggered_by_ADS is false. |
+
charge_full_sound_1p |
+string |
+True | +Sound effect played to the user when charge is full. | ++ |
charge_full_sound_3p |
+string |
+True | +Sound effect played to others when charge is full. | ++ |
charge_is_triggered_by_ADS |
+bool |
+True | +Determines whether charge is triggered by aiming down sights. | +If false, charge is triggered by holding the attack button. If false, appears to break other charge systems, such as sound effects and charge level increased callbacks. |
+
charge_levels |
+int |
+True | +Determines how many equally-spaced charge levels are in the charge (max charge is always the last level). | ++ |
charge_maintained_until_fired |
+bool |
+True | +Unknown. | ++ |
charge_remain_full_when_fired |
+bool |
+True | +Preserves current charge when the weapon is fired. | ++ |
charge_require_input |
+bool |
+True | +Requires the user to hold attack to coninue to charge the weapon. If false, charge continues once triggered. | +Does not work if charge_is_triggered_by_ADS is true. |
+
charge_rumble_max |
+int |
+True | +Controls controller rumble at max charge. | ++ |
charge_rumble_min |
+int |
+True | +Controls controller rumble at minimum charge. | ++ |
charge_sound_1p |
+string |
+True | +Sound effect played to the user when charging begins. | ++ |
charge_sound_3p |
+string |
+True | +Sound effect played to the user when charging begins. | ++ |
charge_sound_seek_to_charge_fraction |
+bool |
+True | +Enables/disables setting the starting point of the charge sound effect to match the charge fraction. | ++ |
charge_sound_stop_when_full |
+bool |
+True | +Enables/disables stopping the charge sound effect when charge is full. | ++ |
charge_time |
+float |
+True | +The time in seconds it takes to charge to full (from empty). | ++ |
charge_wave_max_count |
+int |
+False | +The number of steps charged wave attacks should take. | ++ |
charge_wave_step_dist |
+float |
+False | +The distance each step moves a charged wave attack forward. | ++ |
chargeup_time |
+float |
+False | +Additional time added to the active Core meter to account for charge time. | +Total Core meter time is the sum of this and core_duration . |
+
clear_fx_on_new_view_model |
+bool |
+True | +Unknown. | ++ |
clip_bodygroup |
+string |
+False | +The name of the magazine bodygroup to refer to. | ++ |
clip_bodygroup_index_shown |
+bool |
+False | +Unknown. | ++ |
clip_bodygroup_index_hidden |
+bool |
+False | +Unknown. | ++ |
clip_bodygroup_shown_for_milestone_0 |
+bool |
+False | +Enables/disables showing the body group during the corresponding reload milestone. | +Additional milestone values exist up to clip_bodygroup_shown_for_milestone_4 . |
+
cooldown_time |
+float |
+True | +The time in seconds after the weapon stops firing before it can fire again. | +Unlike fire_rate , does not prevent other actions. |
+
cooldown_viewkick_adsScale |
+float |
+True | +Scalar on cooldown recoil when aiming down sights. | ++ |
cooldown_viewkick_hardScale |
+float |
+True | +Scalar on cooldown hard recoil, which applies recoil instantly. | ++ |
cooldown_viewkick_pitch_base |
+float |
+True | +The base amount of vertical recoil applied when cooldown begins (positive = down). | ++ |
cooldown_viewkick_pitch_random |
+float |
+True | +The maximum absolute value of randomness added to base cooldown vertical recoil. | ++ |
cooldown_viewkick_pitch_random_innerexclude |
+float |
+True | +Unknown. | +The same values exist for yaw (positive = left). | +
cooldown_viewkick_softScale |
+float |
+True | +Scalar on cooldown soft recoil, which applies recoil over time. | ++ |
core_build_time |
+float |
+True | +Unused. The number of seconds to build Core. | ++ |
core_duration |
+float |
+True | +Duration of the Core in seconds. Does not include Core charge up time. | ++ |
critical_hit |
+bool |
+True | +Allows the weapon to land critical hits on heavily armored targets. | ++ |
critical_hit_damage_scale |
+float |
+True | +Scalar on critical hit damage. | ++ |
crosshair_force_sprint_fade_disabled |
+float |
+True | +Prevents the crosshair from fading when sprinting. | ++ |
custom_bool_0 |
+bool |
+True | +Utility value with no specific purpose. | ++ |
custom_float_0 |
+float |
+True | +Utility value with no specific purpose. | ++ |
custom_int_0 |
+int |
+True | +Utility value with no specific purpose. | +These utility values go up to custom_[x]_7 . |
+
damage_additional_bullets |
+int |
+True | +Has no native use. Used by Railgun in script to add damage per charge. | ++ |
damage_additional_bullets_titanarmor |
+int |
+True | +Has no native use. Used by Railgun in script to add heavy armor damage per charge. | ++ |
damage_falloff_type |
+string |
+True | +Determines the class of damage falloff to use. Only used by EVA-8. | ++ |
damage_far_distance |
+float |
+True | +The distance at which the far damage value is reached. | +If headshot_distance is not specified, this also acts as the maximum headshot distance.Controls maximum range for hitscan shotgun attacks. Damage scales linearly between near to far distances and far to very far (if it is used) distances. |
+
damage_far_value |
+float |
+True | +Damage dealt to non-heavily armored targets at damage_far_distance . |
++ |
damage_far_value_titanarmor |
+float |
+True | +Damage dealt to heavily armored targets at damage_far_distance . Optional. |
+If unused, damage_far_value is applied instead. |
+
damage_flags |
+int |
+True | +The damage flags used by the weapon. Only applies by default; flags can be overwritten in script. | ++ |
damage_headshot_scale |
+float |
+True | +Scalar on headshot damage. | ++ |
damage_heavyarmor_nontitan_scale |
+float |
+True | +Unknown. | ++ |
damage_inverse_distance |
+float |
+True | +Distance used alongside inverse damage falloff type. |
++ |
damage_near_distance |
+float |
+True | +The farthest distance at which near damage value is applied. | +Damage scales linearly between near to far distances. | +
damage_near_value |
+float |
+True | +Damage dealt to non-heavily armored targets at or below damage_near_distance . |
++ |
damage_near_value_titanarmor |
+float |
+True | +Damage dealt to heavily armored targets at or below damage_near_distance . Optional. |
+If unused, damage_near_value is applied instead. |
+
damage_rodeo |
+float |
+True | +Damage dealt to the enemy titan during Rodeo. Unused normally. | ++ |
damage_very_far_distance |
+float |
+True | +The distance at which the very far damage value is reached. Optional. | +If unused, damage does not change after far distance. | +
damage_very_far_value |
+float |
+True | +Damage dealt to non-heavily armored targets at or past damage_ver_far_distance . Optional. |
++ |
damage_very_far_value_titanarmor |
+float |
+True | +Damage dealt to heavily armored targets at or past damage_ver_far_distance . Optional. |
+If unused, damage_very_far_value is applied instead. |
+
deploy_time |
+float |
+True | +The time in seconds for the weapon to deploy when swapped to from a main weapon. | ++ |
deploycatch_time |
+float |
+True | +Unknown. | ++ |
deployfirst_time |
+float |
+True | +Unknown. | ++ |
description |
+string |
+True | +Text displayed in weapon flyout descriptions. | ++ |
destroy_on_all_ammo_take |
+bool |
+True | +Unknown. | ++ |
destroy_on_drop |
+bool |
+True | +Destroys the weapon when dropped, preventing pickup. | ++ |
dialog_core_online |
+string |
+False | +Voiceline played when Core is available. | ++ |
dialog_core_activated |
+string |
+False | +Voiceline played when Core is activated. | ++ |
does_not_interrupt_cloak |
+bool |
+False | +Enables/disables not triggering cloak flickering on toss. (Grenades only) | ++ |
dof_nearDepthEnd |
+float |
+True | +Unknown. | ++ |
dof_nearDepthStart |
+float |
+True | +Unknown. | ++ |
dof_zoom_focusArea_Bottom |
+float |
+True | +Unknown. | ++ |
dof_zoom_focusArea_Horizontal |
+float |
+True | +Unknown. | ++ |
dof_zoom_focusArea_Top |
+float |
+True | +Unknown. | ++ |
dof_zoom_nearDepthEnd |
+float |
+True | +Unknown. | ++ |
dof_zoom_nearDepthStart |
+float |
+True | +Unknown. | ++ |
empty_reload_only |
+bool |
+False | +Enables/disables allowing reloads only when there is no ammo in the magazine. | ++ |
enable_highlight_networking_on_creation |
+bool |
+True | +Unknown. | ++ |
explosion_damage |
+int |
+True | +Maximum explosion damage dealt to non-heavily armored targets. | ++ |
explosion_damage_flags |
+int |
+True | +Unused. Likely equivalent to damage_flags , but for explosions. |
++ |
explosion_damage_heavy_armor |
+int |
+True | +Maximum explosion damage dealt to heavily armored targets. Optional. | +If unused, explosion_damage is applied instead. |
+
explosion_damages_owner |
+bool |
+True | +Enables/disables damaging the user with explosions. | ++ |
explosion_inner_radius |
+float |
+True | +The distance within which maximum explosion damage is dealt. Optional. | +If unused, assumed to be 0. | +
explosion_shake_amplitude |
+float |
+True | +Strength of screen shake caused by explosions. | ++ |
explosion_shake_duration |
+float |
+True | +The time in seconds that the explosion screen shake lasts. | ++ |
explosion_shake_frequency |
+float |
+True | +Frequency of the explosion screen shake pattern. | ++ |
explosion_shake_radius |
+float |
+True | +The radius in which the explosion screen shake applies to enemies. | ++ |
explosionradius |
+float |
+True | +The maximum distance within which explosion damage can be dealt. | ++ |
fast_swap_to |
+bool |
+True | +Enables/disables fast swap when swapping to the weapon. | ++ |
fire_anim_rate |
+float |
+False | +Unknown. | ++ |
fire_duration |
+float |
+True | +Duration in seconds that an ability lasts for. Used by offhand weapons. | +Also determines ammo drain time for weapons with ammo_drains_to_empty_on_fire enabled. |
+
fire_mode |
+string |
+True | +Determines weapon attack and holster behavior. | +auto , full-auto : Attacks as long as the trigger is held. (Default)semi-auto : Attacks once per trigger pull.offhand : Swapped to when triggered, attacks as soon as possible, and holsters once fired.offhand_instant : Immediately attacks without being swapped to.offhand_melee : Used by melee weapons.offhand_melee_hybrid : Used by held melee weapons. Triggers a melee attack by attacking with the weapon. |
+
fire_rate |
+float |
+True | +The fire rate of the weapon in attacks per second. | +For burst weapons, determines the fire rate of the burst. For weapons with accelerating fire rate, determines the minimum fire rate. Also prevents other actions (e.g. holstering, melee) until the shot cooldown (1/ fire_rate ) has passed. |
+
fire_rate_max |
+float |
+True | +The maximum fire rate of the weapon in attacks per second. Used for accelerating fire rate. | ++ |
fire_rate_max_time_cooldown |
+float |
+True | +The time in seconds for the fire rate to decrease to minimum. Used for accelerating fire rate. | ++ |
fire_rate_max_time_speedup |
+float |
+True | +The time in seconds for the fire rate to increase to maximum. Used for accelerating fire rate. | ++ |
fire_rate_max_use_ads |
+bool |
+True | +Enables/disables setting fire rate to fire_rate_max when aiming down sights. |
+Disables acceleration defined by fire_rate_max_time_cooldown and fire_rate_max_time_speedup . |
+
fire_rumble |
+string |
+True | +Determines the class used for controller rumble. | ++ |
fire_sound_1 |
+string |
+True | +Deprecated. Sound effect played to user on attack. | ++ |
fire_sound_1_npc |
+string |
+True | +Sound effect played to others on NPC attack. | ++ |
fire_sound_1_player_1p |
+string |
+True | +Sound effect played to user on attack. | ++ |
fire_sound_1_player_3p |
+string |
+True | +Sound effect played to others on attack. | +Additional fire sound effects exist up to fire_sound_3 . |
+
fire_sound_first_shot |
+string |
+True | +Deprecated. Sound effect played for the user on the first attack per trigger pull. | ++ |
fire_sound_first_shot_npc |
+string |
+True | +Deprecated. Sound effect played for others on the first attack per trigger pull done by an NPC. | ++ |
fire_sound_first_shot_player_1p |
+string |
+True | +Deprecated. Sound effect played for the user on the first attack per trigger pull. | ++ |
fire_sound_first_shot_player_3p |
+string |
+True | +Deprecated. Sound effect played for others on the first attack per trigger pull. | +This functionality is done with looping sound effects now. | +
flyoutEnabled |
+bool |
+False | +Enables/disables weapon flyouts for this weapon. | ++ |
fx_muzzle_flash_attach |
+string |
+True | +The weapon part to attach the muzzle flash effect to. | ++ |
fx_muzzle_flash_attach_scoped |
+string |
+True | +The weapon part to attach the muzzle flash effect to while aiming down sights. | ++ |
fx_muzzle_flash_view |
+string |
+True | +The muzzle flash effect shown to the user on attack. | ++ |
fx_muzzle_flash_world |
+string |
+True | +The muzzle flash effect shown to others on attack. | +An additional muzzle flash effect can be used under fx_muzzle_flash2 . |
+
fx_shell_eject_attach |
+string |
+True | +The weapon part to attach the shell eject effect to. | ++ |
fx_shell_eject_attach_scoped |
+string |
+True | +The weapon part to attach the shell eject effect to while aiming down sights. | ++ |
fx_shell_eject_view |
+asset |
+True | +The shell eject effect shown to the user on attack. | ++ |
fx_shell_eject_world |
+asset |
+True | +The shell eject effect shown to others on attack. | +An additional shell eject effect can be used under fx_shell_eject2 . |
+
gamepad_use_yaw_speed_for_pitch_ads |
+bool |
+True | +Uses yaw sensitivity for pitch sensitivity on controllers (normally, pitch is lower). | ++ |
gesture_attack_anim |
+bool |
+False | +Unknown. | ++ |
grapple_maxLength |
+float |
+True | +The maximum horizontal length of grapple (x/y axis). | ++ |
grapple_maxLengthVert |
+float |
+True | +The maximum vertical length of grapple (z axis). | ++ |
grapple_power_required |
+float |
+True | +The minimum amount of power required to use grapple. | ++ |
grapple_power_use_rate |
+float |
+True | +The power drained per second while grapple is in use. | ++ |
grapple_weapon |
+bool |
+True | +Identifies that the weapon is a grapple. | ++ |
grenade_arc_impact_indicator_effect |
+asset |
+True | +The visual effect where the grenade arc indicator touches terrain. | ++ |
grenade_arc_indicator_bounce_count |
+int |
+True | +The number of bounces the grenade arc indicator shows. | ++ |
grenade_arc_indicator_effect |
+asset |
+True | +The particle effect used to create the grenade arc indicator. | ++ |
grenade_arc_indicator_effect_first |
+asset |
+True | +Unknown. | ++ |
grenade_arc_indicator_show_from_hip |
+bool |
+True | +Enables/disables the grenade arc indicator to be shown while not aiming down sights. | ++ |
grenade_bounce_extra_vertical_randomness |
+float |
+True | +Scalar on the maximum random vertical velocity added to grenade velocity on bounce. (Grenades only) | ++ |
grenade_bounce_randomness |
+float |
+True | +Scalar on the maximum random horizontal velocity added to grenade velocity on bounce. (Grenades only) | ++ |
grenade_bounce_vel_frac_along_normal |
+float |
+True | +The fraction of velocity preserved when bouncing off a surface parallel to velocity. (Grenades only) | ++ |
grenade_bounce_vel_frac_shallow |
+float |
+True | +The fraction of velocity preserved when bouncing off a surface at a shallow angle to velocity. (Grenades only) | ++ |
grenade_bounce_vel_frac_sharp |
+float |
+True | +The fraction of velocity preserved when bouncing off a surface at a sharp angle to velocity. (Grenades only) | ++ |
grenade_death_drop_velocity_extraUp |
+float |
+True | +The fraction of velocity applied upward when the grenade is released by dying. (Grenades only) | ++ |
grenade_death_drop_velocity_scale |
+float |
+True | +The fraction of velocity applied forwards (in current direction) when the grenade is released by dying. (Grenades only) | ++ |
grenade_disc_throw |
+bool |
+False | +Unknown. | ++ |
grenade_fuse_time |
+float |
+True | +The time in seconds after being pulled out that a grenade will last before exploding. (Grenades only) | ++ |
grenade_ignition_time |
+float |
+True | +The time in seconds in time after ignition that a grenade will last before expiring. Ignition is triggered in script. (Grenades only) | ++ |
grenade_orient_to_velocity |
+bool |
+True | +Unknown. | ++ |
grenade_radius_horizontal |
+float |
+True | +Horizontal distance a grenade will shift by when bouncing horizontally. (Grenades only) | ++ |
grenade_radius_vertical |
+float |
+True | +Bonus vertical distance on the grenade hitbox. (Grenades only) | ++ |
grenade_roll_vel_frac_per_second |
+float |
+True | +The fraction of velocity preserved per second of rolling. (Grenades only) | ++ |
grenade_show_indicator_to_owner |
+bool |
+False | +Enables/disables the nearby grenade indicator for the user on grenades fired. | ++ |
headshot_distance |
+float |
+False | +The distance within which headshots can occur. Optional. | +If unused, damage_far_distance is applied instead. |
+
hide_holstered_sidearm_when_active |
+bool |
+True | +Hides the holstered weapon while this weapon is active. Primarily used by offhands. | ++ |
holster_time |
+float |
+True | +The time in seconds for the weapon to holster when swapping to a main weapon. | +burst_fire_delay and fire_rate can cause a weapon to take longer to holster than this time. |
+
holster_type |
+string |
+True | +Determines where the weapon is holstered on the player model. | ++ |
holstermodel |
+asset |
+True | +The weapon viewmodel while holstered. | ++ |
hud_icon |
+string |
+True | +The icon shown for the weapon on HUD. | ++ |
idle_sound_player_1p |
+string |
+True | +Sound effect played while the weapon is idle. | ++ |
ignition_distance |
+float |
+True | +The distance at which the missile is ignited and pre_ignition values no longer apply. (Missiles only) |
++ |
ignition_effect |
+float |
+True | +The visual effect played upon ignition. (Missiles only) | ++ |
ignition_sound |
+float |
+True | +The sound effect played upon ignition. (Missiles only) | ++ |
impact_effect_table |
+asset |
+True | +The effect table referenced for collisions. | +Includes both visuals and sound effects. | +
impulse_force |
+int |
+True | +The impulse force applied on damage. | ++ |
impulse_force_explosions |
+int |
+True | +The impulse force applied on explosion damage. (Optional) | +If unused, impulse_force is applied instead. |
+
instant_swap_from |
+bool |
+True | +Instantly swaps when swapping from this weapon to another. | ++ |
instant_swap_to |
+bool |
+True | +Instantly swaps when swapping to this weapon from another. | ++ |
is_burn_mod |
+bool |
+True | +Used to identify Amped attachments and trigger related flags. | ++ |
item_flags |
+int |
+False | +Unknown. | ++ |
leveled_pickup |
+bool |
+False | +Unknown. | ++ |
lifetime_shots_default |
+int |
+True | +Unknown. | ++ |
loadout_child_ref |
+string |
+True | +Unknown. | ++ |
loadout_parent_ref |
+string |
+True | +Unknown. | ++ |
loadout_selectable |
+bool |
+True | +Unknown. | ++ |
loadout_type |
+string |
+True | +Unknown. | ++ |
looping_sounds |
+bool |
+True | +Enables/disables burst_or_looping_fire_sound effects. |
+Does not disable fire_sound effects. |
+
low_ammo_fraction |
+float |
+True | +Unknown. | ++ |
low_ammo_sound_name_1 |
+string |
+True | +Sound effect played to the user on the last shot in the magazine. | +Additional values exist for the i-th to last shot up to low_ammo_sound_name_15 . |
+
low_ammo_sound_range_name_1 |
+string |
+True | +Unknown. | ++ |
low_ammo_sound_range_start_1 |
+int |
+True | +Unknown. | +Additional values for these exist up to low_ammo_sound_range_[x]_3 . |
+
lower_time |
+float |
+True | +The time in seconds it takes to lower the weapon when swapping to an offhand weapon. | ++ |
melee_angle |
+float |
+True | +The angle of the cone used for melee hit detection. | ++ |
melee_anim_1p_number |
+int |
+True | +ID used to determine the first person animation of a melee weapon. | ++ |
melee_anim_3p |
+string |
+True | +Determines the third person animation of a melee weapon. | ++ |
melee_attack_animtime |
+float |
+True | +The time in seconds it takes for the whole first person melee animation to play. | +Also affects the timing of the melee hitbox (both startup and duration). Does not change the length of time the user is locked to their melee weapon. |
+
melee_can_hit_humansized |
+bool |
+True | +Enables/disables the melee weapon to hit non-heavily armored targets. | ++ |
melee_can_hit_titans |
+bool |
+True | +Enables/disables the melee weapon to hit heavily armored targets. | ++ |
melee_damage |
+int |
+True | +Melee damage dealt to non-heavily armored targets. | ++ |
melee_damage_heavyarmor |
+int |
+True | +Melee damage dealt to heavily armored targets. | ++ |
melee_freezelook_on_hit |
+float |
+True | +Unknown. | ++ |
melee_lunge_target_angle |
+float |
+True | +The angle of the cone used for melee lunge. | ++ |
melee_lunge_target_range |
+float |
+True | +The range of the cone used for melee lunge. | ++ |
melee_lunge_time |
+float |
+True | +The time in seconds a melee lunge takes to complete its travel. | ++ |
melee_raise_recovery_animtime_normal |
+float |
+True | +Scalar on the time it takes to raise the held weapon after a melee. | ++ |
melee_raise_recovery_animtime_quick |
+float |
+True | +Unknown. | ++ |
melee_range |
+float |
+True | +The range of the cone used for melee hit detection. | ++ |
melee_respect_next_attack_time |
+bool |
+True | +Unknown. | ++ |
melee_rumble_on_hit |
+string |
+True | +Determines the class used for controller rumble on melee hit. | ++ |
melee_rumble_on_hit_partial |
+string |
+True | +Unknown. | ++ |
melee_sound_attack_1p |
+string |
+True | +Sound effect played to the user on melee attack. | ++ |
melee_sound_attack_3p |
+string |
+True | +Sound effect played to others on melee attack. | ++ |
menu_alt_icon |
+string |
+True | +Unknown. | ++ |
menu_icon |
+string |
+True | +Icon shown for this weapon in menus (e.g. loadout screen). | ++ |
menu_image |
+string |
+True | +Unknown. | ++ |
minimap_reveal_distance |
+float |
+True | +Unknown. | ++ |
mod_description |
+string |
+True | +Unknown. | ++ |
mod_print_name |
+string |
+True | +Unknown. | ++ |
mod_short_print_name |
+string |
+True | +Unknown. | ++ |
move_speed_modifier |
+float |
+True | +Scalar on user movement speed. | ++ |
move_speed_modifier_when_out_of_ammo |
+float |
+True | +Scalar on user movement speed while the weapon is out of ammo (both stockpile and magazine). | ++ |
net_bullet_fix |
+bool |
+True | +Unknown. | ++ |
net_optimize |
+bool |
+False | +Used to improve performance of hitscans. Should always be true for hitscan weapons. | +Projectiles fired with this on will have no third person sound effect and will not ping radar. | +
never_drop |
+bool |
+True | +Makes the weapon unable to be dropped. | ++ |
npc_accuracy_multiplier_heavy_armor |
+float |
+True | +Scalar on NPC accuracy when targeting a heavily armored target. (Higher = better) | ++ |
npc_accuracy_multiplier_npc |
+float |
+True | +Scalar on NPC accuracy when targeting a non-heavily armored NPC. (Higher = better) | ++ |
npc_accuracy_multiplier_pilot |
+float |
+True | +Scalar on NPC accuracy when targeting a non-heavily armored player. (Higher = better) | ++ |
npc_aim_at_feet |
+bool |
+True | +Enables/disables NPCs aiming at the feet of targets. | ++ |
npc_aim_at_feet_vs_heavy_armor |
+bool |
+True | +Enables/disables NPCs aiming at the feet of heavily armored targets. | ++ |
npc_attack_cone_angle |
+float |
+True | +Unknown. | ++ |
npc_burst_secondary |
+int |
+True | +Unknown. | ++ |
npc_charge_time_max |
+float |
+True | +Maximum time in seconds that the NPC will charge the weapon for. | ++ |
npc_charge_time_min |
+float |
+True | +Minimum time in seconds that the NPC will charge the weapon for. | ++ |
npc_clear_charge_if_not_fired |
+bool |
+True | +Enables/disables clearing weapon charge for NPCs if they do not fire when it finishes. | ++ |
npc_damage_far_distance |
+float |
+True | +The distance at which the far damage value is reached for NPCs. Optional. | +Controls maximum range for hitscan shotgun attacks. Damage scales linearly between near to far distances and far to very far (if it is used) distances. |
+
npc_damage_far_value |
+float |
+True | +Damage dealt to non-heavily armored targets at npc_damage_far_distance for NPCs. Optional. |
++ |
npc_damage_far_value_titanarmor |
+float |
+True | +Damage dealt to heavily armored targets at npc_damage_far_distance for NPCs. Optional. |
++ |
npc_damage_near_distance |
+float |
+True | +The farthest distance at which near damage value is applied for NPCs. Optional. | +Damage scales linearly between near to far distances. | +
npc_damage_near_value |
+float |
+True | +Damage dealt to non-heavily armored targets at or below npc_damage_near_distance for NPCs. Optional |
++ |
npc_damage_near_value_titanarmor |
+float |
+True | +Damage dealt to heavily armored targets at or below npc_damage_near_distance for NPCs. Optional. |
++ |
npc_damage_very_far_distance |
+float |
+True | +The distance at which the very far damage value is reached for NPCs. Optional. | ++ |
npc_damage_very_far_value |
+float |
+True | +Damage dealt to non-heavily armored targets at or past npc_damage_ver_far_distance for NPCs. Optional. |
++ |
npc_damage_very_far_value_titanarmor |
+float |
+True | +Damage dealt to heavily armored targets at or past npc_damage_ver_far_distance . Optional. |
+For each npc_damage value, if it is unused, the corresponding player value is applied instead. |
+
npc_dangerous_to_heavy_armor |
+bool |
+True | +Unknown. | ++ |
npc_dangerous_to_normal_armor |
+bool |
+True | +Unknown. | ++ |
npc_directed_fire_ang_limit_cos |
+float |
+True | +Unknown. | ++ |
npc_explosion_damage |
+int |
+True | +Maximum explosion damage dealt to non-heavily armored targets for NPCs. | ++ |
npc_explosion_damage_heavy_armor |
+int |
+True | +Maximum explosion damage dealt to heavily armored targets for NPCs. | +Unlike npc_damage_ values, npc_explosion_damage values are set to 0 if unused. |
+
npc_fire_at_enemy_defense_time |
+float |
+True | +The time in seconds that an NPC will fire at a defensive before holding their shots. | ++ |
npc_full_auto_vs_heavy_armor |
+bool |
+True | +Unknown. | ++ |
npc_lead_time_max_dist |
+float |
+True | +Unknown. | ++ |
npc_lead_time_min_dist |
+float |
+True | +Unknown. | ++ |
npc_lead_time_scale |
+float |
+True | +Unknown. | ++ |
npc_max_burst |
+int |
+True | +The maximum number of shots an NPC will fire in a burst. Weapon need not be a burst weapon. | ++ |
npc_max_engage_range |
+float |
+True | +The maximum range within which an NPC will move towards a non-heavily armored target. | ++ |
npc_max_engage_range_heavy_armor |
+float |
+True | +The maximum range within which an NPC will move towards a heavily armored target. | ++ |
npc_max_range |
+float |
+True | +The maximum range within which an NPC will attack a target. | ++ |
npc_max_range_secondary |
+float |
+True | +Unknown. | ++ |
npc_min_burst |
+int |
+True | +The minimum number of shots an NPC will fire in a burst. Weapon need not be a burst weapon. | ++ |
npc_min_engage_range |
+float |
+True | +The range within which an NPC will stop moving towards a non-heavily armored target. | ++ |
npc_min_engage_range_heavy_armor |
+float |
+True | +The range within which an NPC will stop moving towards a heavily armored target. | ++ |
npc_min_range |
+float |
+True | +The minimum range before an NPC will attack a target. | ++ |
npc_min_range_secondary |
+float |
+True | +Unknown. | ++ |
npc_miss_fast_player |
+bool |
+True | +Unknown. | ++ |
npc_pre_fire_delay |
+float |
+True | +Time in seconds before an NPC can fire the weapon once a target is chosen. | ++ |
npc_pre_fire_delay_interval |
+float |
+True | +Time in seconds before npc_pre_fire_delay triggers again. |
++ |
npc_reload_enabled |
+bool |
+True | +Allows NPCs to reload the weapon. Optional. | +If unused, reload_enabled is applied instead. |
+
npc_rest_time_between_bursts_expedite |
+float |
+True | +Time in seconds before an NPC will fire another burst if staggered during rest time. | ++ |
npc_rest_time_between_bursts_max |
+float |
+True | +Maximum time in seconds before an NPC will fire another burst. | ++ |
npc_rest_time_between_bursts_min |
+float |
+True | +Minimum time in seconds before an NPC will fire another burst. | ++ |
npc_rest_time_secondary |
+float |
+True | +Unknown. | ++ |
npc_self_explosion_safety |
+bool |
+True | +Unknown. | ++ |
npc_spread_cone_focus_time |
+float |
+True | +Unknown. | ++ |
npc_spread_defocused_cone_multiplier |
+float |
+True | +Unknown. | ++ |
npc_spread_pattern_focus_time |
+float |
+True | +Unknown. | ++ |
npc_spread_pattern_not_in_fov_factor |
+float |
+True | +Unknown. | ++ |
npc_spread_pattern_not_in_fov_time |
+float |
+True | +Unknown. | ++ |
npc_suppress_lsp_allowed |
+bool |
+True | +Unknown. | ++ |
npc_titan_ability |
+string |
+False | +Unknown. | ++ |
npc_use_ads_move_speed_scale |
+bool |
+True | +Unknown. | ++ |
npc_use_check_type |
+string |
+False | +Unknown. | ++ |
npc_use_long_duration |
+float |
+True | +Unknown. | ++ |
npc_use_max_damage |
+float |
+True | +Maximum amount of damage taken before an NPC will use this weapon. | ++ |
npc_use_min_damage |
+float |
+True | +Minimum amount of damage taken before an NPC will use this weapon. | ++ |
npc_use_min_projectile_damage |
+float |
+True | +Minimum amount of damage taken from projectiles before an NPC will use this weapon. | ++ |
npc_use_normal_duration |
+float |
+False | +Time in seconds that an NPC will use this weapon. | ++ |
npc_use_short_duration |
+float |
+True | +Unknown. | ++ |
npc_use_strict_muzzle_dir |
+bool |
+True | +Unknown. | ++ |
npc_vortex_block |
+bool |
+True | +Unknown. | ++ |
offhand_blocks_sprint |
+bool |
+True | +Prevents the user from sprinting while this offhand weapon is in use. | ++ |
offhand_default_inventory_slot |
+int |
+True | +Determines the inventory slot this offhand is inserted into with the give cheat command. |
++ |
offhand_hold_enabled |
+bool |
+True | +Enables/disables holding the offhand weapon while it is pressed and attacking on release. | ++ |
offhand_interupts_weapon_anims |
+bool |
+True | +Enables/disables interrupting any currently active weapon and immediately starting deployment when activating the weapon. | ++ |
offhand_switch_force_draw |
+bool |
+True | +Unknown. | ++ |
offhand_transition_has_attach_detach_anim_events |
+bool |
+True | +Unknown. | ++ |
OnClientAnimEvent |
+void functionref( entity weapon, string name ) |
+False | +Unknown. (Client only) | ++ |
OnProjectileCollision |
+void functionref( entity projectile, vector pos, vector normal, entity hitEnt, int hitbox, bool isCritical ) |
+False | +Callback ran when projectiles collide with terrain or entities. | ++ |
OnProjectileIgnite |
+void functionref( entity projectile ) |
+False | +Callback ran when grenades ignite. | ++ |
OnWeaponActivate |
+void functionref( entity weapon ) |
+False | +Callback ran when the weapon becomes active (e.g. when the crosshair becomes visible). | ++ |
OnWeaponAttemptOffhandSwitch |
+bool functionref( entity weapon ) |
+False | +Callback ran when attempting to switch to an offhand weapon. Returns true if successful or false otherwise. | ++ |
OnWeaponChargeBegin |
+bool functionref( entity weapon ) |
+False | +Callback ran when charging begins. Returns true if successful or returns false otherwise and denies the charge. | ++ |
OnWeaponChargeEnd |
+void functionref( entity weapon ) |
+False | +Callback ran when charging ends. | ++ |
OnWeaponChargeLevelIncreased |
+bool functionref( entity weapon ) |
+False | +Callback ran when charge level increases. Returns true. False conditions unknown. | ++ |
OnWeaponCustomActivityStart |
+void functionref( entity weapon ) |
+False | +Unknown. | ++ |
OnWeaponDeactivate |
+void functionref( entity weapon ) |
+False | +Callback ran when the weapon becomes inactive (e.g. when the crosshair disappears) | ++ |
OnWeaponNpcPreAttack |
+void functionref( entity weapon ) |
+False | +Callback ran when an NPC begins the pre-fire delay. (Server only) | ++ |
OnWeaponNpcPrimaryAttack |
+var functionref( entity weapon, WeaponPrimaryAttackParams attackParams ) |
+False | +Callback ran when the weapon attacks from an NPC. Returns ammo cost if successful or 0 if not. (Server only) | ++ |
OnWeaponOwnerChanged |
+void functionref( entity weapon, WeaponOwnerChangedParams changeParams ) |
+False | +Callback ran when the user changes. | ++ |
OnWeaponPrimaryAttack |
+var functionref( entity weapon, WeaponPrimaryAttackParams attackParams ) |
+False | +Callback ran when the weapon attacks. Returns ammo cost if successful or 0 if not. | ++ |
OnWeaponPrimaryAttackAnimEvent |
+var functionref( entity weapon, WeaponPrimaryAttackParams attackParams ) |
+False | +Callback ran at a specific point in the weapon's attack animation. Returns ammo cost if successful or 0 if not. | ++ |
OnWeaponReadyToFire |
+void functionref( entity weapon ) |
+False | +Callback ran when the weapon is ready to fire. | ++ |
OnWeaponReload |
+void functionref( entity weapon, int milestone ) |
+False | +Callback ran when reload begins. Can have a nonzero milestone if reload was previously interrupted. | ++ |
OnWeaponStartZoomIn |
+void functionref( entity weapon ) |
+False | +Callback ran when the user starts aiming down sights. | ++ |
OnWeaponStartZoomOut |
+void functionref( entity weapon ) |
+False | +Callback ran when the user stops aiming down sights. | ++ |
OnWeaponSustainedDischargeBegin |
+bool functionref( entity weapon ) |
+False | +Callback ran when sustained discharge begins. Returns true if successful or returns false otherwise and denies the sustained discharge. | ++ |
OnWeaponSustainedDischargePulse |
+bool functionref( entity weapon, WeaponPrimaryAttackParams attackParams ) |
+False | +Callback ran when the sustained discharge pulses. Returns true if successful or false otherwise and stops the sustained discharge. | +Only used when sustained_discharge_want_pulse_callbacks is enabled. |
+
OnWeaponSustainedDischargeEnd |
+void functionref( entity weapon ) |
+False | +Callback ran when sustained discharge emds. | ++ |
OnWeaponTossCancelDrop |
+void functionref( entity weapon, WeaponPrimaryAttackParams attackParams ) |
+False | +Callback ran when a toss-able object is dropped by swapping. | ++ |
OnWeaponTossPrep |
+void functionref( entity weapon, WeaponTossPrepParams prepParams ) |
+False | +Callback ran when a toss-able object is pulled out. | ++ |
OnWeaponTossReleaseAnimEvent |
+void functionref( entity weapon, WeaponPrimaryAttackParams attackParams ) |
+False | +Callback ran when a toss-able object is released. | ++ |
OnWeaponVortexHitBullet |
+bool functionref( entity weapon, entity vortexSphere, var damageInfo ) |
+False | +Callback ran when a vortex weapon is hit by a hitscan. Returns true if successful and deletes the bullet or false otherwise. | ++ |
OnWeaponVortexHitProjectile |
+bool functionref( entity weapon, entity vortexSphere, entity attacker, entity projectile, vector contactPos ) |
+False | +Callback ran when a vortex weapon is hit by a projectile. Returns true if successful and deletes the projectile or false otherwise. | ++ |
ordnance_crosshair_always_on_start_index |
+int |
+True | +The index of a crosshair in the weapon's crosshair list to always have on. Used to show cooldowns as Pilot. | ++ |
pass_through_damage_preserved_scale |
+float |
+True | +Scalar on damage dealt to following targets after the shot pierces through a target. | ++ |
pass_through_depth |
+float |
+True | +The total amount of depth a shot can pierce through, inclusive. | ++ |
passive |
+string |
+False | +Determines what passive to give when the Core weapon starts attacking. | +Only works for PAS_FUSION_CORE , PAS_SHIELD_BOOST , PAS_BERSERKER , PAS_SHIFT_CORE , and PAS_SMART_CORE . |
+
pickup_hold_prompt |
+string |
+False | +Unknown. | ++ |
pickup_press_prompt |
+string |
+False | +Unknown. | ++ |
play_offhand_charging_anim |
+bool |
+False | +Enables/disables the standard charging animation of an offhand weapon for others. | ++ |
play_offhand_fire_anim |
+bool |
+False | +Unknown. | ++ |
play_offhand_start_end_anim |
+bool |
+False | +Unknown. | ++ |
playermodel |
+asset |
+True | +Model of the weapon shown to others when held. | ++ |
pre_ignition_damage |
+int |
+True | +Damage dealt to non-heavily armored targets before ignition. (Missiles only) | ++ |
pre_ignition_damage_titanarmor |
+int |
+True | +Damage dealt to heavily armored targets before ignition. (Missiles only) | ++ |
pre_ignition_flight_sound |
+string |
+True | +Sound effect played on missiles in flight before ignition. (Missiles only) | ++ |
pre_ignition_impact_effect_table |
+asset |
+True | +The effect table referenced for collisions before ignition. (Missiles only) | +Includes both visuals and sound effects. | +
pre_ignition_npc_damage |
+int |
+True | +Damage dealt to non-heavily armored targets before ignition for NPCs. Optional. (Missiles only) | +If unusued, pre_ignition_damage applies instead. |
+
pre_ignition_npc_damage_titanarmor |
+int |
+True | +Damage dealt to heavily armored targets before ignition for NPCs. Optional. (Missiles only) | +If unusued, pre_ignition_damage_titanarmor applies instead. |
+
pre_ignition_speed |
+float |
+True | +Speed of the missile before ignition. (Missiles only) | ++ |
pre_ignition_trail_effect |
+string |
+True | +Trail effect shown on the missile before ignition. (Missiles only) | ++ |
primary_fire_does_not_block_sprint |
+bool |
+True | +Allows the main weapon to fire while sprinting. | ++ |
printname |
+string |
+True | +Name displayed on the weapon selection screen in loadout creation. | ++ |
proficiency_average_additional_rest |
+float |
+True | +Additional rest time between uses for NPCs with average proficiency. | ++ |
proficiency_average_bias |
+float |
+True | +Unknown. | ++ |
proficiency_average_spreadscale |
+float |
+True | +Scalar on spread for NPCs with average proficiency. | +Additional values exist for poor , good , very_good , and perfect proficiencies. |
+
projectile_airburst_on_death |
+bool |
+True | +Unknown. | ++ |
projectile_adjust_to_gun_barrel |
+bool |
+False | +Adjusts projectiles to visually launch from the weapon's barrel. | ++ |
projectile_chasecamDistanceMax |
+float |
+True | +Unknown. | ++ |
projectile_chasecamMaxOrbitDepth |
+float |
+True | +Unknown. | ++ |
projectile_chasecamMaxPitchUp |
+float |
+True | +Unknown. | ++ |
projectile_chasecamOffsetForward |
+float |
+True | +Forward vector offset of the camera from the projectile in killcams following it. | ++ |
projectile_chasecamOffsetRight |
+float |
+True | +Right vector offset of the camera from the projectile in killcams following it. | ++ |
projectile_chasecamOffsetUp |
+float |
+True | +Up vector offset of the camera from the projectile in killcams following it. | ++ |
projectile_collide_with_owner |
+bool |
+True | +Unknown. | ++ |
projectile_collide_with_owner_grace_time |
+float |
+True | +Unknown. | ++ |
projectile_damage_reduction_per_bounce |
+float |
+True | +Flat damage reduction per bounce. | +Unaffected by normal damage falloff. | +
projectile_damages_owner |
+bool |
+True | +Enables/disables direct hit damage to the user from projectiles fired. | ++ |
projectile_death_sound |
+string |
+True | +Unknown. | ++ |
projectile_do_predict_impact_effects |
+bool |
+True | +Unknown. | ++ |
projectile_drift_intensity |
+float |
+True | +Unknown. | ++ |
projectile_drift_windiness |
+float |
+True | +Unknown. | ++ |
projectile_first_person_offset_fraction |
+float |
+True | +Adjusts how far projectiles fired travel at an offset angle before straightening in first person. | ++ |
projectile_flight_sound |
+string |
+True | +Sound effect played on projectiles in flight. | ++ |
projectile_gravity_scale |
+float |
+True | +Scalar on gravity applied to projectiles. | ++ |
projectile_ignore_owner_damage |
+bool |
+False | +Enables/disables owner damage to projectiles fired if they can take damage. | ++ |
projectile_ignores_vortex |
+string |
+False | +Determines the bounce behavior of projectiles upon hitting vortex entities and prevents them from being deleted. | +Held vortexes (i.e. Vortex and Thermal Shield) only check for fall_vortex and still delete projectiles with other values.mirror : Reverses horizontal velocity. (< -1, -1, 1 > )drop : Reverses and reduces horizontal velocity by 75% and sets vertical velocity to 0. (< -0.25, -0.25, 0 > )fall , fall_vortex : Reverses and reduces all velocity by 75%. (< -0.25, -0.25, -0.25 > ) |
+
projectile_inherit_owner_velocity_scale |
+float |
+True | +Scalar on how much of the user's velocity is applied to the projectile when fired. | ++ |
projectile_killreplay_enabled |
+bool |
+True | +Allows killcams to follow the projectile that dealt the kill. | ++ |
projectile_launch_pitch_offset |
+float |
+True | +Pitch offset projectiles are fired at (positive = up). | ++ |
projectile_launch_speed |
+float |
+True | +Base speed of projectiles fired. | ++ |
projectile_lifetime |
+float |
+True | +The time in seconds projectiles last before disappearing. | ++ |
projectile_max_deployed |
+int |
+True | +The maximum number of projectiles that this weapon can have existing at once. Only works for weapons with tracked projectiles in script. | ++ |
projectile_ricochet_max_count |
+int |
+True | +The maximum number of ricochets projectiles can have before disappearing on terrain collision. | ++ |
projectile_speed_reduction_factor |
+float |
+True | +The fraction of speed lost by projectiles on ricochet. | ++ |
projectile_straight_radius_max |
+float |
+True | +Unknown. | ++ |
projectile_straight_radius_min |
+float |
+True | +Unknown. | ++ |
projectile_straight_time_max |
+float |
+True | +Unknown. | ++ |
projectile_straight_time_min |
+float |
+True | +Unknown. | ++ |
projectile_trail_effect_0 |
+asset |
+True | +Trail effect shown on projectiles. | +Additional trail effects exist up to projectile_trail_effect_4 . |
+
projectile_visible_to_smart_ammo |
+bool |
+True | +Allows projectiles to be targeted by smart weapons. | ++ |
projectiles_per_shot |
+int |
+True | +Has no native use. Used in script by some weapons to determine the number of projectiles fired per shot. | ++ |
raise_from_sprint_time |
+float |
+True | +The time in seconds of the weapon's raise animation after sprinting. | +Not the time before firing is allowed. Animations can allow firing sooner at various fractions of the full raise time. | +
raise_time |
+float |
+True | +The time in seconds to raise the weapon when swapping from an offhand weapon. | +Does not include the time to raise the weapon after melee. Not the time before firing is allowed. Animations can allow firing sooner at various fractions of the full raise time. |
+
readyhint |
+string |
+True | +Usage hint text shown to the user in reminders. Used by Cores and inventory offhands (e.g. e-smoke, boosts). | ++ |
readymessage |
+string |
+True | +Large text shown to the user in reminders. Used by Cores and inventory offhands (e.g. e-smoke, boosts). | ++ |
rechamber_time |
+float |
+True | +The time in seconds to rechamber another round. Use by bolt-action weapons. | +Rechambering only begins after other delays (e.g. fire_rate , burst_fire_delay ) are finished. |
+
red_crosshair_range |
+float |
+True | +The range within which the crosshair will turn red when aiming at an enemy. | ++ |
regen_ammo_refill_rate |
+float |
+True | +The rate in ammo per second at which ammo is regenerated in the magazine. | ++ |
regen_ammo_refill_start_delay |
+float |
+True | +The time in seconds since last shot fired before ammo regeneration begins. | ++ |
regen_ammo_sound_range_name_1 |
+string |
+True | +Unknown. | ++ |
regen_ammo_sound_range_start_1 |
+int |
+True | +Unknown. | +Additional values exist up to regen_ammo_sound_range_[x]_3 . |
+
regen_ammo_stockpile_drain_rate_when_charging |
+float |
+True | +Unknown. | ++ |
regen_ammo_stockpile_max_fraction |
+float |
+True | +Unknown. | ++ |
regen_ammo_while_firing |
+bool |
+True | +Enables/disables incurring regen_ammo_refill_start_delay when firing a shot. |
++ |
reload_alt_anim |
+bool |
+True | +Enables/disables a flag on certain weapons to use an alternate reload animation. | ++ |
reload_enabled |
+bool |
+True | +Allows the weapon to be reloaded. | ++ |
reload_is_segmented |
+bool |
+True | +Enables/disables segmented reloads for the weapon. | +Segmented reloads do not have reload milestones. | +
reload_no_auto_if_ads_pressed |
+bool |
+True | +Disables automatic reloads when the magazine is empty while aiming down sights. | ++ |
reload_time |
+float |
+True | +The time in seconds to complete the reload animation when reloading from a non-empty magazine. | +For segmented reloads, this includes the time to start the reloading animation and load one segment. | +
reload_time_late1 |
+float |
+True | +The time in seconds to complete the non-empty reload animation from the first reload milestone. | +Additional values exist for the i-th reload milestone up to reload_time_late5 . |
+
reloadempty_time |
+float |
+True | +The time in seconds to complete the reload animation when reloading from an empty magazine. | +For segmented reloads, this includes the time to start the reloading animation and reload one segment. | +
reloadempty_time_late1 |
+float |
+True | +The time in seconds to complete the empty reload animation from the first reload milestone. | +Additional values exist for the i-th reload milestone up to reloadempty_time_late5 . |
+
reloadsegment_time_end |
+float |
+True | +The time in seconds to complete the end animation of a segmented non-empty reload. | +The end segment does not prevent any actions and ends on sprint. | +
reloadsegment_time_loop |
+float |
+True | +The time in seconds to complete one segment reload animation. | ++ |
reloadsegmentempty_time_end |
+float |
+True | +The time in seconds to complete the end animation of a segmented empty reload. | +The end segment does not prevent any actions and ends on sprint. | +
rui_crosshair_index |
+int |
+True | +The index of a crosshair in the weapon's crosshair list to display. | ++ |
rumble |
+int |
+False | +Unknown. | ++ |
scripted_projectile_max_timestep |
+float |
+False | +Unknown. | ++ |
shared_energy_charge_cost |
+int |
+True | +Amount of shared energy consumed per frame (60 times per second) while charging. | ++ |
shared_energy_cost |
+int |
+True | +Amount of shared energy consumed on attack. | ++ |
shortprintname |
+string |
+True | +Name displayed for the weapon in most places (e.g. loadout screen, weapon flyouts, pickups). | ++ |
show_grenade_indicator |
+bool |
+False | +Disables the nearby grenade indicator shown to nearby targets on grenades fired. (Grenades only) | ++ |
show_pre_modded_tracer |
+bool |
+True | +Unknown. | ++ |
silenced |
+bool |
+True | +Unknown. | ++ |
smart_ammo_active_shot_damage_multiplier |
+float |
+True | +Unknown. | ++ |
smart_ammo_active_shot_on_first_lock_only |
+bool |
+True | +Unknown. | ++ |
smart_ammo_active_shot_time |
+float |
+True | +Unknown. | ++ |
smart_ammo_alert_npc_fraction |
+float |
+True | +The fraction of lock on at which NPCs will be alerted of the lock. | ++ |
smart_ammo_allow_ads_lock |
+bool |
+True | +Enables/disables smart ammo to lock on while aiming down sights. | ++ |
smart_ammo_allow_hip_fire_lock |
+bool |
+True | +Enables/disables smart ammo to lock on while hipfiring. | ++ |
smart_ammo_allow_search_while_firing |
+bool |
+True | +Allows smart ammo to lock on while firing. | ++ |
smart_ammo_allow_search_while_inactive |
+bool |
+True | +Allows smart ammo to lock on while the weapon is not held. | ++ |
smart_ammo_alt_lock_style |
+bool |
+True | +Enables/disables limiting total locks by charge amount (higher = less locks). | ++ |
smart_ammo_always_do_burst |
+bool |
+True | +Enables/disables always firing the maximum number of attacks per target. | ++ |
smart_ammo_apply_new_target_delay_to_first_target |
+bool |
+True | +Unknown. | ++ |
smart_ammo_bounds_search_tick_interval |
+int |
+False | +Unknown. | ++ |
smart_ammo_draw_acquisition_lines |
+bool |
+True | +Enables/disables displaying lines to target locks on the HUD. | ++ |
smart_ammo_hold_and_reset_after_all_locks |
+float |
+True | +Unknown. | ++ |
smart_ammo_hud_lock_style |
+string |
+True | +Determines the type of lock displayed. | ++ |
smart_ammo_hud_type |
+string |
+True | +Determines the smart ammo layout displayed on the HUD. | ++ |
smart_ammo_humans_only |
+bool |
+False | +Enables/disables targeting only humanoids. | ++ |
smart_ammo_lock_effect_1p |
+asset |
+True | +Visual effect displayed on the weapon to the user while acquiring locks. | ++ |
smart_ammo_lock_effect_3p |
+asset |
+True | +Visual effect displayed on the weapon to others while acquiring locks. | ++ |
smart_ammo_lock_effect_attachment |
+string |
+True | +The weapon part to attach the lock visual effect to. | ++ |
smart_ammo_lock_type |
+string |
+True | +Determines which target types the smart ammo can lock. | +small : Non-heavily armored targets.large : Heavily armored targets.any : Any target. |
+
smart_ammo_looping_sound_acquiring |
+string |
+True | +Sound effect played while smart ammo is acquiring locks. | ++ |
smart_ammo_looping_sound_locked |
+string |
+True | +Sound effect played while smart ammo is locked on to a target. | ++ |
smart_ammo_max_targeted_burst |
+int |
+True | +The maximum burst size fired at any individual target. | ++ |
smart_ammo_max_targets |
+int |
+True | +The maximum number of targets that can be locked simultaneously. | ++ |
smart_ammo_max_trackers_per_target |
+int |
+True | +The maximum number of locks per target. | ++ |
smart_ammo_new_target_delay |
+float |
+True | +Time in seconds before the smart weapon can start acquiring locks. | +Time starts from the moment the weapon is swapped to, even if it can't acquire locks due to deploy/raise time. | +
smart_ammo_npc_targets_must_be_tracked |
+bool |
+True | +Requires targets to be locked for NPCs to fire the weapon. | ++ |
smart_ammo_only_search_on_charge |
+bool |
+True | +Enables/disables limiting lock acquisition to only while the weapon is charging. | ++ |
smart_ammo_other_targets_must_be_tracked |
+bool |
+True | +Unknown. | ++ |
smart_ammo_player_targets_must_be_tracked |
+bool |
+True | +Requires targets to be locked for players to fire the weapon. | ++ |
smart_ammo_points_search_tick_interval |
+int |
+False | +Unknown. | ++ |
smart_ammo_search_angle |
+float |
+True | +The angle of the smart ammo search cone. | ++ |
smart_ammo_search_distance |
+float |
+True | +The range of the smart ammo search cone. | ++ |
smart_ammo_search_enemy_team |
+bool |
+True | +Unknown. | ++ |
smart_ammo_search_friendly_team |
+bool |
+True | +Unknown. | ++ |
smart_ammo_search_neutral_team |
+bool |
+True | +Unknown. | ++ |
smart_ammo_search_npcs |
+bool |
+True | +Enables/disables smart ammo searching for NPC targets. | ++ |
smart_ammo_search_phase_shift |
+bool |
+True | +Unknown. | ++ |
smart_ammo_search_players |
+bool |
+True | +Enables/disables smart ammo searching for player targets. | ++ |
smart_ammo_stick_to_fully_locked_targets |
+bool |
+True | +Unknown. | ++ |
smart_ammo_target_confirmed_sound |
+string |
+True | +Sound effect played when a target is fully locked. | ++ |
smart_ammo_target_confirming_sound |
+string |
+True | +Sound effect played when a lock starts to be acquired. | ++ |
smart_ammo_target_found_sound |
+string |
+True | +Sound effect played when a new target enters the search cone. | ++ |
smart_ammo_target_lost_sound |
+string |
+True | +Sound effect played when locks on a target are lost. | ++ |
smart_ammo_target_max_locks_heavy |
+int |
+True | +The maximum number of locks on a heavily armored target. | ++ |
smart_ammo_target_max_locks_normal |
+int |
+True | +The maximum number of locks on a non-heavily armored target. | +If unused, acquires locks to deal enough damage to kill the target in a single burst (up to general maximum locks). | +
smart_ammo_target_npc_lock_factor |
+float |
+True | +Unknown. | ++ |
smart_ammo_targeting_time_max |
+float |
+True | +The maximum time in seconds it takes to acquire a lock. | ++ |
smart_ammo_targeting_time_max_npc |
+float |
+True | +The maximum time in seconds it takes to acquire a lock on an NPC. | ++ |
smart_ammo_targeting_time_min |
+float |
+True | +The minimum time in seconds it takes to acquire a lock. | ++ |
smart_ammo_targeting_time_min_npc |
+float |
+True | +The minimum time in seconds it takes to acquire a lock on an NPC. | ++ |
smart_ammo_targeting_time_modifier_cloaked |
+float |
+True | +Scalar on lock on time when locking a cloaked target. | ++ |
smart_ammo_targeting_time_modifier_projectile |
+float |
+True | +Scalar on lock on time when locking projectiles. | ++ |
smart_ammo_targeting_time_modifier_projectile_owner |
+float |
+True | +Scalar on lock on time when locking projectiles from the user. | ++ |
smart_ammo_titan_lock_point0 |
+string |
+False | +Unknown. | +Additional values for lock point exist up to smart_ammo_titan_lock_point7 . |
+
smart_ammo_titans_block_los |
+bool |
+False | +Enables/disables titans blocking LoS for acquiring locks on other targets. | ++ |
smart_ammo_track_cloaked_targets |
+bool |
+True | +Allows smart ammo to lock on to cloaked targets. | ++ |
smart_ammo_tracked_targets_check_visibility |
+bool |
+True | +Unknown. | ++ |
smart_ammo_tracker_status_effects |
+bool |
+True | +Unknown. | ++ |
smart_ammo_unlock_debounce_time |
+float |
+True | +The time in seconds that locks on targets outside the search cone will be retained for before disappearing. | +Does not prevent locks from being cleared by looking too far away from the target. | +
smart_ammo_weapon_type |
+<special> |
+True | +Determines the type of smart ammo fired. | +If unused, defaults to hitscan bullets. The only other class is homing_missile . |
+
sound_dryfire |
+string |
+True | +Sound effect played on offhand weapons when an attempted use or swap fails. | ++ |
sound_pickup |
+string |
+True | +Sound effect played when picking up the weapon. | ++ |
sound_trigger_pull |
+string |
+False | +Unknown. | ++ |
sound_weapon_ready |
+string |
+True | +Sound effect played on offhand weapons when they have reached the next ammo_min_to_fire multiple. |
++ |
special_3p_attack_anim |
+bool |
+False | +Enables/disables playing a non-standard animation for an offhand weapon on charge. | ++ |
special_3p_attack_anim_after_charge |
+bool |
+False | +Enables/disables playing a non-standard animation for an offhand weapon after charge ends. | ++ |
spread_air_ads |
+float |
+True | +The angle of the shot spread cone while aiming down sights in the air (includes wallrunning). | ++ |
spread_air_hip |
+float |
+True | +The angle of the shot spread cone while hipfiring in the air (includes wallrunning). | ++ |
spread_crouch_ads |
+float |
+True | +The angle of the shot spread cone while grounded, aiming down sights, and crouching or sliding. | ++ |
spread_crouch_hip |
+float |
+True | +The angle of the shot spread cone while grounded, hipfiring, and crouching or sliding | ++ |
spread_decay_delay |
+float |
+True | +The time in seconds since last shot fired before spread kick begins to decay. | ++ |
spread_decay_rate |
+float |
+True | +The amount of spread kick regressed per second when decaying. | ++ |
spread_kick_on_fire_air_ads |
+float |
+True | +The amount spread increases by per shot when aiming down sights in the air (includes wallrunning). | ++ |
spread_kick_on_fire_air_hip |
+float |
+True | +The amount spread increases by per shot when hipfiring in the air (includes wallrunning). | ++ |
spread_kick_on_fire_crouch_ads |
+float |
+True | +The amount spread increases by per shot when grounded, aiming down sights, and crouching or sliding. | ++ |
spread_kick_on_fire_crouch_hip |
+float |
+True | +The amount spread increases by per shot when grounded, hipfiring, and crouching or sliding. | ++ |
spread_kick_on_fire_stand_ads |
+float |
+True | +The amount spread increases by per shot when grounded, aiming down sights, and standing or sprinting. | ++ |
spread_kick_on_fire_stand_hip |
+float |
+True | +The amount spread increases by per shot when grounded, hipfiring, and standing or sprinting. | ++ |
spread_lerp_speed |
+float |
+True | +Unknown. | ++ |
spread_max_kick_air_ads |
+float |
+True | +The maximum amount of additional spread kick while aiming down sights in the air (includes wallrunning). | ++ |
spread_max_kick_air_hip |
+float |
+True | +The maximum amount of additional spread kick while hipfiring in the air (includes wallrunning). | ++ |
spread_max_kick_crouch_ads |
+float |
+True | +The maximum amount of additional spread kick while grounded, aiming down sights, and crouching or sliding. | ++ |
spread_max_kick_crouch_hip |
+float |
+True | +The maximum amount of additional spread kick while grounded, hipfiring, and crouching or sliding. | ++ |
spread_max_kick_stand_ads |
+float |
+True | +The maximum amount of additional spread kick while grounded, aiming down sights, and standing or sprinting. | ++ |
spread_max_kick_stand_hip |
+float |
+True | +The maximum amount of additional spread kick while grounded, hipfiring, and standing or sprinting. | ++ |
spread_stand_ads |
+float |
+True | +The angle of the shot spread cone while grounded, aiming down sights, and standing or walking. | ++ |
spread_stand_hip |
+float |
+True | +The angle of the shot spread cone while grounded, hipfiring, standing, and not moving. | ++ |
spread_stand_hip_run |
+float |
+True | +The angle of the shot spread cone while grounded, hipfiring, standing, and walking. | ++ |
spread_stand_hip_sprint |
+float |
+True | +The angle of the shot spread cone while grounded, hipfiring, and sprinting. | ++ |
spread_time_to_max |
+float |
+True | +Unknown. | ++ |
spread_wallhanging |
+float |
+True | +The angle of the shot spread cone while wallhanging. Optional. | +If unused, spread_stand_hip is applied instead. |
+
spread_wallrunning |
+float |
+True | +The angle of the shot spread cone while wallrunning. Optional. | +If unused, spread_air values are applied instead. |
+
sprint_fractional_anims |
+bool |
+True | +Unknown. | ++ |
sprintcycle_time |
+float |
+True | +Unknown. | ++ |
stat_accuracy |
+int |
+True | +The accuracy rating shown on the weapon selection screen in loadout creation. | ++ |
stat_damage |
+int |
+True | +The damage rating shown on the weapon selection screen in loadout creation. | ++ |
stat_range |
+int |
+True | +The range rating shown on the weapon selection screen in loadout creation. | ++ |
stat_rof |
+int |
+True | +The fire rate rating shown on the weapon selection screen in loadout creation. | ++ |
stick_pilot |
+bool |
+False | +Enables/disables player entities sticking to projectiles fired. | +Includes player-manned titans. | +
stick_titan |
+bool |
+False | +Enables/disables titan entities sticking to projectiles fired. | ++ |
stick_npc |
+bool |
+False | +Enables/disables npc entities sticking to projectiles fired. | +Includes npc/auto titans. | +
sustained_discharge_allow_melee |
+bool |
+True | +Allows the user to melee in the middle of a sustained discharge. | ++ |
sustained_discharge_duration |
+float |
+True | +The time in seconds that a sustained discharge lasts. | ++ |
sustained_discharge_pulse_frequency |
+float |
+True | +The number of attacks made per second during a sustained discharge. | ++ |
sustained_discharge_require_input |
+bool |
+True | +Whether or not to stop the sustained discharge when the attack button is let go. | ++ |
sustained_discharge_want_pulse_callbacks |
+bool |
+True | +Determines whether to use the OnWeaponSustainedDischargePulse callback or not. |
++ |
sustained_laser_attachment |
+string |
+True | +The weapon part to attach the sustained laser effect to. | ++ |
sustained_laser_effect_1p |
+asset |
+True | +The visual effect shown to the user when the sustained laser attacks. | ++ |
sustained_laser_effect_3p |
+asset |
+True | +The visual effect shown to others when the sustained laser attacks. | ++ |
sustained_laser_effect_loops |
+bool |
+True | +Enables/disables looping on the sustained laser effects. | ++ |
sustained_laser_enabled |
+bool |
+True | +Enables/disables firing the sustained laser on sustained discharge. | ++ |
sustained_laser_impact_distance |
+float |
+True | +The minimum distance for sustained_laser_impact_effect to be applied. |
++ |
sustained_laser_impact_effect |
+asset |
+True | +The visual effect shown where the sustained laser impacts terrain. | ++ |
sustained_laser_impact_effect_loops |
+bool |
+True | +Enables/disables looping on sustained_laser_impact_effect . |
++ |
sustained_laser_new_surface_impact_effect_table |
+asset |
+True | +Unknown. | ++ |
sustained_laser_radial_iterations |
+int |
+True | +The number of radial iterations the sustained laser is composed of. | ++ |
sustained_laser_radial_step |
+int |
+True | +Scalar on the number of evenly spaced lasers created per radial iteration. | +1 radial iteration is required for the single center beam. Successive iterations generate 2 ^ (i - 2) * sustained_laser_radial_step evenly spaced lasers in a circle, where i is the current iteration.E.g. 3 iterations and 3 step makes 1 center beam in the 1st iteration, 3 beams in the 2nd iteration, and 6 beams in the 3rd iteration. |
+
sustained_laser_radius |
+float |
+True | +The radius increase in sustained laser circles as the radial iteration increases. | ++ |
sustained_laser_range |
+float |
+True | +The maximum range of the sustained laser. | ++ |
sway_ |
+Various | +False | +A large set of values controlling view model sway when held. | ++ |
threat_scope_bounds_height |
+float |
+True | +Used instead of tagname to manually specify threat scope height. | ++ |
threat_scope_bounds_tagname1 |
+string |
+True | +Determines one corner of the threat scope. | ++ |
threat_scope_bounds_tagname2 |
+string |
+True | +Determines one corner of the threat scope. | ++ |
threat_scope_bounds_width |
+float |
+True | +Used instead of tagname to manually specify threat scope width. | ++ |
threat_scope_enabled |
+bool |
+True | +Enables/disables threat scope. | ++ |
threat_scope_fadeWithDistance |
+bool |
+True | +Unknown. | ++ |
threat_scope_zoomfrac_end |
+bool |
+True | +Unknown. | ++ |
threat_scope_zoomfrac_start |
+bool |
+True | +Unknown. | ++ |
titanarmor_critical_hit_required |
+bool |
+True | +Enables/disables requiring critical hits to damage titans. | ++ |
toss_overhead_time |
+float |
+True | +Unknown. | ++ |
toss_pullout_time |
+float |
+True | +The time in seconds to pull out the toss-able object. | ++ |
toss_time |
+float |
+True | +The time in seconds to throw the toss-able object. | ++ |
tossholdsprintcycle_time |
+float |
+True | +Unknown. | ++ |
tracer_effect |
+asset |
+True | +Tracer effect shown to others when a hitscan shot is fired. | ++ |
tracer_effect_first_person |
+asset |
+True | +Tracer effect shown to the user when a hitscan shot is fired. | ++ |
trap_warning_enemy_fx |
+asset |
+False | +Has no native use. The visual effect shown to enemy players when a trap light effect is played on projectiles. | ++ |
trap_warning_friendly_fx |
+asset |
+False | +Has no native use. The visual effect shown to allied players when a trap light effect is played on projectiles. | ++ |
trigger_snipercam |
+bool |
+False | +Unknown. | ++ |
ui1_draw_cloaked |
+bool |
+True | +Enables/disables showing ui element 1 while cloaked. | ++ |
ui1_enable |
+bool |
+True | +Enables/disables showing ui element 1. | +Additional values exist for ui elements up to ui8 . |
+
viewdrift_ads_air_scale_pitch |
+float |
+True | +The maximum absolute value pitch the user view will drift by when aiming down sights in the air. | ++ |
viewdrift_ads_crouch_scale_pitch |
+float |
+True | +The maximum absolute value pitch the user view will drift by when grounded, aiming down sights, and crouched or sliding. | ++ |
viewdrift_ads_speed_pitch |
+float |
+True | +Scalar on how quickly pitch viewdrift shifts while aiming down sights. | ++ |
viewdrift_ads_stand_scale_pitch |
+float |
+True | +The maximum absolute value pitch the user view will drift by when grounded, aiming down sights, and standing or sprinting. | ++ |
viewdrift_hipfire_air_scale_pitch |
+float |
+True | +The maximum absolute value pitch the user view will drift by when hipfiring in the air. | ++ |
viewdrift_hipfire_crouch_scale_pitch |
+float |
+True | +The maximum absolute value pitch the user view will drift by when grounded, hipfiring, and crouched or sliding. | ++ |
viewdrift_hipfire_speed_pitch |
+float |
+True | +Scalar on how quickly pitch viewdrift shifts while hipfiring. | ++ |
viewdrift_hipfire_stand_scale_pitch |
+float |
+True | +The maximum absolute value pitch the user view will drift by when grounded, hipfiring, and standing or sprinting. | +The same values exist for yaw viewdrift. | +
viewkick_ads_weaponFraction |
+float |
+True | +Scalar on how much the weapon/reticle recoils separate from viewpoint while aiming down sights (i.e. how much your viewpoint counters the recoil). | ++ |
viewkick_ads_weaponFraction_vmScale |
+float |
+True | +Scalar on how much the weapon viewmodel recoils while aiming down sights. | ++ |
viewkick_air_scale_ads |
+float |
+True | +Scalar on recoil generated while aiming down sights when the user is not grounded (includes wallrunning). | ++ |
viewkick_duck_scale |
+float |
+True | +Scalar on recoil generated while crouching. | ++ |
viewkick_hipfire_weaponFraction |
+float |
+True | +Scalar on how much the weapon/reticle recoils separate from viewpoint while hipfiring (i.e. how much your viewpoint counters the recoil). | ++ |
viewkick_hipfire_weaponFraction_vmScale |
+float |
+True | +Scalar on how much the weapon viewmodel recoils while hipfiring. | ++ |
viewkick_hover_scale |
+float |
+True | +Scalar on recoil generated while using pilot Hover. | ++ |
viewkick_move_scale |
+float |
+True | +Scalar on recoil generated while moving. | ++ |
viewkick_perm_pitch_base |
+float |
+True | +The base amount of vertical recoil (positive = down). Unaffected by viewkick_spring . |
++ |
viewkick_perm_pitch_random |
+float |
+True | +The maximum absolute value of randomness added to base vertical recoil. Unaffected by viewkick_spring . |
++ |
viewkick_perm_pitch_random_innerexclude |
+float |
+True | +Unknown. | ++ |
viewkick_pitch_base |
+float |
+True | +The base amount of vertical recoil (positive = down). | ++ |
viewkick_pitch_random |
+float |
+True | +The maximum absolute value of randomness added to base vertical recoil. | ++ |
viewkick_pitch_softScale |
+float |
+True | +Scalar on soft vertical recoil, which applies recoil over time. | ++ |
viewkick_pitch_hardScale |
+float |
+True | +Scalar on hard vertical recoil, which applies recoil instantly. | +The same values exist for yaw (positive = left) | +
viewkick_roll_base |
+float |
+True | +The absolute value base amount of roll. | ++ |
viewkick_roll_hardScale |
+float |
+True | +Scalar on hard roll recoil. | ++ |
viewkick_roll_randomMax |
+float |
+True | +The maximum amount of random roll added to base roll. | ++ |
viewkick_roll_randomMin |
+float |
+True | +The minimum amount of random roll added to base roll. | ++ |
viewkick_roll_softScale |
+float |
+True | +Scalar on soft roll recoil. Appears to simply be weaker than hard roll recoil. | +Roll values are unaffected by other scalars, such as viewkick_scale and weaponFraction . |
+
viewkick_scale_min_hipfire |
+float |
+True | +Scalar on recoil generated when hipfiring at minimum LERP. | ++ |
viewkick_scale_max_hipfire |
+float |
+True | +Scalar on recoil generated when hipfiring at maximum LERP. | ++ |
viewkick_scale_min_ads |
+float |
+True | +Scalar on recoil generated when aiming down sights at minimum LERP. | ++ |
viewkick_scale_max_ads |
+float |
+True | +Scalar on recoil generated when aiming down sights at maximum LERP. | ++ |
viewkick_scale_valuePerShot |
+float |
+True | +How much each shot advances the LERP. | ++ |
viewkick_scale_valueLerpStart |
+float |
+True | +The minimum value for the LERP. | ++ |
viewkick_scale_valueLerpEnd |
+float |
+True | +The maximum value for the LERP. | ++ |
viewkick_scale_valueDecayDelay |
+float |
+True | +The time in seconds since last shot fired before LERP begins to decay. | ++ |
viewkick_scale_valueDecayRate |
+float |
+True | +The amount of LERP regressed per second when decaying. | ++ |
viewkick_spring |
+string |
+True | +Specifies the class of viewkick spring, which dampens recoil above certain values. | ++ |
viewmodel |
+asset |
+True | +The model of the weapon shown to the user when held. | ++ |
viewmodel_offset_ads |
+vector |
+True | +The position offset of the viewmodel while aiming down sights. | ++ |
viewmodel_offset_hip |
+vector |
+True | +The position offset of the viewmodel while hipfiring. | ++ |
viewpunch_multiplier |
+float |
+False | +Unknown. | ++ |
vortex_absorb_effect |
+asset |
+True | +Shot model shown to the vortex user when a vortex weapon catches this weapon's shot. | ++ |
vortex_absorb_effect_third_person |
+asset |
+True | +Shot model shown to others when a vortex weapon catches this weapon's shot. | ++ |
vortex_absorb_sound |
+string |
+False | +Sound effect played to the vortex user when a vortex weapon catches this weapon's shot. | ++ |
vortex_absorb_sound_1p_vs_3p |
+string |
+False | +Sound effect played to others when a vortex weapon catches this weapon's shot. | ++ |
vortex_drain |
+float |
+True | +Fraction of charge taken from vortex weapons that catch a shot from this weapon. | +If caught by Ion's Vortex, drains a fraction of her energy instead. | +
vortex_impact_effect |
+asset |
+True | +The impact effect table referenced for vortex collisions. | +Vortex collisions refers to standard blocking vortex entities, such as Particle Wall and Gun Shield. | +
vortex_refire_behavior |
+string |
+False | +Defines the behavior for this weapon's projectiles when caught/reflected by Vortex shield. | +absorb : Disappears when caught.bullet : High speed (12000) and slightly higher than moderate spread.explosive_round : Moderate speed (8000) and moderate spread.rocket : Low speed (1800) and low spread.grenade : Low speed (1500) and high spread with 1.25s fuse time.grenade_long_fuse : Low speed (1500) and high spread with 10s fuse time. |
+
wave_max_count |
+int |
+False | +The number of steps wave attacks should take. | ++ |
wave_step_dist |
+float |
+False | +The distance each step moves a wave attack forward. | ++ |
weaponClass |
+string |
+False | +Determines the type of entity that normally uses the weapon. | +human : Humanoid entities.titan : Titans.other : Anything else (e.g. heavily armored NPCs, turrets) |
+
weaponSubClass |
+string |
+False | +Unknown. | +Takes rifle , smg , lmg , sidearm , sniper , projectile_shotgun , rocket , shotgun , or autolock . |
+
weaponType |
+string |
+False | +Determines the type of offhand the weapon is. (Offhands/melees only) | +melee : Melee weapon.shoulder : Offhand pulled from the shoulder used as an offensive ability. (Excluding Energy Siphon)defense : Held or instant offhand used as a defensive ability.tactical : Held or instant offhand used as a utility ability. (Excluding Tether Trap)titan_core : Titan Core weapon.anti_titan : Turret weapon. |
+
zoom_angle_shift_pitch |
+float |
+True | +Unknown. | ++ |
zoom_angle_shift_yaw |
+float |
+True | +Unknown. | ++ |
zoom_effects |
+bool |
+True | +Unknown. | ++ |
zoom_fov |
+float |
+True | +The FoV the weapon will zoom to when aiming down sights. | +Affected by custom player FoV scale. Effectively zooms to ( zoom FoV / base FoV * player FoV scale ) Base FoV is 70 for pilots and 75 for titans. |
+
zoom_fov_viewmodel |
+float |
+True | +Unknown. | ++ |
zoom_scope_frac_end |
+float |
+True | +Unknown. | ++ |
zoom_scope_frac_start |
+float |
+True | +Unknown. | ++ |
zoom_time_in |
+float |
+True | +The time in seconds to fully zoom in from hipfire. | ++ |
zoom_time_out |
+float |
+True | +The time in seconds to fully zoom out from aiming down sights. | ++ |
zoom_toggle_fov |
+float |
+True | +The FoV the weapon will zoom to when aiming down sights with a zoom toggle on. | ++ |
zoom_toggle_lerp_time |
+float |
+True | +The time in seconds for toggleable zoom to change zoom FoV. | ++ |
zoomfrac_autoattack |
+float |
+True | +The fraction of zoom above which the weapon will continually attack | ++ |
Make sure to name your mod in the form <your name>.<mod name>
, similar to the existing default mods, like Northstar.Client
, Northstar.CusomServer
,
+Note that the Northstar name (Northstar.Xyz
) is reserved for mods that come with the Northstar install and should therefore not be used.
It is recommended to upload the source code of your mod to a public repository like Github to give your users a place to suggest changes and leave feedback in an organised manner.
+If the changes your mod makes can be represented in screenshots, gameplay recordings, or GIFs, consider adding those to your README. This way anyone coming across your mod can tell which aspects of the game it changes even before installing it.
+To do so, simply upload the image or gif to a host of your choice (Imgur, GitHub, and even Discord all work). To display the image directly on your page in Thunderstore, add the following line to your README:
+ ![alt text, this text shows up when image cannot be loaded](https://example.com/image/to/link/to.gif)
+
The best place to publish your mod is Thunderstore. To do so, you need to package your mod as a zip with a specific folder structure. You can either set the structure up manually or use this GitHub template
+The Thunderstore package zip structure is as follows:
+ mods/<your name>.<mod name>/
+ icon.png
+ manifest.json
+ README.md
+
icon.png
: 256x256px icon for your mod.README.md
: the description page for your modmanifest.json
outlined hereYou can put multiple mods in the mods/
folder, but only do this if neccessary.
manifest.json
checker:
+https://northstar.thunderstore.io/tools/manifest-v1-validator/
After you have set up the folder structure, head to https://northstar.thunderstore.io and log in with either Discord or Github. Then you can use the Upload
button at the top of the page to upload your zip.
When uploading, it will verify your package structure and you can publish after it's successfully checked.
+To update a mod, change the version in mod.json
and manifest.json
, and upload again. If the mod name is the same, it will update the previous version.
If you want to create a Github repository for your mod, you can use a template that automatically packages and uploads your mod to Thunderstore when you create a Github release of your mod.
+Github only supports files smaller than 100mb without using git-lfs. Using git-lfs isn't recommended. Instead you should use scripts that split your assets like in this template
+ + + + + + + + + + + + + + + + +Before starting to reverse engineer how to push values to the Squirrel stack, it's recommended to read the Squirrel documentation, +especially the Embedding- +and API Reference.
+A lot of Respawn's fork of Squirrel is very similar to Squirrel3, especially the Squirrel API functions.
+Before you can start reverse engineering you need to install software to disassemble and decompile the binaries. This guide will use Ghidra, an open source reverse engineering tool developed by the NSA.
+Since code decompiled by Ghidra is often closer to the raw assembly than the original code, you might want to use IDA for decompilation. +Keep in mind that IDA is not open source and the free version is lacking a lot of features and only offers a cloud decompiler.
+Download the latest Ghidra archive from the Github releases page. Usually the archive is called like ghidra_[version]_PUBLIC_[date]
.
Unzip the archive in a new folder.
+Run ghidraRun.bat
on windows or if you're on Linux make ghidraRun
executable and run it. On Linux, there's a Flatpak image available as well.
Create a new project under File > New Project
and select Non-Shared Project
, then hit next. Afterwards select a location for the project and a name like Titanfall2
.
Import the binary you want to reverse with File > Import File
. This guide will use server.dll
, found in your Titanfall2 install directory. Don't change the settings ghidra auto detects when importing the file.
Open server.dll
in the Ghidra project overview. When Ghidra asks you if you want to analyse the file now, click yes. You do not need to change any analysis settings.
Wait for Ghidra to finish the analysis.
+In vanilla Squirrel you can push values with functions like sq_pushbool
. Since Respawn changed a lot in the SQVM, you should expect these API functions to be different as well.
To start you'll need a simple Squirrel function that is executing native code without any calculations or similar, like IsServer
, or IsClient
.
+These Squirrel functions are registered in native code and return true
/ false
if the script VM is being ran in the SERVER
or CLIENT
.
You can search for a string in memory with Search > Memory
. Select String
as the format you're searching for and enter IsServer
as the search value.
The first occurence is at server.dll+0x2b44f3
. If you wait for the function to be decompiled, you'll see the string in this code:
_DAT_181055f60 = "IsServer";
+ _DAT_181055f68 = "IsServer";
+ _DAT_181055fb8 = 0;
+ _DAT_181055f90 = 0;
+ _DAT_181055f98 = 0;
+ _DAT_181055fc0 = FUN_18029a630;
+ _DAT_181055f88 = _DAT_181055f88 & 0xff;
+ _DAT_181055f70 = ZEXT816(0x1808fa7f8);
+ _DAT_181055f80 = 0;
+ _DAT_181055f8c = 0;
+ _DAT_181055f9c = 6;
+
Because the squirrel function executes native code, the callback FUN_18029a630
is probably where it's located. You can double click the reference to decompile the function.
undefined4 FUN_18029a630(undefined8 param_1)
+ {
+ char cVar1;
+ undefined4 uVar2;
+
+ uVar2 = 1;
+ FUN_180003710(param_1,1);
+ cVar1 = FUN_18001d840(param_1);
+ if (cVar1 != '\0') {
+ uVar2 = 0xffffffff;
+ }
+ return uVar2;
+ }
+
From this you can assume that native closures in squirrel_re still use the SQRESULT
convention, because the closure returns -1
if FUN_18001d840
returns NULL
, which is typically an error and 1
if nothing happens.
+It's also obvious that either FUN_180003710
or FUN_18001d840
pushes a boolean to the stack. It's probably FUN_180003710
because it takes an extra parameter but you can check IsClient
at server.dll+0x29a4d0
as a reference.
undefined4 FUN_18029a4d0(undefined8 param_1)
+ {
+ char cVar1;
+ undefined4 uVar2;
+
+ FUN_180003710(param_1,0);
+ cVar1 = FUN_18001d840(param_1);
+ uVar2 = 1;
+ if (cVar1 != '\0') {
+ uVar2 = 0xffffffff;
+ }
+ return uVar2;
+ }
+
This is virtually the same, except that FUN_180003710
is being called with a 0
.
+This makes it pretty obvious that FUN_180003710
is the equivalent of sq_pushbool
.
+Decompile the function, then right click the function and select Edit Function Signature
.
+Right now the signature looks like this:
void FUN_180003710(longlong param_1, int param_2)
+
param_1
has to be a pointer to the Squirrel VM, because a pointer on 64x systems is 8 bytes long (the same as longlong
) and the HSquirrelVM
struct is larger than 8 bytes.
The second parameter has to be the value that will be pushed to the VM as a boolean, since it was 1
in IsServer
(which always returns true
) and 0
in IsClient
which always returns false
.
You can change the signature now to this, to make code using the function more readable. Because HSquirrelVM
isn't defined yet, the type needs to stay longlong
for now.
void sq_pushbool(longlong sqvm, int value)
+
This tutorial will explain how to create a mod that adds a new menu that's viewable by a footer in the main menu.
+First, create a new folder with this mod.json
:
{
+ "Name": "CustomMenuTutorial",
+ "Description": "Custom menu tutorial",
+ "LoadPriority": 1,
+ "Scripts": [
+ {
+ "Path": "ui/custom_menu.nut",
+ "RunOn": "UI",
+ "UICallback": {
+ "Before": "AddCustomMenu"
+ }
+ }
+ ]
+ }
+
Then create custom_menu.nut
in ./mod/scripts/vscripts/ui
.
Create AddCustomMenu
in custom_menu.nut
like this and make it global:
global function AddCustomMenu
+
+ void function AddCustomMenu()
+ {
+ AddMenu( "CustomMenu", $"resource/ui/menus/custommenu.menu", CustomMenu_Init )
+ }
+
AddCustomMenu
will get called when the UI vm is initializing and instantiate your menu. You can access your menu with GetMenu( "CustomMenu" )
after it has been initialized.
Next, create the file that defines the layout of your menu. It's already referenced in the above code at $"resource/ui/menus/custommenu.menu"
. Create the file ./mod/resource/ui/menus/custommenu.menu
and paste this code in it.
resource/ui/menus/custommenu.menu
+ {
+ menu
+ {
+ ControlName Frame
+ xpos 0
+ ypos 0
+ zpos 3
+ wide f0
+ tall f0
+ autoResize 0
+ visible 1
+ enabled 1
+ pinCorner 0
+ PaintBackgroundType 0
+ infocus_bgcolor_override "0 0 0 0"
+ outoffocus_bgcolor_override "0 0 0 0"
+
+ Vignette // Darkened frame edges
+ {
+ ControlName ImagePanel
+ InheritProperties MenuVignette
+ }
+
+ Title // The title of this menu
+ {
+ ControlName Label
+ InheritProperties MenuTitle
+ labelText "#CUSTOM_MENU_TITLE"
+ }
+
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// Content
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ SomeLabel // A label that is placed in the middle of the screen
+ {
+ ControlName Label
+
+ labelText "Some Label"
+
+ auto_wide_tocontents 1 // Set width automatically relative to the label content
+
+ xpos %50
+ ypos %50
+ }
+
+ SomeButton // A button that is placed directly beneath the label
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+
+ tall 50
+ wide 250
+
+ labelText "Some Button"
+ textAlignment center
+
+ pin_to_sibling SomeLabel
+ pin_corner_to_sibling TOP
+ pin_to_sibling_corner BOTTOM
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// Footer
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ FooterButtons // Allow adding footers to this menu
+ {
+ ControlName CNestedPanel
+ InheritProperties FooterButtons
+ }
+ }
+ }
+
Now you'll need to define CustomMenu_Init
. This is the function previously defined that contains all initializations needed for this menu.
First, create an instantiated struct for variables that should be available in the scope of your custom menu script.
+ struct {
+ var menu
+ } file
+
At the moment, this struct can only contain your menu. To set it, edit AddCustomMenu
like this:
void function AddCustomMenu()
+ {
+ AddMenu( "CustomMenu", $"resource/ui/menus/custommenu.menu", CustomMenu_Init )
++ file.menu = GetMenu( "CustomMenu" )
+ }
+
Now, define CustomMenu_Init
. It doesn't need to be global.
void function CustomMenu_Init()
+ {
+ AddMenuFooterOption( file.menu, BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
+ }
+
This adds a footer to your menu, that allows the user to navigate back.
+Currently, there is no way to access your menu. You can open your (or any other menu) with AdvanceMenu
.
AdvanceMenu( GetMenu( "CustomMenu" ) )
+
This is useful for callbacks triggered by button presses like from footers. To add a footer to the Main menu, first edit your mod.json
code callbacks:
"Scripts": [
+ {
+ "Path": "ui/custom_menu.nut",
+ "RunOn": "UI",
+ "UICallback": {
++ "Before": "AddCustomMenu", // <- Notice the added comma
++ "After": "AddCustomMenuFooter"
+ }
+ }
+ ]
+
We need a new callback that's run after all menus are initialized to add any footers to them. Create the global function AddCustomMenuFooter
in custom_menu.nut
like this:
void function AddCustomMenuFooter()
+ {
+ AddMenuFooterOption(
+ GetMenu( "MainMenu" ), // Get the main menu. We want to add a footer to this one. Change this if you want to add a footer to another menu
+ BUTTON_X, // This sets the gamepad button that will trigger the click callback defined later
+ PrependControllerPrompts( BUTTON_X, " Custom Menu" ), // This is the text that will show as the label of the footer if a gamepad is used
+ "Custom Menu", // This is the label text of the footer if no gamepad is used
+ void function( var button ) // This is the click callback.
+ {
+ /*
+ This is an anonymous function.
+ It will be run every time the footer is pressed.
+ */
+ AdvanceMenu( file.menu )
+ }
+ )
+ }
+
We'll use the button we defined earlier in the .menu
file to increase a number of clicks and the label to show how often the user has clicked that button.
first, add someLabel
and clicks
to the file
struct. Then define the label in the AddCustomMenu
and add a callback to the button.
struct {
+ var menu
++ var someLabel
++ int clicks
+ } file
+
+ void function AddCustomMenu()
+ {
+ AddMenu( "CustomMenu", $"resource/ui/menus/custommenu.menu", CustomMenu_Init )
+ file.menu = GetMenu( "CustomMenu" )
++ file.someLabel = Hud_GetChild( file.menu, "SomeLabel" )
+
++ var someButton = Hud_GetChild( file.menu, "SomeButton" )
++ Hud_AddEventHandler( someButton, UIE_CLICK, OnSomeButtonClick )
+ }
+
Now you need to define the OnSomeButtonClick
callback that's triggered when the button is activated.
void function OnSomeButtonClick( var button )
+ {
+ file.clicks++
+ Hud_SetText( file.someLabel, format( "clicked the button %i times", file.clicks ) )
+ }
+
First you need to add a definition in your custommenu.menu
file:
ResetButton
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+
+ tall 50
+ wide 250
+
+ labelText "Reset Counter"
+ textAlignment center
+
+ pin_to_sibling SomeButton
+ pin_corner_to_sibling TOP
+ pin_to_sibling_corner BOTTOM
+ }
+
Then add a UIE_CLICK
callback for the button. It also makes sense to move the code that updates the label text to it's own function so it can be reused by the reset button.
void function AddCustomMenu()
+ {
+ AddMenu( "CustomMenu", $"resource/ui/menus/custommenu.menu", CustomMenu_Init )
+ file.menu = GetMenu( "CustomMenu" )
+ file.someLabel = Hud_GetChild( file.menu, "SomeLabel" )
+
+ var someButton = Hud_GetChild( file.menu, "SomeButton" )
++ var resetButton = Hud_GetChild( file.menu, "ResetButton" )
+
+ Hud_AddEventHandler( someButton, UIE_CLICK, OnSomeButtonClick )
++ Hud_AddEventHandler( resetButton, UIE_CLICK, OnResetButtonClick )
+ }
+
+ void function OnSomeButtonClick( var button )
+ {
+ file.clicks++
+ - Hud_SetText( file.someLabel, format( "clicked the button %i times", file.clicks ) )
++ UpdateClickLabel()
+ }
+
+ void function OnResetButtonClick( var button )
+ {
+ file.clicks = 0
++ UpdateClickLabel()
+ }
+
++ void function UpdateClickLabel()
++ {
++ Hud_SetText( file.someLabel, format( "clicked the button %i times", file.clicks ) )
++ }
+
You can add callbacks for menu events, for example when a menu is closed or opened.
+If you want to reset the counter if the menu is closed, edit AddCustomMenu
like this:
void function AddCustomMenu()
+ {
+ AddMenu( "CustomMenu", $"resource/ui/menus/custommenu.menu", CustomMenu_Init )
+ file.menu = GetMenu( "CustomMenu" )
+ file.someLabel = Hud_GetChild( file.menu, "SomeLabel" )
+
+ var someButton = Hud_GetChild( file.menu, "SomeButton" )
+ var resetButton = Hud_GetChild( file.menu, "ResetButton" )
+
+ Hud_AddEventHandler( someButton, UIE_CLICK, OnSomeButtonClick )
+ Hud_AddEventHandler( resetButton, UIE_CLICK, OnResetButtonClick )
+
++ AddMenuEventHandler( file.menu, eUIEvent.MENU_CLOSE, OnCloseCustomMenu )
+ }
+
And define the callback OnCloseCustomMenu
to simply call OnResetButtonClick
.
void function OnCloseCustomMenu()
+ {
+ OnResetButtonClick( null )
+ }
+
Creating a gamemode is significantly more complex than making mutators. The main differences are the number of things you must define in order to create a functioning gamemode.
+For example, the client localisation, the way the gamemode is defined (FFA, TDM, etc), the scoring system, respawn system (FFA or TDM spawnpoints) and team mechanics must all be considered.
+mod.json
¶The mod.json
is responsible for governing when, and where your mod is loaded, and follows a layout that is fairly complicated at first glance.
+However, once you get the hang of it, it should be fairly easy to use.
{
+ "Name" : "SimpleRandomiser",
+ "Description" : "A randomiser gamemode that randomizes your loadouts!",
+ "Version": "0.1.0",
+ "LoadPriority": 1,
+
The script above defines the pubic and listed details of the mod.
+ "Scripts": [
+ {
+ "Path": "gamemodes/_gamemode_simplerandomiser.nut",
+ "RunOn": "SERVER && MP"
+ },
+ {
+ "Path": "gamemodes/cl_gamemode_simplerandomiser.nut",
+ "RunOn": "CLIENT && MP"
+ },
+ {
+ "Path": "sh_gamemode_simplerandomiser.nut",
+ "RunOn": "MP",
+ "ClientCallback": {
+ "Before": "simplerandomiser_init"
+ },
+ "ServerCallback": {
+ "Before": "simplerandomiser_init"
+ }
+ }
+ ],
+
The script above defines both what functions to run, when to run them and WHERE to run them,
+The first one being _gamemode_simplerandomiser.nut
runs the server scripts, which handles the portion of everything related to the player, such as taking their weapons and replacing it with a different one.
Second one being cl_gamemode_simplerandomiser.nut
is where the client scripts run to perform stuff locally on the player's game, such as playing music, receiving announcement texts from the server and so on.
Lastly, sh_gamemode_simplerandomiser.nut
is a shared script between server and client, in this case it runs your simplerandomiser_init
in order to assign many variables for the server and client to "know" about this gamemode.
For example, both server and client needs to know whether if this gamemode exists in the private match settings, the scoring HUD and system, the spawnpoints configuration and many more.
+ "Localisation": [
+ "resource/simplerandomiser_localisation_%language%.txt"
+ ]
+ }
+
This defines the path to the language file, and its main use is to localize strings such as the announcement texts, gamemode and so on.
+Name this file mod.json
, and it should go in the mods root folder, that being /yourmodname.
Here's what the end result would look like:
+ {
+ "Name" : "SimpleRandomiser",
+ "Description" : "SimpleRandomiser",
+ "Version": "0.1.0",
+ "LoadPriority": 1,
+ "Scripts": [
+ {
+ "Path": "gamemodes/_gamemode_simplerandomiser.nut",
+ "RunOn": "SERVER && MP"
+ },
+ {
+ "Path": "gamemodes/cl_gamemode_simplerandomiser.nut",
+ "RunOn": "CLIENT && MP"
+ },
+ {
+ "Path": "sh_gamemode_simplerandomiser.nut",
+ "RunOn": "MP",
+ "ClientCallback": {
+ "Before": "simplerandomiser_init"
+ },
+ "ServerCallback": {
+ "Before": "simplerandomiser_init"
+ }
+ }
+ ],
+ "Localisation": [
+ "resource/simplerandomiser_localisation_%language%.txt"
+ ]
+ }
+
This follows a fairly simple template, the only thing of note is that you often get strange behaviour using UTF-8
when saving the file instead of using UTF-16 LE
.
"lang"
+ {
+ "Language" "english"
+ "Tokens"
+ {
+ "MODE_SETTING_CATEGORY_SIMPLERANDOMISER" "Simple Randomiser"
+ "SIMPLERANDOMISER" "Randomise"
+ }
+ }
+
Name this file simplerandomiser_localisation_english.txt
and place it in the yourmodsname/mod/resource/
folder.
Let's begin the process by first creating the file sh_gamemode_simplerandomiser.nut
and making the core components of the gamemode, which is to define the gamemode properties.
global function simplerandomiser_init // initializing functions
+ global const string GAMEMODE_SIMPLERANDOMISER = "rand"
+ // we want a short term to use which allows server owners to
+ // select our gamemode without typing the entire name
+ // also makes it easier for us lol
+
+ void function simplerandomiser_init()
+ {
+ // start defining what to do before the map loads on this gamemode
+ AddCallback_OnCustomGamemodesInit( CreateGamemodeRand ) // define various properties such as name, desc, so on
+ AddCallback_OnRegisteringCustomNetworkVars( RandRegisterNetworkVars ) // server callbacks stuff
+ }
+
+ void function CreateGamemodeRand()
+ {
+ GameMode_Create( GAMEMODE_SIMPLERANDOMISER )
+ GameMode_SetName( GAMEMODE_SIMPLERANDOMISER, "#GAMEMODE_SIMPLERANDOMISER" ) // localizations will be handled later
+ GameMode_SetDesc( GAMEMODE_SIMPLERANDOMISER, "#PL_rand_desc" )
+ GameMode_SetGameModeAnnouncement( GAMEMODE_SIMPLERANDOMISER, "grnc_modeDesc" )
+ GameMode_SetDefaultTimeLimits( GAMEMODE_SIMPLERANDOMISER, 10, 0.0 ) // a time limit of 10 minutes
+ GameMode_AddScoreboardColumnData( GAMEMODE_SIMPLERANDOMISER, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 2 ) // dont fuck with it
+ GameMode_AddScoreboardColumnData( GAMEMODE_SIMPLERANDOMISER, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 ) // dont fuck with it
+ GameMode_SetColor( GAMEMODE_SIMPLERANDOMISER, [147, 204, 57, 255] ) // dont fuck with it
+
+ AddPrivateMatchMode( GAMEMODE_SIMPLERANDOMISER ) // add to private lobby modes
+
+ AddPrivateMatchModeSettingEnum("#PL_rand", "rand_enableannouncements", ["#SETTING_DISABLED", "#SETTING_ENABLED"], "1")
+ // creates a togglable riff whether or not we want to announce a text to the client
+ AddPrivateMatchModeSettingArbitrary("#PL_rand", "rand_announcementduration", "3")
+ // Creates a riff with an arbitrary numerical value for how long the announcement text remains on screen
+ // These riffs can be accessed from server configs or from the private match settings screen, under the "Simple Randomiser" category
+
+
+ // set this to 25 score limit default
+ GameMode_SetDefaultScoreLimits( GAMEMODE_SIMPLERANDOMISER, 25, 0 )
+
+ #if SERVER
+ GameMode_AddServerInit( GAMEMODE_SIMPLERANDOMISER, GamemodeRand_Init ) // server side initalizing function
+ GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_SIMPLERANDOMISER, RateSpawnpoints_Generic )
+ GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_SIMPLERANDOMISER, RateSpawnpoints_Generic )
+ // until northstar adds more spawnpoints algorithm, we are using the default.
+ #elseif CLIENT
+ GameMode_AddClientInit( GAMEMODE_SIMPLERANDOMISER, ClGamemodeRand_Init ) // client side initializing function
+ #endif
+ #if !UI
+ GameMode_SetScoreCompareFunc( GAMEMODE_SIMPLERANDOMISER, CompareAssaultScore )
+ // usually compares which team's score is higher and places the winning team on top of the losing team in the scoreboard
+ #endif
+ }
+
+ void function RandRegisterNetworkVars()
+ {
+ if ( GAMETYPE != GAMEMODE_SIMPLERANDOMISER )
+ return
+
+ Remote_RegisterFunction( "ServerCallback_Randomiser" )
+ // will come in useful later when we want the server to communicate to the client
+ // for example, making an announcement appear on the client
+ }
+
The comments should hopefully explain what most of everything does, but just to summarize:
+Now that we're done, name this file sh_gamemode_simplerandomiser.nut
and place it in the yourmodsname/mod/scripts/vscripts/gamemodes
folder.
Now that we're down with defining the gamemode, its time to focus on the component on that makes the gamemode function in-game. For this, it will be mostly handled by the server scripts, so head into _gamemode_simplerandomiser.nut
to begin writing the randomizing script.
global function GamemodeRand_Init
+
+ void function GamemodeRand_Init()
+ {
+ #if SERVER
+ SetLoadoutGracePeriodEnabled( false ) // prevent modifying loadouts with grace period
+ SetWeaponDropsEnabled( false ) // prevents picking up weapons on the ground
+ AddCallback_OnPlayerRespawned( GiveRandomGun )
+ #endif
+ }
+
As you may have noticed, checking if it is a server is a special case, so we use #if SERVER
and #endif
instead of the usual if(thing){stuff}
Now that our initial function is created, we now have the game triggering GiveRandomGun
when a player spawns, but we don't have any such function, so let's begin creating one.
Firstly, we need to know what weapons we can equip. +For this we define an array:
+ array<string> pilotWeapons = ["mp_weapon_alternator_smg",
+ "mp_weapon_autopistol",
+ "mp_weapon_car",
+ "mp_weapon_dmr"]
+
Here we have defined an array with only 4 weapons in it, you can make this list however you like but remember to separate all but the last item with a ,
As we already know its going to call the function GiveRandomGun
when a player respawns, let's define that now.
+First we strip any existing weapons:
void function GiveRandomGun(entity player)
+ {
+ foreach ( entity weapon in player.GetMainWeapons() )
+ player.TakeWeaponNow( weapon.GetWeaponClassName() )
+
This iterates through each weapon (that being the primary, secondary and anti-titan weapons) and removes them individually.
+Then lets give them a new, random weapon by selecting a random item from our previous array:
+ player.GiveWeapon( pilotWeapons[ RandomInt( pilotWeapons.len() ) ] )
+
Now, remember the server callback that we defined earlier in sh_gamemode_simplerandomiser.nut
? Let's put that to use.
+We are going to make it so the player receives an announcement whenever they have their weapons randomized.
// checks if the toggle option is set to enabled
+ if ( GetCurrentPlaylistVarInt( "rand_enableannouncements", 1 ) == 1 )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_Randomiser" ) // call the function that will be used client-side
+
Overall, the server script should look like this.
+ global function GamemodeRand_Init
+
+ void function GamemodeRand_Init()
+ {
+ #if SERVER
+ SetLoadoutGracePeriodEnabled( false ) // prevent modifying loadouts with grace period
+ SetWeaponDropsEnabled( false ) // prevents picking up weapons on the ground
+ AddCallback_OnPlayerRespawned( GiveRandomGun )
+ #endif
+ }
+
+ array<string> pilotWeapons = ["mp_weapon_alternator_smg",
+ "mp_weapon_autopistol",
+ "mp_weapon_car",
+ "mp_weapon_dmr"]
+
+ void function GiveRandomGun(entity player)
+ {
+ foreach ( entity weapon in player.GetMainWeapons() )
+ player.TakeWeaponNow( weapon.GetWeaponClassName() )
+
+ player.GiveWeapon( pilotWeapons[ RandomInt( pilotWeapons.len() ) ] )
+
+ // checks if the toggle option is set to enabled
+ if ( GetCurrentPlaylistVarInt( "rand_enableannouncements", 1 ) == 1 )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_Randomiser", GetCurrentPlaylistVarFloat( "rand_announcementduration", 3 ) ) // call the function that will be used client-side
+ }
+
Name this file _gamemode_simplerandomiser.nut
and place it in the yourmodsname/mod/scripts/vscripts/gamemodes
folder as well.
+Make sure to double check that all spellings are correct in your mod as everything is case-sensitive.
Lastly, for your cl_gamemode_simplerandomiser.nut
, we are going to utilize the callback functions from earlier, as well as add some music to play during the gamemode.
global function ClGamemodeRand_Init
+ global function ServerCallback_Randomiser
+
+ void function ClGamemodeRand_Init()
+ {
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_freeagents_intro", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_freeagents_intro", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_win", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_win", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_THREE_MINUTE, "music_mp_freeagents_almostdone", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_THREE_MINUTE, "music_mp_freeagents_almostdone", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_freeagents_lastminute", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_freeagents_lastminute", TEAM_MILITIA )
+ }
+
+ void function ServerCallback_Randomiser( float duration )
+ {
+ AnnouncementData announcement = Announcement_Create( "#RAND_RANDOMIZED" )
+ Announcement_SetSubText( announcement, "#RAND_RANDOMIZED_DESC" )
+ Announcement_SetTitleColor( announcement, <0,0,1> )
+ Announcement_SetPurge( announcement, true )
+ Announcement_SetPriority( announcement, 200 ) //Be higher priority than Titanfall ready indicator etc
+ Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK )
+ Announcement_SetDuration( announcement, duration )
+ Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK )
+ AnnouncementFromClass( GetLocalViewPlayer(), announcement )
+ }
+
What this script does is quite simple. It registers default music to play during the intro portion, when winning, drawing or losing, as well as the event when the timelimit reaches 3 minutes or 1 minute left.
+Also, it also displays an announcement towards the player when they have their weapons randomized.
+"So we're all done with the scripting stuff, right? That means we can finally run the gamemode itself!"
+Technically, yes, you could. But it wouldn't look pretty. Remember all those strings with the # symbol in front of them? We have to localize them first so it displays correctly.
+Hence, open your simplerandomiser_localisation_english.txt
which is located in the yourmodsname/mod/resource/
folder.
"lang"
+ {
+ "Language" "english"
+ "Tokens"
+ {
+ "PL_rand" "Simple Randomiser" // displays in the lobby settings
+ "rand_enableannouncements" "Toggle announcements" // describe the togglable setting
+ "rand_announcementduration" "Announcement duration" // describe the numerical setting
+ "PL_rand_lobby" "Simple Randomiser Lobby" // displays in lobby
+ "PL_rand_desc" "Your weapons are randomised! Fight and win!" // displays in the description of the gamemode in the lobby
+ "PL_rand_hint" "Your weapons are randomised! Fight and win!" // displays in the scoreboard of the gamemode ingame
+ "PL_rand_abbr" "RAND"
+ "GAMEMODE_TBAG" "Simple Randomiser" // displays in the loading screen
+ "RAND_RANDOMIZED" "Weapons Randomized" // displays in the announcement text
+ "RAND_RANDOMIZED_DESC" "Fight and win!" // displays below the announcement text, as a description
+ }
+ }
+
Alright, we're finally done! However, there's just one thing missing, which is to let the game know what maps are available for this gamemode to play on.
+We will need to create a file called playlists_v2.txt
and place it in yourmodsname/keyvalues
folder.
Yes, you will need to create a folder called keyvalues
which is separate from the mod
folder that we placed all our scripts and localization inside.
Next, inside this playlists_v2.txt
, we will need to allow/disallow what maps can the gamemode be played on.
playlists
+ {
+ Gamemodes
+ {
+ rand
+ {
+ inherit defaults
+ vars
+ {
+ name #PL_rand
+ lobbytitle #PL_rand_lobby
+ description #PL_rand_desc
+ hint #PL_rand_hint
+ abbreviation #PL_rand_abbr
+ max_players 12
+ max_teams 2
+ classic_mp 1
+
+ gamemode_score_hint #GAMEMODE_SCORE_HINT_TDM
+ }
+ }
+ }
+ Playlists
+ {
+ rand
+ {
+ inherit defaults
+ vars
+ {
+ name #PL_rand
+ lobbytitle #PL_rand_lobby
+ description #PL_rand_desc
+ abbreviation #PL_rand_abbr
+ image ps
+ //mixtape_slot 7
+ mixtape_timeout 120
+ visible 0
+ }
+ gamemodes
+ {
+ rand
+ {
+ maps
+ {
+ mp_forwardbase_kodai 1
+ mp_grave 1
+ mp_homestead 1
+ mp_thaw 1
+ mp_black_water_canal 1
+ mp_eden 1
+ mp_drydock 1
+ mp_crashsite3 1
+ mp_complex3 1
+ mp_angel_city 1
+ mp_colony02 1
+ mp_glitch 1
+ mp_lf_stacks 1
+ mp_lf_deck 1
+ mp_lf_meadow 1
+ mp_lf_traffic 1
+ mp_lf_township 1
+ mp_lf_uma 1
+ mp_relic02 1
+ mp_wargames 1
+ mp_rise 1
+ mp_coliseum 1
+ mp_coliseum_column 1
+ }
+ }
+ }
+ }
+ }
+ }
+
There isn't much to say here except that we enabled this gamemode to played on all maps. So if this gamemode is set to auto-rotate maps in a server, it will go from one map to the next in order. You could disable certain maps by changing the 1
to a 0
.
Another thing to note is that under the Playlists
tab, there is an image
slot. You could change the image that displays when selecting a gamemode in the private match lobby. You can find out what the keyvalues for the other images by checking out other gamemodes in Northstar.Custom/keyvalues/playlists_v2.txt
.
And that should be all you need in order to create a gamemode. Thanks for reading all the way to this point, and I hope you have learnt a thing or two.
+If you ever have a question or two, feel free to head into the Northstar Discord and ask about in #modding-chat.
+x3Karma#6984
Note
+The ZIP file containing the installer is password protected, you can find it on the download page
+The Video will now be in the same folder as the original one and converted to a .bik file
+menu_act01.bik
author.mod/
+ ├─ mod.json
+ ├─ media/
+ │ ├─ menu_act01.bik
+
Not only can you replace the pre-existing BIK files Respawn uses, you can also make your own custom ones and play them whenever you like with console commands.
+playvideo [video name] [horizontal resolution] [vertical resolution]
will play the named BIK file within the specified resolution.
EX. playvideo mycoolvideo 100 100
will play the BIK file named "mycoolvideo" within a 100x100 resolution square in the top-left corner.
The stopvideos
command will end any currently playing BIK videos.
With these commands, BIK files can be used as a substitute for custom audio outside of audio overrides, though they of course can only be played directly on the client and have no directional audio. Videos running in a 1x1 resolution in the top-left corner will be nearly unnoticeable outside of whatever audio they're playing.
+Some things to note while using custom BIK videos:
+Model (MDL) is the extension for Source's proprietary model format. It defines the structure of the model along with animation, bounding box, hit box, materials, mesh and LOD information. Unlike other Source games in Titanfall 2 files like the .phy, .vtx, .vvd etc. are not separate files and are instead included in the .mdl file.
+ +While creating an entire Model from scratch is possible it would be an extremely long and tedious task. Instead its recommended to use an existing Model as your base.
+- One of the following on Steam:
+- [Portal 2](https://store.steampowered.com/app/620/Portal_2/) (and its Authoring Tools [Portal 2 SDK](https://developer.valvesoftware.com/wiki/Authoring_Tools/SDK_(Portal_2)))
+- [SourceFilmMaker](https://store.steampowered.com/app/1840/Source_Filmmaker/) (FREE)
+
Note
+This Guide is tailored towards users with atleast some experience with Blender. If you are new to Blender I recommend you to first familiarize yourself with the basics of Blender. There are many tutorials on the Internet that can help you with that. +Especially the following topics are important for modifying or creating game assets:
+• Modeling +• UV Mapping +• Texturing +• Rigging
+These things work in conjunction with each other and are all important for creating a game ready model. An understanding of these topics is very helpful for proper usage of Blender.
+The workflow for editing a model is as follows:
+This is a short video guide on MDL Editing, there are some discrepancies between the video and this guide but the general workflow is the same. +Its relatively quick but shows the process quite well.
+ + +
+
Blender will be used to edit the model. It is a free and open-source 3D computer graphics software toolset with many features. It is widely used for animation, modeling, scene creation, and game development.
+The Source Tools add-on for Blender allows us to import and export Source Engine assets. It is a community-driven project that aims to provide users with a convenient way to import and export Source Engine assets from and to Blender, for a wide range of applications, including animation, modeling, scene creation, and game development.
+We will use both in conjunction to edit the model.
+Crowbar is a tool written by ZeqMacaw. It is used to decompile and compile Source Engine assets, such as models, textures, and sounds.
+We will use it to decompile and compile the model.
+MDLShit is a tool written by headassbtw. It is used to combine the MDL file with the other output files from Crowbar.
+We will use it to combine the MDL file with the other output files from Crowbar.
+Harmony VPK is a tool written by the Harmony Team. It's an electron-based GUI for VPKTool, and is used to extract files from VPK files.
+We will use it to extract the model from the game files.
+In this section we will go through the workflow in detail by using the Flatline as an example. +We will add a cube to the side of the Flatline and assign a custom material to it.
+Note
+Can be skipped if you downloaded the model from the Titanfall 2 Skin Modding Repo.
+Open VPK
button./vpk/
folder..vpk
file you want to extract (most multiplayer weapons are in client_mp_common.bsp.pak000_000.vpk
since you most likely want one of those)..vpk
file on the left side looking something like this: RootDir
+ ├── materials
+ ├── resource
+ ├── cfg
+ ├── scripts
+ ├── models
+ ├── maps
+ └── depot
+
models
folder (this is where all models in this file are located).weapons/vinson
which is the internal name for the Flatline)..mdl
file you want to extract (for example ptpov_vinson.mdl
).Note
+Weapons are mostly split into two models, one for the first person view(ptpov_
and the world model w_
.
ptpov
is used when you are in first person.
w_
is used when the weapon is viewed in the world (for example when its dropped on the ground).
Unpack
button on the top right.Select Folder
.Note
+In order to use Crowbar the way we will we need to setup a few things first.
+This step only needs to be done once
+Set Up Games
tab on the top left.Portal 2
or Source Filmmaker
in the dropdown menu on the top.The Game Setup
section should now be filled with the correct paths.
Select the Decompile
tab on the top.
In this tab make sure the following settings are set:
+MDL input
: File
Output to
: Subfolder (of MDL input)
(change the text in the box to the right of that to decompiled
)Check the following boxes:
+QC file
Each $texturegroup skin-familiy on single line
Include $definebones lines (typical for view models)
Use MixedCase for keywords
Reference mesh SMD file
Physics mesh SMD file
Vertex animation VTA file (flexes)
Procedural bones VRD file
Bone animation SMD files
Place in "anims" subfolder
Prefix mesh file names with model name
Select the Compile
tab on the top.
In this tab make sure the following settings are set:
+QC input
: File
Output to
: Subfolder (of QC input)
(change the text in the box to the right of that to compiled
)
+ Check the following boxes:
No P4
Verbose
Set Game that has the model compiler
to the game you selected in the Set Up Games
tab on the top left.
Browse
button on the top right..mdl
file you want to decompile (for example ptpov_vinson.mdl
).Decompile
button.decompiled
folder in the same folder as the .mdl
file.File
-> Import
-> Source Engine
..qc
file (for example ptpov_vinson.qc
) and uncheck the Import Animations
box and check the Create Collections
box.Note
+This step is entirely up to you and depends on what you want to do with the model. +In this example we will add a cube to the side of the Flatline and assign a custom material to it.
+Before editing let me explain how the model is structured in Blender.
+By selecting a qc file in the import menu we told Blender to import all SMD files referenced in that qc file.
+This means that the model is split into multiple collections based on the SMD files referenced in the qc file.
+For example the ptpov_vinson.qc
file references the ptpov_vinson_v_vinson.smd
file which contains the model for the Flatline.
+For now the smd file will be imported into blender when you import the qc file, later we will change this to be an dmx file instead.
ptpov_vinson_v_vinson.smd
mesh in the outliner.EDIT Mode
.EDIT Mode
add a cube to the side of the Flatline.EDIT Mode
.ptpov_vinson_v_vinson
mesh.Note
+On Weapons the most safe bone to weight paint to isdef_c_base
since it is the root bone of the weapon. This means that the cube will always move with the weapon.
+If you want the cube to move with a specific part of the weapon you can also weight paint it to the bone that moves that part of the weapon.
+Again, remember to somewhat learn how rigging works in Blender to properly understand this process.Now that we have our cube we want to assign a material to it.
+This step has two ways of being done, using a qc
file that references smd
files or references dmx
files.
+Usually the smd
way is what you will want todo when first importing and editing a model, however if you want to reimport a EDITED model you will need to use the dmx
way.
This is the way you will usually do it when first importing a vanilla model that you have not edited yet.
+EDIT Mode
.Material Properties
tab on the right click on the New
button.models\weapons_r2\coolmaterial\cool_material
)EDIT Mode
.This is the way you will usually do it when reimporting a model that you have edited.
+EDIT Mode
.Material Properties
tab on the right click on the New
button.Set the name of the material to its NAME in the game files
+Note
+with dmx files you can set the material path later on in the export menu. If you have multiple needed paths we will talk about that in the next step Multiple Material Paths. +This is why we set the name of the material to its name in the game files instead of its path.
+Exit EDIT Mode
.
Your cube should now have the material assigned to it ingame after compiling.
+Note
+To clearify: the material of a mesh or individual faces in the game will be associated using the name of the assigned material in Blender.
+If you have materials with multiple paths (different folders in the game files) you will want to use the $renamematerial
command in the qc file.
+Usage:
$renamematerial <current material> <new material>
+
Example:
+ $renamematerial "models\weapons_r2\coolmaterial\cool_material" "models\amazing\uncoolmaterial\cool_material2"
+
Command docs: VALVe developer docs $renamematerial
+Source Engine Export
Menu in the Scene Properties
select an Export Path
usually the same folder as the original qc file.Set the Export Format
to DMX
.
Note
+This is important since we want to export the model as a dmx file instead of an smd file, this is because of limitation in the smd format that we want to avoid. +dmx by default will, uppon importing set a "material path" which is the path to the material in the game files, if you reimport this model later on you will need to
+Press the Export
button and select Scene Export
(this will export all meshes in the scene to DMX files, you can also individually export meshes by selecting them in the outliner and then pressing the Export
button and selecting the mesh in the Export Menu).
.qc
file in a text editor of your choice.On the top of the file you will see so called "bodygroup" lines, these are used to define the bodygroups of the model. They look a bit like this: +
$bodygroup "body"
+ {
+ studio "ptpov_vinson_v_vinson.smd"
+ blank
+ }
+
For each bodygroup you will want to change the files to be the dmx files you exported in the previous step. (in most cases its gonna be just renaming all the files to .dmx
instead of .smd
).
Note
+If you have multiple bodygroups you will need to do this for each bodygroup, if you have multiple meshes in a bodygroup you will need to do this for each mesh in the bodygroup. +We do this so Crowbar uses the dmx files instead of the smd files when compiling the model.
+Open Crowbar.
+Compile
tab on the top.Browse
button on the top right..qc
file you want to compile (for example ptpov_vinson.qc
).Games that has the model compiler
(for example Portal 2
)Compile
button.Crowbar now compiles the model and outputs the files to the compiled
folder in the same folder as the .qc
file, inside the compiled
folder you will find the full folder path of the model (for example models\weapons\vinson\
).
Note
+Usually the error is self explainatory and you can fix it by yourself. +By default Crowbar will not output a compiled file if any errors occur during the compilation process.
+If you have Visual Studio Code installed you can also use the Valve KeyValue Files Support
extension to have a better overview of the qc file, extension id: GEEKiDoS.vdf
compiled\models\weapons\vinson\
)..mdl
file and multiple other files, in our case there will be 3 files .mdl
, .vvd
and .vtx
) all with the same name.Check
button.Convert
button._conv.mdl
file, this is our final exported and working model you can now close MDLShit and use that model in a mod.In this list the word <filename>
will be used to refer to the name of the file that is causing the error and X will refer to a number depending on your file, line
will refer to the line in the qc file that is causing the error.
+The following words correspond to the following:
<filename>
: The name of the file that is causing the error.<line>
: The line in the qc file that is causing the error.modelpath.qc(<line>): - could not load file '<<filename>.smd'
:
+ This error means that the qc file is trying to reference a file that does not exist, make sure that the file is in the same folder as the qc file and that the name of the file is correct.
+ If the above solution does not work think about if you need said file actually, if not you can remove it from the qc file. Or if you need it you can create it yourself.
+ You can also see if a Physics or LOD file is the missing file, if it is make sure you have the correct settings in Crowbar for the file to be generated.
Crowbar ERROR: The model compiler, "<filename>", does not exist.
Crowbar ERROR: The game's "<filename>" file does not exist.
+ Mostly happens if you did not properly set up Crowbar, make sure you set the Game that has the model compiler
to your prerequisite game.
VTF, short for "Valve Texture Format", is a texture type used by Valve in the source engine and is occasionally in Titanfall. vtf's are used for fx, animation, and other textures.
+VMT, short for "Valve Material Type", is a text material system that dictates how the game perceives a vtf outside of how it looks. It uses parameters and proxies to dictate how shaders will show the game. We will go into greater detail later.
+A lot of fx in Titanfall use vtf's as textures. Therefore, if the corresponding vtf can be found, we can do almost anything with the fx's appearence. +Example Mod: Exrill's Blue L-Star. +Since the L-Star has a physical bullet that is counted as fx, we can edit how it looks.
+Since vtf modding was originally for introducing custom weapon skins, most of the development on it was focused on that. The same concepts apply to modding other textures though.
+Most textures in the game use .dds but we can make them use .vtf.
+What we'll be doing is:
+You have 2 options for a VPK tool. Pick either the older VPK tool: +cra0 VPKTool +or the Newer VPK tool: Harmony VPKTool (better).
+With your VPK tool opened. 'Open' englishclient_mp_common.pak000_dir.vpk
which is located in Titanfall2/vpk
.
Inside of the VPK, not all guns filenames match their ingame names. Here is list of weapon names to help you out.
+Navigate to models/weapons/car101
. Extract all the viewmodel versions (ptpov) and normal model (w) mdl's.
To change the path in the .mdl to the custom .vmt. +We need a hex editor. Before editing with hex editors, you need to be aware that hex editors cannot add or delete data, only replace it. I will use HxD, but you can also use ida, or anything else as its personal preference.
+Open your .mdl in your hex editor.
+We want to get closer to the path we need or else you'll be scrolling and searching for hours. Search:(CTRL+F) for skin_31. If you don't get any matches, try skn_31, skin31, elite, or prime. The path should look something like .models\Weapons_R2\weaponname\weaponname_skin_31
.
+Don't change this unless you want to effect skin31 textures.
The path we do need to change is .models\Weapons_R2\weaponname\weaponname
. This comes before the skin_31
path.
+I recommend only changing the last section of the path. We'll change .models\Weapons_r2\car_smg\CAR_smg
to .models\weapons_r2\car_smg\car_ctm
. Note the capitalization, as some vpk repacking tools cannot repack properly if the changed path contains capitals.
Now copy these changes for ptpov_
and/or w_
model(s). As these are the stow (On your back) and main menu models. If don't change these. Your texture will only work when in a match.
In the same folder you extracted your mdl's. Make a materials
folder next to the models
folder.
Example: +
models
+ materials
+
Recreate the path you changed in the materials
folder, such that the last section is a .vmt file:
materials
+ └─ models
+ └─ weapons_r2
+ └─ car_smg
+ └─ car_ctm.vmt
+
Inside your .vmt paste:
+ "UnlitTwoTexture"
+ {
+
+ "$surfaceprop" "metal"
+ "$basetexture" ""
+ "$texture2" ""
+ "$bumpmap" ""
+ "$allowoverbright" "1"
+ "$vertexcolor" 1
+ "$vertexalpha" 1
+ "$decal" "1"
+ "$model" 1
+ "$nocull" "1"
+ }
+
When we use vtf textures, we can only use the albedo and normal. Learn more about texture maps here.
+VTFEdit is a tool to edit, view, and create .vtf files.
+Launch VTFEdit. Top left, click File
, Import
, find and Import your custom texture(s).
When importing your normal map. Choose to import as a Volume Map
+When importing your diffuse map. Choose to import as a Animated Map
More info about .vtf format possibilities here, or the official source docs here.
+After that, save your new .vtf's into the same folder as your custom .vmt with a simple name.
+In the "$basetexture"
argument enter your .vtf texture directory. We'll use models\weapons_r2\car_smg\car_ctm\NAMEOFVTF
. This should point to your custom diffuse .vtf with the simple name. The game expects these paths to be without the .vtf
file extension - don't add it.
Do the same for adding your normal map with the "$bumpmap"
argument.
In some cases you might have to create another vtf with literally any image. Put its path in the "$texture2"
argument. As far as i know, this is sometimes necessary even though the texture isn't used.
Your root folder should look somewhat like this
+ root
+ ├─ materials
+ │ └─ models
+ │ └─ weapons_r2
+ │ └─ car_smg
+ │ ├─ YOURTEXTURE.vtf
+ │ ├─ YOURTEXTURE.vtf
+ │ └─ car_ctm.vmt
+ └─ models
+ └─ weapons
+ └─car101
+ ├─ ptpov_car101.mdl
+ └─ w_car101.mdl
+
You're done! You just need to pack it into a vpk with a vpk tool (for our gun mod, we'd repack to englishclient_mp_common.pak000_dir.vpk
), and put the vpk into a Northstar mod inside a vpk
folder.
Help with repacking here, and help with Northstar mods here.
+To add animation functionality, all we need to do is add a Proxie; which is just a modifier inside a .vmt
, and change our albedo vtf texture.
You need to create a .vtf texture with multiple frames imported to a single .vtf texture, that's your animated texture. You can do this with VTFEdit. Then assign the texture in $basetexture
.
At the bottom of your vmt but before the }
, add this:
"Proxies"
+ {
+ AnimatedTexture
+ {
+ animatedTextureVar $basetexture
+ animatedTextureFrameNumVar $frame
+ animatedTextureFrameRate 30
+ }
+ }
+
To change the fps of the texture, change the value after animatedTextureFrameRate
, and you'll be done making your texture animated!
.rpak files are a file format created by Respawn as the main way to store and load in-game assets, +such as textures, materials, datatables, animation recordings, etc. The assets in the .rpak file are kept stored in memory +as long as the .rpak file is loaded.
+.starpak files are another file format created by Respawn to complement the .rpak file format. +They contain streamed asset data, saving hardware resources by only loading the data when needed. +The most common example of streamed asset data is high resolution textures. The low resolution versions are kept permanently loaded +in a .rpak file, whilst the higher resolution versions are loaded as needed.
+RPak mods can be used for the following:
+The lastest RePak release can be downloaded from here. +Once it has been downloaded, it is recommended to set up your file structure as follows:
+Note
+Depending on the version of RePak, some of these folders and files might be already there for you
+ RePak
+ ├── RePak.exe
+ ├── pack_all.bat
+ ├── rpaks
+ ├── maps
+ └── assets
+
RePak
: the base folder where your RePak/RPak related files goRePak.exe
: the unzipped
file you downloaded from GitHubpack_all.bat
: a .bat file that will pack all of your RPaks when opened (outlined below)rpaks
: the folder where RePak.exe will put your RPaks when they have been createdmaps
: the folder where you will write your "map" files, these define the contents of your RPaksassets
: the folder where you will put your various different images and folders, used to create your RPakspack_all.bat
is recommended for using RePak, as it allows for quick and easy packing of all of your RPaks.
+Below is the script that should be copied into the file.
for %%i in ("%~dp0maps\*") do "%~dp0RePak.exe" "%%i"
+ pause
+
This section will walk you through the process of making an RPak that replaces a camo. +For information on making other types of RPaks, check the RePak Docs:
+../repak/map
../repak/index
Before you can make your RPak, you need to know which assets you want to replace.
+Camos in Titanfall 2 tend to have their own RPaks, with the naming scheme camo_skin<number>_col.rpak
Firstly, make sure you have LegionPlus downloaded, if you don't, it can be downloaded from here.
+Then use LegionPlus to open one of the camo_skin<number>_col.rpak
RPaks, it should look like this:
Note
+If your LegionPlus doesn't show the "Name" as a full path, go into "Settings" and make sure that "Show Full Asset Paths" is ticked, then click "Refresh"
+Extract the asset by double clicking on it, or by selecting it and clicking "Export Selected"
+Make a note of the Name of the asset, in this example it's models\camo_skins\camo_skin04_col
Find the extracted file that LegionPlus created, and open it in some image editing software
+Warning
+The image editing software you choose must be able to export images as .dds files
+Examples of image editing software that supports .dds files:
+After you have made the desired changes to the image, export it as a .dds file with DXT1 (BC1) compression and the same name as it had originally.
+ +Warning
+Try to make your textures have dimensions that are powers of two, so that mipmaps can be used.
+For example 256x256
512x512
1024x512
4096x1024
are all fine, but 350x700
might cause issues.
Place your newly created .dds file in the assets\texture
folder, following the path in the Name you noted down above.
+In this example the .dds file would go in RePak\assets\texture\models\camo_skins
, with the path of the image being ..\RePak\assets\texture\models\camo_skins\camo_skin04_col.dds
Once you have edited your texture image and placed it in the right folder, you are ready to make your map file.
+Map files are what RePak uses to create the .rpak file (and .starpak files if needed) and are in the .json file format.
+They can be named anything you want, but should be put in the RePak\maps
folder.
Below is an example of a map file that creates an RPak called example.rpak
which contains 1 texture asset.
{
+ "name":"example",
+ "assetsDir":"../assets",
+ "outputDir":"../rpaks",
+ "starpakPath": "example.starpak",
+ "version": 7,
+ "files":[
+ {
+ "$type":"txtr",
+ "path":"texture/models/camo_skins/camo_skin04_col"
+ }
+ ]
+ }
+
name
: the name of the file that gets created by RePak.assetsDir
: the folder that RePak bases the file path on when looking for textures.outputDir
: the folder that RePak will put the files that it creates in.starpakPath
: the path of the starpak file for streaming textures.version
: the RPak version RePak will use when creating the RPaks. Version 7 is Titanfall 2, version 8 is Apex Legends.files
: an array of all of the assets that RePak will create in the RPak.$type
: the type of asset that this asset is, use txtr
for textures.path
: the path of the asset, used in texture assets for finding the image. This should start with texture/
and the rest should match the Name given by LegionPlus.Warning
+If the path
doesn't match up with the location of your file, RePak will throw an error
Warning
+If the path
contains any \
characters, make sure that you either replace them with /
or you duplicate them (\\
)
This is because \
is the escape character in JSON, and will therefore break the path
To create your RPak file, simply open pack_all.bat
.
Alternatively, click and drag your map file over RePak.exe
. (I don't recommend this, it's a pain)
Look at the console for any errors.
+If there are no errors, a .rpak file should have been created in the rpaks
folder.
Create the basis of the mod using the gettingstarted
guide.
Inside the mod's folder, create a new folder, called paks
. Move your .rpak file (and .starpak files if you have any) into the folder.
Inside the paks
folder that you created, make a new .json file called rpak.json
.
+In this example, the camo_skin04_col.rpak
rpak is completely replaced by example.rpak
.
+This is fine for camo RPaks, but isn't suitable for more complex RPaks
{
+ "Preload":
+ {
+ "example.rpak": false
+ },
+ "Aliases":
+ {
+ "camo_skin04_col.rpak": "example.rpak"
+ },
+ "Postload":
+ {
+
+ }
+ }
+
Preload
: if set to true
this makes RPaks get loaded as soon as possible.Aliases
: this completely replaces the RPak with the specified RPak. In this example camo_skin04_col.rpak
is replaced by example.rpak
.Postload
: this makes RPaks get loaded directly after the specified RPak.This field tells Northstar whether or not to load a specific RPak as soon as RPak loading starts.
+The field is a boolean. (true
or false
) and should be formatted like "<target_rpak>": true
or "<target_rpak>": false
Example: "my_new.rpak": true
This field tells Northstar that a specific RPak should never be loaded, and a different RPak should be loaded instead.
+The field should be formatted like "<target_rpak>": "<replacement_rpak>"
Example: "common.rpak": "my_new.rpak"
This field tells Northstar that a specific RPak must be loaded directly after another specified RPak has finished loading.
+The field should be formatted like "<target_rpak>": "<rpak_to_load_after>"
Example: "my_new.rpak": "common.rpak"
Warning
+If an asset in your RPak references another asset, it must be loaded after the asset that it references, or the game will infinitely loop when launched.
+This is mostly a problem for matl
assets, txtr
assets don't reference other assets.
The file structure of your paks
folder should be similar to this:
paks
+ ├── example.rpak
+ ├── example.starpak
+ └── rpak.json
+
example.rpak
: this is the RPak file that you made.rpak.json
: this controls how the game loads your RPak filesAfter rpak.json
is set up correctly, your RPak mod should be complete and functional!
Note
+If when you test the rpak the colour looks weird, use SRGB in the .dds compression, or use non-SRGB if you were already using SRGB
+WAV
format and either 48000hz
or 44100hz
sample rate.First you need to identify the exact sound. There's a command for this:
+ns_print_played_sounds 1
It will show all the audio events that
+are happening at that moment on the console.
For example, use your Grapple and open the console the event ID will be
+pilot_grapple_fire
How it looks in the console:
+All weapons, boosts, tacticals have different events IDs on different surfaces (concrete, solidmetal, wood, dirt etc.) +That's why you must identify the exact event/s. Examples based on Grapple:
+concrete_grapple_impact_1p_vs_3p
solidmetal_grapple_extract_1p_vs_3p
etc.NOTE: ns_print_played_sounds 1
will show you every event ID. Not
+just in-match ones. For example:
menu_focus
, and clicking sound will be menu_accept
or menu music mainmenu_music
Check the console often, as it's easy to miss your sound - there can be a lot of sounds playing.
+There's also a list of all EvenIDs (audio).
+Sounds can be played in-game from the console via script_ui EmitUISound(soundname)
.
Additionally subtitles in the form of plain text can also be useful.
+You can also export sounds with LegionPlus instead of playing them in-game.
+Open LegionPlus.exe -> Load File -> Titanfall 2/r2/sound pick general.mbnk
+If you want to export only a specific sound use search. It's possible to export selected sounds and all (after a search it exports only the found assets).
+When you successfully identified your event and have the audio file/s
+ready it's time to set up the folder structure.
+Assuming the event name is pilot_grapple_fire
, the folder structure of your mod should look like this:
author.mod/
+ ├── audio/
+ │ ├── pilot_grapple_fire/
+ │ │ └── my_audio.wav
+ │ └── pilot_grapple_fire.json
+ └── mod.json
+
Example of a mod.json
(documented here: Getting Started)
{
+ "Name": "MOD_NAME_HERE",
+ "Description": "DESCRIPTION_HERE",
+ "Version": "0.1.0",
+ "LoadPriority": 2
+ }
+
Inside the audio/
folder:
pilot_grapple_fire/
folder which needs to contain your .wav file(s)pilot_grapple_fire.json
json used to configure the sound override, dont forget to edit.You will have to add that folder with your sound and the json for each event you want to override.
+The event JSON files must contain both EventId
and AudioSelectionStrategy
like this:
{
+ "EventId": [ "pilot_grapple_fire" ],
+ "AudioSelectionStrategy": "sequential"
+ }
+
The AudioSelectionStrategy
can be either:
sequential
: If you have one sound or you want to play them in alphabetical order.random
: If you have more than one sound and you want to randomize them.Open/Add your audio as a track to Audacity and set the project rate accordingly.
+Either 48000hz
or 44100hz
can work well in most cases, but a few sounds don't use either. Which sampling rate you should use depends on the original sound's sampling rate.
For example: if you use 44100khz
and the sound is slightly too high pitched in game, use 48000khz
. If you use 48000khz
and it's slightly low pitched in game, use 44100khz
.
If you're unsure which to use, or the sound still seems too high, or low pitched, you can use LegionPlus to determine the original rate.
+After that, export your track as a 16-bit wav
file (any other bit-depth will cause Northstar to crash).
+Make sure you don't add any metadata as this will cause white noise to be at the end of the sound.
This is usually because there's some metadata left in the audio. Remove it to fix this issue.
+You can bulk remove it with Mp3tag or individually with Audacity.
+You can bulk remove it with Metadata Cleaner or a shell script (requires ffmpeg to be installed) and also individually with Audacity.
+metadata_remover.sh
(WAV only)
shopt -s globstar nullglob
+ for f in *.wav **/*.wav
+ do
+ ffmpeg -i "$f" -map 0 -map_metadata -1 -c:v copy -c:a copy "${f%.wav}.new.wav"
+ mv -f "${f%.wav}.new.wav" "$f"
+ done
+
_creating_your_sound
for more information.# todo
+On Linux you can use shell scripts that convert all WAV or MP3 audio files from the current directory (including folders) to WAV 48000Hz 16-bit. They require ffmpeg to be installed.
+MP3 and other formats scripts don't delete previous files, so just search for them (.format) and delete after conversion. WAV script automatically replaces old files.
+ #WAV to WAV 16-bit 48000 Hz.
+ #wav_converter.sh
+
+ shopt -s globstar nullglob
+ for f in *.wav **/*.wav
+ do
+ ffmpeg -i "$f" -acodec pcm_s16le -ar 48000 "${f%.wav}.new.wav"
+ mv -f "${f%.wav}.new.wav" "$f"
+ done
+
#MP3 to WAV 16-bit 48000 Hz.
+ #mp3-wav_converter.sh
+
+ shopt -s globstar nullglob
+ for f in *.mp3
+ do
+ ffmpeg -i "${f}" -vn -c:a pcm_s16le -ar 48000 "${f%.*}.wav"
+ done
+
#Replace .format with the one you want to convert.
+ #format-wav_converter.sh
+
+ shopt -s globstar nullglob
+ for f in *.format
+ do
+ ffmpeg -i "${f}" -vn -c:a pcm_s16le -ar 48000 "${f%.*}.wav"
+ done
+
mod.json
needs to be inside Titanfall 2/r2Northstar/Mods/
.Tacent View - DDS viewer.
+Highly recommended to check the NoSkill Gitbook modding section +for more tools.
+ + + + + + + + + + + + + + + + +Note
+This project is under active development. Please PR everything you can!
+Check Contributing to ModdingDocs section for getting started with readthedocs and reStructuredText.
+These docs contain info on squirrel usage and northstar/respawn functions. Its very much +WIP.
+If you know anything about any function, object or concept please dont hesitate to +contribute it, even if its just a quick and dirty PR.
+Loosely structured dump of information to be sorted in the future:
+Level editor for Respawn Entertainment Source based games.
+https://github.com/F1F7Y/MRVN-radiant
+Python library for analysing .bsp files
+https://github.com/snake-biscuits/bsp_tool
+Docs: https://github.com/snake-biscuits/bsp_tool/blob/master/docs/supported/titanfall.md
+Netradiant Custom Tutorial - Part 2:
+ + +How titanfall custom map:
+ + +Improper corners can cause the player to get stuck
+ + + + +Sample clip:
+ + +Sample map: https://cdn.discordapp.com/attachments/925435799057604709/1041813222547791953/corner_test_map.map
+ + + + + + + + + + + + + + + + +the plugins system now use source interfaces.
+The launcher exposes almost everything required by plugins in interfaces that allow for backwards compatibility. +The only thing that's passed to a plugin directly is the northstar dll HWND and a struct of data that's different for each plugin.
+Plugins are required to expose a void* CreateInterface(const char* name, int* status)
function to share their own interfaces.
+The launcher loads the PluginId
interface from the plugin to query info such as it's name.
Plugins can use the CreateInterface
function exposed by the northstarDll to use northstar interfaces such as for logging.
+An interface is just an abstract class to force all functions into a vftable.
Exposes some system functionality to plugins
+ // 32 bit
+ enum LogLevel {
+ INFO,
+ WARN,
+ ERR,
+ };
+
+ // handle: handle of the plugin. Passed to the plugin on init.
+ void Log(HMODULE handle, LogLevel level, char* msg); // logs a message with the plugin's log name
+ void Unload(HMODULE handle); // unloads the plugin
+ void Reload(HMODULE handle);
+
Interfaces that have to be exposed for the plugin to be loaded.
+ // strings of data about the plugin itself. may be extended in the future
+ // 32 bit
+ enum PluginString {
+ NAME, // the name of the plugin
+ LOG_NAME, // the name used for logging
+ DEPENDENCY_NAME, // the name used for squirrel dependency constants created. The value returned for this has to be a valid squirrel identifier or the plugin will fail to load
+ }
+
+ // bitfields about the plugin
+ // 32 bit
+ enum PluginField {
+ CONTEXT // 0x1 if the plugin is allowed to run on dedicated servers and 0x2 if the plugin is allowed to run on clients (is this even needed seems useless to me)
+ }
+
+ char* GetString(PluginString prop);
+ i64 GetField(PluginField prop);
+
struct PluginNorthstarData { HMODULE handle; };
+
+ // COPY THE initData IT MAY BE MOVED AT RUNTIME
+ void Init(HMODULE nsModule, const PluginNorthstarData* initData, bool reloaded); // called after the plugin has been validated. The nsmodule allows northstar plugins to work for the ronin client as well (assuming they update their fork lmao)
+ void Finalize(); // called after all plugins have been loaded. Useful for dependencies
+ void Unload(); // called just before the plugin is getting unloaded
+ void OnSqvmCreated(CSquirrelVM* sqvm); // the context of the sqvm is contained in the instance
+ void OnSqvmDestroying(CSquirrelVM* sqvm); // callback with the sqvm instance that's about to be destroyed (for UI, CLIENT is destroyed for some reason??)
+ void OnLibraryLoaded(HMODULE module, const char* libraryName); // called for any library loaded by the game (for example engine.dll)
+ void RunFrame(); // just runs on every frame of the game I think
+
Interfaces are just abstract classes. So make sure the first parameter is always a pointer to the instance of the interface you're using.
+an example what NSSys001 looks like in C:
+ typedef enum {
+ LOG_INFO,
+ LOG_WARN,
+ LOG_ERR,
+ };
+
+ typedef struct CSys {
+ struct {
+ void (*log)(struct CSys* self, HMODULE handle, LogLevel level, char* msg);
+ void (*unload)(struct CSys* self, HMODULE handle);
+ }* vftable;
+ } CSys;
+
+ // use like this
+ g_c_sys->vftable->log(g_c_sys, g_handle, LOG_INFO, "my balls are itching");
+
Interfaces are created with CreateInterface that's exposed in another dll.
+ + + + + + + + + + + + + + + + +Callbacks added by Northstar
+Callbacks within squirrel trigger functions when certain events occur.
+They will also often pass arguments to those functions based on the callbacks used.
+Please refer to Respawn Callbacks for the list of callbacks defined in respawn code.
+void AddClientCommandNotifyCallback( string commandString, void functionref( entity player, array
void CServerGameDLL_OnReceivedSayTextMessageCallback()
+void AddCallback_OnReceivedSayTextMessage( ClServer_MessageStruct functionref (ClServer_MessageStruct) callbackFunc )
+void AddCallback_OnRegisterCustomItems( void functionref() callback )
+bool ClientCommandCallback_RequestPilotLoadout( entity player, array
bool ClientCommandCallback_RequestTitanLoadout( entity player, array
bool ClientCommandCallback_SetPersistentLoadoutValue( entity player, array
bool ClientCommandCallback_SwapSecondaryAndWeapon3PersistentLoadoutData( entity player, array
bool ClientCommandCallback_SetBurnCardPersistenceSlot( entity player, array
bool ClientCommandCallback_SetCallsignIcon( entity player, array
bool ClientCommandCallback_SetCallsignCard( entity player, array
bool ClientCommandCallback_SetFactionChoicePersistenceSlot( entity player, array
bool ClientCommandCallback_LoadoutMenuClosed( entity player, array
bool ClientCommandCallback_InGameMPMenuClosed( entity player, array
void MenuCallbacks_Init()
+bool ClientCommandCallback_LeaveMatch( entity player, array
bool ClientCommandCallback_GenUp( entity player, array
void AddCallback_OnRegisteringCustomNetworkVars( void functionref() callback )
+void Evac( int evacTeam, float initialWait, float arrivalTime, float waitTime, bool functionref( entity, entity ) canBoardCallback, bool functionref( entity ) shouldLeaveEarlyCallback, void functionref( entity ) completionCallback, entity customEvacNode = null )
+void GamemodeFRA_AddAdditionalInitCallback()
+void AddCallback_OnCustomGamemodesInit( void functionref() callback )
+bool ClientCommandCallback_StartPrivateMatchSearch( entity player, array
bool ClientCommandCallback_PrivateMatchLaunch( entity player, array
bool ClientCommandCallback_PrivateMatchSetMode( entity player, array
bool ClientCommandCallback_SetCustomMap( entity player, array
bool ClientCommandCallback_PrivateMatchSwitchTeams( entity player, array
bool ClientCommandCallback_PrivateMatchToggleSpectate( entity player, array
bool ClientCommandCallback_PrivateMatchSetPlaylistVarOverride( entity player, array
bool ClientCommandCallback_ResetMatchSettingsToDefault( entity player, array
void AddCallback_IsValidMeleeExecutionTarget( bool functionref( entity attacker, entity target ) callbackFunc )
+This callback gets triggered after the melee button is pressed to check if the action to execute the target is still valid. + If one of the added callbacks return false the target won't be executed and will just be meleed.
+Note
+The execution prompt still pops up.
+bool SPMP_Callback_ForceAIMissPlayer( entity npc, entity player )
+bool ClientCommandCallback_spec_next( entity player, array
bool ClientCommandCallback_spec_prev( entity player, array
bool ClientCommandCallback_spec_mode( entity player, array
void AddCallback_OnRoundEndCleanup( void functionref() callback )
+void SetTimeoutWinnerDecisionFunc( int functionref() callback )
+void AddMouseMovementCaptureHandler( var capturePanelOrMenu, void functionref( int deltaX, int deltaY ) func )
+This document provides usage of the Chathook API added in Northstar v1.6.0
.
+For an example of chathooks in use, check out EmmaM's OwOfier mod.
Warning
+Your mod needs to be load priority 1 or above to use the structs and callbacks in your script.
+The client chat callbacks allow you to intercept chat messages and modify or block them.
+struct ClClient_MessageStruct
+Contains details on a chat message to be displayed. You can receive one of these by adding a chat callback with
+AddCallback_OnReceivedSayTextMessage
.
string message
+the text sent by the player.
+entity player
+the player who sent the chat.
+string playerName
+the display name of the player who sent the chat.
+bool isTeam
+whether this chat has a [TEAM]
tag.
bool isDead
+whether this chat has a [DEAD]
tag.
bool isWhisper
+whether this chat has a [WHISPER]
tag.
bool shouldBlock
+if true, this chat will not be displayed.
+void AddCallback_OnReceivedSayTextMessage(callbackFunc)
+Adds a callback that will be run when a chat message is received from the server. This will only be triggered for +messages from players, not server messages.
+The provided function should accept a ClClient_MessageStruct
and return an optionally modified copy of it. When a
+chat message is received, each registered callback is run in sequence to modify or block the message.
Example:
+ ClClient_MessageStruct function MyChatFilter(ClClient_MessageStruct message)
+ {
+ if (message.message.find("nft") != null)
+ {
+ message.shouldBlock = true
+ }
+
+ message.message = StringReplace(message.message, "yes", "no", true, true)
+
+ return message
+ }
+
+ void function MyModInit()
+ {
+ AddCallback_OnReceivedSayTextMessage(MyChatFilter)
+ }
+
A handful of functions are provided to write messages in local chat windows. These do not send messages to other +players, they only display them locally.
+void Chat_GameWriteLine(string text)
+Writes a line of text in local game chat panels. Supports ANSI escape codes.
+Example:
+ void function OnGameStarted()
+ {
+ Chat_GameWriteLine("You got this, " + GetLocalClientPlayer().GetPlayerName() + "!")
+ }
+
void Chat_GameWrite(string text)
+Appends text to local game chat panels. Supports ANSI escape codes.
+Example:
+ void function InitialiseHEVSuit()
+ {
+ Chat_GameWriteLine("SENSOR ARRAYS-")
+ ActivateSensorArrays()
+ Chat_GameWrite("ACTIVATED")
+ wait 1
+ Chat_GameWriteLine("BIOMETRIC MONITORING SYSTEMS-")
+ ActivateBiometricMonitoringSystems()
+ Chat_GameWrite("ACTIVATED")
+ wait 1
+ Chat_GameWriteLine("HAVE A VERY SAFE DAY.")
+ }
+
void Chat_NetworkWriteLine(string text)
+Writes a line of text in local network chat panels. Supports ANSI escape codes.
+Example:
+ void function MyModInit()
+ {
+ Chat_NetworkWriteLine("MyMod v1.0.0 is good to go!")
+ }
+
void Chat_NetworkWrite(string text)
+Appends text to local network chat panels. Supports ANSI escape codes.
+Example:
+ void function OnButtonPressed()
+ {
+ Chat_NetworkWrite("Connecting in 3...")
+ wait 1
+ Chat_NetworkWrite("2...")
+ wait 1
+ Chat_NetworkWrite("1...")
+ wait 1
+ Chat_NetworkWrite("0")
+ Connect()
+ }
+
The server chat callbacks allow you to intercept incoming chat messages and modify or block them.
+ClServer_MessageStruct
+Contains details on an incoming chat message. You can receive one of these by adding a chat callback with
+AddCallback_OnReceivedSayTextMessage
.
string message
+the text sent by the player.
+entity player
+the player who sent the chat.
+bool isTeam
+whether this chat is only sent to the player's team.
+bool shouldBlock
+if true, this chat will not be sent.
+void AddCallback_OnReceivedSayTextMessage(callbackFunc)
+Adds a callback that will be run when a chat message is received from a player. This will not be fired for custom +messages sent by server mods.
+The provided function should accept a ClServer_MessageStruct
and return an
+optionally modified copy of it. When a chat message is received, each registered callback is run in sequence to modify
+or block the message.
Example:
+ ClServer_MessageStruct function MyChatFilter(ClServer_MessageStruct message)
+ {
+ if (message.message.find("nft") != null)
+ {
+ message.shouldBlock = true
+ }
+
+ message.message = StringReplace(message.message, "yes", "no", true, true)
+
+ return message
+ }
+ void function MyModInit()
+ {
+ AddCallback_OnReceivedSayTextMessage(MyChatFilter)
+ }
+
With custom messages you can send chat messages at any time, to all players or to specific players.
+void Chat_Impersonate(entity player, string text, bool isTeamChat)
+Displays a chat message as if the player sent it. Only use this when the player has performed a clear action to send a +chat message.
+Parameters:
+entity player
- the player that the chat message will appear to be from.string text
- the contents of the chat message. Supports ANSI escape codes for colors.bool isTeamChat
- whether this chat is only sent to the player's team.Example:
+ void function OnSayRedCommand(entity player, string text)
+ {
+ Chat_Impersonate(player, "red text -> \x1b[31m" + text)
+ }
+
void Chat_PrivateMessage(entity fromPlayer, entity toPlayer, string text, bool whisper)
+Sends a private chat message from a player that is only displayed to one other player. Only use this when the player has +performed a clear action to send a chat message.
+Parameters:
+entity fromPlayer
- the player the message will be from.entity toPlayer
- the player that the message will be shown to.string text
- the contents of the chat message. Supports ANSI escape codes for colors.bool whisper
- if true, [WHISPER]
will be displayed before the message to indicate the message is private.Example:
+ void function OnSendToFriendsCommand(entity fromPlayer, string text)
+ {
+ array<entity> friends = GetPlayerFriends(fromPlayer)
+ foreach (friend in friends)
+ {
+ Chat_PrivateMessage(fromPlayer, friend, text, true)
+ }
+ }
+
void Chat_ServerBroadcast(string text, bool withServerTag = true)
+Displays a server message to all players in the chat.
+Parameters:
+string text
- the contents of the chat message. Supports ANSI escape codes for colors.bool withServerTag
- if true, [SERVER]
will appear before the message in chat. Defaults to true.Example:
+ void function RestartServerThread()
+ {
+ // wait one hour
+ wait 3600
+ Chat_ServerBroadcast("Server will be shut down in \x1b[93m5 seconds")
+ wait 1
+ Chat_ServerBroadcast("Server will be shut down in \x1b[93m4 seconds")
+ wait 1
+ Chat_ServerBroadcast("Server will be shut down in \x1b[93m3 seconds")
+ wait 1
+ Chat_ServerBroadcast("Server will be shut down in \x1b[93m2 seconds")
+ wait 1
+ Chat_ServerBroadcast("Server will be shut down in \x1b[93m1 second")
+ wait 1
+ StopServer()
+ }
+
void Chat_ServerPrivateMessage(entity toPlayer, string text, bool whisper, bool withServerTag = true)
+Sends a server message to a specific player in the chat.
+Parameters:
+entity toPlayer
- the player that the message will be shown to.string text
- the contents of the chat message. Supports ANSI escape codes for colors.bool whisper
- if true, [WHISPER]
will be displayed before the message to indicate the message is private.bool withServerTag
- if true, [SERVER]
will appear before the message in chat. Defaults to true.Example:
+ void function OnBanCommand(entity player, array<string> args)
+ {
+ if (!PlayerIsModerator(player))
+ {
+ Chat_ServerPrivateMessage(player, "You do not have the permissions to perform this command.", true, false)
+ return
+ }
+
+ BanPlayerByName(args[0])
+ }
+
All messages support ANSI escape codes for customising text color. These are commands in strings that have special +meaning. For example, the string:
+ Hello world, \x1b[31mthis text is red\x1b[0m. And \x1b[34mthis text is blue\x1b[0m.
+
\x1b
is a special character that Squirrel (and other languages) replace with a reserved ASCII character. For future
+reference this will be referred to with ESC
(e.g. setting red text is ESC[31m
).
The following commands are available:
+Codes | +Description | +
---|---|
ESC[0m and ESC[39m |
+Reset text formatting. | +
ESC[30-37m , ESC[90-97m |
+Set to one of the available color presets. | +
ESC[38;5;Xm |
+Set to one of the available 8-bit colors. | +
ESC[38;2;R;G;Bm |
+Set to an RGB color, with R , G and B in the range 0-255. |
+
ESC[110m |
+Set to chat text color. | +
ESC[111m |
+Set to friendly player name color. | +
ESC[112m |
+Set to enemy player name color. | +
ESC[113m |
+Set to network name color. | +
Custom damage source IDs can be used to create new damage source IDs for modded weapons, abilities, damage, etc.
+They can only be registered server-side and cannot modify existing damage source IDs. Clients pre-1.9.4 will not see the custom damage sources in the obituary.
+To add a single damage source ID, use:
+void RegisterWeaponDamageSource( string weaponRef, string damageSourceName )
+To add multiple damage source IDs, use
+void RegisterWeaponDamageSources( table< string, string > newValueTable )
+The first string parameter is the in-code weapon name while the latter is the name displayed in the obituary.
+Damage source IDs should be added in "After"
server callbacks.
For example, we can call the methods from a function in damage_source_example.nut
:
global function SimpleSourceInit
+
+ void function SimpleSourceInit()
+ {
+ // Server-side code
+
+ // Register a single damage source ID
+ RegisterWeaponDamageSource( "mp_weapon_minigun", "Minigun" )
+
+ // Register multiple damage source IDs
+ RegisterWeaponDamageSources(
+ {
+ mp_titanweapon_barrage_core_launcher = "Barrage Core",
+ mp_titanweapon_grenade_launcher = "Grenade Launcher"
+ }
+ )
+ }
+
Then call the function as an "After"
server callback in the mod.json
:
{
+ "Scripts": [
+ {
+ "Path": "damage_source_example.nut",
+ "RunOn": "SERVER && MP",
+ "ServerCallback": {
+ "After": "SimpleSourceInit"
+ }
+ },
+ ]
+ }
+
Now, these damage source IDs can be referenced in script like so:
+ eDamageSourceId.mp_weapon_minigun
+ eDamageSourceId.mp_titanweapon_barrage_core_launcher
+ eDamageSourceId.mp_titanweapon_grenade_launcher
+
and their corresponding precached weapons (if applicable) will automatically use their custom damage source IDs.
+ + + + + + + + + + + + + + + + +Compiler directives are a way to compile code only if a specific condition is met. To
+use this you have the #if
, #endif
, #else
and #elseif
keyword.
Contditons you can check for are
+SERVER
Checks if the code is compiled on the server VM.CLIENT
Checks if the code is compiled on the client VM.UI
Checks if the code is compiled on the UI VM.MP
Checks if the code is compiled in a multiplayer match.SP
Checks if the code is compiled in a singeplayer match.DEV
Checks if the code is compiled with the -dev
keyword in the startup
+ arguments.These conditions can also be combined with the regular squirrel boolean expressions
+ #if SERVER
+ Chat_ServerBroadcast("Message from the server VM")
+ #endif
+
#if (CLIENT && MP) || DEV
+ ...
+ #elseif SP
+ ...
+ #endif
+
Dependency constants are used to only compile code if a dependency your mod requires is +loaded, these use the Compiler directives syntax.
+Inside your mod.json
define a constant as:
{
+ // mod.json stuff
+ "Dependencies": {
+ // sets the constant to 0 or 1, depending if the mod with the name "Mod Name" exists and is enabled
+ "CONSTANT_NAME": "Mod Name"
+ }
+ }
+
For Example:
+ "PLAYER_HAS_ROGUELIKE_MOD": "TF|Roguelike"
+
Will define a constant PLAYER_HAS_ROGUELIKE_MOD
that is set to 0
or 1
+depending if the mod is enabled. It then can be used as a constant/compiler flag.
#if PLAYER_HAS_ROGUELIKE_MOD
+ print("player has roguelike mod")
+ Roguelike_Function();
+ #else
+ print("Can't use the function because the mod is off :'(")
+ #endif
+
As of v1.12.0, you can now make HTTP requests from Squirrel scripts. +HTTP requests allow you to query online APIs, send, retrieve data and much more.
+This is particularly useful for custom APIs you might want to build for your servers, for instance if you want to wrap +a database with an API so that your servers can save player stats.
+Warning
+For security reasons, private network hosts, such as localhost
or 192.168.1.106
are blocked by default, meaning you cannot make HTTP requests to them.
+This includes a blanket ban on IPv6 hosts.
You are also limited to HTTP
and HTTPS
for protocols. Any other protocols will prevent the request from being made.
There are a few launch arguments you may use to bypass some of the limitations, or outright disable HTTP requests.
+These should be applied to your client or server's launch commandline.
+Name | +Description | +
---|---|
-allowlocalhttp |
+Disables private network checks for HTTP requests, allowing any IPv4 and IPv6 host to be used. | +
-disablehttprequests |
+Disables HTTP requests. Any attempts at making requests will fail. | +
-disablehttpssl |
+Disables SSL verifications, useful when the host's SSL certificate is invalid, and insecure HTTP cannot be used. | +
This section documents all the available functions, structs and enums used to make HTTP request in Squirrel scripts.
+Warning
+HTTP requests are multithreaded, as such they will run in the background until completion, whether successful or failed. +Be mindful of how many requests you make at a time, as you may potentially get ratelimited or blacklisted by the remote host. +Use the callbacks to execute code when a request has completed.
+The HTTP system uses a few enums and structs for requests and their callbacks.
+enum HttpRequestMethod
+Contains the different allowed methods for a HTTP request. Please work.
+GET = 0
+Uses the GET
HTTP method for the request.
POST = 1
+Uses the POST
HTTP method for the request.
HEAD = 2
+Uses the HEAD
HTTP method for the request.
PUT = 3
+Uses the PUT
HTTP method for the request.
DELETE = 4
+Uses the DELETE
HTTP method for the request.
PATCH = 5
+Uses the PATCH
HTTP method for the request.
OPTIONS = 6
+Uses the OPTIONS
HTTP method for the request.
struct HttpRequest
+Contains the settings for a HTTP request. This is used for the more flexible `NSHttpRequest
function.
(HttpRequestMethod) int method
+HTTP method used for this HTTP request.
+string url
+Base URL of this HTTP request.
+table< string, array< string > > headers
+Headers used for this HTTP request. Some may get overridden or ignored.
+table< string, array< string > > queryParameters
+Query parameters for this HTTP request.
+string contentType = "application/json; charset=utf-8"
+The content type of this HTTP request. Defaults to application/json & UTF-8 charset.
+string body
+The body of this HTTP request. If set, will override queryParameters.
+int timeout = 60
+The timeout for this HTTP request in seconds. Clamped between 1 and 60.
+string userAgent
+If set, the override to use for the User-Agent header.
+Warning
+Only POST
requests can send a body to the remote end. You may only choose to send a body, or query parameters.
+Having both will give priority to the body and clear the parameters.
struct HttpRequestResponse
+Contains the response from the remote host for a successful HTTP request.
+int statusCode
+The status code returned by the remote the call was made to.
+string body
+The body of the response.
+string rawHeaders
+The raw headers returned by the remote.
+table< string, array< string > > headers
+A key -> values table of headers returned by the remote.
+struct HttpRequestFailure
+Contains the failure code and message when Northstar fails to make a HTTP request.
+int errorCode
+The status code returned by the remote the call was made to.
+string errorMessage
+The reason why this HTTP request failed.
+Warning
+Your mod needs to be load priority 1 or above to use HttpRequest
and HttpRequestResponse
in your script.
bool NSHttpRequest( HttpRequest requestParameters, void functionref( HttpRequestResponse ) onSuccess = null, void functionref( HttpRequestFailure ) onFailure = null )
+Launches a HTTP request with the given request data. +This function is async, and the provided callbacks will be called when it is completed, if any.
+Parameters:
+HttpRequest requestParameters
- The parameters to use for this request.[OPTIONAL] void functionref( HttpRequestResponse ) onSuccess
- The callback to execute if the request is successful.[OPTIONAL] void functionref( HttpRequestFailure ) onFailure
- The callback to execute if the request has failed.Returns:
+Example:
+Below is a working example of an HTTP request for a mod.
+As you can see, you can either use named functions for the callbacks, or create lambdas.
+Lambdas are particularly useful as they let you capture local variables of the functions to re-use later
+such as callback
in this example.
HttpRequest request
+ request.method = HttpRequestMethod.GET
+ request.url = "https://my.spyglass.api/sanctions/get_by_id"
+ request.queryParameters[ "id" ] <- [ id.tostring() ]
+
+ void functionref( HttpRequestResponse ) onSuccess = void function ( HttpRequestResponse response ) : ( callback )
+ {
+ SpyglassApi_OnQuerySanctionByIdSuccessful( response, callback )
+ }
+
+ void functionref( HttpRequestFailure ) onFailure = void function ( HttpRequestFailure failure ) : ( callback )
+ {
+ SpyglassApi_OnQuerySanctionByIdFailed( failure, callback )
+ }
+
+ return NSHttpRequest( request, onSuccess, onFailure )
+
bool NSHttpGet( string url, table< string, array< string > > queryParameters = {}, void functionref( HttpRequestResponse ) onSuccess = null, void functionref( HttpRequestFailure ) onFailure = null )
+Launches an HTTP GET request at the specified URL with the given query parameters. +Shortcut wrapper of NSHttpRequest(). +This function is async, and the provided callbacks will be called when it is completed, if any.
+Parameters:
+string url
- The url to make the HTTP request at.[OPTIONAL] table< string, array< string > > queryParameters
- A table of key value parameters to insert in the url. [OPTIONAL] void functionref( HttpRequestResponse ) onSuccess
- The callback to execute if the request is successful.[OPTIONAL] void functionref( HttpRequestFailure ) onFailure
- The callback to execute if the request has failed.Returns:
+Example:
+This is the same example as NSHttpRequest()'s example. However, it uses this function instead.
+ table<string, array<string> > params
+ params[ "id" ] <- [ id.tostring() ]
+
+ void functionref( HttpRequestResponse ) onSuccess = void function ( HttpRequestResponse response ) : ( callback )
+ {
+ SpyglassApi_OnQuerySanctionByIdSuccessful( response, callback )
+ }
+
+ void functionref( HttpRequestFailure ) onFailure = void function ( HttpRequestFailure failure ) : ( callback )
+ {
+ SpyglassApi_OnQuerySanctionByIdFailed( failure, callback )
+ }
+
+ return NSHttpGet( "https://my.spyglass.api/sanctions/get_by_id", params, onSuccess, onFailure )
+
bool NSHttpPostQuery( string url, table< string, array< string > > queryParameters, void functionref( HttpRequestResponse ) onSuccess = null, void functionref( HttpRequestFailure ) onFailure = null )
+Launches an HTTP POST request at the specified URL with the given query parameters. +Shortcut wrapper of NSHttpRequest(). +This function is async, and the provided callbacks will be called when it is completed, if any.
+Parameters:
+string url
- The url to make the HTTP request at.[OPTIONAL] table< string, array< string > > queryParameters
- A table of key value parameters to insert in the url. [OPTIONAL] void functionref( HttpRequestResponse ) onSuccess
- The callback to execute if the request is successful.[OPTIONAL] void functionref( HttpRequestFailure ) onFailure
- The callback to execute if the request has failed.Returns:
+bool NSHttpPostBody( string url, string body, void functionref( HttpRequestResponse ) onSuccess = null, void functionref( HttpRequestFailure ) onFailure = null )
+Launches an HTTP POST request at the specified URL with the given body. +Shortcut wrapper of NSHttpRequest(). +This function is async, and the provided callbacks will be called when it is completed, if any.
+This is the more interesting POST function, as you can use it to convert a table into JSON and "POST" it to the remote server.
+Parameters:
+string url
- The url to make the HTTP request at.string body
- The body to send with the request. Expects JSON by default. [OPTIONAL] void functionref( HttpRequestResponse ) onSuccess
- The callback to execute if the request is successful.[OPTIONAL] void functionref( HttpRequestFailure ) onFailure
- The callback to execute if the request has failed.Returns:
+Example:
+In this example, we'll convert a table to JSON, and send it over to a web API.
+ table myData = {}
+ myData[ "uid" ] <- player.GetUID()
+ myData[ "username" ] <- player.GetPlayerName()
+ myData[ "isBot" ] <- player.IsBot().tostring()
+
+ string json = EncodeJSON( myData )
+ if ( NSHttpPostBody( "https://api.stats.tf/player/connect", json ) )
+ {
+ printt( "Successfully attempted to upload player connection stats to API." )
+ }
+
bool NSIsSuccessHttpCode( int statusCode )
+Checks whether or not the given HTTP status code is considered a "success" code.
+This is true for status codes between 200 and 299.
+Parameters:
+int statusCode
- The status code to verify.Returns:
+Warning
+The JSON parser currently supports the following types for values: string
, integer
, float
, bool
, table
, and array
.
Tables and arrays can only hold supported types. Unsupported types will be ignored. Keys can only be strings.
+The JSON parser currently does not support keys holding null
values, and simply won't include them in decoded tables or encoded JSON strings.
table DecodeJSON( string json, bool fatalParseErrors = false )
+Converts a JSON string to a Squirrel table.
+Parameters:
+string json
- The JSON string to decode into a table.[OPTIONAL] bool fatalParseErrors
- Whether or not parsing errors should throw a fatal script error. Default to false.Returns:
+{}
on parse failure (if fatalParseErrors is false).string EncodeJSON( table data )
+Converts a Squirrel table to a JSON string.
+Parameters:
+table data
- The table to encode to a JSON string.Returns:
+Paired with HTTP and JSON , this allows you to send and retrieve JSON data from external sources.
+ + + + + + + + + + + + + + + + +ConVars are the easiest way to implement settings for your mod using the Mod Settings API.
+Your mod needs to register itself and all ConVars that are a part of your mod that should be accessible in the Mod Settings menu. To do this, simply add a new script to your mod that runs only in the UI VM like this:
+ "Path": "ui/ms_example_mod.nut",
+ "RunOn": "UI",
+ "UICallback": {
+ "Before": "ExampleMod_AddModSettings"
+ }
+
Inside of the callback specified here, you can add your settings.
+Warning
+ConVar values will only persist if the ConVar has an ARCHIVE flag. For Clients, use FCVAR_ARCHIVE_PLAYERPROFILE
.
All Mod Settings functions have a stackPos
paramter. This parameter should only be changed if you're writing custom wrappers for the settings.
void ModSettings_AddModTitle( string modName, int stackPos = 2 )
+Adds a new category in the settings for your mod
+Note
+It's mandatory to register a mod before you can add any settings
+void ModSettings_AddModCategory( string categoryName )
+Adds a new category to your mod
+Note
+It's mandatory to register a category for your mod. A mod may have multiple categories
+void ModSettings_AddSetting( string conVar, string displayName, string type = "", int stackPos = 2 )
+Adds a basic setting to the last declared category.
+Parameters:
+string conVar
- the ConVar this setting modifiesstring displayName
- The display string of this setting. This can be a localization token.string type = ""
- Optional type of this ConVar. This guards against users inserting invalid values.int stackPos = 2
Types:
+int
bool
float
float2
float3
/ vector
other types will default to setting a string for the ConVar.
+void ModSettings_AddEnumSetting( string conVar, string displayName, array
Adds a setting to the menu that uses an enum. Users can navigate with buttons next to the input between possible values.
+Parameters:
+string conVar
- the ConVar this setting modifiesstring displayName
- The display string of this setting. This can be a localization token.array<string> values
- all possible values of this enum. The ConVar value will be set to the index of the selected value.int stackPos = 2
void ModSettings_AddSliderSetting( string conVar, string displayName, float min = 0.0, float max = 1.0, float stepSize = 0.1, bool forceClamp = false )
+Adds a ConVar setting to the menu that has a slider.
+Parameters:
+string conVar
- the conVar this setting modifiesstring displayName
- The display string of this setting. This can be a localization token.float min = 0.0
- the minimum value of the ConVarfloat max = 0.0
- the maximum value of the ConVarfloat stepSize = 0.1
- the distance between each possible value.bool forceClamp = false
- wether to force the value to round to the nearest interval of stepValue
.Note
+Whenever Mod Settings is used, the value will be clamped to the nearest value available in the slider.
+void ModSettings_AddButton( string buttonLabel, void functionref() onPress, int stackPos = 2 )
+Adds a button to the menu that has a custom click callback.
+Parameters:
+string conVar
- the conVar this setting modifiesvoid functionref() onPress
- callback that gets triggered when this button is pressed.int stackPos
AddModTitle( "#MY_LOCALIZED_MOD_TITLE" )
+
+ AddModCategory( "Gameplay" )
+ AddConVarSetting( "my_mod_gamer_setting", "Gamer Setting", "string" )
+ AddConVarSettingEnum( "my_mod_enum_setting_whatever", "Cool Feature", [ "Disabled", "Enabled" ] )
+
+ AddModCategory( "Visuals" )
+ AddConVarSetting( "my_mod_display_color", "Display Color", "vector" )
+ AddModSettingsButton( "Preview", void function(){ AdvanceMenu( "MyModMenu" ) } ) // Assumes you have "MyModMenu" set up etc.
+
To create custom wrapper functions you need to specify the stack position where the root of your Mod Setting declarations take place.
+ void function AddModSettingsDropDown( string displayName, array<string> options )
+ {
+ NSModSettingsAddButton( displayName, void function() { OpenDropDown( options ) }, 3 )
+ }
+
Note that in this example the stack position is 3
, since AddModSettingsButton
needs to walk one additional step to the callback function.
Note
+All of these functions are only exposed to the UI
VM.
These are functions required for the ingame server browser and the authorization process for the Masterserver and game servers.
+bool NSIsMasterServerAuthenticated()
+Returns true
if the client is authenticated with the Masterserver
bool NSMasterServerConnectionSuccessful()
+Returns true
if a successful connection has been established
void NSTryAuthWithServer( int serverIndex, string password = "" )
+Tries authing with the fetched server at serverIndex
and the provided password
bool NSIsAuthenticatingWithServer()
+Returns true
if the client is currently authing with a game server
bool NSWasAuthSuccessful()
+Returns true
if the client successfully authed with a game server
void NSConnectToAuthedServer()
+Tries to connect to the game server that has previously been authenticated with
+string NSGetAuthFailReason()
+Returns the API reason why the last authentification failed
+void NSTryAuthWithLocalServer()
+Tries to authenticate with the local game server
+void NSCompleteAuthWithLocalServer()
+Call this after NSWasAuthSuccessful
returns true
to complete the local authorization process.
void NSRequestServerList()
+Start fetching all available game servers from the Masterserver
+bool NSIsRequestingServerList()
+Returns true
if the last request by
int NSGetServerCount()
+Returns the total amount of fetched game servers
+void NSClearRecievedServerList()
+Clears all fetched game servers
+array
Returns an array of all available Servers fetched from the Masterserver.
+void AddConnectToServerCallback( void functionref( ServerInfo ) callback )
+Add a callback to be executed right before connecting to a game server via the Server Browser
+void RemoveConnectToServerCallback( void functionref( ServerInfo ) callback )
+Remove a function object from the list of callbacks
+void TriggerConnectToServerCallbacks()
+Runs all callbacks that have been registered with AddConnectToServerCallback
ServerInfo
+Contains all info about a game server.
+int index
+Index of the native Object equivalent
+string id
+ID assigned to the game server by the Masterserver in the registration
+string name
+Name of this game server
+string description
+Description of this game server
+string map
+Unlocalized name of the map that's currently running on the game server
+string playlist
+Unlocalized name of the playlist that's currently running on the game server
+int playerCount
+The total amount of players currently connected to the player
+int maxPlayerCount
+The maximum amount of players that can connect to the server
+bool requiresPassword
+If true
an extra password is required to connect to the server. Otherwise the password is an empty string
string region
+Unlocalized region where the physical server is located
+array
Array of all mods that are required to be loaded on the client to be able to join the server
+RequiredModInfo
+Information of a mod that has to be loaded on the client in order to join a game server
+string name
+Name of the mod
+string version
+Version of the mod
+If you want to store an extended amount of data in your mod it is not sustainable to only use ConVars as they are limited in space and easily reset. With Safe I/O you are able to write to one folder (<profile>/saves/<mod directory name>
). In this folder you can store text files of any type (e.g. .txt
, .json
), it's also possible to use non text file formats (e.g. .exe
) however you won't be able to run them on your PC. It also allows for sub-folders.
To save a file you need the content you want to save as strings-overview
, for this the json_overview
functions can be useful if you want to store table_overview
or arrays_overview
.
To actually save the file you use:
+void NSSaveFile( string file, string data )
+string file
The name of the file you want to store, this supports sub folders. Needs to be with the file type (e.g. /TitanData/tone.txt
).
string data
The saved data, this can be any valid String.
Alternatively if you want a faster way to store table_overview
you can use:
void NSSaveJSONFile(string file, table data)
+string file
The name of the file you want to store, this supports sub folders. Doesn't have to be .json
but will use the correct formatting for a .json
.
table data
The table that will be written to the file, this only supports the types specified in the json_overview
.
void function NSLoadFile( string file, void functionref( string ) onSuccess, void functionref() onFailure = null )
+string file
This is the name of the file you want to load, it has the same formating as in NSSaveFile
.
void functionref( string ) onSuccess
The function that gets execued when the file is successfully loaded, the parameter string
is the content of the loaded file.
void functionref() onFailure = null
The function that gets execued when the loading was NOT successful, by default the function is just null
.
Note
+If you are having trouble with functionrefs you can read up on them here: :ref:functionref_overview
You can also get all saved file:
+array
<profile>/saves/<mod directory name>
.Returns: An array with all file names in the specified path.
+void NSDeleteFile(string file)
+string file
This is the name of the file you want to check exsits, it has the same formating as in NSSaveFile
.
bool NSDoesFileExist(string file)
+string file
This is the name of the file you want to check exsits, it has the same formating as in NSSaveFile
.
Returns: true
if the file was found, otherwise it returns false
.
int NSGetFileSize(string file)
+string file
: This is the name of the file you want to get the file size from.
Returns: KB size of the specified file.
+Warning
+This fucntion will raise an error when the file doesnt exist.
+bool NSIsFolder(string path)
+string file
This is the path you want to check.
Returns: true
if the path is a folder, otherwise it returns false
.
int NSGetTotalSpaceRemaining()
+Returns: Amount of KB you have left to write on.
+Note
+The max size of data you can store is 50MB
per mod. Can be overwritten with -maxfoldersize BYTES
in the launch args.
Server-side Rui provides a set of functions enabling servers to display complex hud elements on clients without requiring a client-side mod. These functions were introduced in Northstar 1.10.0
.
It should be noted that there’s no guarantee the client will see the hud elements.
+Creates a poll on player
.
Definition:
+void NSCreatePollOnPlayer( entity player, string header, array
Example:
+ void function CreateDummyPoll()
+ {
+ array<string> options = [ "Vote for a map!", "Amongsus", "sussy", "when", "1.10", "hi H0l0" ]
+ foreach(entity player in GetPlayerArray())
+ NSCreatePollOnPlayer(player, "Vote who's the biggest dummy!", options, 30)
+ }
+
Definition:
+int NSGetPlayerResponse( entity player )
+Returns the index of the item from options
the player voted for. If the player hadn't voted yet it returns a -1.
Example:
+ void function CheckResponseToDummyPoll(entity player)
+ {
+ if(NSGetPlayerResponse(player) != -1)
+ print("Player has voted!")
+ }
+
Sends a large message to player
which will appear in the top right corner.
Definition:
+void NSSendLargeMessageToPlayer( entity player, string title, string description, float duration, string image )
+Example:
+ void function SendDummyLargeMessage(entity player)
+ {
+ NSSendLargeMessageToPlayer(player,"I'm not a dummy >:(", "You are", 10, "ui/fd_tutorial_tip.rpak")
+ }
+
Sends a smaller message to player
which will appear from the center right.
Definition:
+void NSSendInfoMessageToPlayer( entity player, string text )
+Example:
+ void function SendDummyInfoMessage(entity player)
+ {
+ NSSendInfoMessageToPlayer(player, "we were sent at the same time but I was sent sooner")
+ }
+
Send a small popup to player
which will appear in the lower half of their screen under their cursor.
Definition:
+void function NSSendPopUpMessageToPlayer( entity player, string text )
+Example:
+ void funcions SendDummyPopUp(entity player)
+ {
+ NSSendPopUpMessageToPlayer(player, "very cool text I like")
+ }
+
Sends a large announcement to player
.
Definition:
+void NSSendAnnouncementMessageToPlayer( entity player, string title, string description, vector color, int priority, int style )
+Example:
+ void function SendDummyAnnouncement(entity player)
+ {
+ NSSendAnnouncementMessageToPlayer(player, "Very cool announcement", "Hi Karma", <1,1,0>, 1, ANNOUNCEMENT_STYLE_QUICK)
+ }
+
Status messages allow you to show live data to the player. +Currently status messages are limited to 4 and there's no way to know if the player can see your message.
+ + +Definitions:
+void NSCreateStatusMessageOnPlayer( entity player, string title, string description, string id )
+Creates a status message on player
. id
is used to identify and edit the message, make sure your id is unique! To generate a unique id, use UniqueString().
void NSEditStatusMessageOnPlayer( entity player, string title, string description, string id )
+Allows for editing of the title
and description
of a message which was created using id
.
void NSDeleteStatusMessageOnPlayer( entity player, string id )
+Deletes the status message which was created with id
Examples:
+ void function TestStatusMessage_Threaded(entity player)
+ {
+ string id = UniqueString("votes#")
+ NSCreateStatusMessageOnPlayer(player, "have voted", "[0/12]", id)
+ wait 3
+ NSEditStatusMessageOnPlayer(player, "have voted", "[1/12]", id)
+ wait 10
+ NSDeleteStatusMessageOnPlayer(player, id)
+ }
+
When ejecting the game selects a random number between 0 and 1, if this number is greater than 0.15 then a random common eject message is returned, if it is less than 0.15 then a rare ejection message is returned.
+Using AddCommonEjectMessage( String message )
and AddRareEjectMessage( String message )
in script additional messages can be added to the pool of potential ejection messages
Like most things custom ejection messages can be localised through keyvalues
+There are no functions to remove ejection messages, however existing ones can be altered by modifying localisation files
+Below are a list of useful functions added by Northstar.
+bool function HasWeapon( entity ent, string weaponClassName, array
bool function HasOrdnance( entity ent, string weaponClassName, array
bool function HasCoreAbility( entity ent, string weaponClassName, array
bool function HasSpecial( entity ent, string weaponClassName, array
bool function HasAntiRodeo( entity ent, string weaponClassName, array
bool function HasMelee( entity ent, string weaponClassName, array
bool function HasOffhandForSlot( entity ent, int slot, string weaponClassName, array
bool function WeaponHasSameMods( entity weapon, array
bool function HasOffhandWeapon( entity ent, string weaponClassName )
+bool function PilotHasSniperWeapon( entity player )
+bool function PilotActiveWeaponIsSniper( entity player )
+string function GetActiveWeaponClass( entity player )
+entity function GetPilotAntiPersonnelWeapon( entity player )
+entity function GetPilotSideArmWeapon( entity player )
+entity function GetPilotAntiTitanWeapon( entity player )
+bool function TakePrimaryWeapon( entity player )
+bool function TakeSecondaryWeapon( entity player )
+bool function TakeSidearmWeapon( entity player )
+void function EnableOffhandWeapons( entity player )
+void function DisableOffhandWeapons( entity player )
+void function EnableOffhandWeapons( entity player )
+void function TakeAllWeapons( entity ent )
+void function TakeWeaponsForArray( entity ent, array
bool function PlayerCanTeleportHere( entity player, vector testOrg, entity ignoreEnt = null )
+Note
+Respawn comment next to the function:
+TODO: This is a copy of SP's PlayerPosInSolid(). Not changing it to avoid patching SP. Merge into one function next game
bool function PlayerSpawnpointIsValid( entity ent )
+bool function EntityInSolid( entity ent, entity ignoreEnt = null, int buffer = 0 )
+Note
+Respawn comment next to the function:
+TODO: This function returns true for a player standing inside a friendly grunt. It also returns true if you are right up against a ceiling.Needs fixing for next game
bool function EntityInSpecifiedEnt( entity ent, entity specifiedEnt, int buffer = 0 )
+void function MakeInvincible( entity ent )
+void function ClearInvincible( entity ent )
+bool function IsInvincible( entity ent )
+bool function IsFacingEnemy( entity guy, entity enemy, int viewAngle = 75 )
+bool function PlayerHasTitan( entity player )
+void function ScaleHealth( entity ent, float scale )
+float function GetEntHeight( entity ent )
+float function GetEntWidth( entity ent )
+float function GetEntDepth( entity ent )
+void function PushEntWithVelocity( entity ent, vector velocity )
+vector function GetCenter( array
void function TurretChangeTeam( entity turret, int team )
+void function MakeTurretInvulnerable( entity turret )
+void function MakeTurretVulnerable( entity turret )
+void function UpdateTurretClientSideParticleEffects( entity turret )
+array
entity function GetLocalClientPlayer()
+Note
+this function only exists on clients
+array
Note
+A cleaner way to get a player:
+entity function GetPlayerByIndex( int index )
+array
void function DropWeapon( entity npc )
+Note
+this function only works on NPCs and not on players
+void function ClearDroppedWeapons( float delayTime = 0.0 )
+void function ClearActiveProjectilesForTeam( int team, vector searchOrigin = <0,0,0>, float searchDist = -1 )
+void function ClearChildren( entity parentEnt )
+bool function TitanHasRegenningShield( entity soul )
+void function DelayShieldDecayTime( entity soul, float delay )
+void function GiveAllTitans()
+float ornull function GetTitanCoreTimeRemaining( entity player )
+int function GetCurrentWinner( int defaultWinner = TEAM_MILITIA )
+Note
+Does not work for FFA modes
+string NSGetLocalPlayerUID()
+Returns the local player's UID, else null
.
+Available on CLIENT, UI and SERVER VM.
bool function IsMultiplayer()
+bool function IsSingleplayer()
+string function GetMapName()
+void function WaitTillLookingAt( entity player, entity ent, bool doTrace, float degrees, float minDist = 0, float timeOut = 0, entity trigger = null, string failsafeFlag = "" )
+void function WaitUntilShieldFades( entity player, entity titan, entity bubbleShield, float failTime )
+entity function WaitUntilPlayerPicksUp( entity ent )
+void function WaitForHotdropToEnd( entity titan )
+var function WaittillGameStateOrHigher( state )
+void function WaitTillCraneUsed( entity craneModel )
+void function WaitTillHotDropComplete( entity titan )
+var function WaitForNPCsDeployed( npcArray )
+var function WaittillPlayDeployAnims( ref )
+bool function IsPlayerMalePilot( entity player )
+bool function IsPlayerFemalePilot( entity player )
+void function RandomizeHead( entity model )
+Randomize head across all available heads
+void function RandomizeHeadByTeam( entity model, int headIndex, int numOfHeads )
+Randomize head across heads available to a particular team. Assumes for a model all imc heads are first, then all militia heads are later.
+void function CreateZipline( vector startPos, vector endPos )
+bool function HasTeamSkin( entity model )
+void function DropToGround( entity ent )
+void function DropTitanToGround( entity titan, array
void function TableRemoveInvalid( table
void function TableRemoveInvalidByValue( table
void function TableRemoveDeadByKey( table
array TableKeysToArray( table Table )
+int function array.find(var value)
+this returns -1
if the item was not found in the array
array.fastremove(var)
+Removes a variable by value instead of index.
+array.randomize()
+Reorders the array randomly.
+array.getrandom()
+returns a random element from array
+array.resize(int newSize, var fillValue = 0)
+changes the size of the array to the first int, new slots will be filled with the 2nd argument.
+array.sort( compare_func = null )
+Note
+A few built-in functions you can give as arguments to sort an array.
+int function SortLowest( var a, var b )
+int function SortHighest( var a, var b )
+int function SortItemsAlphabetically(var a, var b )
+int function SortAlphabetize( var a, var b )
+int function SortStringAlphabetize( string a, string b )
+int function SortStringAsset( asset a, asset b )
+int function SortBySpawnTime( entity a, entity b )
+Functions for score comparison
+int function CompareKills( entity a, entity b )
+int function CompareAssaultScore( entity a, entity b )
+int function CompareScore( entity a, entity b )
+int function CompareAssault( entity a, entity b )
+int function CompareDefense( entity a, entity b )
+int function CompareLTS( entity a, entity b )
+int function CompareCP( entity a, entity b )
+int function CompareCTF( entity a, entity b )
+int function CompareSpeedball( entity a, entity b )
+int function CompareMFD( entity a, entity b )
+int function CompareScavenger( entity a, entity b )
+int function CompareFW( entity a, entity b )
+int function CompareHunter( entity a, entity b )
+int function CompareATCOOP( entity a, entity b )
+int function CompareFD( entity a, entity b )
+int function CompareTitanKills( entity a, entity b )
+array.reverse()
+reverse the array in place
+array.slice(int start, int end = null)
+Note
+Returns a section of the array as new array. Copies from start to the end (not included). If start is negative the index is calculated as length + start, if end is negative the index is calculated as length + end. If end is omitted end is equal to the array length.
+var function UniqueString( titleString = "" )
+returns a unique string
+string function StringReplace(string original, string toReplace, string replacement)
+Note
+Returns the new string with the first occurance of the toReplace string."
+string function format( string template, ... )
+Returns a formatted template
+string function Localize( string token )
+Note
+replaces text that should be localized on the client
+ string localized = Localize( token )
+
int ornull function string.find( string s )
+Warning
+returns null
if the string is not found.
You can eliminate the possibility of the returned index being null by casting like this:
+ int ornull index = GetMapName().find( "mp" )
+
+ if( !index )
+ return
+ int( index )
+ int n = index + 1 //now we do not need the ornull anymore
+
string function string.slice( int start, int end = null )
+float function RandomFloatRange( float min, float max)
+int function RandomIntRange( int min, int max )
+int function RandomIntRangeInclusive( int min, int max )
+vector function RandomVec( float range )
+Callbacks from Respawn native code
+Callbacks within squirrel trigger functions when certain events occur.
+They will also often pass arguments to those functions based on the callbacks used.
+Please refer to Custom Northstar Callbacks for callbacks defined in Northstar.
+void AddDamageCallback( string className, void functionref( entity, var ) callbackFunc )
+void RemoveDamageCallback( string className, void functionref( entity, var ) callbackFunc )
+void RunClassDamageCallbacks( entity ent, var damageInfo )
+void AddDamageFinalCallback( string className, void functionref( entity, var ) callbackFunc )
+void RunClassDamageFinalCallbacks( entity ent, var damageInfo )
+void AddPostDamageCallback( string className, void functionref( entity, var ) callbackFunc )
+void RunClassPostDamageCallbacks( entity ent, var damageInfo )
+void AddDamageByCallback( string className, void functionref( entity, var ) callbackFunc )
+void AddDamageCallbackSourceID( int id, void functionref(entity, var) callbackFunc )
+void AddDeathCallback( string className, void functionref( entity, var ) callbackFunc )
+void RemoveDeathCallback( string className, void functionref( entity, var ) callbackFunc )
+void AddSoulDeathCallback( void functionref( entity, var ) callbackFunc )
+void AddCallback_OnTouchHealthKit( string className, bool functionref( entity player, entity healthpack ) callbackFunc )
+void AddCallback_OnPlayerRespawned( void functionref( entity ) callbackFunc )
+void AddCallback_OnPlayerKilled( void functionref( entity victim, entity attacker, var damageInfo ) callbackFunc )
+void AddCallback_OnNPCKilled( void functionref( entity victim, entity attacker, var damageInfo ) callbackFunc )
+void AddCallback_OnTitanDoomed( void functionref( entity victim, var damageInfo ) callbackFunc )
+void AddCallback_OnTitanHealthSegmentLost( void functionref( entity victim, entity attacker ) callbackFunc )
+void AddCallback_OnClientConnecting( void functionref( entity player ) callbackFunc )
+void AddCallback_OnClientConnected( void functionref( entity player ) callbackFunc )
+void AddCallback_OnClientDisconnected( void functionref( entity player ) callbackFunc )
+void AddCallback_OnPilotBecomesTitan( void functionref( entity pilot, entity npc_titan ) callbackFunc )
+void AddCallback_OnTitanBecomesPilot( void functionref( entity pilot, entity npc_titan ) callbackFunc )
+void AddCallback_OnPlayerAssist( void functionref( entity attacker, entity victim ) callbackFunc )
+void AddCallback_EntityChangedTeam( string className, void functionref( entity ent ) callbackFunc )
+void AddCallback_OnTitanGetsNewTitanLoadout( void functionref( entity titan, TitanLoadoutDef newTitanLoadout ) callbackFunc )
+void AddCallback_OnPlayerGetsNewPilotLoadout( void functionref( entity player, PilotLoadoutDef newTitanLoadout ) callbackFunc )
+void AddCallback_OnUpdateDerivedTitanLoadout( void functionref( TitanLoadoutDef newTitanLoadout ) callbackFunc )
+void AddCallback_OnUpdateDerivedPlayerTitanLoadout( void functionref( entity player, TitanLoadoutDef newTitanLoadout ) callbackFunc )
+void AddCallback_OnUpdateDerivedPilotLoadout( void functionref( PilotLoadoutDef newPilotLoadout ) callbackFunc )
+void AddClientCommandCallback( string commandString, bool functionref( entity player, array
void AddPlayerDropScriptedItemsCallback( void functionref(entity player) callbackFunc )
+void RegisterForDamageDeathCallbacks( entity ent )
+void AddTitanCallback_OnHealthSegmentLost( entity ent, void functionref( entity titan, entity victim ) callbackFunc )
+void RemoveTitanCallback_OnHealthSegmentLost( entity ent, void functionref( entity titan, entity victim ) callbackFunc )
+void AddEntityCallback_OnDamaged( entity ent, void functionref( entity ent, var damageInfo ) callbackFunc )
+void RemoveEntityCallback_OnDamaged( entity ent, void functionref( entity ent, var damageInfo ) callbackFunc )
+void AddEntityCallback_OnPostDamaged( entity ent, void functionref( entity ent, var damageInfo ) callbackFunc )
+void RemoveEntityCallback_OnPostDamaged( entity ent, void functionref( entity ent, var damageInfo ) callbackFunc )
+void AddEntityCallback_OnKilled( entity ent, void functionref( entity, var ) callbackFunc )
+void RemoveEntityCallback_OnKilled( entity ent, void functionref( entity, var ) callbackFunc )
+void AddEntityCallback_OnPostShieldDamage( entity ent, void functionref( entity, var, float ) callbackFunc )
+void RemoveEntityCallback_OnPostShieldDamage( entity ent, void functionref( entity, var, float ) callbackFunc )
+void AddPlayerMovementEventCallback( entity player, int playerMovementEvent, void functionref( entity player ) callbackFunc )
+void RemovePlayerMovementEventCallback( entity player, int playerMovementEvent, void functionref( entity player ) callbackFunc )
+void AddCallback_OnPlayerInventoryChanged( void functionref( entity ) callbackFunc )
+void AddPlayerInputEventCallback_Internal( entity player, PlayerInputEventCallbackStruct inputCallbackStruct ) //Not really meant to be used directly unless you know what you're doing! Use utility functions like AddButtonPressedPlayerInputCallback instead
+void RemovePlayerInputEventCallback_Internal( entity player, PlayerInputEventCallbackStruct inputCallbackStruct ) //Not really meant to be used directly unless you know what you're doing! Use utility functions like RemoveButtonPressedPlayerInputCallback instead
+bool InputEventCallbackAlreadyExists( entity player, PlayerInputEventCallbackStruct inputCallbackStruct )
+void AddPlayerHeldButtonEventCallback( entity player, int buttonEnum, void functionref( entity player ) callbackFunc, float buttonHeldTime = 1.0 )
+void RemovePlayerHeldButtonEventCallback( entity player, int buttonEnum, void functionref( entity player ) callbackFunc, float buttonHeldTime = 1.0 )
+bool HeldEventCallbackAlreadyExists( entity player, PlayerHeldButtonEventCallbackStruct callbackStruct )
+void AddButtonPressedPlayerInputCallback( entity player, int buttonEnum, void functionref( entity player ) callbackFunc )
+void RemoveButtonPressedPlayerInputCallback( entity player, int buttonEnum, void functionref( entity player ) callbackFunc )
+void AddButtonReleasedPlayerInputCallback( entity player, int buttonEnum, void functionref( entity player ) callbackFunc )
+void RemoveButtonReleasedPlayerInputCallback( entity player, int buttonEnum, void functionref( entity player ) callbackFunc )
+void RunHeldCallbackAfterTimePasses( entity player, PlayerHeldButtonEventCallbackStruct callbackStruct )
+string GetEndSignalNameForHeldButtonCallback( PlayerHeldButtonEventCallbackStruct callbackStruct )
+bool InputCallbackStructsAreTheSame( PlayerInputEventCallbackStruct callbackStruct1, PlayerInputEventCallbackStruct callbackStruct2 ) //Really just a comparison function because == does a compare by reference, not a compare by value
+bool PlayerInputsMatchCallbackInputs( int cmdsHeld, int cmdsPressed, int cmdsReleased, PlayerInputEventCallbackStruct callbackStruct )
+bool HeldButtonCallbackStructsAreTheSame( PlayerHeldButtonEventCallbackStruct struct1, PlayerHeldButtonEventCallbackStruct struct2 ) //Really just a comparison function because == does a compare by reference, not a compare by value
+void TurnOffInputCallbacksIfNecessary( entity player )
+PlayerInputAxisEventCallbackStruct MakePressedForwardCallbackStruct()
+void AddPlayerPressedForwardCallback( entity player, bool functionref( entity player ) callbackFunc, float debounceTime = 2.0 )
+void RemovePlayerPressedForwardCallback( entity player, bool functionref( entity player ) callbackFunc, float debounceTime = 2.0 )
+PlayerInputAxisEventCallbackStruct MakePressedBackCallbackStruct()
+void AddPlayerPressedBackCallback( entity player, bool functionref( entity player ) callbackFunc, float debounceTime = 2.0 )
+void RemovePlayerPressedBackCallback( entity player, bool functionref( entity player ) callbackFunc, float debounceTime = 2.0 )
+PlayerInputAxisEventCallbackStruct MakePressedLeftCallbackStruct()
+void AddPlayerPressedLeftCallback( entity player, bool functionref( entity player ) callbackFunc, float debounceTime = 2.0 )
+void RemovePlayerPressedLeftCallback( entity player, bool functionref( entity player ) callbackFunc, float debounceTime = 2.0 )
+PlayerInputAxisEventCallbackStruct MakePressedRightCallbackStruct()
+void AddPlayerPressedRightCallback( entity player, bool functionref( entity player ) callbackFunc, float debounceTime = 2.0 )
+void RemovePlayerPressedRightCallback( entity player, bool functionref( entity player ) callbackFunc, float debounceTime = 2.0 )
+void AddPlayerInputAxisEventCallback_Internal( entity player, PlayerInputAxisEventCallbackStruct callbackStruct )
+void RemovePlayerInputAxisEventCallback_Internal( entity player, PlayerInputAxisEventCallbackStruct callbackStruct )
+bool InputAxisEventCallbackAlreadyExists( entity player, PlayerInputAxisEventCallbackStruct callbackStruct )
+bool InputAxisCallbackStructsAreTheSame( PlayerInputAxisEventCallbackStruct callbackStruct1, PlayerInputAxisEventCallbackStruct callbackStruct2 ) //Really just a comparison function because == does a compare by reference, not a compare by value
+bool ShouldRunPlayerInputAxisCallbackFunc( float horizAxis, float vertAxis, PlayerInputAxisEventCallbackStruct callbackStruct )
+bool IsValidPlayerInputAxisEventCallbackStruct( PlayerInputAxisEventCallbackStruct callbackStruct )
+void RunPlayerInputAxisCallbackFunc( entity player, PlayerInputAxisEventCallbackStruct callbackStruct )
+void RunInputAxisCallbackAfterTimePasses( entity player, PlayerInputAxisEventCallbackStruct callbackStruct )
+function( callback )
+void StatsCallback_ItemUnlockUpdate( entity player, float changeInValue, string itemRef )
+void StatsCallback_SubItemUnlockUpdate( entity player, float changeInValue, string fullRef )
+void AddSpawnCallback( string classname, void functionref( entity ) func )
+void AddSpawnCallbackEditorClass( string classname, string editorClassname, void functionref( entity ) func )
+function( entity self )
+function( entity self )
+void RunScriptNameCallbacks( entity ent )
+void AddSpawnCallback_ScriptName( string scriptName, void functionref( entity ) func )
+void RunScriptNoteworthyCallbacks( entity ent )
+void AddScriptNoteworthySpawnCallback( string script_noteworthy, void functionref( entity ) func )
+void PassiveDeathCallback( entity player, var damageInfo )
+void AddCallback_ScriptTriggerEnter( entity trigger, void functionref( entity, entity ) callbackFunc )
+void AddCallback_ScriptTriggerLeave( entity trigger, void functionref( entity, entity ) callbackFunc )
+void AddCallback_OnUseEntity( entity ent, callbackFunc )
+void RunCallbacks_EntitiesDidLoad()
+void AddCallback_EntitiesDidLoad( EntitiesDidLoadCallbackType callback )
+void AddCallback_GameStateEnter( int gameState, void functionref() callbackFunc )
+void GM_SetObserverFunc( void functionref( entity ) callbackFunc )
+void GM_AddPlayingThinkFunc( void functionref() callbackFunc )
+void GM_AddThirtySecondsLeftFunc( void functionref() callbackFunc )
+void GM_SetMatchProgressAnnounceFunc( void functionref( int ) callbackFunc )
+void AddCallback_NPCLeeched( void functionref( entity, entity ) callbackFunc )
+void UpdateDerivedPilotLoadoutData( PilotLoadoutDef loadout, bool doOverrideCallback = true )
+void MarvinSpawnCallback( entity npc_marvin )
+void MortarMissileFiredCallback( entity missile, entity weaponOwner )
+void AutoTitan_NuclearPayload_PostDamageCallback( entity titan, var damageInfo )
+function( pilot, titan )
+function( pilot, titan )
+function( callbackFunc )
+function( callbackFunc )
+void SpectreSuicideOnDamaged_Callback( entity spectre, var damageInfo )
+void AddEarnMeterThresholdEarnedCallback( float thresholdForCallback, void functionref( entity player ) callbackFunc, bool triggerFunctionOnFullEarnMeter = false )
+bool AlreadyContainsThresholdCallback( EarnMeterThresholdEarnedStruct thresholdStruct )
+void SetCallback_EarnMeterGoalEarned( void functionref( entity player ) callback )
+void SetCallback_EarnMeterRewardEarned( void functionref( entity player ) callback )
+void DummyRewardEarnedCallback( entity player )
+void DummyGoalEarnedCallback( entity player )
+void AddCalculateFrontlineCallback( void functionref() callbackFunc )
+bool ScriptCallback_ShouldEntTakeDamage( entity ent, damageInfo )
+function( ent, callbackFunc )
+void Bleedout_SetCallback_OnPlayerStartBleedout( void functionref(entity) callback )
+void Bleedout_SetCallback_OnPlayerGiveFirstAid( void functionref(entity) callback )
+void EmptyDeathCallback( entity _1, var _2 )
+void AddSpectreRackCallback( void functionref( entity, entity ) func )
+void AddOnTetherCallback( void functionref( entity, entity ) callback )
+void VR_GroundTroopsDeathCallback( entity guy, var damageInfo )
+void TryLeechStartCallback( entity self, entity leecher )
+void TryLeechAbortCallback( entity self, entity leecher )
+void AddCallback_ZiplineStart( void functionref(entity,entity) callback )
+void AddCallback_ZiplineStop( void functionref(entity) callback )
+void AddOnRodeoStartedCallback( void functionref(entity,entity) callbackFunc )
+void AddOnRodeoEndedCallback( void functionref(entity,entity) callbackFunc )
+void SetApplyBatteryCallback( void functionref(entity,entity,entity) func )
+void ClientDestroyCallback_ArcCannon_Stop( entity ent )
+void ClientDestroyCallback_GrenadeDestroyed( entity grenade )
+unknown ServerCallback_GuidedMissileDestroyed()
+unknown ServerCallback_AirburstIconUpdate( toggle )
+Client commands are how the clients communicate with the server. Mods can define custom Client Commands that people can then use from the console, or that can be called from a clientside script.
+void AddClientCommandCallback(string, void functionref(entity, array
Registers a function as a callback for a client command. This can only be done once per client command string.
+ AddClientCommandCallback("commandname", commandcallback)
+
+ void CommandCalled(entity player, array<string> args) {
+ print("commandname: was kalled with " + args);
+ }
+
Heres a (incomplete) list of client commands that are used in the game.
+Note
+Please note that this list is very incomplete. If you find any new ones, please PR them into the referenced CSV.
+Client Command | +Arguments | +Description | +
---|---|---|
PrivateMatchSetMode | +mode shortname | +Player changed the mode in private lobby menu | +
SetCustomMap | +map name | +Player changed the map in private lobby menu | +
PrivateMatchSetPlaylistVarOverride | ++ | playlistvaroverride, value | +
RequestPilotLoadout | +index | +Player has changed their loadout to index 2 | +
ClearNewStatus | +pilot_loadout_3 | ++ |
VModEnable | +number | +unknown, logged with number 0 | +
vban | +number | +unknown, logged with number 0 | +
HoldToRodeo | +number0 | +unknown, logged with number 0 | +
ClientStatus | +number | +unknown, logged with number 0 | +
AllDialogueFinished | ++ | unknown | +
After a player receives damage it is stored by the game in an array of the following struct. The information in that struct is final and changing it does not affect the game.
+DamageHistoryStruct
+string attackerName
+Name of the attacker
+string attackerPetName
+Name of the titan
+vector origin
+Position of the victim
+float damage
+The amount of damage inflicted
+int damageType
+A value from the Damage Flags
+int damageSourceId
+Damage souce ID from the gun ( damage-source-id-overview )
+entity attacker
+Entity of the attacker
+int attackerEHandle
+float attackerHealthPercent
+How much health the attacker has in %
+float time
+When the damage was inflicted
+array
Array of mods on the attacking gun
+bool victimIsTitan
+true
if the victim died in the Titan
bool rodeoDamage
+true
if the damage was inflicted in rodeo mode
You can get the damage history for any player entity with player.e.recentDamageHistory
, this is of the type array<DamageHistoryStruct>
and gets automatically updated by the game. The higher the index the older the DamageHistoryStruct
is ( so to get the most recent struct you do player.e.recentDamageHistory[ 0 ]
).
array
entity player
The player you want the damage history from.
float time
How old the damage history can be in seconds.
Returns: All DamageHistoryStruct
found in the given time frame.
DamageHistoryStruct function StoreDamageHistoryAndUpdate( entity storeEnt, float maxTime, float damage, vector damageOrigin, int damageType, int damageSourceId, entity attacker = null, array
void function UpdateDamageHistory( entity player, float maxTime, float time )
+Removes all DamageHistoryStruct
in the time frame time - maxTime
entity player
The player you want to update the damage history from.
float maxTime
How old the damage history can maximally be
float time
How old the damage history can be in seconds.
float function GetLastDamageTime( entity player )
+bool function WasRecentlyHitByEntity( entity player, entity ent, float hitTime )
+bool function WasRecentlyHitForDamage( entity player, float damageAmount, float hitTime )
+bool function WasRecentlyHitForDamageType( entity player, float damageType, float hitTime )
+float function GetTotalDamageTaken( entity player )
+float function GetTotalDamageTakenInTime( entity player, float hitTime )
+array
float function GetTotalDamageTakenByPlayer( entity player, entity attacker )
+array
bool function WasRecentlyHitByDamageSourceId( entity player, int damageSourceId, float hitTime )
+AssistingPlayerStruct function GetLatestAssistingPlayerInfo( entity ent )
+Note
+AssistingPlayerStruct
+entity player
+int damageSourceId
+float assistTime
+array
string function GetLastDamageSourceStringForAttacker( entity victim, entity attacker )
+float function TotalDamageOverTime_BlendedOut( entity soul, float start, float end )
+void function ClearRecentDamageHistory( entity player )
+Dialogs are a way for a client to open a text window with up to 4 buttons.
+All the data in the struct that can be changed.
+DialogData
+var menu
+The instance of the menu
+string header
+The headline of the dialog
+string message
+The body of text under the headline, it supports newline with \n
DialogMessageRuiData &ruiMessage
+Stores relevant RUI data
+array
The colour of the message body, in the format of RGBA
+string image
+Path to the asset of the image displayed on the left of the text body
+string rightImage = $""
+Path to the asset of the image displayed on the right of the text body
+bool forceChoice = false
+unknown
+bool noChoice = false
+unknown
+bool noChoiceWithNavigateBack = false
+unknown
+bool showSpinner = false
+Sets the left image as an animated spinner
+bool showPCBackButton = false
+Shows an additional button below all other buttons that closes the dialog for the client when pressed, works the same as pressing the esc
button
float inputDisableTime = 0
+How long it takes before the client is able to press a button
+table
The int is the index of the Button
+bool darkenBackground = false
+Darkens the colour of the dialog window slightly
+bool useFullMessageHeight = false
+Creates a larger dialog window even if there is no text or buttons to fill that space
+array
Stores the information added by the AddDialogButton
function
array
Stores the information added by the AddDialogFooter
function
DialogMessageRuiData
+string message = ""
+vector style1Color = <1.0, 1.0, 1.0>
+vector style2Color = <0.5, 0.5, 0.5>
+vector style3Color = <0.5, 0.5, 0.5>
+float style1FontScale = 1.0
+float style2FontScale = 1.0
+float style3FontScale = 1.0
+DialogButtonData
+string label
+void functionref() activateFunc
+string focusMessage
+bool startFocused
+DialogFooterData
+string label
+void functionref() activateFunc
+void OpenDialog( DialogData dialog )
+Shows the local player the dialog with the data from the struct.
+DialogData dialog
Instance of a DialogData structExample
+ DialogData dialog
+ dialog.message = "Hello there"
+ OpenDialog( dialog )
+
void AddDialogButton( DialogData dialog, string text, void functionref() callback )
+Add one button to the given struct
+DialogData dialog
Instance of a DialogData struct
string text
The Text that is shown on the button, supports some assets with %ASSET PATH%
void functionref() callback
Function that is executed when the button is pressed.
Example
+ void function SendDialogWithButton()
+ {
+ DialogData dialog
+ dialog.message = "Hello there"
+ AddDialogButton(dialog, "Button 1 %%$r2_ui/menus/loadout_icons/primary_weapon/primary_kraber%%", void function():() {
+ printt( "pressed button 1" )
+ })
+ OpenDialog( dialog )
+ }
+
void AddDialogFooter( DialogData dialog, string text )
+Adds a footer to the dialog struct
+DialogData dialog
Instance of a DialogData struct
string text
The Text that is shown on the button, supports some assets with %ASSET PATH%
bool IsDialogActive( DialogData dialogData )
+DialogData dialog
Instance of a DialogData struct
Returns: true
if the dialog with that struct is currently open, otherwise it returns false
void OpenErrorDialog( string errorDetails )
+string errorDetails
User facing information about the errorthe folowing code produces this output:
+ DialogData dialog
+ dialog.header = "This is the header"
+ dialog.message = "this is the body, it is green \n \n Hello There \n \n General Kenobi"
+ dialog.messageColor = [0,200,0,100]
+ dialog.showSpinner = true
+ dialog.showPCBackButton = true
+ AddDialogButton( dialog, "Button 1 %%$r2_ui/menus/loadout_icons/primary_weapon/primary_kraber%%", ButtonOnePressed )
+ OpenDialog( dialog )
+
There are different Classes for Server and Client. Classes that start with C_
are exclusive to the Client VM and classes that only have the C
Prefix are only usable in the Server VM.
Most entries have three sub entries: The class available to the SERVER, the CLIENT and methods that are available on both VMs.
+For a graphic reprasentation of the Server and Client class inheritance, refer to this chart
+Note
+Pay attention to the extends
keyword for each class! You can use every property of that the parent class has access to!
This List of Classes and their Methods is incomplete!
+Basic entity that most other entities inherit from.
+CBaseEntity / C_BaseEntity
+void Hide()
+Hides the ent. (invisible)
+void Show()
+Shows the ent. (visible)
+void Destroy()
+Destroys this entity.
+void Signal( string signal )
+:doc:../../native/async
Signals on this entity
+void EndSignal( string signal )
+Ends this thread when the identifier is signaled on this entity
+void WaitSignal( string signal )
+Halts this thread until a signal is activated for this entity
+void ConnectOutput( string event, void functionref( entity trigger, entity activator, entity caller, var value ) callback )
+Executes the callback function when the signal is fired.
+void DisconnectOutput( string event, void functionref( entity trigger, entity activator, entity caller, var value ) callback )
+Disconnects the callback from the signal.
+void AddOutput( string outputName, string | entity target, string inputName, string parameter = "", float delay = 0, float maxFires = 0 )
+Connects an output on this entity to an input on another entity via code. The target
can be a name or a named entity.
vector GetOrigin()
+Returns the Coordinates of this entity
+vector EyeAngles()
+Returns the direction this entity is facing to
+vector EyePosition()
+Returns the position of this entities eyes
+entity GetOwner()
+Returns the owner of this entity. Set the owner with SetOwner
entity GetBossPlayer()
+void SetBossPlayer( entity boss )
+string GetClassName()
+Internal class name of the parent class. May return the identifier name for some classes
+bool IsNPC()
+Returns true
if this entity is an NPC
bool IsTitan()
+Returns true
if this entity is a Titan
bool IsHuman()
+Returns true if this entity is a gameplay-wise a human.
+For example, returns true
if the entity is a grunt but false
if the entity is a Titan or spectre etc.
However, players will always be Human, even when they are spectating or their playermodel is robotic (for example when playing as stim)
+bool IsMechanical()
+Returns true
if this entity is mechanical. Examples are Titans and spectres
Note
+This returns true
for players that are playing mechanical classes
bool IsPhaseShifted()
+Returns true
if this entity is currently phase shifting
bool IsPlayer()
+Returns true
if this entity is a player
bool IsProjectile()
+Returns true
if this entity is a projectile
asset GetModelName()
+Returns the asset this entity is being rendered with
+void SetParent( entity parent, ..., string type = "" )
+Binds this entity to the parent. The child inherits the position and rotation of the parent
+void ClearParent( entity parent )
+Make this entity independent from its parent
+entity GetParent()
+Returns this entities parent
+void SetValueForModelKey( asset model )
+Set the model of this entity.
+void SetValueForEffectNameKey( asset effect )
+Similar to SetValueForModelKey
but for FX.
table CreateTableFromModelKeyValues()
+int GetArmorType()
+Returns 0
for light armor and 1
for heavy armor
Titans use heavy armor while pilots and similar use light armor
+int GetMaxHealth()
+Returns the max health of this entity
+int GetHealth()
+Returns the current health
+int GetShieldHealth()
+Returns the current shield health
+int GetShieldHealthMax()
+Returns the maximum shield health of this entity
+bool HasGibModel()
+Returns true
if this entity has gib models
bool HasKey( string key )
+bool IsMarkedForDeletion()
+void SetOrigin( vector position )
+Set the position of this entity
+string GetTargetName()
+int GetTeam()
+Returns the team of this entity
+vector GetAngles()
+Returns the rotation of this entity
+void SetAngles( vector angle)
+Set the rotation of this entity
+var GetValueForKey( string key )
+var Get( string key )
+The same as GetValueForKey
void SetValueForKey( var key, var val )
+var Set( string key )
+The same as SetValueForKey
vector GetVelocity()
+Returns the velocity of this entity
+void Kill_Deprecated_UseDestroyInstead()
+Kill this entity: this function is deprecated because it has a one-frame delay; instead, call ent.Destroy()
+vector GetBoundingMaxs()
+vector GetBoundingMins()
+bool IsInvulnerable()
+returns true
if this entity is invulnerable
vector GetWorldSpaceCenter()
+int Highlight_GetCurrentContext()
+float Highlight_GetCurrentInsideOpacity()
+float Highlight_GetCurrentOutlineOpacity()
+unknown Highlight_GetInheritHighlight()
+int Highlight_GetInsideFunction( int contextID )
+int Highlight_GetOutlineFunction( int contextID )
+float Highlight_GetOutlineRadius()
+unknown Highlight_GetParam( int contextID, int parameterNum )
+int Highlight_GetState( int contextID )
+void Highlight_HideInside( float duration )
+void Highlight_HideOutline( float duration )
+bool Highlight_IsAfterPostProcess( int contextID )
+bool Highlight_IsEntityVisible( int contextID )
+void Highlight_SetCurrentContext( int contextID )
+void Highlight_SetFunctions( int contextID, int hightlightFillID, bool entityVisible, int colorMode, float radius, int highlightID, bool afterPostProcess)
+void Highlight_SetParam( int contextID, int parameterID, vector highlightColor )
+void Highlight_ShowInside( float duration )
+void Highlight_ShowOutline( float duration )
+void Highlight_SetInheritHighlight( bool set )
+void HighlightDisableForTeam( int team )
+void HighlightEnableForTeam( int team )
+void HighlightSetTeamBitField( int bitField )
+int GetEntIndex()
+Returns the index of this entity
+array
entity GetLinkEnt()
+void LinkToEnt( entity ent )
+entity GetLinkParent()
+void Code_SetTeam( int team )
+Set the team for this entity
+Skins based on team do not get updated. Use SetSkin
for this
bool IsCloaked()
+Returns true
if this entity is cloaked
bool IsEntAlive()
+Returns true
if this entity is alive
bool IsValidInternal()
+Returns true
if this entity is Valid.
IsValid( ent )
is a good alternative
vector GetForwardVector()
+Returns a normalized vector pointing forwards away from this entity.
+vector GetRightVector()
+Returns a normalized vector pointing to the right of this entity.
+vector GetUpVector()
+Returns a normalized vector pointing upwards from this entity.
+entity constructor( unknown )
+Depends on the class.
+Returns a new instance of a class.
+You can invoke the constructor with brackets as well, for example like this: CBaseEntity()
void SetDoDestroyCallback( bool doCallBack )
+int GetLifeState()
+void DisableDraw()
+void EnableDraw()
+void SetCanCloak( bool canCloak )
+Allow or disallow this entity to cloak itself
+bool GetCritsPrevented()
+Returns true
if this entity can't be critted.
bool IsHologram()
+Returns true
if this entity is a hologram
bool IsOnGround()
+Returns true
if this entity is touching the ground
void SetModel( asset model )
+Set the model this entity is being rendered with
+void MarkAsNonMovingAttachment()
+string GetScriptName()
+Returns the script name of this entity.
+void SetScriptName( string name )
+Set the script name of this entity.
+bool IsBreakableGlass()
+Returns true
if this entity is breakable glass
bool IsWorld()
+Returns ``true```if this entity is the gameworld
+void DispatchImpactEffects( entity ent, vector startPos, vector endPos, vector hitNormal, entity prop, int propIndex, int damageType, int impactIndex, entity orig, int impactEffectFlags )
+void IsPlayerDecoy()
+Returns true
if this entity is a decoy
void SetPassThroughDirection( float dir )
+void SetPassThroughThickness( float thickness )
+void SetTakeDamageType( int takeDamageType )
+DAMAGE_NO
, DAMAGE_YES
, DAMAGE_EVENTS_ONLY
void SetPreventCrits( bool prevent )
+Set if this entity takes crit damage.
+void SetVelocity( vector vel )
+Set the velocity of this entity.
+void EnableRenderAlways()
+Always render this entity
+void DisableRenderAlways()
+Disable always rendering this entity
+entity GetParentAttachment()
+void SetFadeDistance( int distance )
+Sets the distance between a player and the entity at which the ent will begin to fade out.
+void SetLocalOrigin( vector origin )
+bool HasPusherRootParent()
+void StopPhysics()
+Disable all physics for this entity
+void SetLocalAngles( vector angles )
+void SetParentWithHitbox( entity parent, int hitGroup, bool unknown )
+void RenderWithViewModels( bool renderWith )
+void SetValueForTextureKey( asset texture )
+asset GetValueForModelKey()
+vector GetLocalAngles()
+bool GetNoTarget()
+void SetForceVisibleInPhaseShift( bool visible )
+table GetScriptScope()
+CBaseEntity
+int SetHealth( int health )
+Set current health of this entity
+int SetMaxHealth( int health )
+Set max health of this entity
+void SetOwner( entity owner )
+Set the owner of this entity
+entity GetSpawner()
+void Die()
+Kill this entity in the game sense - all callbacks and signals get triggered
+bool NotSolid()
+Returns false
if this entity is solid
void MoveTo( vector pos, float moveTime, float easeIn = 0, float easeOut = 0 )
+Moves this entity to pos
over the duration of moveTime
with ease in and ease out
Note
+Entites that are not movers get teleported instantly
+void RotateTo( vector pos, float moveTime, float easeIn = 0, float easeOut = 0 )
+Rotate to the specified angles over time with ease in and ease out.
+void ClearInvulnerable()
+Make this entity vulnerable again
+void SetInvulnerable()
+Make this entity invulnerable
+void SetNextThinkNow()
+Trigger AI now
+void SetNoTarget( bool noTarget )
+void SetNoTargetSmartAmmo( bool noTarget )
+void Minimap_SetClampToEdge( bool clamp )
+void Minimap_SetCustomState( int state )
+void Minimap_SetZOrder( int order )
+void Minimap_SetAlignUpright( bool align )
+void Minimap_SetObjectScale( float scale )
+void SetShieldHealth( int )
+void SetShieldHealthMax( int )
+int GetEncodedEHandle()
+void SetUsable( bool usable )
+Make this entity usable
+void SetUsableByGroup( string group )
+Make this entity usable only for a specific group
+void SetUsableRadius( float distance )
+Set the radius in which this entity can be interacted with
+void UnsetUsable()
+Make this entity unusable
+void SetUsableValue( int val )
+void Solid()
+Make this entity solid
+void Fire( string output, string param = "", float delay = 0, entity activator = null, entity caller = null )
+Fire an output on this entity, with optional parm and delay
+void FireNow( string output, string param = "", float delay = 0, entity activator = null, entity caller = null )
+Fire an output on this entity, with optional parm and delay (synchronous)
+void DisableHibernation()
+void SetSize( float width, float height )
+void SetCloakFlicker( float intensity, float duration )
+Trigger cloak flicker effect
+void TakeDamage( int damageAmount, entity attacker_1, entity attacker_2, table { int scriptType, int damageType, int damageSourceId, vector origin, vector force } )
+vector GetCenter()
+void TraceAttackToTriggers( int damageAmount, entity attacker_1, entity attacker_2, table { int scriptType, int damageType, int damageSourceId, vector force }, vector startPos, vector endPos, vector direction )
+void SetBlocksRadiusDamage( bool blocks )
+void SetDamageNotifications( bool getNotifs )
+entity NextMovePeer()
+void SetNameVisibleToEnemy( bool visible )
+void SetNameVisibleToFriendly( bool visible )
+void SetNameVisibleToOwner( bool visible )
+entity FirstMoveChild()
+entity GetRootMoveParent()
+void RemoveFromSpatialPartition()
+void SetUsePrompts( string pc_prompt, string console_prompt)
+void SetAngularVelocity( float x, float y, float z )
+void MakeInvisible()
+Make this entity invisible
+void MakeVisible()
+Make this entity visible
+entity GetGroundEntity()
+vector GetGroundRelativePos()
+int GetPhysicsSolidMask()
+void EnableAttackableByAI( int ai_priority_no_threat, int unknown, int ai_ap_flag )
+Set if this entity can be attacked by AI
+void SetDeathNotifications( bool notifs )
+void SetTitle( string title )
+void SetAbsAngles( vector angles )
+void SetAbsOrigin( void origin )
+void Minimap_AlwaysShow( int team, entity ent )
+void RoundOriginAndAnglesToNearestNetworkValue()
+void ClearBossPlayer()
+Remove boss player reference from this entity.
+void Minimap_DisplayDefault( int team, entity ent )
+void _typeof()
+Prints ent index, classname and target name of this entity to the console.
+void DisableDraw()
+"consider this the mega hide"
+void EnableDraw()
+"it's back!"
+string CreateStringForFunction( function func )
+this is a general purpose function that returns a string which, when executed, runs the given function on this entity.
+the function must be called (or the entity deleted) at some point to avoid leaking the new slot we make in this Table.
+compile with output with compilestring
C_BaseEntity
+string GetSignifierName()
+string GetBossPlayerName()
+void ForceShadowVisible( bool visible )
+void clKill()
+Kill this client side prop.
+float Highlight_GetNearFadeDist()
+void Highlight_ResetFlags()
+void Highlight_SetFadeInTime( float time )
+void Highlight_SetFadeOutTime( float time )
+void Highlight_SetFarFadeDist( float dist )
+void Highlight_SetFlag( int highlightFlag, bool enable )
+void Highlight_SetLifeTime( float time )
+void Highlight_SetNearFadeDist( float dist )
+void Highlight_SetVisibilityType( int type )
+void Highlight_StartOn()
+Starts the highlight with the set configuration
+void DisableRenderWithViewModelsNoZoom()
+void EnableRenderWithCockpit()
+void EnableRenderWithHud()
+void SetAttachOffsetAngles( vector angles )
+void SetAttachOffsetOrigin( vector origin )
+void SetVisibleForLocalPlayer( int visible )
+void InitHudElem( var key )
+string GetTitleForUI()
+float GetCloakFadeFactor()
+int Dev_GetEncodedEHandle()
+Returns the EHandle of this entity.
+int Minimap_GetCustomState()
+int Minimap_GetZOrder()
+void DoDeathCallback( bool doCallback )
+void EnableHealthChangedCallback()
+void HideHUD()
+Hide HUD elements
+void ShowHUD()
+Show HUD elements
+bool IsHUDVisible()
+Return true
if HUD is shown on this entity
CDynamicProp / C_DynamicProp : extends CBaseAnimating / C_BaseAnimating
+CDynamicProp : extends CBaseAnimating
+void SetFullBodygroup( int group )
+C_DynamicProp : extends C_BaseAnimating
+CScriptProp / C_ScriptProp : extends CDynamicProp / C_DynamicProp
+void SetSmartAmmoLockType( int salt )
+int GetScriptPropFlags()
+CScriptProp : extends CDynamicProp
+void SetFootstepType( int type )
+void SetArmorType( int armor )
+void SetScriptPropFlags( int flags )
+CBaseCombatWeapon / C_BaseCombatWeapon : extends CBaseAnimating / C_BaseAnimating
+string GetWeaponDescription()
+Returns the weapon description
+CBaseCombatWeapon : extends CBaseAnimating
+C_BaseCombatWeapon : extends C_BaseAnimating
+Weapons hold by a player or that are lying on the ground are of this type.
+CWeaponX / C_WeaponX : extends CBaseCombatWeapon / C_BaseCombatWeapon
+entity GetWeaponOwner()
+Returns the owner of this weapon
+bool GetAllowHeadShots()
+Returns true
if this weapon can deal crits
float GetMaxDamageFarDist()
+Returns the max damage at the maximum travel distance
+bool GetWeaponSettingBool( int setting )
+Get a weapon setting
+float GetWeaponSettingFloat( int setting )
+Get a weapon setting
+int GetWeaponSettingInt( int setting )
+Get a weapon setting
+vector GetAttackDirection()
+vector GetAttackPosition()
+int GetWeaponPrimaryAmmoCount()
+Return the amount of ammo in a weapon
+int GetWeaponPrimaryClipCount()
+Returns the amount of clips remaining in a weapon
+int GetWeaponPrimaryClipCountMax()
+Returns the maximum ammo in a clip of a primary weapon
+bool IsChargeWeapon()
+Returns true
if this weapon is a charge weapon
void SetNextAttackAllowedTime( float time )
+You need to set a game time as time.
+void SetWeaponChargeFractionForced( float frac )
+void SetWeaponPrimaryClipCount( int )
+string GetWeaponClassName()
+Returns the weapon class name
+var GetWeaponInfoFileKeyField( string key )
+float GetCoreDuration()
+int GetWeaponType()
+array
Get all mods of this weapon
+bool IsWeaponOffhand()
+Returns true
if this weapon is equipped as a offhand weapon
float GetWeaponChargeFraction()
+Returns the percent this weapon has been charged
+float GetWeaponChargeTime()
+Returns the time this weapon needs to be charged
+bool HasMod( string mod )
+Check if the array of mods for this weapon contains mod
int GetWeaponCurrentEnergyCost()
+Returns the amount of energy consumed per use.
+bool GetMeleeCanHitHumanSized()
+bool GetMeleeCanHitTitans()
+void DoMeleeHitConfirmation( float severityScale )
+void EmitWeaponNpcSound_DontUpdateLastFiredTime( int volume, float time )
+int GetDamageAmountForArmorType( int armor )
+float GetMeleeAttackRange()
+float GetMeleeLungeTargetRange()
+void SetMods( array
Set the mods this weapon.
+void EmitWeaponNpcSound( int volume, float duration )
+int GetWeaponDamageFlags()
+Returns a bitflag of damage types this weapon has.
+bool SmartAmmo_IsEnabled( bool enabled )
+int SmartAmmo_GetNumTrackersOnEntity( entity target )
+array
bool SmartAmmo_IsVisibleTarget( entity trackedEnt )
+string GetWeaponClass()
+void SetWeaponSkin( int skin )
+entity FireWeaponGrenade( vector attackPos, vector throwVelocity, vector angularVelocity, float fuseTime, int contactDamageType, int explosionDamageType, bool isPredicted, bool isLagCompensated, bool bounce? )
+int GetScriptFlags0()
+Returns script flags of this weapon.
+bool ShouldPredictProjectiles()
+Returns true
if clients should be predicting the projectiles fired from this weapon.
float GetScriptTime0()
+Returns the script time of this weapon.
+void SetScriptTime0( float gameTime )
+gameTime
needs to be game time. The current game time can be retrieved with Time()
bool IsReloading()
+Returns true
if this weapon is currently being reloaded.
void SetForcedADS()
+Force the holder to ADS this weapon.
+void ClearForcedADS()
+Allow the holder to hipfire.
+void EmitWeaponSound_1p3p(string sound1P, string sound3P)
+int GetChargeAnimIndex()
+void PlayWeaponEffectNoCull(asset effect1P, asset effect3P, string tagName)
+void RegenerateAmmoReset()
+void SetChargeAnimIndex( int index )
+void SetWeaponPrimaryAmmoCount( int count )
+Set the ammo of a primary weapon.
+void StopWeaponEffect(asset effect1P, asset effect3P)
+Stops the effects.
+int GetReloadMilestoneIndex()
+Reload progress. Reloading continues from there.
+int GetAmmoPerShot()
+Returns the amount of ammo that is being used per shot.
+bool IsBurstFireInProgress()
+void PlayWeaponEffect(asset effect1P, asset effect3P, string tagName)
+void StopWeaponSound(string sound)
+float GetSustainedDischargeDuration()
+void SetSustainedDischargeFractionForced(float frac)
+entity FireWeaponMissile(vector origin, vector dir, float missileSpeed, int contactDamageType, int explosionDamageType, bool doPopup, bool predict)
+int GetBurstFireShotsPending()
+bool AllowUse()
+Allow entities to use the weapon.
+void RemoveMod( string mod )
+Remove the passed mod from this weapon.
+array
void SmartAmmo_TrackEntity(entity hitEnt, LMG_SMART_AMMO_TRACKER_TIME)
+void EmitWeaponSound( string sound )
+Play a sound on this weapon.
+float GetWeaponChargeLevel()
+void SetWeaponBurstFireCount(int amount)
+int GetCurrentAltFireIndex()
+void ForceRelease()
+float SetWeaponChargeFraction()
+int GetProjectilesPerShot()
+entity FireWeaponBolt(vector origin, vector dir, float projectileSpeed, int contactDamageType, int explosionDamageType, bool predict, int index)
+bool IsWeaponInAds()
+Returns true
if this weapon is in ADS.
void ResetWeaponToDefaultEnergyCost()
+Reset the amount of energy consumed per use
+void SetWeaponEnergyCost( int cost )
+Set the amount of energy consumed per use.
+entity FireWeaponBullet( vector origin, vector dir, int numBullets, damageType )
+Fires a hitscan bullet from this weapon.
+bool IsWeaponAdsButtonPressed()
+Returns true
while the ADS button is pressed.
float GetWeaponChargeLevelMax()
+bool IsReadyToFire()
+Returns true
if the weapon can be fired.
void SetAttackKickRollScale(float scale)
+int GetShotCount()
+void AddMod( string mod )
+Add a mod to this weapon
+void FireWeaponBullet_Special(vector origin, vector direction, int numShots, int damageType, bool noAntilag, bool noSpread, bool onlyDamageEntitiesOnce, bool noImpactFX, bool noTracers, bool activeShot, bool doTraceBrushOnly)
+string GetWeaponSettingString( string setting )
+void SmartAmmo_UntrackEntity( entity target )
+string GetSmartAmmoWeaponType()
+Check if weaponType is valid: Assert( weaponType in VALID_WEAPON_TYPES )
int GetWeaponBurstFireCount()
+void SmartAmmo_Clear( bool unknown_purpose, bool clearPartialLocks )
+vector SmartAmmo_GetFirePosition( entity target, int burstIndex )
+array
void SmartAmmo_StoreTargets()
+bool IsSustainedDischargeWeapon()
+int GetDamageSourceID()
+float GetGrenadeFuseTime()
+Note that fuse time of 0 means the grenade won't explode on its own, instead it depends on OnProjectileCollision() functions to be defined and explode there.
+void SetWeaponPrimaryClipCountAbsolute(int clipsize)
+entity GetWeaponUtilityEntity()
+bool IsForceRelease()
+bool IsWeaponRegenDraining()
+void SetWeaponPrimaryClipCountNoRegenReset(int clipsize)
+CWeaponX : extends CBaseCombatWeapon
+void SetWeaponUtilityEntity( entity ent )
+void ForceDryfireEvent()
+Force this weapon to dry fire
+void PlayWeaponEffectOnOwner( asset effect, int bodypart )
+Play an effect on the weapon owner
+void ForceReleaseFromServer()
+Will eventually result in Grenade_OnWeaponToss_()
or equivalent function
bool IsForceReleaseFromServer()
+returns true
if this weapon has been forced to be released
C_WeaponX : extends C_BaseCombatWeapon
+void PlayWeaponEffectReturnViewEffectHandle( asset fpEffect, asset unknown_purpose, string tag )
+the second asset is probably the third person effect played.
+void SetViewmodelAmmoModelIndex( int index )
+index
may be the number of rounds in the clip etc.
Projectiles.
+CProjectile / C_Projectile : extends CDynamicProp / C_DynamicProp
+bool GetProjectileWeaponSettingBool( string setting )
+float GetProjectileWeaponSettingFloat( string setting )
+int GetProjectileWeaponSettingInt( string setting )
+string ProjectileGetWeaponClassName()
+void SetImpactEffectTable( string fxTableHandle )
+array
Returns an array of every mod this projectile has equipped
+void SetProjectilTrailEffectIndex( int index )
+Set the projectile trail by index
+void SetProjectileLifetime( float lifetime )
+Set the time after which this projectile gets destroyed programmatically
+string ProjectileGetWeaponInfoFileKeyField( string key )
+void SetReducedEffects()
+Only use reduced effects for this projectile
+asset GetProjectileWeaponSettingAsset( string setting )
+void SetVortexRefired( bool refired )
+Tells the code that the projectile was refired from the vortex so that it uses "projectile_vortex_vscript"
+float GetProjectileCreationTime()
+Get the gametime when this projectile has been created
+asset ProjectileGetWeaponInfoFileKeyFieldAsset( string key )
+CProjectile : extends CDynamicProp
+int ProjectileGetDamageSourceID()
+void ProjectileSetDamageSourceID( int id )
+void SetWeaponClassName( string name )
+void SetProjectileImpactDamageOverride( int flag )
+C_Projectile : extends C_DynamicProp
+Grenade entities in worldspace. Grenades that are equipped ("cooked") by players are instances from the CWeaponX class.
+CBaseGrenade / C_BaseGrenade : extends CProjectile / C_Projectile
+float GetDamageRadius()
+Get the damage radius
+float GetExplosionRadius()
+Get the explosion radius
+void GrenadeExplode( vector normal )
+Explode this grenade now
+entity GetThrower()
+Get the entity that has thrown this grenade
+bool GrenadeHasIgnited()
+Returns true
if this grenade has already been ignited
void GrenadeIgnite()
+void SetDoesExplode( bool explodes )
+void InitMagnetic( float force, string attractKey )
+void ExplodeForCollisionCallback( vector normal )
+void MarkAsAttached()
+CBaseGrenade : extends CProjectile
+void SetGrenadeTimer( float fuseTime )
+Set fuse time for this grenade
+void SetGrenadeIgnitionDuration( float fuseTime )
+Set the ignition duration for this grenade
+C_BaseGrenade : extends C_Projectile
+CMissile / C_Missile : extends CProjectile / C_Projectile
+void MissileExplode()
+Make this missile explode now
+void InitMissileForRandomDriftFromWeaponSettings( vector pos, vector dir )
+void SetHomingSpeeds( int speed, int speed_for_dodging_player )
+void SetMissileTarget( enity target, vector offset )
+void SetMissileTargetPosition( vector pos )
+void InitMissileSpiral( vector pos, vector dir, int missileNumber, bool unknown_purpose1, bool unknown_purpose2 )
+If both slowAndExpand
and consistentSpacing
are true, missiles fly faster instead of normal slowAndExpand
behavior.
void SetSpeed( float speed )
+entity GetMissileTarget()
+void InitMissileExpandContract( vector outward, vector inward, float launchOutTime, float launchInLerpTime, float launchInTime, float launchStraightLerpTime, vector missileEndPos, bool applyRandSpread )
+void InitMissileForRandomDrift( vector pos, vector dir )
+CMissile : extends CProjectile
+C_Missile : extends C_Projectile
+CPlayer / C_Player : extends CBaseCombatCharacter / C_BaseCombatCharacter
+int GetGen()
+Returns the gen of the player account.
+int GetLevel()
+Returns the level of the player account.
+int GetXP()
+Returns the xp of the player account.
+entity GetFirstPersonProxy()
+string GetPlayerClass()
+void Lunge_ClearTarget()
+bool Lunge_IsActive()
+Returns true
if the player is currently lunging to a melee victim.
bool GetForcedDialogueOnly()
+float GetLastPingTime()
+int GetNumPingsAvailable()
+int GetPingGroupAccumulator()
+float GetPingGroupStartTime()
+void SetLastPingTime( float time)
+void SetNumPingsAvailable( int num )
+void SetNumPingsUsed( int num )
+void SetPingGroupAccumulator( int acc )
+void SetPingGroupStartTime( float gametime )
+string GetPlayerName()
+Returns the player account name
+int GetPlayerGameStat( int PGS )
+Returns the score of the player in the provided category. some categories are: PGS_KILLS, PGS_DEATHS, PGS_SCORE etc.
+entity GetPetTitan()
+Returns the soul of the player's autotitan.
+bool GetTitanDisembarkEnabled()
+bool GetTitanEmbarkEnabled()
+bool IsBot()
+void SetTitanDisembarkEnabled( bool enabled )
+void SetTitanEmbarkEnabled( bool enabled )
+string GetPlayerSettings()
+int GetActiveBurnCardIndex()
+Returns the index of the selected burn card.
+int Code_GetActiveBurnCardIndex()
+Use GetActiveBurnCardIndex
instead
string GetPlayerSettingsField( string field )
+int GetCinematicEventFlags()
+entity GetObserverTarget()
+vector GetViewRight()
+vector GetViewVector()
+vector GetViewForward()
+vector GetViewUp()
+int GetPersistentVarAsInt( string key )
+entity GetViewModelEntity()
+int GetOutOfBoundsDeadTime()
+entity GetTitanSoulBeingRodeoed()
+vector CameraAngles()
+Returns the angles of this player's camera.
+float GetObjectiveEndTime()
+entity GetObjectiveEntity()
+int GetObjectiveIndex()
+entity GetPredictedFirstPersonProxy()
+int GetPetTitanMode()
+bool IsWallHanging()
+Returns true
if the player is wall hanging.
float GetNextTitanRespawnAvailable()
+var GetPersistentVar( string key )
+bool HasBadReputation()
+int GetObserverMode()
+float GetPlayerModHealth()
+bool IsInputCommandHeld( int flag )
+int GetPlayerNetInt( string state )
+float GetPlayerNetFloat( string state )
+entity GetHardpointEntity()
+bool GetPlayerNetBool( string key )
+bool IsCrouched()
+Returns true
if the player is crouching.
bool IsStanding()
+Returns true
if the player is standing
void IsTraversing()
+void IsWallRunning()
+Returns true
if the player is wallrunning.
bool IsZiplining()
+Returns true
if the player is currently attached to a zipline
vector Lunge_GetStartPositionOffset()
+void Lunge_SetTargetEntity( entity target, bool unknown_purpose )
+int PlayerMelee_GetState()
+bool PlayerMelee_IsAttackActive()
+void PlayerMelee_SetState( int state )
+void Lunge_EnableFlying()
+vector Lunge_GetEndPositionOffset()
+bool Lunge_IsGroundExecute()
+bool Lunge_IsLungingToEntity()
+void Lunge_LockPitch( bool lock )
+void Lunge_SetEndPositionOffset( vector offset )
+void Lunge_SetTargetPosition( vector pos )
+void PlayerMelee_EndAttack()
+entity PlayerMelee_GetAttackHitEntity()
+void PlayerMelee_SetAttackHitEntity( entity ent )
+void PlayerMelee_SetAttackRecoveryShouldBeQuick( bool beQuick )
+void PlayerMelee_StartAttack( int attackState )
+void SetSelectedOffhandToMelee()
+void Weapon_StartCustomActivity( string animation, bool unknown_purpose )
+float GetPlayerNetTime( string key )
+vector CameraPosition()
+Returns the camera position of this player.
+entity GetPlayerNetEnt( string key )
+bool HasPassive( int passive )
+void Lunge_SetSmoothTime( float time )
+float SmartAmmo_GetHighestLockOnMeFraction()
+array
float SmartAmmo_GetPreviousHighestLockOnMeFraction()
+void Grapple( vector direction )
+bool MayGrapple()
+int GetSuitGrapplePower()
+void SetSuitGrapplePower( float power )
+array
void ClearMeleeDisabled()
+void SetMeleeDisabled()
+void RumbleEffect( int x, int y, int z )
+float GetInputAxisForward()
+Y Axis
+float GetInputAxisRight()
+X Axis
+int GetDodgePower()
+void HolsterWeapon()
+Holsters this weapon. The player can't use it until it's deployed again with DeployWeapon
void DeployWeapon()
+May not work with DeployAndEnableWeapons()
and HolsterAndDisableWeapons()
float GetZoomFrac()
+Returns the fractal of the current weapon zoom. 1.0 is fully zoomed in.
+entity GetRemoteTurret()
+CPlayer : extends CBaseCombatCharacter
+void CockpitStartDisembark()
+void NotifyDidDamage( entity damagedEnt, int hitbox, vector damagePosition, int customDamageType, float damage, int damageFlags, int hitGroup, entity weapon, float distanceFromAttackOrigin )
+void Server_SetDodgePower( float dodgePower )
+void SetDodgePowerDelayScale( float delay )
+void SetPowerRegenRateScale( float scale )
+void SetPersistentVar( string key, var val )
+void ForceStand()
+Force this player to stand up
+void UnforceStand()
+Allow crouching
+void SetPlayerNetBool( string key, bool val )
+void Anim_StopGesture( int gesture )
+void PlayerCone_Disable()
+void PlayerCone_FromAnim()
+void PlayerCone_SetLerpTime( float time )
+void PlayerCone_SetMaxPitch( int maxPitch )
+void PlayerCone_SetMaxYaw( int maxYaw )
+void PlayerCone_SetMinPitch( int min )
+void PlayerCone_SetMinYaw( int min )
+entity CreateAnimatedPlayerDecoy( string decoyType )
+Decoy Types: pt_mp_execution_attacker_hologram_01
, pt_mp_execution_attacker_hologram_02
, pt_mp_execution_attacker_hologram_03
void StopObserverMode()
+void CockpitStartEject()
+void FreezeControlsOnServer()
+void UnfreezeControlsOnServer()
+void CockpitStartBoot()
+void SetStaggering()
+void ForceCrouch()
+Force this player to crouch.
+void UnforceCrouch()
+Allow this player to stand.
+bool IsNoclipping()
+Returns true
if noclip is enabled.
void SetCinematicEventFlags( int flag )
+void SetSyncedEntity( entity synced )
+void SnapEyeAngles( vector angles )
+void SnapFeetToEyes()
+void TouchGround()
+Allows the player to double jump again.
+void ViewOffsetEntity_Clear()
+entity CreatePlayerDecoy( float stickPercentToRun )
+void SetPlayerSettingsWithMods( string settings, array
void Server_TurnOffhandWeaponsDisabledOff()
+void Server_TurnOffhandWeaponsDisabledOn()
+void SetPlayerNetInt( string key, int val )
+void Anim_PlayGesture( string anim3p, float unknown_purpose, float unknown_purpose1, float unknown_purpose2 )
+void Server_TurnDodgeDisabledOff()
+void Server_TurnDodgeDisabledOn()
+void SetGroundFrictionScale( int scale )
+void PlayerCone_SetSpecific( vector viewAngles )
+void GiveExtraWeaponMod( string mod )
+C_Player : extends C_BaseCombatCharacter
+void ClientCommand( string command )
+Executes a command on the player's client. For a server to execute a console command on a client, the client has to launch the game with the norestrictservercommands
launch argument for security reasons.
entity GetCockpit()
+string GetBodyType()
+float GetAdsFraction()
+bool IsInThirdPersonReplay()
+float GetHotDropImpactTime( entity titan = this.titan, string animation = HOTDROP_TURBO_ANIM )
+If called without paramets returns time for the player's titan drop.
+string GetPlayerNameWithClanTag()
+bool HasMic()
+bool InPartyChat()
+bool IsMuted()
+bool IsPartyLeader()
+bool IsTalking()
+void CockpitJolt( vector joltDir, float severity )
+play a jolt effect on the player
+void SetScriptMenuOff()
+void SetScriptMenuOn()
+EntityScreenSpaceBounds GetEntScreenSpaceBounds( entity ent, int padding )
+void HideCrosshairNames()
+Disable showing crosshair names of entities when aiming at them.
+void UnhideCrosshairNames()
+Enable showing crosshair names of entities when aiming at them. (default)
+void FreezeControlsOnClient()
+Stop player input.
+void Rodeo_StartCameraSmoothing( float factor )
+void Rodeo_StopCameraSmoothing( float factor )
+void StartArcCannon()
+void StopArcCannon()
+CTitanSoul / C_TitanSoul : extends CBaseEntity / C_BaseEntity
+entity GetTitan()
+bool HasValidTitan()
+bool IsDoomed()
+Returns true
if this soul is in doomed state
float GetTitanSoulNetFloat( string key )
+entity GetInvalidHealthBarEnt()
+Returns an instance of CNPC_Titan
int GetTitanSoulNetInt( string key )
+float GetLastRodeoHitTime()
+bool IsEjecting()
+int GetStance()
+int GetPlayerSettingsNum()
+float GetCoreChargeExpireTime()
+float GetCoreChargeStartTime()
+float GetNextCoreChargeAvailable()
+CTitanSoul : extends CBaseEntity
+void SetEjecting( bool ejecting )
+void SetPlayerSettingsNum( int enum )
+void SetStance( int stance )
+void SoulDestroy()
+void SetCoreChargeExpireTime( float gametime )
+void SetTitanSoulNetFloat( string key, float val )
+void SetTitanSoulNetFloatOverTime( string key, float val, float time )
+float GetCoreUseDuration()
+void SetTitanSoulNetInt( string key, int val )
+void SetLastRodeoHitTime( float gametime )
+void SetCoreChargeStartTime( float gametime )
+void SetCoreUseDuration( float duration )
+void SetNextCoreChargeAvailable( float time )
+C_TitanSoul : extends C_BaseEntity
+CBaseCombatCharacter / C_BaseCombatCharacter : extends CBaseAnimating / C_BaseAnimating
+entity GetTitanSoul()
+void ContextAction_ClearBusy()
+bool ContextAction_IsActive()
+bool ContextAction_IsBusy()
+void ContextAction_SetBusy()
+vector Anim_GetStartForRefEntity_Old( string anim, vector reference, string optionalTag )
+array
entity GetOffhandWeapon( int slot )
+entity GetActiveWeapon()
+entity GetLatestPrimaryWeapon()
+int GetSkin()
+int LookupSequence( string sequence )
+void SetSkin( int skin )
+entity GetAntiTitanWeapon()
+AnimRefPoint Anim_GetStartForRefPoint( string anim, vector origin, vector angles )
+vector GetPlayerOrNPCViewVector()
+vector Anim_GetStartForRefPoint_Old( animation, origin, angles )
+void Anim_PlayWithRefPoint( string animation, vector origin, vector angles, float blendTime )
+bool IsWeaponDisabled()
+int GetActiveWeaponPrimaryAmmoLoaded()
+bool ContextAction_IsMeleeExecution()
+int GetWeaponAmmoStockpile( entity weapon )
+entity GetMeleeWeapon()
+bool ContextAction_IsMeleeExecutionTarget()
+entity GetFirstRodeoRider()
+Returns the first rodeo rider found or null if there are none.
+int GetNumRodeoSlots()
+Returns number of rodeo slots available on this entity.
+entity GetRodeoRider()
+Returns rodeo rider (if there is one) at the given slot.
+void PhaseShiftBegin( float warmUpTime, float duration )
+void PhaseShiftCancel()
+vector OffsetPositionFromView( vector startPos, vector offset )
+int GetWeaponAmmoLoaded( entity weapon )
+int GetWeaponAmmoMaxLoaded( entity weapon )
+float GetAttackSpreadAngle()
+array
bool ContextAction_IsLeeching()
+void DisablePhaseShiftFlags()
+void EnablePhaseShiftFlags()
+entity GetEntityAtPhaseShiftExitPosition()
+float PhaseShiftTimeRemaining()
+bool CanUseSharedEnergy( int curCost )
+bool CanUseSharedEnergy( int curCost )
+void AddSharedEnergy( int amount )
+int GetSharedEnergyTotal()
+int GetSharedEnergyCount()
+void SetSharedEnergyRegenDelay( float delay )
+void TakeSharedEnergy( int amount )
+CBaseCombatCharacter : extends CBaseAnimating
+void SetFullBodygroup( int group )
+void GetSettingsHeadshotFX()
+Looks for "headshotFX" in an AI settings file or a player set file
+void GiveOffhandWeapon( string ordnanceName, int slot, array
void GiveWeapon( string weapon )
+void SetActiveWeaponByName( string weapon )
+void TakeOffhandWeapon( int slot )
+void TakeWeaponNow( string weapon )
+void TakeWeapon( string weapon )
+int GetOutOfBoundsDeadTime()
+void SetNumRodeoSlots( int )
+Sets the maximum number of rodeo slots available on this entity.
+void SetRodeoRider( int slot, entity rider )
+Sets the rodeo rider at the given slot
+void SetNPCPriorityOverride_NoThreat()
+void SetTitanSoul( entity soul )
+vector GetPlayerOrNPCViewRight()
+void ResetHealthChangeRate()
+C_BaseCombatCharacter : extends C_BaseAnimating
+TraceResults TraceToLocalPlayer()
+float TraceToLocalPlayerSimple()
+CAI_BaseNPC / C_AI_BaseNPC : extends CBaseCombatCharacter
+var Dev_GetAISettingByKeyField( string key )
+Expect as string
+bool IsInterruptable()
+int GetAIClass()
+AIC_SMALL_TURRET
, AIC_MARVIN
, AIC_SPECTRE
, AIC_STALKER_CRAWLING
, AIC_FRAG_DRONE
, AIC_HUMAN
string GetBodyType()
+string GetAISettingsName()
+int GetMeleeDamageMaxForTarget( entity target )
+float AISetting_MaxFlyingSpeed()
+get the current flying speed of the npc.
+string AISetting_LeechAnimSet()
+string AISetting_LeechDataKnifeTag()
+CAI_BaseNPC : extends C_BaseCombatCharacter
+void AssaultPoint( vector point )
+void EnableBehavior( string behaviour )
+void DisableBehavior( string behaviour )
+Possible behaviours: Follow
, Assault
void SetThinkEveryFrame( bool think )
+void ClearEnemy( entity enemy )
+Clears the enemy of this npc.
+void SetEnemy( entity enemy )
+Sets the enemy this npc will attack.
+void Anim_ScriptedPlay( string anim )
+void ForceCheckGroundEntity()
+string GetNPCState()
+float GetMaxEnemyDist()
+Max pilot engagement distance
+float GetMaxEnemyDistHeavyArmor()
+Max titan engagement distance
+float GetMaxTurretYaw()
+void SetSecondaryEnemy( entity enemy )
+void DisableNPCMoveFlag( int flag )
+void EnableNPCMoveFlag( int flag )
+void SetAISettings( string settings )
+void SetCapabilityFlag( int flag, bool active )
+Sets if a capability is available to the player
+void Anim_ScriptedPlayActivityByName( string activity, bool unknown_purpose1, float unknown_purpose2 )
+entity GetEnemy()
+bool CanSee( entity ent )
+Returns true
if the npc can see the ent
.
bool IsCrouching()
+bool IsSecondaryAttack()
+entity GetFollowTarget()
+void InitFollowBehavior( entity followMe, string followBehaviour )
+void DisableNPCFlag( int flag )
+void EnableNPCFlag( int flag )
+void Freeze()
+Freezes all animations and movement of this entity.
+void Unfreeze()
+unfreezes all animations and movement of this entity.
+bool HasXRaySupport()
+Returns if this.supportsXRay
not null.
void ForceCombat()
+Force into combat state by updating NPC's memory of the player.
+bool InCombat()
+Returns true if NPC is in combat.
+C_AI_BaseNPC : extends C_BaseCombatCharacter
+CNPC_Titan / C_NPC_Titan : extends CAI_BaseNPC / C_AI_BaseNPC
+bool GetCanStand()
+CNPC_Titan : extends CAI_BaseNPC
+void SetCanStand( bool canStand )
+void GrappleNPC( vector dir )
+CNPC_Dropship / C_NPC_Dropship : extends CAI_BaseNPC / C_AI_BaseNPC
+bool IsJetWakeFXEnabled()
+CNPC_Dropship : extends CAI_BaseNPC
+C_NPC_Dropship : extends C_AI_BaseNPC
+CNPC_Drone : extends CAI_BaseNPC
+void SetAttackMode( bool attack )
+Set to false
to not attack enemies.
CNPC_SentryTurret / C_NPC_SentryTurret
+CNPC_SentryTurret / C_NPC_SentryTurret : extends CAI_BaseNPC / C_AI_BaseNPC
+int GetTurretState()
+entity GetControlPanel()
+CNPC_SentryTurret : extends CAI_BaseNPC
+void StartDeployed()
+C_NPC_SentryTurret : extends C_AI_BaseNPC
+CFirstPersonProxy / C_FirstPersonProxy : extends CBaseAnimating / C_BaseAnimating
+CFirstPersonProxy : extends CBaseAnimating
+C_FirstPersonProxy : extends C_BaseAnimating
+CBaseAnimating / C_BaseAnimating : extends CBaseEntity / C_BaseEntity
+vector GetAttachmentOrigin()
+int LookupAttachment( string attachment )
+int FindBodyGroup( string group )
+int GetBodyGroupState( int bodyGroupIndex )
+int GetBodyGroupModelCount( int bodyGroupIndex )
+void SetBodygroup( int groupIndex, int newIndex )
+vector GetAttachmentAngles()
+Attachment Anim_GetAttachmentAtTime( string animation, string attachmentName, float time )
+float GetScriptedAnimEventCycleFrac( string anim, string event )
+float GetSequenceDuration( string anim )
+bool Anim_IsActive()
+void Anim_Play( string anim )
+void Anim_SetInitialTime( float time )
+void Anim_Stop()
+vector Anim_GetStartForRefEntity_Old( string anim, vector reference, string optionalTag )
+int GetSkin()
+int LookupSequence( string sequence )
+void SetSkin( int skin )
+AnimRefPoint Anim_GetStartForRefPoint( string anim, vector origin, vector angles )
+vector Anim_GetStartForRefPoint_Old( animation, origin, angles )
+void Anim_PlayWithRefPoint( string animation, vector origin, vector angles, float blendTime )
+void Anim_NonScriptedPlay( string animation )
+bool Anim_HasSequence( string animation )
+void SetPlaybackRate( float rate )
+void Anim_SetStartTime( float time )
+void LerpSkyScale( float skyScale, float time )
+void SetPoseParameter( int pose, float offset )
+vector GetAttachmentForward( int attachID )
+CBaseAnimating : extends CBaseEntity
+int GetFullBodygroup()
+void BecomeRagdoll( vector push, bool skipAnim )
+void Dissolve( int dissolveID, vector normal, int unknown_purpose )
+void Gib( vector forceVec )
+void SetContinueAnimatingAfterRagdoll( bool cont )
+void PlayRecordedAnimation( asset animation, vector unknown_purpose1, vecor unknown_purpose2, float blendTime, entity ref )
+void SetRecordedAnimationPlaybackRate( float rate )
+void Anim_EnablePlanting()
+int LookupPoseParameterIndex( string poseParam )
+void Anim_DisableUpdatePosition()
+C_BaseAnimatin : extends C_BaseEntity
+void SetGroundEffectTable( string tableIdentifier )
+float GetAttachmentOrigin_ViewModelNoFOVAdjust( int index )
+void Anim_SetPaused( bool pause )
+void SetCycle( float cycle )
+void DoBodyGroupChangeScriptCallback( bool doCallback, int bodygroup )
+CPlayerDecoy / C_PlayerDecoy : extends CBaseAnimating / C_BaseAnimating
+CPlayerDecoy : extends CBaseAnimating
+void Decoy_Dissolve()
+Dissolve this decoy. You might want to clear decoy fx with CleanupFXAndSoundsForDecoy( entity decoy )
afterwards.
void SetTimeout( float duration )
+Set the time after which the decoy gets automatically dissolved.
+void SetDecoyRandomPulseRateMax( float pulse_amount_per_second )
+void SetFriendlyFire( bool enabled )
+Sets if friendly fire is enabled for this decoy.
+void SetKillOnCollision( bool kill )
+Sets if this decoy gets killed when colliding with props or geometry.
+CPlayerDecoy : extends CBaseAnimating
+CTurret : extends CBaseAnimating
+void ClearDriver()
+entity GetDriver()
+voit SetDriver( entity driver )
+C_Titan_Cockpit : extends C_BaseEntity
+void AddToTitanHudDamageHistory( int panel, int damage )
+void SetCaptureScreenBeforeViewmodels( bool cap )
+float GetTimeInCockpit()
+Returns the total time of time spent in this cockpit.
+Cockpit booting takes 1.3 seconds, so anything less than 1.3 seconds is still playing the booting animation. You can use TitanCockpit_IsBooting( entity cockpit )
to determine this state.
void SetOpenViewmodelOffset( float x, float y, float z )
+CParticleSystem : extends CBaseEntity
+void FXEnableRenderAlways()
+void SetStopType( string type )
+void SetControlPointEnt( int controlPoint, entity destEnt )
+CVortexSphere / C_VortexSphere : extends CBaseEntity / C_BaseEntity
+int GetBulletAbsorbedCount()
+int GetProjectileAbsorbedCount()
+CVortexSphere : extends CBaseEntity
+void SetGunVortexAngles( vector angles )
+void SetGunVortexAttachment( string attach )
+void SetOwnerWeapon( entity owner )
+void SetVortexEffect( entity fx )
+void DisableVortexBlockLOS()
+entity GetOwnerWeapon()
+void AddBulletToSphere()
+void AddProjectileToSphere()
+void ClearAllBulletsFromSphere()
+void RemoveBulletFromSphere()
+void RemoveProjectileFromSphere()
+C_VortexSphere : extends C_BaseEntity
+CEnvExplosion : extends CBaseEntity
+Elements are notated as VDF
+UI elements are created when a menu is initialized. References to the elements will stay the same, regardless if the menu is open or not.
+It is not possible to create elements at runtime so you have to define all elements a menu or panel contains beforehand in appropriate files.
+An Element is declared in the following way:
+ // please follow this structure
+ ElementName
+ {
+ ControlName name
+ // optional: classname, inheritance, ids ...
+
+ // optional: other properties
+
+ // optional: pinning
+ }
+
If you're working on a menu, you need a menu
object that contains all elements, for example like this:
resource/ui/menus/profiles_menu.menu
+ {
+ menu
+ {
+ ControlName Frame
+ xpos 0
+ ypos 0
+ zpos 3
+ wide f0
+ tall f0
+ autoResize 0
+ visible 1
+ enabled 1
+ pinCorner 0
+ PaintBackgroundType 0
+ infocus_bgcolor_override "0 0 0 0"
+ outoffocus_bgcolor_override "0 0 0 0"
+
+ // elements
+ }
+ }
+
The first line of a .menu
or .res
file needs to be the resource path to itself, starting from the resource folder.
It's not possible to load other files as menus or panels. A .menu
represents an independant menu of the game, while .res
files are "Panels" that can be loaded from other elements.
The rest of the file needs to be wrapped in curly brackets.
+ resource/ui/menus/more/folders/my_menu.menu
+ {
+ MyObject
+ {
+ // object properties go here
+ }
+
+ // more objects ...
+ }
+
Capitalization of the properties shouldn't matter.
+(ElementName)
+This isn't a regular property and comes before the opening bracket
+Unique string identifier used in scripts to look up an element. Every element is required to have a name.
+ControlName
+Controls what type of Component the element is and what parameters have an effect. Every element is required to have control name.
+InheritProperties
+controlSettingsFile
+Load a .res
file. All elements in the settings file are instantiated and set as children of the element.
Hud_GetChild
only works if the parent element is (has the ControlName
) a CNestedPanel!
classname
+Classname used for identifying groups of elements
+scriptID
+Set an unique integer id for this element that's retrievable in script.
+xpos
+Set the base x position relative to the element's sibling position.
+inverted when attached to the left corner or smth
+ypos
+Set the base y position relative to the element's sibling position.
+inverted when attached to the top corner or smth
+zpos
+The layer this element sits in. Elements with a higher z will be prioritized to be selected / focused. They are also drawn on top of elements with a lower z position.
+wide
+Set the base width of this element.
+tall
+Set the base height of this element.
+scale
+Float that scales the element.
+labelText
+Set the label text of this element, if it is a Label.
+textAlignment
+Controls the element boundary point the element's text gets aligned with. east
-> Left, north
-> Top, west
-> Right, south
Bottom.
You can also combine the directions like this: north-west
.
allcaps
+Controls if the text of this element is rendered in all caps. Defaults to 0.
+font
+Set the text font of this element.
+textinsetx
+textinsety
+dulltext
+brighttext
+textalign
+NoWrap
+don't wrape text
+wrap
+wrap text from east
+centerwrap
+wrap text from center
+keyboardTitle
+keyboardDescription
+selectedFont
+text
+multiline
+Set if the text input supports multiline input.
+use_proportional_insets
+pin_to_sibling
+Controls the sibling this element will be pinned to. Takes an element's name as a parameter.
+pin_corner_to_sibling
+Sets which corner of this element is pinned to the sibling.
+pin_to_sibling_corner
+Set to which corner of the sibling this element is pinned to.
+pinCorner
+rui
+image
+vgui asset to render
+scaleImage
+fg_image
+drawColor
+apply this color on top. Format is "r g b a".
+fillcolor
+Fill transparent pixels with this color. Format is "r g b a"
+navUp
+navDown
+navLeft
+navRight
+stepSize
+isValueClampedToStepSize
+visible
+Controls if this element is rendered. Defaults to 1.
+enable
+Controls if this element starts enabled. Defaults to 1.
+auto_wide_to_contents
+auto_wide_tocontents
+auto_tall_tocontents
+enabled
+Controls if this element is enabled. Only enabled elements can be focused / selected. Defaults to 1.
+destination
+frame
+fieldName
+autoResize
+barCount
+barSpacing
+dialogstyle
+style
+command
+ActivationType
+paintbackground
+tabposition
+activeInputExclusivePaint
+paintborder
+CircularEnabled
+CircularClockwise
+consoleStyle
+unicode
+Default
+selected
+maxchars
+listName
+arrowsVisible
+verifiedColumnWidth
+nameColumnWidth
+totalMembersColumnWidth
+chatBorderThickness
+messageModeAlwaysOn
+interactive
+rowHeight
+nameSpaceX
+nameSpaceY
+micWide
+micTall
+micSpaceX
+micOffsetY
+textHidden
+editable
+NumericInputOnly
+allowRightClickMenu
+allowSpecialCharacters
+SelectedTextColor
+SelectedBgColor
+clip
+teamRelationshipFilter
+activeColumnWidth
+happyHourColumnWidth
+onlinePlayersColumnWidth
+PaintBackgroundType
+// 0 for normal(opaque), 1 for single texture from Texture1, and 2 for rounded box w/ four corner textures
+ConVar
+alpha
+conCommand
+minValue
+maxValue
+inverseFill
+syncedConVar
+showConVarAsFloat
+modal
+headerHeight
+panelBorder
+linespacing
+rightClickEvents
+conCommandDefault
+You can declare properties for specific conditions by adding [CONDITION]
after the property value.
When putting a condition after an element's name, the element will only be created if the condition evaluates to true.
+Usable conditions are:
+$WIN32
+game is running on 32 bit windows
+$WINDOWS
+game is running on windows
+$DURANGO
+game is running on xbox
+$PS4
+game is running on a PS4
+$GAMECONSOLE
+$WIDESCREEN_16_9
+game resolution is 16/9
+$LANGUAGE
+the game's language.
+.. code-block:: text
+ // use allcaps only in russian
+ allCaps 0 [!$RUSSIAN]
+ allCaps 1 [$RUSSIAN]
+
On top of that, logical operators like !
, &&
and ||
are available as well.
// This element only shows on pc
+ IngameTextChat [$WINDOWS]
+ {
+ ControlName CBaseHudChat
+ InheritProperties ChatBox
+
+ destination "match"
+
+ visible 0
+
+ pin_to_sibling Screen
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner TOP_LEFT
+ xpos -45
+ ypos -616
+ }
+
+ // This element has different widths depending on the game resolution
+ LoadingTip
+ {
+ ControlName Label
+ ypos 10
+ wide 1630 [$WIDESCREEN_16_9]
+ wide 1441 [!$WIDESCREEN_16_9]
+ auto_tall_tocontents 1
+ labelText ""
+ textalign "north-west"
+ font Default_28
+ wrap 1
+ fgcolor_override "217 170 75 255"
+ visible 0
+
+ pin_to_sibling LoadingGameMode
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ }
+
CENTER
+The calculated center of the element
+TOP
+Element's top y bounds, x axis center.
+BOTTOM
+Element's lowest y bounds, x axis center.
+LEFT
+Element's lowest x bounds, y axis center.
+RIGHT
+Element's highest x bounds, y axis center.
+TOP_LEFT
+Top left corner
+TOP_RIGHT
+Top right corner
+BOTTOM_LEFT
+Bottom left corner
+BOTTOM_RIGHT
+Bottom right corner
+You can calculate the position or dimensions etc. with different units. If you provide no extra unit, the game uses pixels.
+%x
+x percent of the screen.
+.. code:block::
+// cover the entire screen
+width %100
+height %100
+
fx
+use 100%
+c±x
+something with the screen edges not exactly sure how positions get calculated
+To include another KeyValue file, use #base "filepath"
at the top of a VDF file.
Before working on HUD, it's recommended to extract <https://noskill.gitbook.io/titanfall2/intro/duction/vpk-packpack>
_ the englishclient_frontend.bsp.pak000_dir.vpk
vpk. This file contains all vanilla menus and UI logic and will be a very helpful reference!
In your mod.json
, add a Before
UI callback like this:
{
+ "Path": "ui/profiles_menu.nut",
+ "RunOn": "UI",
+ "UICallback": {
+ "Before": "InitProfilesMenu",
+ }
+ }
+
In the script you referenced, create a global in which you register your menu with the AddMenu
like this:
global function InitProfilesMenu
+
+ void function InitProfilesMenu()
+ {
+ AddMenu( "MenuName", $"path/to/menu.menu" )
+ }
+
If you want to, you can add a init to AddMenu
like this: AddMenu( "MenuName", $"path/to/menu.menu", func )
The returns void
and takes no parameters. It gets called once the menu is initialized.
It's recommended to create a file struct in which you store menu states:
+ struct {
+ var menu
+ } file
+
+ void function MenuInitCallback()
+ {
+ file.menu = GetMenu( "MenuName" )
+ }
+
Useless functions have been left out. From _menus.nut
UICodeCallback_ActivateMenus
+var AddMenu( string blockName, asset resourceFile, void functionref() initFunc = null, string displayName = "" )
+Register a normal HUD menu. The init function will be called once all menus are registered and created.
+var AddMenu_WithCreateFunc( string blockName, asset resourceFile, void functionref() initFunc, var functionref( ... ) createMenuFunc )
+Registers a normal HUD menu with a custom function to create the menu. The create function needs to be native since scripts can't create HUD elements.
+var AddPanel( var menu, string panelName, void functionref() initFunc = null )
+var AddSubmenu( blockName, asset resourceFile, void functionref() initFunc = null )
+void AdvanceMenu( string name )
+Push a menu to the stack / open a menu
+void OpenSubmenu( var menu, bool updateMenuPos = true )
+if updateMenuPos
is not null
, the menu is required to have a ButtonFrame
element that is the main content reference.
var GetMenu( string name )
+Get the menu reference
+var GetPanel( string name )
+var GetActiveMenu()
+array GetAllMenuPanels( var menu )
+void CloseActiveMenu( bool cancelled = false, bool openStackMenu = true )
+void CloseAllMenus()
+void CloseAllDialogs()
+void CloseAllToTargetMenu( var menu )
+Close until the menu is the most recent opened.
+void CloseSubmenu( bool openStackMenu = true )
+void CleanupInGameMenus()
+void AddMenuEventHandler( var menu, int event, void functionref() func )
+Accepted events:
+eUIEvent.MENU_OPEN
eUIEvent.MENU_CLOSE
eUIEvent.MENU_SHOW
eUIEvent.MENU_HIDE
eUIEvent.MENU_NAVIGATE_BACK
eUIEvent.MENU_TAB_CHANGED
eUIEvent.MENU_ENTITLEMENTS_CHANGED
eUIEvent.MENU_INPUT_MODE_CHANGED
void AddPanelEventHandler( var panel, int event, void functionref() func )
+Accepted events:
+eUIEvent.PANEL_SHOW
eUIEvent.PANEL_HIDE
void AddButtonEventHandler( var button, int event, void functionref( var ) func )
+void AddEventHandlerToButton( var menu, string buttonName, int event, void functionref( var ) func )
+Add an event handler to an element.
+If you have a reference to the element, use Hud_AddEventHandler
void AddEventHandlerToButtonClass( var menu, string classname, int event, void functionref( var ) func )
+Add a event handler for every element of a class
+var GetTopNonDialogMenu()
+Get the last openend menu that isn't a dialog
+bool IsDialog( var menu )
+Returns true
if the menu is a dialog.
Not recommended to use.
+CloseAllInGameMenus()
+OpenSubmenu( var menu, bool updateMenuPos = true )
+CloseSubmenu( bool openStackMenu = true )
+void PrintMenuStack()
+Debugging
+void AddMenuElementsByClassname( var menu, string classname )
+void FocusDefaultMenuItem( var menu )
+Set the default focus element to be focused
+void FocusDefault( var menu )
+Like FocusDefaultMenuItem
but excludes some menus.
To use footers, add this element to your menu:
+ FooterButtons
+ {
+ ControlName CNestedPanel
+ InheritProperties FooterButtons
+ }
+
void AddMenuFooterOption( var menu, int input, string gamepadLabel, string mouseLabel = "", void functionref( var ) activateFunc = null, bool functionref() conditionCheckFunc = null, void functionref( InputDef ) updateFunc = null )
+Adds a footer to a menu.
+void AddPanelFooterOption( var panel, int input, string gamepadLabel, string mouseLabel = "", void functionref( var ) activateFunc = null, bool functionref() conditionCheckFunc = null, void functionref( InputDef ) updateFunc = null )
+Adds a footer to a panel
+void UpdateFooterOptions()
+Update the footers of the active menu.
+void SetFooterText( var menu, int index, string text )
+Change the text of a specific footer.
+Script methods to manipulate hud elements.
+string Hud_GetHudName( var menu )
+var GetParentMenu( var elem )
+Returns a reference to the menu the passed element is parented to
+var Hud_GetParent( var elem )
+Returns the next higher parent of the element
+bool Hud_HasChild( var elem, string childName )
+Returns true
if the element has a child named like childName
var Hud_GetChild( var elem, string childName )
+Returns the child element of the passed element named like childName
array GetElementsByClassname( var elem, string className )
+Returns all children that have the given class
+var GetElem( var menu, string elementName )
+var Hud_GetScriptID( var elem )
+Returns the script ID of the element declared in the .menu file
+var GetFocus()
+Returns the currently focused element.
+void Hud_SetPos( var elem, int x, int y )
+Set the position of the element relative to the base position.
+var Hud_GetPos( var elem )
+Returns an array of type int[2]
as a var
. The position is relative to the element's base position.
void Hud_SetX( var elem, int x )
+Only change the x position relative to the base position.
+void Hud_SetY( var elem, int y )
+Only change the y position relative to the base position.
+int Hud_GetX( var elem )
+Returns the x position of the element relative to it's base position.
+int Hud_GetY( var elem )
+Returns the y position of the element relative to it's base position.
+void Hud_ReturnToBasePos( var elem )
+Set the position of this element to it's base position.
+var Hud_GetBasePos( var elem )
+Returns an orray of type int[2]
as a var
. Base position is always [0,0]
int Hud_GetBaseX( var elem )
+Returns the base x of this element.
+var Hud_GetBaseY( var elem )
+Returns the base y of this element.
+var Hud_GetAbsPos( var elem )
+Returns an array of type int[2]
as a var
. Absolute coordinates on the screen of this element.
int Hud_GetAbsX( var elem )
+Returns the absolute x position on the screen of this element.
+int Hud_GetAbsY( var elem )
+Returns the absolute y position of the screen of this element.
+!!! cpp-function "void Hud_SetXOverTime( var elem, int x, float transitionTime, int interpolation_mode "= 0 )
+Move to relative x over time with interpolation.
+
+* ``INTERPOLATOR_LINEAR``: linear interpolation
+
+* ``INTERPOLATOR_ACCEL``: move with accelerating speed
+
+* ``INTERPOLATOR_DEACCEL``: move with deaccelerating speed
+
+* ``INTERPOLATOR_PULSE``: one time bounce
+
+* ``INTERPOLATOR_FLICKER``: no transition
+
+* ``INTERPOLATOR_SIMPLESPLINE``: ease in / out
+
+* ``INTERPOLATOR_BOUNCE``: gravitational bounce
+
!!! cpp-function "void Hud_SetYOverTime( var elem, int y, float transitionTime, int interpolation_mode "= 0 )
+Move to relative y over time with interpolation
+
!!! cpp-function "void Hud_MoveOverTime( var elem, int x, int y, float transitionTime, int "interpolation_mode = 0 )
+float Hud_GetRotation( var elem )
+Returns the angles of the element
+void Hud_SetRotation( var elem, float angles )
+Set the angles of the element
+void Hud_Show( var elem )
+Make this element visible
+void Hud_Hide( var elem )
+Make this element invisible
+bool Hud_IsVisible( var elem )
+Returns true
if the element is visible
void Hud_SetVisible( var elem, bool visible )
+Set if the element is visible
+void Hud_SetColor( var elem, int r, int g, int b, int alpha )
+Set the color of the element
+!!! cpp-function "void Hud_ColorOverTime( var elem, int r, int g, int b, int alpha, float time, int "accel )
+Change the color of the element over time
+
!!! cpp-function "void Hud_ColorOverTimeDelayed( var elem, int r, int g, int b, int alpha, float "time, ,float delay, int accel )
+Change the color of the element over time
+
void Hud_SetAlpha( var elem, int alpha )
+Change the opacity of the element
+var Hud_GetBaseColor( var elem )
+var Hud_GetBaseAlpha( var elem )
+void Hud_SetPanelAlpha( var elem )
+void Hud_FadeOverTime( var elem, int fadeTarget, float fadeTime )
+Change the opacity of the element over time
+void Hud_FadeOverTimeDelayed( var elem, int target, float delay, float accel )
+Change the opacity of the element over time after a delay
+int Hud_GetWidth( var elem )
+Returns the current width of the element.
+void Hud_SetWidth( var elem, int width )
+Set the width of an element.
+int Hud_GetBaseWidth( var elem )
+Returns the width an element got initialized with.
+int Hud_GetHeigth( var elem )
+Returns the current height of an element.
+void Hud_SetHeigth( var elem, int height )
+Set the heigth of an element.
+int Hud_GetBaseHeigth( var elem )
+Returns the heigth an element got initialized with.
+var Hud_GetSize( var elem )
+Returns an array of type int[2]
as a var
. The first index is width and the second height of the element.
void Hud_SetSize( var elem, int x, int y )
+Set width and height of the element.
+var Hud_GetBaseSize( var elem )
+Returns the width and height values the element got initialized with as an array of type int[2]
as var
.
!!! cpp-function "void Hud_ScaleOverTime( var elem, float width_factor, float height_factor, float "time, int interpolation_mode )
+Set the width and height of the element over time.
+
+The final width and height is calculated like this: ``width * width_factor``
+
void Hud_SetScaleX( var elem, float xStretch )
+Set the width of the element calculated with a factor.
+void Hud_SetScaleY( var elem, float yStretch )
+Set the height of the element calculated with a factor.
+void Hud_ReturnToBaseSize( var elem )
+Return to base width and height
+void Hud_SetText( var elem, string text )
+Set the text content of this element
+string Hud_GetText( var elem )
+Returns the current text of the element. Useful for text inputs
+void RHud_SetText( var elem, string text )
+Set the text of an rui, if the element contains an rui that takes string input.
+void Hud_SetUTF8Text( var elem, string text )
+string Hud_GetUTF8Text( var elem )
+bool Hud_IsLocked( var elem )
+Returns true
if the element is locked.
Locked elements are visible, can be focused and selected but don't trigger events and play a locked sound if they are selected
+void Hud_SetLocked( var elem, bool locked )
+Set this element locked status
+bool Hud_IsEnabled( var elem )
+Returns true
if the element is enabled
Disabled elements are visible but can't be focused or selected and don't trigger events.
+void Hud_SetEnabled( var elem, bool enabled )
+Set this element to be enabled / disabled
+bool Hud_IsFocused( var elem )
+Returns true
if this element is focused
Focused elements will be selected when pressing enter
+void Hud_SetFocused( var elem )
+Set the element to be focused
+bool Hud_IsSelected( var elem )
+Returns true
if this element is selected
void Hud_SetSelected( var elem, bool selected )
+Set this element to be selected / not unselected
+void Hud_SelectAll( var elem )
+Select this element and all children
+bool Hud_IsLabel( var elem )
+Returns true
if the element is a label
bool Hud_IsRuiPanel( var elem )
+Returns true
if this element can contain ruis
var Hud_GetRui( var elem )
+Returns the rui instance of this element.
+void Hud_SetNavUp( var elem, var navTo )
+Set the element that will be selected when navigating up (arrow up) from this selected element.
+void Hud_SetNavDown( var elem, var navTo )
+Set the element that will be selected when navigating up (arrow up) from this selected element.
+void Hud_SetNavLeft( var elem, var navTo )
+Set the element that will be selected when navigating left (arrow left) from this selected element.
+void Hud_SetNavRight( var elem, var navTo )
+Set the element that will be selected when navigating right (arrow right) from this selected element.
+void Hud_HandleEvent( var elem, int event )
+Fire the specified event for the element
+var Hud_AddEventHandler( var elem, int event, var functionref( var button ) )
+Handle an event for the element
+Accepted events:
+UIE_CLICK
UIE_GET_FOCUS
UIE_LOSE_FOCUS
UIE_CHANGE
void Hud_SetNew( var elem, bool isNew )
+void Hud_SetImage( var elem, asset image )
+Set the image displayed by the element, if the elements controlName allows for it.
+void Hud_EnableKeyBindingIcons( var elem )
+void Hud_RunAnimationScript( var elem, string animation )
+void Hud_SliderControl_SetStepSize( var elem, float size )
+void Hud_SliderControl_SetMin( var elem, float min )
+void Hud_SliderControl_SetMax( var elem, float max )
+float Hud_SliderControl_GetCurrentValue( var elem )
+void Hud_SetBarProgress( var elem, float progress )
+void Hud_SetGamemodeIdx( var elem, int index )
+void Hud_SetPlaylistVarName( var elem, string playlist )
+void Hud_DialogList_AddListItem( var elem, string val, string enum_ )
+string Hud_GetListPanelSelectedItem( var elem )
+Movers are entites that move and rotate smoothly.
+script_mover
allows for smooth movement and rotation contrary to script_mover_lightweight
which is not able to rotate.
entity CreateExpensiveScriptMover( vector origin , vector angles )
+returns script_mover
entity CreateExpensiveScriptMoverModel( asset model, vector origin, vector angles, int solidType, float fadeDist )
+returns script_mover
which has a model
entity CreateScriptMover( vector origin, vector angles )
+returns script_mover_lightweight
entity CreateScriptMoverModel( asset model, vector origin, vector angles, int solidType, float fadeDist )
+returns script_mover_lightweight
which has a model
entity CreateOwnedScriptMover( entity owner )
+returns script_mover
which will be at the location of the owner
void NonPhysicsMoveTo( vector position, float time, float easeIn, float easeOut )
+void NonPhysicsMoveInWorldSpaceToLocalPos( vector localPos, float time, float easeIn, float easeOut )
+void NonPhysicsMoveWithGravity( vector velocityForOthers, vector fakeGravity )
+void NonPhysicsRotateTo( vector angles, float time, float easeIn, float easeOut )
+Only usable by expensive movers
+void NonPhysicsRotate( vector angles, float speed )
+Only usable by expensive movers
+void NonPhysicsStop()
+Immediately stop this mover from moving
+void ChangeNPCPathsOnMove( bool recalculate )
+void SetPusher( bool isPusher )
+Pushers move everything that stands on top of them or next to them and can also kill entities by crushing them.
+void NonPhysicsSetRotateModeLocal( bool useLocal )
+void PhysicsDummyEnableMotion( bool enabled )
+ entity mover = CreateScriptMover( <0,0,0> )
+
+ // moving the mover to <0,0,10> in 1 second
+ mover.NonPhysicsMoveTo( <0,0,10>, 1, 0, 0 )
+
+ // wating so the mover gets to the destination
+ wait 1
+
+ // sending it back
+ mover.NonPhysicsMoveTo( <0,0,0>, 1, 0, 0 )
+
+ // then stoping the mover in 0.5 seconds
+ wait 0.5
+ mover.NonPhysicsStop()
+
// creating a elevator
+ // using a mover with a model
+ entity mover = CreateScriptMoverModel( $"models/props/turret_base/turret_base.mdl", < -40.5605, -1827.87, -223.944 >, <0,0,0>, SOLID_VPHYSICS, 1000 )
+ mover.SetPusher( true ) // making it into a pusher
+
+ // for loop to move the elevator up and down
+ for(;;)
+ {
+ mover.NonPhysicsMoveTo( < -35.4312, -1827.87, 523.046 >, 4.8, 0.1, 0.1 )
+ wait 6
+
+ mover.NonPhysicsMoveTo( < -35.4312, -1827.87, -223.944 >, 4.8, 0.1, 0.1 )
+ wait 6
+ }
+
// Phase shifting the player to a destination
+ // setting a position
+ vector destination = <250,1000,100>
+ // checking if the player is valid
+ if ( IsValid( player ) )
+ {
+ // creating the mover
+ entity mover = CreateOwnedScriptMover( player )
+ player.SetParent( mover ) // parenting the player ( so they move with the mover )
+ mover.NonPhysicsMoveTo( destination, 0.5, 0, 0 ) // saying to the moveer to move
+ vector angles = player.GetAngles() // angles saved
+ PhaseShift( player, 0.1, 1 ) // phase shifitng the player
+ player.SetAngles( angles ) // setting the player to the original angles
+ }
+ wait 0.6 // wating so the mover moves
+
+ if ( IsValid( player ) )
+ {
+ player.ClearParent() // removing the player from the mover
+ }
+
Warning
+Not all of these are fully implemented, either check the asset dump or try it out.
+void CodeCallback_PlayerDecoyDie( entity, int )
+void CodeCallback_PlayerDecoyDissolve( entity, int )
+void CodeCallback_PlayerDecoyRemove( entity, int )
+void CodeCallback_PlayerDecoyStateChange( entity, int, int )
+void CodeCallback_ScriptedDialogue( entity guy, int dialogueID )
+void CodeCallback_LeechStart( entity player, entity target )
+var CodeCallback_ClientCommand( entity player, array< string > args )
+void CodeCallback_DamagePlayerOrNPC( entity ent, var damageInfo )
+void CodeCallback_DamageEntity( entity ent, var damageInfo )
+void CodeCallback_OnEntityDestroyed( entity ent )
+void CodeCallback_PreSpawn( entity ent )
+void CodeCallback_OnSpawned( entity ent )
+void CodeCallback_OnPlayerKilled( entity player, var damageInfo )
+void CodeCallback_OnNPCKilled( entity npc, var damageInfo )
+void CodeCallback_OnEntityKilled( entity npc, var damageInfo )
+void CodeCallback_OnEntityChangedTeam( entity ent )
+void CodeCallback_NPCMeleeChargedPlayerOrNPC( entity ent, var damageInfo )
+int CodeCallback_GetWeaponDamageSourceId( entity weapon )
+void CodeCallback_WeaponFireInCloak( entity player )
+void CodeCallback_OnServerAnimEvent( entity ent, string name )
+void CodeCallback_OnNPCLookAtHint( entity npc, entity hint )
+bool CodeCallback_OnTouchHealthKit( entity player, entity ent )
+void CodeCallback_OnInventoryChanged( entity player )
+void CodeCallback_OnMeleeKilled( entity target )
+void CodeCallback_StartRodeo( entity player, entity rodeoTarget)
+void CodeCallback_ForceEndRodeo( entity player )
+void CodeCallback_EmbarkTitan( entity player, entity titan)
+bool CodeCallback_EmbarkTitanFromGrapple( entity player, entity titan )
+void CodeCallback_OnWeaponAttack( entity player, entity weapon, string weaponName, int ammoUsed )
+Warning
+This code callback does not work
+void CodeCallback_OnPrecache()
+void CodeCallback_OnVehiclePass( table params )
+bool CodeCallback_ForceAIMissPlayer( entity npc, entity player )
+void CodeCallback_GamerulesThink()
+void CodeCallback_OnPlayerRespawned( entity player )
+void CodeCallback_OnClientConnectionStarted( entity player )
+void CodeCallback_OnClientConnectionCompleted( entity player )
+void CodeCallback_OnClientDisconnected( entity player, string reason )
+void CodeCallback_PlayerHasBeenConnectedForDuration( entity player, float durationInSeconds )
+void CodeCallback_OnClientSendingPersistenceToNewServer( entity player )
+void CodeCallback_MatchIsOver()
+void CodeCallback_OnPlayerMatchmakingChanged( entity player )
+void CodeCallback_OnPlayerJump( entity player )
+void CodeCallback_OnPlayerDoubleJump( entity player )
+void CodeCallback_OnPlayerDodge( entity player )
+void CodeCallback_OnPlayerLeaveGround( entity player )
+void CodeCallback_OnPlayerTouchGround( entity player )
+void CodeCallback_OnPlayerMantle( entity player )
+void CodeCallback_OnPlayerBeginWallrun( entity player )
+void CodeCallback_OnPlayerEndWallrun( entity player )
+void CodeCallback_OnPlayerBeginWallhang( entity player )
+void CodeCallback_OnPlayerEndWallhang( entity player )
+void CodeCallback_OnPlayerInputCommandChanged( entity player, int cmdsHeld, int cmdsPressed, int cmdsReleased )
+void CodeCallback_OnPlayerInputAxisChanged( entity player, float horizAxis, float vertAxis )
+void CodeCallback_OnPlayerGrappled( entity attacker, entity victimPlayer )
+void CodeCallback_OnProjectileGrappled( entity attacker, entity projectile )
+void CodeCallback_OnLoadSaveGame()
+void CodeCallback_OnClientReloadConnectionCompleted( entity player )
+bool CodeCallback_OnSavedSaveGame()
+bool CodeCallback_IsSaveGameSafeToCommit()
+void CodeCallback_OnTetherRemove( entity player, int tetherID )
+void CodeCallback_OnTetherDamageMilestone( entity player, int tetherID, int damageMileStoneIndex, float health )
+void CodeCallback_ClaimClientSidePickup_MatchCandy( entity player, int amount, int flags, int recieptID )
+void CodeCallback_WeaponDropped( entity weapon )
+void CodeCallback_OnWeaponReload( entity weapon )
+void CodeCallback_GivePersistentItem( entity player, string itemName, int count )
+void CodeCallback_OnWeaponTouch( entity player, entity weapon, int ammoRecieved )
+bool CodeCallback_CanUseEntity( entity player, entity ent )
+bool CodeCallback_OnUseEntity( entity player, entity ent )
+void CodeCallback_OnUsePressed( entity player )
+void CodeCallback_OnUseReleased( entity player )
+bool CodeCallback_IsLeechable( entity player, entity target )
+void CodeCallback_Ping( entity player )
+void CodeCallback_OnMeleePressed( entity player )
+void CodeCallback_OnMeleeReleased( entity player )
+void CodeCallback_OnMeleeAttackAnimEvent( entity player )
+void CodeCallback_AnimationDone( entity ent )
+void CodeCallback_AnimationInterrupted( entity ent )
+void CodeCallback_PlayerClassChanged( entity ent )
+bool CodeCallback_CanUseZipline(entity player, entity zipline, vector ziplineClosestPoint )
+void CodeCallback_ZiplineMount( entity player, entity zipline )
+void CodeCallback_ZiplineStart( entity player, entity zipline )
+void CodeCallback_ZiplineMove( entity player, entity zipline )
+void CodeCallback_ZiplineStop( entity player )
+bool CodeCallback_IsValidRodeoTarget( entity player, entity rodeoTarget )
+var CodeCallback_OnRodeoAttach( entity rider, entity rodeoTarget )
+void CodeCallback_TitanRiderEntVarChanged( entity soul, int rodeoSlotIndex, entity oldRider, entity newRider )
+bool CodeCallback_OnVortexHitBullet( entity weapon, entity vortexSphere, var damageInfo )
+bool CodeCallback_OnVortexHitProjectile( entity weapon, entity vortexSphere, entity attacker, entity projectile, vector contactPos )
+void CodeCallback_OnTurretCancelPressed( entity player )
+void CodeCallback_ForceScriptError( entity ent, string errorMsg )
+void CodeCallback_EnterPhaseShift( entity ent )
+void CodeCallback_ExitPhaseShift( entity ent )
+string CodeCallback_CheckPassThroughAddsMods( entity player, entity hitEnt, string currWeaponName )
+bool CodeCallback_IsValidMeleeExecutionTarget( entity attacker, entity target )
+bool CodeCallback_IsValidMeleeAttackTarget( entity attacker, entity target )
+void CodeCallback_OnGrapple( entity attacker, entity target, vector hitpos, vector hitNormal )
+bool AABBIntersectsAABB( vector a1, vector a2, vector a3, vector b1, vector b2, vector b3, number c1 )
+bool OBBIntersectsOBB( vector a1, vector a2, vector a3, vector a4, vector b1, vector b2, vector b3, vector b4, number c1 )
+bool TraceLOSMultiple( array startsArray, array endsArray, entity ignoreEntity, int mask, int group )
+Do muliple LOS checks, early out if any return true. Runs on multiple threads.
+mask
: TRACE_MASK_*
group
: TRACE_COLLISION_GROUP_*
TraceResults TraceLine( vector startPos, vector endPos, var ignoreEntOrArrayOfEnts = null, int traceMask = 0, int collisionGroup = 0 )
+Does a trace and returns struct of result values.
+TraceResults TraceLineHighDetail( vector startPos, vector endPos, var ignoreEntOrArrayOfEnts = null, int traceMask = 0, int collisionGroup = 0 )
+Does a high-detail (per poly on static models) trace and returns struct of result values.
+TraceResults TraceHull( vector startPos, vector endPos, vector hullMins, vector hullMaxs, var ignoreEntOrArrayOfEnts = null, int traceMask = 0, int collisionGroup = 0 )
+Does a hull trace and returns table of result values.
+TraceResults TraceLineNoEnts( vector startPos, vector endPos, int traceMask = 0 )
+Does a trace and returns table of result values.
+float TraceLineSimple( vector startPos, vector endPos, entity ignoreEnt )
+Does a trace and returns the distance from startPos
to hit.
float TraceHullSimple( vector startPos, vector endPos, vector hullMins, vector hullMaxs, entity ignoreEnt )
+Does a trace and returns the distance from startPos
to hit.
void DoTraceCoordCheck( bool check )
+array
Does a trace and returns all ents along a line.
+bool CheckPassThroughDir( entity ent, vector dir, vector endPos )
+bool IsPointInFrontofLine( vector point, vector startPos, vector endPos )
+array
Returns an array of entities that are inside of a cone and visible to the apex
+VortexBulletHit ornull VortexBulletHitCheck( entity attacker, vector startPos, vector endPos )
+Check for vortexSphere collisions between two points.
+ + + + + + + + + + + + + + + + +string GetConVarString( string cv )
+Gets the value of a convar as a string
+int GetConVarInt( string cv )
+Gets the value of a convar as an integer
+float GetConVarFloat( string cv )
+Gets the value of a convar as a float
+bool GetConVarBool( string cv )
+Gets the value of a convar as a boolean
+bool EverythingUnlockedConVarEnabled()
+void SetConVarString( string cv, string value )
+Sets the value of a convar with a string
+void SetConVarInt( string cv, int value )
+Sets the value of a convar with an integer
+void SetConVarFloat( string cv, float value )
+Sets the value of a convar with a float
+void SetConVarBool( string cv, bool value )
+Sets the value of a convar with a boolean
+void SetConVarToDefault( string cv )
+Sets the value of a convar to its internal default value
+ + + + + + + + + + + + + + + + +Created entites do not spawn until they are dispatched with DispatchSpawn
.
+Some script wrappers may dispatch entites themselves.
Getting entities is documented here
+entity CreateProp( string type, vector origin, string s1, number n1 )
+void DispatchSpawn( entity ent )
+Tells the specified entity to spawn. Should only be called once per entity.
+entity Entities_CreateByClassname( string className )
+entity Entities_CreateProjectileByClassname( string entName, string weaponClassName )
+entity Entities_CreateByTemplate( string template )
+array Entities_CreateByTemplateMultiple( string template )
+Create zero or more entities from templates that match the given string, and return them as an array. Wildcards allowed.
+All array contents are entites but the array is not typed itself.
+array Entities_CreateByPointTemplates( string matchingString , vector origin, vector angles )
+Create zero or more entities from point-templates that match the given string, and return them as an array. Wildcards allowed
+entity CreateWeaponEntityByName( string weaponName, vector origin, vector angles )
+entity CreateWeaponEntityByNameConstrained( string name, vector origin, vector angles )
+entity CreateWeaponEntityByNameWithPhysics( string name, vector origin, vector angles )
+entity CreateTurretEnt( vector origin, vector angles, entity ownerEnt, asset turretModel, string turretSettingsName )
+entity CreateControlPanelEnt( vector origin, vector angles, entity ownerEnt, asset model )
+Note
+These are defined in ai/_ai_spawn.gnut
entity CreateArcTitan( int team, vector origin, vector angles, array
entity CreateAtlas( int team, vector origin, vector angles, array
entity CreateHenchTitan( string titanType, vector origin, vector angles )
+entity CreateNPCTitan( string settings, int team, vector origin, vector angles, array
entity CreateOgre( int team, vector origin, vector angles, array
entity CreateFragDrone( int team, vector origin, vector angles )
+entity CreateFragDroneCan( int team, vector origin, vector angles )
+Creates an unarmed drone
+entity CreateGenericDrone( int team, vector origin, vector angles )
+entity CreateRocketDrone( int team, vector origin, vector angles )
+entity CreateShieldDrone( int team, vector origin, vector angles )
+entity CreateElitePilot( int team, vector origin, vector angles )
+entity CreateElitePilotAssassin( int team, vector origin, vector angles )
+entity CreateSoldier( int team, vector origin, vector angles )
+entity CreateRocketDroneGrunt( int team, vector origin, vector angles )
+entity CreateShieldDroneGrunt( int team, vector origin, vector angles )
+entity CreateSpectre( int team, vector origin, vector angles )
+entity CreateStalker( int team, vector origin, vector angles )
+entity CreateStryder( int team, vector origin, vector angles, array
entity CreateSuperSpectre( int team, vector origin, vector angles )
+entity CreateZombieStalker( int team, vector origin, vector angles )
+entity CreateZombieStalkerMossy( int team, vector origin, vector angles )
+entity CreateMarvin( int team, vector origin, vector angles )
+entity CreateWorkerDrone( int team, vector origin, vector angles )
+entity CreateProwler( int team, vector origin, vector angles )
+entity CreateGunship( int team, vector origin, vector angles )
+entity CreateNPC( string baseClass, int team, vector origin, vector angles )
+entity CreateNPCFromAISettings( string aiSettings, int team, vector origin, vector angles )
+int DamageDef_GetCount()
+string DamageDef_GetName( int id )
+string DamageDef_GetObituary( int id )
+bool DamageDef_GetDeathProtection( int id )
+var Dev_DamageDef_GetSettingByKeyField( int id, string key )
+DamageInfos are variables holding information about damage inflicted on an entity.
+Because damageInfo instances are implemented as userdata they can't be typed.
+entity DamageInfo_GetAttacker( var damageInfo )
+Returns the attacker of this damageInfo
+entity DamageInfo_GetInflictor( var damageInfo )
+Returns the inflictor of this damageInfo
+entity DamageInfo_GetWeapon( var damageInfo )
+Returns the weapon that dealt this damage
+bool DamageInfo_GetForceKill( var damageInfo )
+Get if this damage is supposed to kill the victim regardless of health
+float DamageInfo_GetDamage( var damageInfo )
+Get the inflicted damage
+float DamageInfo_GetDamageCriticalHitScale( var damageInfo )
+vector DamageInfo_GetDamagePosition( var damageInfo )
+Returns the position where the damage originated. + Usually this is the barrel attachment of the weapon that inflicted the damage.
+int DamageInfo_GetHitGroup( var damageInfo )
+int DamageInfo_GetHitBox( var damageInfo )
+string DamageInfo_GetDeathPackage( var damageInfo )
+int DamageInfo_GetDamageType( var damageInfo )
+int DamageInfo_GetCustomDamageType( var damageInfo )
+int DamageInfo_GetDamageSourceIdentifier( var damageInfo )
+Returns the eDamageSourceId
damageSourceId
is an int
that references an enum
and can be used to identify what source damage came from.
damageSourceId
is mostly found as an argument in some kill and damage related functions. Respawn has created a function that will attempt to localise the damageSourceId inputed.
+To add your own custom damageSourceID
, see: :doc:../../northstar/customdamagesources
Other useful functions can be found in the damageinfo
section of this page and in :doc:entities
GetObitFromdamageSourceId
is a global function that attempts to localise the damageSourceId
inputed, if it cannot get a localised string it will simply return the localisation string of the source.
float DamageInfo_GetViewPunchMultiplier( vare damageInfo )
+float DamageInfo_GetDistFromAttackOrigin( var damageInfo )
+Get the distance from where the bullet/projectile was fired.
+float DamageInfo_GetDistFromExplosionCenter( var damageInfo )
+If it's a radius damage, gives the distance from the center of the blast. Otherwise defaults to zero.
+vector DamageInfo_GetDamageForce( var damageInfo )
+Get damage force vector.
+bool DamageInfo_IsRagdollAllowed( var damageInfo )
+Checks if code is allowing this entity to ragdoll on death
+int DamageInfo_GetDamageFlags( var damageInfo )
+Get all DAMAGEFLAG_* flags.
+bool DamageInfo_HasDamageFlags( var damageInfo, int damageFlags )
+"Returns true if contains all given DAMAGEFLAG_* flags.
+string DamageInfo_GetDamageWeaponName( var damageInfo )
+Returns weapon name, even if weapon entity is gone
+bool DamageInfo_ShouldRecordStatsForWeapon( var damageInfo )
+Returns if stats should be recorded for damage weapon
+void DamageInfo_SetForceKill( var damageInfo, bool force )
+Sets whether this damage should force a kill
+void DamageInfo_SetDamage( var damageInfo, float damage )
+Set the amount of damage
+void DamageInfo_SetCustomDamageType( var damageInfo, int damageType )
+Overrides the damage type that was set by script when firing the weapon.
+void DamageInfo_AddCustomDamageType( var damageInfo, int damageType )
+Add a damage flag.
+void DamageInfo_RemoveCustomDamageType( var damageInfo, int damageType )
+Remove damage flag.
+void DamageInfo_SetDamageSourceIdentifier( var damageInfo, int identifier )
+Sets the damage source identifier.
+void DamageInfo_SetDeathPackage( var damageInfo, string package )
+Set what death (anim) package to use if this damage kills the guy.
+void DamageInfo_SetDamageForce( var damageInfo, vector force )
+Set damage force vector
+void DamageInfo_SetFlinchDirection( var damageInfo, number direction )
+Set which direction the target should flinch in.
+void DamageInfo_AddDamageFlags( var damageInfo, int flags )
+Add a DAMAGEFLAG_* flag.
+bool IsCriticalHit( entity attacker, entity victim, number hitBox, number damage, int damageType )
+bool IsRodeoHitBox( entity e, number f )
+bool HeavyArmorCriticalHitRequired( var damageInfo )
+.. note::
+SERVER only
+
bool CritWeaponInDamageInfo( var damageInfo )
+.. note::
+SERVER only
+
float GetCriticalScaler( ent, damageInfo )
+.. note::
+SERVER only
+
bool IsValidHeadShot( var damageInfo = null, entity victim = null, entity attacker = null, entity weapon = null, int hitGroup = -1, float attackDist = -1.0, entity inflictor = null )
+bool IsMaxRangeShot( damageInfo )
+bool IsMidRangeShot( damageInfo )
+bool IsInstantDeath( var damageInfo )
+bool IsTitanCrushDamage( damageInfo )
+bool IsSuicide( entity attacker, entity victim, int damageSourceId )
+string GetObitFromdamageSourceId( int damageSourceId )
+You can get a bitflag of all damage types used with DamageInfo_GetDamageType
.
List of all Damage flags
+Variable name | +Value | +
---|---|
DF_GIB | +1 | +
DF_DISSOLVE | +2 | +
DF_INSTANT | +3 | +
DF_NO_SELF_DAMAGE | +4 | +
DF_IMPACT | +5 | +
DF_BYPASS_SHIELD | +6 | +
DF_RAGDOLL | +7 | +
DF_TITAN_STEP | +8 | +
DF_RADIUS_DAMAGE | +9 | +
DF_ELECTRICAL | +10 | +
DF_BULLET | +11 | +
DF_EXPLOSION | +12 | +
DF_MELEE | +13 | +
DF_NO_INDICATOR | +14 | +
DF_KNOCK_BACK | +15 | +
DF_STOPS_TITAN_REGEN | +16 | +
DF_DISMEMBERMENT | +17 | +
DF_MAX_RANGE | +18 | +
DF_SHIELD_DAMAGE | +19 | +
DF_CRITICAL | +20 | +
DF_SKIP_DAMAGE_PROT | +21 | +
DF_HEADSHOT | +22 | +
DF_VORTEX_REFIRE | +23 | +
DF_RODEO | +24 | +
DF_BURN_CARD_WEAPON | +25 | +
DF_KILLSHOT | +26 | +
DF_SHOTGUN | +27 | +
DF_SKIPS_DOOMED_STATE | +28 | +
DF_DOOMED_HEALTH_LOSS | +29 | +
DF_DOOM_PROTECTED | +30 | +
DF_DOOM_FATALITY | +31 | +
DF_NO_HITBEEP | +32 | +
global enum damageTypes
+ {
+ gibs = (DF_GIB)
+ largeCaliberExp = (DF_BULLET | DF_GIB | DF_EXPLOSION)
+ gibBullet = (DF_BULLET | DF_GIB)
+ instant = (DF_INSTANT)
+ dissolve = (DF_DISSOLVE)
+ projectileImpact = (DF_GIB)
+ pinkMist = (DF_GIB) //If updated from DF_GIB, change the DF_GIB in Arc Cannon to match.
+ ragdoll = (DF_RAGDOLL)
+ titanStepCrush = (DF_TITAN_STEP)
+ arcCannon = (DF_DISSOLVE | DF_GIB | DF_ELECTRICAL )
+ electric = (DF_ELECTRICAL) //Only increases Vortex Shield decay for bullet weapons atm.
+ explosive = (DF_RAGDOLL | DF_EXPLOSION )
+ bullet = (DF_BULLET)
+ largeCaliber = (DF_BULLET | DF_KNOCK_BACK)
+ shotgun = (DF_BULLET | DF_GIB | DF_SHOTGUN )
+ titanMelee = (DF_MELEE | DF_RAGDOLL)
+ titanBerserkerMelee = (DF_MELEE | DF_RAGDOLL)
+ titanEjectExplosion = (DF_GIB | DF_EXPLOSION)
+ dissolveForce = (DF_DISSOLVE | DF_KNOCK_BACK | DF_EXPLOSION)
+ rodeoBatteryRemoval = (DF_RODEO | DF_EXPLOSION | DF_STOPS_TITAN_REGEN )
+ }
+
var GetDataTable( asset datatablepath )
+Gets the given datable asset
+var GetDataTableColumnByName( var datatable, string columnName )
+Finds the column in the datatable with the given name. -1 if none.
+int GetDataTableRowCount( var dtatatable )
+Returns the number of rows of a given datatable
+bool GetDatatableBool( var dtatable, int row, int column )
+Gets a bool from the given row/column of a datatable
+int GetDataTableInt( var datatable, int row, int column )
+Gets an integer from the given row/column of a datatable
+float GetDataTableFloat( var datatable, int row, int column )
+Gets a float from the given row/column of a datatable
+vector GetDataTableVector( var datatable, int row, int column )
+Gets a vector from the given row/column of a datatable
+string GetDataTableString( var datatable, int row, int column )
+Gets a string from the given row/column of a datatable
+asset GetDataTableAsset( var datatable, int row, int column )
+Gets an asset from the given row/column of a datatable
+bool GetDataTableRowMatchingBoolValue( var datatable, int column, bool value )
+Finds and returns the first row of the datatable for which the bool in the given column matches the given value. -1 if none.
+int GetDataTableRowMatchingIntValue( var datatable, int column, int value )
+Finds and returns the first row of the datatable for which the int in the given column matches the given value. -1 if none.
+int GetDataTableRowLessThanOrEqualToIntValue( var datatable, int column, int value )
+Finds and returns the first row of the datatable for which the int in the given column is less than or equal to the given value. -1 if none.
+int GetDataTableRowGreaterThanOrEqualToIntValue( var datatable, int column, int value )
+Finds and returns the first row of the datatable for which the int in the given column is greater than or equal to the given value. -1 if none.
+int GetDataTableRowMatchingFloatValue( var datatable, int column, float value )
+Finds and returns the first for of the datatable for which the float in the given colmn matches the given value. -1 if none.
+int GetDataTableRowLessThanOrEqualToFloatValue( var datatable, int column, float value )
+Finds and returns the first row of the datatable for which the float in the given column is less than or equal to the given value. -1 if none.
+int GetDataTableRowGreaterThanOrEqualToFloatValue( var datatable, int column, float value )
+Finds and returns the first row of the datatable for which the float in the given column is greater than or equal to the given value. -1 if none.
+int GetDataTableRowMatchingVectorValue( var datatable, int column, vector value )
+Finds and returns the first row of the datatable for which the vector in the given column matches the given value. -1 if none.
+int GetDataTableRowMatchingStringValue( var datatable, int column, string value )
+Finds and returns the first row of the datatable for which the string in the given column matches the given value. -1 if none.
+int GetDataTableRowMatchingAssetValue( car datatable, int column, asset value )
+Finds and returns the first row of the dtatable for which the asset in the given column matches the given value. -1 if none.
+ + + + + + + + + + + + + + + + +Note
+Only DebugDrawLine
, DebugDrawBox
and DebugDrawScreenText
are native functions.
The rest are defined in scripts using these.
+In Titanfall it is possible to draw shapes in 3D, from the SERVER and CLIENT VM, using the debug draw functions, however in order for them to actually render you will need to set sv_cheats 1
and enable_debug_overlays 1
in your launch config or console.
These debug drawing functions are available:
+void DebugDrawLine( vector start, vector end, int r, int b, int g, bool drawThroughObject, float time)
+void DebugDrawCylinder( vector origin, vector angles, float radius, float height, int r, int g, int b, bool throughGeo, float time )
+array
void DebugDrawMark( vector origin, float radius, array
void DebugDrawSphere( vector origin, float radius, int r, int g, int b, bool throughGeo, float time, int segments = 16 )
+void DrawArrow( vector origin, vector angles = <0,0,0>, float time = 5.0, float scale = 50, vector rgb = <0,0,0> )
+void DrawStar( vector origin, int size, float time = 1.0, bool throughWorld = false )
+void DebugDrawBoxSimple( vector origin, vector min = < -4.0, -4.0, -4.0>, vector max = <4.0, 4.0, 4.0>, int r = 255, int g = 255, int b = 100, int alpha = 255, float time = 0.2 )
+void DrawBox( vector org, vector mins, vector maxs, int r, int g, int b, bool throughSolid, float time )
+void DebugDrawCube( vector cubeCenter, float cubeSize, int r, int g, int b, bool throughSolid, float time )
+void DebugDrawBox( vector org, vector min, vector max, int r, int g, int b, int a, float time)
+void DrawAngledBox( vector org, vector ang, vector mins, vector maxs, int r, int g, int b, bool throughSolid, float time )
+void DrawBoxFromEye( vector org, vector mins, vector maxs, int r, int g, int b, bool throughSolid, float time )
+vector[8] GetBoxCorners( vector org, vector ang, vector mins, vector maxs )
+void DebugDrawRotatedBox( vector origin, vector mins, vector maxs, vector angles, int r, int g, int b, bool throughGeo, float duration )
+void DebugDrawCircleTillSignal( entity ent, string signalName, vector origin, float radius, int r, int g, int b )
+void DebugDrawOriginMovement( entity ent, int r, int g, int b, float time = 9999.0, float trailTime = 5.0 )
+void DebugDrawSpawnpoint( entity spawnpoint, int r, int g, int b, bool throughSolid, float time )
+void DrawArrowOnTag( entity ent, string ornull tag = null, float time = 5.0, float scale = 50, vector rgb = <0,0,0> )
+void DrawArrowOnTagThread( entity ent, string ornull tag, float time, float scale, vector rgb = <0,0,0> )
+void DrawTag( entity ent, string tag )
+void DrawOrg( entity ent )
+void DrawAttachment( entity pod, string attachment, float time = 0.1, vector ornull color = null )
+void DrawAttachmentForever( entity pod, string attachment )
+void DrawEntityOrigin( entity ent, float time = 0.1, vector ornull color = null )
+void DrawOrigin( vector origin, float time = 0.1, vector ornull color = null )
+vector[16] DebugDrawTrigger( vector origin, float radius, int r, int g, int b )
+void DebugDrawCircleOnEnt( entity ent, float radius, int r, int g, int b, float time )
+void DebugDrawSphereOnEnt( entity ent, float radius, int r, int g, int b, float time )
+void _DebugThreadDrawCircleOnEnt( entity ent, float radius, int r, int g, int b, float time, vector anglesDelta = Vector( 0, 0, 0 ) )
+void DebugDrawCircleOnTag( entity ent, string tag, float radius, int r, int g, int b, float time )
+void DebugDrawSphereOnTag( entity ent, string tag, float radius, int r, int g, int b, float time )
+void _DebugThreadDrawCircleOnTag( entity ent, string tag, float radius, int r, int g, int b, float time, vector anglesDelta = Vector( 0, 0, 0 ) )
+void DrawTracerOverTime( vector origin, vector dir, float time )
+void DebugDrawWeapon( entity weapon )
+void DebugDrawAngles( vector position, vector angles, float duration = 9999.0 )
+void DrawAnglesForMovingEnt( entity ent, float duration, string optionalTag = "" )
+void DrawLineFromEntToEntForTime( entity ent1, entity ent2, float duration, int r = 255, int g = 255, int b = 0 )
+void DrawLineFromVecToEntForTime( vector vec, entity ent, float duration, int r = 255, int g = 255, int b = 0 )
+void DrawLineForPoints( array
void DebugScreenText( float posX, float posY, string text )
+Error
+This function is stripped. It does nothing.
+Warning
+Most of these functions are stripped and have no functionality.
+bool Dev_CommandLineHasParam( string param )
+string Dev_CommandLineParmValue( string param )
+void Dev_CommandLineRemoveParam( string param )
+void Dev_CommandLineAddParm( string param )
+array
Returns list of files in scripts/model_view_list.txt
, which is written by reRun (respawn internal tool)
void NativeFuncTest( number a, bool b, number c )
+int GetDeveloperLevel()
+int GetBugReproNum()
+bool DevLobbyIsFrozen()
+void TriggerBreakpoint()
+void SpamLog( string text )
+Prints to the game's spam logfile (usually stored in DevNet).
+void CodeWarning( string s )
+void PerfInitLabel( string s, int n )
+void PerfStart( int n )
+void PerfEnd( int n)
+void PerfClearAll()
+void PerfReset()
+void PerfDump()
+void RProfStart( string, int n )
+void RProfEnd( int n )
+Error
+Stripped in Northstar for security.
+void DevP4Checkout( string s )
+void DevP4Add( string s )
+Error
+Stripped in Northstar for security.
+void DevTextBufferWrite( string s )
+Append string to a temp buffer. Dev only.
+void DevTextBufferClear()
+void DevTextBufferDumpToFile( string file )
+Dump temp buffer out to specified path/filename.
+void LogPlayerMatchStat_KilledAPilot( entity e )
+void LogPlayerMatchStat_Death( entity e )
+void LogPlayerMatchStat_EarnedXP( entity e )
+void LogPlayerMatchStat_UsedBurncard( entity e )
+void LogPlayerMatchStat_HappyHourMeritsGiven( entity e )
+void LogPlayerStat_BurncardDiscard( entity e )
+void DoEntFire( string target, string action, string value, number delay, entity activator, entity caller )
+void EntFireNow( string target, string action, string value, entity activator, entity caller )
+void EntFireByHandle( entity ent, string action, string value, number delay, entity activator, entity caller )
+void EntFireByHandleNow( entity ent, string action, string value, entity activator, entity caller )
+string GameRules_GetGameMode()
+int GameRules_GetTeamScore( int team )
+int GameRules_GetTeamScore2( int team )
+Used for round based game modes
+int GameRules_GetTeamKills( int team )
+int GameRules_GetTeamDeaths( int team )
+string GameRules_GetTeamName( int team )
+bool GameRules_TimeLimitEnabled()
+bool GameRules_AllowMatchEnd()
+int GameRules_GetClassMax( string class )
+void GameRules_SetGameMode( string gameMode )
+void GameRules_SetTeamScore( int team, int score )
+void GameRules_SetTeamScore2( int team, int score )
+Presumably used for round based game modes
+void GameRules_EndMatch()
+void GameRules_MarkGameStatePrematchEnding()
+void GameRules_MarkGameStateWinnerDetermined()
+void GameRules_ChangeMap( string mapName, string gameMode )
+string GameRules_GetRecentMap( number unk1 )
+string GameRules_GetRecentGameMode( number unk1 )
+int GameRules_GetRecentTeamScore( number unk1, int team )
+void GameRules_EnableGlobalChat( bool enabled )
+string GameRules_GetUniqueMatchID()
+void GameRules_SetDeadPlayersCanOnlySpeakToDeadPlayersInHudChat( bool b )
+There are a multitude of selectors to get specific kinds of entities.
+Creating entities is documented here.
+array
Get array of all players
+array
Get array of all players by class, team within dist. team -1 for any team, "any"
for any class, otherwise "titan"
or "pilot"
, -1 for any dist
array
Get array of all players that are in a team
+array
Get array of all players that are not allied with the team
+array
Get array of all players that are alive
+array
Get array of all players in a team that are alive
+array
array
Get array of all players that are not titans
+array
array
array
array
array
array
array
Get array of all players, even ones who are connecting
+int GetPendingClientsCount()
+Get all players in a titan and souls.
+array
array
array
array
int GetTitanCountForTeam( int team )
+int GetTeamPlayerCount( int team )
+int GetSurfacePropForEntity( entity ent )
+entity GetEntByIndex( int index )
+array
array
array
array
Get array of all NPCs by class, team, within dist. team -1 for any team, "any"
for any class, otherwise "titan"
or "pilot"
, -1 for any dist
GetNPCArrayWithSubclassEx( string classname, int onSameTeamAsNum, int enemiesOfTeamNum, vector origin, float maxdist, array
Get array of all NPCs by class, team, and subclass (array), within dist. team -1 for any team, "'any"
for any class, -1 for any dist
array
Get array of all NPCs of class
+array
Get array of all NPCs of class and subclass
+array
array
Get array of all NPCs by class, team, within dist. team -1 for any team, "any"
for any class, otherwise "titan"
or "pilot"
, -1 for any dist
entity Entities_First()
+entity Entities_Next( entity ent )
+entity Entities_FindByClassname( entity ent, string className )
+entity Entities_FindByName( entity ent, string name )
+entity Entities_FindInSphere( entity ent, vector sphereDir, float radius )
+entity Entities_FindByTarget( entity ent, string target )
+entity Entities_FindByNameNearest( string name, vector dir, float length )
+entity Entities_FindByNameWithin( entity ent, string name, vector v, float len )
+entity Entities_FindByClassnameNearest( string className, vector v, float f )
+entity Entities_FindByClassnameWithin( entity ent, string className, vector v, float f )
+entity GetEntByScriptName( string name )
+entity GetEntByScriptNameInInstance( string name, string instanceName )
+entity GetTeamEnt( int team )
+array
Get array of entitites matching a name
+array
Get array of entities matching a name with support for *
+array
Get array of entities matching a class
+array
Get array of entities matching a class with support for *
+array
Get array of entities matching a script name
+array
Get array of entities matching a script name and instance
+array
Get weapons in the world
+ + + + + + + + + + + + + + + + +Functions to render text on the client screen.
+Because these can not be removed in demos and are somewhat ugly, it is recommended to use Serverside RUI instead
+void SendHudMessage( entity player, string text, int xPos, int yPos, int r1, int g1, int b1, number a1, number r2, number g2, number b2, number a2, number fadeTimeIn, number holdTime, number fadeTimeOut )
+Send a HUD message to the given player.
+void SendHudMessageToAll( string text, int xPos, int yPos, int r1, int g1, int b1, number a1, number r2, number g2, number b2, number a2, number fadeTimeIn, number holdTime, number fadeTimeOut )
+Send a HUD message to all players.
+void CenterPrintAll( string text )
+Prints white text in the center of the screen on all clients.
+ + + + + + + + + + + + + + + + +int CreateScriptManagedEntArray()
+Returns the index of the new array
+void AddToScriptManagedEntArray( int index, entity ent )
+void RemoveFromScriptManagedEntArray( int index, entity ent )
+int GetScriptManagedEntArrayLen( int index )
+array
Get the script managed ent array for the given index
+array
Get the script managed ent array for the given index within distance of a point
+ + + + + + + + + + + + + + + + +vector AngleNormalize( vector v )
+vector CalcRelativeVector( vector a, vector b )
+vector CalcRelativeAngles( vector a, vector b )
+bool BoxIntersectsBox( vector mins1, vector maxs1, vector mins2, vector maxs2 )
+float clamp( number a, number b, number c )
+float Interpolate( number startTime, number moveTime, number easeIn, number easeOut )
+Interpolate with cubic hermite during ease-in and ease-out times
+vector LerpVector( vector vecFrom, vector vecTo, float percent )
+Lineraly interpolate between two vectors
+vector GetRandom3DPointIn2DCircle( number radius, var base3D_or_null )
+Get a random 2d point in a circle, as a 3d point, with optional 3d base
+float Graph( number a, number b, number c, number d, number e )
+float GraphCapped( number a, number b, number c, number d, number e )
+vector GraphCappedVector( number a, number b, number c, vector d, vector e )
+float Smooth01( float f, int i )
+var SmoothCD( number a, number b, number c, number d, number e )
+var SmoothCDVector( vector a, vector b, vector c, number d, number e )
+vector GetApproxClosestHitboxToRay( entity ent, vector v1, vector v2 )
+bool IsServer()
+bool IsClient()
+bool IsToolsMode()
+bool IsFNF()
+bool IsGameFromReload()
+int GetCPULevel()
+bool IsEnemyTeam( int ownTeam, int otherTeam )
+Returns if otherTeam
is an enemy of ownTeam
void SetMaxActivityMode( int mode )
+int CalculateHashForString( string s )
+void CreateRope( vector start, vector end, float length = 0, entity startEnt = null, entity endEnt = null, int startAttachment = 0, int endAttachment = 0, string material = "", int segmentCount = 0 )
+Creates a rope between two points or entities.
+float GetTeamSkill( int team )
+void SendToConsole( string cmd )
+Execute cmd
on the local host
void RecordAchievementEvent( string s1, number n1 )
+void ServiceEventQueue()
+void SetDucking( string s1, string s2, number n1 )
+void GrantClientSidePickup_MatchCandy( entity player, int amount, vector origin, int flags, int recieptID )
+void NoteMatchState( number a1, number a2, number a3, number a4, number a5, number a6, number a7, number a8, number a9 )
+void NoteLobbyState( number a1, string a2 )
+bool IsHighPerfDevServer()
+bool ShouldAwardHappyHourBonus( entity player )
+bool InPrediction()
+bool IsFirstTimePredicted()
+string GetMapName()
+Get the map name of the current map
+bool IsFastIterationEnabled()
+bool BuildingCubeMaps()
+bool IsTestMap()
+Returns value of IsTestMap from the level's script list .rson file
+void AssertNoPlayerChildren( entity parent )
+void TryClearParent( entity parent )
+void SetForceDrawWhileParented( entity child, bool force )
+void SetCrosshairTeamColoringDisabled( entity player, bool disabled )
+void SetHideOnCloak( entity ent, bool hide )
+void VPKNotifyFile( string file )
+bool IsPlayerSafeFromNPCs( entity player )
+bool IsPlayerSafeFromProjectiles( entity player, vector origin )
+entity GetWindowHint( vector startPos, number radius, number height, vector dir, number distance, number gravity, number margin, entity ignoreEnt )
+Returns the best window hint.
+void ScreenFade( entity player, number r, number g, number b, number fadeTime, number fadeHold, int fadeFlags )
+Fade the player's scren.
+Fade flags start with FFADE_
void SetXPForLevel( int a, int b )
+Sets the XP required for a player to get to a certain level
+int GetLevelForXP( int n )
+float GetHealthFrac( entity ent )
+bool IsMagneticTarget( entity ent )
+Returns if an entity is a magnetic target
+bool IsTurret( entity ent )
+int GetHitgroupForHitboxOnEntity( var a, number b )
+void PutEntityInSafeSpot( entity ent, entity referenceEnt, entity movingGroundEnt, vector, safeStartingLocationForEntity, vector positionAtEndOfAnimationForEntity )
+float GetHealthFrac( entity ent )
+bool IsMagneticTarget( entity ent )
+Returns if an entity is a magnetic target
+bool IsTurret( entity ent )
+Is entity a turret
+void Weapon_SetDespawnTime( number time )
+int GetImpactEffectTable( string weapon )
+float CalcWeaponDamage( entity owner, entity target, entity weapon, number distanceToTarget, int extraMods )
+bool IsGameFullyInstalled()
+Returns true if the full game is installed. You can't start mp or any sp map but sp_training and sp_crashsite if this is false.
+bool IsGamePartiallyInstalled()
+Returns true if the game is partially installed. You can't start sp training this is false.
+float GetGameFullyInstalledProgress()
+Returns fraction 0.0 to 1.0 of downloading of full game progress.
+bool Script_IsRunningTrialVersion()
+Only call when we have an active user.
+void ReloadScriptCallbacks()
+void ReloadingScriptsBegin()
+void ReloadingScriptsEnd()
+void RegisterNetworkedVariable( string name, int SNDC_category, int SNVT_type, var defaultValue = 0, float rangemin = 0, float rangemax = 0 )
+Registers a named networked variable.
+int GetNetworkedVariableIndex( string name )
+Gets the internal index used to reference a scripted network variable. For use with FX_PATTACH_SCRIPT_NETWORK_VAR
.
void SetGlobalNetBool( string name, bool value )
+void SetGlobalNetInt( string name, int value )
+void SetGlobalNetFloat( string name, float value )
+void SetGlobalNetFloatOverTime( string name, float value, float time )
+void SetGlobalNetTime( string name, float value )
+void SetGlobalNetEnt( string name, entity value )
+bool GetGlobalNetBool( string name )
+int GetGlobalNetInt( string name )
+float GetGlobalNetFloat( string name )
+float GetGlobalNetTime( string name )
+entity GetGlobalNetEnt( string name )
+Remote functions allow the SERVER
to call registered script functions on the CLIENT
and UI
VM.
void Remote_BeginRegisteringFunctions()
+Begin registration of remote functions.
+void Remote_EndRegisteringFunctions()
+Finish registration of remote functions.
+void AddCallback_OnRegisteringCustomNetworkVars( void functionref() callback )
+Note
+This function is not native. It's defined in Northstar.CustomServers
+Registers a callback when Remote functions are being registered.
+To register custom remote functions you are required to use this callback because functions can only be registered once.
+ globalize_all_functions
+
+ void function MyMod_Init()
+ {
+ AddCallback_OnRegisteringCustomNetworkVars( MyModRegisterRemoteFunctions )
+ }
+
+ void function MyModRegisterRemoteFunctions()
+ {
+ Remote_RegisterFunction( "ExampleRemoteFunction" )
+ }
+
+ void function ExampleRemoteFunction() {}
+
void Remote_RegisterFunction( string name )
+Register a function name to be used in remote calls.
+void Remote_CallFunction_Replay( entity player, string functionName, ... )
+Note
+Allowed extra parameter types are null, bool, int, and float.
+Given a player, function name, and optional parameters, call function in client script.
+Then call it again if we rewind and play a kill replay. + The command will not reach the client at all if called during a span of time the player skips because they were watching a replay.
+void Remote_CallFunction_NonReplay( entity player, string functionName, ... )
+Note
+Allowed extra parameter types are null, bool, int, and float.
+Given a player, function name, and optional parameters, call function in client script.
+Does not get called again in replays.
+void Remote_CallFunction_UI( entity player, string functionName, ... )
+Note
+Allowed extra parameter types are null, bool, int, and float.
+Given a player, function name and optional parameters, call function in UI script.
+While not being networked themselves, these are used by remote functions.
+bool ShouldDoReplayIsForcedByCode()
+bool Replay_IsEnabled()
+For settings, see AI Settings
+void UpdateEnemyMemoryFromTeammates( entity guy )
+void UpdateEnemyMemoryWithinRadius( entity guy, number radius )
+void SetEnableNPCs( bool enabled )
+void ToggleNPCPathsForEntity( entity ent, bool pathable )
+Controls if ent
is traversable by NPCs
void ToggleNPCPathsForEntityAtPosition( entity ent, vector pos, bool pathable )
+void TransitionNPCPathsForEntity( entity ent, vector pos, bool b )
+void SetVisibleEntitiesInConeQueriableEnabled( entity ent, bool enabled )
+void CreateNPCSquad( string name )
+int GetNPCSquadSize( string name )
+void SetNPCSquadMode( string name, number mode )
+array
void NPCSetSearchPathUseDist( number distance )
+float NPCGetSearchPathUseDist()
+void NPCSetAimConeFocusParams( number a, number b )
+void NPCSetAimPatternFocusParams( number a, number b )
+void NPCSetReacquireParams( number a, number b )
+void NPCSetSquadNoFlankThreshold( string a, number b, number c )
+int GetNearestNodeToPos( vector pos )
+Returns a node index
+int GetBestNodeForPosInWedge( vector tPos, vector startPos, number yaw, number minFov, number maxFov, number fovPenalty, number some_index, number steps_maybe )
+Returns a node index
+vector GetNodePos( int nodeIndex )
+int GetNodeCount()
+bool GetNodeScriptData_Boolean( int nodeIndex, int otherNodeIndex )
+void SetNodeScriptData_Boolean( int nodeIndex, int otherNodeIndex, bool value )
+int GetAINScriptVersion()
+bool NavMesh_IsUpToDate()
+vector ornull NavMesh_ClampPointForAI( vector point, entity npc )
+Clamps a goal point to the NavMesh for a given AI. Uses AIs hull size as test extents
+vector ornull NavMesh_ClampPointForAIWithExtents( vector pointToClamp, entity contextAI, vector extents )
+Clamps a goal point to the NavMesh for a given AI. + As extents increase in size more possible clamp positions become available, + but too large and the clamped position may be very far from the original point.
+vector ornull NavMesh_ClampPointForHull( vector pointToClamp, int hull )
+Clamps a goal point to the NavMesh for a given hull
+vector ornull NavMesh_ClampPointForHullWithExtents( vector pointToClamp, int hull, vector extents )
+Clamps a goal point to the NavMesh for a given hull. + As extents increase in size more possible clamp positions become available, + but too large clamped position may be very far from the original point.
+array
Get nearby ground positions by following the NavMesh graph
+array
Get n( < 64 ) ground positions around a spot within minDist
and maxDist
array
Get up to n ground positions around a spot within minDist
and maxDist
. Gets center of random polygons.
bool NavMesh_IsPosReachableForAI( entity npc, vector point )
+Checks if the npc can reach the position over graph
+vector GetBoundsMin( int hull )
+vector GetBoundsMax( int hull )
+void SkitSetDistancesToClosestHarpoints()
+array
Get skit nodes sorted by nearest to average player position with some randomization
+array
Get skit nodes sorted by nearest to hardpoints with some randomization
+array
Get skit nodes sorted by nearest to pos with some randomization
+void AI_CreateDangerousArea( entity lifetimeEnt, entity weaponOrProjectile, float radius, int safeTeam, bool affectsNormalArmor, bool affectsHeavyArmor )
+Create a known dangerous are that AI should avoid if necessary. + The lifetime of the danger is tied to an entity
+void AI_CreateDangerousArea_Static( entity lifetimeEnt, entity weaponOrProjectile, float radius, int safeTeam, bool affectsNormalArmor, bool affectsHeavyArmor, vector staticOrigin )
+Same as AI_CreateDangerousArea except the origin is always in a single place
+void AI_CreateDangerousArea_DamageDef( int damageDef, entity lifetimeEnt, int safeTeam, bool affectsNormalArmor, bool affectsHeavyArmor )
+Create dangerous area using damage def
+bool AINFileIsUpToDate()
+bool AINExists()
+void SetAINScriptVersion( int version )
+array
Get array of spawners matching a class name
+array
Get array of spawners matching a script name
+entity GetSpawnerByScriptName( string name )
+Warning
+Appears to be a scrapped feature.
+float Operator_FindFloorHeight( entity op, number n, bool b )
+void Operator_DiveEnable( entity op )
+void Operator_DiveDisable( entity op )
+bool Operator_IsDiving( entity op )
+void Operator_ForceDefaultFloorHeight( entity op, bool force )
+void Operator_SetDefaultFloorHeight( entity op, number height )
+void Operator_IgnoreWorldForMovement( entity op, bool ignore )
+void Operator_IgnoreWorldForFloorTrace( entity op, bool ignore )
+void Operator_MoveGridSizeScale( entity op, number scale )
+void Operator_MoveFloorHeightOffset( entity op, number offset )
+void Operator_JumpIsDodge( entity op, bool isDodge )
+void Operator_SetMaxJumpSpeed( entity op, number speed )
+void Operator_SetJumpAcceleration( entity op, number acc )
+Methods to create and manage particles
+int PrecacheParticleSystem( asset particle )
+int GetParticleSytemIndex( asset particle )
+string GetParticleSystemName( asset particle )
+void StartParticleEffectInWorld( number particleIndex, vector origin, vector angles )
+void StartParticleEffectInWorldWithControlPoint( number particleIndex, vector origin, vector angles, vector controlPoint )
+entity StartParticleEffectInWorld_ReturnEntity( number particleIndex, vector origin, vector angles )
+void StartParticleEffectOnEntity( entity ent, number particleIndex, number attachPoint, number attachID )
+void StartParticleEffectOnEntityWithControlPoint( entity ent, number particleIndex, number attachPoint, number attachID, number unk1, number unk2 )
+void StartParticleEffectOnEntityWithPos( entity ent, number particleIndex, number attachPoint, number attachID, vector origin, vector angles )
+void StartParticleEffectOnEntityWithPosWithControlPoint( entity ent, number particleIndex, number attachPoint, number attachID, vector unk2, vector unk3, number unk4, number unk5 )
+entity StartParticleEffectOnEntity_ReturnEntity( entity ent, number particleIndex, number attachPoint, number attachID )
+entity StartParticleEffectOnEntityWithPos_ReturnEntity( entity ent, number particleIndex, number attachPoint, number attachID, vector origin, vector angles )
+void EffectStop( entity effect )
+void EffectSleep( entity effect )
+void EffectWake( entity effect )
+void EffectSetControlPointVector( entity effect, number unk1, vector origin_maybe )
+void EffectSetControlPointAngles( entity effect, number unk1, vector angles )
+void EffectSetControlPointEntity( entity effect, number unk1, entity ent )
+void EffectAddTrackingForControlPoint( entity effect, number unk1, entity unk3, number unk4, number unk5 )
+int PersistenceGetEnumCount( string e )
+Get a count of how many distinct values the given enum has.
+int PersistenceGetEnumIndexForItemName( string a, string b )
+Get a count of how many distinct values the given enum has.
+int PersistenceGetEnumItemNameForIndex( string a, number b )
+Get a count of how many distinct values the given enum has.
+bool PersistenceEnumValueIsValid( string a, string b )
+Returns true if the given enum value contains the given value.
+int PersistenceGetArrayCount( string e )
+Get a count of how many elements the given item has.
+ + + + + + + + + + + + + + + + +entity PlayerMeleeLungeConeTrace( entity plaer, int callback )
+Do a lunge cone trace returning the target closest to center of screen
+array
Returns an array of entities that are inside a cone and visible to the player
+table PlayerMelee_AttackTrace( entity player, float range, bool functionref( entity attacker, entity target ) isValidTargetFunc )
+Do a trace for potential melee targets in front of player.
+ Returns a table with keys entity
and position
, which is the hit entity and position
bool PlayerMelee_IsExecutionReachable( entity attacker, entity target, number dist )
+bool PlayerMelee_IsServerSideEffects()
+void PlayerMelee_StartLagCompensateTarget( entity attacker, entity target )
+void PlayerMelee_StartLagCompensateTargetForLunge( entity attacker, entity target )
+void PlayerMelee_FinishLagCompensateTarget( entity attacker )
+int GetCurrentPlaylistGamemodesCount()
+int GetPlaylistGamemodesCount( string playlistName )
+string GetCurrentPlaylistGamemodeByIndex( int index )
+string GetPlaylistGamemodeByIndex( string playlistName, int modeIndex )
+string GetPlaylistGamemodeByIndexVar( string playlistName, int modeIndex, string index )
+string GetCurrentPlaylistGamemodeByIndexVar( int modeIndex, string unk1, bool unk2 )
+int GetCurrentPlaylistGamemodeByIndexMapsCount( int modeIndex )
+int GetPlaylistGamemodeByIndexMapsCount( string playlistName, int modeIndex )
+string GetCurrentPlaylistGamemodeByIndexMapByIndex( int modeIndex, int mapIndex )
+string GetPlaylistGamemodeByIndexMapByIndex( string playlistName, int modeIndex, int mapIndex )
+int GetCurrentPlaylistMapsCount()
+bool IsPrivateMatch()
+bool IsCoopMatch()
+bool GetLobbyTeamsShowAsBalanced()
+string GetGamemodeVarOrUseValue( string modeName, string gameModeVar, string useVar )
+int GetPlaylistCount()
+string GetPlaylistName( int index )
+string GetPlaylistVarOrUseValue( string playlistName, string plVar, string useVal )
+string GetPlaylistVarOrUseValueOriginal( string playlistName, string plVar, string useVal )
+string GetCurrentPlaylistName()
+var Code_GetCurrentPlaylistVar( string val )
+string Code_GetCurrentPlaylistVarOrUseValue( string val, string useVal )
+string GetCurrentPlaylistVarOrUseValueOriginal( string val, string useVal )
+int GetMaxPlayersForPlaylistName( string playlistName )
+int GetMaxTeamsForPlaylistName( string playlistName )
+void SetPlaylistVarOverride( string var, string val )
+void ClearPlaylistVarOverrides()
+void SetCurrentPlaylist( string playlistName )
+Before you can use any resource in the game it needs to be precached.
+bool WeaponIsPrecached( string weapon )
+bool ModelIsPrecached( asset model )
+void PrecacheWeapon( string weapon )
+Precache a weapon.
+void PrecacheModel( asset modelFile )
+Precache a model.
+void PrecacheMaterial( asset material )
+Precache a material
+void PrecacheImpactEffectTable( string effects )
+Precache an impact effects table.
+int PrecacheParticleSystem( asset particle )
+For more information about particles read the native particle documentation
+ + + + + + + + + + + + + + + + +var LoadRecordedAnimation( asset recordedAnimPath )
+Loads an anim_recording asset generated by bakery.
+Can only load animations from rpaks.
+float GetRecordedAnimationDuration( var recordedAnim )
+Returns the duration in seconds of the recorded anim.
+vector GetRecordedAnimationStartForRefPoint( var recordedAnim, vector refOrg, vector refAng )
+Calculates the position of the first frame of the recorded animation if it were played so that its reference origin/angles line up with the given origin/angles.
+ + + + + + + + + + + + + + + + +bool Rodeo_IsAttached( entity ent )
+void Rodeo_Detach( entity ent )
+void Rodeo_Allow( entity ent )
+void Rodeo_Disallow( entity ent )
+bool Rodeo_IsPreviousEntity( entity attacker, entity ent )
+void Rodeo_SetCooldown( entity ent, number cooldown )
+void Rodeo_OnFinishClimbOnAnimation( entity ent )
+This is to let code know the rodeoPilot has finished climbind on the rodeo and ready to fire
+bool Leech_IsLeechable( entity ent )
+void Leech_SetLeechable( entity ent )
+void Leech_ClearLeechable( entity ent )
+var GetPlayerSettingsFieldForClassName( string className, string fieldName, var unknown = 0 )
+Returns the value for the requested field from the corresponding .set file.
+float GetPlayerSettingsFieldForClassName_Health( string className )
+Returns the value for the default health field from the corresponding .set file.
+float GetPlayerSettingsFieldForClassName_HealthShield( string className )
+Returns the value for the default health shield field from the corresponding .set file.
+float GetPlayerSettingsFieldForClassName_HealthDoomed( string className )
+Returns the value for the default health doomed field from the corresponding .set file.
+float GetPlayerSettingsFieldForClassName_HealthPerSegment( string className )
+table GetSettingsForPlayer_DodgeTable( entity player )
+Returns a table with all the dodge related active settings for a given player
+int PlayerSettingsNameToIndex( string className )
+string PlayerSettingsIndexToName( int settingsNum )
+asset GetPlayerSettingsAssetForClassName( string className, fieldName )
+Returns the value for the requested field from the corresponding .set file.
+var Dev_GetPlayerSettingByKeyField_Global( string a, string b )
+var Dev_GetPlayerSettingAssetByKeyField_Global( string a, string b )
+var GetWeaponInfoFileKeyField_Global( string a, string b )
+var GetWeaponInfoFileKeyField_WithMods_Global( string weaponName, array
Given a weapon name, a list of weapon mods to apply, and key, returns the value of that field in that weapons info file.
+array
Given a weapon name, returns a list of the mods available on that weapon
+void SetBodyGroupsForWeaponConfig( entity ent, string weaponName, array
asset GetWeaponInfoFileKeyFieldAsset_Global( string weaponName, string key )
+Given a weaon name and key, resolves a string key to its value in that weapons info file. assumes no mods set.
+asset GetWeaponInfoFileKeyFieldAsset_WithMods_Global( string weaponName, array
Given a weapon name, a list of weapon mods to apply and key, returns the value of that field in that weapons info file.
+int GetAISettingHullType( string aiSettingsName )
+var Dev_GetAISettingAssetByKeyField_Global( string a, string b )
+var Dev_GetAISettingByKeyField_Global( string a, string b )
+void SetCustomSmartAmmoTarget( entity ent, bool target )
+void SetPreventSmartAmmoLock( entity ent, bool lockable )
+void SetSmartAmmoLockFromTitansOnly( entity ent, bool titanLockOnly )
+void SmartAmmo_SetCustomFractionSource( entity ent, entity src, number fraction )
+void SmartAmmo_ClearCustomFractionSource( entity ent, entity src )
+float SmartAmmo_GetCustomFractionSource( entity ent, entity src )
+int SmartAmmo_GetMaxTargetedBurst( entity weapon )
+float SmartAmmo_GetTargetingTime( entity ent, entity src )
+int SmartAmmo_GetTargetMaxLocks( entity ent, entity src )
+bool SmartAmmo_IsTrackingEntity( entity weapon, entity target )
+bool SmartAmmo_IsValidTarget( entity weapon, entity target )
+void EmitSoundOnEntity( entity ent, string sound )
+void EmitSoundOnEntityNoSave( entity ent, string sound )
+void EmitSoundOnEntityAfterDelay( entity ent, string sound, number delay )
+void EmitSoundOnEntityOnlyToPlayerWithSeek( entity ent, entity reciever, string sound, number duration_maybe )
+void EmitSoundOnEntityExceptToPlayerWithSeek( entity ent, entity reciever, string sound, number duration_maybe )
+void EmitSoundOnEntityToTeam( entity ent, string sound, int team )
+void EmitSoundOnEntityToEnemies( entity ent, string sound, int team )
+void EmitSoundAtPosition( int team, vector origin, string sound )
+void EmitSoundAtPositionOnlyToPlayer( int team, vector origin, entity player, string sound )
+void EmitSoundAtPositionExceptToPlayer( int team, vector origin, entity player, strign sound )
+void StopSoundOnEntity( entity ent, string sound )
+void StopSoundAtPosition( vector pos, string sound )
+void FadeOutSoundOnEntity( entity ent, string sound, number fadeOut )
+void EmitSoundOnEntityOnlyToPlayer( entity ent, entity reciever, string sound )
+void EmitSoundOnEntityOnlyToPlayerWithFadeIn( entity ent, entity reciever, string sound, number fadeIn )
+void EmitSoundOnEntityExceptToPlayer( entity ent, entity exception, string sound )
+void EmitSoundOnEntityExceptToPlayerNotPredicted( entity ent, entity exception, string sound )
+bool DoesAliasExist( string dialogueAlias )
+int GetSoundTags( string sound )
+void SetRapidShiftOffset( number offset )
+Sounds the AI can react to
+void EmitAISound( int soundFlags, int contextFlags, vector pos, float radius, float duration )
+Create a new sound event that AI can response to.
+void EmitAISoundWithOwner( entity owner, int soundFlags, int contextFlags, vector pos, float radius, float duration )
+Create a sound event that AI can respond to, specifying the owner of the sound.
+void EmitAISoundToTarget( entity target, int soundFlags, int contextFlags, vector pos, float radius, float duration )
+Create a sound event that AI can respond to, specifying who the sound should target.
+void EmitAISoundWithOwnerToTarget( entity ownerEnt, entity targetEnt, int soundFlags, int contextFlags, vectorPos, float radius, float duration )
+Create a sound event that AI can respond to, specifying who the sound should target, and the owner of the sound.
+ + + + + + + + + + + + + + + + +void SaveGame_Create( string saveName, int saveVersion, int start_point )
+Do a save.
+void SaveGame_CreateWithCommitDelay( string saveName, int saveVersion, float delay, int trycount )
+Do a save.
+Will call back bool CodeCallback_SaveGameIsSafeToCommit()
to validate if it is ok to commit the save file.
void SaveGame_Commit()
+If there is an outstanding save commit, accept it asap.
+void SaveGame_Reject()
+If there is an outstanding save commit, reject it asap.
+void SaveGame_Load( string saveName )
+Do a restore.
+bool SaveGame_IsValid( string saveName )
+Checks if a file is ok to use.
+int SaveGame_GetVersion( string saveName )
+Return the script version of a save load.
+int SaveGame_GetStartPoint( string saveName )
+Return the script start point of a save load.
+string SaveGame_GetMapName( string saveName )
+Return the map name of a save load.
+void ChangeLevel( string mapName, LevelTransitionStruct transitionStruct )
+Loads a new level. The data in transitionStruct
can be read in the next level with GetLevelTransitionStruct()
.
LevelTransitionStruct ornull GetLevelTransitionStruct()
+Reads the transition data set by ChangeLevel()
on the previous map.
+ Return null
if this is the first map or the previous map didn't supply any data.
void SetTimeshiftOfDay_Night()
+void SetTimeshiftOfDay_Day()
+void SetTimeshiftArmDeviceSkin( int skinIndex )
+void SetBTLoadoutUnlocked( int loadout )
+void SetBTLoadoutsUnlockedBitfield( int unlockedBits )
+int GetBTLoadoutsUnlockedBitfield()
+bool IsBTLoadoutUnlocked( int loadout )
+void SpawnPoints_SetRatingMultipliers_Enemy( number a1, number a2, number a3, number a4 )
+void SpawnPoints_SetRatingMultipliers_Friendly( number a1, number a2, number a3, number a4 )
+void SpawnPoints_SetRatingMultiplier_PetTitan( number muliplier )
+array
Get pilot spawn points
+array
Get titan spawn points
+array
Get droppod spawn points
+array
Get pilot start spawn points for a team
+array
Get titan start spawn points for a team
+array
Get droppod start spawn for a team
+void SpawnPoints_SortPilot()
+void SpawnPoints_SortTitan()
+void SpawnPoints_SortDropPod()
+void SpawnPoints_SortPilotStart()
+void SpawnPoints_SortTitanStart()
+void SpawnPoints_SortDropPodStart()
+void SpawnPoints_InitRatings( entity point, number rating )
+void SpawnPoints_DiscardRatings()
+!!! cpp-function "int StatusEffect_AddTimed( entity ent, int eStatusEffect, float severity01, float "duration, float easeOut )
+Adds a status effect that will stop automatically after a given time.
+int StatusEffect_AddEndless( entity ent, int eStatusEffect, float severity01 )
+Adds a status effect
+bool StatusEffect_Stop( entity ent, int effectHandle )
+Stops a status effect given its handle (return value of StatusEffect_AddTimed or StatusEffect_AddEndless).
+int StatusEffect_StopAll( entity ent, int eStatusEffect )
+Stops all status effects of a given type. Returns the number that were stopped.
+float StatusEffect_Get( entity ent, int eStatusEffect )
+array
Stryder is in a sense like the masterserver Northstar uses but for vanilla. It handles player data, matchmaking, servers and more.
+Note
+Not exclusive to vanilla lobbies. These may be used in northstar as well
+Methods for pregame lobbies.
+string GetLobbyType()
+entity GetPlayerByIndex( number index )
+void SendPlayersToPartyScreen( var unk1 )
+Sends a group of players off to the party screen, possibly by allocating a server first
+void SendAllPlayersBackToPartyScreen()
+Methods for communication with the vanilla master server
+void SendTrainingGauntletStatsToBackend( entity player, number numRunsBeforeBeatRequiredTime, number numChallengeRuns, number bestTime )
+bool IsMatchmakingServer()
+bool ShouldSendDevStats()
+Some proprietary telemetry system used by respawn.
+void CreatePINTelemetryHeader( int versionMajor, int versionMinor, table keyValuePairs )
+void AddPINTelemetryEvent( string eventName, table headerKeyValueParis, table bodyKeyValuePairs )
+string GetPINPlatformName()
+Gets the platform name the way PIN likes it.
+void BeginPrivateMatchSearchForPlayer( entity player )
+void MatchmakePlayer( entity player )
+void AbortMatchSearchesForPlayer( string unk1, entity player )
+string GetDatacenterName()
+Gets the name of this server's datacenter
+void MarkTeamsAsBalanced_On()
+void MarkTeamsAsBalanced_Off()
+For async code read Threads, Signals and Flags.
+float Time()
+Get ms since the VM has been started
+void TimerStart()
+float TimerStop()
+Does what it says on the tin
+int GetUnixTimestamp()
+float FrameTime()
+float PlatformTime()
+float IntervalPerTick()
+int FrameCount()
+void Explosion( vector center, entity attacker, entity inflictor, number damage, number damageHeavyArmor, number innerRadius, number outerRadius, int flags, vector projectileLaunchOrigin, number explosionForce, int scriptDamageFlags, int scriptDamageSourceIdentifier, string impactEffectTableName )
+"Creates an explosion. Does damage in an area, moves physics objects, plays effects.
+void Explosion_DamageDefSimple( int damageDefID, vector center, entity attacker, entity inflictor, vector projectileLaunchOrigin )
+Creates an explosion. Does damage in an area, moves physics objects, plays effects.
+void Explosion_DamageDef( int damageDefID, vector center, entity attacker, entity inflictor, number damage, number damageHeavyArmor, vector innerRadius, vector outerRadius, vector projectileLaunchOrigin )
+Same as Explosion_DamageDefSimple but specify damage and radius.
+void RadiusDamage( vector center, entity attacker, entity, inflictor, number damage, number damageHeavyArmor, number innerRadius, number outerRadius, int flags, number distanceFromAttacker, number explosionForce, int scriptDamageFlags, int scriptDamageSourceIdentifier )
+Does silent, invisible damage in a spherical area.
+void RadiusDamage_DamageDefSimple( int damageDefID, vector center, entity attacker, entity inflictor, number distanceFromAttacker )
+Does silent, invisible damage in a spherical area.
+void RadiusDamage_DamageDef( int damageDefId, vector center, entity attacker, entity inflictor, number damager, number damageHeavyArmor, number innerRadius, number outerRadius, number distanceFromAttacker )
+Same as RadiusDamage_DamageDefSimple but specify damage and radius.
+void Weapon_SetDespawnTime( number time )
+int GetImpactEffectTable( string weapon )
+float CalcWeaponDamage( entity owner, entity target, entity weapon, number distanceToTarget, int extraMods )
+Functions for creating a rui, and methods of the rui object
+ // To create one, do:
+ rui = RuiCreate( $"ui/assetname.rpak", topology, drawGroup, sortKey ) // sortkey = int to prevent z-fighting. higher -> in front
+
+ // You can then manipulate it using the following:
+ RuiSetDrawGroup( rui, drawGroup )
+ RuiSetString( rui, argName, value )
+ RuiSetBool( rui, argName, value )
+ RuiSetInt( rui, argName, value )
+ RuiSetFloat( rui, argName, value )
+ RuiSetFloat2( rui, argName, value ) // value is a vector; only x and y are used
+ RuiSetFloat3( rui, argName, value )
+ RuiSetColorAlpha( rui, argName, color, alpha ) // color is a vector
+
+ // To destroy it, just do:
+ RuiDestroy( rui )
+
Drawgroups
+ RUI_DRAW_WORLD
+ RUI_DRAW_HUD
+ RUI_DRAW_COCKPIT
+ RUI_DRAW_NONE
+
Trackers
+ // VECTOR TYPES
+ RUI_TRACK_ABSORIGIN_FOLLOW // Create at absorigin, and update to follow the entity
+ RUI_TRACK_POINT_FOLLOW // Create on attachment point, and update to follow the entity
+ RUI_TRACK_OVERHEAD_FOLLOW // Create at the top of the entity's bbox
+ RUI_TRACK_EYEANGLES_FOLLOW
+
+ // FLOAT TYPES
+ RUI_TRACK_HEALTH // Health as fraction from 0 to 1
+ RUI_TRACK_FRIENDLINESS // 0 if ent is enemy, 1 if it's friendly
+ RUI_TRACK_PLAYER_SUIT_POWER // Player's suit power from 0 to 1
+ RUI_TRACK_PLAYER_GRAPPLE_POWER // Player's grapple power from 0 to 1
+ RUI_TRACK_PLAYER_SHARED_ENERGY // Players shared energy value
+ RUI_TRACK_WEAPON_CHARGE_FRACTION // Weapon charge as fraction from 0 to 1
+ RUI_TRACK_WEAPON_SMART_AMMO_LOCK_FRACTION // Smart ammo weapon lock fraction from 0 to N
+ RUI_TRACK_WEAPON_READY_TO_FIRE_FRACTION // Weapon cooldown as fraction from 0 to 1
+ RUI_TRACK_WEAPON_RELOAD_FRACTION // Weapon reloading as fraction from 0 to 1
+ RUI_TRACK_WEAPON_DRYFIRE_FRACTION
+ RUI_TRACK_WEAPON_CLIP_AMMO_FRACTION // Weapon clip ammo as fraction from 0 to 1
+ RUI_TRACK_WEAPON_REMAINING_AMMO_FRACTION // Weapon remaining ammo as fraction from 0 to 1
+ RUI_TRACK_WEAPON_CLIP_AMMO_MAX
+ RUI_TRACK_WEAPON_STOCKPILE_AMMO_MAX
+ RUI_TRACK_WEAPON_LIFETIME_SHOTS
+ RUI_TRACK_WEAPON_AMMO_REGEN_RATE
+ RUI_TRACK_BOOST_METER_FRACTION // Player boost meter as fraction from 0 to 1
+ RUI_TRACK_GLIDE_METER_FRACTION // Player glide meter as fraction from 0 to 1
+ RUI_TRACK_SHIELD_FRACTION // Shield health as fraction from 0 to 1
+ RUI_TRACK_STATUS_EFFECT_SEVERITY // Status effect severity as fraction from 0 to 1; attachmentIndex used as status effect index
+ RUI_TRACK_SCRIPT_NETWORK_VAR // Value of a script network variable (use GetNetworkedVariableIndex())
+ RUI_TRACK_SCRIPT_NETWORK_VAR_GLOBAL // Value of a script network variable without an entity (use GetNetworkedVariableIndex())
+ RUI_TRACK_SCRIPT_NETWORK_VAR_LOCAL_VIEW_PLAYER // Value of a script network variable on the local view player (changes automatically during kill replay) (use GetNetworkedVariableIndex())
+ RUI_TRACK_FRIENDLY_TEAM_SCORE
+ RUI_TRACK_FRIENDLY_TEAM_ROUND_SCORE // The value of score2 for friendlies
+ RUI_TRACK_ENEMY_TEAM_SCORE
+ RUI_TRACK_ENEMY_TEAM_ROUND_SCORE // The value of score2 for enemies
+ RUI_TRACK_MINIMAP_SCALE
+ RUI_TRACK_SOUND_METER // Sound meter as fraction from 0 to 1.
+
+ // INT TYPES
+ RUI_TRACK_MINIMAP_FLAGS,
+ RUI_TRACK_MINIMAP_CUSTOM_STATE,
+ RUI_TRACK_TEAM_RELATION_VIEWPLAYER, // ENEMY: -1, NEUTRAL: 0, FRIENDLY: 1
+ RUI_TRACK_TEAM_RELATION_CLIENTPLAYER, // ENEMY: -1, NEUTRAL: 0, FRIENDLY: 1
+ RUI_TRACK_SCRIPT_NETWORK_VAR_INT, // Value of a script network variable (use GetNetworkedVariableIndex())
+ RUI_TRACK_SCRIPT_NETWORK_VAR_GLOBAL_INT, // Value of a script network variable without an entity (use GetNetworkedVariableIndex())
+ RUI_TRACK_SCRIPT_NETWORK_VAR_LOCAL_VIEW_PLAYER_INT, // Value of a script network variable on the local view player (changes automatically during kill replay) (use GetNetworkedVariableIndex())
+
+ // GAMETIME TYPES
+ RUI_TRACK_LAST_FIRED_TIME,
+ RUI_TRACK_MINIMAP_THREAT_SECTOR,
+
+ // IMAGE TYPES
+ RUI_TRACK_WEAPON_MENU_ICON,
+ RUI_TRACK_WEAPON_HUD_ICON
+
rui : public var
+void RuiSetResolution(rui, screenSizeX, screenSizey)
+ screenSizeX = GetScreenSize()[0]
+ screenSizeY = GetScreenSize()[1]
+
void RuiSetDrawGroup( rui, drawGroup )
+void RuiSetString( rui, argName, value )
+void RuiSetBool( rui, argName, value )
+void RuiSetInt( rui, argName, value )
+void RuiSetFloat( rui, argName, value )
+void RuiSetFloat2( rui, argName, value )
+value is a vector; only x and y are used
+void RuiSetFloat3( rui, argName, value )
+void RuiSetColorAlpha( rui, argName, color, alpha )
+color is a vector
+void RuiDestroyIfAlive( rui )
+Functions for getting titan, and methods of the titan object
+ entity soul = player.IsTitan() ? player.GetTitanSoul() : player.GetPetTitan().GetTitanSoul()
+ // getting the titan depends on wether the player is in the titan or not
+
titan : public entity
+unknown GetAISettingsName()
+entity GetOffhandWeapon()
+float GetTitanSoulNetFloat( string )
+e.g. "coreAvailableFrac"
+void SetShieldHealth()
+bool HasSoul()
+bool IsArcTitan()
+bool IsDoomed()
+bool IsPetTitan()
+bool IsPhaseShifted()
+bool IsTitanNPC()
+RUI elements are rendered on topologies.
+The position of topologies are relative to the position of their parent.
+Since the number of topologies that can be created is very limited and Vanilla uses most of the slots already, try to minimize your topology uses. Instead of creating new ones, check if you can use one that already exists:
+ clGlobal.topoFullScreen
+ clGlobal.topoCockpitHudPermanent
+ clGlobal.topoTitanCockpitLowerHud
+ clGlobal.topoTitanCockpitInstrument1 // yes, with a 1
+ clGlobal.topoTitanCockpitHud
+ clGlobal.topoCockpitHud
+
void RuiTopology_CreatePlane( vector origin, vector angles, vector right, vector down, bool doClipping )
+This creates a simple topology at the specified origin relative to the parent position.
+The parameters right
and down
specify the dimensions of the topology relative to the origin. For example, passing <GetScreenSize()[0],0,0>
and <0,GetScreenSize()[1],0>
will create a topology that covers the entire screen. Note that in this example the origin is the top left corner. The unit used is pixels.
void RuiTopology_CreateSphere( vector origin, vector angles, vector right, vector down, COCKPIT_RUI_RADIUS, COCKPIT_RUI_WIDTH, COCKPIT_RUI_HEIGHT, float subDiv )
+Similar to RuiTopology_CreatePlane
but creates an arched sphere instead of a plane. Unlike in RuiTopology_CreatePlane
, right and down are angles and not relative positions. The width and height are instead controlled by their respective parameters.
void RuiTopology_Destroy( var topology )
+This destroys the passed topology. However, ruis that are already drawn on top of it do not get destroyed.
+void RuiTopology_SetParent( var topology, entity anchor, string attachName = "" )
+Parents the given topology to the anchor entity. The topology moves and rotates relative to the parent.
+Set the position of the topology to <0,0,0>
to render at the parent's position.
void RuiTopology_UpdatePos( topo, updateOrg, right, down )
+Update the position and dimensions of the topology
+void RuiTopology_ShareWithCode( topology, ruiCode )
+Drawcalls determine how and where RUIs on a topology are being rendered.
+RUI_DRAW_NONE
: Don't render rui at allRUI_DRAW_HUD
: Render rui on screen. Uses screen coordinates in pixels.RUI_DRAW_WORLD
: Render rui in worldspace on a two dimensional surface facing the direction of the topology.RUI_DRAW_COCKPIT
: Similiar to RUI_DRAW_HUD
but follows the cockpit headbob movement.Drawcalls are not set for a topology but for each rui individually
+ // Cover the top left quadrant of the screen with a basic image
+ float[2] s = GetScreenSize()
+ var topo = RuiTopology_CreatePlane( <0,0,0>, <s[0] / 2,0,0>, <0,s[1] / 2,0>, true ) // RUIs scale with the topology they are being drawn on so make sure to use the correct dimensions
+ RuiCreate( $"ui/basic_image.rpak", topo, RUI_DRAW_HUD, 0 )
+
// REMEMBER TO DESTROY ALL TOPOS, RUIS AND PROPS YOU CREATE WHEN YOU NO LONGER NEED THEM
+ // ripped from respawn
+ var function Worldspace_CreateRUITopology( vector org, vector ang, float width, float height )
+ {
+ // adjust so the RUI is drawn with the org as its center point
+ org += ( (AnglesToRight( ang )*-1) * (width*0.5) )
+ org += ( AnglesToUp( ang ) * (height*0.5) )
+
+ // right and down vectors that get added to base org to create the display size
+ vector right = ( AnglesToRight( ang ) * width )
+ vector down = ( (AnglesToUp( ang )*-1) * height )
+
+ return RuiTopology_CreatePlane( org, right, down, true )
+ }
+
+ void function WorldSpaceTopoTest()
+ {
+ // To rotate a topology without manually calculating and updating position and dimensions you can parent the topology to a client side prop
+ entity player = GetLocalClientPlayer()
+ entity weapon = player.GetActiveWeapon()
+
+ vector fwd = AnglesToForward( weapon.GetAngles() )
+ vector right = AnglesToRight( weapon.GetAngles() )
+ vector up = AnglesToUp( weapon.GetAngles() )
+ vector conf = < 20, -40, 30 > // float next to the player's weapon
+
+ int attachIndex = weapon.LookupAttachment( "muzzle_flash" )
+ entity anchor = CreateClientSidePropDynamic( weapon.GetAttachmentOrigin( attachIndex ) + fwd * conf.x + right * conf.y + up * conf.z, <0,0,0>, $"models/dev/empty_model.mdl") // props need a model but this one is invisible so we don't need to set visibility manually
+ var topo = Worldspace_CreateRUITopology( <0,0,0>, <0,90,0>, 128, 64 ) // origin <0,0,0> so the topo sits at the origin of the prop
+
+ var tm_box = RuiCreate( $"ui/helmet_scanning_percentbar.rpak", topo, RUI_DRAW_WORLD, 0 )
+ RuiSetString( tm_box, "stage3TextTop", "Top" )
+ RuiSetString( tm_box, "stage3TextBottom", "Bottom" )
+
+ anchor.SetParent( weapon )
+ RuiTopology_SetParent( topo, anchor )
+ }
+
{
+ "$type": "dtbl",
+ "path": "datatable/custom_datatable",
+ }
+
.csv
File¶setFile |
+titanRef |
+difficulty |
+isPrime |
+coreBuildingIcon |
+
---|---|---|---|---|
titan_buddy |
+bt |
+1 |
+0 |
+rui\titan_loadout\core\titan_core_burst_core |
+
titan_atlas_tracker |
+tone |
+2 |
+0 |
+rui\titan_loadout\core\titan_core_salvo |
+
titan_ogre_meteor |
+scorch |
+3 |
+0 |
+rui\titan_loadout\core\titan_core_flame_wave |
+
string |
+string |
+int |
+bool |
+asset |
+
$type
¶For an asset to be a datatable asset, the $type
field must be "dtbl"
.
path
¶The path
field of a datatable asset is used to determine the location in the RPak's assetsDir
that the .csv
file is in.
Warning
+If the .csv file has no columns, RePak will output the following warning to the console, before skipping the asset.
+Attempted to add dtbl asset with no columns. Skipping asset...
Warning
+If the .csv file has fewer than 2 rows, RePak will output the following warning to the console, before skipping the asset.
+Attempted to add dtbl asset with invalid row count. Skipping asset...
+DTBL - CSV must have a row of column types at the end of the table
The file must be a valid .csv
file, with at least 2 rows, and at least 1 column.
The final row of the .csv
determines the type of each column, and each entry must be one of the following values:
bool
- either 0
(false) or 1
(true)int
- any integer valuefloat
- any float valuevector
- three float values in the format <val1,val2,val3>
string
- any string valueasset
- any string value (must be a valid asset)assetnoprecache
- any string value (must be a valid asset)Textures are the foundation of some RPak asset types. They cannot be used directly by +the game, but are instead referenced by other asset types which the game can use by +itself.
+The image used by a texture must be in the .dds format and must be in one of the +following compression types:
+Warning
+SRGB DDS compression types are preferred, as they can prevent the texture's colour +from looking "washed out"
+ {
+ "$type": "txtr",
+ "path": "textures/models/humans/test_texture",
+ "disableStreaming": true
+ }
+
Note
+The image file in this texture asset will be called test_texture.dds
and will be
+at <ASSETSDIR>/textures/models/humans/test_texture.dds
Note
+Because disableStreaming
is true
, this texture will not be stored in a
+.starpak file, and all mip levels will be stored in the .rpak file
{
+ "$type": "txtr",
+ "path": "textures/models/humans/test_texture_2",
+ "starpakPath": "test_texture_2.starpak"
+ }
+
Note
+The image file in this texture asset will be called test_texture_2.dds
and will
+be at <ASSETSDIR>/textures/models/humans/test_texture_2.dds
Note
+Because disableStreaming
is not present, this texture will have it's higher
+resolution mip levels stored in test_texture_2.starpak
, as defined by the
+starpakPath
. It will not use the default starpakPath
if one is defined
+outside of the files
array
$type
¶For an asset to be a texture asset, the $type
field must be "txtr"
.
path
¶The path
field of a texture asset is used to determine the location in the RPak's
+assetsDir
that the image file is in.
It is also used as the asset's unique identifier, allowing other assets to reference and +use it.
+The path
field must start with textures/
and must not end with a file extension.
Error
+If RePak is unable to locate a file at the given path
, it will output the
+following error to the console:
Failed to find texture source file %s. Exiting...
where %s
is the path
+field of the texture.
Error
+If the file at the given path
is not a .dds file, RePak will output the
+following error to the console:
Attempted to add txtr asset '%s' that was not a valid DDS file (invalid magic).
+where %s
is the path
field of the texture.
Error
+If an unsupported .dds compression type is used, RePak will output the following +error to the console:
+Attempted to add txtr asset '%s' that was not using a supported DDS type.
+Exiting...
where %s
is the path
field of the texture.
starpakPath
¶The starpakPath
field of a texture asset determines the path of the starpak in which
+the higher resolution mip levels should be stored.
If no starpakPath
value is specified, RePak will default to using the default
+starpakPath
, defined at file scope in the map file.
The starpakPath
field should be a string, and importantly, should end in
+.starpak
.
Note
+If the starpak name ends in _hotswap.starpak
(e.g. my_thing_hotswap.starpak
)
+then Titanfall 2 will view it as optional. This allows the starpak to be moved,
+removed, or replaced while the game is running and streaming the texture. This can
+be useful for debugging.
Error
+If the starpakPath
is not present, and no starpakPath
is defined at file
+scope, RePak will output the following error to the console.
attempted to add asset '%s' as a streaming asset, but no starpak files were
+available. to fix: add 'starpakPath' as an rpak-wide variable or: add 'starpakPath'
+as an asset specific variable
where %s is the path
of the texture asset
disableStreaming
¶The disableStreaming
field of a texture asset determines if the texture should use a
+starpak to store the higher resolution mip levels.
It should be a boolean value, with true
disabling the use of a starpak,
disableStreaming
defaults to false
if it is not present.
UI Image Atlases (uimg
) are what the game uses to store multiple UI assets, they
+reference a single texture asset, known as the atlas
and have an array of
+textures
which defines the different usable UI assets.
{
+ "$type":"uimg",
+ "path":"rui/atlas/example1",
+ "atlas":"rui/example1",
+ "textures":
+ [
+ {
+ "path":"rui/example1/texture1",
+ "width":128,
+ "height":128,
+ "posX":0,
+ "posY":0
+ },
+ {
+ "path":"rui/example1/texture2",
+ "width":128,
+ "height":128,
+ "posX":128,
+ "posY":0
+ }
+ ]
+ }
+
Note
+This UI Image Atlas expects a texture with the path of rui/example1
which is at
+least 256x128
{
+ "name":"blue_fire",
+ "assetsDir":"../depot",
+ "outputDir":"../rpaks",
+ "version": 7,
+ "files":[
+ {
+ "$type":"txtr",
+ "path":"rui/blue_fire"
+ },
+ {
+ "$type":"uimg",
+ "path":"rui/atlas/blue_fire",
+ "atlas":"rui/blue_fire",
+ "textures":[
+ {
+ "path":"blue_fire/hud/flame_wall",
+ "width":128,
+ "height":128,
+ "posX":0,
+ "posY":0
+ },
+ {
+ "path":"blue_fire/menu/flame_wall",
+ "width":128,
+ "height":128,
+ "posX":128,
+ "posY":0
+ },
+ {
+ "path":"blue_fire/hud/flame_shield",
+ "width":128,
+ "height":128,
+ "posX":0,
+ "posY":128
+ },
+ {
+ "path":"blue_fire/menu/flame_shield",
+ "width":128,
+ "height":128,
+ "posX":128,
+ "posY":128
+ }
+ ]
+ }
+ ]
+ }
+
Note
+This map file is a shortened version of the one used in EXRILL's +Blue Fire mod
+$type
¶For an asset to be a UI Image Atlas asset, the $type
field must be "uimg"
.
path
¶The path
field for a UI Image Atlas asset is mostly unused, and as such can be set
+to almost any value. It is used when logging information about the asset.
atlas
¶The atlas
field for a UI Image Atlas asset determines which texture asset it will
+use.
Error
+If the uimg asset doesn't contain a valid atlas
field, RePak will output one of
+the following errors to the console:
Required field 'atlas' not found for uimg asset '%s'. Exiting...
'atlas' field is not of required type 'string' for uimg asset '%s'. Exiting...
where %s
is the path
field of the UI Image Atlas
Error
+If the texture asset cannot be found, RePak will output the following message to the +console before exiting:
+Atlas asset was not found when trying to add uimg asset '%s'. Make sure that the
+txtr is above the uimg in your map file. Exiting..."
where %s
is the path
field of the UI Image Atlas
textures
¶The textures
array in a UI Image Atlas asset defines the different UI textures that
+the atlas contains. Any number of UI textures may be contained within one UI Image
+Atlas.
path
¶An entry in the textures
array must have a path
field, as the game must use it
+to identify and show the texture.
Error
+If the entry in the textures
array doesn't contain a valid path
field, RePak
+will output one of the following errors to the console:
Required field 'path' not found for a texture in uimg asset '%s'. Exiting...
'path' field is not of required type 'string' for a texture in uimg asset '%s'.
+Exiting...
where %s
is the path
field of the UI Image Atlas
width
and height
¶An entry in the textures
array must have both a width
and a height
field,
+these values should both be integers.
Error
+If the entry in the textures
array doesn't contain a valid width
or a valid
+height
field, RePak will output one of the following errors to the console:
Required field 'width' not found for texture '%s' in uimg asset '%s'. Exiting...
Required field 'height' not found for texture '%s' in uimg asset '%s'.
+Exiting...
'width' field is not of required type 'number' for texture '%s' in uimg asset
+'%s'. Exiting...
'height' field is not of required type 'number' for texture '%s' in uimg asset
+'%s'. Exiting...
where the first %s
is the path
field of the texture, and the second %s
+is the path
field of the UI Image Atlas
posX
and posY
¶An entry in the textures
array must have both a posX
and a posY
field, these
+values should both be integers. These fields determine the location of the top-left
+pixel in the UI texture.
Error
+If the entry in the textures
array doesn't contain a valid posX
or a valid
+posY
field, RePak will output one of the following errors to the console:
Required field 'posX' not found for texture '%s' in uimg asset '%s'. Exiting...
Required field 'posY' not found for texture '%s' in uimg asset '%s'. Exiting...
'posX' field is not of required type 'number' for texture '%s' in uimg asset '%s'.
+Exiting...
'posY' field is not of required type 'number' for texture '%s' in uimg asset '%s'.
+Exiting...
where the first %s
is the path
field of the texture, and the second %s
+is the path
field of the UI Image Atlas
example1.json
{
+ "version": 7
+ }
+
root
+ ├── RePak.exe
+ ├── example1.json
+ └── build
+ └─ example1.rpak
+
Note
+This example map file is honestly pretty useless. It has no assets, because there is
+no files
field.
It also will have the name new.rpak
and will be created in the ./build
+folder.
example2.json
{
+ "name": "example2",
+ "assetsDir": "../depot",
+ "outputDir": "../output",
+ "version": 7,
+ "starpakPath": "example2.starpak",
+ "files":
+ [
+ {
+ "$type": "txtr",
+ "path": "textures/models/my_texture"
+ }
+ ]
+ }
+
root
+ ├── RePak.exe
+ ├── maps
+ | └─ example2.json
+ ├── depot
+ | └─ textures
+ | └─ models
+ | └─ my_texture.dds
+ └── output
+ ├─ example2.starpak
+ └─ example2.rpak
+
Note
+This example map file creates an RPak named example2.rpak
which contains 1
+texture asset. This texture will have it's higher resolution mip levels stored in
+example2.starpak
Note
+The texture will replace any vanilla textures that have the same path. (
+textures/models/my_texture
)
This is useful for creating basic skins and camos.
+example3.json
{
+ "name": "example3",
+ "assetsDir": "../depot",
+ "outputDir": "../output",
+ "version": 7,
+ "starpakPath": "example3.starpak",
+ "files":
+ [
+ {
+ "$type": "txtr",
+ "path": "textures/models/my_texture_col"
+ },
+ {
+ "$type": "txtr",
+ "path": "textures/models/my_texture_nml"
+ },
+ {
+ "$type": "txtr",
+ "starpakPath": "example3-spc.starpak",
+ "path": "textures/models/my_texture_spc"
+ }
+ ]
+ }
+
root
+ ├── RePak.exe
+ ├── maps
+ | └─ example3.json
+ ├── depot
+ | └─ textures
+ | └─ models
+ | ├─ my_texture_col.dds
+ | ├─ my_texture_nml.dds
+ | └─ my_texture_spc.dds
+ └── output
+ ├─ example3.starpak
+ ├─ example3-spc.starpak
+ └─ example3.rpak
+
Note
+This example map file creates an RPak named example3.rpak
which contains 3
+texture assets. These textures each have their higher resolution mip levels stored
+in starpaks.
my_texture_col
and mp_texture_nml
use example3.starpak
, as they do not
+specify their own starpakPath
. This makes them use the default starpakPath
+that is defined at the file scope, instead of in the individual textures.
my_texture_spc
uses example3-spc.starpak
, as it specifies it's own
+starpakPath
.
Note
+This RPak is a good example of a skin that would normally require the skin tool to +install. The advantage of this method is that the skin can be uninstalled or +temporarily disabled when packed as a mod.
+name
¶The name
field of a map file determines the name of the resulting RPak.
The name
is appended with .rpak
and defaults to new
if no name
is
+provided. This results in a default RPak called new.rpak
.
Warning
+In the event that no name
is provided in the map file, RePak will output the
+following warning to the console:
Map file should have a 'name' field containing the string name for the new rpak,
+but none was provided. Defaulting to 'new.rpak' and continuing...\n
assetsDir
¶The assetsDir
field of a map file determines the root path which the program
+combines with the path
for assets in order to find the correct file. This path may
+be a relative path, or an absolute path.
The assetsDir
provided in the map file is appended with a slash ( \
) if
+necessary
Warning
+If no assetsDir
is provided, it defaults to the working directory ( .\
) as
+well as outputting the following warning to the console:
No assetsDir field provided. Assuming that everything is relative to the working
+directory.\n
outputDir
¶The outputDir
field of a map file determines the folder that the program will write
+the RPak and StaRPak files to once they have been created. This path may be a relative
+path, or an absolute path.
The outputDir
provided in the map file is appended with a slash ( \
) if
+necessary
If no outputDir
is provided in the map file, RePak defaults to .\build\
version
¶The version
field of a map file determines the RPak version that RePak will create.
Error
+If no version
field is provided, RePak will output the following error and the
+program will stop:
Map file doesn't specify an RPak version\nUse 'version: 7' for Titanfall 2 or
+'version: 8' for Apex\n
Error
+If an invalid version
field is provided, RePak will output the following error
+and the program will stop:
Invalid RPak version specified\nUse 'version: 7' for Titanfall 2 or 'version: 8'
+for Apex\n
version
values:¶6
: Titanfall 2: Tech Test [UNSUPPORTED]7
: Titanfall 28
: Apex LegendsstarpakPath
¶The starpakPath
field of a map file determines the default starpak path for textures
+(and other streamed assets) to use.
Note
+If the starpak name ends in _hotswap.starpak
(e.g. my_thing_hotswap.starpak
)
+then Titanfall 2 will view it as optional. This allows the starpak to be moved,
+removed, or replaced while the game is running and streaming the texture. This can
+be useful for debugging.
Note
+RePak will not throw any errors if no starpakPath
field is specified, however
+the individual textures may throw errors if they do not have a starpakPath
+specified
files
¶The files
field of a map file is an array of JSON objects, each one representing an
+RPak asset.
RePak will not throw any errors if no files
field is specified, however the
+resulting RPak will contain no assets, rendering it useless.
The documentation for reverse engineering Titanfall2, adding new hooks to Northstar +using information gathered during reverse engineering etc. is very much incomplete.
+If you have experience with reversing Titanfall2 to develop for Northstar or just +reverse engineering experience in general, feel free to add more information here, be it +tools, workflows, etc.
+ + + + + + + + + + + + + + + + +Squirrel allows scripts to spin off function calls in a thread. All subsequential function calls will be threaded as well.
+In threaded functions, it's possible to halt a threaded function with wait
statements, signals, flags and by suspending a thread object.
You can use the IsNewThread()
function to determine if the current function is threaded off.
For more information, check out the squirrel documentation on threads and sq functions of threads. rsquirrel is very similar to vanilla squirrel in this regard.
+A thread is considered finished, after the threaded function returned a value. This may be null
.
To create a new coroutine, call a function with the thread
keyword before.
thread void function(){}()
+ thread MyFunction()
+
To get a thread object, use the newthread
function.
void function CoroutineExample()
+ {
+ suspend( "passback" ) // passback is optional
+ print( "threaded statement" )
+ }
+
+ var co = newthread( CoroutineExample )
+ var suspendedReturn = co.call() // you NEED to use .call, invoking the function with () won't work
+ co.wakeup() // continue thread
+
The wait
statement halts threads for a set amount of time specified after the wait
keyword. Integers and floats are accepted as times in seconds.
void function WaitExample( float n )
+ {
+ wait 1 // wait 1 second
+ wait n // wait n seconds
+ }
+
+ thread WaitExample( 0.5 ) // thread will halt for a total 1.5 seconds
+
To wait a single frame, don't use wait 0
since it doesn't actually wait a game frame. For example, if you have a client loop that does wait 0 even if the game is paused the loop will still run. Use WaitFrame()
instead.
When using infinite loops it's important to work with wait
statements to avoid the game freezing.
If you want to wait until a thread is finished, you can spin off the thread that you wait for with the waitthread
keyword.
void function ParentThread()
+ {
+ printt( "pre spinoff " + string( Time() ) )
+ waitthread void function()
+ {
+ printt( "mid spinoff " + string( Time() ) )
+ wait 1
+ }
+ printt( "post spinoff" + string( Time() ) )
+ }
+
Use the OnThreadEnd
function to execute a callback after a thread has ended. This is useful for cleanup functions that remove entities after they're used or similar.
void function PlayIncomingFX( vector origin, int teamNum )
+ {
+ wait 1.50
+ EmitSoundAtPosition( teamNum, origin, "Titan_1P_Warpfall_Start" )
+
+ local colorVec = Vector( 0, 255, 0 )
+ entity cpoint = CreateEntity( "info_placement_helper" )
+ SetTargetName( cpoint, UniqueString( "pickup_controlpoint" ) )
+ DispatchSpawn( cpoint )
+ cpoint.SetOrigin( colorVec )
+ entity glowFX = PlayFXWithControlPoint( INCOMING_SPAWN_FX, origin, cpoint, -1, null, null, C_PLAYFX_LOOP )
+
+ OnThreadEnd(
+ function() : ( glowFX, cpoint )
+ {
+ if ( IsValid( glowFX ) )
+ glowFX.Destroy()
+ if ( IsValid( cpoint ) )
+ cpoint.Destroy()
+ }
+ )
+
+ wait 1.25
+ }
+
void function SetPositionDelayed( entity ent, vector pos, float delay )
+ {
+ wait delay
+ ent.SetOrigin( pos )
+ }
+
+ SetPositionDelayed( player, <0, 0, 100>, 5.0 )
+ SetPositionDelayed( player, <0, 0, 50>, 2.5 ) // this will finish sooner.
+
Signals and flags allow threads to wait for events before running some code.
+void RegisterSignal( string signal )
+Registers a Signals to use on any entity. It's required to register signals before using them.
+CBaseEntity
+ +void Signal( string signal, table results = null )
+Trigger a signal on this entity. The passed results
will be returned by WaitSignal
.
void EndSignal( string signal )
+Ends this thread when the identifier is signaled on this entity
+table WaitSignal( string signal )
+Halts this thread until a signal is activated for this entity
+void ConnectOutput( string signal, void functionref( entity trigger, entity activator, entity caller, var value ) callback )
+Register a callback that executes when the signal
has been fired on this Entity
void DisconnectOutput( string event, void functionref( entity trigger, entity activator, entity caller, var value ) callback )
+Disconnects the callback from the signal.
+void AddOutput( string outputName, string | entity target, string inputName, string parameter = "", float delay = 0, float maxFires = 0 )
+Connects an output on this entity to an input on another entity via code. The target
can be a name or a named entity.
void Fire( string signal, string param = "", float delay = 0, entity activator = null, entity caller = null )
+Fire a signal on this entity, with optional parm and delay
+void FireNow( string output, string param = "", float delay = 0, entity activator = null, entity caller = null )
+Fire a signal on this entity, with optional parm and delay (synchronous)
+It's also possible to trigger and catch signals with methods that aren't properties of an entity.
+void Signal( var obj, string signal, table results = null )
+Trigger a signal on ent
. The passed results
will be returned by WaitSignal
.
table WaitSignal( entity ent, ... )
+Wait for any of the passed signals to be triggered.
+ // Wait for the NPC to die, delete, or get leeched, then remove the npc from the array
+ WaitSignal( ent, "OnDeath", "OnDestroy", "OnLeeched" )
+
void EndSignal( var obj, string signal )
+Ends this thread when the identifier is signaled on ent
For example, if we want to tell a player not to give up after being killed several times, we can write it this way:
+ // First, we register signal we want to use
+ RegisterSignal("OnMultipleDeaths")
+
+
+ void function WatchForDeaths (entity player)
+ {
+ int deathsCount = 0
+
+ while( GamePlayingOrSuddenDeath() )
+ {
+ if ( player.isDead() ) // This doesn't exist, don't try this at home
+ {
+ deathsCount += 1
+
+ if (deathsCount >= 42)
+ {
+ // This sends "OnMultipleDeaths" signal on player entity
+ player.Signal( "OnMultipleDeaths" )
+ }
+ }
+ }
+ }
+
+
+ void function DontGiveUp (entity player)
+ {
+ // This is a blocking call
+ player.WaitSignal("OnMultipleDeaths");
+
+ // This will not run until entity received "OnMultipleDeaths" signal
+ SendHudMessage( player, "Don't give up!", -1, 0.4, 255, 0, 0, 0, 0, 3, 0.15 )
+ }
+
+ // Launch our methods in dedicated threads
+ entity player = GetPlayerArray()[0]
+ thread WatchForDeaths( player )
+ thread DontGiveUp( player )
+
In this example, the DontGiveUp
method is launched at the same time as WatchForDeaths
; but it will not
+run until player died 42 times.
When you want your thread to die on a given event, you can use entity.EndSignal( "OnMultipleDeaths" )
; when said signal
+is set, thread will end (after calling any OnThreadEnd
methods).
Flags
work pretty much the same way as Signals
, except they can be set up without target entity:
void FlagInit( string flag, bool isSet = false )
+Create a flag
+void FlagWait( string flag )
+Halts a thread until a flag is set. Callee must be threaded off.
+void FlagWaitAll( ... )
+Halts until every passed flag is set. Callee must be threaded off.
+void FlagWaitWithTimeout( string flag, float timeOut )
+Halts until the passed flag is set or the timer runs out. Callee must be threaded off.
+void FlagSet( string flag )
+Raise a flag
+void FlagSetOnFlag( string flagset, string flagwait, float delay = 0 )
+Set flagset
after flagwait
is set and the delay is met.
void FlagClear( string flag )
+Reset a flag
+void FlagWaitClearAll( ... )
+Resets all passed flags.
+void FlagClearOnFlag( string flagclear, string flagwait )
+Reset flagclear
when flagwait
is set.
void FlagWaitClearWithTimeout( string flag, float timeOut )
+Resets a flag after the timer runs out.
+void FlagWaitClearAny( ... )
+Wait until any passed flag is cleared.
+void FlagClearEnd( string flag )
+void FlagToggle( string flag )
+Raise a flag if it is reset, or reset it if it's raised.
+void FlagEnd( string flag )
+Ends this thread when the flag is set
+bool Flag( string flag )
+Returns the current state of a flag.
+bool FlagExists( string flag )
+Returns true
if the flag is initialized
array
Splits the flag on " "
array
Splits the value of the keyvalues of the entity on the index field
on " "
void function FlagExample()
+ {
+ FlagInit( "BombHasExploded" )
+
+ thread BombTicker()
+
+ FlagWait( "BombHasExploded" )
+ print( "bomb just exploded" )
+ }
+
+ void function BombTicker()
+ {
+ Assert( IsNewThread(), "BombTicker must be threaded off" )
+ wait RandomFloatRange( 3, 9 )
+ FlagSet( "BombHasExploded" )
+ }
+
Note
+The version Respawn is using differs in some places from classes that are in use in +Squirrel 3.x
+This is by no means complete. Please add anything you know.
+To declare a class, first add the untyped
keyword and the class as a variable at
+file level.
untyped
+ var ExampleClass
+
The untyped
declaration is required because instances have an unknown type and it's
+not possible to use classes as types.
var [classname]
represents the class. After declaring the class inside of a function
+you can use it in the script. You can use any type that can hold vars to store classes.
+Refer to Namespaces for more info.
If needed, add the global keyword for the variable to make the class usable everywhere +in the vm.
+It's not possible to declare classes on local variables. It's required to declare the +class inside of a function.
+Most classes use a constructor. A constructor is a function of the instance that gets +executed on object creation.
+ void function initClient() {
+ class ExampleClass {
+ constructor(){print("Instance of ExampleClass created");}
+ }
+ }
+
You can require parameters in the constructor. Keep in mind that you have to pass those +when creating an object.
+Function parameters are passed as type var
, but the type keyword is not required.
+constructor( parameter ){}; func( parameter ){};
and constructor( var parameter
+){}; func( var parameter ){};
are both correct.
class ExampleClass {
+ propertyString = null // Actual type is var
+ propertyInt = null // Actual type is var
+ constructor( var pString, var pInt ) {
+ this.propertyString = expect string(pString);
+ this.propertyInt = expect int(pInt);
+ }
+ }
+
+ // See section "Declaring Objects" for more information on object creation
+ var obj = ExampleClass( "foo", 1 );
+ printt(obj.propertyString, obj.propertyString ) // foo, 1
+ var lObj = ExampleClass(); tObj = ExampleClass( "" , 0 , 0); // Both throw an error compile time because parameters don't match with the constructor
+
Usually objects have properties. To define them, just add their identifier into the
+class without type declaration. The properties will be of type var
. However, you are
+required to set a default value of a property. This may be null
.
Every object has a reference to itself called this
. You can change parameters of an
+object by reference.
void function initClient() {
+ class ExampleClass {
+ property = null
+ constructor( var parameter ) {
+ this.property = expect int(parameter);
+ }
+ }
+ }
+
You can't use the class name as a type. Use var
instead. You can't expect
them
+either.
Functions of a class have to return a value of type var
. This may be null
.
+Define functions like this:
global var ExampleClass;
+ void function initClassF(){
+ class ExampleClass {
+ variable = "default value"
+
+ // Set field 'variable' of this instance to passed parameter
+ function setV( pV ){
+ this.variable = pV
+ }
+
+ // Return field 'variable' of this instance
+ function getV(){
+ return this.variable; // return value can be of any type
+ }
+ }
+ var inst = ExampleClass();
+ print(inst.getV()); // -> default value
+ inst.setV("new value");
+ print(inst.getV()); // -> new value
+ }
+
It's possible to insert more properties into a class at runtime. To achieve this, use
+the <-
operator.
// Using ``ExampleClass`` and ``exampleObject`` from example above
+ ExampleClass.newProperty <- "New property in class"
+ // The value of the new index may be of any type.
+ ExampleClass.newFunc <- function(){return "Function return value";}
+
Note
+It is not possible to insert new fields into an instance or a class after +instantiation
+ var ExampleErrorClass;
+
+ func(){
+ class ExampleErrorClass {};
+ var eInst = ExampleErrorClass()
+ eInst.e <- "Instance error value"; // Asserts error: class instances do not support the new slot operator
+ ExampleErrorClass.e <- "Class error value"; // Fails because an instance of class ExampleErrorClass has already been created. Asserts error: trying to modify a class that has already been instantiated
+ }
+
Inserting functions is also possible using the ::
operator
function ExampleClass::AddOne( var param /* parameters have to be var */ ){ return expect int( param ) + 1 }
+ var e = ExampleClass()
+ print( expect int( e.AddOne( 1 ) ) ) // prints 2
+
This allows mods to extend functionality of classes declared in the base game and other +mods that have already been loaded.
+For example, extending functionality of the CPlayer class might look like this:
+ global function InitCPlayerInsert
+
+ void function InitCPlayerInsert()
+ {
+ CPlayer.afkCount <- 0 // Insert new property into the CPlayer class
+ CPlayer.maxAFKCount <- 3
+ function CPlayer::AFK(){ // Kick a player when they are afk multiple times in a match
+ if ( this.afkCount >= this.maxAFKCount )
+ ClientCommand( this, "disconnect You have been AFK too often in a match")
+ else
+ {
+ this.afkCount++
+ SendHudMessage( this, format( "You are AFK!\nYou will get kicked after %i more violations", this.maxAFKCount - this.afkCount ), -1, 0.4, 255, 255, 255, 0, 0.5, 5, 0.9 )
+ }
+ }
+
+ // To trigger the method, do GetPlayerArray()[0].AFK()
+ }
+
This will allow scripts to run the AFK
method on CPlayer entities, which will kick a
+player after 3
Make sure to load this script after the class has been declared and before it's +instantiated!
+Note that any properties added to classes don't apply to other classes that are +inherited from a modified class.
+To create an instance, do:
+ class ExampleClass {
+ property = null
+ constructor( var parameter ) {
+ this.property = expect int(parameter);
+ }
+ }
+
+ var exampleObject = ExampleClass(1);
+ int n = exampleObject.property // n = 1
+ exampleObject.property++;
+ n = exampleObject.property // n = 2
+
It's also possible to create an instance without calling the constructor.
+ // Using 'ExampleClass' from previous examples
+ var e = ExampleClass.instance()
+ e.constructor(1) // Constructor is a normal function so you can call it manually.
+
Like the example above shows you can manipulate properties of a class directly. There is +no way to make a private property.
+Methods from a class can be accessed without an instance. Note that the class itself
+doesn't have a reference to itself, meaning that the this
keyword refers to the root
+table.
var class = ExampleClass
+ var instance = class.constructor()
+
Unlike other types, passing an object does not pass a copy of the object, but a +reference to itself. This means that any modifications inside of a function are applied +to the original object.
+ void function initClass(){
+ class Container {
+ content = null
+ constructor ( var pString ) {
+ this.content = expect string(pString);
+ }
+ }
+ var con = Container("original string")
+ manipulateContainer( con )
+ print(con.content) // -> manipulated string
+ }
+
+ void function manipulateContainer( var con ){
+ con.content = "manipulated string";
+ }
+
You can avoid this by using cloned objects. Use the clone
keyword to create a copy
+of an object.
// Assumes the 'Container' class from the previous example has already been declared
+ void function initClass(){
+ var originalObj = Container("original string")
+ var clonedObj = clone originalObj
+ manipulateContainer( clonedObj )
+ printt(orignalObj.content, clonedObj.content) // -> original string, manipulated string
+ }
+
+ void function manipulateContainer( var con ){
+ con.content = "manipulated string";
+ }
+
Instead of declaring classes as a global var, you can use other types such as tables to +hold multiple class objects that emulate the behaviour of namespaces to a certain +extend.
+ global table<string, var> fakeNamespace = {
+ class1 = null,
+ class2 = null
+ }
+
This allows you to group classes together in a single global variable.
+You can use the classes inside of the table like this:
+ // Create a class object in field
+ class fakeNamespace.class1 { constructor(){ print("constructing instance of class1") } }
+ class fakeNamespace.class2 { constructor(){ print("constructing instance of class2") } }
+
+ // Access class object in field
+ var c1 = fakeNamespace.class1()
+ var c2 = fakeNamespace.class2()
+
+ // Insert functions into class object in field
+ fakeNamespace.class1.testfunc <- var function(){ print( "inserted function in class1" ) }
+
You can also declare classes in an array:
+ array<var> classes // This has to be at file level
+
+ // This has to be inside of a function:
+ classes.append( class { constructor(){ print( "inline constructor" ) } )
+ var instance = classes[0]()
+
And in a similar fashion in structs:
+ struct {
+ var class1 = null
+ var class2 = null
+ } classes // This has to be at file level
+
+ // This has to be inside of a function:
+ classes.class1 = class { constructor(){ print( "inline constructor" ) } )
+ classes.class2 = class { constructor(){ print( "inline constructor" ) } )
+ var c1 = classes.class1()
+ var c2 = classes.class2()
+
Warning
+Respawn's fork doesn't appear to support inheritance. Using the extend
keyword
+won't compile.
class Child extends Parent{}
+
Make sure you check out the squirrel documentation on +classes and built +in class instance +methods for more information.
+ + + + + + + + + + + + + + + + +The Squirrel VMs can be manipulated and controlled by Northstar and Plugins.
+All functions and macros documented are defined and used in the Northstar Client. For plugins it's recommended to use F1F7Y's +template that mirrors the definitions to an extend or rrplug if you prefer Rust. +Note that this documentation only covers the C++ API available in the Launcher.
+For more information you can read the official library documentation. +However be aware that implementations or behaviour might be different and features are missing in Respawn's Squirrel fork.
+ + + + + + + + + + + + + + + + +Warning
+Parameters or descriptions may be wrong or incomplete
+This list is incomplete and only lists methods available in squirrel.h.
+Some descriptions are taken from the Squirrel Documentation
+SquirrelManager
+You can access all sq functions only with a SquirrelManager
instance. You have one available inside the ADD_SQFUNC
macro.
void pushbool(HSquirrelVM* sqvm, const SQBool bVal)
+HSquirrelVM* sqvm
the target VMSQInteger bVal
the bool that will be pushedpushes a boolean to the stack
+void pushinteger(HSquirrelVM* sqvm, const SQInteger iVal)
+HSquirrelVM* sqvm
the target VMSQInteger iVal
the integer that will be pushedvoid pushfloat(HSquirrelVM* sqvm, const SQFloat fVal)
+HSquirrelVM* sqvm
the target VMSQInteger fVal
the float that will be pushedpushes a float to the stack
+void pushstring(HSquirrelVM* sqvm, const SQChar* sVal, int length = -1)
+HSquirrelVM* sqvm
the target VMSQChar* sVal
the string that will be pushedint len
length of the string sVal
+. If the parameter length is less than 0 the VM will calculate the length using strlen
pushes a string to the stack
+void pushasset(HSquirrelVM* sqvm, const SQChar* sVal, int length = -1)
+HSquirrelVM* sqvm
the target VMSQChar* sVal
the string that will be pushedint len
length of the string sVal
+. If the parameter length is less than 0 the VM will calculate the length using strlen
pushes an asset to the stack
+void pushvector(HSquirrelVM* sqvm, const Vector3 vVal)
+HSquirrelVM* sqvm
the target VMVector3 vVal
the vector that will be pushedpushes a vector to the stack
+void pushobject(HSquirrelVM* sqvm, SQObject obj)
+HSquirrelVM* sqvm
the target VMSQObject obj
the object that will be pushedpushes an object like functions to the stack
+void pushroottable(HSquirrelVM* sqvm)
+HSquirrelVM* sqvm
the target VMpushes the current root table into the stack
+Note
+sq_pushnull
(0x33D0
) and more aren't included in squirrel.h
right now but may be in the future.
SQBool getbool(HSquirrelVM* sqvm, const SQInteger stackpos)
+HSquirrelVM* sqvm
the target vmSQInteger stackpos
stack position of the objectSQInteger getinteger(HSquirrelVM* sqvm, const SQInteger stackpos)
+HSquirrelVM* sqvm
the target vmSQInteger stackpos
stack position of the objectSQFloat getfloat(HSquirrelVM* sqvm, const SQInteger stackpos)
+HSquirrelVM* sqvm
the target vmSQInteger stackpos
stack position of the objectSQChar* getstring(HSquirrelVM* sqvm, const SQInteger stackpos)
+HSquirrelVM* sqvm
the target vmSQInteger stackpos
stack position of the objectVector3 getvector(HSquirrelVM* sqvm, const SQInteger stackpos)
+HSquirrelVM* sqvm
the target vmSQInteger stackpos
stack position of the objectSQChar* getasset(HSquirrelVM* sqvm, const SQInteger stackpos)
+HSquirrelVM* sqvm
the target vmSQInteger stackpos
stack position of the objectSQTable* getConstants(HSquirrelVM* sqvm)
+Note
+This function (``server.dll+0x5920```) is not available in the launcher or plugins at the moment.
+You can open a PR if you need it now.
+To define an integer constant you can use defconst
instead.
HSquirrelVM* sqvm
the target vmPushes the constants table to the stack.
+Used to add global constants for scripts.
+ getConstants(sqvm);
+
+ pushstring(sqvm, "MY_CONSTANT");
+ pushstring(sqvm, "MY_VALUE");
+ newslot(sqvm, -3, false);
+
+ removeFromStack(sqvm); // don't forget this!
+
int sq_getfunction(HSquirrelVM* sqvm, const SQChar* name, SQObject* returnObj, const SQChar* signature)
+HSquirrelVM* sqvm
the target vmSQChar* name
the function name to search forSQObject* returnObj
reference to the object to hold the function objectSQChar* signature
returns 0
if the function was found.
SQObject functionobj {};
+ int result = sq_getfunction(m_pSQVM->sqvm, funcname, &functionobj, 0);
+ if (result != 0) // This func returns 0 on success for some reason
+ {
+ NS::log::squirrel_logger<context>()->error("Call was unable to find function with name '{}'. Is it global?", funcname);
+ return SQRESULT_ERROR;
+ }
+
T* getentity(HSquirrelVM* sqvm, SQInteger iStackPos)
+HSquirrelVM* sqvm
The target vmSQInteger iStackPos
Stack position of the entityvoid* __sq_getentityfrominstance(CSquirrelVM* sqvm, SQObject* pInstance, char** ppEntityConstant)
+CSquirrelVM* sqvm
The target vmSQObject* pInstance
Instance holding an entitychar** ppEntityConstant
Entity constant like ref``__sq_GetEntityConstant_CBaseEntity <sq-GetEntityConstant-CBaseEntity>
char** __sq_GetEntityConstant_CBaseEntity()
+There are entity constants for other types, but seemingly CBaseEntity's is the only one needed
+SQRESULT __sq_getobject(HSquirrelVM* sqvm, SQInteger iStackPos, SQObject* obj)
+HSquirrelVM* sqvm
The target vmSQInteger iStackPos
Stack position of the objectSQObject* obj
Pointer that will hold the objectobj
will be overwritten to hold the squirrel object.
This example adds a native function with the ADD_SQFUNC
macro.
+The function takes a function reference as a callback and calls it immediately.
+More information about function calls are available here
ADD_SQFUNC("void", SQCallbackTest, "void functionref()", "", ScriptContext::UI)
+ {
+ SQObject fn; // Make an empty sqobject. This will hold the function object later
+ g_pSquirrel<context>->__sq_getobject(sqvm, 1, &fn); // Assign the function object to the SQOBJECT
+ g_pSquirrel<context>->pushobject(sqvm, &fn); // Push the function object for the call
+ g_pSquirrel<context>->pushroottable(sqvm); // Push the root table for the function stack
+ g_pSquirrel<context>->__sq_call(sqvm, 1, false, true); // call the function with one parameter (the 'this' object)
+
+ return SQRESULT_NULL;
+ }
+
SQRESULT get(HSquirrelVM* sqvm, const SQInteger stackpos)
+HSquirrelVM* sqvm
the target vmSQInteger stackpos
stack position of the objectReturns an SQRESULT
that indicates whether or not the access was successful.
pops a key from the stack and performs a get operation on the object at the position idx in the stack; and pushes the result in the stack.
+SQRESULT sq_stackinfos(HSquirrelVM* sqvm, int level, SQStackInfos& out)
+HSquirrelVM* sqvm
the target vmint level
stack depth of the infoSQStackInfos& out
instance that will hold the informationMod* getcallingmod(HSquirrelVM* sqvm, int depth = 0)
+HSquirrelVM* sqvm
the target vmint depth
stack depth of the origin modNote
+Not available in plugins
+void defconst(CSquirrelVM* csqvm, const SQChar* pName, int nValue)
+CSquirrelVM* csqvm
the target vmSQChar* pName
the constant nameint nValue
the constant valuedefines a global squirrel integer constant
+SQRESULT newarray(HSquirrelVM* sqvm, const SQInteger size = 0)
+HSquirrelVM* sqvm
the target vmSQInteger size
initial size of the arrayReturns a SQRESULT
creates a new array and pushes it to the stack
+ newarray(sqvm, 0);
+ pushstring(sqvm, "val1");
+ arrayappend(sqvm, -2);
+ pushinteger(sqvm, 15);
+ arrayappend(sqvm, -2);
+
+ /*
+ The array on the stack now looks like this:
+ [ "val1", 15 ]
+ */
+
SQRESULT arrayappend(HSquirrelVM* sqvm, const SQInteger stackpos)
+HSquirrelVM* sqvm
the target vmSQInteger stackpos
stack position of the array to append toSQRESULT
pops a value from the stack and pushes it to the back of the array at the position idx in the stack
+SQRESULT newtable(HSquirrelVM* sqvm)
+HSquirrelVM* sqvm
the target vmReturns a SQRESULT
creates a new table and pushes it onto the stack.
+SQRESULT newslot(HSquirrelVM* sqvm, SQInteger stackpos, SQBool bstatic)
+HSquirrelVM* sqvm
the target vmSQInteger stackpos
the index of the table to insert intoSQBool bstatic
if SQTrue
creates a static member. This parameter is only used if the target object is a class.pops a key and a value from the stack and performs a set operation on the table or class that is at position idx in the stack, if the slot does not exist it will be created.
+ newtable(sqvm);
+ // slot 1
+ pushstring(sqvm, "key");
+ pushstring(sqvm, "value");
+ newslot(sqvm, -3);
+ // slot 2
+ pushstring(sqvm, "key2");
+ pushasset(sqvm, "value2");
+ newslot(sqvm, -3);
+ // slot 3
+ pushstring(sqvm, "key3");
+ newtable(sqvm);
+ pushstring(sqvm, "sub");
+ pushinteger(sqvm, 13);
+ newslot(sqvm, -3);
+ newslot(sqvm, -3);
+
+ /*
+ The table on the stack now looks like this:
+ {
+ key = "value"
+ key2 = $"value2"
+ key3 = { sub = 13 }
+ }
+ */
+
Note
+These functions aren't available for plugins yet.
+SQRESULT::SQRESULT_NULL pushnewstructinstance(HSquirrelVM* sqvm, int fieldCount)
+HSquirrelVM* sqvm
The target vmint fieldCount
total number of fields the struct containsCreates and pushes a struct instance with fieldCount
to the stack.
SQRESULT::SQRESULT_NULL sealstructslot(HSquirrelVM* sqvm, int fieldIndex)
+HSquirrelVM* sqvm
The target vmint fieldIndex
Index of the field to fillPops a value from the stack and fills the field at fieldIndex
from the struct object that needs to be at the top of the stack.
pushnewstructinstance(sqvm, 2); // create a struct instance with 2 slots
+ pushinteger(sqvm, 12);
+ sealstructslot(sqvm, 0);
+ pushstring(sqvm, "example", -1);
+ sealstructslot(sqvm, 1);
+
+ /*
+ Assuming the compiler expects this slot:
+ struct ExStruct { int i, string s }
+ , the struct on the stack looks like this
+
+ ExStruct {
+ i = 12,
+ s = "example"
+ }
+ */
+
T* createuserdata(HSquirrelVM* sqvm, SQInteger size)
+HSquirrelVM* sqvm
The target vmSQInteger size
bit size of the userdata objectWhen the function sq_newuserdata is called, +Squirrel allocates a new userdata with the specified size, +returns a pointer to his payload buffer and push the object in the stack; +at this point the application can do whatever it want with this memory chunk, +the VM will automatically take care of the memory deallocation like for every other built-in type. +A userdata can be passed to a function or stored in a table slot. By default Squirrel cannot manipulate directly userdata; +however is possible to assign a delegate to it and define a behavior like it would be a table. +Because the application would want to do something with the data stored in a userdata object when it get deleted, +is possible to assign a callback that will be called by the VM just before deleting a certain userdata. +This is done through the API call sq_setreleasehook.
+SQRESULT setuserdatatypeid(HSquirrelVM* sqvm, const SQInteger stackpos, uint64_t typeId)
+HSquirrelVM* sqvm
The target vmSQInteger stackpos
Stack position of the userdataSQRESULT getuserdata(HSquirrelVM* sqvm, const SQInteger stackpos, T* data, uint64_t* typeId)
+HSquirrelVM* sqvm
The target vmSQInteger stackpos
Stack position of the userdataT* data
Pointer to an arbitrary variable the userdata gets mapped touint64_t* typeid
Pointer to a variable edited to hold the userdata typeYou can use the ADD_SQFUNC
macro defined in squirrelautobind.h
to easily add new Squirrel functions for specific contexts.
Inside the macro you have access to the Squirrel Manager of the context the function has been called from and the SQVM.
+Parameters are the initial stack in the function context.
+macro ADD_SQFUNC(return_type, funcName, argTypes, helpText, runOnContext)
+return_type
The squirrel return type the compiler expects from this functionfuncName
The squirrel function nameargTypes
The args with types the compiler expectshelpText
A help text describing the functionrunOnContext
The contexts that have access to this functionReturn a string from a native registered function:
+ ADD_SQFUNC("string", CPlugTest, "", "returns \"native gaming\"", ScriptContext::CLIENT | ScriptContext::SERVER)
+ {
+ g_pSquirrel<context>->pushstring(sqvm, "native gaming"); // push a string to the stack
+
+ return SQRESULT_NOTNULL; // Signal that the topmost item on the stack is returned by this function
+ }
+
Return a complex ornull
type:
ADD_SQFUNC("array<int> ornull", CPlugComplex, "int n", "returns null", ScriptContext::CLIENT | ScriptContext::SERVER | ScriptContext::UI)
+ {
+ SQInteger n = g_pSquirrel<context>->getinteger(sqvm, 1);
+
+ if (n == 0)
+ return SQRESULT_NULL;
+
+ g_pSquirrel<context>->newarray(sqvm, 0);
+ g_pSquirrel<context>->pushinteger(sqvm, n);
+ g_pSquirrel<context>->arrayappend(sqvm, 2);
+ g_pSquirrel<context>->pushinteger(sqvm, n * 2);
+ g_pSquirrel<context>->arrayappend(sqvm, 2);
+
+ return SQRESULT_NOTNULL; // return the array [ n, n * 2 ] or NULL if n == 0
+ }
+
Note
+Replacing functions is not possible in plugins
+You can use the REPLACE_SQFUNC
macro to replace an existing sq function.
macro REPLACE_SQFUNC(funcName, runOnContext)
+funcName
The name of the function to replacerunOnContext
The contexts that have access to this functionIt's also possible to add an override directly with the AddFuncOverride
function of the SquirrelManager
class.
void AddFuncOverride(std::string name, SQFunction func)
+std``string name
The name of the function to overrideSQFunc func
A function object that replaces the logic // Replaces dangerous vanilla functions to only log their call with no further logic.
+ g_pSquirrel<context>->AddFuncOverride("DevTextBufferWrite", SQ_StubbedFunc<context, "DevTextBufferWrite">);
+ g_pSquirrel<context>->AddFuncOverride("DevTextBufferClear", SQ_StubbedFunc<context, "DevTextBufferClear">);
+ g_pSquirrel<context>->AddFuncOverride("DevTextBufferDumpToFile", SQ_StubbedFunc<context, "DevTextBufferDumpToFile">);
+
Scriptcontexts are used to define the VMs that have access to a native function. Available Contexts are
+ScriptContext::SERVER
- The SERVER sqvmScriptContext::CLIENT
- The CLIENT sqvmScriptContext::UI
- The UI vmSquirrel functions need to return a SQRESULT
. Valid results are
SQRESULT_NULL
- This function returns null
. Nothing is left over on the stack.SQRESULT_NOTNULL
- This functions returns the last item on the stack.SQRESULT_ERROR
- This function has thrown an error.SQRESULT Call(const char* funcname)
+char* funcname
Name of the function to callNote
+This is a squirrel API wrapper added by northstar. It's not available for plugins and is supposed to abstract squirrel calls.
+This function assumes the squirrel VM is stopped/blocked at the moment of call
+Calling this function while the VM is running is likely to result in a crash due to stack destruction
+If you want to call into squirrel asynchronously, use AsyncCall
_ instead.
Call("PluginCallbackTest"); // PluginCallbackTest()
+
SQRESULT Call(const char* funcname, Args... args)
+char* funcname
Name of the function to callArgs... args
vector of args to pass to the functionNote
+This is a squirrel API wrapper added by northstar. It's not available for plugins and is supposed to abstract squirrel calls.
+ Call("PluginCallbackTest", "param"); // PluginCallbackTest("param")
+
SquirrelMessage AsyncCall(std::string funcname)
+char* funcname
Name of the function to callNote
+This is a squirrel API wrapper added by northstar. It's not available for plugins and is supposed to abstract squirrel calls.
+This function schedules a call to be executed on the next frame
+This is useful for things like threads and plugins, which do not run on the main thread.
+SquirrelMessage AsyncCall(std::string funcname, Args... args)
+char* funcname
Name of the function to callArgs... args
vector of args to pass to the functionNote
+This is a squirrel API wrapper added by northstar. It's not available for plugins and is supposed to abstract squirrel calls.
+SQRESULT _call(HSquirrelVM* sqvm, const SQInteger args)
+HSquirrelVM* sqvm
the target vmSQInteger args
number of arguments to call this function with_call
adds one to the args
count for this
.
Note
+This is a squirrel API wrapper added by northstar. It's not available for plugins and is supposed to abstract squirrel calls.
+ SQObject functionobj {};
+ SQRESULT result = g_pSquirrel<context>->sq_getfunction(sqvm, "PluginCallbackTest", &functionobj, 0); // Get a global squirrel function called "PluginCallbackTest"
+
+ if (result == SQRESULT_ERROR)
+ {
+ spdlog::error("Unable to find function. Is it global?");
+ return SQRESULT_ERROR;
+ }
+
+ g_pSquirrel<context>->pushobject(sqvm, &functionobj);
+ g_pSquirrel<context>->pushroottable(sqvm);
+ g_pSquirrel<context>->pushstring(sqvm, "param");
+ return g_pSquirrel<context>->_call(sqvm, 1); // PluginCallbackTest("param")
+
SQRESULT __sq_call(HSquirrelVM* sqvm, SQInteger iArgs, SQBool bShouldReturn, SQBool bThrowError)
+HSquirrelVM* sqvm
the target vmSQInteger iArgs
number of parameters of the functionSQBool bShouldReturn
if true the function will push the return value to the stackSQBool bThrowError
if true, if a runtime error occurs during the execution of the call, the vm will invoke the error handlercalls a closure or a native closure. The function pops all the parameters and leave the closure in the stack; if retval is true the return value of the closure is pushed. If the execution of the function is suspended through sq_suspendvm(), the closure and the arguments will not be automatically popped from the stack.
+When using to create an instance, push a dummy parameter to be filled with the newly-created instance for the constructor's this
parameter.
SQRESULT raiseerror(HSquirrelVM* sqvm, const SQChar* error)
+HSquirrelVM* sqvm
the target vmSQChar* error
string thrownSQRESULT_ERROR
Throws an error with error
being the thrown object.
ADD_SQFUNC("void", CPlugThrowTest, "", "", ScriptContext::UI)
+ {
+ return g_pSquirrel<context>->raiseerror(sqvm, "test error");
+ }
+
+ /* sq:
+ try {
+ CPlugThrowTest()
+ } catch(e) {
+ print(e) // "test error"
+ }
+ */
+
Squirrel exchanges values with the virtual machine through a stack. +This mechanism has been inherited from the language Lua. +For instance to call a Squirrel function from native code it is necessary to push the function and the arguments in the stack and then invoke the function; +also when Squirrel calls a native function the parameters will be in the stack as well.
+Many API functions can arbitrarily refer to any element in the stack through an index. The stack indexes follow these conventions:
+See this example stack for reference:
+Stack | +Positive index | +Negative index | +
---|---|---|
p4 | +4 | +-1 | +
p3 | +3 | +-2 | +
p2 | +2 | +-3 | +
p1 | +1 | +-4 | +
The Squirrel API offers several functions to push and retrieve data from the Stack.
+__int64 removeFromStack(HSquirrelVM* sqvm)
+Note
+This function (``server.dll+0x7000```) is not available in the launcher or plugins at the moment.
+You can open a PR if you need it now.
+HSquirrelVM* sqvm
the target vmpops the top item of the stack.
+Functions are an integral part of any programming language. They allow to repeat blocks of code whenever and however often is needed.
+Functions in squirrel are defined with this syntax: <return type> function <name>(<parameters>) <body>
For example, a simple function that returns either true
or false
would look like this:
bool function CoinFlip()
+ {
+ return RandomInt( 2 ) == 0 // generate a random number from 0 - 1
+ }
+
It is not possible to have multiple functions that share the same name (often called "overriding" functions). Every function needs to have an unique name from any global or local variable or function.
+If you need some data after a function is finished (for example after a calculation), you need to return that data.
+You can return anything, however the type of the returned variable needs to match with the return type of the function.
+ string function GetNorthstarName()
+ {
+ return "Northstar" // this would be valid
+ return 1 // this would be invalid because the function needs to return a string
+ }
+
Keep in mind that no code after a return statement will get executed.
+If you don't want to return any value, use void
as the return type. This indicates that your function returns null
.
If nothing is returned by a function, null
will get returned implicitly.
void function ReturnNull()
+ {
+ // return null regardless what happens, this all does the same
+ switch( RandomInt( 3 ) )
+ {
+ case 0:
+ return
+ case 1:
+ return null
+ }
+
+ // only if a 2 was rolled, code here will be executed before the other paths already returned.
+ // because a return statement is lacking, null is getting returned implicitly.
+ }
+
In untyped
files you may leave out the return type. In those cases the return type will default to var
.
Parameters are the input a function gets when called. They are local variables whose values come from the calling function.
+ void function main()
+ {
+ int refcount = 0
+ refcount = IncreaseRefcount( refcount )
+ Assert( refcount == 1 )
+ }
+
+ int function IncreaseRefcount( int n )
+ {
+ return n + 1
+ }
+
Sometimes you need parameters that are optional for a function, like extra options. If a parameter name is followed by = <default-value>
, that parameter is not required to call the function.
Optional parameters need to be the last parameters of a function.
+ void function main()
+ {
+ array a = [ 1, 2, 3, 4 ]
+ PopN( a )
+ PopN( a, 2 )
+
+ Assert( a.len() == 1 )
+ }
+
+ void function PopN( array arr, int n = 1 )
+ {
+ for ( int i; i < n; i++ )
+ {
+ arr.pop()
+ }
+ }
+
With vargs you can pass a function an unlimited amount of parameters. The parameters will be inside a pseudo array called vargv
. The length of the vargs the function receives will be stored inside a variable called vargc
.
You can denote a function to have vargs with adding ...
to the end of the parameter list.
string function CombineStuff( string base, ... )
+ {
+ string s = base
+ for ( int i; i < argc; i++ )
+ {
+ base += vargv[i].tostring()
+ }
+ }
+
Closures are functions that are anonymous (unnamed) functions created in a specific script context that can use variables from the parent scope.
+ void function main()
+ {
+ void functionref() fn = void function(){ print( "I'm a closure" ) } // create a closure
+ fn() // call the closure
+ }
+
Closures can capture variables from their parent scope.
+ void function PlayFXOnEntity( entity ent )
+ {
+ int fxHandle = StartParticleEffectOnEntity( ent, PILOT_THROWN_TICK_WARNING, FX_ATTACH_POINT_FOLLOW, ent.LookupAttachment( "head_base" )
+ OnThreadEnd( void function() : ( fxHandle ){ EffectStop( fxHandle, false, true ) } ) // create a function to stop the fx effect and give it the fx handle it needs
+ ent.EndSignal( "OnDestroy" ) // stop the thread when the entity dies
+ WaitForever()
+ }
+
Squirrel is the programming language used by Respawn. A lot of the logic for the game is written in squirrel scripts because of how convenient it is, even for people with little programming knowledge.
+Squirrel is an dynamically typed interpreted language that is compiled to bytecode when loading. +The version of squirrel used by Respawn is heavily modified. Most notable is the added optional static typing of the language.
+Respawn's fork branched off at version 2.3 of vanilla squirrel so newer features do not exist, like generators.
+Because of how different Respawn's fork of squirrel is, the language is often called "rSquirrel" or "squirrel_re" (the official name found in Apex Legends).
+For Notepad++, define a custom language for Squirrel. Luckily, samisalreadytaken has +written a squirrel highlighter.
+ext="nut"
to ext="nut gnut"
so it works with gnut files as
+ well(If the colors/style are not to your taste) 1. Select Squirrel
in User Language at
+the top 2. Navigate through the tabs to find what you want to change 3. Click its
+'Styler
' button and make the changes you wish to
RespawnSquirrel has been added to the vscode marketplace, you can download it here:
+https://marketplace.visualstudio.com/items?itemName=FrothyWi-Fi.rspn-squirrel
+Otherwise you can simply search "Respawn Squirrel" in the extensions tab
+Squirrel is a high level imperative, object-oriented programming language used in Titanfall to script large amounts of content in the game.
+Respawn modified large parts of the language to fit their needs, for example adding a static type compiler to the language.
+Squirrel still allows you to write dynamically typed code but this is discouraged if possible.
+The syntax of squirrel is very similar to C++ or Javascript and very easy to learn.
+The programmer doesn't need to think about memory management in scripts since all objects are refcounted and the garbage collector can be invoked manually.
+ int function fibonacci( int n )
+ {
+ if ( n < 2 )
+ return n
+
+ return fibonacci( n - 1 ) + fibonacci( n - 2 )
+ }
+
The language provides easy interfaces for coroutines and asynchronous code.
+ void main()
+ {
+ thread timer( 1.0, timercallback )
+ }
+
+ void function timercallback( int iteration )
+ {
+ print( iteration )
+ }
+
+ // call the callback function every n seconds
+ void function timer( float n, void functionref( int ) callback )
+ {
+ int iterations
+ while ( true )
+ {
+ wait n
+ iterations += 1
+ callback( iterations )
+ }
+ }
+
Signals and Flags allow you to control code execution based on events that happen elsewhere in the code or in the ingame world.
+ void main()
+ {
+ AddCallback_OnPlayerRespawned( OnPlayerRespawned )
+ }
+
+ void function OnPlayerRespawned( entity player )
+ {
+ thread CountPlayerTimeAlive( Time() ) // execute this function as threaded so we can use Signals in there
+ }
+
+ void function CountPlayerTimeAlive( entity player, float time )
+ {
+ player.WaitSignal( "OnDestroy" ) // wait until the player dies or disconnects
+ print( Time() - time ) // print how long the player was alive
+ }
+
All VMs (CLIENT
, UI
, SERVER
) are seperate from each other and do not share any variables, even when running on the same machine.
However, there are different interfaces to communicate between all VMs.
+SERVER
to CLIENT
vm¶Remote functions allow the SERVER
vm to call a function from the CLIENT
vm with parameters.
To use remote functions, you have to make a registration on both the CLIENT
and the SERVER
vm with Remote_RegisterFunction
.
Northstar provides the
+AddCallback_OnRegisteringCustomNetworkVars( void functionref() identifierFn)
+callback in which you can use the
+Remote_RegisterFunction(string identifier)
+function. It's not possible to register remote functions after Remote_EndRegisteringFunctions
has been called. The callback exists to allow multiple mods to register remote vars.
Warning
+You can only pass parameters of the types null
, int
, float
or bool
.
It is possible to communicate entities using eHandles. To get an eHandle, use the entity.GetEncodedEHandle()
function. To get the corresponding entity of a handle, use entity ent = GetEntityFromEncodedEHandle( eHandle )
. eHandles are of type int
.
mod.json extract:
+ "Scripts": [
+ {
+ "Path": "sh_spaceships.nut",
+ "RunOn": "CLIENT || SERVER", // execute the same function on both CLIENT and SERVER
+ "ClientCallback": {
+ "Before": "Spaceship_Network"
+ },
+ "ServerCallback": {
+ "Before": "Spaceship_Network"
+ }
+ },
+ {
+ // more script registrations ...
+
sh_spaceships.nut:
+The networked CLIENT
function has to be global
#if CLIENT
+ global function Server_GetNetworkedVariable // make the networked function only global on CLIENT
+ #endif //CLIENT
+
+ global function Spaceship_Network // this gets executed on both CLIENT & SERVER
+
+ void function Spaceship_Network()
+ {
+ AddCallback_OnRegisteringCustomNetworkVars( RegisterNetworkVars ) // you can only register remote functions inside of this callback
+ }
+
+ void function RegisterNetworkVars()
+ {
+ // this has to be executed on both CLIENT and SERVER, else they will be out of sync and the client disconnects
+ Remote_RegisterFunction( "Server_GetNetworkedVariable" ) // register a remote function. Note that the parameters are not declared here
+ }
+
+ #if CLIENT
+ void function Server_GetNetworkedVariable( int number ) // you can declare as many or few parameters as you wish
+ {
+ printt("got integer", number)
+ }
+ #endif //CLIENT
+
Calling the CLIENT
function Server_GetNetworkedVariable
on SERVER
vm:
// player: CPlayer entity that should execute the function
+ // func: function identifier string
+ // ...: any parameters passed to the function
+ Remote_CallFunction_NonReplay( entity player, string func, ... ) // NOT reexecuted in a replay
+ Remote_CallFunction_Replay( entity player, string func, ... ) // reexecuted in a replay
+
+ // for the previous example, this would be a valid remote function call:
+
+ Remote_CallFunction_NonReplay( player, "Server_GetNetworkedVariable", RandomIntRange( 1, 100 ) )
+
Allows the SERVER
vm to create a ServerToClientStringCommand
on a player which is linked to a Callback locally
Note
+this has to be executed on the Before
Client callback
the formatting for the server command is like a normal console command. Arguments are seperated by spaces
+Register with the function clientside:
+AddServerToClientStringCommandCallback( string func, void functionref( array
and execute with the function serverside:
+ServerToClientStringCommand( entity player /CPlayer/, string command )
+ void function MessageUtils_ClientInit()
+ {
+ AddServerToClientStringCommandCallback( "ServerHUDMessageShow", ServerCallback_CreateServerHUDMessage )
+ }
+
+ void function ServerCallback_CreateServerHUDMessage ( array<string> args )
+ {
+ // client side command handle logic ...
+ }
+
SERVER
to UI
vm¶Remote_CallFunction_UI( entity player, string functionName, ... )
+Given a player, function name, and optional parameters, call function in UI script. Allowed var types are null, bool, int, and float.
+ Remote_CallFunction_UI( player, "ScriptCallback_UnlockAchievement", achievementID )
+
CLIENT
to SERVER
vm¶Register a client command callback serverside with
+!!! cpp-function "AddClientCommandCallback( string command, bool functionref( entity player /CPlayer/, array
player
is the player that called the command clientside. The callback function should return true
if the command was accepted and false
if it was invalid.
The CLIENT
vm can execute commands with the function:
player.ClientCommand( string command )
+These will be handled by the SERVER
if the command is registered.
Since version 1.5 mods can receive notifications when a client command has been handled. This is different from AddClientCommandCallback
void AddClientCommandNotifyCallback( string, void functionref( entity, array
Example usage with the :doc:PrivateMatchLaunch
clientcommand
void function init(){
+ AddClientCommandNotifyCallback("PrivateMatchLaunch", started)
+ }
+
+ void function started(entity player, array<string> args){
+ print(player + " started the match")
+ }
+
Please refer to :ref:list_client_commands
for a list of native client commands you could catch.
CLIENT
to UI
vm¶Create a global function in the UI
vm and call it in the CLIENT
vm with the function:
RunUIScript( string identifier, ... )
+You can also pass parameters to the function. identifier
is the name of the function you want to call.
#if UI
+ global function CallMe
+
+ void function CallMe( int a, int b )
+ {
+ printt( a + b )
+ }
+ #elseif CLIENT
+ RunUIScript( "CallMe", 1, 2 ) // 3
+ #endif
+
UI
to CLIENT
vm¶Create a global function in the CLIENT
vm and call it in the UI
vm with the function:
RunClientScript( string identifier, ... )
+You can also pass parameters to the function. identifier
is the name of the function you want to call.
#if CLIENT
+ global function CallMe
+
+ void function CallMe( int a, int b )
+ {
+ printt( a + b )
+ }
+ #elseif UI
+ RunClientScript( "CallMe", 1, 2 ) // 3
+ #endif
+
Unlike the data types previously covered, arrays can hold multiple values.
+Their size is dynamic and you can add and remove elements at will.
+The type keyword is array
.
By default, uninitialized arrays are empty.
+Arrays are always zero indexed with [ <expression> ]
. The indexes are always numbers. If you index an array with a key that does not exist, an error will get thrown.
Array literals are a comma or newline seperated sequence of expressions delimited by an opening bracket [
and a corresponding closing bracket ]
.
array a = [ 1, 2, 3 ]
+ array b = [
+ 1
+ 2
+ 3
+ ]
+
Primitive arrays are arrays that can hold any value. Their content is therefore untyped.
+ array a
+ a.append( 1 ) // add a number
+ a.append( "str" ) // add a string
+ a.append( [] ) // add an empty array
+
+ // because the content type of the array is not defined, all content will be var
+ var n = a[0]
+ var str = a[1]
+ var arr = a[2]
+
Complex Arrays are arrays that can only hold values that have a specific type.
+The content type needs to be specified within <
and >
brackets.
There is no way to define a complex array that holds multiple different types.
+ array<int> a
+ a.append( 1 )
+ a.append( 0x2 )
+ a.append( "3" ) // this will throw a compile error because the array can only contain integers
+
Static arrays are a different kind of complex type. Like complex arrays they can only hold values of one specific type. However unlike complex arrays static arrays have a set length that cannot be changed.
+The typing for static arrays is type[size]
, where type
is the content type of the array and size
is an integer literal of the total size of the array.
Uninitialized static arrays have their size by default and all content values are the default values of their content type.
+You can index and change content values like with regular arrays.
+When initializing a static array you can omit all values after your initial values with ...
. All following values will get default initialized with the content's default.
float[3] v1
+ float[8] v2 = [ 1.0, 2.0, ... ]
+ v2[2] = 3.0
+
+ print( v1[0] ) // notice how no value needs to be pushed into the vector
+ print( v2[7] ) // will print 0.0 because it has been default initialized
+
It is not possible to cast or convert an array between their different forms. For example you can't assign an array<string>
variable to a different variable that has the type array
or the other way around.
Instead you need to create an entirely new array with the target type or add all contents manually.
+ array<string> orig = [ "a", "b", "c" ]
+ array target
+
+ target.clear() // clear all contents from the target array
+ foreach( v in orig ) // iterate over the original array and add all contents to the target array
+ target.append( v )
+
Furthermore it's important to understand that array
and array<var>
behave the same but are not identical.
Entities are a primitive type that can refer to any in-game object.
+The type keyword is entity
.
Entities are always class instances of classes that are defined by native code. The classes differ between the CLIENT or UI, and SERVER vm.
+You can not specify which entity class a variable is supposed to hold so you need to be careful you know what entity is expected where.
+If you need to check the class of an entity at runtime you can do so with the instanceof
operator.
bool function IsCPlayer( entity e )
+ {
+ return e instanceof CPlayer
+ }
+
Entities are null
initialized and there are no literals for entities.
entity e
+ Assert( e == null )
+
Floats are 32 bit floating point numbers that can be any decimal number.
+An unitilized float will have the default value 0.0
.
The type keyword for floats is float
.
Float literals need to contain a .
to distinguish them from integer literals.
They may omit the decimal before the period, however after the period a value is required.
+ float a = 1.1
+ float b = 0.0
+ float c = .0 // 0.0
+ float d = 0. // INVALID, this will throw a compile error because the value after the period is missing.
+
Function references are a complex type that can reference any function or closure.
+The type keyword is functionref
and needs to include any parameter types and optionally return types.
void function CallDelayed( void functionref() fn )
+ {
+ wait 1
+ fn()
+ }
+
You can call functionrefs like a regular function. The return type of a functionref will default to var
if omitted. Omitting the return type is only possible in untyped
files.
Parameter names are optional in functionrefs. Otherwise the parameter syntax is like in regular functions.
+ void function Example( int n, ... ) {}
+
+ void functionref( int, ... ) fn = Example
+
At it's core, squirrel is a dynamically typed language with static typing mostly being a compile time feature.
+The language distinguishes between primitive types and complex types. Some features of the language can only be used for either type.
+ + + + + + + + + + + + + + + + +Integers in Squirrel are 32 bit signed integers and can be any whole number (in the 32 bit confines).
+An Integer is a primitive type with a default value of 0
.
The type keyword for Integers is int
.
Integers can be represented with multiple different literals.
+Decimal
+Regular decimal letters will always be an integer decimal literal. +
int n = 123
+
Hexadecimal
+If any number is prefixed with 0x
, it is a hexadecimal literal.
int n = 0x0012 // 18
+
Octal
+Numbers starting with a 0
are octal literals.
int n = 075 // 61
+
Chars
+A single letter or escaped sequence are character literals. Their value is the ASCII value of the letter.
+int a = 'a' // 97
+int newline = '\n' // 10
+
ornull
is a type suffix that flags the variable to be able to contain null
.
+This is required for nesting structs inside themselves to ensure they are fixed size.
ornull
makes any type complex and stops you from using any inbuilt functions or passing it to a function that does not expect that exact ornull
type.
To use the value of an ornull
variable you need to ensure that it is not null
and then cast to the correct type.
int ornull n = null
+ n = 1
+
+ if( n != null )
+ {
+ expect int( n ) // n is now in this scope an int
+ n += 2
+ }
+
+ print( n ) // 3
+
Being required to cast the value of ornull
variables makes it impossible to use it with types that cannot be casted like complex arrays. You can still make complex ornull variables, just be aware that the content type can never be recasted.
You can use ornull
types in complex type as well, for example in complex arrays.
array<int ornull> a = [ 1, null ]
+ a.append( 2 )
+ a.append( null )
+
Additionally, ornull
is useful for adding optional parameters to functions that need to preserve backwards compatability.
SomeAPIFunction( int ornull n = null ) {}
+
+ // both are valid
+ SomeAPIFunction()
+ SomeAPIFunction( 1 )
+
ornull
-ing a type will make a variable always default initia will make a variable always default initialize with null
instead of the types respective default value.lize with null
instead of the types respective default value.
Unlike in other languages, strings in squirrel are primitive types and immutable. That means you can't change the value of a string but will need to copy and change it in another variable.
+The default value of strings is an empty string with a length of 0.
+The type keyword for strings is string
.
To create strings, simply write the text of the literals in "
quotes.
string s = "this is an example string literal"
+
Verbatim strings do not escape sequences. They begin with a @
token before a regular string literal.
+Verbatim strings can also extend over multiple lines.
+If they do they include any white space between the matching string quotes.
string a = "simple string\nover two lines"
+ string b = @"simple string
+ over two lines"
+
+ Assert( a == b )
+
However, a doubled quotation mark within a verbatim string is replaced by a single quotation mark.
+ string a = "extra quotation mark\""
+ string b = @"extra quotation mark """
+
+ Assert( a == b )
+
Assets and strings are internally the same but at compile time they are different types.
+Assets are used to reference a specific resource (often in rpak files).
+The type keyword for assets is asset
.
Asset literals are regular string literals prefixed with the $
token. Verbatim strings can't be an asset.
asset a = $"my/resource"
+
Northstar added the StringToAsset
function that allows converting any string into an asset.
Structs are statically typed, fixed size structures that contain other values. Similar to tables they are used to store mutliple values, however structs allow for each value to have a seperate type.
+Before using a struct you need to define it and all contents.
+The fields are typed like any regular variable.
+ struct MyStruct
+ {
+ int field1
+ string field2
+ array<float> field3
+ }
+
You can then use MyStruct
as a type anywhere in the file.
+Structs are default initialized by assigning each field it's appropriate default value.
+Struct fields can be indexed by writing instance.field
, just like with tables.
MyStruct myStructInstance
+ printt( myStructInstance.field1 ) // 0
+
Structs are passed by reference so if a function changes a field that field is changed for everything that uses the struct instance.
+Struct instances can also get initiaized with different default values if required.
+Similar like in static arrays, you can omit any fields that should have their type's default value with ...
.
MyStruct ins = { field3 = [], field1 = 1, ... }
+ printt( ins.field1, ins.field2 ) // 1, ""
+
Struct fields can be any type, this includes previously declared structs as well.
+ struct Engine
+ {
+ string manufacturer
+ }
+
+ struct Tire
+ {
+ string material
+ }
+
+ struct Car
+ {
+ Engine engine,
+ Tire[4] tires
+ }
+
Structs can contain fields of their own type, however they need to be null initialized. You can achieve this by specifying their type as ornull
.
struct LinkedList
+ {
+ var content
+ LinkedList ornull nextNode
+ }
+
Any struct field can have an optional default value. If omitted, the type's default value is used instead.
+Default values need to be a constant expression that can be evaluated at compile time.
+ struct Dice
+ {
+ int[6] sides = [ 1, 2, 3, 4, 5, 6 ]
+ }
+
You can define a struct and initialize a local variable of that struct instantly with singletons. These are often used to have global variables that are only used in a single script file.
+ struct {
+ var menu
+ } file
+
+ void function InitMyMenu()
+ {
+ file.menu = GetMenu( "SomeMenu" )
+ }
+
Singletons can also be used for struct fields.
+ struct Car
+ {
+ struct {
+ string manufacturer
+ } engine
+ }
+
+ // ...
+ Car car
+ car.engine.manufacturer = "Vinson Dynamics"
+
Like arrays, tables are dynamically sized data structures that can have entries added or removed at runtime.
+In other languages tables might be called Hashmaps, Maps or Objects.
+Entries are added with a key that can subsequently be used to read that object from the table back.
+The type keyword is table
.
To index an array with a string you can write t.index
, or with an expression just like in arrays with t.["index"]
.
table t = { val = "value" }
+ string v = t.val
+ string v2 = t["val"]
+
Table literals are comma or newline seperated expressions that are delimited by {
and }
.
Each entry needs to have a key, seperated from the initial value with a =
.
Table keys will be by default strings if you just write their identifier in the literal. However they can also be any expression if wrapped with [
and ]
.
table t = { key1 = 1, key2 = "2" }
+ table t2 = {
+ randomValue = getSomethingRandom()
+ [1] = 0x1
+ }
+
+ printt( t["key1"], t2[1] ) // 1 1
+
Like arrays primitive tables can hold any type, both as values and keys.
+Any value of key of the table will therefore be var
if retrieved.
Complex tables are tables that have their content types defined. It is necessary to both define the key and value types.
+ table<string, int> numbers = {
+ one = 1,
+ two = 2,
+ three = 3,
+ four = 4,
+ five = 5,
+ six = 6,
+ seven = 7,
+ eight = 8,
+ nine = 9
+ }
+
var
stands for a variable of any type. Any primitive can be var
, however complex types can never be var
.
// var can be just about anything.
+ var v = 1
+ v = "string"
+ v = []
+ v = {}
+
in untyped
files you can also use the local
keyword instead of var
. However the keyword is deprecated and should not be used.
If possible, usage of var
should be avoided and other static types should be used instead to benefit from the type checking of squirrel.
Vectors are a primitive data type to describe velocities or positions of objects in the game.
+Usually the positions are absolute in the game world, but that may depend on the function.
+Vectors store 3 float values that can be accessed with the x
, y
and z
keys.
A vector literal is a comma seperated list of expressions that evaluate to either a float or integer delimited by <
and >
brackets.
vector v = < 1, 2.5, 3 >
+ v.y = 2
+ printt( v.x, v.y, v.z ) // 1 2 3
+
{"use strict";Ys.formatArgs=a2e;Ys.save=s2e;Ys.load=o2e;Ys.useColors=i2e;Ys.storage=l2e();Ys.destroy=(()=>{let t=!1;return()=>{t||(t=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})();Ys.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function i2e(){if(typeof window<"u"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs))return!0;if(typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;let t;return typeof document<"u"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window<"u"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator<"u"&&navigator.userAgent&&(t=navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/))&&parseInt(t[1],10)>=31||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}o(i2e,"useColors");function a2e(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+Bb.exports.humanize(this.diff),!this.useColors)return;let e="color: "+this.color;t.splice(1,0,e,"color: inherit");let r=0,n=0;t[0].replace(/%[a-zA-Z%]/g,i=>{i!=="%%"&&(r++,i==="%c"&&(n=r))}),t.splice(n,0,e)}o(a2e,"formatArgs");Ys.log=console.debug||console.log||(()=>{});function s2e(t){try{t?Ys.storage.setItem("debug",t):Ys.storage.removeItem("debug")}catch{}}o(s2e,"save");function o2e(){let t;try{t=Ys.storage.getItem("debug")}catch{}return!t&&typeof process<"u"&&"env"in process&&(t=process.env.DEBUG),t}o(o2e,"load");function l2e(){try{return localStorage}catch{}}o(l2e,"localstorage");Bb.exports=KF()(Ys);var{formatters:c2e}=Bb.exports;c2e.j=function(t){try{return JSON.stringify(t)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}});var uit,ZF=R(()=>{"use strict";MF();GF();HF();YF();WF();uit=Xi(QF(),1)});var FC,BC,JF,Fb,u2e,zb,V1=R(()=>{"use strict";ut();ZF();FC={body:' Qi))l.moveTo(0,0);else if(v>Gp-Qi)l.moveTo(m*Dh(g),m*xl(g)),l.arc(0,0,m,g,y,!x),p>Qi&&(l.moveTo(p*Dh(y),p*xl(y)),l.arc(0,0,p,y,g,x));else{var b=g,w=y,S=g,T=y,E=v,_=v,A=s.apply(this,arguments)/2,L=A>Qi&&(n?+n.apply(this,arguments):gd(p*p+m*m)),M=R3(J8(m-p)/2,+r.apply(this,arguments)),N=M,k=M,I,C;if(L>Qi){var O=e_(L/p*xl(A)),D=e_(L/m*xl(A));(E-=O*2)>Qi?(O*=x?1:-1,S+=O,T-=O):(E=0,S=T=(g+y)/2),(_-=D*2)>Qi?(D*=x?1:-1,b+=D,w-=D):(_=0,b=w=(g+y)/2)}var P=m*Dh(b),F=m*xl(b),B=p*Dh(T),$=p*xl(T);if(M>Qi){var z=m*Dh(w),Y=m*xl(w),Q=p*Dh(S),X=p*xl(S),ie;if(v ${this.parser.parseInline(e)} An error occurred: ${i.tokens?.map(n).join("")}
/gi,Cbe=o(t=>t?_$(t).replace(/\\n/g,"#br#").split("#br#"):[""],"getRows"),Sbe=(()=>{let t=!1;return()=>{t||(Abe(),t=!0)}})();o(Abe,"setupDompurifyHooks");A$=o(t=>(Sbe(),bp.default.sanitize(t)),"removeScript"),C$=o((t,e)=>{if(e.flowchart?.htmlLabels!==!1){let r=e.securityLevel;r==="antiscript"||r==="strict"?t=A$(t):r!=="loose"&&(t=_$(t),t=t.replace(/
"),"placeholderToBreak"),_$=o(t=>t.replace(Qf,"#br#"),"breakToPlaceholder"),Nbe=o(t=>{let e="";return t&&(e=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,e=e.replaceAll(/\(/g,"\\("),e=e.replaceAll(/\)/g,"\\)")),e},"getUrl"),yr=o(t=>!(t===!1||["false","null","0"].includes(String(t).trim().toLowerCase())),"evaluate"),Mbe=o(function(...t){let e=t.filter(r=>!isNaN(r));return Math.max(...e)},"getMax"),Ibe=o(function(...t){let e=t.filter(r=>!isNaN(r));return Math.min(...e)},"getMin"),gh=o(function(t){let e=t.split(/(,)/),r=[];for(let n=0;n0)for(var r=new Array(i),n=0,i,a;n{"use strict";J$()});var z4,wS,TS=R(()=>{"use strict";z4="http://www.w3.org/1999/xhtml",wS={svg:"http://www.w3.org/2000/svg",xhtml:z4,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"}});function ic(t){var e=t+="",r=e.indexOf(":");return r>=0&&(e=t.slice(0,r))!=="xmlns"&&(t=t.slice(r+1)),wS.hasOwnProperty(e)?{space:wS[e],local:t}:t}var G4=R(()=>{"use strict";TS();o(ic,"default")});function I4e(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===z4&&e.documentElement.namespaceURI===z4?e.createElement(t):e.createElementNS(r,t)}}function O4e(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function dy(t){var e=ic(t);return(e.local?O4e:I4e)(e)}var kS=R(()=>{"use strict";G4();TS();o(I4e,"creatorInherit");o(O4e,"creatorFixed");o(dy,"default")});function P4e(){}function wh(t){return t==null?P4e:function(){return this.querySelector(t)}}var $4=R(()=>{"use strict";o(P4e,"none");o(wh,"default")});function ES(t){typeof t!="function"&&(t=wh(t));for(var e=this._groups,r=e.length,n=new Array(r),i=0;i{"use strict";hl();o(FS,"default")});function zS(){for(var t=this._groups,e=-1,r=t.length;++e0;){if(h=fy(s,l,r),h===u)return n[i]=s,n[a]=l,e(n);if(h>0)s=Math.floor(s/h)*h,l=Math.ceil(l/h)*h;else if(h<0)s=Math.ceil(s*h)/h,l=Math.floor(l*h)/h;else break;u=h}return t},t}function gl(){var t=By();return t.copy=function(){return x3(t,gl())},Ah.apply(t,arguments),U5e(t)}var XH=R(()=>{"use strict";bh();R8();Py();qH();o(U5e,"linearish");o(gl,"linear")});function M8(t,e){t=t.slice();var r=0,n=t.length-1,i=t[r],a=t[n],s;return a{"use strict";o(M8,"nice")});function dn(t,e,r,n){function i(a){return t(a=arguments.length===0?new Date:new Date(+a)),a}return o(i,"interval"),i.floor=a=>(t(a=new Date(+a)),a),i.ceil=a=>(t(a=new Date(a-1)),e(a,1),t(a),a),i.round=a=>{let s=i(a),l=i.ceil(a);return a-s0))return u;let h;do u.push(h=new Date(+a)),e(a,l),t(a);while(hdn(s=>{if(s>=s)for(;t(s),!a(s);)s.setTime(s-1)},(s,l)=>{if(s>=s)if(l<0)for(;++l<=0;)for(;e(s,-1),!a(s););else for(;--l>=0;)for(;e(s,1),!a(s););}),r&&(i.count=(a,s)=>(I8.setTime(+a),O8.setTime(+s),t(I8),t(O8),Math.floor(r(I8,O8))),i.every=a=>(a=Math.floor(a),!isFinite(a)||!(a>0)?null:a>1?i.filter(n?s=>n(s)%a===0:s=>i.count(0,s)%a===0):i)),i}var I8,O8,mu=R(()=>{"use strict";I8=new Date,O8=new Date;o(dn,"timeInterval")});var oc,KH,P8=R(()=>{"use strict";mu();oc=dn(()=>{},(t,e)=>{t.setTime(+t+e)},(t,e)=>e-t);oc.every=t=>(t=Math.floor(t),!isFinite(t)||!(t>0)?null:t>1?dn(e=>{e.setTime(Math.floor(e/t)*t)},(e,r)=>{e.setTime(+e+r*t)},(e,r)=>(r-e)/t):oc);KH=oc.range});var Ks,QH,B8=R(()=>{"use strict";mu();Ks=dn(t=>{t.setTime(t-t.getMilliseconds())},(t,e)=>{t.setTime(+t+e*1e3)},(t,e)=>(e-t)/1e3,t=>t.getUTCSeconds()),QH=Ks.range});var gu,H5e,b3,Y5e,F8=R(()=>{"use strict";mu();gu=dn(t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*1e3)},(t,e)=>{t.setTime(+t+e*6e4)},(t,e)=>(e-t)/6e4,t=>t.getMinutes()),H5e=gu.range,b3=dn(t=>{t.setUTCSeconds(0,0)},(t,e)=>{t.setTime(+t+e*6e4)},(t,e)=>(e-t)/6e4,t=>t.getUTCMinutes()),Y5e=b3.range});var yu,W5e,w3,q5e,z8=R(()=>{"use strict";mu();yu=dn(t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*1e3-t.getMinutes()*6e4)},(t,e)=>{t.setTime(+t+e*36e5)},(t,e)=>(e-t)/36e5,t=>t.getHours()),W5e=yu.range,w3=dn(t=>{t.setUTCMinutes(0,0,0)},(t,e)=>{t.setTime(+t+e*36e5)},(t,e)=>(e-t)/36e5,t=>t.getUTCHours()),q5e=w3.range});var Do,X5e,zy,j5e,T3,K5e,G8=R(()=>{"use strict";mu();Do=dn(t=>t.setHours(0,0,0,0),(t,e)=>t.setDate(t.getDate()+e),(t,e)=>(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/864e5,t=>t.getDate()-1),X5e=Do.range,zy=dn(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/864e5,t=>t.getUTCDate()-1),j5e=zy.range,T3=dn(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/864e5,t=>Math.floor(t/864e5)),K5e=T3.range});function fd(t){return dn(e=>{e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)},(e,r)=>{e.setDate(e.getDate()+r*7)},(e,r)=>(r-e-(r.getTimezoneOffset()-e.getTimezoneOffset())*6e4)/6048e5)}function dd(t){return dn(e=>{e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)},(e,r)=>{e.setUTCDate(e.getUTCDate()+r*7)},(e,r)=>(r-e)/6048e5)}var yl,_h,k3,E3,cc,C3,S3,JH,Q5e,Z5e,J5e,ewe,twe,rwe,pd,Bp,eY,tY,Lh,rY,nY,iY,nwe,iwe,awe,swe,owe,lwe,$8=R(()=>{"use strict";mu();o(fd,"timeWeekday");yl=fd(0),_h=fd(1),k3=fd(2),E3=fd(3),cc=fd(4),C3=fd(5),S3=fd(6),JH=yl.range,Q5e=_h.range,Z5e=k3.range,J5e=E3.range,ewe=cc.range,twe=C3.range,rwe=S3.range;o(dd,"utcWeekday");pd=dd(0),Bp=dd(1),eY=dd(2),tY=dd(3),Lh=dd(4),rY=dd(5),nY=dd(6),iY=pd.range,nwe=Bp.range,iwe=eY.range,awe=tY.range,swe=Lh.range,owe=rY.range,lwe=nY.range});var vu,cwe,A3,uwe,V8=R(()=>{"use strict";mu();vu=dn(t=>{t.setDate(1),t.setHours(0,0,0,0)},(t,e)=>{t.setMonth(t.getMonth()+e)},(t,e)=>e.getMonth()-t.getMonth()+(e.getFullYear()-t.getFullYear())*12,t=>t.getMonth()),cwe=vu.range,A3=dn(t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCMonth(t.getUTCMonth()+e)},(t,e)=>e.getUTCMonth()-t.getUTCMonth()+(e.getUTCFullYear()-t.getUTCFullYear())*12,t=>t.getUTCMonth()),uwe=A3.range});var Qs,hwe,vl,fwe,U8=R(()=>{"use strict";mu();Qs=dn(t=>{t.setMonth(0,1),t.setHours(0,0,0,0)},(t,e)=>{t.setFullYear(t.getFullYear()+e)},(t,e)=>e.getFullYear()-t.getFullYear(),t=>t.getFullYear());Qs.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:dn(e=>{e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)},(e,r)=>{e.setFullYear(e.getFullYear()+r*t)});hwe=Qs.range,vl=dn(t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCFullYear(t.getUTCFullYear()+e)},(t,e)=>e.getUTCFullYear()-t.getUTCFullYear(),t=>t.getUTCFullYear());vl.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:dn(e=>{e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,r)=>{e.setUTCFullYear(e.getUTCFullYear()+r*t)});fwe=vl.range});function sY(t,e,r,n,i,a){let s=[[Ks,1,1e3],[Ks,5,5*1e3],[Ks,15,15*1e3],[Ks,30,30*1e3],[a,1,6e4],[a,5,5*6e4],[a,15,15*6e4],[a,30,30*6e4],[i,1,36e5],[i,3,3*36e5],[i,6,6*36e5],[i,12,12*36e5],[n,1,864e5],[n,2,2*864e5],[r,1,6048e5],[e,1,2592e6],[e,3,3*2592e6],[t,1,31536e6]];function l(h,f,d){let p=f{"use strict";bh();P8();B8();F8();z8();G8();$8();V8();U8();o(sY,"ticker");[pwe,mwe]=sY(vl,A3,pd,T3,w3,b3),[H8,Y8]=sY(Qs,vu,yl,Do,yu,gu)});var _3=R(()=>{"use strict";P8();B8();F8();z8();G8();$8();V8();U8();oY()});function W8(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function q8(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function Gy(t,e,r){return{y:t,m:e,d:r,H:0,M:0,S:0,L:0}}function X8(t){var e=t.dateTime,r=t.date,n=t.time,i=t.periods,a=t.days,s=t.shortDays,l=t.months,u=t.shortMonths,h=$y(i),f=Vy(i),d=$y(a),p=Vy(a),m=$y(s),g=Vy(s),y=$y(l),v=Vy(l),x=$y(u),b=Vy(u),w={a:P,A:F,b:B,B:$,c:null,d:dY,e:dY,f:Fwe,g:Xwe,G:Kwe,H:Owe,I:Pwe,j:Bwe,L:vY,m:zwe,M:Gwe,p:z,q:Y,Q:gY,s:yY,S:$we,u:Vwe,U:Uwe,V:Hwe,w:Ywe,W:Wwe,x:null,X:null,y:qwe,Y:jwe,Z:Qwe,"%":mY},S={a:Q,A:X,b:ie,B:j,c:null,d:pY,e:pY,f:tTe,g:hTe,G:dTe,H:Zwe,I:Jwe,j:eTe,L:bY,m:rTe,M:nTe,p:J,q:Z,Q:gY,s:yY,S:iTe,u:aTe,U:sTe,V:oTe,w:lTe,W:cTe,x:null,X:null,y:uTe,Y:fTe,Z:pTe,"%":mY},T={a:M,A:N,b:k,B:I,c:C,d:hY,e:hY,f:Rwe,g:uY,G:cY,H:fY,I:fY,j:Awe,L:Dwe,m:Swe,M:_we,p:L,q:Cwe,Q:Mwe,s:Iwe,S:Lwe,u:bwe,U:wwe,V:Twe,w:xwe,W:kwe,x:O,X:D,y:uY,Y:cY,Z:Ewe,"%":Nwe};w.x=E(r,w),w.X=E(n,w),w.c=E(e,w),S.x=E(r,S),S.X=E(n,S),S.c=E(e,S);function E(H,q){return function(K){var se=[],ce=-1,ue=0,te=H.length,De,oe,ke;for(K instanceof Date||(K=new Date(+K));++ce{"use strict";am();Kp();o(v6e,"copyObject");Bo=v6e});function x6e(t,e){for(var r=-1,n=Array(t);++r
"},r),We.lineBreakRegex.test(t)))return t;let n=t.split(" ").filter(Boolean),i=[],a="";return n.forEach((s,l)=>{let u=Cl(`${s} `,r),h=Cl(a,r);if(u>e){let{hyphenatedStrings:p,remainingWord:m}=sCe(s,e,"-",r);i.push(a,...p),a=m}else h+u>=e?(i.push(a),a=s):a=[a,s].filter(Boolean).join(" ");l+1===n.length&&i.push(a)}),i.filter(s=>s!=="").join(r.joinWith)},(t,e,r)=>`${t}${e}${r.fontSize}${r.fontWeight}${r.fontFamily}${r.joinWith}`),sCe=qp((t,e,r="-",n)=>{n=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:0},n);let i=[...t],a=[],s="";return i.forEach((l,u)=>{let h=`${s}${l}`;if(Cl(h,n)>=e){let d=u+1,p=i.length===d,m=`${h}${r}`;a.push(p?h:m),s=""}else s=h}),{hyphenatedStrings:a,remainingWord:s}},(t,e,r="-",n)=>`${t}${e}${r}${n.fontSize}${n.fontWeight}${n.fontFamily}`);o(g5,"calculateTextHeight");o(Cl,"calculateTextWidth");t9=qp((t,e)=>{let{fontSize:r=12,fontFamily:n="Arial",fontWeight:i=400}=e;if(!t)return{width:0,height:0};let[,a]=mc(r),s=["sans-serif",n],l=t.split(We.lineBreakRegex),u=[],h=$e("body");if(!h.remove)return{width:0,height:0,lineHeight:0};let f=h.append("svg");for(let p of s){let m=0,g={width:0,height:0,lineHeight:0};for(let y of l){let v=iCe();v.text=y||K_;let x=aCe(f,v).style("font-size",a).style("font-weight",i).style("font-family",p),b=(x._groups||x)[0][0].getBBox();if(b.width===0&&b.height===0)throw new Error("svg element not in render tree");g.width=Math.round(Math.max(g.width,b.width)),m=Math.round(b.height),g.height+=m,g.lineHeight=Math.round(Math.max(g.lineHeight,m))}u.push(g)}f.remove();let d=isNaN(u[1].height)||isNaN(u[1].width)||isNaN(u[1].lineHeight)||u[0].height>u[1].height&&u[0].width>u[1].width&&u[0].lineHeight>u[1].lineHeight?0:1;return u[d]},(t,e)=>`${t}${e.fontSize}${e.fontWeight}${e.fontFamily}`),j_=class{constructor(e=!1,r){this.count=0;this.count=r?r.length:0,this.next=e?()=>this.count++:()=>Date.now()}static{o(this,"InitIDGenerator")}},oCe=o(function(t){return m5=m5||document.createElement("div"),t=escape(t).replace(/%26/g,"&").replace(/%23/g,"#").replace(/%3B/g,";"),m5.innerHTML=t,unescape(m5.textContent)},"entityDecode");o(r9,"isDetailedError");lCe=o((t,e,r,n)=>{if(!n)return;let i=t.node()?.getBBox();i&&t.append("text").text(n).attr("x",i.x+i.width/2).attr("y",-r).attr("class",e)},"insertTitle"),mc=o(t=>{if(typeof t=="number")return[t,t+"px"];let e=parseInt(t??"",10);return Number.isNaN(e)?[void 0,void 0]:t===String(e)?[e,t+"px"]:[e,t]},"parseFontSize");o(Ts,"cleanAndMerge");Lt={assignWithDepth:On,wrapLabel:e9,calculateTextHeight:g5,calculateTextWidth:Cl,calculateTextDimensions:t9,cleanAndMerge:Ts,detectInit:j6e,detectDirective:kX,isSubstringInArray:K6e,interpolateToCurve:om,calcLabelPosition:eCe,calcCardinalityPosition:tCe,calcTerminalLabelPosition:rCe,formatUrl:Q6e,getStylesFromArray:lm,generateId:Z_,random:J_,runFunc:Z6e,entityDecode:oCe,insertTitle:lCe,parseFontSize:mc,InitIDGenerator:j_},SX=o(function(t){let e=t;return e=e.replace(/style.*:\S*#.*;/g,function(r){return r.substring(0,r.length-1)}),e=e.replace(/classDef.*:\S*#.*;/g,function(r){return r.substring(0,r.length-1)}),e=e.replace(/#\w+;/g,function(r){let n=r.substring(1,r.length-1);return/^\+?\d+$/.test(n)?"\uFB02\xB0\xB0"+n+"\xB6\xDF":"\uFB02\xB0"+n+"\xB6\xDF"}),e},"encodeEntities"),to=o(function(t){return t.replace(/fl°°/g,"").replace(/fl°/g,"&").replace(/¶ß/g,";")},"decodeEntities"),y5=o((t,e,{counter:r=0,prefix:n,suffix:i})=>`${n?`${n}_`:""}${t}_${e}_${r}${i?`_${i}`:""}`,"getEdgeId")});function Sl(t,e,r,n,i){if(!e[t].width)if(r)e[t].text=e9(e[t].text,i,n),e[t].textLines=e[t].text.split(We.lineBreakRegex).length,e[t].width=i,e[t].height=g5(e[t].text,n);else{let a=e[t].text.split(We.lineBreakRegex);e[t].textLines=a.length;let s=0;e[t].height=0,e[t].width=0;for(let l of a)e[t].width=Math.max(Cl(l,n),e[t].width),s=g5(l,n),e[t].height=e[t].height+s}}function RX(t,e,r,n,i){let a=new w5(i);a.data.widthLimit=r.data.widthLimit/Math.min(n9,n.length);for(let[s,l]of n.entries()){let u=0;l.image={width:0,height:0,Y:0},l.sprite&&(l.image.width=48,l.image.height=48,l.image.Y=u,u=l.image.Y+l.image.height);let h=l.wrap&&Nt.wrap,f=v5(Nt);if(f.fontSize=f.fontSize+2,f.fontWeight="bold",Sl("label",l,h,f,a.data.widthLimit),l.label.Y=u+8,u=l.label.Y+l.label.height,l.type&&l.type.text!==""){l.type.text="["+l.type.text+"]";let g=v5(Nt);Sl("type",l,h,g,a.data.widthLimit),l.type.Y=u+5,u=l.type.Y+l.type.height}if(l.descr&&l.descr.text!==""){let g=v5(Nt);g.fontSize=g.fontSize-2,Sl("descr",l,h,g,a.data.widthLimit),l.descr.Y=u+20,u=l.descr.Y+l.descr.height}if(s==0||s%n9===0){let g=r.data.startx+Nt.diagramMarginX,y=r.data.stopy+Nt.diagramMarginY+u;a.setData(g,g,y,y)}else{let g=a.data.stopx!==a.data.startx?a.data.stopx+Nt.diagramMarginX:a.data.startx,y=a.data.starty;a.setData(g,g,y,y)}a.name=l.alias;let d=i.db.getC4ShapeArray(l.alias),p=i.db.getC4ShapeKeys(l.alias);p.length>0&&DX(a,t,d,p),e=l.alias;let m=i.db.getBoundarys(e);m.length>0&&RX(t,e,a,m,i),l.alias!=="global"&&LX(t,l,a),r.data.stopy=Math.max(a.data.stopy+Nt.c4ShapeMargin,r.data.stopy),r.data.stopx=Math.max(a.data.stopx+Nt.c4ShapeMargin,r.data.stopx),x5=Math.max(x5,r.data.stopx),b5=Math.max(b5,r.data.stopy)}}var x5,b5,_X,n9,Nt,w5,i9,hv,v5,cCe,LX,DX,ks,AX,uCe,hCe,fCe,a9,NX=R(()=>{"use strict";Zt();AW();ut();VC();rr();lS();_t();cp();xr();Yn();x5=0,b5=0,_X=4,n9=2;U1.yy=hy;Nt={},w5=class{static{o(this,"Bounds")}constructor(e){this.name="",this.data={},this.data.startx=void 0,this.data.stopx=void 0,this.data.starty=void 0,this.data.stopy=void 0,this.data.widthLimit=void 0,this.nextData={},this.nextData.startx=void 0,this.nextData.stopx=void 0,this.nextData.starty=void 0,this.nextData.stopy=void 0,this.nextData.cnt=0,i9(e.db.getConfig())}setData(e,r,n,i){this.nextData.startx=this.data.startx=e,this.nextData.stopx=this.data.stopx=r,this.nextData.starty=this.data.starty=n,this.nextData.stopy=this.data.stopy=i}updateVal(e,r,n,i){e[r]===void 0?e[r]=n:e[r]=i(n,e[r])}insert(e){this.nextData.cnt=this.nextData.cnt+1;let r=this.nextData.startx===this.nextData.stopx?this.nextData.stopx+e.margin:this.nextData.stopx+e.margin*2,n=r+e.width,i=this.nextData.starty+e.margin*2,a=i+e.height;(r>=this.data.widthLimit||n>=this.data.widthLimit||this.nextData.cnt>_X)&&(r=this.nextData.startx+e.margin+Nt.nextLinePaddingX,i=this.nextData.stopy+e.margin*2,this.nextData.stopx=n=r+e.width,this.nextData.starty=this.nextData.stopy,this.nextData.stopy=a=i+e.height,this.nextData.cnt=1),e.x=r,e.y=i,this.updateVal(this.data,"startx",r,Math.min),this.updateVal(this.data,"starty",i,Math.min),this.updateVal(this.data,"stopx",n,Math.max),this.updateVal(this.data,"stopy",a,Math.max),this.updateVal(this.nextData,"startx",r,Math.min),this.updateVal(this.nextData,"starty",i,Math.min),this.updateVal(this.nextData,"stopx",n,Math.max),this.updateVal(this.nextData,"stopy",a,Math.max)}init(e){this.name="",this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0,widthLimit:void 0},this.nextData={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0,cnt:0},i9(e.db.getConfig())}bumpLastMargin(e){this.data.stopx+=e,this.data.stopy+=e}},i9=o(function(t){On(Nt,t),t.fontFamily&&(Nt.personFontFamily=Nt.systemFontFamily=Nt.messageFontFamily=t.fontFamily),t.fontSize&&(Nt.personFontSize=Nt.systemFontSize=Nt.messageFontSize=t.fontSize),t.fontWeight&&(Nt.personFontWeight=Nt.systemFontWeight=Nt.messageFontWeight=t.fontWeight)},"setConf"),hv=o((t,e)=>({fontFamily:t[e+"FontFamily"],fontSize:t[e+"FontSize"],fontWeight:t[e+"FontWeight"]}),"c4ShapeFont"),v5=o(t=>({fontFamily:t.boundaryFontFamily,fontSize:t.boundaryFontSize,fontWeight:t.boundaryFontWeight}),"boundaryFont"),cCe=o(t=>({fontFamily:t.messageFontFamily,fontSize:t.messageFontSize,fontWeight:t.messageFontWeight}),"messageFont");o(Sl,"calcC4ShapeTextWH");LX=o(function(t,e,r){e.x=r.data.startx,e.y=r.data.starty,e.width=r.data.stopx-r.data.startx,e.height=r.data.stopy-r.data.starty,e.label.y=Nt.c4ShapeMargin-35;let n=e.wrap&&Nt.wrap,i=v5(Nt);i.fontSize=i.fontSize+2,i.fontWeight="bold";let a=Cl(e.label.text,i);Sl("label",e,n,i,a),Tl.drawBoundary(t,e,Nt)},"drawBoundary"),DX=o(function(t,e,r,n){let i=0;for(let a of n){i=0;let s=r[a],l=hv(Nt,s.typeC4Shape.text);switch(l.fontSize=l.fontSize-2,s.typeC4Shape.width=Cl("\xAB"+s.typeC4Shape.text+"\xBB",l),s.typeC4Shape.height=l.fontSize+2,s.typeC4Shape.Y=Nt.c4ShapePadding,i=s.typeC4Shape.Y+s.typeC4Shape.height-4,s.image={width:0,height:0,Y:0},s.typeC4Shape.text){case"person":case"external_person":s.image.width=48,s.image.height=48,s.image.Y=i,i=s.image.Y+s.image.height;break}s.sprite&&(s.image.width=48,s.image.height=48,s.image.Y=i,i=s.image.Y+s.image.height);let u=s.wrap&&Nt.wrap,h=Nt.width-Nt.c4ShapePadding*2,f=hv(Nt,s.typeC4Shape.text);if(f.fontSize=f.fontSize+2,f.fontWeight="bold",Sl("label",s,u,f,h),s.label.Y=i+8,i=s.label.Y+s.label.height,s.type&&s.type.text!==""){s.type.text="["+s.type.text+"]";let m=hv(Nt,s.typeC4Shape.text);Sl("type",s,u,m,h),s.type.Y=i+5,i=s.type.Y+s.type.height}else if(s.techn&&s.techn.text!==""){s.techn.text="["+s.techn.text+"]";let m=hv(Nt,s.techn.text);Sl("techn",s,u,m,h),s.techn.Y=i+5,i=s.techn.Y+s.techn.height}let d=i,p=s.label.width;if(s.descr&&s.descr.text!==""){let m=hv(Nt,s.typeC4Shape.text);Sl("descr",s,u,m,h),s.descr.Y=i+20,i=s.descr.Y+s.descr.height,p=Math.max(s.label.width,s.descr.width),d=i-s.descr.textLines*5}p=p+Nt.c4ShapePadding,s.width=Math.max(s.width||Nt.width,p,Nt.width),s.height=Math.max(s.height||Nt.height,d,Nt.height),s.margin=s.margin||Nt.c4ShapeMargin,t.insert(s),Tl.drawC4Shape(e,s,Nt)}t.bumpLastMargin(Nt.c4ShapeMargin)},"drawC4ShapeArray"),ks=class{static{o(this,"Point")}constructor(e,r){this.x=e,this.y=r}},AX=o(function(t,e){let r=t.x,n=t.y,i=e.x,a=e.y,s=r+t.width/2,l=n+t.height/2,u=Math.abs(r-i),h=Math.abs(n-a),f=h/u,d=t.height/t.width,p=null;return n==a&&ri?p=new ks(r,l):r==i&&na&&(p=new ks(s,n)),r>i&&n=f?p=new ks(r,l+f*t.width/2):p=new ks(s-u/h*t.height/2,n+t.height):r=f?p=new ks(r+t.width,l+f*t.width/2):p=new ks(s+u/h*t.height/2,n+t.height):ra?d>=f?p=new ks(r+t.width,l-f*t.width/2):p=new ks(s+t.height/2*u/h,n):r>i&&n>a&&(d>=f?p=new ks(r,l-t.width/2*f):p=new ks(s-t.height/2*u/h,n)),p},"getIntersectPoint"),uCe=o(function(t,e){let r={x:0,y:0};r.x=e.x+e.width/2,r.y=e.y+e.height/2;let n=AX(t,r);r.x=t.x+t.width/2,r.y=t.y+t.height/2;let i=AX(e,r);return{startPoint:n,endPoint:i}},"getIntersectPoints"),hCe=o(function(t,e,r,n){let i=0;for(let a of e){i=i+1;let s=a.wrap&&Nt.wrap,l=cCe(Nt);n.db.getC4Type()==="C4Dynamic"&&(a.label.text=i+": "+a.label.text);let h=Cl(a.label.text,l);Sl("label",a,s,l,h),a.techn&&a.techn.text!==""&&(h=Cl(a.techn.text,l),Sl("techn",a,s,l,h)),a.descr&&a.descr.text!==""&&(h=Cl(a.descr.text,l),Sl("descr",a,s,l,h));let f=r(a.from),d=r(a.to),p=uCe(f,d);a.startPoint=p.startPoint,a.endPoint=p.endPoint}Tl.drawRels(t,e,Nt)},"drawRels");o(RX,"drawInsideBoundary");fCe=o(function(t,e,r,n){Nt=de().c4;let i=de().securityLevel,a;i==="sandbox"&&(a=$e("#i"+e));let s=i==="sandbox"?$e(a.nodes()[0].contentDocument.body):$e("body"),l=n.db;n.db.setWrap(Nt.wrap),_X=l.getC4ShapeInRow(),n9=l.getC4BoundaryInRow(),V.debug(`C:${JSON.stringify(Nt,null,2)}`);let u=i==="sandbox"?s.select(`[id="${e}"]`):$e(`[id="${e}"]`);Tl.insertComputerIcon(u),Tl.insertDatabaseIcon(u),Tl.insertClockIcon(u);let h=new w5(n);h.setData(Nt.diagramMarginX,Nt.diagramMarginX,Nt.diagramMarginY,Nt.diagramMarginY),h.data.widthLimit=screen.availWidth,x5=Nt.diagramMarginX,b5=Nt.diagramMarginY;let f=n.db.getTitle(),d=n.db.getBoundarys("");RX(u,"",h,d,n),Tl.insertArrowHead(u),Tl.insertArrowEnd(u),Tl.insertArrowCrossHead(u),Tl.insertArrowFilledHead(u),hCe(u,n.db.getRels(),n.db.getC4Shape,n),h.data.stopx=x5,h.data.stopy=b5;let p=h.data,g=p.stopy-p.starty+2*Nt.diagramMarginY,v=p.stopx-p.startx+2*Nt.diagramMarginX;f&&u.append("text").text(f).attr("x",(p.stopx-p.startx)/2-4*Nt.diagramMarginX).attr("y",p.starty+Nt.diagramMarginY),Sr(u,g,v,Nt.useMaxWidth);let x=f?60:0;u.attr("viewBox",p.startx-Nt.diagramMarginX+" -"+(Nt.diagramMarginY+x)+" "+v+" "+(g+x)),V.debug("models:",p)},"draw"),a9={drawPersonOrSystemArray:DX,drawBoundary:LX,setConf:i9,draw:fCe}});var dCe,MX,IX=R(()=>{"use strict";dCe=o(t=>`.person {
+ stroke: ${t.personBorder};
+ fill: ${t.personBkg};
+ }
+`,"getStyles"),MX=dCe});var OX={};hr(OX,{diagram:()=>pCe});var pCe,PX=R(()=>{"use strict";VC();lS();NX();IX();pCe={parser:rz,db:hy,renderer:a9,styles:MX,init:o(({c4:t,wrap:e})=>{a9.setConf(t),hy.setWrap(e)},"init")}});function o9(t){let e=[];for(let r of t){let n=dv.get(r);n?.styles&&(e=[...e,...n.styles??[]].map(i=>i.trim())),n?.textStyles&&(e=[...e,...n.textStyles??[]].map(i=>i.trim()))}return e}var vCe,zX,cm,$h,Es,dv,Cu,l9,c9,T5,s9,Fo,k5,E5,C5,S5,xCe,bCe,wCe,TCe,kCe,ECe,CCe,u9,SCe,ACe,_Ce,GX,LCe,DCe,h9,$X,VX,RCe,UX,NCe,MCe,ICe,OCe,PCe,fv,HX,YX,BCe,FCe,WX,zCe,GCe,$Ce,VCe,UCe,qX,XX,HCe,YCe,WCe,qCe,XCe,jCe,A5,f9=R(()=>{"use strict";Zt();xr();_t();rr();ut();bi();vCe="flowchart-",zX=0,cm=de(),$h=new Map,Es=[],dv=new Map,Cu=[],l9=new Map,c9=new Map,T5=0,s9=!0,E5=[],C5=o(t=>We.sanitizeText(t,cm),"sanitizeText"),S5=o(function(t){for(let e of $h.values())if(e.id===t)return e.domId;return t},"lookUpDomId"),xCe=o(function(t,e,r,n,i,a,s={}){if(!t||t.trim().length===0)return;let l,u=$h.get(t);u===void 0&&(u={id:t,labelType:"text",domId:vCe+t+"-"+zX,styles:[],classes:[]},$h.set(t,u)),zX++,e!==void 0?(cm=de(),l=C5(e.text.trim()),u.labelType=e.type,l.startsWith('"')&&l.endsWith('"')&&(l=l.substring(1,l.length-1)),u.text=l):u.text===void 0&&(u.text=t),r!==void 0&&(u.type=r),n?.forEach(function(h){u.styles.push(h)}),i?.forEach(function(h){u.classes.push(h)}),a!==void 0&&(u.dir=a),u.props===void 0?u.props=s:s!==void 0&&Object.assign(u.props,s)},"addVertex"),bCe=o(function(t,e,r){let a={start:t,end:e,type:void 0,text:"",labelType:"text"};V.info("abc78 Got edge...",a);let s=r.text;if(s!==void 0&&(a.text=C5(s.text.trim()),a.text.startsWith('"')&&a.text.endsWith('"')&&(a.text=a.text.substring(1,a.text.length-1)),a.labelType=s.type),r!==void 0&&(a.type=r.type,a.stroke=r.stroke,a.length=r.length>10?10:r.length),Es.length<(cm.maxEdges??500))V.info("Pushing edge..."),Es.push(a);else throw new Error(`Edge limit exceeded. ${Es.length} edges found, but the limit is ${cm.maxEdges}.
+
+Initialize mermaid with maxEdges set to a higher number to allow more edges.
+You cannot set this config via configuration inside the diagram as it is a secure config.
+You have to call mermaid.initialize.`)},"addSingleLink"),wCe=o(function(t,e,r){V.info("addLink",t,e,r);for(let n of t)for(let i of e)bCe(n,i,r)},"addLink"),TCe=o(function(t,e){t.forEach(function(r){r==="default"?Es.defaultInterpolate=e:Es[r].interpolate=e})},"updateLinkInterpolate"),kCe=o(function(t,e){t.forEach(function(r){if(typeof r=="number"&&r>=Es.length)throw new Error(`The index ${r} for linkStyle is out of bounds. Valid indices for linkStyle are between 0 and ${Es.length-1}. (Help: Ensure that the index is within the range of existing edges.)`);r==="default"?Es.defaultStyle=e:(Es[r].style=e,(Es[r]?.style?.length??0)>0&&!Es[r]?.style?.some(n=>n?.startsWith("fill"))&&Es[r]?.style?.push("fill:none"))})},"updateLink"),ECe=o(function(t,e){t.split(",").forEach(function(r){let n=dv.get(r);n===void 0&&(n={id:r,styles:[],textStyles:[]},dv.set(r,n)),e?.forEach(function(i){if(/color/.exec(i)){let a=i.replace("fill","bgFill");n.textStyles.push(a)}n.styles.push(i)})})},"addClass"),CCe=o(function(t){Fo=t,/.*/.exec(Fo)&&(Fo="LR"),/.*v/.exec(Fo)&&(Fo="TB"),Fo==="TD"&&(Fo="TB")},"setDirection"),u9=o(function(t,e){for(let r of t.split(",")){let n=$h.get(r);n&&n.classes.push(e);let i=l9.get(r);i&&i.classes.push(e)}},"setClass"),SCe=o(function(t,e){if(e!==void 0){e=C5(e);for(let r of t.split(","))c9.set(k5==="gen-1"?S5(r):r,e)}},"setTooltip"),ACe=o(function(t,e,r){let n=S5(t);if(de().securityLevel!=="loose"||e===void 0)return;let i=[];if(typeof r=="string"){i=r.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(let s=0;s
+`:"'+(n?a:ro(a,!0))+`
+`}blockquote({tokens:e}){return`"+(n?a:ro(a,!0))+`
+${this.parser.parse(e)}
+`}html({text:e}){return e}heading({tokens:e,depth:r}){return`
+`}list(e){let r=e.ordered,n=e.start,i="";for(let l=0;l
+
+`+r+`
+`+i+`
+`}tablerow({text:e}){return`
+${e}
+`}tablecell(e){let r=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+r+`${n}>
+`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${e}
`}br(e){return"
"}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:r,tokens:n}){let i=this.parser.parseInline(n),a=ZX(e);if(a===null)return i;e=a;let s='"+i+"",s}image({href:e,title:r,text:n}){let i=ZX(e);if(i===null)return n;e=i;let a=`",a}text(e){return"tokens"in e&&e.tokens?this.parser.parseInline(e.tokens):e.text}},yv=class{static{o(this,"_TextRenderer")}strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return""+e}image({text:e}){return""+e}br(){return""}},Au=class t{static{o(this,"_Parser")}options;renderer;textRenderer;constructor(e){this.options=e||Sd,this.options.renderer=this.options.renderer||new fm,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new yv}static parse(e,r){return new t(r).parse(e)}static parseInline(e,r){return new t(r).parseInline(e)}parse(e,r=!0){let n="";for(let i=0;i"+ro(n.message+"",!0)+"
";return r?Promise.resolve(i):i}if(r)return Promise.reject(n);throw n}}},Cd=new p9;o(jr,"marked");jr.options=jr.setOptions=function(t){return Cd.setOptions(t),jr.defaults=Cd.defaults,rj(jr.defaults),jr};jr.getDefaults=m9;jr.defaults=Sd;jr.use=function(...t){return Cd.use(...t),jr.defaults=Cd.defaults,rj(jr.defaults),jr};jr.walkTokens=function(t,e){return Cd.walkTokens(t,e)};jr.parseInline=Cd.parseInline;jr.Parser=Au;jr.parser=Au.parse;jr.Renderer=fm;jr.TextRenderer=yv;jr.Lexer=Su;jr.lexer=Su.lex;jr.Tokenizer=hm;jr.Hooks=um;jr.parse=jr;mkt=jr.options,gkt=jr.setOptions,ykt=jr.use,vkt=jr.walkTokens,xkt=jr.parseInline,bkt=Au.parse,wkt=Su.lex});function R7e(t,{markdownAutoWrap:e}){let n=t.replace(/
/g,`
+`).replace(/\n{2,}/g,`
+`),i=Gb(n);return e===!1?i.replace(/ /g," "):i}function dj(t,e={}){let r=R7e(t,e),n=jr.lexer(r),i=[[]],a=0;function s(l,u="normal"){l.type==="text"?l.text.split(`
+`).forEach((f,d)=>{d!==0&&(a++,i.push([])),f.split(" ").forEach(p=>{p&&i[a].push({content:p,type:u})})}):l.type==="strong"||l.type==="em"?l.tokens.forEach(h=>{s(h,l.type)}):l.type==="html"&&i[a].push({content:l.text,type:"normal"})}return o(s,"processNode"),n.forEach(l=>{l.type==="paragraph"?l.tokens?.forEach(u=>{s(u)}):l.type==="html"&&i[a].push({content:l.text,type:"normal"})}),i}function pj(t,{markdownAutoWrap:e}={}){let r=jr.lexer(t);function n(i){return i.type==="text"?e===!1?i.text.replace(/\n */g,"
").replace(/ /g," "):i.text.replace(/\n */g,"
"):i.type==="strong"?`${i.tokens?.map(n).join("")}`:i.type==="em"?`${i.tokens?.map(n).join("")}`:i.type==="paragraph"?`
/g,"
"),d=dj(f.replace("
","
"),h),p=P7e(l,t,d,e?u:!1);if(s){/stroke:/.exec(r)&&(r=r.replace("stroke:","lineColor:"));let m=r.replace(/stroke:[^;]+;?/g,"").replace(/stroke-width:[^;]+;?/g,"").replace(/fill:[^;]+;?/g,"").replace(/color:/g,"fill:");$e(p).attr("style",m)}else{let m=r.replace(/stroke:[^;]+;?/g,"").replace(/stroke-width:[^;]+;?/g,"").replace(/fill:[^;]+;?/g,"").replace(/background:/g,"fill:");$e(p).select("rect").attr("style",m.replace(/background:/g,"fill:"));let g=r.replace(/stroke:[^;]+;?/g,"").replace(/stroke-width:[^;]+;?/g,"").replace(/fill:[^;]+;?/g,"").replace(/color:/g,"fill:");$e(p).select("text").attr("style",g)}return p}},"createText")});function wj(t,e){e&&t.attr("style",e)}function B7e(t){let e=$e(document.createElementNS("http://www.w3.org/2000/svg","foreignObject")),r=e.append("xhtml:div"),n=t.label,i=t.isNode?"nodeLabel":"edgeLabel",a=r.append("span");return a.html(n),wj(a,t.labelStyle),a.attr("class",i),wj(r,t.labelStyle),r.style("display","inline-block"),r.style("white-space","nowrap"),r.attr("xmlns","http://www.w3.org/1999/xhtml"),e.node()}var F7e,ra,bv=R(()=>{"use strict";Zt();ut();_t();rr();xr();Al();o(wj,"applyStyle");o(B7e,"addHtmlLabel");F7e=o((t,e,r,n)=>{let i=t||"";if(typeof i=="object"&&(i=i[0]),yr(de().flowchart.htmlLabels)){i=i.replace(/\\n|\n/g,"
"),V.debug("vertexText"+i);let a={isNode:n,label:E9(to(i)),labelStyle:e.replace("fill:","color:")};return B7e(a)}else{let a=document.createElementNS("http://www.w3.org/2000/svg","text");a.setAttribute("style",e.replace("color:","fill:"));let s=[];typeof i=="string"?s=i.split(/\\n|\n|
/gi):Array.isArray(i)?s=i:s=[];for(let l of s){let u=document.createElementNS("http://www.w3.org/2000/svg","tspan");u.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),u.setAttribute("dy","1em"),u.setAttribute("x","0"),r?u.setAttribute("class","title-row"):u.setAttribute("class","row"),u.textContent=l.trim(),a.appendChild(u)}return a}},"createLabel"),ra=F7e});function z7e(t,e){return t.intersect(e)}var Tj,kj=R(()=>{"use strict";o(z7e,"intersectNode");Tj=z7e});function G7e(t,e,r,n){var i=t.x,a=t.y,s=i-n.x,l=a-n.y,u=Math.sqrt(e*e*l*l+r*r*s*s),h=Math.abs(e*r*s/u);n.x{"use strict";o(G7e,"intersectEllipse");R5=G7e});function $7e(t,e,r){return R5(t,e,e,r)}var Ej,Cj=R(()=>{"use strict";C9();o($7e,"intersectCircle");Ej=$7e});function V7e(t,e,r,n){var i,a,s,l,u,h,f,d,p,m,g,y,v,x,b;if(i=e.y-t.y,s=t.x-e.x,u=e.x*t.y-t.x*e.y,p=i*r.x+s*r.y+u,m=i*n.x+s*n.y+u,!(p!==0&&m!==0&&Sj(p,m))&&(a=n.y-r.y,l=r.x-n.x,h=n.x*r.y-r.x*n.y,f=a*t.x+l*t.y+h,d=a*e.x+l*e.y+h,!(f!==0&&d!==0&&Sj(f,d))&&(g=i*l-a*s,g!==0)))return y=Math.abs(g/2),v=s*h-l*u,x=v<0?(v-y)/g:(v+y)/g,v=a*u-i*h,b=v<0?(v-y)/g:(v+y)/g,{x,y:b}}function Sj(t,e){return t*e>0}var Aj,_j=R(()=>{"use strict";o(V7e,"intersectLine");o(Sj,"sameSign");Aj=V7e});function U7e(t,e,r){var n=t.x,i=t.y,a=[],s=Number.POSITIVE_INFINITY,l=Number.POSITIVE_INFINITY;typeof e.forEach=="function"?e.forEach(function(g){s=Math.min(s,g.x),l=Math.min(l,g.y)}):(s=Math.min(s,e.x),l=Math.min(l,e.y));for(var u=n-t.width/2-s,h=i-t.height/2-l,f=0;f
"):d,e.labelStyle,!0,!0));if(yr(de().flowchart.htmlLabels)){let y=m.children[0],v=$e(m);f=y.getBoundingClientRect(),v.attr("width",f.width),v.attr("height",f.height)}let g=e.padding/2;return $e(m).attr("transform","translate( "+(f.width>p.width?0:(p.width-f.width)/2)+", "+(p.height+g+5)+")"),$e(h).attr("transform","translate( "+(f.width
"),V.info("vertexText"+i);let a={isNode:n,label:to(i).replace(/fa[blrs]?:fa-[\w-]+/g,l=>``),labelStyle:e&&e.replace("fill:","color:")};return await kSe(a)}else{let a=document.createElementNS("http://www.w3.org/2000/svg","text");a.setAttribute("style",e.replace("color:","fill:"));let s=[];typeof i=="string"?s=i.split(/\\n|\n|
/gi):Array.isArray(i)?s=i:s=[];for(let l of s){let u=document.createElementNS("http://www.w3.org/2000/svg","tspan");u.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),u.setAttribute("dy","1em"),u.setAttribute("x","0"),r?u.setAttribute("class","title-row"):u.setAttribute("class","row"),u.textContent=l.trim(),a.appendChild(u)}return a}},"createLabel"),gc=ESe});var _u,Sv=R(()=>{"use strict";_u=o((t,e,r,n,i)=>["M",t+i,e,"H",t+r-i,"A",i,i,0,0,1,t+r,e+i,"V",e+n-i,"A",i,i,0,0,1,t+r-i,e+n,"H",t+i,"A",i,i,0,0,1,t,e+n-i,"V",e+i,"A",i,i,0,0,1,t+i,e,"Z"].join(" "),"createRoundedRectPathD")});var Lu,Jj,CSe,Br,Fr,ki=R(()=>{"use strict";_t();Lu=o(t=>{let{handDrawnSeed:e}=de();return{fill:t,hachureAngle:120,hachureGap:4,fillWeight:2,roughness:.7,stroke:t,seed:e}},"solidStateFill"),Jj=o(t=>{let e=CSe([...t.cssCompiledStyles||[],...t.cssStyles||[]]);return{stylesMap:e,stylesArray:[...e]}},"compileStyles"),CSe=o(t=>{let e=new Map;return t.forEach(r=>{let[n,i]=r.split(":");e.set(n.trim(),i?.trim())}),e},"styles2Map"),Br=o(t=>{let{stylesArray:e}=Jj(t),r=[],n=[],i=[],a=[];return e.forEach(s=>{let l=s[0];l==="color"||l==="font-size"||l==="font-family"||l==="font-weight"||l==="font-style"||l==="text-decoration"||l==="text-align"||l==="text-transform"||l==="line-height"||l==="letter-spacing"||l==="word-spacing"||l==="text-shadow"||l==="text-overflow"||l==="white-space"||l==="word-wrap"||l==="word-break"||l==="overflow-wrap"||l==="hyphens"?r.push(s.join(":")+" !important"):(n.push(s.join(":")+" !important"),l.includes("stroke")&&i.push(s.join(":")+" !important"),l==="fill"&&a.push(s.join(":")+" !important"))}),{labelStyles:r.join(";"),nodeStyles:n.join(";"),stylesArray:e,borderStyles:i,backgroundStyles:a}},"styles2String"),Fr=o((t,e)=>{let{themeVariables:r,handDrawnSeed:n}=de(),{nodeBorder:i,mainBkg:a}=r,{stylesMap:s}=Jj(t);return Object.assign({roughness:.7,fill:s.get("fill")||a,fillStyle:"hachure",fillWeight:4,stroke:s.get("stroke")||i,seed:n,strokeWidth:1.3},e)},"userNodeOverrides")});var eK,SSe,ASe,_Se,LSe,DSe,tK,Y5,rK,X9=R(()=>{"use strict";_t();rr();ut();_d();Zt();ti();Al();q9();H5();Sv();ki();eK=o(async(t,e)=>{V.info("Creating subgraph rect for ",e.id,e);let r=de(),{themeVariables:n,handDrawnSeed:i}=r,{clusterBkg:a,clusterBorder:s}=n,{labelStyles:l,nodeStyles:u,borderStyles:h,backgroundStyles:f}=Br(e),d=t.insert("g").attr("class","cluster "+e.cssClasses).attr("id",e.id).attr("data-look",e.look),p=yr(r.flowchart.htmlLabels),m=d.insert("g").attr("class","cluster-label "),g=await ta(m,e.label,{style:e.labelStyle,useHtmlLabels:p,isNode:!0}),y=g.getBBox();if(yr(r.flowchart.htmlLabels)){let _=g.children[0],A=$e(g);y=_.getBoundingClientRect(),A.attr("width",y.width),A.attr("height",y.height)}let v=e.width<=y.width+e.padding?y.width+e.padding:e.width;e.width<=y.width+e.padding?e.diff=(v-e.width)/2-e.padding:e.diff=-e.padding;let x=e.height,b=e.x-v/2,w=e.y-x/2;V.trace("Data ",e,JSON.stringify(e));let S;if(e.look==="handDrawn"){let _=Jt.svg(d),A=Fr(e,{roughness:.7,fill:a,stroke:s,fillWeight:3,seed:i}),L=_.path(_u(b,w,v,x,0),A);S=d.insert(()=>(V.debug("Rough node insert CXC",L),L),":first-child"),S.select("path:nth-child(2)").attr("style",h.join(";")),S.select("path").attr("style",f.join(";").replace("fill","stroke"))}else S=d.insert("rect",":first-child"),S.attr("style",u).attr("rx",e.rx).attr("ry",e.ry).attr("x",b).attr("y",w).attr("width",v).attr("height",x);let{subGraphTitleTopMargin:T}=io(r);if(m.attr("transform",`translate(${e.x-y.width/2}, ${e.y-e.height/2+T})`),l){let _=m.select("span");_&&_.attr("style",l)}let E=S.node().getBBox();return e.offsetX=0,e.width=E.width,e.height=E.height,e.offsetY=y.height-e.padding/2,e.intersect=function(_){return Dd(e,_)},{cluster:d,labelBBox:y}},"rect"),SSe=o((t,e)=>{let r=t.insert("g").attr("class","note-cluster").attr("id",e.id),n=r.insert("rect",":first-child"),i=0*e.padding,a=i/2;n.attr("rx",e.rx).attr("ry",e.ry).attr("x",e.x-e.width/2-a).attr("y",e.y-e.height/2-a).attr("width",e.width+i).attr("height",e.height+i).attr("fill","none");let s=n.node().getBBox();return e.width=s.width,e.height=s.height,e.intersect=function(l){return Dd(e,l)},{cluster:r,labelBBox:{width:0,height:0}}},"noteGroup"),ASe=o(async(t,e)=>{let r=de(),{themeVariables:n,handDrawnSeed:i}=r,{altBackground:a,compositeBackground:s,compositeTitleBackground:l,nodeBorder:u}=n,h=t.insert("g").attr("class",e.cssClasses).attr("id",e.id).attr("data-id",e.id).attr("data-look",e.look),f=h.insert("g",":first-child"),d=h.insert("g").attr("class","cluster-label"),p=h.append("rect"),m=d.node().appendChild(await gc(e.label,e.labelStyle,void 0,!0)),g=m.getBBox();if(yr(r.flowchart.htmlLabels)){let L=m.children[0],M=$e(m);g=L.getBoundingClientRect(),M.attr("width",g.width),M.attr("height",g.height)}let y=0*e.padding,v=y/2,x=(e.width<=g.width+e.padding?g.width+e.padding:e.width)+y;e.width<=g.width+e.padding?e.diff=(x-e.width)/2-e.padding:e.diff=-e.padding;let b=e.height+y,w=e.height+y-g.height-6,S=e.x-x/2,T=e.y-b/2;e.width=x;let E=e.y-e.height/2-v+g.height+2,_;if(e.look==="handDrawn"){let L=e.cssClasses.includes("statediagram-cluster-alt"),M=Jt.svg(h),N=e.rx||e.ry?M.path(_u(S,T,x,b,10),{roughness:.7,fill:l,fillStyle:"solid",stroke:u,seed:i}):M.rectangle(S,T,x,b,{seed:i});_=h.insert(()=>N,":first-child");let k=M.rectangle(S,E,x,w,{fill:L?a:s,fillStyle:L?"hachure":"solid",stroke:u,seed:i});_=h.insert(()=>N,":first-child"),p=h.insert(()=>k)}else _=f.insert("rect",":first-child"),_.attr("class","outer").attr("x",S).attr("y",T).attr("width",x).attr("height",b).attr("data-look",e.look),p.attr("class","inner").attr("x",S).attr("y",E).attr("width",x).attr("height",w);d.attr("transform",`translate(${e.x-g.width/2}, ${T+1-(yr(r.flowchart.htmlLabels)?0:3)})`);let A=_.node().getBBox();return e.height=A.height,e.offsetX=0,e.offsetY=g.height-e.padding/2,e.labelBBox=g,e.intersect=function(L){return Dd(e,L)},{cluster:h,labelBBox:g}},"roundedWithTitle"),_Se=o((t,e)=>{let r=de(),{themeVariables:n,handDrawnSeed:i}=r,{nodeBorder:a}=n,s=t.insert("g").attr("class",e.cssClasses).attr("id",e.id).attr("data-look",e.look),l=s.insert("g",":first-child"),u=0*e.padding,h=e.width+u;e.diff=-e.padding;let f=e.height+u,d=e.x-h/2,p=e.y-f/2;e.width=h;let m;if(e.look==="handDrawn"){let v=Jt.svg(s).rectangle(d,p,h,f,{fill:"lightgrey",roughness:.5,strokeLineDash:[5],stroke:a,seed:i});m=s.insert(()=>v,":first-child")}else m=l.insert("rect",":first-child"),m.attr("class","divider").attr("x",d).attr("y",p).attr("width",h).attr("height",f).attr("data-look",e.look);let g=m.node().getBBox();return e.height=g.height,e.offsetX=0,e.offsetY=0,e.intersect=function(y){return Dd(e,y)},{cluster:s,labelBBox:{}}},"divider"),LSe=eK,DSe={rect:eK,squareRect:LSe,roundedWithTitle:ASe,noteGroup:SSe,divider:_Se},tK=new Map,Y5=o(async(t,e)=>{let r=e.shape||"rect",n=await DSe[r](t,e);return tK.set(e.id,n),n},"insertCluster"),rK=o(()=>{tK=new Map},"clear")});function W5(t,e){if(t===void 0||e===void 0)return{angle:0,deltaX:0,deltaY:0};t=q5(t),e=q5(e);let[r,n]=[t.x,t.y],[i,a]=[e.x,e.y],s=i-r,l=a-n;return{angle:Math.atan(l/s),deltaX:s,deltaY:l}}var Uh,q5,X5,j9=R(()=>{"use strict";Uh={aggregation:18,extension:18,composition:18,dependency:6,lollipop:13.5,arrow_point:4};o(W5,"calculateDeltaAndAngle");q5=o(t=>Array.isArray(t)?{x:t[0],y:t[1]}:t,"pointTransformer"),X5=o(t=>({x:o(function(e,r,n){let i=0;if(r===0&&Object.hasOwn(Uh,t.arrowTypeStart)){let{angle:a,deltaX:s}=W5(n[0],n[1]);i=Uh[t.arrowTypeStart]*Math.cos(a)*(s>=0?1:-1)}else if(r===n.length-1&&Object.hasOwn(Uh,t.arrowTypeEnd)){let{angle:a,deltaX:s}=W5(n[n.length-1],n[n.length-2]);i=Uh[t.arrowTypeEnd]*Math.cos(a)*(s>=0?1:-1)}return q5(e).x+i},"x"),y:o(function(e,r,n){let i=0;if(r===0&&Object.hasOwn(Uh,t.arrowTypeStart)){let{angle:a,deltaY:s}=W5(n[0],n[1]);i=Uh[t.arrowTypeStart]*Math.abs(Math.sin(a))*(s>=0?1:-1)}else if(r===n.length-1&&Object.hasOwn(Uh,t.arrowTypeEnd)){let{angle:a,deltaY:s}=W5(n[n.length-1],n[n.length-2]);i=Uh[t.arrowTypeEnd]*Math.abs(Math.sin(a))*(s>=0?1:-1)}return q5(e).y+i},"y")}),"getLineFunctionsWithOffset")});var iK,RSe,nK,aK=R(()=>{"use strict";ut();iK=o((t,e,r,n,i)=>{e.arrowTypeStart&&nK(t,"start",e.arrowTypeStart,r,n,i),e.arrowTypeEnd&&nK(t,"end",e.arrowTypeEnd,r,n,i)},"addEdgeMarkers"),RSe={arrow_cross:"cross",arrow_point:"point",arrow_barb:"barb",arrow_circle:"circle",aggregation:"aggregation",extension:"extension",composition:"composition",dependency:"dependency",lollipop:"lollipop"},nK=o((t,e,r,n,i,a)=>{let s=RSe[r];if(!s){V.warn(`Unknown arrow type: ${r}`);return}let l=e==="start"?"Start":"End";t.attr(`marker-${e}`,`url(${n}#${i}_${a}-${s}${l})`)},"addEdgeMarker")});function j5(t,e){de().flowchart.htmlLabels&&t&&(t.style.width=e.length*9+"px",t.style.height="12px")}function ISe(t){let e=[],r=[];for(let n=1;n
"):p,e.labelStyle,!0,!0)),y=g.children[0],v=$e(g);d=y.getBoundingClientRect(),v.attr("width",d.width),v.attr("height",d.height);let x=(e.padding||0)/2;$e(g).attr("transform","translate( "+(d.width>m.width?0:(m.width-d.width)/2)+", "+(m.height+x+5)+")"),$e(f).attr("transform","translate( "+(d.width0&&r(l)?e>1?VQ(l,e-1,r,n,i):Em(i,l):n||(i[i.length]=l)}return i}var bc,Cm=R(()=>{"use strict";hw();$Q();o(VQ,"baseFlatten");bc=VQ});function r8e(t){var e=t==null?0:t.length;return e?bc(t,1):[]}var Gr,fw=R(()=>{"use strict";Cm();o(r8e,"flatten");Gr=r8e});function n8e(t){return d5(f5(t,void 0,Gr),t+"")}var UQ,HQ=R(()=>{"use strict";fw();H_();W_();o(n8e,"flatRest");UQ=n8e});function i8e(t,e,r){var n=-1,i=t.length;e<0&&(e=-e>i?0:i+e),r=r>i?i:r,r<0&&(r+=i),i=e>r?0:r-e>>>0,e>>>=0;for(var a=Array(i);++n{"use strict";o(i8e,"baseSlice");dw=i8e});function d8e(t){return f8e.test(t)}var a8e,s8e,o8e,l8e,c8e,u8e,h8e,f8e,YQ,WQ=R(()=>{"use strict";a8e="\\ud800-\\udfff",s8e="\\u0300-\\u036f",o8e="\\ufe20-\\ufe2f",l8e="\\u20d0-\\u20ff",c8e=s8e+o8e+l8e,u8e="\\ufe0e\\ufe0f",h8e="\\u200d",f8e=RegExp("["+h8e+a8e+c8e+u8e+"]");o(d8e,"hasUnicode");YQ=d8e});function p8e(t,e,r,n){var i=-1,a=t==null?0:t.length;for(n&&a&&(r=t[++i]);++i{"use strict";o(p8e,"arrayReduce");qQ=p8e});function m8e(t,e){return t&&Bo(e,Dr(e),t)}var jQ,KQ=R(()=>{"use strict";kd();vc();o(m8e,"baseAssign");jQ=m8e});function g8e(t,e){return t&&Bo(e,bs(e),t)}var QQ,ZQ=R(()=>{"use strict";kd();zh();o(g8e,"baseAssignIn");QQ=g8e});function y8e(t,e){for(var r=-1,n=t==null?0:t.length,i=0,a=[];++r=l)return u;var h=r[n];return u*(h=="desc"?-1:1)}}return t.index-e.index}var bee,wee=R(()=>{"use strict";xee();o(sDe,"compareMultiple");bee=sDe});function oDe(t,e,r){e.length?e=Ss(e,function(a){return wt(a)?function(s){return Yh(s,a.length===1?a[0]:a)}:a}):e=[ea];var n=-1;e=Ss(e,Oo(cn));var i=Uw(t,function(a,s,l){var u=Ss(e,function(h){return h(a)});return{criteria:u,index:++n,value:a}});return gee(i,function(a,s){return bee(a,s,r)})}var Tee,kee=R(()=>{"use strict";Md();Dv();Qa();DL();yee();Td();wee();Eu();Bn();o(oDe,"baseOrderBy");Tee=oDe});var lDe,Eee,Cee=R(()=>{"use strict";SL();lDe=Pw("length"),Eee=lDe});function bDe(t){for(var e=See.lastIndex=0;See.test(t);)++e;return e}var Aee,cDe,uDe,hDe,fDe,dDe,pDe,zL,GL,mDe,_ee,Lee,Dee,gDe,Ree,Nee,yDe,vDe,xDe,See,Mee,Iee=R(()=>{"use strict";Aee="\\ud800-\\udfff",cDe="\\u0300-\\u036f",uDe="\\ufe20-\\ufe2f",hDe="\\u20d0-\\u20ff",fDe=cDe+uDe+hDe,dDe="\\ufe0e\\ufe0f",pDe="["+Aee+"]",zL="["+fDe+"]",GL="\\ud83c[\\udffb-\\udfff]",mDe="(?:"+zL+"|"+GL+")",_ee="[^"+Aee+"]",Lee="(?:\\ud83c[\\udde6-\\uddff]){2}",Dee="[\\ud800-\\udbff][\\udc00-\\udfff]",gDe="\\u200d",Ree=mDe+"?",Nee="["+dDe+"]?",yDe="(?:"+gDe+"(?:"+[_ee,Lee,Dee].join("|")+")"+Nee+Ree+")*",vDe=Nee+Ree+yDe,xDe="(?:"+[_ee+zL+"?",zL,Lee,Dee,pDe].join("|")+")",See=RegExp(GL+"(?="+GL+")|"+xDe+vDe,"g");o(bDe,"unicodeSize");Mee=bDe});function wDe(t){return YQ(t)?Mee(t):Eee(t)}var Oee,Pee=R(()=>{"use strict";Cee();WQ();Iee();o(wDe,"stringSize");Oee=wDe});function TDe(t,e){return jw(t,e,function(r,n){return Ow(t,n)})}var Bee,Fee=R(()=>{"use strict";FL();CL();o(TDe,"basePick");Bee=TDe});var kDe,Fd,zee=R(()=>{"use strict";Fee();HQ();kDe=UQ(function(t,e){return t==null?{}:Bee(t,e)}),Fd=kDe});function SDe(t,e,r,n){for(var i=-1,a=CDe(EDe((e-t)/(r||1)),0),s=Array(a);a--;)s[n?a:++i]=t,t+=r;return s}var EDe,CDe,Gee,$ee=R(()=>{"use strict";EDe=Math.ceil,CDe=Math.max;o(SDe,"baseRange");Gee=SDe});function ADe(t){return function(e,r,n){return n&&typeof n!="number"&&eo(e,r,n)&&(r=n=void 0),e=vm(e),r===void 0?(r=e,e=0):r=vm(r),n=n===void 0?e{"use strict";Pt();Ec();o(Ste,"addBorderSegments");o(Cte,"addBorderNode")});function Lte(t){var e=t.graph().rankdir.toLowerCase();(e==="lr"||e==="rl")&&Rte(t)}function Dte(t){var e=t.graph().rankdir.toLowerCase();(e==="bt"||e==="rl")&&rRe(t),(e==="lr"||e==="rl")&&(nRe(t),Rte(t))}function Rte(t){Ee(t.nodes(),function(e){_te(t.node(e))}),Ee(t.edges(),function(e){_te(t.edge(e))})}function _te(t){var e=t.width;t.width=t.height,t.height=e}function rRe(t){Ee(t.nodes(),function(e){QL(t.node(e))}),Ee(t.edges(),function(e){var r=t.edge(e);Ee(r.points,QL),Xe(r,"y")&&QL(r)})}function QL(t){t.y=-t.y}function nRe(t){Ee(t.nodes(),function(e){ZL(t.node(e))}),Ee(t.edges(),function(e){var r=t.edge(e);Ee(r.points,ZL),Xe(r,"x")&&ZL(r)})}function ZL(t){var e=t.x;t.x=t.y,t.y=e}var Nte=R(()=>{"use strict";Pt();o(Lte,"adjust");o(Dte,"undo");o(Rte,"swapWidthHeight");o(_te,"swapWidthHeightOne");o(rRe,"reverseY");o(QL,"reverseYOne");o(nRe,"swapXY");o(ZL,"swapXYOne")});function Mte(t){t.graph().dummyChains=[],Ee(t.edges(),function(e){aRe(t,e)})}function aRe(t,e){var r=e.v,n=t.node(r).rank,i=e.w,a=t.node(i).rank,s=e.name,l=t.edge(e),u=l.labelRank;if(a!==n+1){t.removeEdge(e);var h,f,d;for(d=0,++n;n{"use strict";Pt();Ec();o(Mte,"run");o(aRe,"normalizeEdge");o(Ite,"undo")});function Iv(t){var e={};function r(n){var i=t.node(n);if(Xe(e,n))return i.rank;e[n]=!0;var a=Ll(qe(t.outEdges(n),function(s){return r(s.w)-t.edge(s).minlen}));return(a===Number.POSITIVE_INFINITY||a===void 0||a===null)&&(a=0),i.rank=a}o(r,"dfs"),Ee(t.sources(),r)}function $d(t,e){return t.node(e.w).rank-t.node(e.v).rank-t.edge(e).minlen}var tT=R(()=>{"use strict";Pt();o(Iv,"longestPath");o($d,"slack")});function rT(t){var e=new lr({directed:!1}),r=t.nodes()[0],n=t.nodeCount();e.setNode(r,{});for(var i,a;sRe(e,t)h)&&Sre(r,p,f)})})}o(n,"scan");function i(a,s){var l=-1,u,h=0;return Ee(s,function(f,d){if(t.node(f).dummy==="border"){var p=t.predecessors(f);p.length&&(u=t.node(p[0]).order,n(s,h,d,l,u),h=d,l=u)}n(s,h,s.length,u,a.length)}),s}return o(i,"visitLayer"),Vr(e,i),r}function MRe(t,e){if(t.node(e).dummy)return Za(t.predecessors(e),function(r){return t.node(r).dummy})}function Sre(t,e,r){if(e>r){var n=e;e=r,r=n}var i=t[e];i||(t[e]=i={}),i[r]=!0}function IRe(t,e,r){if(e>r){var n=e;e=r,r=n}return Xe(t[e],r)}function ORe(t,e,r,n){var i={},a={},s={};return Ee(e,function(l){Ee(l,function(u,h){i[u]=u,a[u]=u,s[u]=h})}),Ee(e,function(l){var u=-1;Ee(l,function(h){var f=n(h);if(f.length){f=Tc(f,function(y){return s[y]});for(var d=(f.length-1)/2,p=Math.floor(d),m=Math.ceil(d);p<=m;++p){var g=f[p];a[h]===h&&u{"use strict";Pt();ya();Ec();o(RRe,"findType1Conflicts");o(NRe,"findType2Conflicts");o(MRe,"findOtherInnerSegmentNode");o(Sre,"addConflict");o(IRe,"hasConflict");o(ORe,"verticalAlignment");o(PRe,"horizontalCompaction");o(BRe,"buildBlockGraph");o(FRe,"findSmallestWidthAlignment");o(zRe,"alignCoordinates");o(GRe,"balance");o(Are,"positionX");o($Re,"sep");o(VRe,"width")});function Lre(t){t=eT(t),URe(t),ML(Are(t),function(e,r){t.node(r).x=e})}function URe(t){var e=Qh(t),r=t.graph().ranksep,n=0;Ee(e,function(i){var a=_s(qe(i,function(s){return t.node(s).height}));Ee(i,function(s){t.node(s).y=n+a/2}),n+=a+r})}var Dre=R(()=>{"use strict";Pt();Ec();_re();o(Lre,"position");o(URe,"positionY")});function lo(t,e){var r=e&&e.debugTiming?kte:Ete;r("layout",function(){var n=r(" buildLayoutGraph",function(){return eNe(t)});r(" runLayout",function(){HRe(n,r)}),r(" updateInputGraph",function(){YRe(t,n)})})}function HRe(t,e){e(" makeSpaceForEdgeLabels",function(){tNe(t)}),e(" removeSelfEdges",function(){uNe(t)}),e(" acyclic",function(){gte(t)}),e(" nestingGraph.run",function(){ere(t)}),e(" rank",function(){hD(eT(t))}),e(" injectEdgeLabelProxies",function(){rNe(t)}),e(" removeEmptyRanks",function(){wte(t)}),e(" nestingGraph.cleanup",function(){rre(t)}),e(" normalizeRanks",function(){bte(t)}),e(" assignRankMinMax",function(){nNe(t)}),e(" removeEdgeLabelProxies",function(){iNe(t)}),e(" normalize.run",function(){Mte(t)}),e(" parentDummyChains",function(){Ere(t)}),e(" addBorderSegments",function(){Ste(t)}),e(" order",function(){Tre(t)}),e(" insertSelfEdges",function(){hNe(t)}),e(" adjustCoordinateSystem",function(){Lte(t)}),e(" position",function(){Lre(t)}),e(" positionSelfEdges",function(){fNe(t)}),e(" removeBorderNodes",function(){cNe(t)}),e(" normalize.undo",function(){Ite(t)}),e(" fixupEdgeLabelCoords",function(){oNe(t)}),e(" undoCoordinateSystem",function(){Dte(t)}),e(" translateGraph",function(){aNe(t)}),e(" assignNodeIntersects",function(){sNe(t)}),e(" reversePoints",function(){lNe(t)}),e(" acyclic.undo",function(){yte(t)})}function YRe(t,e){Ee(t.nodes(),function(r){var n=t.node(r),i=e.node(r);n&&(n.x=i.x,n.y=i.y,e.children(r).length&&(n.width=i.width,n.height=i.height))}),Ee(t.edges(),function(r){var n=t.edge(r),i=e.edge(r);n.points=i.points,Xe(i,"x")&&(n.x=i.x,n.y=i.y)}),t.graph().width=e.graph().width,t.graph().height=e.graph().height}function eNe(t){var e=new lr({multigraph:!0,compound:!0}),r=mD(t.graph());return e.setGraph(Gh({},qRe,pD(r,WRe),Fd(r,XRe))),Ee(t.nodes(),function(n){var i=mD(t.node(n));e.setNode(n,Xh(pD(i,jRe),KRe)),e.setParent(n,t.parent(n))}),Ee(t.edges(),function(n){var i=mD(t.edge(n));e.setEdge(n,Gh({},ZRe,pD(i,QRe),Fd(i,JRe)))}),e}function tNe(t){var e=t.graph();e.ranksep/=2,Ee(t.edges(),function(r){var n=t.edge(r);n.minlen*=2,n.labelpos.toLowerCase()!=="c"&&(e.rankdir==="TB"||e.rankdir==="BT"?n.width+=n.labeloffset:n.height+=n.labeloffset)})}function rNe(t){Ee(t.edges(),function(e){var r=t.edge(e);if(r.width&&r.height){var n=t.node(e.v),i=t.node(e.w),a={rank:(i.rank-n.rank)/2+n.rank,e};kc(t,"edge-proxy",a,"_ep")}})}function nNe(t){var e=0;Ee(t.nodes(),function(r){var n=t.node(r);n.borderTop&&(n.minRank=t.node(n.borderTop).rank,n.maxRank=t.node(n.borderBottom).rank,e=_s(e,n.maxRank))}),t.graph().maxRank=e}function iNe(t){Ee(t.nodes(),function(e){var r=t.node(e);r.dummy==="edge-proxy"&&(t.edge(r.e).labelRank=r.rank,t.removeNode(e))})}function aNe(t){var e=Number.POSITIVE_INFINITY,r=0,n=Number.POSITIVE_INFINITY,i=0,a=t.graph(),s=a.marginx||0,l=a.marginy||0;function u(h){var f=h.x,d=h.y,p=h.width,m=h.height;e=Math.min(e,f-p/2),r=Math.max(r,f+p/2),n=Math.min(n,d-m/2),i=Math.max(i,d+m/2)}o(u,"getExtremes"),Ee(t.nodes(),function(h){u(t.node(h))}),Ee(t.edges(),function(h){var f=t.edge(h);Xe(f,"x")&&u(f)}),e-=s,n-=l,Ee(t.nodes(),function(h){var f=t.node(h);f.x-=e,f.y-=n}),Ee(t.edges(),function(h){var f=t.edge(h);Ee(f.points,function(d){d.x-=e,d.y-=n}),Xe(f,"x")&&(f.x-=e),Xe(f,"y")&&(f.y-=n)}),a.width=r-e+s,a.height=i-n+l}function sNe(t){Ee(t.edges(),function(e){var r=t.edge(e),n=t.node(e.v),i=t.node(e.w),a,s;r.points?(a=r.points[0],s=r.points[r.points.length-1]):(r.points=[],a=i,s=n),r.points.unshift(XL(n,a)),r.points.push(XL(i,s))})}function oNe(t){Ee(t.edges(),function(e){var r=t.edge(e);if(Xe(r,"x"))switch((r.labelpos==="l"||r.labelpos==="r")&&(r.width-=r.labeloffset),r.labelpos){case"l":r.x-=r.width/2+r.labeloffset;break;case"r":r.x+=r.width/2+r.labeloffset;break}})}function lNe(t){Ee(t.edges(),function(e){var r=t.edge(e);r.reversed&&r.points.reverse()})}function cNe(t){Ee(t.nodes(),function(e){if(t.children(e).length){var r=t.node(e),n=t.node(r.borderTop),i=t.node(r.borderBottom),a=t.node(ma(r.borderLeft)),s=t.node(ma(r.borderRight));r.width=Math.abs(s.x-a.x),r.height=Math.abs(i.y-n.y),r.x=a.x+r.width/2,r.y=n.y+r.height/2}}),Ee(t.nodes(),function(e){t.node(e).dummy==="border"&&t.removeNode(e)})}function uNe(t){Ee(t.edges(),function(e){if(e.v===e.w){var r=t.node(e.v);r.selfEdges||(r.selfEdges=[]),r.selfEdges.push({e,label:t.edge(e)}),t.removeEdge(e)}})}function hNe(t){var e=Qh(t);Ee(e,function(r){var n=0;Ee(r,function(i,a){var s=t.node(i);s.order=a+n,Ee(s.selfEdges,function(l){kc(t,"selfedge",{width:l.label.width,height:l.label.height,rank:s.rank,order:a+ ++n,e:l.e,label:l.label},"_se")}),delete s.selfEdges})})}function fNe(t){Ee(t.nodes(),function(e){var r=t.node(e);if(r.dummy==="selfedge"){var n=t.node(r.e.v),i=n.x+n.width/2,a=n.y,s=r.x-i,l=n.height/2;t.setEdge(r.e,r.label),t.removeNode(e),r.label.points=[{x:i+2*s/3,y:a-l},{x:i+5*s/6,y:a-l},{x:i+s,y:a},{x:i+5*s/6,y:a+l},{x:i+2*s/3,y:a+l}],r.label.x=r.x,r.label.y=r.y}})}function pD(t,e){return Pd(Fd(t,e),Number)}function mD(t){var e={};return Ee(t,function(r,n){e[n.toLowerCase()]=r}),e}var WRe,qRe,XRe,jRe,KRe,QRe,ZRe,JRe,Rre=R(()=>{"use strict";Pt();ya();Ate();Nte();qL();JL();fD();nre();kre();Cre();Dre();Ec();o(lo,"layout");o(HRe,"runLayout");o(YRe,"updateInputGraph");WRe=["nodesep","edgesep","ranksep","marginx","marginy"],qRe={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"},XRe=["acyclicer","ranker","rankdir","align"],jRe=["width","height"],KRe={width:0,height:0},QRe=["minlen","weight","width","height","labeloffset"],ZRe={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},JRe=["labelpos"];o(eNe,"buildLayoutGraph");o(tNe,"makeSpaceForEdgeLabels");o(rNe,"injectEdgeLabelProxies");o(nNe,"assignRankMinMax");o(iNe,"removeEdgeLabelProxies");o(aNe,"translateGraph");o(sNe,"assignNodeIntersects");o(oNe,"fixupEdgeLabelCoords");o(lNe,"reversePointsForReversedEdges");o(cNe,"removeBorderNodes");o(uNe,"removeSelfEdges");o(hNe,"insertSelfEdges");o(fNe,"positionSelfEdges");o(pD,"selectNumberAttrs");o(mD,"canonicalize")});var Vd=R(()=>{"use strict";qL();Rre();JL();fD()});function zn(t){var e={options:{directed:t.isDirected(),multigraph:t.isMultigraph(),compound:t.isCompound()},nodes:dNe(t),edges:pNe(t)};return er(t.graph())||(e.value=Qr(t.graph())),e}function dNe(t){return qe(t.nodes(),function(e){var r=t.node(e),n=t.parent(e),i={v:e};return er(r)||(i.value=r),er(n)||(i.parent=n),i})}function pNe(t){return qe(t.edges(),function(e){var r=t.edge(e),n={v:e.v,w:e.w};return er(e.name)||(n.name=e.name),er(r)||(n.value=r),n})}var Pv=R(()=>{"use strict";Pt();Zw();o(zn,"write");o(dNe,"writeNodes");o(pNe,"writeEdges")});var cr,Ud,Mre,Ire,aT,mNe,Ore,Pre,gNe,Bm,Nre,Bre,Fre,zre,Gre,$re=R(()=>{"use strict";ut();ya();Pv();cr=new Map,Ud=new Map,Mre=new Map,Ire=o(()=>{Ud.clear(),Mre.clear(),cr.clear()},"clear"),aT=o((t,e)=>{let r=Ud.get(e)||[];return V.trace("In isDescendant",e," ",t," = ",r.includes(t)),r.includes(t)},"isDescendant"),mNe=o((t,e)=>{let r=Ud.get(e)||[];return V.info("Descendants of ",e," is ",r),V.info("Edge is ",t),t.v===e||t.w===e?!1:r?r.includes(t.v)||aT(t.v,e)||aT(t.w,e)||r.includes(t.w):(V.debug("Tilt, ",e,",not in descendants"),!1)},"edgeInCluster"),Ore=o((t,e,r,n)=>{V.warn("Copying children of ",t,"root",n,"data",e.node(t),n);let i=e.children(t)||[];t!==n&&i.push(t),V.warn("Copying (nodes) clusterId",t,"nodes",i),i.forEach(a=>{if(e.children(a).length>0)Ore(a,e,r,n);else{let s=e.node(a);V.info("cp ",a," to ",n," with parent ",t),r.setNode(a,s),n!==e.parent(a)&&(V.warn("Setting parent",a,e.parent(a)),r.setParent(a,e.parent(a))),t!==n&&a!==t?(V.debug("Setting parent",a,t),r.setParent(a,t)):(V.info("In copy ",t,"root",n,"data",e.node(t),n),V.debug("Not Setting parent for node=",a,"cluster!==rootId",t!==n,"node!==clusterId",a!==t));let l=e.edges(a);V.debug("Copying Edges",l),l.forEach(u=>{V.info("Edge",u);let h=e.edge(u.v,u.w,u.name);V.info("Edge data",h,n);try{mNe(u,n)?(V.info("Copying as ",u.v,u.w,h,u.name),r.setEdge(u.v,u.w,h,u.name),V.info("newGraph edges ",r.edges(),r.edge(r.edges()[0]))):V.info("Skipping copy of edge ",u.v,"-->",u.w," rootId: ",n," clusterId:",t)}catch(f){V.error(f)}})}V.debug("Removing node",a),e.removeNode(a)})},"copy"),Pre=o((t,e)=>{let r=e.children(t),n=[...r];for(let i of r)Mre.set(i,t),n=[...n,...Pre(i,e)];return n},"extractDescendants"),gNe=o((t,e,r)=>{let n=t.edges().filter(u=>u.v===e||u.w===e),i=t.edges().filter(u=>u.v===r||u.w===r),a=n.map(u=>({v:u.v===e?r:u.v,w:u.w===e?e:u.w})),s=i.map(u=>({v:u.v,w:u.w}));return a.filter(u=>s.some(h=>u.v===h.v&&u.w===h.w))},"findCommonEdges"),Bm=o((t,e,r)=>{let n=e.children(t);if(V.trace("Searching children of id ",t,n),n.length<1)return t;let i;for(let a of n){let s=Bm(a,e,r),l=gNe(e,r,s);if(s)if(l.length>0)i=s;else return s}return i},"findNonClusterChild"),Nre=o(t=>!cr.has(t)||!cr.get(t).externalConnections?t:cr.has(t)?cr.get(t).id:t,"getAnchorId"),Bre=o((t,e)=>{if(!t||e>10){V.debug("Opting out, no graph ");return}else V.debug("Opting in, graph ");t.nodes().forEach(function(r){t.children(r).length>0&&(V.warn("Cluster identified",r," Replacement id in edges: ",Bm(r,t,r)),Ud.set(r,Pre(r,t)),cr.set(r,{id:Bm(r,t,r),clusterData:t.node(r)}))}),t.nodes().forEach(function(r){let n=t.children(r),i=t.edges();n.length>0?(V.debug("Cluster identified",r,Ud),i.forEach(a=>{let s=aT(a.v,r),l=aT(a.w,r);s^l&&(V.warn("Edge: ",a," leaves cluster ",r),V.warn("Descendants of XXX ",r,": ",Ud.get(r)),cr.get(r).externalConnections=!0)})):V.debug("Not a cluster ",r,Ud)});for(let r of cr.keys()){let n=cr.get(r).id,i=t.parent(n);i!==r&&cr.has(i)&&!cr.get(i).externalConnections&&(cr.get(r).id=i)}t.edges().forEach(function(r){let n=t.edge(r);V.warn("Edge "+r.v+" -> "+r.w+": "+JSON.stringify(r)),V.warn("Edge "+r.v+" -> "+r.w+": "+JSON.stringify(t.edge(r)));let i=r.v,a=r.w;if(V.warn("Fix XXX",cr,"ids:",r.v,r.w,"Translating: ",cr.get(r.v)," --- ",cr.get(r.w)),cr.get(r.v)||cr.get(r.w)){if(V.warn("Fixing and trying - removing XXX",r.v,r.w,r.name),i=Nre(r.v),a=Nre(r.w),t.removeEdge(r.v,r.w,r.name),i!==r.v){let s=t.parent(i);cr.get(s).externalConnections=!0,n.fromCluster=r.v}if(a!==r.w){let s=t.parent(a);cr.get(s).externalConnections=!0,n.toCluster=r.w}V.warn("Fix Replacing with XXX",i,a,r.name),t.setEdge(i,a,n,r.name)}}),V.warn("Adjusted Graph",zn(t)),Fre(t,0),V.trace(cr)},"adjustClustersAndEdges"),Fre=o((t,e)=>{if(V.warn("extractor - ",e,zn(t),t.children("D")),e>10){V.error("Bailing out");return}let r=t.nodes(),n=!1;for(let i of r){let a=t.children(i);n=n||a.length>0}if(!n){V.debug("Done, no node has children",t.nodes());return}V.debug("Nodes = ",r,e);for(let i of r)if(V.debug("Extracting node",i,cr,cr.has(i)&&!cr.get(i).externalConnections,!t.parent(i),t.node(i),t.children("D")," Depth ",e),!cr.has(i))V.debug("Not a cluster",i,e);else if(!cr.get(i).externalConnections&&t.children(i)&&t.children(i).length>0){V.warn("Cluster without external connections, without a parent and with children",i,e);let s=t.graph().rankdir==="TB"?"LR":"TB";cr.get(i)?.clusterData?.dir&&(s=cr.get(i).clusterData.dir,V.warn("Fixing dir",cr.get(i).clusterData.dir,s));let l=new lr({multigraph:!0,compound:!0}).setGraph({rankdir:s,nodesep:50,ranksep:50,marginx:8,marginy:8}).setDefaultEdgeLabel(function(){return{}});V.warn("Old graph before copy",zn(t)),Ore(i,t,l,i),t.setNode(i,{clusterNode:!0,id:i,clusterData:cr.get(i).clusterData,label:cr.get(i).label,graph:l}),V.warn("New graph after copy node: (",i,")",zn(l)),V.debug("Old graph after copy",zn(t))}else V.warn("Cluster ** ",i," **not meeting the criteria !externalConnections:",!cr.get(i).externalConnections," no parent: ",!t.parent(i)," children ",t.children(i)&&t.children(i).length>0,t.children("D"),e),V.debug(cr);r=t.nodes(),V.warn("New list of nodes",r);for(let i of r){let a=t.node(i);V.warn(" Now next level",i,a),a?.clusterNode&&Fre(a.graph,e+1)}},"extractor"),zre=o((t,e)=>{if(e.length===0)return[];let r=Object.assign([],e);return e.forEach(n=>{let i=t.children(n),a=zre(t,i);r=[...r,...a]}),r},"sorter"),Gre=o(t=>zre(t,t.children()),"sortNodesByHierarchy")});var Ure={};hr(Ure,{render:()=>yNe});var Vre,yNe,Hre=R(()=>{"use strict";Vd();Pv();ya();Q9();ri();$re();tL();X9();K9();ut();_d();_t();Vre=o(async(t,e,r,n,i,a)=>{V.warn("Graph in recursive render:XAX",zn(e),i);let s=e.graph().rankdir;V.trace("Dir in recursive render - dir:",s);let l=t.insert("g").attr("class","root");e.nodes()?V.info("Recursive render XXX",e.nodes()):V.info("No nodes found for",e),e.edges().length>0&&V.info("Recursive edges",e.edge(e.edges()[0]));let u=l.insert("g").attr("class","clusters"),h=l.insert("g").attr("class","edgePaths"),f=l.insert("g").attr("class","edgeLabels"),d=l.insert("g").attr("class","nodes");await Promise.all(e.nodes().map(async function(y){let v=e.node(y);if(i!==void 0){let x=JSON.parse(JSON.stringify(i.clusterData));V.trace(`Setting data for parent cluster XXX
+ Node.id = `,y,`
+ data=`,x.height,`
+Parent cluster`,i.height),e.setNode(i.id,x),e.parent(y)||(V.trace("Setting parent",y,i.id),e.setParent(y,i.id,x))}if(V.info("(Insert) Node XXX"+y+": "+JSON.stringify(e.node(y))),v?.clusterNode){V.info("Cluster identified XBX",y,v.width,e.node(y));let{ranksep:x,nodesep:b}=e.graph();v.graph.setGraph({...v.graph.graph(),ranksep:x+25,nodesep:b});let w=await Vre(d,v.graph,r,n,e.node(y),a),S=w.elem;ar(v,S),v.diff=w.diff||0,V.info("New compound node after recursive render XAX",y,"width",v.width,"height",v.height),lQ(S,v)}else e.children(y).length>0?(V.trace("Cluster - the non recursive path XBX",y,v.id,v,v.width,"Graph:",e),V.trace(Bm(v.id,e)),cr.set(v.id,{id:Bm(v.id,e),node:v})):(V.trace("Node - the non recursive path XAX",y,d,e.node(y),s),await rw(d,e.node(y),s))})),await o(async()=>{let y=e.edges().map(async function(v){let x=e.edge(v.v,v.w,v.name);V.info("Edge "+v.v+" -> "+v.w+": "+JSON.stringify(v)),V.info("Edge "+v.v+" -> "+v.w+": ",v," ",JSON.stringify(e.edge(v))),V.info("Fix",cr,"ids:",v.v,v.w,"Translating: ",cr.get(v.v),cr.get(v.w)),await Q5(f,x)});await Promise.all(y)},"processEdges")(),V.info("Graph before layout:",JSON.stringify(zn(e))),V.info("############################################# XXX"),V.info("### Layout ### XXX"),V.info("############################################# XXX"),lo(e),V.info("Graph after layout:",JSON.stringify(zn(e)));let m=0,{subGraphTitleTotalMargin:g}=io(a);return await Promise.all(Gre(e).map(async function(y){let v=e.node(y);if(V.info("Position XBX => "+y+": ("+v.x,","+v.y,") width: ",v.width," height: ",v.height),v?.clusterNode)v.y+=g,V.info("A tainted cluster node XBX1",y,v.id,v.width,v.height,v.x,v.y,e.parent(y)),cr.get(v.id).node=v,eL(v);else if(e.children(y).length>0){V.info("A pure cluster node XBX1",y,v.id,v.x,v.y,v.width,v.height,e.parent(y)),v.height+=g,e.node(v.parentId);let x=v?.padding/2||0,b=v?.labelBBox?.height||0,w=b-x||0;V.debug("OffsetY",w,"labelHeight",b,"halfPadding",x),await Y5(u,v),cr.get(v.id).node=v}else{let x=e.node(v.parentId);v.y+=g/2,V.info("A regular node XBX1 - using the padding",v.id,"parent",v.parentId,v.width,v.height,v.x,v.y,"offsetY",v.offsetY,"parent",x,x?.offsetY,v),eL(v)}})),e.edges().forEach(function(y){let v=e.edge(y);V.info("Edge "+y.v+" -> "+y.w+": "+JSON.stringify(v),v),v.points.forEach(S=>S.y+=g/2);let x=e.node(y.v);var b=e.node(y.w);let w=J5(h,v,cr,r,x,b,n);Z5(v,w)}),e.nodes().forEach(function(y){let v=e.node(y);V.info(y,v.type,v.diff),v.isGroup&&(m=v.diff)}),V.warn("Returning from recursive render XAX",l,m),{elem:l,diff:m}},"recursiveRender"),yNe=o(async(t,e)=>{let r=new lr({multigraph:!0,compound:!0}).setGraph({rankdir:t.direction,nodesep:t.config?.nodeSpacing||t.config?.flowchart?.nodeSpacing||t.nodeSpacing,ranksep:t.config?.rankSpacing||t.config?.flowchart?.rankSpacing||t.rankSpacing,marginx:8,marginy:8}).setDefaultEdgeLabel(function(){return{}}),n=e.select("g");ew(n,t.markers,t.type,t.diagramId),cQ(),lK(),rK(),Ire(),t.nodes.forEach(a=>{r.setNode(a.id,{...a}),a.parentId&&r.setParent(a.id,a.parentId)}),V.debug("Edges:",t.edges),t.edges.forEach(a=>{if(a.start===a.end){let s=a.start,l=s+"---"+s+"---1",u=s+"---"+s+"---2",h=r.node(s);r.setNode(l,{domId:l,id:l,parentId:h.parentId,labelStyle:"",label:"",padding:0,shape:"labelRect",style:"",width:10,height:10}),r.setParent(l,h.parentId),r.setNode(u,{domId:u,id:u,parentId:h.parentId,labelStyle:"",padding:0,shape:"labelRect",label:"",style:"",width:10,height:10}),r.setParent(u,h.parentId);let f=structuredClone(a),d=structuredClone(a),p=structuredClone(a);f.label="",f.arrowTypeEnd="none",f.id=s+"-cyclic-special-1",d.arrowTypeEnd="none",d.id=s+"-cyclic-special-mid",p.label="",h.isGroup&&(f.fromCluster=s,p.toCluster=s),p.id=s+"-cyclic-special-2",r.setEdge(s,l,f,s+"-cyclic-special-0"),r.setEdge(l,u,d,s+"-cyclic-special-1"),r.setEdge(u,s,p,s+"-cyc
/g),m=t.append("text").classed("er relationshipLabel",!0).attr("id",d).attr("x",f.x).attr("y",f.y).style("text-anchor","middle").style("dominant-baseline","middle").style("font-family",de().fontFamily).style("font-size",Ii.fontSize+"px");if(p.length==1)m.text(e.roleA);else{let y=-(p.length-1)*.5;p.forEach((v,x)=>{m.append("tspan").attr("x",f.x).attr("dy",`${x===0?y:1}em`).text(v)})}let g=m.node().getBBox();t.insert("rect","#"+d).classed("er relationshipLabelBox",!0).attr("x",f.x-g.width/2).attr("y",f.y-g.height/2).attr("width",g.width).attr("height",g.height)},"drawRelationshipFromLayout"),tMe=o(function(t,e,r,n){Ii=de().er,V.info("Drawing ER diagram");let i=de().securityLevel,a;i==="sandbox"&&(a=$e("#i"+e));let l=(i==="sandbox"?$e(a.nodes()[0].contentDocument.body):$e("body")).select(`[id='${e}']`);$o.insertMarkers(l,Ii);let u;u=new lr({multigraph:!0,directed:!0,compound:!1}).setGraph({rankdir:Ii.layoutDirection,marginx:20,marginy:20,nodesep:100,edgesep:100,ranksep:100}).setDefaultEdgeLabel(function(){return{}});let h=QNe(l,n.db.getEntities(),u),f=JNe(n.db.getRelationships(),u);lo(u),ZNe(l,u),f.forEach(function(y){eMe(l,y,u,h,n)});let d=Ii.diagramPadding;Lt.insertTitle(l,"entityTitleText",Ii.titleTopMargin,n.db.getDiagramTitle());let p=l.node().getBBox(),m=p.width+d*2,g=p.height+d*2;Sr(l,g,m,Ii.useMaxWidth),l.attr("viewBox",`${p.x-d} ${p.y-d} ${m} ${g}`)},"draw"),rMe="28e9f9db-3c8d-5aa5-9faf-44286ae5937c";o(nMe,"generateId");o(Tne,"strWithHyphen");Ene={setConf:jNe,draw:tMe}});var iMe,Sne,Ane=R(()=>{"use strict";iMe=o(t=>`
+ .entityBox {
+ fill: ${t.mainBkg};
+ stroke: ${t.nodeBorder};
+ }
+
+ .attributeBoxOdd {
+ fill: ${t.attributeBackgroundColorOdd};
+ stroke: ${t.nodeBorder};
+ }
+
+ .attributeBoxEven {
+ fill: ${t.attributeBackgroundColorEven};
+ stroke: ${t.nodeBorder};
+ }
+
+ .relationshipLabelBox {
+ fill: ${t.tertiaryColor};
+ opacity: 0.7;
+ background-color: ${t.tertiaryColor};
+ rect {
+ opacity: 0.5;
+ }
+ }
+
+ .relationshipLine {
+ stroke: ${t.lineColor};
+ }
+
+ .entityTitleText {
+ text-anchor: middle;
+ font-size: 18px;
+ fill: ${t.textColor};
+ }
+ #MD_PARENT_START {
+ fill: #f5f5f5 !important;
+ stroke: ${t.lineColor} !important;
+ stroke-width: 1;
+ }
+ #MD_PARENT_END {
+ fill: #f5f5f5 !important;
+ stroke: ${t.lineColor} !important;
+ stroke-width: 1;
+ }
+
+`,"getStyles"),Sne=iMe});var _ne={};hr(_ne,{diagram:()=>aMe});var aMe,Lne=R(()=>{"use strict";nne();sne();Cne();Ane();aMe={parser:rne,db:ane,renderer:Ene,styles:Sne}});function Xn(t){return typeof t=="object"&&t!==null&&typeof t.$type=="string"}function xa(t){return typeof t=="object"&&t!==null&&typeof t.$refText=="string"}function ED(t){return typeof t=="object"&&t!==null&&typeof t.name=="string"&&typeof t.type=="string"&&typeof t.path=="string"}function Wd(t){return typeof t=="object"&&t!==null&&Xn(t.container)&&xa(t.reference)&&typeof t.message=="string"}function co(t){return typeof t=="object"&&t!==null&&Array.isArray(t.content)}function ef(t){return typeof t=="object"&&t!==null&&typeof t.tokenType=="object"}function zv(t){return co(t)&&typeof t.fullText=="string"}var Yd,Vo=R(()=>{"use strict";o(Xn,"isAstNode");o(xa,"isReference");o(ED,"isAstNodeDescription");o(Wd,"isLinkingError");Yd=class{static{o(this,"AbstractAstReflection")}constructor(){this.subtypes={},this.allSubtypes={}}isInstance(e,r){return Xn(e)&&this.isSubtype(e.$type,r)}isSubtype(e,r){if(e===r)return!0;let n=this.subtypes[e];n||(n=this.subtypes[e]={});let i=n[r];if(i!==void 0)return i;{let a=this.computeIsSubtype(e,r);return n[r]=a,a}}getAllSubTypes(e){let r=this.allSubtypes[e];if(r)return r;{let n=this.getAllTypes(),i=[];for(let a of n)this.isSubtype(a,e)&&i.push(a);return this.allSubtypes[e]=i,i}}};o(co,"isCompositeCstNode");o(ef,"isLeafCstNode");o(zv,"isRootCstNode")});function cMe(t){return typeof t=="string"?t:typeof t>"u"?"undefined":typeof t.toString=="function"?t.toString():Object.prototype.toString.call(t)}function hT(t){return!!t&&typeof t[Symbol.iterator]=="function"}function Kr(...t){if(t.length===1){let e=t[0];if(e instanceof uo)return e;if(hT(e))return new uo(()=>e[Symbol.iterator](),r=>r.next());if(typeof e.length=="number")return new uo(()=>({index:0}),r=>r.index${l.name}<- can never be matched.
+Because it appears AFTER the Token Type ->${n.name}<-in the lexer's definition.
+See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNREACHABLE`;e.push({message:u,type:Gn.UNREACHABLE_PATTERN,tokenTypes:[n,l]})}})}),e}function dIe(t,e){if(zo(e)){let r=e.exec(t);return r!==null&&r.index===0}else{if(wi(e))return e(t,0,[],{});if(Xe(e,"exec"))return e.exec(t,0,[],{});if(typeof e=="string")return e===t;throw Error("non exhaustive match")}}function pIe(t){return Za([".","\\","[","]","|","^","$","(",")","?","*","+","{"],r=>t.source.indexOf(r)!==-1)===void 0}function mie(t){let e=t.ignoreCase?"i":"";return new RegExp(`^(?:${t.source})`,e)}function gie(t){let e=t.ignoreCase?"iy":"y";return new RegExp(`${t.source}`,e)}function xie(t,e,r){let n=[];return Xe(t,Qm)||n.push({message:"A MultiMode Lexer cannot be initialized without a <"+Qm+`> property in its definition
+`,type:Gn.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE}),Xe(t,GT)||n.push({message:"A MultiMode Lexer cannot be initialized without a <"+GT+`> property in its definition
+`,type:Gn.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY}),Xe(t,GT)&&Xe(t,Qm)&&!Xe(t.modes,t.defaultMode)&&n.push({message:`A MultiMode Lexer cannot be initialized with a ${Qm}: <${t.defaultMode}>which does not exist
+`,type:Gn.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST}),Xe(t,GT)&&Ee(t.modes,(i,a)=>{Ee(i,(s,l)=>{if(er(s))n.push({message:`A Lexer cannot be initialized using an undefined Token Type. Mode:<${a}> at index: <${l}>
+`,type:Gn.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED});else if(Xe(s,"LONGER_ALT")){let u=wt(s.LONGER_ALT)?s.LONGER_ALT:[s.LONGER_ALT];Ee(u,h=>{!er(h)&&!Fn(i,h)&&n.push({message:`A MultiMode Lexer cannot be initialized with a longer_alt <${h.name}> on token <${s.name}> outside of mode <${a}>
+`,type:Gn.MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE})})}})}),n}function bie(t,e,r){let n=[],i=!1,a=wc(Gr(or(t.modes))),s=Kh(a,u=>u[a0]===ni.NA),l=Cie(r);return e&&Ee(s,u=>{let h=Eie(u,l);if(h!==!1){let d={message:gIe(u,h),type:h.issue,tokenType:u};n.push(d)}else Xe(u,"LINE_BREAKS")?u.LINE_BREAKS===!0&&(i=!0):zT(l,u.PATTERN)&&(i=!0)}),e&&!i&&n.push({message:`Warning: No LINE_BREAKS Found.
+ This Lexer has been defined to track line and column information,
+ But none of the Token Types can be identified as matching a line terminator.
+ See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#LINE_BREAKS
+ for details.`,type:Gn.NO_LINE_BREAKS_FLAGS}),n}function wie(t){let e={},r=Dr(t);return Ee(r,n=>{let i=t[n];if(wt(i))e[n]=[];else throw Error("non exhaustive match")}),e}function Tie(t){let e=t.PATTERN;if(zo(e))return!1;if(wi(e))return!0;if(Xe(e,"exec"))return!0;if(di(e))return!1;throw Error("non exhaustive match")}function mIe(t){return di(t)&&t.length===1?t.charCodeAt(0):!1}function Eie(t,e){if(Xe(t,"LINE_BREAKS"))return!1;if(zo(t.PATTERN)){try{zT(e,t.PATTERN)}catch(r){return{issue:Gn.IDENTIFY_TERMINATOR,errMsg:r.message}}return!1}else{if(di(t.PATTERN))return!1;if(Tie(t))return{issue:Gn.CUSTOM_LINE_BREAK};throw Error("non exhaustive match")}}function gIe(t,e){if(e.issue===Gn.IDENTIFY_TERMINATOR)return`Warning: unable to identify line terminator usage in pattern.
+ The problem is in the <${t.name}> Token Type
+ Root cause: ${e.errMsg}.
+ For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#IDENTIFY_TERMINATOR`;if(e.issue===Gn.CUSTOM_LINE_BREAK)return`Warning: A Custom Token Pattern should specify the u.length){u=s,h=f,ie=ce;break}}}break}}if(u!==null){if(d=u.length,p=ie.group,p!==void 0&&(m=ie.tokenTypeIdx,g=this.createTokenInstance(u,T,m,ie.tokenType,M,N,d),this.handlePayload(g,h),p===!1?E=this.addToken(A,E,g):k[p].push(g)),e=this.chopInput(e,d),T=T+d,N=this.computeNewColumn(N,d),I===!0&&ie.canLineTerminator===!0){let q=0,K,se;C.lastIndex=0;do K=C.test(u),K===!0&&(se=C.lastIndex-1,q++);while(K===!0);q!==0&&(M=M+q,N=d-se,this.updateTokenEndLineColumnLocation(g,p,se,q,M,N,d))}this.handleModes(ie,Q,X,g)}else{let q=T,K=M,se=N,ce=j===!1;for(;ce===!1&&T{"use strict";Pt();i2();s0();o(zu,"tokenLabel");o(gN,"hasTokenLabel");EIe="parent",Rie="categories",Nie="label",Mie="group",Iie="push_mode",Oie="pop_mode",Pie="longer_alt",Bie="line_breaks",Fie="start_chars_hint";o(VT,"createToken");o(CIe,"createTokenInternal");fo=VT({name:"EOF",pattern:ni.NA});Fu([fo]);o(o0,"createTokenInstance");o(s2,"tokenMatcher")});var Gu,zie,Ol,Jm=R(()=>{"use strict";l0();Pt();ns();Gu={buildMismatchTokenMessage({expected:t,actual:e,previous:r,ruleName:n}){return`Expecting ${gN(t)?`--> ${zu(t)} <--`:`token of type --> ${t.name} <--`} but found --> '${e.image}' <--`},buildNotAllInputParsedMessage({firstRedundant:t,ruleName:e}){return"Redundant input, expecting EOF but found: "+t.image},buildNoViableAltMessage({expectedPathsPerAlt:t,actual:e,previous:r,customUserDescription:n,ruleName:i}){let a="Expecting: ",l=`
+but found: '`+na(e).image+"'";if(n)return a+n+l;{let u=Vr(t,(p,m)=>p.concat(m),[]),h=qe(u,p=>`[${qe(p,m=>zu(m)).join(", ")}]`),d=`one of these possible Token sequences:
+${qe(h,(p,m)=>` ${m+1}. ${p}`).join(`
+`)}`;return a+d+l}},buildEarlyExitMessage({expectedIterationPaths:t,actual:e,customUserDescription:r,ruleName:n}){let i="Expecting: ",s=`
+but found: '`+na(e).image+"'";if(r)return i+r+s;{let u=`expecting at least one iteration which starts with one of these possible Token sequences::
+ <${qe(t,h=>`[${qe(h,f=>zu(f)).join(",")}]`).join(" ,")}>`;return i+u+s}}};Object.freeze(Gu);zie={buildRuleNotFoundError(t,e){return"Invalid grammar, reference to a rule which is not defined: ->"+e.nonTerminalName+`<-
+inside top level rule: ->`+t.name+"<-"}},Ol={buildDuplicateFoundError(t,e){function r(f){return f instanceof fr?f.terminalType.name:f instanceof Zr?f.nonTerminalName:""}o(r,"getExtraProductionArgument");let n=t.name,i=na(e),a=i.idx,s=Rs(i),l=r(i),u=a>0,h=`->${s}${u?a:""}<- ${l?`with argument: ->${l}<-`:""}
+ appears more than once (${e.length} times) in the top level rule: ->${n}<-.
+ For further details see: https://chevrotain.io/docs/FAQ.html#NUMERICAL_SUFFIXES
+ `;return h=h.replace(/[ \t]+/g," "),h=h.replace(/\s\s+/g,`
+`),h},buildNamespaceConflictError(t){return`Namespace conflict found in grammar.
+The grammar has both a Terminal(Token) and a Non-Terminal(Rule) named: <${t.name}>.
+To resolve this make sure each Terminal and Non-Terminal names are unique
+This is easy to accomplish by using the convention that Terminal names start with an uppercase letter
+and Non-Terminal names start with a lower case letter.`},buildAlternationPrefixAmbiguityError(t){let e=qe(t.prefixPath,i=>zu(i)).join(", "),r=t.alternation.idx===0?"":t.alternation.idx;return`Ambiguous alternatives: <${t.ambiguityIndices.join(" ,")}> due to common lookahead prefix
+in
/gi):t).forEach(n=>{let i=document.createElementNS("http://www.w3.org/2000/svg","tspan");i.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),i.setAttribute("dy","1em"),i.setAttribute("x","0"),i.setAttribute("class","row"),i.textContent=n.trim(),e.appendChild(i)}),e},"drawText"),Ile=o(t=>{let e,r,n;return Tr==="BT"?(r=o((i,a)=>i<=a,"comparisonFunc"),n=1/0):(r=o((i,a)=>i>=a,"comparisonFunc"),n=0),t.forEach(i=>{let a=Tr==="TB"||Tr=="BT"?Os.get(i)?.y:Os.get(i)?.x;a!==void 0&&r(a,n)&&(e=i,n=a)}),e},"findClosestParent"),fBe=o(t=>{let e="",r=1/0;return t.forEach(n=>{let i=Os.get(n).y;i<=r&&(e=n,r=i)}),e||void 0},"findClosestParentBT"),dBe=o((t,e,r)=>{let n=r,i=r,a=[];t.forEach(s=>{let l=e.get(s);if(!l)throw new Error(`Commit not found for key ${s}`);l.parents.length?(n=mBe(l),i=Math.max(n,i)):a.push(l),gBe(l,n)}),n=i,a.forEach(s=>{yBe(s,n,r)}),t.forEach(s=>{let l=e.get(s);if(l?.parents.length){let u=fBe(l.parents);n=Os.get(u).y-df,n<=i&&(i=n);let h=Is.get(l.branch).pos,f=n-ff;Os.set(l.id,{x:h,y:f})}})},"setParallelBTPos"),pBe=o(t=>{let e=Ile(t.parents.filter(n=>n!==null));if(!e)throw new Error(`Closest parent not found for commit ${t.id}`);let r=Os.get(e)?.y;if(r===void 0)throw new Error(`Closest parent position not found for commit ${t.id}`);return r},"findClosestParentPos"),mBe=o(t=>pBe(t)+df,"calculateCommitPosition"),gBe=o((t,e)=>{let r=Is.get(t.branch);if(!r)throw new Error(`Branch not found for commit ${t.id}`);let n=r.pos,i=e+ff;return Os.set(t.id,{x:n,y:i}),{x:n,y:i}},"setCommitPosition"),yBe=o((t,e,r)=>{let n=Is.get(t.branch);if(!n)throw new Error(`Branch not found for commit ${t.id}`);let i=e+r,a=n.pos;Os.set(t.id,{x:a,y:i})},"setRootPosition"),vBe=o((t,e,r,n,i,a)=>{if(a===Hr.HIGHLIGHT)t.append("rect").attr("x",r.x-10).attr("y",r.y-10).attr("width",20).attr("height",20).attr("class",`commit ${e.id} commit-highlight${i%E0} ${n}-outer`),t.append("rect").attr("x",r.x-6).attr("y",r.y-6).attr("width",12).attr("height",12).attr("class",`commit ${e.id} commit${i%E0} ${n}-inner`);else if(a===Hr.CHERRY_PICK)t.append("circle").attr("cx",r.x).attr("cy",r.y).attr("r",10).attr("class",`commit ${e.id} ${n}`),t.append("circle").attr("cx",r.x-3).attr("cy",r.y+2).attr("r",2.75).attr("fill","#fff").attr("class",`commit ${e.id} ${n}`),t.append("circle").attr("cx",r.x+3).attr("cy",r.y+2).attr("r",2.75).attr("fill","#fff").attr("class",`commit ${e.id} ${n}`),t.append("line").attr("x1",r.x+3).attr("y1",r.y+1).attr("x2",r.x).attr("y2",r.y-5).attr("stroke","#fff").attr("class",`commit ${e.id} ${n}`),t.append("line").attr("x1",r.x-3).attr("y1",r.y+1).attr("x2",r.x).attr("y2",r.y-5).attr("stroke","#fff").attr("class",`commit ${e.id} ${n}`);else{let s=t.append("circle");if(s.attr("cx",r.x),s.attr("cy",r.y),s.attr("r",e.type===Hr.MERGE?9:10),s.attr("class",`commit ${e.id} commit${i%E0}`),a===Hr.MERGE){let l=t.append("circle");l.attr("cx",r.x),l.attr("cy",r.y),l.attr("r",6),l.attr("class",`commit ${n} ${e.id} commit${i%E0}`)}a===Hr.REVERSE&&t.append("path").attr("d",`M ${r.x-5},${r.y-5}L${r.x+5},${r.y+5}M${r.x-5},${r.y+5}L${r.x+5},${r.y-5}`).attr("class",`commit ${n} ${e.id} commit${i%E0}`)}},"drawCommitBullet"),xBe=o((t,e,r,n)=>{if(e.type!==Hr.CHERRY_PICK&&(e.customId&&e.type===Hr.MERGE||e.type!==Hr.MERGE)&&Ko?.showCommitLabel){let i=t.append("g"),a=i.insert("rect").attr("class","commit-label-bkg"),s=i.append("text").attr("x",n).attr("y",r.y+25).attr("class","commit-label").text(e.id),l=s.node()?.getBBox();if(l&&(a.attr("x",r.posWithOffset-l.width/2-Hu).attr("y",r.y+13.5).attr("width",l.width+2*Hu).attr("height",l.height+2*Hu),Tr==="TB"||Tr==="BT"?(a.attr("x",r.x-(l.width+4*Oc+5)).attr("y",r.y-12),s.attr("x",r.x-(l.width+4*Oc)).attr("y",r.y+l.height-12)):s.attr("x",r.posWithOffset-l.width/2),Ko.rotateCommitLabel))if(Tr==="TB"||Tr==="BT")s.attr("transform","rotate(-45, "+r.x+", "+r.y+")"),a.attr("transform","rotate(-45, "+r.x+", "+r.y+")");else{let u=-7.5-(l.width+10)/25*9.5,h=10+l.width/25*8.5;i.attr("transform","translate("+u+", "+h+") rotate(-45, "+n+", "+r.y+")")}}},"drawCommitLabel"),bBe=o((t,e,r,n)=>{if(e.tags.length>0){let i=0,a=0,s=0,l=[];for(let u of e.tags.reverse()){let h=t.insert("polygon"),f=t.append("circle"),d=t.append("text").attr("y",r.y-16-i).attr("class","tag-label").text(u),p=d.node()?.getBBox();if(!p)throw new Error("Tag bbox not found");a=Math.max(a,p.width),s=Math.max(s,p.height),d.attr("x",r.posWithOffset-p.width/2),l.push({tag:d,hole:f,rect:h,yOffset:i}),i+=20}for(let{tag:u,hole:h,rect:f,yOffset:d}of l){let p=s/2,m=r.y-19.2-d;if(f.attr("class","tag-label-bkg").attr("points",`
+ ${n-a/2-Oc/2},${m+Hu}
+ ${n-a/2-Oc/2},${m-Hu}
+ ${r.posWithOffset-a/2-Oc},${m-p-Hu}
+ ${r.posWithOffset+a/2+Oc},${m-p-Hu}
+ ${r.posWithOffset+a/2+Oc},${m+p+Hu}
+ ${r.posWithOffset-a/2-Oc},${m+p+Hu}`),h.attr("cy",m).attr("cx",n-a/2+Oc/2).attr("r",1.5).attr("class","tag-hole"),Tr==="TB"||Tr==="BT"){let g=n+d;f.attr("class","tag-label-bkg").attr("points",`
+ ${r.x},${g+2}
+ ${r.x},${g-2}
+ ${r.x+ff},${g-p-2}
+ ${r.x+ff+a+4},${g-p-2}
+ ${r.x+ff+a+4},${g+p+2}
+ ${r.x+ff},${g+p+2}`).attr("transform","translate(12,12) rotate(45, "+r.x+","+n+")"),h.attr("cx",r.x+Oc/2).attr("cy",g).attr("transform","translate(12,12) rotate(45, "+r.x+","+n+")"),u.attr("x",r.x+5).attr("y",g+3).attr("transform","translate(14,14) rotate(45, "+r.x+","+n+")")}}}},"drawCommitTags"),wBe=o(t=>{switch(t.customType??t.type){case Hr.NORMAL:return"commit-normal";case Hr.REVERSE:return"commit-reverse";case Hr.HIGHLIGHT:return"commit-highlight";case Hr.MERGE:return"commit-merge";case Hr.CHERRY_PICK:return"commit-cherry-pick";default:return"commit-normal"}},"getCommitClassType"),TBe=o((t,e,r,n)=>{let i={x:0,y:0};if(t.parents.length>0){let a=Ile(t.parents);if(a){let s=n.get(a)??i;return e==="TB"?s.y+df:e==="BT"?(n.get(t.id)??i).y-df:s.x+df}}else return e==="TB"?tE:e==="BT"?(n.get(t.id)??i).y-df:0;return 0},"calculatePosition"),kBe=o((t,e,r)=>{let n=Tr==="BT"&&r?e:e+ff,i=Tr==="TB"||Tr==="BT"?n:Is.get(t.branch)?.pos,a=Tr==="TB"||Tr==="BT"?Is.get(t.branch)?.pos:n;if(a===void 0||i===void 0)throw new Error(`Position were undefined for commit ${t.id}`);return{x:a,y:i,posWithOffset:n}},"getCommitPosition"),Nle=o((t,e,r)=>{if(!Ko)throw new Error("GitGraph config not found");let n=t.append("g").attr("class","commit-bullets"),i=t.append("g").attr("class","commit-labels"),a=Tr==="TB"||Tr==="BT"?tE:0,s=[...e.keys()],l=Ko?.parallelCommits??!1,u=o((f,d)=>{let p=e.get(f)?.seq,m=e.get(d)?.seq;return p!==void 0&&m!==void 0?p-m:0},"sortKeys"),h=s.sort(u);Tr==="BT"&&(l&&dBe(h,e,a),h=h.reverse()),h.forEach(f=>{let d=e.get(f);if(!d)throw new Error(`Commit not found for key ${f}`);l&&(a=TBe(d,Tr,a,Os));let p=kBe(d,a,l);if(r){let m=wBe(d),g=d.customType??d.type,y=Is.get(d.branch)?.index??0;vBe(n,d,p,m,y,g),xBe(i,d,p,a),bBe(i,d,p,a)}Tr==="TB"||Tr==="BT"?Os.set(d.id,{x:p.x,y:p.posWithOffset}):Os.set(d.id,{x:p.posWithOffset,y:p.y}),a=Tr==="BT"&&l?a+df:a+df+ff,a>hf&&(hf=a)})},"drawCommits"),EBe=o((t,e,r,n,i)=>{let s=(Tr==="TB"||Tr==="BT"?r.x