Skip to content

Language Basics

  • Data
  • Task flow
  • Conditional
  • Loops
  • Scheduling Tasks

Data

You can create data that is any valid python type. This data can be used in your MAST tasks.

Simple assignment

To do so you use the assignment statement::

fred = 3

Assignment has a variable name an equals followed by a value.

Using python with assignment

The assignment is simple and has trouble with more complex python statements e.g. a list of list, etc. To allow more complex assignments you can wrap the value in 'snakes' to have the python compiler used::

players_inventory = ~~ [ [2,3], [4,5]] ~~

You need at least 2 'snakes' (the tilde character), before and after the python values. But you can have more tha two, and the number doesn't need to be exactly the same, as long as you have at least two::

players_inventory = 
    ~~~~~~~~~ 
    [
        [2,3], 
        [4,5]
    ] 
    ~~~~~~

Shared data assignment

Data has multiple scopes. Data can be at the scope of a MAST story, a scheduler, a task, or a label.

There are times you want data to be shared by all tasks within a story. To share data you add the 'shared' marked in front of the assignment::

shared enemy_count = 20
shared beer_count = 8

When using Data, scope is automatically handled. You only need to specify shared at assignment::

shared beer_count = 8
my_beer = 0

# Drink all the beer
my_beer = my_beer + beer_count
share beer_count = 0

Task Flow: Story sections via labels

A MAST story is broken into sections using labels. You also can have comments, and there are also other 'markers' that can help organizing sections and help have them stand out in the file.

Labels

Labels have a Name with no spaces and are enclosed in 2 or more equals

====== GotoBar ====
 . . .
== ShowHelm ==
 . . .

========================================== MoreStuff ===========================
 . . .

There are two labels that are implied: main and END.

The label "main" is the very start of the script. The label "END" end the current task.

They are predefined and don't need to be defined in script.

Labels are not 'functions', one label passes into the next label

======== One =====
log("One")
======== Two =====
log("Two")
===== Three ====
log("Three")
One
Two
Three

State/Flow changes: Jump

There are times you will want to change what part of a task is running. This is done by redirecting the flow to a label.

Jump

This can be done by a Jump command. Which is a 'thin arrow' followed by the label name.::

-> Here

======== NotHere =====
log("Got here later")
-> End

======== Here =====
log("First")
-> NotHere

======== End =====
log("Done")
->END
======== Never =====
log("Can never reach")
First
Got Here later
Done

Jump to End

To immediately end a task you can use a Jump to End.

Jump to end looks like a Jump with a thin arrow and the label "END"

===== start ====

log("See you later")
->END
log("Never gets here")
@label()
def start(self):
    print("See you later")
    yield END()
See you later

Jump to End ends the task. If that task the only task, the whole story ends.

Scheduling tasks and waiting for them to complete

A story can have multiple tasks running in parallel.

For example, a ship maybe have multiple Tasks associated with it. One tracking it comms, several for its client consoles, and several related to 'quest' it is involved in.

To do so, new task can be scheduled. Either in python or via Mast.

Scheduling tasks in mast

Schedule a task is similar to a Jump, but it uses the Fat arrow. The difference is another task begins, and the original task continues on.

log("before")
#
task_schedule(ATask)
log("after")

=== ATask ===
log("in task")
@label()
def start(self):
    logger()
    log("before")
    task_schedule(self.a_task)
    log("after")

@label()
def a_task(self):
    log("in task")
before
after
in task

passing data to a task

You can pass data to a new task. The data passed is different than the original task.

message = "Different"
task_schedule(ATask, {"message": "Hello"})
log(f"{message}")

=== ATask ===
log(f"{message}")
message = "Who cares"
@label()
def start():
    logger()

    task_schedule(a_task, {"message": "Hello"})
    message = get_variable("message")
    log(f"{message}")

@label()
def a_task():
    log(task.message)
    set_variable("message", "Should not change original")
different
Hello

Named task and waiting for a Task or Tasks

You can assign a task to a variable by putting a name in front of the fat arrow.

This can be used to await the task later.

Example scheduling a task

log("Start")
task1 task_schedule(ATask)
await task1
log("Done")

=== ATask ===
log("task run")
Start
task run
Done

Awaiting for any or all tasks

This can be used to await a list of tasks. You can await for ay task to complete. And you can await for all tasks to finish.

Example await all

log("Start")
task1 = task_schedule(ATask, data= {"say": "Task1"})
task2 = task_schedule(ATask, data= {"say": "Task2"})
#### This needs to be refactored it isn't valid yet
await task_all(task1,task2)
log("Done")

=== ATask ===
log("{say}")
Start
Task1
Task2
Done

Await any

log("Start")
task1 = task_schedule(ATask, data= {"say": "Task1"})
task2 = task_schedule(ATask, data= {"say": "Task2"})
#### This needs to be refactored
await task_any(task1,task2)
log("Done")

=== ATask ===
log("{say}")"
Start
Task1
Task2
Done

The order maybe be different based on timing of the tasks.

For an await any if any task end, the await is satisfied.

Canceling a task

You can cancel a tasks by name from another task.

log("Start")
task1 task_schedule(ATask)
task_cancel(task1)
log("Done")

=== ATask ===
log("May not run")
@label()
def start(self):
    logger()
    log("Start")
    task1 = task_schedule(a_task)
    task_cancel(task1)
    log("Done")

@label()
def a_task(self):
    log("May not run")

Start Done

Conditional Statements

MAST supports both if and match statements similar to python syntax. pyMAST simply uses the python statements.

If statements

MAST supports if statements similar to python with if, elif, and else.

If conditionals can be nested as well.

===== start ====
value = 300

if value < 300:
    log("less")
elif value > 300:
log("more")
else:
    log("equal")
@label()
def start():
    value = 300
    if value < 300:
        log("less")
    elif value > 300:
        log("more")
    else:
        log("equal")
equal

Match statements

MAST supports match statements similar to python with match, case.

===== start ====
value = 300

match value:
    case 200:
        log("200")
    case 300:
        log("300")
    case _:
        log*("something else")
@label()
def start(self):
    value = 300
    match value:
        case 200:
            log("200")
        case 300:
            log("300")
        case _:
            log("something else")
300

For loops

MAST supports for loop similar to python with for, break, continue .

pyMAST uses the standard python for or while loop.

However, MAST support a for ... in loop and a for .. while loop.

for x in range(3):
    log("{x}")


y = 10
for z while y < 30:
    log("{z} {y}")
    y += 10
for x in range(3):
    log("{x}")

y = 10
z = 0
while y < 30:
    log("{z} {y}")
    y += 10
    z += 1
1
2
3
0 10
1 20
2 30

Comments

Comments provide code extra information to help make it more understandable.

Comments

Single line comments start with a # and go until the end of the line.

Comments use the # like python does

fred = 10 # set fred to 10

Multi line Comments aka block comments

You can have a c style block comment

/*********
Beware
This is the tricky part
****/

Using block comments to 'disable' code it can quickly get confusing. Therefore, an additional block comment is supported.

Importing

You can break up mast content into multiple files and use import to included them

import story_two.mast

The import command also supports importing from a zip fill

from my_lib.zip import bar.mast

One use of the zip file concept it to create a sharable library of things.

Logging

MAST supports syntax to simplify pythons logging features.

logger command

The logger command sets up logging.

Logging needs to be enabled

Logging can enabled for stdout, to a string stream (stringIO) variable, and a file

# enable logging to stdout
logger()
# enable logging to stdout, and a string
logger(var="string my_string_logger")
# enable logging to stdout, and a file
logger(file="{mission_dir}/my_log.log")
# enable logging to stdout, a string and a file
logger(var="my_string_logger", file="{mission_dir}/my_log.log")
@label
def some_label(self):
    # Logging to stdout
    logger()
    # Logging to string IO
    logger(var="my_string_logger")
    # Logging to file
    logger(file="{mission_dir}/my_log.log")
    logger()

You can have multiple loggers, each logger can have separate strings, or files.

The default logger does not need to specify the name.

To create a new loggers by using the logger command specifying a name

logger(name="tonnage")
logger(name="tonnage", var="tonnage")
logger(name="tonnage",file="{get_mission_dir()}/tonnage.txt")
# this import is needed for get_mission_dir
from sbs_utils.fs import get_mission_dir

@label
def some_label(self):
    logger(name="tonnage")
    logger(name="tonnage", var="tonnage")
    logger(name="tonnage",file="{get_mission_dir()}/tonnage.txt")

log command

The log command is how you send messages to the log.

# no logger name defaults to name "mast"
log("Hello, World")
# Specify a name to log to a secondary logger
log("Tonnage score {tonnage}", name="tonnage")
@label
def some_label(self):
    # no logger name defaults to name "mast"
    log("Hello, World")
    # Specify a name to log to a secondary logger
    log("Tonnage score {tonnage}", name="tonnage")

The log command can accept levels. These are visible is the stdout messages.

log("Hello, World", level="info")
log("Hello, World", level= "debug")
log("Hello, World", level= "error")
@label
def some_label(self):

    log("Hello, World", level="info")
    log("Hello, World", level= "debug")
    log("Hello, World", level= "error")

Delay commands

The delay command continues to execute for a period of time.

A Delay needs a clock to use. Artemis Cosmos has two clocks: gui and sim. The gui clock is running continuously (realtime). The sim clock can be paused when the simulation is not running (game time).

For gui and other things use the gui clock. If you want to delay 10s of game time use sim.

Delay can specify minutes and seconds.

await delay_app(minutes=1)
await delay_app(seconds=10)
await delay_app(seconds=5, minutes=1)
await delay_sim(0, 10)
yield AWAIT(delay_app(minutes=1))
yield AWAIT(delay_app(seconds=10))
yield AWAIT(delay_app(seconds=5, minutes=1))
yield AWAIT(delay_sim(0, 10))

Delay can delay the flow of the code

for x in range(3):
    log("{x}")
    await delay_app(1)
for x in range(3):
    log("{x}")
    yield AWAIT(delay_app(1))
1
2
3