-
-
Notifications
You must be signed in to change notification settings - Fork 0
Creating your first Cathode script
CathodeLib makes it easy to develop custom scripts for Alien: Isolation in C#, by allowing access to the game's internal "Cathode scripting system".
Setting up a project with CathodeLib is simple:
- Create a new C# .NET Framework project in Visual Studio (if you haven't already)
- Right click on the project and select "Manage NuGet Packages"
- Click "Browse" and search for "CathodeLib", then click "Install"
- Navigate to your CS file, and at the top, add
using CATHODE.Scripting
Lets write some code! In this example, we'll create a brand new commands file, and a script within it to show an objective when our level has loaded.
-
Create your Commands file:
Commands commands = new Commands("COMMANDS.PAK");
-
Create your first script (called a Composite):
-
Composite composite = commands.AddComposite("My Cool Script", true);
- The first parameter names the script "My Cool Script".
- The second parameter sets this script as the one we'll run first when we load our level (the root).
-
-
You can now add entities to your composite to perform logic in-game:
- For this example, we want to show an objective when the level starts up - so let's add some Function entities to our composite:
-
FunctionEntity checkpoint = composite.AddFunction(FunctionType.Checkpoint);
- The
Checkpoint
Function entity will allow us to trigger other entities when it itself is triggered.
- The
-
FunctionEntity objective = composite.AddFunction(FunctionType.SetPrimaryObjective);
- The
SetPrimaryObjective
function will show an objective when triggered.
- The
-
- For this example, we want to show an objective when the level starts up - so let's add some Function entities to our composite:
-
With your entities added, you can now give them parameters to customise them:
- Continuing with the objective example, let's add parameters to our
Checkpoint
Function entity:-
checkpoint.AddParameter("is_first_checkpoint", new cBool(true));
- The parameter
is_first_checkpoint
tells the game that ourCheckpoint
should be triggered when the level starts up. - The datatype of the parameter is boolean, which we define as a
cBool
- various other types are available, such ascString
, which we'll use next!
- The parameter
-
checkpoint.AddParameter("section", new cString("Entry"));
- The parameter
section
gives ourCheckpoint
a name - in this case, we're calling it "Entry".
- The parameter
-
- Now, let's customise our objective by giving our
SetPrimaryObjective
Function some parameters:-
objective.AddParameter("title", new cString("Do Something!"));
- Here we're giving our objective a title - this can be a regular string, or a localisation ID.
-
objective.AddParameter("additional_info", new cString("Hey, you should go and do something!"));
- Now, we give our objective a description, which players can view when they open the TAB menu.
-
- Continuing with the objective example, let's add parameters to our
-
Add links between the entities:
- Entities can be linked via their parameters, just like how nodes are linked within Blueprint in Unreal Engine. This allows entities to share data, or simply just trigger eachother when events occur. For our example here, we'll trigger our objective when our
Checkpoint
has loaded.-
checkpoint.AddParameterLink("finished_loading", objective, "trigger");
-
finished_loading
is the parameter ourCheckpoint
entity activates when it has loaded, andtrigger
is the parameter on ourSetPrimaryObjective
entity which causes it to activate, setting our objective and displaying the popup in-game.
-
-
- Entities can be linked via their parameters, just like how nodes are linked within Blueprint in Unreal Engine. This allows entities to share data, or simply just trigger eachother when events occur. For our example here, we'll trigger our objective when our
-
Save your Commands file:
commands.Save();
Our code is complete! Run your application and a "COMMANDS.PAK" file should be generated in its working directory.
To run our commands in-game, copy the generated PAK file into an Alien: Isolation level's "WORLD" folder (I'm gonna use BSP_TORRENS
) and launch the game to that level - you can do this easily by using OpenCAGE's "Launch Game" functionality. You should see your objective pop up when the level loads!
//Create our Commands file to contain our scripts
Commands commands = new Commands("COMMANDS.PAK");
//Create our first script (a "Composite") and give it a name
Composite composite = commands.AddComposite("My Cool Script", true);
//Add a "Checkpoint" function to our script
FunctionEntity checkpoint = composite.AddFunction(FunctionType.Checkpoint);
//Let the game know we want to load in to our checkpoint
checkpoint.AddParameter("is_first_checkpoint", new cBool(true));
//Give our checkpoint a name
checkpoint.AddParameter("section", new cString("Entry"));
//Add a "SetPrimaryObjective" function to our script
FunctionEntity objective = composite.AddFunction(FunctionType.SetPrimaryObjective);
//Give our objective a title (visible in the initial popup, and tab menu)
objective.AddParameter("title", new cString("Do Something!"));
//Give our objective a description (visible in the tab menu)
objective.AddParameter("additional_info", new cString("Hey, you should go and do something!"));
//Trigger our objective when our checkpoint has finished loading
checkpoint.AddParameterLink("finished_loading", objective, "trigger");
//Save the Commands file
commands.Save();
Starting from scratch is cool, but why not utilise existing scripts, written by the game's developers?
CathodeLib supports loading existing commands files, so instead of creating our own, lets open an existing one and add our own script to it!
-
Load the commands file:
-
Commands commands = new Commands("Alien Isolation/DATA/ENV/PRODUCTION/ENG_ALIEN_NEST/WORLD/COMMANDS.PAK");
- Just like before, we create our
Commands
object, however this time we're loading fromENG_ALIEN_NEST
.
- Just like before, we create our
-
-
We'll make a new composite again to write our script in, but we could just as easily edit an existing composite:
-
Composite composite = commands.AddComposite("My Cool Script", true);
- Just like before, we specify
true
on the second parameter to set this composite as the one that runs when the level first starts up. This is especially important in this example, as we'll want to override the composite that is already set to run on startup. It didn't matter so much before when there were no others in the commands file.
- Just like before, we specify
-
-
Because we're loading a commands file with existing Composites, we can take advantage of that, and instance them:
- For this example, lets spawn a player, using the pre-made composite which provides that functionality:
-
FunctionEntity checkpoint = composite.AddFunction(FunctionType.Checkpoint);
- Just like before, we'll use the
Checkpoint
function to trigger our logic on level load.
- Just like before, we'll use the
-
FunctionEntity playerSpawn = composite.AddFunction(commands.GetComposite("ARCHETYPES\\SCRIPT\\MISSION\\SPAWNPOSITIONSELECT"));
- Now, we instance the pre-defined
SPAWNPOSITIONSELECT
script which is already in the commands file, to use as a function.
- Now, we instance the pre-defined
-
- For this example, lets spawn a player, using the pre-made composite which provides that functionality:
-
Just like before, lets add parameters to our
Checkpoint
function so it knows to execute on load:checkpoint.AddParameter("is_first_checkpoint", new cBool(true));
checkpoint.AddParameter("section", new cString("Entry"));
-
Now, lets add a link to trigger our player spawner composite instance when the checkpoint is called:
checkpoint.AddParameterLink("finished_loading", playerSpawn, "SpawnPlayer");
-
Save your Commands file:
commands.Save();
Now, run your application again and your ENG_ALIEN_NEST
commands file should be updated.
Launch the level, just like you did before, and see the player spawn! You should have no weapons and no environment. You may still have some models floating around, as levels are populated by a mixture of commands scripts and "mover" data, the latter we'll get on to in another tutorial. You should also still have collision, as collision for the level is baked to a Havok collision file. You can instance your own CollisionBarrier
function entities to add custom colliders.
//This time, we load an existing Commands file, from the game's ENG_ALIEN_NEST level
Commands commands = new Commands("Alien Isolation/DATA/ENV/PRODUCTION/ENG_ALIEN_NEST/WORLD/COMMANDS.PAK");
//Create our new composite and set it as the one that loads first in the level's Commands
Composite composite = commands.AddComposite("My Cool Script", true);
//Create our checkpoint just like last time to act on the level entry, and apply its parameters
FunctionEntity checkpoint = composite.AddFunction(FunctionType.Checkpoint);
checkpoint.AddParameter("is_first_checkpoint", new cBool(true));
checkpoint.AddParameter("section", new cString("Entry"));
//Since we loaded in our Commands file, we can grab composites that already exist within it
Composite spawnPositionSelect = commands.GetComposite("ARCHETYPES\\SCRIPT\\MISSION\\SPAWNPOSITIONSELECT");
//We can then create function entities that instance these composites to execute their functionality
FunctionEntity playerSpawn = composite.AddFunction(spawnPositionSelect);
//This particular composite implements a public variable called SpawnPlayer which spawns the player
//Lets link to that public variable off of our checkpoint when it finishes loading
checkpoint.AddParameterLink("finished_loading", playerSpawn, "SpawnPlayer");
//Save the Commands out back to ENG_ALIEN_NEST
commands.Save();
You can now continue on to build up more advanced scripts, for example - spawning objects and moving them around, spawning particle systems, and giving weapons/items to the player. Have fun!
If you'd like to understand more about the basics of the Cathode scripting system, check out this Wiki page!
As mentioned above, for easy testing going forward, you can use OpenCAGE's "Launch Game" functionality, which allows you to load directly into a level, with support for custom levels outside of the ones that ship with the game. This also optionally supports "hot reloading", which allows you to press the "INSERT" key at runtime to reload a level - super useful for testing script changes without restarting the game!
You can check out the OpenCAGE Wiki to view a list of all entities and parameters that you can utilise through Commands
.
Entities can optionally be created with all their known parameters (not including inherited parameters from interfaces, listed in the above links). Be aware though, this will add ALL parameter defaults (including link placeholders) which may not be ideal. You may want to remove parameters with ParameterVariant
values that do not match ParameterVariant.PARAMETER
after creation (when using the autopopulate option) to avoid inadvertently triggering events.
As the game is built in release mode, debugging is limited, and as such any incorrect configurations in your scripts may, on occasion, hard-crash the game (although the scripting system is very forgiving). Be patient!