-
-
Notifications
You must be signed in to change notification settings - Fork 15
InkCPP Commands
Commands (or instructions) tell the inkcpp interpreter what to do. Many of the commands in inkcpp are analogous to commands in the Ink JSON format, but there are a few special additions to maintain ink functionality in the binary format. All the currently supported commands are enumerated in command.h.
Commands are encoded in binary. The size of each command varies depending on its arguments, but each begins with two bytes:
- The Command Type: An unsigned char which indicates which command this is. Again, see command.h.
- The Command Flag: An unsigned char which may contain multiple flags modifying the behaviour of the command. Most commands don't have flags, and this is just zero. See CommandFlag in command.h.
In this document,
- uint means a 4-byte unsigned integer. int means a 4-byte signed integer
- float means an IEEE-754 encoded 4-byte floating point number
- byte means a signed 1-byte number. ubyte means an unsigned 1-byte number.
WARNING: As mentioned in the Compiler documentation, there is currently no handling for endianness in the compiled data. You have to compile and run InkCPP on machines of matching endianness.
- Value Commands
- Divert Commands
- Variable Definitions
- Evaluation Stack
- String Stack
- Choice Commands
- Binary Operators
- Unary Operators
- Container Tracking
- Other Commands
Value commands out data. If the runner is in evaluation mode (see START_EVAL), then the value is pushed onto the evaluation stack. Otherwise, it's pushed into the output stream.
STR uint offset
Pushes the string at the given offset in the file's string table (0 being the start of the string table).
INT int value
Pushes the given integer value.
BOOL int value
pushes a given boolean value (0 for false, 1 for true).
FLOAT float value
Pushes the given floating point number.
LIST uint offset
Pushes list at given offset in the file's list_table (0 being the start of the list_table).
LIST_FLAG list_flag value
Pushes the given list flag value.
DIVERT_VAL uint offset
Pushes an offset in the instruction data which could be used later to divert to that instruction. This command only works if the runner is in evaluation mode.
NEWLINE
Pushes a newline marker.
GLUE
Pushes the glue marker.
VOID
Pushes the void marker. Only works in evaluation mode. Used only when a function is returning nothing.
Implementation Node: Right now I just push a zero since the caller never checks the return value of a void function.
These commands change the current position of the instruction pointer.
DIVERT uint destination
Immediately changes the instruction pointer to the given destination offset in the instruction data.
If the destination is the byte immediately following the last command in the instruction data, this DIVERT acts the same as a DONE command.
Flags
-
DIVERT_HAS_CONDITION
: This divert is conditional. Only execute if the top of the evaluation stack is true. Otherwise, ignore. -
DIVERT_IS_FALLTHROUGH
: This is a special "fallthrough divert" inserted by the compiler to simulate falling out of a container back into its parent. See Fallthrough Diverts on the Containers page.
DIVERT_TO_VARIABLE uint variableHash
Read the value of the variable with the given hash and jump to that offset in the instruction data.
Flags
-
DIVERT_HAS_CONDITION
: This divert is conditional. Only execute if the top of the evaluation stack is true. Otherwise, ignore.
TUNNEL uint destination
Diverts to the given destination similarly to DIVERT, except push an execution frame onto the stack to remember where we tunneled from. See TUNNEL_RETURN.
TUNNEL_RETURN
Jumps back to where the tunnel was originally started from.
Same as TUNNEL and TUNNEL_RETURN except output is handled a bit differently.
Flags
-
FUNCTION_TO_VARIABLE
: The function address is stored in a variable with the given hash -
FALLBACK_FUNCTION
: The function is a fallback for an external function
TODO: Document this? I think it wasn't even documented in the Ink runtime but I had to reverse engineer what was going on.
DONE
Pops the active thread or safely ends the story flow without losing the instruction pointer (example: giving control back to the player after creating all the choices).
END
Clears the current instruction pointer, signalling the end of the Ink file.
These commands deal with variables.
DEFINE_TEMP uint variableNameHash
Defines a new temporary variable on the stack with the given hash. Its initial value is popped off the top of the stack.
SET_VARIABLE uint variableNameHash
Pops the top value off the stack and assigns it to the variable of the given hash. Whether the variable is local or global is determined automatically by checking the stack.
Flags
-
ASSIGNMENT_IS_REDEFINE
: Is this assignment a redefinition? I'm not sure why this is important yet but maybe it pertains to a feature I haven't implemented yet? Right now I just ignore it.
These commands push, pop, or otherwise deal with the evaluation stack.
START_EVAL
Puts the runtime in evaluation mode. Pushed values will now go to the evaluation stack.
END_EVAL
Takes the runtime out of evaluation mode. Pushed values now go to the output stream.
OUTPUT
Pops the top value off the evaluation stack and pushes it into the output stream.
POP
Deletes the top element off the evaluation stack.
DUPLICATE
Duplicates the top element on the evaluation stack and pushes it onto the top.
PUSH_VARIABLE_VALUE uint variableNameHash
Pushes the value of a given variable (local or global) onto the top of the evaluation stack.
PUSH_VARIABLE_POINTER_VALUE uint variableNameHash
Pushes the value of the variable which is pointer to on the evaluation stack. The ci value is the flag - 1.
VISIT
Pushes the visit count of the current container to the top of the evaluation stack.
READ_COUNT uint containerIndex
Pushes the visit count of the container with the given index to the top of the evaluation stack.
SEQUENCE
Pops the top two values off the evaluation stack. The first is a length, the second is an index.
A random number from 0 to length-1 is pushed onto the stack.
WARNING: Not fully implemented. I think there's some fancy logic in Ink to prevent the same value being picked more than once before the entire list is gone through, but I haven't done that yet. Also I don't know what index is for.
SEED
Pops the top value off the evaluation stack and uses that as the new seed for random number generation.
Also then pushes void onto the stack (for some reason? I think because it looks like a function call in Ink.)
The following instructions deal with the string stack, a special stack for appending strings and numbers into bigger strings.
START_STR
Puts the runner into "string mode". Any output or value commands will start appending to a new dynamically created string instead of to the game's output.
END_STR
End "string mode". A new value with all the outputted values since START_STR
is pushed onto the top of the evaluation stack.
START_TAG
Puts the runner into "tag mode". Any output or value commands will start appending to a new dynamically created string instead of to the game's output.
END_TAG
Ends the "tag mode". A new value with all outputted values since START_TAG
is stored in the tag list.
CHOICE uint destinationInstruction
Creates a new choice that leads to the given offset in the instruction data.
Flags
-
CHOICE_IS_ONCE_ONLY
: If set, this choice can only be taken once. This is implemented by checking if the destination container has a visit count of 1 or more. -
CHOICE_HAS_CONDITION
: If set, only show this choice if the top value of the evaluation stack is true -
CHOICE_HAS_START_CONTENT
: If set, pop the top value of the evaluation stack and add it to the choice text -
CHOICE_HAS_CHOICE_ONLY_CONTENT
: If set, pop the top value of the evaluation stack and add it to the choice text -
CHOICE_IS_INVISIBLE_DEFAULT
: Does something I haven't implemented yet. TODO
All the following commands are binary operators and function in the exact same way. They pop the first two values off the top of the evaluation stack, run their operator on those two values, and push the result to the top of the evaluation stack.
ADD
SUBTRACT
DIVIDE
MULTIPLY
MOD
IS_EQUAL
GREATER_THAN
LESS_THAN
GREATER_THAN_EQUALS
LESS_THAN_EQUALS
NOT_EQUAL
AND
OR
MIN
MAX
?
!?
L^
Like binary operators, except they only pop one value off the evaluation stack then push the result back.
NOT
NEGATE
LIST_COUNT
LIST_MIN
LIST_MAX
READ_COUNT_VAR reads cont of container pointet to by var on stack
lrnd
LIST_ALL
LIST_INVERT
These commands are unique to InkCPP and help track the current container (which is needed only for visit counting purposes).
START_CONTAINER_MARKER uint containerId
Signals that we are entering the container identified by the given container id.
Flags
-
CONTAINER_MARKER_TRACK_VISITS
: If set, track the number of times we have visited this container.
END_CONTAINER_MARKER uint containerId
Signals that we are leaving the container identified by the given container id.
This will cause a runtime assertion if the top of the container stack does not match the given id.
Special Behavior: If the result of this command is popping off the last container in the container stack, interpret this as...
- A void return statement if we are in a function or tunnel
- An
END
command otherwise.
CALL_EXTERNAL uint functionNameHash
Special use of the flags byte: The flags byte for this command is interpreted as the number of arguments to pass to the function.
Executes a function bound to the runner using the bind
method. The runtime will pop N values off the execution stack (one per argument) and pass them in order of popping to the bound function.
TODO: Link to function binding wiki page.