Catspeak and GML

Overview

Catspeak is very similar to Gamemaker Language (GML), and both are high-level, dynamically-typed, procedural programming languages featuring first-class functions. Both run on top of the Gamemaker engine, which is object-oriented with an event-driven model. If those buzzwords confuse you, ignore them, and let’s write some code.

If you want more information, I recommend looking at the GameMaker and Catspeak documentation sites. If you’ve used Javascript, a lot of the basics will be similar.

Semicolons

Both GML and Catspeak use semicolons (;) to separate statements within a single line.

CatspeakGML
let i = 0; i += 2 var i = 0; i += 2

Because of boring technical reasons, all modded code must fit within one line, using semicolons to separate statements.

The with Statement and self

The with statement is GML and Catspeak’s most powerful scope-changing and Instance-finding tool. When used with a struct-like structure, it changes self to reference that struct.

let struct = {
  "name": "Fern",
  "sprite": splayer_offscreen,
}
with struct {
  if self.name == "Fern" {
    -- runs
  }
}

Naturally, this isn’t very useful. What is useful though, is when you use it with an Instance.

let player = instance_find(oplayer, 0)
with player {
  self.y -= 10
  draw_self()
}

The highlighted function draws whatever self is, which we get to manipulate using a with statement. We can also use with statements with Game Objects (or the all keyword) to loop over Game Objects.

with par_enemy {
  instance_destroy(self)
}

Instead of using instance_number, instance_find, and a while loop, we just use a with statement. Keep in mind that if you’re using modded Game Objects with a with statement, you’ll want to filter for only your Instances.

Advanced Scoping

If you’ve come from Javascript or other similar languages, then you might expect something like this to work.

let _i = 0
let count = fun (a,b) {
  _i += 1
  return _i
}

let a = count()

You would be disappointed, because line 3 throws an error. In other languages, the variable _i would be available to the function, however in Catspeak functions are far more isolated, so you crash when you try to add 1 to undefined.

Catspeak Truthy Values

When using boolean expressions, certain values can be coerced into true or false. For example, the following code works.

if 123 { show_message("Hello World!") }

This is because 123 can be coerced into a true-like value. Here is a list of keywords that do or don’t coerce into true-like values.

Truthy Values
CatspeakTruthy-nessDescription
123 true Positive numbers
0 false Zero
-123 false Negative numbers (!!!)
NaN false Not-A-Number
undefined false Undefined keyword
"Hello" -- runtime error Strings of any kind (!!!)
{ } true Structs of any kind

This probably also applies to GML, but I’ve only tested it on Catspeak. Fun fact: !"Hello World" is the simplest “Hello World” Catspeak program, since the unary boolean operator causes an exception and prints the string in the error message.

Differences

This is a condensed list of the major differences between the two languages. If you want something else added, slap that contribute button.

Catspeak and GML Comparisons
CatspeakGMLDescription
let x = 1 var x = 1 Variable assignment
fn = fun(a) { ... } fn = function(a) { ... } Function declaration
self.x x Implicit/explicit self
a // b; a //= b a div b; a = a div b Modulo division operator
a and b or c a && b || c Boolean operators
a = if b { c } else { d } a = b ? c : d Ternary operator
-- comment // comment Comments
x += 1 x++ Unary increment/decrement
r = room_get() r = room Special variable access
instance_find(oplayer).state oplayer.state Singleton Instance access
c = 'A' c = ord("A") Character shorthand
let _i = 0
while _i < 4 {
  ...
  _i += 1
}
repeat 4 { ... } Repeat loops
match a {
  case 0 { b = 1 }
  else { b = 0 }
}
switch a {
  case 0:
    b = 1
    break
  default:
    b = 0
}
Switch Statement

Constructors

Catspeak also lacks constructor functions, however you can emulate them using normal structs and functions. Catspeak doesn’t preserve self scoping in the same way that GML does, so you can’t use self properly.

Constructor Comparison
CatspeakGML
let Vec = {}
vec.make = fun (x, y) {
  return { "x": x, "y": y }
}
vec.add = fun (v1, v2) {
  v1.x += v2.x
  v1.y += v2.y
}

let a = Vec.make(2, 3)
let b = Vec.make(4, 5)
Vec.add(a, b)
function Vec(_x, _y) constructor {
  x = _x;
  y = _y;

  static add = function(v2) {
    x += v2.x
    y += v2.y
  }
}

var a = new Vec(2, 3)
var b = new Vec(4, 5)
a.add(b)