Skip to content

Instantly share code, notes, and snippets.

@katsaii
Last active July 26, 2026 18:06
Show Gist options
  • Select an option

  • Save katsaii/ee89efa1161778cd838660849e7f5c28 to your computer and use it in GitHub Desktop.

Select an option

Save katsaii/ee89efa1161778cd838660849e7f5c28 to your computer and use it in GitHub Desktop.
Misc GML snippets from the olden times (pre-2.3 update)
/// @desc Returns a clone of an array.
/// @param variable {Array} The array to clone.
/// @author Kat @katsaii
if (array_length_1d(argument0) < 1) then return [];
argument0[0] = argument0[0];
return argument0;
/// @desc Draws a bezier curve with 3 control points.
/// @param x1 {Real} The X coordinate of the first control point.
/// @param y1 {Real} The Y coordinate of the first control point.
/// @param x2 {Real} The X coordinate of the second control point.
/// @param y2 {Real} The Y coordinate of the second control point.
/// @param x3 {Real} The X coordinate of the third control point.
/// @param y3 {Real} The Y coordinate of the third control point.
/// @author Kat @katsaii
var x1 = argument0;
var y1 = argument1;
var x2 = argument2;
var y2 = argument3;
var x3 = argument4;
var y3 = argument5;
var step = 0.1;
draw_primitive_begin(pr_linestrip);
draw_vertex(x1, y1);
for (var i = 0; i <= 1; i += step) {
// get intermediate coordinates
var ix = lerp(x1, x2, i);
var iy = lerp(y1, y2, i);
var jx = lerp(x2, x3, i);
var jy = lerp(y2, y3, i);
// get final curve point
var bx = lerp(ix, jx, i);
var by = lerp(iy, jy, i);
draw_vertex(bx, by);
}
draw_vertex(x3, y3);
draw_primitive_end();
/// @desc Draws a bezier curve with 4 control points.
/// @param x1 {Real} The X coordinate of the first control point.
/// @param y1 {Real} The Y coordinate of the first control point.
/// @param x2 {Real} The X coordinate of the second control point.
/// @param y2 {Real} The Y coordinate of the second control point.
/// @param x3 {Real} The X coordinate of the third control point.
/// @param y3 {Real} The Y coordinate of the third control point.
/// @param x4 {Real} The X coordinate of the fourth control point.
/// @param y4 {Real} The Y coordinate of the fourth control point.
/// @author Kat @katsaii
var x1 = argument0;
var y1 = argument1;
var x2 = argument2;
var y2 = argument3;
var x3 = argument4;
var y3 = argument5;
var x4 = argument6;
var y4 = argument7;
var step = 0.1;
draw_primitive_begin(pr_linestrip);
draw_vertex(x1, y1);
for (var i = 0; i <= 1; i += step) {
// get intermediate coordinates
var ix = lerp(x1, x2, i);
var iy = lerp(y1, y2, i);
var jx = lerp(x2, x3, i);
var jy = lerp(y2, y3, i);
var kx = lerp(x3, x4, i);
var ky = lerp(y3, y4, i);
// get further intermediate coordinates
var iix = lerp(ix, jx, i);
var iiy = lerp(iy, jy, i);
var jjx = lerp(jx, kx, i);
var jjy = lerp(jy, ky, i);
// get final curve point
var bx = lerp(iix, jjx, i);
var by = lerp(iiy, jjy, i);
draw_vertex(bx, by);
}
draw_vertex(x4, y4);
draw_primitive_end();
///@param id,size,x,y
var G=argument0,W,H,S=argument1,i=0,j,k,a,b;if ds_exists(G,5){W=ds_grid_width(G)H=ds_grid_height(G)while(i<W*H){j=i%W;k=i++div W;a=argument2+j*S;b=argument3+k*S;draw_rectangle(a,b,a+S,b+S,1)draw_text(a,b,G[#j,k])}}
/// @desc Uses the scan line algorithm to render a polygon.
/// @param polygon {Integer} The id of the ds_list which stores pairwise positios of verticies.
/// @param outline {Boolean} Draw an outline instead of a filled shape.
/// @author Kat @katsaii
var count = ds_list_size(argument0) div 2;
if (count > 1) {
if (argument1) {
// outline
draw_primitive_begin(pr_linestrip);
draw_vertex( // last vertex
argument0[| (count - 1) * 2 + 0],
argument0[| (count - 1) * 2 + 1]);
for(var i = 0; i < count; i++){
draw_vertex( // current vertex
argument0[| i * 2 + 0],
argument0[| i * 2 + 1]);
}
draw_primitive_end();
} else {
// scanline
var top = infinity;
var bottom = -infinity;
// compile edge table
var x1 = argument0[| (count - 1) * 2 + 0]; // last vertex
var y1 = argument0[| (count - 1) * 2 + 1];
var edges = ds_grid_create(4, count); // every vert has a corresponding edge
for (var i = 0; i < count; i++){
var x2 = argument0[| i * 2 + 0];
var y2 = argument0[| i * 2 + 1];
// add vertex data to the edge table
if (y2 < y1){
edges[# 0, i] = x1;
edges[# 1, i] = y1;
edges[# 2, i] = x2;
edges[# 3, i] = y2;
} else {
edges[# 0, i] = x2;
edges[# 1, i] = y2;
edges[# 2, i] = x1;
edges[# 3, i] = y1;
}
// update last vertex
x1 = x2;
y1 = y2;
// check whether the vertex is the new max/min
top = min(top, y1);
bottom = max(bottom, y1);
}
// sort the edge table by y-axis
ds_grid_sort(edges,3,true);
// draw scan lines
var line_count = 0;
top = floor(top);
bottom = ceil(bottom);
draw_primitive_begin(pr_linelist);
for (var yy = top; yy <= bottom; yy++) {
var intersections = ds_list_create();
// step through y-axis
for (var i = 0; i < count; i++) {
var x1 = edges[# 0,i];
var y1 = edges[# 1,i];
var x2 = edges[# 2,i];
var y2 = edges[# 3,i];
// check for a collision (where y1 < yy)
if (y2 > yy) {
// no more collisions
break;
} else if (y1 > yy) {
// collision with the edge: find the point of intersection
var xx;
if (abs(x2 - x1) < 0.01) {
// vertical intersection where the gradient is undefined
xx = (x2 + x1) * 0.5;
} else {
// y = mx + c => y + c = mx => x = (y + c)/m
var mm = (y2 - y1) / (x2 - x1);
var dy = yy - y1;
var dx = dy / mm;
xx = x1 + dx;
}
// add x vertex to the vertex list
ds_list_add(intersections, xx);
}
}
// sort verts
ds_list_sort(intersections, true);
for (var j = 0; j < ds_list_size(intersections); j++){
var xx = intersections[| j];
draw_vertex(xx, yy);
if (line_count < 1000) {
// increment the draw count for the vertex system
line_count++;
} else {
// create a new primitve when the vertex count has reached the threshold
draw_primitive_end();
draw_primitive_begin(pr_linelist);
line_count = 0;
}
}
ds_list_destroy(intersections);
}
draw_primitive_end();
ds_grid_destroy(edges);
}
}
/// @desc Draws the bounding box outline for this sprite. Only supports axis-aligned and oriented bounding boxes.
/// @param sprite {Real} The sprite to draw.
/// @param subimg {Real} The subimage of the sprite to use.
/// @param x {Real} X position.
/// @param y {Real} Y position.
/// @param xscale {Real} X scale.
/// @param yscale {Real} Y scale.
/// @param rot {Real} Rotation.
/// @param outline {Boolean} Whether to draw an outline or use a solid fill.
/// @author Kat @katsaii
if (argument1 < 0 || argument1 >= sprite_get_number(argument0)) then return;
var ox = sprite_get_xoffset(argument0);
var oy = sprite_get_yoffset(argument0);
var left = sprite_get_bbox_left(argument0) - 1 - ox;
var top = sprite_get_bbox_top(argument0) - 1 - oy;
var right = sprite_get_bbox_right(argument0) - ox;
var bottom = sprite_get_bbox_bottom(argument0) - oy;
var world = matrix_get(matrix_world);
var trans = matrix_multiply(
matrix_build(0, 0, 0, 0, 0, 0, argument4, argument5, 1),
matrix_build(argument2, argument3, 0, 0, 0, argument6, 1, 1, 1));
matrix_set(matrix_world, trans);
draw_rectangle(left, top, right, bottom, argument7);
matrix_set(matrix_world, world);
/// @desc Draws a sprite in the middle of some fixed region, scaling where appropriate.
/// @param sprite {Integer} The id of the sprite to draw.
/// @param index {Integer} The image index of the sprite to draw.
/// @param x1 {Integer} The left-most position of the region.
/// @param y1 {Integer} The top-most position of the region.
/// @param x2 {Integer} The right-most position of the region.
/// @param y2 {Integer} The bottom-most position of the region.
/// @author Kat @katsaii
var x1 = argument2 < argument4 ? argument2 : argument4;
var y1 = argument3 < argument5 ? argument3 : argument5;
var x2 = argument4 > argument2 ? argument4 : argument2;
var y2 = argument5 > argument3 ? argument5 : argument3;
var width_max = x2 - x1;
var height_max = y2 - y1;
var uvs = sprite_get_uvs(argument0, argument1);
var width = sprite_get_width(argument0) * uvs[6];
var height = sprite_get_height(argument0) * uvs[7];
var scale = 1;
switch ((width > width_max) << 0 | (height > height_max) << 1) {
case 1 << 0 | 1 << 1:
// choose best scale orientation
var scalex = width_max / width;
var scaley = height_max / height;
scale = min(scalex, scaley);
break;
case 0 << 0 | 1 << 1:
// scale height
scale = height_max / height;
break;
case 1 << 0 | 0 << 1:
// scale width
scale = width_max / width;
break;
}
// actually draw the sprite
width = floor(width * scale);
height = floor(height * scale);
var cx = mean(x2, x1);
var cy = mean(y2, y1);
var left = floor(cx - width * 0.5);
var top = floor(cy - height * 0.5);
var right = left + width;
var bottom = top + height;
draw_sprite_pos(argument0, argument1,
left, top,
right, top,
right, bottom,
left, bottom,
draw_get_alpha());
/// @desc Returns the type of data structure nested at an index of a ds_list.
/// @param id {Integer} The id of the ds_list to consider.
/// @param pos {Integer} The index of the ds_list to inspect.
/// @author Kat @katsaii
if (is_real(argument0[| argument1])) {
// copy the ds_list into a duplicate list that can be manipulated freely
var clone = ds_list_create();
ds_list_copy(json_list, argument0);
// delete all records other than what's required
repeat (argument1) {
// trim the left hand side of the list
json_list[| 0] = noone;
/* This prevents nested structures from being deleted when we remove
* the record from the cloned map.
*/
ds_list_delete(json_list, 0);
}
repeat (ds_list_size(json_list) - 1) {
// trim the right hand side of the list
json_list[| 1] = noone; // see above
ds_list_delete(json_list,1);
}
// encode the final json and free dynamic structures
var obj = ds_map_create();
ds_map_add_list(obj, "list", clone);
var context = json_encode(obj);
clone[| 0] = noone; // see above
ds_map_destroy(obj);
// use the json string to determine the type of the value contained
var trim_start = string_pos("[", context) + 2;
var trim_end = string_length(context) - 3;
var trimmed = string_copy(context,
trim_start,
trim_end - trim_start);
switch (string_char_at(trimmed, 1)) {
// the first character is enough information to infer the type of structure
case "[":
return ds_type_list;
break;
case "{":
return ds_type_map;
break;
}
}
// if the key is not real, there is no chance of it being a data structure
return -1;
/// @desc Returns the type of data structure nested at a key of a ds_map.
/// @param id {Integer} The id of the ds_map to consider.
/// @param key {String} The key of the ds_map to inspect.
/// @author Kat @katsaii
if (is_real(argument0[? argument1])) {
// copy the ds_map into a duplicate map that can be manipulated freely
var clone = ds_map_create();
ds_map_copy(clone, argument0);
// delete all records other than what's required
var key = ds_map_find_first(clone);
repeat (ds_map_size(clone)) {
var key_next = ds_map_find_next(json_map,json_map_key);
if (key != argument1) {
clone[? key] = noone;
/* This prevents nested structures from being deleted when we remove
* the record from the cloned map.
*/
ds_map_delete(clone, key);
}
key = key_next;
}
// encode the final json and free dynamic structures
var context = json_encode(clone);
clone[? argument1] = noone; // see above
ds_map_destroy(clone);
// actual inspection occurs here
context = string_replace(context, "\"" + string(argument1) + "\"", "");
var trim_start = string_pos(":", context) + 2;
var trim_end = string_length(context) - 1;
var trimmed = string_copy(context,
trim_start,
trim_end - trim_start);
switch (string_char_at(trimmed, 1)) {
// the first character is enough information to infer the type of structure
case "[":
return ds_type_list;
break;
case "{":
return ds_type_map;
break;
}
}
// if the key is not real, there is no chance of it being a data structure
return -1;
gml_pragma("forceinline");
/// @desc Calculates a triangle wave.
/// @param angle {Real} The angle in degrees to consider.
return tri(degtorad(argument0));
gml_pragma("global", @'
global.keyboard_mapping = undefined;');
/// @desc Disables/enables the keyboard.
/// @param enable {Bool} Whether to enable the keyboard.
/// @author Kat @katsaii
if (argument0) {
// revert remaps
if (global.keyboard_mapping != undefined) {
var maps = global.keyboard_mapping;
var key = ds_map_find_first(maps);
repeat (ds_map_size(maps)) {
keyboard_set_map(key, maps[? key]);
// goto next key
key = ds_map_find_next(maps, key);
}
ds_map_destroy(maps);
global.keyboard_mapping = undefined;
}
} else {
// unmap keyboard keys
if (global.keyboard_mapping == undefined) {
var maps = ds_map_create();
for (var key = 0; key < 255; key++) {
switch (key) {
case vk_anykey:
case vk_nokey:
// blacklisted keys
continue;
}
maps[? key] = keyboard_get_map(key);
keyboard_clear(key);
keyboard_set_map(key, vk_nokey);
}
global.keyboard_mapping = maps;
}
}
/// @desc Returns whether the keyboard is enabled.
/// @author Kat @katsaii
return global.keyboard_mapping == undefined;
gml_pragma("global", @'
global.mouse_clicks = true;');
/// @desc Disables/enables mouse clicks.
/// @param enable {Bool} Whether to enable mouse clicks.
/// @author Kat @katsaii
var enable = bool(argument0);
if (enable ^^ global.mouse_clicks) then global.mouse_clicks = enable;
/// @desc Returns whether mouse clicks are enabled.
/// @author Kat @katsaii
return global.mouse_clicks;
gml_pragma("global", @'
// search for singletons
global.singletons = ds_list_create();
for (var i = 0; object_exists(i); i++) {
if (object_prefix(i) == SINGLETON_PREFIX) {
ds_list_add(global.singletons, i);
// automatically insert singleton into the first room
object_set_persistent(i, true);
room_instance_add(room_first, 0, 0, i);
}
}');
#macro SINGLETON_PREFIX "the"
/// @desc Returns whether an object is a singleton
/// @param obj {Integer} The id of the object to check.
/// @author Kat @katsaii
if (is_real(argument0) && !object_exists(argument0) &&
instance_exists(argument0)) then argument0 = argument0.object_index;
return ds_list_find_index(global.singletons, argument0) != -1;
/// @desc Returns the object prefix
/// @param obj {Integer} The id of the object to check.
/// @author Kat @katsaii
var obj = argument0;
if (is_real(obj) && !object_exists(obj) &&
instance_exists(obj)) then obj = obj.object_index;
var name = object_get_name(obj);
var pos = string_pos("_", name);
return pos == -1 ? "" : string_copy(name, 1, pos - 1);
/// @desc Finds the minimum distance between a point and a 2D line.
/// @param x {Real} The X position of the point.
/// @param y {Real} The Y position of the point.
/// @param u1 {Real} The first X position of the line.
/// @param w1 {Real} The first Y position of the line.
/// @param u2 {Real} The second X position of the line.
/// @param w2 {Real} The second Y position of the line.
var qx = argument0;
var qy = argument1;
var px = argument2;
var py = argument3;
var ux = argument4 - px;
var uy = argument5 - py;
var nx = uy;
var ny = -ux;
return (dot_product(nx, ny, px, py) - dot_product(nx, ny, qx, qy)) /
dot_product(nx, ny, nx, ny);
#macro VAR "foo"
#macro GETTER variable_global_get(VAR)
#macro SETTER \
for (var __value = GETTER;; { \
if (is_real(__value) && __value > 0) { \
variable_global_set(VAR, __value); \
} else { \
show_error("Global property '" + VAR + "' must be a positive number", false); \
} \
break; \
}) __value
/*
## Examples
Getting and setting:
```gml
SETTER = 12;
show_message(GETTER); // prints: '12'
```
Setting an invalid value:
```gml
SETTER = 1.0; // success
SETTER = "five"; // error
```
Using the addition assignment operator on the property:
```gml
SETTER = 2; // value: 2
SETTER += 5; // value: 7
```
You may also be able to create a getter-setter using the macro:
```gml
#macro GETSET GETTER; SETTER
```
with the limitation of not being able to use it 'inline' like the original getter.
In other words, the following is invalid code:
```gml
GETSET = 3;
show_message(GETSET); // invalid token
```
since the macro `GETSET` will be transformed into:
```gml
GETTER; SETTER = 3;
show_message(GETTER; SETTER); // invalid token
```
which is obviously invalid.
*/
#macro mouse_check_button scr_mouse_click
/// @desc Checks for a mouse click.
/// @param button {Integer} The id of the mouse button to check.
/// @author Kat @katsaii
return global.mouse_clicks && device_mouse_check_button(0, argument0);
#macro mouse_check_button_pressed scr_mouse_click_pressed
/// @desc Checks for a mouse click.
/// @param button {Integer} The id of the mouse button to check.
/// @author Kat @katsaii
return global.mouse_clicks && device_mouse_check_button_pressed(0, argument0);
#macro mouse_check_button_released scr_mouse_click_released
/// @desc Checks for a mouse click.
/// @param button {Integer} The id of the mouse button to check.
/// @author Kat @katsaii
return global.mouse_clicks && device_mouse_check_button_released(0, argument0);
/// @desc Returns the id of the current script.
/// @author Kat @katsaii
var callstack = debug_get_callstack();
var header = "gml_Script_";
var delimiter = ":";
for (var i = 1; i < array_length_1d(callstack); i++) {
var currentTrace = callstack[i];
if (string_pos(header, currentTrace) == 1) {
var name = string_replace(currentTrace, header, "");
var length = string_pos(delimiter, name);
if (length > 0) then name = string_copy(name, 1, length - 1);
if (asset_get_type(name) == asset_script) then
return asset_get_index(name);
}
}
return -1;
/// @desc Tokenises a string into a list of values.
/// @param str {String} The string to tokenise.
/// @author Kat @katsaii
var str = argument0;
var tokens = [];
var len = string_length(str);
for (var i = 1; i <= len; i++) {
var prefix = string_char_at(str, i);
if (char_is_whitespace(prefix)) then continue;
var value = undefined;
switch (prefix) {
case "\"":
// tokenise string
var start = i + 1;
var count = 0;
while (start + count <= len) {
var char = string_char_at(str, start + count);
if (char == "\"") then break;
if (char == "\\") then count++;
count++;
}
i = start + count;
value = string_copy(str, start, count);
break;
default:
// tokenise number or word
var validNumber = true;
var decimal = false;
var start = i;
var count = 0;
while (start + count <= len) {
var char = string_char_at(str, start + count);
if (char_is_whitespace(char)) then break;
switch (char) {
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
break;
case "-":
validNumber = count == 0;
break;
case ".":
if (decimal) {
validNumber = false;
} else {
decimal = true;
}
break;
default:
validNumber = false;
break;
}
count++;
}
i = start + count;
value = string_copy(str, start, count);
if (validNumber) {
value = real(value);
} else {
// keywords
switch (value) {
case "null":
case "void":
case "empty":
case "()":
case "undefined":
value = undefined;
break;
case "noone":
value = noone;
break;
case "global":
value = global;
break;
case "self":
value = self;
break;
case "other":
value = other;
break;
case "all":
value = all;
break;
case "inf":
case "infinity":
value = infinity;
break;
case "NaN":
value = NaN;
break;
case "yes":
case "enable":
case "true":
value = true;
break;
case "no":
case "disable":
case "false":
value = false;
break;
default:
// check for an asset, otherwise just accept the word itself
var index = asset_get_index(value);
if (index != -1) then value = index;
break;
}
}
break;
}
tokens[@ array_length_1d(tokens)] = value;
}
return tokens;
gml_pragma("forceinline");
/// @desc Calculates a triangle wave.
/// @param radian_angle {Real} The angle in radians to consider.
var pheta = argument0 / 2 / pi - 0.25;
return 1 - 4 * abs(round(pheta) - pheta);
/// @desc Returns the size (in bytes) of a vertex format.
/// @param vformat {Integer} The vertex format to inspect.
/// @author Kat @katsaii
var max_size = 256;
/* 32 bytes is a realistic size.
* 256 is an, unrealistic, finite upper limit.
*/
var buff = buffer_create(max_size, buffer_grow, 1);
var vbuff = vertex_create_buffer_from_buffer(buff, argument0);
var vbuff_buff = buffer_create_from_vertex_buffer(vbuff, buffer_grow, 1);
var vcount = vertex_get_number(vbuff);
var size = vcount > 0 ? floor(buffer_get_size(vbuff_buff) / vcount) : -1;
buffer_delete(buff);
vertex_delete_buffer(vbuff);
buffer_delete(vbuff_buff);
return size;
gml_pragma("forceinline");
/// @desc Sets the window to the desired size and centres it.
/// @param w {Integer} The width to set the window to.
/// @param h {Integer} The height to set the window to.
window_set_rectangle(
(display_get_width() - argument0) div 2,
(display_get_height() - argument1) div 2,
argument0, argument1);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment