You are not logged in.
A very small script that prevented tons of else-ifs/switches in certain situations
/// pick(val, case1, return1, case2, return2, ...)
//
// returns one of the values or undefined
//
// val - value to compare with
// case<x> - compare this to val
// return<x> - return this if case<x> equals to val
//
/// GMLscripts.com/license
function pick() {
var val = argument[0];
for(var i = 1; i < argument_count; i += 2) if argument[i] == val return argument[i+1];
return undefined;
}
Example:
enum e_TYPE { A, B, C, D }
type = e_TYPE.C;
var c = pick(type, e_TYPE.A, c_red, e_TYPE.B, c_lime, e_TYPE.C, c_orange, e_TYPE.D, c_aqua);
draw_set_color(c);
Main issue with this script is that it uses macros to make some tricks with GML, and replace things on compiling game. While this is OK for very advanced programmers, I think it's not a style that should be promoted for clean code.
Well it was never really meant to be clean. The main goal was to make it look simple, quick to type and being able to access variables without passing them as arguments first. Dirty macros were the only thing that came to mind
Hmm wouldn't this be a little bit faster?
return floor(number) == number;
A foreach loop for arrays, lists, maps, structs, grids, strings and number ranges.
This foreach was made using macros so you don't have to pass variables like arguments. You can access them inside of the loop directly.
Reserved keywords: foreach, in, in_reversed, exec, as_list, as_grid, as_map, fe, fe_break, global.FEDATA
Changes:
[v2.0.8] "Shut up Feather" update
Feather - no more errors and red lines (still some warnings tho)
Speed - around 15% faster
The insides are much nicer to look at
Stack is limited to max 10 nested loops (I've never seen anyone use more than 4 but you can edit the value if you need more for some reason)
[v2.0.7] Tiny update
Updated autodetect
Added fe_break; to correctly break the loop. There is no way to do that for return tho so you still have to call fe.done(); before return statement
[v2.0.6] Tiny update
Removed temp local variables. All is now hidden inside global.FEDATA
Also replaced the global struct with global array so theoretically it should be a bit faster
[v2.0.5] Syntax update
The syntax is much simpler thanks to Gamer-XP
Syntax
foreach <var> in <data> exec
<var> - variable
<data> - any supported data
The only datatypes that require exact specifications are DS types
foreach v in <some_ds_map> as_map exec
foreach v in <some_ds_list> as_map exec
foreach v in <some_ds_grid> as_grid exec
If you use return statement call fe.done() before otherwise it will freeze
To break the loop use fe_break otherwise it's not gonna clean up the loop from the memory
continue can be used normally
The variable fe contains these variables:
- index fe.i (array, list, number range, string)
- key fe.key (map, struct)
- position fe.xpos, fe.ypos (grid)
- write function fe.set(val) (anything but string and number range)
EXAMPLES HERE:
https://github.com/mh-cz/GameMaker-Foreach
/// Foreach
//
// foreach <var> in <data> exec
//
// <var> - variable
// <data> - any supported data
//
/// GMLscripts.com/license
// v2.0.8
// global var for each value (it's faster this way)
global.FEDATA0 = array_create(10, undefined); // STACK
global.FEDATA1 = undefined; // CURRENT LOOP (fe)
global.FEDATA2 = false; // DATA LOADED
global.FEDATA3 = false; // i < len
global.FEDATA4 = -1; // CURRENT STACK INDEX
#macro fe global.FEDATA1
#macro fe_break { fe.done(); break; }
#macro foreach \
for(global.FEDATA2 = false; true; { var
#macro in \
= global.FEDATA2 ? fe.get() : _FeAuto_(false,
#macro in_reverse \
= global.FEDATA2 ? fe.get() : _FeAuto_(true,
#macro exec \
); if !global.FEDATA3 fe_break; }) if global.FEDATA2
#macro as_list ,undefined,ds_type_list
#macro as_map ,undefined,ds_type_map
#macro as_grid ,undefined,ds_type_grid
function _FeAuto_(a0, a1, a2 = undefined, a3 = undefined) {
if a2 == undefined and a3 != undefined switch(a3) {
case ds_type_list: fe = new _FeList_(a0, a1); break;
case ds_type_map: fe = new _FeMap_(a1); break;
case ds_type_grid: fe = new _FeGrid_(a0, a1); break;
default: throw "Unsupported DS type";
}
else if is_array(a1) fe = new _FeArray_(a0, a1);
else if is_string(a1) fe = new _FeString_(a0, a1);
else if is_struct(a1) fe = new _FeStruct_(a1);
else if is_numeric(a1) {
if a2 == undefined fe = new _FeRange_(0, a1, 1);
else if a3 == undefined fe = new _FeRange_(a1, a2, 1)
else fe = new _FeRange_(a1, a2, a3)
}
else throw "Unsupported type";
global.FEDATA2 = true;
global.FEDATA0[@ ++global.FEDATA4] = fe;
return fe.get(false);
}
function _FeArray_() constructor {
rev = argument0; // had to do it this way
data = argument1; // otherwise Feather won't shut up
len = array_length(data);
i = rev ? len-1 : 0;
step = rev ? -1 : 1;
static set = function(v) {
data[@ i] = v;
}
static next = function() {
i += step;
return in_range();
}
static in_range = function() {
return i < len and i > -1;
}
static get = function(nxt = true) {
global.FEDATA3 = (nxt ? next() : in_range());
return (global.FEDATA3 ? data[@ i] : undefined);
}
static done = function() {
fe = (--global.FEDATA4 != -1 ? global.FEDATA0[@ global.FEDATA4] : undefined);
}
}
function _FeList_() constructor {
rev = argument0;
data = argument1;
len = ds_list_size(data);
i = rev ? len-1 : 0;
step = rev ? -1 : 1;
static set = function(v) {
data[| i] = v;
}
static next = function() {
i += step;
return in_range();
}
static in_range = function() {
return i < len and i > -1;
}
static get = function(nxt = true) {
global.FEDATA3 = (nxt ? next() : in_range());
return (global.FEDATA3 ? data[| i] : undefined);
}
static done = function() {
fe = (--global.FEDATA4 != -1 ? global.FEDATA0[@ global.FEDATA4] : undefined);
}
}
function _FeMap_() constructor {
data = argument0;
i = 0;
key = "";
keys = ds_map_keys_to_array(data);
len = array_length(keys);
step = 1;
static set = function(v) {
data[? key] = v;
}
static next = function() {
i += step;
return in_range();
}
static in_range = function() {
return i < len and i > -1;
}
static get = function(nxt = true) {
global.FEDATA3 = (nxt ? next() : in_range());
if global.FEDATA3 key = keys[@ i];
return (global.FEDATA3 ? data[? key] : undefined);
}
static done = function() {
fe = (--global.FEDATA4 != -1 ? global.FEDATA0[@ global.FEDATA4] : undefined);
}
}
function _FeStruct_() constructor {
data = argument0;
i = 0;
key = "";
keys = variable_struct_get_names(data);
len = array_length(keys);
step = 1;
static set = function(v) {
data[$ key] = v;
}
static next = function() {
i += step;
return in_range();
}
static in_range = function() {
return i < len and i > -1;
}
static get = function(nxt = true) {
global.FEDATA3 = (nxt ? next() : in_range());
if global.FEDATA3 key = keys[@ i];
return (global.FEDATA3 ? data[$ key] : undefined);
}
static done = function() {
fe = (--global.FEDATA4 != -1 ? global.FEDATA0[@ global.FEDATA4] : undefined);
}
}
function _FeGrid_() constructor {
rev = argument0;
data = argument1;
xpos = 0;
ypos = 0;
w = ds_grid_width(data);
h = ds_grid_height(data);
len = w * h;
i = rev ? len-1 : 0;
step = rev ? -1 : 1;
static set = function(v) {
data[# xpos, ypos] = v;
}
static next = function() {
i += step;
xpos = i mod w;
ypos = i div h;
return in_range();
}
static in_range = function() {
return i < len and i > -1;
}
static get = function(nxt = true) {
global.FEDATA3 = (nxt ? next() : in_range());
if global.FEDATA3 {
xpos = i mod w;
ypos = i div h;
}
return (global.FEDATA3 ? data[# xpos, ypos] : undefined);
}
static done = function() {
fe = (--global.FEDATA4 != -1 ? global.FEDATA0[@ global.FEDATA4] : undefined);
}
}
function _FeRange_() constructor {
from = argument0;
to = argument1;
step = argument2;
i = from;
static next = function() {
i += step;
return in_range();
}
static in_range = function() {
return (from < to and i <= to) or (from > to and i >= to);
}
static get = function(nxt = true) {
global.FEDATA3 = (nxt ? next() : in_range());
return (global.FEDATA3 ? i : undefined);
}
static done = function() {
fe = (--global.FEDATA4 != -1 ? global.FEDATA0[@ global.FEDATA4] : undefined);
}
}
function _FeString_() constructor {
rev = argument0;
data = argument1;
len = string_length(data);
i = rev ? len-1 : 0;
step = rev ? -1 : 1;
static next = function() {
i += step;
return in_range();
}
static in_range = function() {
return i < len and i > -1;
}
static get = function(nxt = true) {
global.FEDATA3 = (nxt ? next() : in_range());
return (global.FEDATA3 ? string_char_at(data, i+1) : undefined);
}
static done = function() {
fe = (--global.FEDATA4 != -1 ? global.FEDATA0[@ global.FEDATA4] : undefined);
}
}
A curve that goes through all points
/// draw_curve(points_list, tension, closed)
//
// points_list a list of points, ds_list
// tension 0 to 1, real
// closed bool
//
// each point is an array of 2 values [x, y] and they can be simply added like this:
// ds_list_add(points_list, [x, y]);
//
/// GMLscripts.com/license
function draw_curve(points_list, tension, closed) {
var ps = [];
var points_len = ds_list_size(points_list);
if points_len < 2 return;
if !closed {
for(var i = 0; i < points_len + 1; i++) {
if i == 0 ps[array_length(ps)] = points_list[| i];
if i < points_len ps[array_length(ps)] = points_list[| i];
else ps[array_length(ps)] = points_list[| points_len-1];
}
}
else {
for(var i = 0; i < points_len + 1; i++) {
if i == 0 ps[array_length(ps)] = points_list[| points_len-1];
if i < points_len ps[array_length(ps)] = points_list[| i];
else {
ps[array_length(ps)] = points_list[| 0];
ps[array_length(ps)] = points_list[| 1];
}
}
}
for(var i = 1; i < array_length(ps) - 2; i++) {
var p0 = ps[i - 1];
var p1 = ps[i];
var p2 = ps[i + 1];
var p3 = ps[i + 2];
var m1x = (1 - tension) * (p2[0] - p1[0] + ((p1[0] - p0[0]) / 1 - (p2[0] - p0[0]) * 0.5));
var m2x = (1 - tension) * (p2[0] - p1[0] + ((p3[0] - p2[0]) / 1 - (p3[0] - p1[0]) * 0.5));
var m1y = (1 - tension) * (p2[1] - p1[1] + ((p1[1] - p0[1]) / 1 - (p2[1] - p0[1]) * 0.5));
var m2y = (1 - tension) * (p2[1] - p1[1] + ((p3[1] - p2[1]) / 1 - (p3[1] - p1[1]) * 0.5));
var ax = 2 * p1[0] - 2 * p2[0] + m1x + m2x;
var ay = 2 * p1[1] - 2 * p2[1] + m1y + m2y;
var bx = -3 * p1[0] + 3 * p2[0] - 2 * m1x - m2x;
var by = -3 * p1[1] + 3 * p2[1] - 2 * m1y - m2y;
var cx = m1x;
var cy = m1y;
var dx = p1[0];
var dy = p1[1];
var amount = 25; // number of points in each segment, 25 is fine
var prevx = dx;
var prevy = dy;
for(var j = 1; j <= amount; j++) {
var t = j / amount;
var px = ax * t * t * t + bx * t * t + cx * t + dx;
var py = ay * t * t * t + by * t * t + cy * t + dy;
draw_line(px, py, prevx, prevy);
prevx = px;
prevy = py;
}
}
}
if mouse_check_button_pressed(mb_left) ds_list_add(list, [mouse_x, mouse_y]);
update 2. 12. 2022
https://github.com/mh-cz/draw_sprite_bent
Small performance boost and added return_pts to return the final transformed coordinates of each segment so you can maybe attach something to it
/// draw_sprite_bent(spr, img_index, x, y, around_x, around_y, bend_angle, segments, xscale = 1, yscale = 1, rot = 0, color = c_white, alpha = 1, return_pts = -1)
//
// returns array of points if return_pts != -1
//
// spr sprite index, real
// img_index image index, real
// x draw it here, real
// y draw it here, real
// around_x bend the sprite around this x position, real
// around_y bend the sprite around this y position, real
// bend_angle apply angle, real
// segments the more segments, the better quality (but lower performance), real
// xscale scale on the x axis, real
// yscale scale on the y axis, real
// rot rotation of the final drawn texture (does not affect actual bending and around_x/y position), real
// color colour
// alpha alpha, real
// return_pts return array of transformed coords from bottom to top, real 0 to 1 (-1 is off)
//
// THE IMAGE REQUIRES TO HAVE "Separate texture page" TICKED!
//
// return_pts = 0 -> return points on the left side
// return_pts = 0.5 -> return points in the middle
// return_pts = 1 -> return points on the right side
//
/// GMLscripts.com/license
global.BEND_SPR_DATA = [];
vertex_format_begin();
vertex_format_add_position();
vertex_format_add_colour();
vertex_format_add_texcoord();
global.BEND_SPR_DATA[0] = vertex_format_end(); // vf
global.BEND_SPR_DATA[1] = vertex_create_buffer(); // b
function draw_sprite_bent(spr, img_index, x, y, around_x, around_y, bend_angle, segments, xscale = 1, yscale = 1, rot = 0, color = c_white, alpha = 1, return_pts = -1) {
var spr_w = sprite_get_width(spr);
var spr_h = sprite_get_height(spr);
var xoff = sprite_get_xoffset(spr) * xscale;
var yoff = sprite_get_yoffset(spr) * yscale;
segments = max(4, ceil(segments));
var step = spr_h / segments;
var angle_step = bend_angle / segments;
// split the sprite into triangles with UVs
var uv = array_create(2 + ceil((spr_h + 0.1) / step));
var ci = 0;
uv[ci++] = [1, 0];
var right = false;
for(var h = 0; h < spr_h + 0.1; h += step) {
uv[ci++] = [real(right), h/spr_h];
right = !right;
}
uv[ci++] = [real(right), 1];
// rotate the triangles around pivot and save the new coordinates
var counter = 0;
var angl = 0;
var uvlen = array_length(uv);
var coords = array_create(uvlen);
ci = 0;
var u, v, pivot_x, pivot_y, pdr, pds, xx, yy;
for(var i = uvlen-1; i > -1; i--) {
u = uv[i][0];
v = uv[i][1];
pivot_x = (around_x - (x - xoff)) / xscale;
pivot_y = (around_y - (y - yoff)) / yscale;
pdr = point_direction(pivot_x, pivot_y, u * spr_w, v * spr_h);
pds = point_distance(pivot_x, pivot_y, u * spr_w, v * spr_h);
xx = pivot_x + lengthdir_x(pds, pdr + angl);
yy = pivot_y + lengthdir_y(pds, pdr + angl);
coords[ci++] = [ xx * xscale, yy * yscale, (counter == 1), u, v ];
if counter++ == 2 {
counter = 0;
if i == 0 break;
i += 2;
angl += angle_step;
}
}
var vf = global.BEND_SPR_DATA[0];
var b = global.BEND_SPR_DATA[1];
vertex_begin(b, vf);
var final_pts = return_pts != -1 ? array_create(floor(segments/2)) : [];
var ptsi = 0;
var prev_px = undefined;
var prev_py = undefined;
var this_px = 0;
var this_py = 0;
var c_prev, c_next, c_curr;
for(var i = 0; i < ci; i++) {
// connect corners so the image can stretch
c_curr = coords[i];
if i == clamp(i, 2, ci-3) {
c_prev = coords[i-2];
c_next = coords[i+2];
if c_prev[2] {
c_curr[0] = c_prev[0];
c_curr[1] = c_prev[1];
}
if c_next[2] {
c_curr[0] = c_next[0];
c_curr[1] = c_next[1];
}
}
// apply rotation and draw
xx = c_curr[0] - xoff;
yy = c_curr[1] - yoff;
if rot != 0 {
pdr = point_direction(0, 0, xx, yy);
pds = point_distance(0, 0, xx, yy);
xx = lengthdir_x(pds, pdr + rot);
yy = lengthdir_y(pds, pdr + rot);
}
vertex_position(b, x + xx, y + yy);
vertex_colour(b, color, alpha);
vertex_texcoord(b, c_curr[3], c_curr[4]);
if return_pts != -1 and i > 0 and i % 6 == 1 {
prev_px = x + coords[i-1][0] - xoff;
prev_py = y + coords[i-1][1] - yoff;
this_px = x + xx;
this_py = y + yy;
var ds = point_distance(this_px, this_py, prev_px, prev_py) * return_pts;
var dr = point_direction(this_px, this_py, prev_px, prev_py);
final_pts[ptsi++] = [ this_px + lengthdir_x(ds, dr), this_py + lengthdir_y(ds, dr) ];
}
}
vertex_end(b);
vertex_submit(b, pr_trianglelist, sprite_get_texture(spr, img_index));
return final_pts;
}
Make sure to tick "Separate texture page" otherwise it will be glitched
The green dot is around_x and around_y attached to the mouse cursor
Adjusting the bend_angle with mouse wheel
40 segments
A simple keyboard input without async events (GMS2)
Works with proportional fonts
You can draw multiple of them at the same time
Doesn't support multi line text
Edit: added input_copy and input_set_text
24. 12. 2021 - fixed an error while while using input_copy that I shou've fixed like 2 months ago
/// input_init()
//
// call this once when the game starts
//
/// GMLscripts.com/license
function input_init() {
global.input_map = ds_map_create();
}
/// input_create(input_id)
//
// input_id string
//
// create a new inputbox under this input_id
//
/// GMLscripts.com/license
function input_create(input_id) {
global.input_map[? input_id] = {
str : "",
placeholder_text : "",
prev_str : "",
input_w : 300,
input_h : 32,
hold_delay : 0.25,
spam_delay : 0.02,
hold_timer : 0,
spam_timer : 0,
cursor_flick_speed : 0.4,
cursor_flick_timer : 0,
char_spacing : 1,
cursor_pos : 0,
cursor_vis : true,
has_focus : false,
max_chars : 29,
clamp_width : true,
font : -1,
padding : 4,
text_color : c_white,
placeholder_text_color : c_black,
cursor_color : c_white,
bkg_color : c_dkgray,
focused_bkg_color : merge_color(c_dkgray, c_white, 0.15),
bkg_alpha : 1,
focused_bkg_alpha : 1,
focused_text_color : c_white
};
}
/// input_delete(input_id)
//
// input_id string
//
// delete an inputbox under this input_id
//
/// GMLscripts.com/license
function input_delete(input_id) {
global.input_map[? input_id] = 0;
}
/// input_draw(input_id, x, y, gui)
//
// input_id string
// x inputbox x position, real
// y inputbox y position, real
// gui is this GUI event or not, bool
//
// call in any draw or draw GUI event
//
/// GMLscripts.com/license
function input_draw(input_id, x, y, gui) {
var s = global.input_map[? input_id];
if is_struct(s) {
var mx = mouse_x;
var my = mouse_y;
if gui {
mx = device_mouse_x_to_gui(0);
my = device_mouse_y_to_gui(0);
}
if device_mouse_check_button_pressed(0, mb_left) {
s.has_focus = point_in_rectangle(mx, my, x, y, x + s.input_w, y + s.input_h);
}
var slen = string_length(s.str);
if s.has_focus {
// TYPING
if keyboard_check(vk_anykey) {
s.str = string_insert(keyboard_string, s.str, s.cursor_pos+1);
if s.prev_str != s.str {
s.cursor_pos += string_length(keyboard_string);
s.cursor_flick_timer = 0;
s.cursor_vis = true;
}
s.prev_str = s.str;
slen = string_length(s.str);
}
keyboard_string = "";
// KEYS
if keyboard_check_pressed(vk_right)
or keyboard_check_pressed(vk_left)
or keyboard_check_pressed(vk_backspace)
or keyboard_check_pressed(vk_delete)
or device_mouse_check_button_pressed(0, mb_left) {
s.hold_timer = 0;
s.spam_timer = s.spam_delay * room_speed + 1;
s.cursor_flick_timer = 0;
s.cursor_vis = true;
}
// ctrl+v
if keyboard_check_pressed(ord("V")) and keyboard_check(vk_control) {
if clipboard_has_text() {
var cb = clipboard_get_text();
s.str += cb;
s.cursor_pos += string_length(cb);
slen = string_length(s.str);
}
}
// backspace key
if keyboard_check_pressed(vk_backspace) or (keyboard_check(vk_backspace) and s.hold_timer++ > s.hold_delay * room_speed) {
s.cursor_flick_timer = 0;
s.cursor_vis = true;
if s.spam_timer++ > s.spam_delay * room_speed {
s.spam_timer = 0;
if s.cursor_pos > 0 {
s.str = string_delete(s.str, s.cursor_pos--, 1);
slen = string_length(s.str);
}
}
}
// delete key
if keyboard_check_pressed(vk_delete) or (keyboard_check(vk_delete) and s.hold_timer++ > s.hold_delay * room_speed) {
s.cursor_flick_timer = 0;
s.cursor_vis = true;
if s.spam_timer++ > s.spam_delay * room_speed {
s.spam_timer = 0;
if s.cursor_pos < slen {
s.str = string_delete(s.str, s.cursor_pos+1, 1);
slen = string_length(s.str);
}
}
}
// move cursor right
if keyboard_check_pressed(vk_right) or (keyboard_check(vk_right) and s.hold_timer++ > s.hold_delay * room_speed) {
s.cursor_flick_timer = 0;
s.cursor_vis = true;
if s.spam_timer++ > s.spam_delay * room_speed {
s.spam_timer = 0;
s.cursor_pos++;
}
}
// move cursor left
if keyboard_check_pressed(vk_left) or (keyboard_check(vk_left) and s.hold_timer++ > s.hold_delay * room_speed) {
s.cursor_flick_timer = 0;
s.cursor_vis = true;
if s.spam_timer++ > s.spam_delay * room_speed {
s.spam_timer = 0;
s.cursor_pos--;
}
}
}
else {
s.cursor_flick_timer = 0;
s.cursor_vis = false;
}
// text max length clamp
if string_length(s.str) > s.max_chars s.str = string_copy(s.str, 1, s.max_chars);
// MOUSE + DRAW
if s.has_focus {
draw_set_color(s.focused_bkg_color);
draw_set_alpha(s.focused_bkg_alpha);
}
else {
draw_set_color(s.bkg_color);
draw_set_alpha(s.bkg_alpha);
}
draw_rectangle(x, y, x + s.input_w, y + s.input_h, false);
draw_set_alpha(1);
draw_set_font(s.font);
draw_set_color(s.placeholder_text_color);
// placeholder text
if slen == 0 draw_text(x + s.padding, y + s.padding, s.placeholder_text);
if s.has_focus {
draw_set_color(s.focused_text_color);
}
else {
draw_set_color(s.text_color);
}
var chrx = s.padding;
var nearest_dist = 9999999;
for(var i = 0; i < slen+2; i++) {
// move cursor to mouse position
if s.has_focus and device_mouse_check_button_pressed(0, mb_left) {
var check_curs_pos = x + chrx;
var dist = abs(mx - check_curs_pos);
if dist < nearest_dist {
nearest_dist = dist;
s.cursor_pos = i-1;
}
}
// draw each character of the text
if i == clamp(i, 1, slen+1) {
var ch = string_char_at(s.str, i);
var chw = string_width(ch);
draw_text(x + chrx, y + s.padding, ch)
chrx += chw + s.char_spacing;
if s.clamp_width and i < slen {
if chrx + string_width(string_char_at(s.str, i+1)) + s.padding > s.input_w {
s.str = string_copy(s.str, 1, i);
s.cursor_pos = i;
slen = string_length(s.str);
break;
}
}
}
}
// CURSOR STUFF
s.cursor_pos = clamp(s.cursor_pos, 0, slen);
if s.cursor_flick_timer++ > s.cursor_flick_speed * room_speed {
s.cursor_flick_timer = 0;
s.cursor_vis = !s.cursor_vis;
}
draw_set_color(s.cursor_color);
if s.cursor_vis {
var cx = s.padding + string_width(string_copy(s.str, 1, s.cursor_pos)) + s.cursor_pos * s.char_spacing;
draw_line(
x + cx,
y + s.padding - 2,
x + cx,
y + s.padding + 2 + string_height("I")
);
}
}
else show_debug_message("Unknown input ID: " + input_id);
}
/// input_copy(input_id, new_input_id)
//
// input_id string
// new_input_id string
//
/// GMLscripts.com/license
function input_copy(input_id, new_input_id) {
input_create(new_input_id);
var s1 = global.input_map[? input_id];
var s2 = global.input_map[? new_input_id];
if is_struct(s1) and is_struct(s2) {
s2.str = s1.str;
s2.placeholder_text = s1.placeholder_text;
s2.input_w = s1.input_w;
s2.input_h = s1.input_h;
s2.hold_delay = s1.hold_delay;
s2.spam_delay = s1.spam_delay;
s2.cursor_flick_speed = s1.cursor_flick_speed;
s2.char_spacing = s1.char_spacing;
s2.max_chars = s1.max_chars
s2.clamp_width = s1.clamp_width
s2.font = s1.font
s2.padding = s1.padding
s2.text_color = s1.text_color
s2.focused_text_color = s1.focused_text_color
s2.placeholder_text_color = s1.placeholder_text_color
s2.cursor_color = s1.cursor_color
s2.bkg_color = s1.bkg_color
s2.focused_bkg_color = s1.focused_bkg_color
s2.bkg_alpha = s1.bkg_alpha
s2.focused_bkg_alpha = s1.focused_bkg_alpha
}
}
/// Functions to control the inputbox properties and look
// return what's inside the inputbox
// input_id string
function input_get_text(input_id) {
var s = global.input_map[? input_id];
return is_struct(s) ? s.str : "";
}
// set the text inside of the inputbox
// input_id string
// text string
function input_set_text(input_id, text) {
var s = global.input_map[? input_id];
if is_struct(s) s.str = text;
}
// only when the inputbox is focused you can type in
// you can focus it using mouse or using this script
// just make sure to focus only one inputbox
// input_id string
// focus bool
function input_set_focus(input_id, focus) {
var s = global.input_map[? input_id];
if is_struct(s) s.has_focus = focus;
}
// resize the inputbox (this doesn't shrink the text)
// input_id string
// w real
// h real
function input_set_dimensions(input_id, w, h) {
var s = global.input_map[? input_id];
if is_struct(s) {
s.input_w = w;
s.input_h = h;
}
}
// max characters limit
// set it to something high like 99999 if you don't want to use it
// input_id string
// max_chars real
function input_set_max_chars(input_id, max_chars) {
var s = global.input_map[? input_id];
if is_struct(s) s.max_chars = max_chars;
}
// if clamp is true you will not be able to write more text if you reach the end of the inputbox
// input_id string
// clamp bool
function input_set_clamp_text_width(input_id, clamp) {
var s = global.input_map[? input_id];
if is_struct(s) s.clamp_width = clamp;
}
// set the placeholder text
// this text will only show up when the inputbox is empty
// input_id string
// text string
function input_set_placeholder_text(input_id, text) {
var s = global.input_map[? input_id];
if is_struct(s) s.placeholder_text = string(text);
}
// font
// -1 is default
// input_id string
// font font
function input_set_font(input_id, font) {
var s = global.input_map[? input_id];
if is_struct(s) s.font = font;
}
// move the text further away from borders
// input_id string
// padding real
function input_set_text_padding(input_id, padding) {
var s = global.input_map[? input_id];
if is_struct(s) s.padding = padding;
}
// text color
// input_id string
// col colour
function input_set_text_color(input_id, col) {
var s = global.input_map[? input_id];
if is_struct(s) s.text_color = col;
}
// focused text color
// input_id string
// col colour
function input_set_focused_text_color(input_id, col) {
var s = global.input_map[? input_id];
if is_struct(s) s.focused_text_color = col;
}
// placeholder text color
// input_id string
// col colour
function input_set_placeholder_text_color(input_id, col) {
var s = global.input_map[? input_id];
if is_struct(s) s.placeholder_text_color = col;
}
// background color
// input_id string
// col colour
function input_set_bkg_color(input_id, col) {
var s = global.input_map[? input_id];
if is_struct(s) s.bkg_color = col;
}
// focused background color
// input_id string
// col colour
function input_set_focused_bkg_color(input_id, col) {
var s = global.input_map[? input_id];
if is_struct(s) s.focused_bkg_color = col;
}
// background alpha
// input_id string
// alpha real
function input_set_bkg_alpha(input_id, alpha) {
var s = global.input_map[? input_id];
if is_struct(s) s.bkg_alpha = alpha;
}
// focused background alpha
// input_id string
// alpha real
function input_set_focused_bkg_alpha(input_id, alpha) {
var s = global.input_map[? input_id];
if is_struct(s) s.focused_bkg_alpha = alpha;
}
// cursor color
// input_id string
// col colour
function input_set_cursor_color(input_id, col) {
var s = global.input_map[? input_id];
if is_struct(s) s.cursor_color = col;
}
// how long you need to hold a key before the cursor starts going on its own (in seconds)
// input_id string
// delay real
function input_set_cursor_hold_delay(input_id, delay) {
var s = global.input_map[? input_id];
if is_struct(s) s.hold_delay = delay;
}
// delay when the cursor is going on its own (in seconds)
// input_id string
// delay real
function input_set_cursor_spam_delay(input_id, delay) {
var s = global.input_map[? input_id];
if is_struct(s) s.spam_delay = delay;
}
// cursor flickering speed (in seconds)
// input_id string
// spd real
function input_set_cursor_flick_speed(input_id, spd) {
var s = global.input_map[? input_id];
if is_struct(s) s.cursor_flick_speed = spd;
}
// spacing between characters
// input_id string
// space real
function input_set_char_spacing(input_id, space) {
var s = global.input_map[? input_id];
if is_struct(s) s.char_spacing = space;
}
List of all the functions
// INIT
input_init()
// CREATE / DELETE
input_create(input_id)
input_delete(input_id)
input_copy(input_id, new_input_id)
// DRAW
input_draw(input_id, x, y)
// MAIN
input_set_focus(input_id, focus)
input_set_dimensions(input_id, w, h)
input_set_max_chars(input_id, max_chars)
input_set_clamp_text_width(input_id, clamp)
input_set_placeholder_text(input_id, text)
// GET
input_get_text(input_id)
// COLOURS n STUFF
input_set_text(input_id, text)
input_set_font(input_id, font)
input_set_text_padding(input_id, padding)
input_set_text_color(input_id, col)
input_set_focused_text_color(input_id, col)
input_set_placeholder_text_color(input_id, col)
input_set_bkg_color(input_id, col)
input_set_bkg_alpha(input_id, col)
input_set_focused_bkg_color(input_id, col)
input_set_focused_bkg_alpha(input_id, col)
input_set_cursor_color(input_id, padding)
// EXTRA
input_set_cursor_hold_delay(input_id, delay)
input_set_cursor_spam_delay(input_id, delay)
input_set_cursor_flick_speed(input_id, spd)
input_set_char_spacing(input_id, space)
Doesn't work with precision
/// collision_cone(x, y, obj, cone_dir, cone_len, cone_angle, return_arr, force_col_point)
//
// returns array/bool
//
// x x from, real
// y y from, real
// obj object to check, id/object_index
// cone_dir facing direction of the cone, real
// cone_len length of the sides, real
// cone_angle inner cone angle, real
// return_arr return all objects inside as an array, otherwise return true/false, bool
// force_col_point check x, y coordinates only instead of sprite bboxes, bool
//
// if the object doesn't have a sprite it checks the x, y coordinates automatically
//
/// GMLscripts.com/license
function collision_cone(x, y, obj, cone_dir, cone_len, cone_angle, return_arr, force_col_point) {
if !instance_exists(obj) return return_arr ? [] : false;
var obj_arr = [];
cone_len = max(cone_len, 1);
cone_angle = clamp(cone_angle, 0.1, 360);
var tri = [];
var tri_count = 0;
var ab_len = cone_len * 1.425;
var dir = cone_dir - cone_angle * 0.5;
var next_a = 90;
var br = false;
for(var a = 0; a < 360; a += 90) {
if a + next_a >= cone_angle {
next_a = cone_angle - a;
br = true;
}
tri[tri_count++] = [
x + lengthdir_x(ab_len, a + dir),
y + lengthdir_y(ab_len, a + dir),
x + lengthdir_x(ab_len, a + dir + next_a),
y + lengthdir_y(ab_len, a + dir + next_a)
];
if br break;
}
// just in case you'd like to see it
/*draw_circle(x, y, cone_len, true);
draw_line(x, y, tri[0][0], tri[0][1]);
draw_line(x, y, tri[tri_count-1][2], tri[tri_count-1][3]);
draw_set_alpha(0.35);
draw_arrow(x, y, x+lengthdir_x(cone_len, cone_dir), y+lengthdir_y(cone_len, cone_dir), 20);
draw_set_alpha(1);*/
with(obj) {
if sprite_index != -1 and !force_col_point {
if rectangle_in_circle(id.bbox_left, id.bbox_top, id.bbox_right, id.bbox_bottom, x, y, cone_len) {
for(var ti = 0; ti < array_length(tri); ti++) {
if rectangle_in_triangle(id.bbox_left, id.bbox_top, id.bbox_right, id.bbox_bottom,
x, y, tri[ti][0], tri[ti][1], tri[ti][2], tri[ti][3]) {
if !return_arr return true;
else obj_arr[array_length(obj_arr)] = id;
}
}
}
}
else {
if point_in_circle(id.x, id.y, x, y, cone_len) {
for(var ti = 0; ti < array_length(tri); ti++) {
if point_in_triangle(id.x, id.y, x, y, tri[ti][0], tri[ti][1], tri[ti][2], tri[ti][3]) {
if !return_arr return true;
else obj_arr[array_length(obj_arr)] = id;
}
}
}
}
}
return return_arr ? obj_arr : false;
}
Works with angles over 180
A tiny script but can be handy
also does anyone have a better name for it?
/// random_range_towards(mn, mx, to, f, int)
//
// returns real
//
// mn min value, real
// mx max value, real
// to the result will get closer towards this value, real
// f force towards the desired value, real (0-1)
// int round the result or not, bool
//
/// GMLscripts.com/license
function random_range_towards(mn, mx, to, f, int) {
var r = lerp(random_range(mn, mx), to, random(f));
return int ? round(r) : r;
}
min = 0, max = 100, towards = 10
force = 0
> average: 50
force = 0.5
> average: 42
force = 1
> average: 29
Some line x shape intersections converted to GM
Line and circle
/// line_circle_intersection(x1, y1, x2, y2, cx, cy, rad)
//
// returns bool
//
// x1 line x from, real
// y1 line y from, real
// x2 line x to, real
// y2 line y to, real
// cx circle x, real
// cy circle y, real
// rad circle radius, real
//
/// GMLscripts.com/license
function line_circle_intersection(x1, y1, x2, y2, cx, cy, rad) {
var ac1 = cx - x1;
var ac2 = cy - y1;
var ab1 = x2 - x1;
var ab2 = y2 - y1;
var abab = dot_product(ab1, ab2, ab1, ab2);
var acab = dot_product(ac1, ac2, ab1, ab2);
var t = clamp(acab/abab, 0, 1);
var h1 = ab1 * t + x1 - cx;
var h2 = ab2 * t + y1 - cy;
return dot_product(h1, h2, h1, h2) <= rad * rad
}
Line and rectangle
/// line_rect_intersection(x1, y1, x2, y2, rx1, ry1, rx2, ry2)
//
// returns bool
//
// x1 line x from, real
// y1 line y from, real
// x2 line x to, real
// y2 line y to, real
// rx1 rectangle min x, real
// ry1 rectangle min y, real
// rx2 rectangle max x, real
// ry2 rectangle max y, real
//
/// GMLscripts.com/license
function line_rect_intersection(x1, y1, x2, y2, rx1, ry1, rx2, ry2) {
if point_in_rectangle(x1, y1, rx1+1, ry1+1, rx2-1, ry2-1) return true;
if point_in_rectangle(x2, y2, rx1+1, ry1+1, rx2-1, ry2-1) return true;
if ((x1 <= rx1 and x2 <= rx1)
or (y1 <= ry1 and y2 <= ry1)
or (x1 >= rx2 and x2 >= rx2)
or (y1 >= ry2 and y2 >= ry2))
return false;
var m = (y2 - y1) / (x2 - x1);
var yy = m * (rx1 - x1) + y1;
if (yy > ry1 and yy < ry2) return true;
yy = m * (rx2 - x1) + y1;
if (yy > ry1 and yy < ry2) return true;
var xx = (ry1 - y1) / m + x1;
if (xx > rx1 and xx < rx2) return true;
xx = (ry2 - y1) / m + x1;
if (xx > rx1 and xx < rx2) return true;
return false;
}
Line and rotated rectangle (rotates around the middle)
/// line_rot_rect_intersection(x1, y1, x2, y2, rx1, ry1, rx2, ry2, rot)
//
// returns bool
//
// x1 line x from, real
// y1 line y from, real
// x2 line x to, real
// y2 line y to, real
// rx1 rectangle min x, real
// ry1 rectangle min y, real
// rx2 rectangle max x, real
// ry2 rectangle max y, real
// rot rectangle rotation, real
//
/// GMLscripts.com/license
function line_rot_rect_intersection(x1, y1, x2, y2, rx1, ry1, rx2, ry2, rot) {
var pivot_x = (rx1 + rx2) * 0.5;
var pivot_y = (ry1 + ry2) * 0.5;
var s = [
[point_distance(pivot_x, pivot_y, x1, y1), point_direction(pivot_x, pivot_y, x1, y1) - rot],
[point_distance(pivot_x, pivot_y, x2, y2), point_direction(pivot_x, pivot_y, x2, y2) - rot]
];
x1 = pivot_x + lengthdir_x(s[0][0], s[0][1]);
y1 = pivot_y + lengthdir_y(s[0][0], s[0][1]);
x2 = pivot_x + lengthdir_x(s[1][0], s[1][1]);
y2 = pivot_y + lengthdir_y(s[1][0], s[1][1]);
if point_in_rectangle(x1, y1, rx1+1, ry1+1, rx2-1, ry2-1) return true;
if point_in_rectangle(x2, y2, rx1+1, ry1+1, rx2-1, ry2-1) return true;
if ((x1 <= rx1 and x2 <= rx1)
or (y1 <= ry1 and y2 <= ry1)
or (x1 >= rx2 and x2 >= rx2)
or (y1 >= ry2 and y2 >= ry2))
return false;
var m = (y2 - y1) / (x2 - x1);
var yy = m * (rx1 - x1) + y1;
if (yy > ry1 and yy < ry2) return true;
yy = m * (rx2 - x1) + y1;
if (yy > ry1 and yy < ry2) return true;
var xx = (ry1 - y1) / m + x1;
if (xx > rx1 and xx < rx2) return true;
xx = (ry2 - y1) / m + x1;
if (xx > rx1 and xx < rx2) return true;
return false;
}
Line and line
/// line_line_intersection(x1, y1, x2, y2, x3, y3, x4, y4)
//
// returns bool
//
// x1 line 1 x from, real
// y1 line 1 y from, real
// x2 line 1 x to, real
// y2 line 1 y to, real
// x3 line 2 x from, real
// y3 line 2 y from, real
// x4 line 2 x to, real
// y4 line 2 y to, real
//
/// GMLscripts.com/license
function line_line_intersection(x1, y1, x2, y2, x3, y3, x4, y4) {
var s1_x = x2 - x1;
var s1_y = y2 - y1;
var s2_x = x4 - x3;
var s2_y = y4 - y3;
var s = (-s1_y * (x1 - x3) + s1_x * (y1 - y3)) / (-s2_x * s1_y + s1_x * s2_y);
var t = ( s2_x * (y1 - y3) - s2_y * (x1 - x3)) / (-s2_x * s1_y + s1_x * s2_y);
return s >= 0 and s <= 1 and t >= 0 and t <= 1;
}
f hell...
Found a way to get the type
it's pretty heavy tho
nvm
Draw a slider with sprites
Using device_mouse so it'll also work on phones
/// draw_slider(slider_map, slider_id, x, y, sprite_first, sprite_mid, sprite_last, sprite_rider, snaps, scale)
//
// returns nothing
//
// slider_map map to store the slider data, ds_map
// slider_id id for this slider so it can be accessed, real/string
// x x position, real
// y y position, real
// sprite_first the first sprite, sprite
// sprite_mid the middle sprite, sprite
// sprite_last the last sprite, sprite
// sprite_rider that moving thing, sprite
// snaps how many snap points it'll have, real
// scale scale, real
//
// the slider ds_map needs to be created in a Create event (it can be also global)
// to access the values use: slider_map[? <slider_id>][<data_index>]
// data_indexes:
// 0: rider distance from the middle sprite's pivot
// 1: slider value between 0 and 1
// 2: current snapped point
//
// slider_map can be only accessed AFTER it's been initialized by the sliders otherwise it'll throw an error that the value you want to access is not an array
//
// slider_id cannot be "$ID". that is reserved for the current moving slider
// the length of the slider depends on the length of the middle sprite
// if snap is less than 2, it will not snap
//
/// GMLscripts.com/license
function draw_slider(SLIDER_MAP, slider_id, pos_x, pos_y, sprite_first, sprite_mid, sprite_last, sprite_rider, snaps, scale) {
if !ds_map_exists(SLIDER_MAP, "$ID") SLIDER_MAP[? "$ID"] = undefined; // init selected slider if not done already
if !ds_map_exists(SLIDER_MAP, slider_id) SLIDER_MAP[? slider_id] = [0, 0, 0]; // init defaults for this slider_id if not already created [rider_x, 0-1, snap], can be done manually
var mx = device_mouse_x_to_gui(0);
var my = device_mouse_y_to_gui(0);
scale = max(scale, 0.001);
var line_w = sprite_get_width(sprite_mid) * scale;
var rider_w = sprite_get_width(sprite_first) * scale;
var hitbox_h = max(sprite_get_height(sprite_mid) * scale, rider_w);
pos_x += sprite_get_width(sprite_first) * scale;
pos_y += round(hitbox_h / 2);
if snaps < 2 or snaps > line_w snaps = line_w;
var selected_id = SLIDER_MAP[? "$ID"];
if device_mouse_check_button_pressed(0, mb_left) and selected_id == undefined
and point_in_rectangle(mx, my, pos_x - rider_w, pos_y - hitbox_h/2, pos_x + rider_w + line_w, pos_y + hitbox_h/2)
SLIDER_MAP[? "$ID"] = slider_id;
if device_mouse_check_button_released(0, mb_left) and selected_id != undefined SLIDER_MAP[? "$ID"] = undefined;
var rider_x = SLIDER_MAP[? slider_id][0];
if selected_id == slider_id {
var snap_size = line_w / clamp(--snaps, 1, line_w);
rider_x = clamp(round((mx - pos_x) / snap_size) * snap_size, 0, line_w);
SLIDER_MAP[? slider_id] = [rider_x, rider_x / line_w, floor(rider_x / snap_size)];
}
var alpha = draw_get_alpha();
var col = draw_get_color();
draw_sprite_ext(sprite_first, 0, pos_x, pos_y, scale, scale, 0, col, alpha); // first part
draw_sprite_ext(sprite_mid, 0, pos_x, pos_y, scale, scale, 0, col, alpha); // middle part
draw_sprite_ext(sprite_last, 0, pos_x + line_w, pos_y, scale, scale, 0, col, alpha); // last part
draw_sprite_ext(sprite_rider, 0, pos_x + rider_x, pos_y, scale, scale, 0, col, alpha); // rider
}
Ohh ok I get it
And for array you can also use ds_type_list and then check it with is_array()
Already tried that but ds_exists does not check it correctly
When I use
ds_exists(list, ds_type_list)
it returns true
but when I use
ds_exists(list, ds_type_map)
it also returns true
/// foreach(data, type, f_each)
//
// returns nothing
//
// data array / ds_list / ds_map / ds_grid
// type what type of data is entering, ds_type
// f_each what to do with each value, function
//
// type:
// for array / ds_list: ds_type_list
// for ds_map: ds_type_map
// for ds_grid: ds_type_grid
//
// f_each:
// function(data, index, val)
//
// returns undefined or true
//
// data returned data so it's accessible inside of the inner function
// index returned current data index/key
// array: position, real
// ds_list: position, real
// ds_map: key, string
// ds_grid: positions, array[x, y]
//
// val returned current data value
//
// to break the foreach loop simply return true inside of the inner function
//
/// GMLscripts.com/license
function foreach(data, type, f_each) {
switch(type) {
case ds_type_list:
if is_array(data){
var len = array_length(data);
for(var i = 0; i < len; i++) {
if f_each(data, i, data[i]) break;
}
}
else {
var len = ds_list_size(data);
for(var i = 0; i < len; i++) {
if f_each(data, i, data[| i]) break;
}
}
break;
case ds_type_map:
for(var key = ds_map_find_first(data); !is_undefined(key); key = ds_map_find_next(data, key)) {
if f_each(data, key, data[? key]) break;
}
break;
case ds_type_grid:
var w = ds_grid_width(data);
var h = ds_grid_height(data);
for(var xx = 0; xx < w; xx++) {
for(var yy = 0; yy < h; yy++) {
if f_each(data, [xx, yy], data[# xx, yy]) break;
}
}
break;
}
}
Example code:
var list = ds_list_create();
ds_list_add(list, "one");
ds_list_add(list, "two");
ds_list_add(list, "three");
foreach(list, ds_type_list, function(d, i, v) {
if i == 2 return true; // break
show_debug_message(string(i)+": "+string(v));
});
// *console*
// 0: one
// 1: two
Might be useful for people using HTTP stuff
Works just like regular base64 but with characters that can be used in URL
/// base64url_encode(string)
//
// returns encoded string
//
// s string to encode, string
//
/// GMLscripts.com/license
function base64url_encode(s) {
s = base64_encode(string(s));
s = string_replace_all(s, "/", "_");
s = string_replace_all(s, "=", "");
s = string_replace_all(s, "+", "-");
return s;
}
/// base64url_decode(string)
//
// returns decoded string
//
// s string to decode, string
//
/// GMLscripts.com/license
function base64url_decode(s) {
s = string(s);
s = string_copy(s + "===", 0, string_length(s) + (string_length(s) % 4));
s = string_replace_all(s, "_", "/");
s = string_replace_all(s, "-", "+");
return base64_decode(s);
}
/// draw_curve_cb(p1, p2, p3, p4, prec)
//
// p1 start point, array [x, y]
// p2 curvature point 1, array [x, y]
// p3 curvature point 2, array [x, y]
// p4 end point, array [x, y]
// prec precision, number of segments, real
//
/// GMLscripts.com/license
function draw_curve_cb(p1, p2, p3, p4, prec) {
prec = round(prec);
var prev_cb = p1;
for(var i = 0; i < prec; i++) {
var cb = cubic_bezier(p1, p2, p3, p4, i / prec);
draw_line(cb[0], cb[1], prev_cb[0], prev_cb[1]);
prev_cb = cb;
}
draw_line(prev_cb[0], prev_cb[1], p4[0], p4[1]);
}
/// cubic_bezier(p1, p2, p3, p4, t)
//
// returns array [x, y], a single point on the curve depending on t
//
// p1 start point, array [x, y]
// p2 curvature point 1, array [x, y]
// p3 curvature point 2, array [x, y]
// p4 end point, array [x, y]
// t curve position, real
//
/// GMLscripts.com/license
function cubic_bezier(p1, p2, p3, p4, t) {
t = clamp(t, 0, 1);
return [
p1[0] * power(1-t, 3) + 3 * p2[0] * power(1-t, 2) * t + 3 * p3[0] * (1-t) * power(t, 2) + p4[0] * power(t, 3),
p1[1] * power(1-t, 3) + 3 * p2[1] * power(1-t, 2) * t + 3 * p3[1] * (1-t) * power(t, 2) + p4[1] * power(t, 3)
];
}
I feel like this is faster than using string_pos because this script checks the string and returns the result in one go
With string_pos you'd need to call it twice and then use string_copy or something to get the result
I actually hesitated a bit when I was submitting this script because of the length and complexity
I'm not happy with so many helper functions in the global scope.
So... I rewrote all the functions and now they are all local
I'm new with GMS2 so I didn't even know that local functions are now a thing (finally tbh)