Core Commands

These commands are the core of the scripting system. They allow you structuring your programs, and interacting with the interpreter. Many of them have a special syntax. Most of these commands have little or no knowledge of the Planet game -- for commands to interact with your game, see the next sections.

 Abort


  Abort expression

`Abort' cancels the script. The result is the same as if you had a syntax error or some other error in your program. The expression must evaluate to a string; this is the error message that will be printed to the console.

If there's a surrounding `Try' block, execution resumes at its `Else' part, or after its `EndTry' if there is no `Else'. `System.Err' will receive the value of the expression in this case.

 Bind


  Bind keymap key := command, key := command ...

This command arranges for the key key in the specified keymap to run the command. key and command are expressions that evaluate to strings; keymap is a literal keymap name. For example,

  Bind PlanetScreen "a" := "Autobuild"

makes the [A] key on the planet screen run the `Autobuild' command. Your key definitions override the defaults, that is, after

  Bind ControlScreen "esc" := "Messagebox 'hehe'"

you'll no longer be able to exit control screens with [ESC]. In this particular case, clicking the [ESC] button will still work, though. Only a few buttons are affected by `Bind'. This is changing, however.

You can define multiple keys in the same keymap in one line, by simply writing multiple assignments separated by commas.

Possible key combinations are listed in the tables section. Note that due to the oddities of usual keyboard drivers, PCC can't recognize every combination you could imagine.

To undefine a key, bind it to the empty string. Unlike object properties, keymaps do not survive PCC exiting and re-loading.

The commands you bind to keys can examine the `UI.Prefix' variable to find out the current prefix argument.

Predefined Keymaps:

BaseScreenthe starbase screen ([F3])
PlanetScreenthe planet screen ([F2])
Planetplanet screen or planet locked in starchart
ShipScreenthe ship screen ([F1])
Shipship screen or ship locked in starchart
FleetScreenthe fleet screen ([F10])
HistoryScreenthe ship history screen ([F6])
ShipBuildScreenthe ship build screen at a starbase
Starchartthe starchart
ShipTaskScreenthe ship auto task screen
PlanetTaskScreenthe planet auto task screen
BaseTaskScreenthe starbase auto task screen
AutoTaskScreenall auto task screens
ControlScreenall control screens or planet/ship in starchart
RaceScreenthe player main screen
Globalall of the above

When locked at a ship or planet in the starchart, the appropriate `PlanetScreen' or `ShipScreen' keymap is active in addition to the starchart keymap. Bindings in a more specific keymap override those in the general one (see `CreateKeymap').

Note that, because actions are managed as atoms in PCC (see User Interface section), you can also bind a key to a number (=atom). For example, you can also use

  Bind PlanetScreen, "a" := Atom("Autobuild")

instead of the above example. Do not do that unless you have to.

 Break


  Break

Exits a loop immediately, canceling all iterations that would follow. This equals a jump just after the `Loop' or `Next' keyword. You can't always `Break' out of a single-line `ForEach' / `For' loop, use a multi-line loop if you need that.

 Close


  Close #fd

Closes the file on file descriptor #fd. After this, you can no longer use the specified file descriptor until you open it again.

See also: File I/O

 Continue


  Continue

Continues with the next iteration of a loop, if there's one. This equals a jump just before the `Loop' or `Next' keyword. You can't always `Continue' a single-line `ForEach' / `For' loop, use a multi-line loop if you need that.

 CreateKeymap


  CreateKeymap name(parent), ...

Creates a keymap named name. The keymap can later be filled with key/command associations using the `Bind' command. The keymap `inherits' from parent, that is, keystrokes not defined in this keymap will be looked up in the parent. If you do not want your keymap to inherit anything, omit the part in parentheses. For example,

  CreateKeymap ControlScreen, ShipScreen(ControlScreen)

creates a keymap `ControlScreen', and a keymap `ShipScreen' which inherits from it.

Keymap names are case-insensitive, global, and distinct from everything else (that is, they do not collide with subroutine or variable names). If the keymap already exists, nothing happens. Unlike object properties, keymaps do not survive PCC exiting and re-loading.

 CreateShipProperty, CreatePlanetProperty


  CreateShipProperty var-list
  CreatePlanetProperty var-list

Creates new properties for ships/planets. var-list is a comma-separated list of the names you want the properties to have. You can define and use these properties as you like. New properties start out EMPTY. For example, after

  CreatePlanetProperty hap.goal

all planets will have an empty property `hap.goal'. You can assign to it with

  Planet(19).hap.goal := 94
  % ... or ...
  With Planet(19) Do hap.goal := 94

If a property you create with either of these commands was already created, nothing happens.

Properties created with these commands `shadow' the normal built-in properties. That is, the built-in ones become (currently) inaccessible. This gives a nice rope to hang yourself, so be careful: if you say,

  CreatePlanetProperty Name

you'll not be able to access real planet names any more, but you'll be able to assign to them (with no visual effect to the rest of PCC, of course...)

Properties are saved in the starcharts file (chartX.cc). If the starcharts file contains an undeclared property with an interesting value (non-EMPTY), the property is automatically declared to avoid data loss. To get rid of a property forever, set all its values to EMPTY and do no longer declare it.

 Dim


  Dim storage-class var-list

  Dim var-list

  storage-class var-list

The storage class can be one of `Static', `Shared' or `Local'; it defaults to `Local' if omitted. If you specify a storage class, `Dim' is optional. See the section on variables above for an explanation of storage classes.

The variable list consists of a comma-separated list of variable names, optionally followed by an initializer:

  Dim foo, bar, qux
  Dim frob = 17, fred
  Dim barney := 99

Variables can be defined as often as needed. If they already exist, the initialisation is ignored:

  Dim a = 1
  Dim a = 2

leaves you with a variable `a' with the value 1.

 Do


  Do loop-condition
    statements
  Loop loop-condition

Executes the statements in a loop. The loop-conditions have the form `While expression' or `Until expression', and are both optional. The condition at the `Do' is checked before each iteration, the condition at `Loop' is checked afterwards. A missing condition is treated as `While True', so just `Do / ... / Loop' is an endless loop.

You can use `Break' and `Continue' in these loops.

 End


  End

Ends processing this script or hook. The result is the same as if you `fell off the edge' of the script.

 Eval


  Eval expr, ...

Each of the expressions must evaluate to a string. These strings are then executed as script code. For example,

  Eval "Print i"

does the same as

  Print i

With this function, you can dynamically generate code.

This is also usable to execute multi-line commands on the console:

  Eval "For i:=1 To 10", "Print i", "Next"

The code in `Eval' is executed as if it were a subroutine. Hence, local variables defined in `Eval' are only valid there, `Return' exits the `Eval', and multi-line commands must be complete.

 For


  For var := start To end Do one-line-statement

  For var := start To end Do
    statements
  Next

The standard arithmetic loop. Evaluates start and end, and then executes the loop body for all values between start and end, increasing, in steps of 1. For example (no pun intended),

  For i:=1 To 5 Do Print i

prints the numbers 1, 2, 3, 4 and 5.

If the end value is less then start, the loop is not executed at all. If end equals start, the loop body is run exactly once.

The induction variable var must be a single word, e.g. `i' or `ship_id'; it must not contain context references or functions.

Generally, one uses integers as start and end. PCC also permits use of floating-point variables, but I do not recommend that; it easily leads to misunderstandings due to the way floating point arithmetic in general and `For' loops in particular work.

The `Break' and `Continue' statements can be used to influence a `For' loop.

 ForEach


  ForEach set Do one-line-statement

  ForEach set Do
    statements
  Next

Executes the statement(s) for each object in the specified set. The set is one of the following

The set can optionally be preceded by `Global.'.

If you need filtering, use an `If' inside the loop. For example,

  ForEach Minefield Do
    If LastScan < Turn-10 Then Delete
  Next

deletes all minefields more than 10 turns old. Note how this uses the `LastScan' minefield property and the `Delete' minefield command, as well as the `Turn' global property, which are both accessible during a `ForEach Minefield' loop.

If you want to access an object by Id number, you can usually use the context function of the same name as the set, e.g. `Ship(93)' to access ship #93.

The `Break' and `Continue' statements can be used to influence a `ForEach' loop.

 Get


  Get #fd, data, length

Binary I/O: reads length bytes from the specified file, and stores them as data block data. The length must be in the range 0 to 255, inclusive.

See also the File I/O section for more information.

 If


  If condition Then one-line-statement

  If condition Then
    statements
  Else If condition Then
    statements
  Else
    statements
  EndIf

If the condition (any expression) yields true, non-zero, or a non-empty string, the `Then' part is executed. Otherwise, the `Else' part is run.

In the multi-line form, the word `Then' is optional. There can be any number of `Else If' parts (including none at all), and an optional `Else' part.

 If a Then
   Print "a"
 Else If b Then
   Print "b"
 Else If c Then
   Print "c"
 Else
   Print "neither a nor b nor c"
 EndIf

`Else If' parts are supported since PCC 1.1.13; in earlier versions you have to write explicitly nested `If's, as in

 If a Then
   Print "a"
 Else
   If b Then
     Print "b"
   Else
     If c Then
       Print "c"
     Else
       Print "neither a nor b nor c"
     EndIf
   EndIf
 EndIf

The new version avoids some indentation.

Note that PCC supports `Else' only in multi-line `If's.

 Input


  Input #fd, variable
  Input #fd, variable, flag

Reads one line from the specified file handle, and stores it in the variable as a string.

When the end of the file is hit, action depends on the flag:

Hence, to iterate through a file, you can do

 Try
   Do
     Input #1, s
     % do something with s
   Loop
 EndTry

or

 Do
   Input #1, s, 1
   If IsEmpty(s) Then Break
   % do something with s
 Loop

See also: File I/O

 Load


  Load filename

The filename is an expression evaluating to a string. The specified file is read and executed.

If the file name contains no directory specifications, it is searched like all other Planets files: in the game directory first, then in the PCC directory. To explicitly specify a file in the current directory, say `Load '.\file.q''.

The file is treated as if it were a subroutine (see `Sub'):

Note that `Try Load "foo"' may not do what you want (it will ignore all errors inside the script, not only errors executing the `Load' command itself). Use `TryLoad "foo"' instead.

Note that when writing literal file names in your code, remember that the backslash is special in double-quoted strings. `Load ".\file.q"' will fail claiming the file does not exist, because what `Load' finally gets to see is (currently) `".file.q"'. Your options:

See also the introduction on expressions for more information.

 Lock()


  With Lock(lockname, infotext) Do
    % ...
  EndWith

Attempts to obtain a lock named lockname. Locks are a cooperation-based synchronisation mechanism; they are intended to avoid that multiple scripts (and the user) disturb each other. Only one process can have a particular lock at any time. See the User Interface section for details.

When the lock is already in use, this command fails with an error you can catch with `Try' around the `With'.

PCC honors certain locks (see the User Interface section for a list). When the user attempts to change the locked value, PCC will display a dialog box containing the info text, warning the user of the lock. When you omit the second parameter to `Lock', PCC generates a standard message.

The lock is held as long as the code within the `With' executes, even if PCC suspends in between.

 On


  On event Do one-line-command

Stores the specified command for execution when the specified event happens. You can define any number of commands for one event (with multiple `On' statements), which are then executed in the order they were stored. This concept is called `hooks'. event is a single identifier naming the hook. The following hooks are called automatically by PCC (more to follow). Note that most hooks are called only in the graphical user interface; the only hooks to be run in text mode are `On Load' and `On Exit'.

NameWhen
On Load Do...a game is loaded (when entering the race screen);
On NewTurn Do...a new turn was loaded (run after `On Load'). Since PCC 1.1.13; previous versions need `revive.q' for this functionality, and run this hook during `On Load';
On Exit Do...you're exiting the race screen, just before saving the game;
On Quit Do...just before exiting PCC ([ESC] on race selection);
On BeforeLoad Do...before loading a game from the race screen, after verifying the password. This hook is NOT called when you switch to another race using [Ctrl-Alt-digit], though;
On AfterExit Do...after exiting from race screen, before getting back to the race selection. This hook is also called when the load was aborted due to data file errors;
On Unpack Do...after unpacking turn files;
On UnpackScan Do...when scanning for turn files, after opening the Unpack screen;
On Maketurn Do...after making turn files.

Please take extreme care when writing the one-line-command: the syntax is checked when the hook is run, not when you issue the `On' command, so errors will only be visible as console messages later on; and currently there's no way to get rid of a typo in a hook other than restarting PCC.

Actually, you can use any valid identifier for event. The so-defined hook can then be called manually using the `RunHook' command. Hook names are managed separately from command/subroutine and variable names.

Example:

  On Load Do Messagebox "Hello, Imperator of " & My.Race

(in `autoexec.q') gives you a message box each time you open a game.

  On AttackThem Do SetMission 4

defines a hook which can be run by `RunHook AttackThem', which runs the command `SetMission 4' in the same context as `RunHook'. Note that the enqueued hook code does NOT survive PCC exit and re-load, so you can not use hooks to enqueue instructions for later in the game!

 Open


  Open name For mode As #fd

Opens the specified file name on file descriptor fd for mode. mode is one of the following:

If there was already a file open on #fd, it will be closed before.

See also: File I/O

 Option


  Option token-list

This command is ignored and does nothing. Future versions of the scripting language may use it to control interpreter/compiler settings. Those versions will document what parameters they accept for `Option'.

You should not use this command when writing scripts for PCC 1.x.

 Print


  Print expression, expression, ...
  Print #fd, expression, ...

This command converts all its arguments to string form (by means of the `&' operator), and displays them on the console (calculator window). This even works if the console is currently not visible.

You can print any number of expressions with one `Print' command; they will be placed in one line. `Print' without parameters just inserts a blank line.

For example, `Print Hull(1).Name' shows the text `OUTRIDER CLASS SCOUT' on a new line in the console. Compare this to what you get by just saying `Hull(1).Name' at the calculator: if you enter an expression, the calculator will specially format the result (put quotes around it and right-justify it), while `Print' just adds ordinary text lines.

When fd is specified (integer expression preceded by a hash sign), output goes to the specified file instead of the console.

 Put


  Put #fd, data, length

Binary I/O: writes length bytes of the binary data block data to the specified file.

The length parameter can be omitted, PCC then uses the actual length of the specified data. Lengths can be in the range 0 to 255, inclusive.

See also the File I/O section for more information.

 Return


  Return

`Return' exits the current subroutine or `Load'ed script.

Example:

  Sub TweakShip
    If Owner$<>My.Race$ Then Return
    % some commands to tweak a ship you own
  EndSub

This routine is functionally equivalent, but slightly more efficient and maybe better readable than the following version:

  Sub TweakShip
    If Owner$=My.Race$ Then
      % some commands...
    EndIf
  EndSub

 RunHook


  RunHook event

Runs the hook event. See `On' for a discussion of hooks.

event can be any identifier. If there are no commands defined for that hook, nothing happens.

All the hook commands are executed in the current context of the `RunHook' command.

 Seek


  Seek #fd, position

Sets the file pointer on the specified file handle.

The position can be a number in the range 0 to FSize(#fd), where 0 places the file pointer at the very beginning of the file and FSize(#fd) places it just after the end (so you can extend the file using Print and Put statements). Values outside this range cause an error.

You can query the current file pointer using the FPos(#fd) function.

See also: File I/O

 Select


  Select Case select-expression
    Case expression, expression...
      statements
    Case Is <= expression...
      statements
    Case Else
      statements
  EndSelect

Executes one of several statement blocks, depending on the value of the select-expression.

There can be any number of `Case' branches, each of which lists a number of values to match. For example,

 Case 1, 3, 5

matches numbers one, three or five. Using the `Is' keyword, you can also match on relations, as in

 Case Is >= 9

which matches all numbers greater or equal than nine. Each `Case' can contain any number of selectors separated by comma. Although these examples all use integer numbers, you can also select on real numbers or strings.

Cases are evaluated from top to bottom, the first matching one is taken. If no case matches, the `Case Else', if present, is run.

Values in `Case' expressions should be constants, although this is not enforced in PCC 1.x.

 SetByte, SetWord, SetLong


  SetByte block, offset, value, ...
  SetWord block, offset, value, ...
  SetLong block, offset, value, ...

These functions are used for preparing binary I/O. They enter a value of the specified type into the block at the specified offset; 0 means the first byte in the block. When you specify multiple values, they are stored one after the other.

Ranges are as follows:

Example:

  Dim block
  SetWord block, 0, Loc.X
  SetWord block, 2, Loc.Y
  SetWord block, 4, Owner$

creates an entry for the XYPLANx.DAT file which could actually be written to the file using

  Put #3, block, 6     % 6 bytes total

See also File I/O for more information about these commands. See also SetStr, GetWord.

 SetStr


  SetStr block, length, offset, value

Stores the string value in the block at the specified offset, as a `BASIC string' of length length.

This command is used to prepare binary File I/O, see there for more information. See also SetWord, GetStr.

 Stop


  Stop

`Stop' suspends the process.

It is unspecified when the process will wake up (i.e. continue to run) again, but it will run at least once per turn. This can be used to implement things like `Wait one turn':

  Local t = Turn
  Do While t = Turn
    Stop
  Loop

If `Stop' fails, your process will be terminated with an error. That error is among the few errors which are not catchable with `Try' (unlike advertised in the PCC 1.0.6 version of this file). Possible reasons for this include

When a script wakes up again, all sorts of things may have been changed (for example, a turn has passed). PCC will not wake up scripts when you temporarily switched back to an earlier turn. When the script executes in a context which no longer exists, e.g., a now-nonexistent UFO, it is terminated with a message on the console.

Currently, PCC wakes up all processes whenever it loads a turn. You should not depend on that exact behaviour, though.

 Sub


  Sub name (args)
    statements
  EndSub

Defines a subroutine called name, taking the specified arguments. The argument list can have the following forms:

A subroutine is defined once the `Sub' statement is executed. That is, you can conditionally define subroutines:

  If System.Host$=2 Then
    Sub GivePlanetToRace(pid, r)
      % ...
    EndSub
  EndIf

A subroutine can reference subroutines that are not yet defined. PCC is an interpreter; you won't get an error until you actually try to invoke an undefined subroutine. Subroutines can be recursive.

To invoke a subroutine, write its name followed by its arguments, without parentheses. For example, to invoke the above `MoveTo' routine, write `MoveTo 2000, 1000'. Optional parameters can be omitted, the subroutine will see an empty value (`IsEmpty' returns true) in them.

If you redefine a subroutine, all new invocations will use the new versions. If some processes are still in this routine, they will continue to execute the old code. For example, the following (weird) example will print "Foo", then "Bar":

  Sub Hack                % 1
    Sub Hack              % 2
      Print "Foo"         % 3
    EndSub                % 4
    Hack                  % 5
    Print "Bar"           % 6
  EndSub                  % 7
  Hack                    % 8

Explained: when seeing the statement `Sub Hack' in line 1, PCC will save all code up to the matching `EndSub' (line 7) as subroutine `Hack'. It will then invoke that subroutine (line 8). The first action of that subroutine is to define another sub, which happens to have the name `Hack', too. On line 5, the invoked routine is the newly defined one. If that sounds crazy, you're right -- don't do that. But this behaviour is required. If you modify, for example, the `MoveTo' subroutine while some processes are sleeping there, they must continue to execute the old code, they would get hopelessly confused if we'd change their basics while they are sleeping (like you were confused if I'd change your house while you're sleeping ;-)

 Try


  Try one-line-statement

  Try
    statements
  Else
    statements
  EndTry

Usually, any error in a script causes execution of the process to be terminated (as if it called `Abort'). With `Try', you can recover from errors and continue execution. With the multi-line form, an error in the first statement sequence causes the statements after `Else' to be executed. If the first statement sequence is successful, the `Else' part is skipped.

The `Else' branch is optional. A one-line `Try' or a multi-line `Try' without `Else' just causes errors to be ignored.

Use this to protect you from possibly dangerous statements:

  ForEach Planet Do Autobuild

will fail on the first planet you don't own.

  ForEach Planet Do Try Autobuild

won't.

`Try' can be used to escape nested commands, and even subroutines.

Note, however, that `Try' will protect you from _all_ errors -- in the current version even from syntax errors. You can use the variable `System.Err' in the `Else' part (and later) to access the actual error message, but I don't guarantee for PCC messages to have a standardized format and remain the same over years. Some messages I'm trying to keep constant where it fits are:

 TryLoad


  TryLoad filename

This command is like `Load', except that it does not generate an error if the specified file does not exist or cannot be opened. This differs from

  Try Load filename

in that `TryLoad' will diagnose errors in the script, the `Try' construction will not (they get blocked by `Try').

Useful example: you want some common code be executed each time you start PCC, and you want game-specific code be executed in each game. Place the common code in the PCC directory, in `autoexec.q', and place the game specific code in `gameinit.q' in each game directory. Then, add the following statement to `autoexec.q':

  TryLoad "gameinit.q"

This will load the game specific script, or do nothing if that does not exist.

See also `Load' for some other hints about this command.

 UseKeymap


  UseKeymap keymap

This command temporarily enables a secondary keymap, that is, it causes the next keypress to be processed according to the specified keymap instead of the normal keymap for the current place. This way, you can create multi-keystroke commands.

For example,

  CreateKeymap CtrlXMap
  Bind CtrlXMap 'C-s' := 'SaveGame'

will create a keymap `CtrlXMap' in which Ctrl-S invokes the `SaveGame' command. You can now bind that to a key:

  Bind ControlScreen 'C-x' := 'UseKeymap CtrlXMap'

Now, the key sequence Ctrl-X Ctrl-S will save the game from any control screen.

Only one `UseKeymap' command can be active at a time. A second command will cancel the first.

This command does not wait for the keystroke to actually occur; it immediately proceeds execution of the script. The secondary keymap is used when PCC is waiting for input next time. As a reminder of the temporarily changed keybindings, a pop-up message will occur after about a second of idle time, or when a key is pressed which is not bound in the keymap. As a quick way out, ESC cancels the secondary keymap, unless ESC is bound in it.

It is recommended that you only bind direct invocations of `UseKeymap' to keys. In particular, the keymap debugger can then help you to look at these alternate keymaps. Although it is possible to call `UseKeymap' from subroutines, you should avoid that if you can. In particular, you should not call any complicated user-interface command after `UseKeymap'; this will not always do what you want.

 With


  With expression Do one-line-statement

  With expression Do
    statements
  EndWith

Executes the statements in the context specified by the expression. The expression must be a record reference, i.e., something which can be followed by `.fieldname' in an expression:

  With Ship(10) Do SetFCode "foo"

In a multi-line `With', the word `Do' is optional.


[ << Previous | Up | Next >> ]

Stefan Reuther <Streu@gmx.de>