In Midnight Protocol's user content tools, you can easily create networks that feature the standard gameplay with nodes, connections, ICE and programs, and so forth. You can also go a step further and give your network unique, custom behaviour by scripting various nodes or the overall mission controller.
Midnight Protocol makes use of Pinion, a custom scripting language designed to strike a balance between easy-to-read syntax and flexibility. If you have so much as a passing familiarity with programming in things like Python, Java, Go, or pretty much anything, it should not present you with much of a challenge.
Read on for a basic overview of the scripting language, along with an overview of all game functionality you can interact with.
All scripts on a network are attached to particular events. If this event comes to pass, the associated script will be run.
Event | Notes |
---|---|
On Map Load | Called when the network starts. Useful for setting up some things, adding objectives, or showing some kind of intro. |
On Enter | Present on each node type. Triggers each time the player enters this node. |
On Download | Data node only. Triggers when the data node's contents have finished downloading. |
Chose Option 1 | System node only. Triggers when the player selects the first dialog option. By default, the system node is disabled after this. |
Chose Option 2 | System node only. Triggers when the player selects the second dialog option. By default, the system node is disabled after this. |
Chose Option 3 | System node only. Triggers when the player selects the third dialog option. By default, the system node is disabled after this. |
On Open | Vault node only. Triggers when the vault is opened. |
On Drained | Financial node only. Triggers when all credits have been drained. |
Once triggered, a script runs to completion instantly before the rest of the game continues, unless you specifically design it to behave otherwise.
Pinion scripts are executed line-per-line, top to bottom. Ideally, you restrict yourself to one instruction per line, though this is not strictly required. Other than line breaks, whitespace (spaces, tabs, empty lines, ...) is completely ignored.
For the sake of legibility, you might want to indent certain blocks of code (as is common in most programming languages), but this is otherwise syntactically meaningless.
ShowPopup("This is a script. This line will run first.")
ShowText("Then this one.")
if (GetCredits() > 50)
ShowPopup("Finally, we'll see this popup, but only if the player has more than 50 credits.")
endif
Comments are bits of text in the script that are not part of the actual code. They are handy to leave notes for yourself or others. To make a comment, put two forward slashes ( //
) at the beginning of a line. The entire line will be ignored by the script compiler.
// This popup will only show if the player has more than 50 credits.
// Comments can cover multiple lines too!
if (GetCredits() > 50)
ShowPopup("You have over 50 credits!")
endif
Comments can also follow an expression, like so:
if (GetCredits() > 50) // Let's see if the player has enough money
ShowPopup("You have over 50 credits!")
endif
In Pinion all information you handle is typed. Any bit of data is of a particular type and cannot be interpreted as some other type unless you explicitly convert it first. Trying to perform an action on the wrong type will create a compilation error and your script will not be able to run. Pinion supports four types:
Type | Notes | Valid representations |
---|---|---|
bool |
Boolean value. True or false, yes or no, correct or incorrect... | true , false |
int |
Integer. a negative or positive integer number. | 0, 1, 21457, -15, -7841, ... |
float |
Floating point number. Simply put, a negative or positive number that allows decimal values. The point '.' is the only accepted decimal separator. Can always be styled with an 'f' at the end to distinguish it from an int in the case of round numbers. | 1.5, 24.7135, -0.0047, 8f, -16f, ... |
string |
String. A string of characters, AKA a bit of text. Always has to be surrounded by "quotation marks". | "Hello World", "abcdefg", "ERROR", ... |
Note: there are technical limitations (e.g. minimum/maximum values, accuracy) for most of these. For the sake of brevity, such finer details have been omitted. You are unlikely to hit these limits in typical scenarios.
Pinion supports variables, bits of memory with a meaningful name in which you can store a value and retrieve it for later use. Variable name always begin with the dollar sign character ( $
) and must consist of alphanumeric (upper or lower case) characters only, e.g. $hasDownloadedData
, $playerScore
, $secondLeft
, $searchTerm
, ...
Variables can be used in the place of in-line values, but must similarly match the expected type. E.g.
ShowPopup($popupMessage)
AddCredits($playerProfit)
SetConnectionActive($connectionFrom, $connectionTo, $connectionActive)
Before you can reference a variable, it has to be declared to assign it a name and type. Declaring a variable is done with the declare
command, which takes three comma-separated arguments: 1) the type, 2) the variable name, 3) an optional starting value. These are valid variable declarations:
declare(string, $popupMessage, "Good job!")
declare(string, $popupMessage)
declare(bool, $connectionActive, true)
declare(bool, $connectionActive)
declare(int, $playerProfit, 114)
declare(int, $playerProfit)
declare(float, $timer, 0.0f)
declare(float, $timer)
After declaring the variable, you can assign it a new value with the set
command, which takes two comma-separated arguments: 1) the variable name, 2) the new value. Obviously, the new value can in itself be a complex statement. These are all valid variable assignments:
set($popupMessage, "Failed! Try again!")
set($popupMessage, "You looted " + ToString($credits) + " credits!")
set($connectionActive, true)
set($connectionActive, GetCredits() > 50)
set(int, $suspicionLevel, 3)
set(int, $suspicionLevel, (GetTrace() / 2) + 1)
Control statements can be used to direct the behavior of a script, by only running instructions under certain conditions, or running them repeatedly. They come in two varieties.
The if
statement allow you to only execute certain behaviour if a particular condition is met (true). If the condition is not met (false), the script will immediately skip to the corresponding endif
. An if
statement must be followed by the condition between parentheses. The condition must be a bool, or an expression that resolves to a bool. An if
must always be closed by an endif
. These are all valid if
statements:
if ($playerIsLoggedIn)
ShowPopup("Welcome to the secret part of the network.")
endif
if (GetCredits() < 50)
ShowPopup("You must have at least 50 credits to proceed!")
endif
if
statements can be nested. Each if
still needs to be closed with a corresponding endif
:
if ($playerIsLoggedIn)
ShowPopup("Welcome to the secret part of the network.")
if (GetCredits() < 50)
ShowPopup("To continue further, you must have at least 50 credits.")
endif
endif
The else
statement can be used to define logic that should run only when the condition is not met:
if ($playerIsLoggedIn)
ShowPopup("Welcome to the secret part of the network.")
else
ShowPopup("ERROR! You are not logged in!")
endif
Finally, the elseif
statement can be used to test some other condition if the first one failed. elseif
must always precede any else
statement.
if ($playerIsLoggedIn)
ShowPopup("Welcome to the secret part of the network.")
elseif (GetCredits() > 500)
ShowPopup("You are not logged in, but we'll make an exception since you're very rich.")
elseif (GetTrace > 8)
ShowPopup("You are not logged in, but you don't look like you have time to waste.")
else
ShowPopup("ERROR! You are not logged in!")
endif
The while
statement creates a loop. It repeats a part of the script as long as a certain condition is met (true). When the condition is no longer met (false), the script will break out of its loop and skip to the corresponding endwhile
. A while
statement must be followed by the condition between parentheses. The condition must be a bool, or an expression that resolves to a bool. A while
must always be closed by an endwhile
.
These are all valid while
statements:
while (GetTrace() < 8)
ShowText("Angered the network god!")
IncreaseTrace()
endwhile
while (!InputFieldSubmitted("searchTerm")) // wait for the player to submit something
Sleep() // continue script here next frame
endwhile
CAUTION: It's common programming knowledge that while-loops can easily trip you up. If you somehow create a situation where the test condition remains true
forever, your script will be stuck in the loop indefinitely, effectively freezing the game. To prevent this, a script that is taking a suspiciously long time will be forcibly stopped after a few seconds. Only use while-loops if you know what you are doing.