GMLscripts.com

Discuss and collaborate on GML scripts
Invert

You are not logged in.

#1 Re: Script Submission » Foreach v2.0.5 » 2022-07-24 03:10:37

gnysek wrote:

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

#2 Re: Script Submission » is_int » 2022-04-09 06:02:20

Hmm wouldn't this be a little bit faster?

Expand return floor(number) == number; 

#3 Script Submission » Foreach v2.0.5 » 2021-12-16 05:58:17

maras
Replies: 3

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 + global variable FEDATA

Changes
[v2.0.5] Syntax update
The syntax is much simpler again thanks to Gamer-XP

Syntax

Expandforeach <var> in <data> exec

    <var> - variable
    <data> - any supported data

The only datatypes that require exact specifications are DS types

Expandforeach 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

You can use break and continue

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

Expand/// Foreach v2.0.5
//
// foreach <var> in <data> exec
// 
//     <var> - variable
//     <data> - any supported data
//     
/// GMLscripts.com/license

global.FEDATA = { stack: [], current: undefined };

#macro fe global.FEDATA.current

#macro foreach \
	for(var _DataLoadeD_ = false, _CanLooP_ = false; true; { \
	if _DataLoadeD_ { _CanLooP_ = true; if !fe.next() { fe.done(); break; } var 

#macro in \
	= fe.get(); } else { _DataLoadeD_ = true; fe = _FeDetectType_(false, 

#macro in_reverse \
	= fe.get(); } else { _DataLoadeD_ = true; fe = _FeDetectType_(true, 

#macro exec \
	); array_push(global.FEDATA.stack, fe); }}) if _CanLooP_

#macro as_list ,[ds_type_list]
#macro as_map ,[ds_type_map]
#macro as_grid ,[ds_type_grid]

function _FeDetectType_(reversed, data) {
	
	if is_array(data) return new _FeArray_(reversed, data);
	else if is_string(data) return new _FeString_(reversed, data);
	else if is_struct(data) return new _FeStruct_(data);
	else if argument_count == 3 and is_array(argument[2]) {
		switch(argument[2][0]) {
			case ds_type_list: return new _FeList_(reversed, data);
			case ds_type_map:  return new _FeMap_(data);
			case ds_type_grid: return new _FeGrid_(reversed, data);
			default: throw "Unsupported ds type: " + string(argument[2][0]);
		}
	}
	else if is_numeric(data) {
		if argument_count <= 2 return new _FeRange_(data);
		else if argument_count == 3 return new _FeRange_(data, argument[2])
		else return new _FeRange_(data, argument[2], argument[3]);
	}
	else throw "Unsupported type: " + string(data);
}

function _FeArray_(reversed, data) constructor {

	self.data = data;
	self.i = -1;
	self.len = array_length(data);
	self.step = 1;
		
	if reversed {
		i = len;
		step = -step;
	}

	static set = function(v) {
		data[@ i] = v;
	}

	static next = function() {
		i += step;
		return i < len and i > -1;
	}

	static get = function() {
		return data[i];
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

function _FeList_(reversed, data) constructor {

	self.data = data;
	self.i = -1;
	self.len = ds_list_size(data);
	self.step = 1;

	if reversed {
		i = len;
		step = -step;
	}

	static set = function(v) {
		data[| i] = v;
	}

	static next = function() {
		i += step;
		return i < len and i > -1;
	}

	static get = function() {
		return data[| i];
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

function _FeMap_(data) constructor {

	self.data = data;
	self.i = -1;
	self.key = "";
	self.keys = ds_map_keys_to_array(data);
	self.len = array_length(keys);
	self.step = 1;

	static set = function(v) {
		data[? key] = v;
	}

	static next = function() {
		i += step;
		return i < len;
	}

	static get = function() {
		key = keys[i];
		return data[? key];
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

function _FeStruct_(data) constructor {

	self.data = data;
	self.i = -1;
	self.key = "";
	self.keys = variable_struct_get_names(data);
	self.len = array_length(keys);
	self.step = 1;

	static set = function(v) {
		data[$ key] = v;
	}

	static next = function() {
		i += step;
		return i < len;
	}

	static get = function() {
		key = keys[i];
		return data[$ key];
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

function _FeGrid_(reversed, data) constructor {

	self.data = data;
	self.xpos = 0;
	self.ypos = 0;
	self.w = ds_grid_width(data);
	self.h = ds_grid_height(data);
	self.i = -1;
	self.len = w * h;
	self.step = 1;

	if reversed {
		i = len;
		step = -step;
	}

	static set = function(v) {
		data[# xpos, ypos] = v;
	}

	static next = function() {
		i += step;
		return i < len and i > -1;
	}

	static get = function() {
		xpos = i mod w;
		ypos = i div h;
		return data[# xpos, ypos];
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

function _FeRange_() constructor {

	self.from = -1;
	self.to = 1;
	self.step = 1;
	self.i = 0;

	switch(argument_count) {
		case 1:
			to = argument[0];
			i = from;
			break;
		case 2:
			from = argument[0] - step;
			to = argument[1];
			i = from;
			break;
		case 3:
			step = abs(argument[2]);
			from = argument[0] - step;
			to = argument[1];
			i = from;
			break;
	}

	static next = function() {
		i += step;
		return (from < to and i <= to) or (from > to and i >= to);
	}

	static get = function() {
		return i;
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

function _FeString_(reversed, data) constructor {

	self.data = data;
	self.i = -1;
	self.len = string_length(data);
	self.step = 1;

	if reversed {
		i = len;
		step = -step;
	}

	static next = function() {
		i += step;
		return i < len and i > -1;
	}

	static get = function() {
		return string_char_at(data, i+1);
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

#4 Script Submission » draw_curve (cardinal spline) » 2021-12-06 12:46:04

maras
Replies: 0

A curve that goes through all points

Expand/// 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;
	    }
	}
}

FMDteuO.gif

Expandif mouse_check_button_pressed(mb_left) ds_list_add(list, [mouse_x, mouse_y]);

#5 Script Submission » Draw a bent sprite » 2021-09-09 15:36:37

maras
Replies: 1

Don't remember seeing any GM script that can do that soo... here it is

Expand/// draw_sprite_bent(vf, spr, img, x, y, around_x, around_y, bend_angle, segments, xscale, yscale, rot, color, alpha)
//
// returns nothing
//
//	vf	vertex format, get it with get_vf()
//	spr	sprite index, real
//	img	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
//
// *note*
// create the "vf" variable in the Create event or create it once somewhere and make it global
// there is no need to keep recreating it every step
//
// the default number of segments is around 20 but more will be required for larger bend angles
// 
// if the sprite is a png with alpha around DON'T FORGET TO TICK THE "Separate texture page" BUTTON
// otherwise it will mess the sprite up
// 
/// GMLscripts.com/license

function draw_sprite_bent(vf, spr, img, x, y, around_x, around_y, bend_angle, segments, xscale, yscale, rot, color, alpha) {
	
	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;
	
	var prec = max(2, round(segments));
	var step = spr_h / prec;
	var angle_step = bend_angle / prec;
	
	// split the sprite into triangles with UVs
	
	var uv = [];
	var c_len = 0;
	
	uv[c_len++] = [1, 0];
	
	var on_right = false;
	for(var h = 0; h < spr_h + step * 0.5; h += step) {
		
		uv[c_len++] = [real(on_right), h/spr_h];
		on_right = !on_right;
	}
	uv[c_len++] = [real(on_right), 1];
	
	// rotate the triangles around pivot and save their coordinates
	
	var counter = 0;
	var angl = 0;
	var coords = [];
	c_len = 0;
	
	for(var i = array_length(uv)-1; i > -1; i--) {
		
		var u = uv[i][0];
		var v = uv[i][1];
		
		var pivot_x = (around_x - (x - xoff)) / xscale;
		var pivot_y = (around_y - (y - yoff)) / yscale;
		
		var pdr = point_direction(pivot_x, pivot_y, u * spr_w, v * spr_h);
		var pds = point_distance(pivot_x, pivot_y, u * spr_w, v * spr_h);
		var xx = pivot_x + lengthdir_x(pds, pdr + angl);
		var yy = pivot_y + lengthdir_y(pds, pdr + angl);
		
		coords[c_len++] = [xx * xscale, yy * yscale, (counter == 1), u, v];
		
		if counter == 2 {
			counter = 0;
			
			if i == 0 break;
			
			i += 2;
			angl += angle_step;
		}
		else counter++;
	}
	
	// draw it
	
	var b = vertex_create_buffer();
	vertex_begin(b, vf);
	
	for(var i = 0; i < array_length(coords); i++) {
		
		if i == clamp(i, 2, array_length(coords)-3) {
			
			var c_prev = coords[i-2];
			var c_next = coords[i+2];
			if c_prev[2] coords[i] = [ c_prev[0], c_prev[1], false, coords[i][3], coords[i][4] ];
			if c_next[2] coords[i] = [ c_next[0], c_next[1], false, coords[i][3], coords[i][4] ];
		}
		
		var xx = coords[i][0] - xoff;
		var yy = coords[i][1] - yoff;
		
		if rot != 0 {
			var pdr = point_direction(0, 0, xx, yy);
			var 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, coords[i][3], coords[i][4]);
	}
	
	vertex_end(b);
	vertex_submit(b, pr_trianglelist, sprite_get_texture(spr, img));
}
Expandfunction get_vf() {
	vertex_format_begin();
	vertex_format_add_position();
	vertex_format_add_colour();
	vertex_format_add_textcoord();
	return vertex_format_end();	
}

The green dot is around_x and around_y attached to the mouse cursor
Adjusting the bend_angle with mouse wheel
40 segments
ezgif-2-b48a104d230e.gif

#6 Script Submission » InputBox » 2021-08-06 12:32:29

maras
Replies: 0

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 sleep

Expand/// 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

Expand// 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)

3Pwv8IH.gif

#7 Script Submission » collision_cone » 2021-07-26 12:42:14

maras
Replies: 0

Doesn't work with precision

Expand/// 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
5IlmU3P.png

#8 Script Submission » random_range_towards » 2021-07-25 09:53:28

maras
Replies: 0

A tiny script but can be handy

also does anyone have a better name for it?

Expand/// 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

#9 Script Submission » Line x shape collisions » 2021-07-13 15:30:03

maras
Replies: 0

Some line x shape intersections converted to GM

Line and circle

Expand/// 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

Expand/// 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)

Expand/// 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

Expand/// 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;
}

#11 Re: Script Submission » foreach » 2021-06-20 18:16:46

Found a way to get the type
it's pretty heavy tho

Expandnvm

#12 Script Submission » draw_slider » 2021-06-18 21:46:17

maras
Replies: 0

Draw a slider with sprites
Using device_mouse so it'll also work on phones

Expand/// 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
}

cqd9AF0.png

#13 Re: Script Submission » foreach » 2021-06-17 16:11:18

Ohh ok I get it
And for array you can also use ds_type_list and then check it with is_array()

#14 Re: Script Submission » foreach » 2021-06-17 10:57:58

Already tried that but ds_exists does not check it correctly
When I use

Expandds_exists(list, ds_type_list)

it returns true
but when I use

Expandds_exists(list, ds_type_map)

it also returns true

https://www.reddit.com/r/gamemaker/comm … _and_data/

#15 Script Submission » foreach » 2021-06-16 07:08:37

maras
Replies: 8
Expand/// 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:

Expandvar 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

#16 Script Submission » base64 URL encode / decode » 2021-06-10 03:08:08

maras
Replies: 1

Might be useful for people using HTTP stuff
Works just like regular base64 but with characters that can be used in URL

Expand/// 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;
}
Expand/// 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);
}

#17 Script Submission » cubic_bezier - Draw a curved line » 2021-06-01 05:53:58

maras
Replies: 0
Expand/// cubic_bezier(p1, p2, p3, p4, t)
//
// returns array [x, y], a single point on the curve depending on t
//
//    p1    start point, array
//    p2    curvature point 1, array
//    p3    curvature point 2, array
//    p4    end point, array
//    t     curve point position, real
//
// t needs to be between 0 and 1
//
/// 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)
    ];
}


Example code

ExpandCREATE:
points = [[x,y], [x+50,y+100], [x+100, y-100], [x+150, y]];
precision = 20; // smoothness of the line

DRAW:
var prev_cb = points[0]; // start point
for(var i = 0; i < precision; i++)
	{
	var cb = cubic_bezier(points[0], points[1], points[2], points[3], i / precision);
	draw_line(cb[0], cb[1], prev_cb[0], prev_cb[1]); // connect points between
	prev_cb = cb;
	}
draw_line(prev_cb[0], prev_cb[1], points[3][0], points[3][1]); // connect end point

AovkbN5.gif

#18 Re: Script Submission » string_get_between » 2021-05-26 18:30:04

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

#19 Re: Script Submission » string_match - a RegEx like script » 2021-05-25 15:40:50

I actually hesitated a bit when I was submitting this script because of the length and complexity

xot wrote:

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)

#20 Script Submission » string_match - a RegEx like script » 2021-05-25 06:47:03

maras
Replies: 2

Not as powerful as RegEx but good enough for some basic stuff
Tutorial below the code

Expand/// string_match(str, match, show_err<optional>)
//
// checks if a string is formatted correctly
//
// returns
//     true/false    (if show_err = false)
//     true/string   (if show_err = true)
//
//     str             string to check, string
//     match           match format, string
//     show_err        show where the check failed (optional), bool
//
//    made for GMS2
//
/// GMLscripts.com/license

function string_match(str, match, show_err) {
	
	if is_undefined(show_err) show_err = false;
	
	if str == "" or match == ""
		{
		if show_err return "str_is_empty";
		return false;
		}
	
	var __compare_conditions_with_string = function(cond_str, str) {
		
		var __check_custom_condition = function(cond_str, str) {
			
			var __string_get_between = function(str, substr1, substr2, skip) {
	
				var first_found = false;
				var str_between = "";
	
				var times_found = 0;
	
				var str_length = string_length(str);
				var substr1_length = string_length(substr1);
				var substr2_length = string_length(substr2);
	
				for(var i = 1; i < str_length+1; i++)
					{
					if !first_found
						{
						if string_ord_at(str, i) == string_ord_at(substr1, 1)
							{
							var same_chars = true;
							for(var s = 0; s < substr1_length-1; s++)
								{
								same_chars = string_ord_at(str, i+s) == string_ord_at(substr1, s);
								if !same_chars break;
								}
				
							if same_chars
								{
								if is_undefined(skip) or (!is_undefined(skip) and times_found++ == skip)
									{
									first_found = true;
									i += substr1_length-1;
									}
								}
							}
						}
					else
						{
						if string_ord_at(str, i) == string_ord_at(substr2, 1)
							{
							var same_chars = true;
							for(var s = 0; s < substr2_length-1; s++)
								{
								same_chars = string_ord_at(str, i+s) == string_ord_at(substr2, s);
								if !same_chars break;
								}
							if same_chars return str_between;
							}
			
						str_between += string_char_at(str, i);
						}
					}
	
				if first_found str_between += string_char_at(str, i);
				return str_between;
			}
			
			var __check_chars_and_char_ranges = function(char_str, str, allow) {

				var ranges = [];
				var chars = "";
				var char_str_len = string_length(char_str);
	
				for(var i = 1; i < char_str_len+1; i++)
					{
					var ch = string_char_at(char_str, i);
					var ch1 = string_char_at(char_str, i+1);
					var ch2 = string_char_at(char_str, i+2);
		
					if ch1 == "-" and ch2 != "" and string_lettersdigits(ch) == ch and string_lettersdigits(ch2) == ch2
						{
						var arr_l = array_length(ranges);
						ranges[arr_l] = ord(ch);
						ranges[arr_l+1] = ord(ch2);
						i += 2;
						}
					else chars += ch;
					}
	
				var ranges_len = array_length(ranges);
				var chars_len = string_length(chars);
				var str_len = string_length(str);
	
				for(var i = 1; i < str_len+1; i++)
					{
					var this_ord = string_ord_at(str, i);
					var in_range = false;
					var in_chars = false;
		
					for(var i2 = 1; i2 < chars_len+1; i2++)
						{
						in_chars = string_ord_at(chars, i2) == this_ord;
						if in_chars { if allow break; else return chr(this_ord); }
						}
		
					for(var r = 0; r < ranges_len; r += 2)
						{
						in_range = clamp(this_ord, ranges[r], ranges[r+1]) == this_ord;
						if in_range { if allow break; else return chr(this_ord); }
						}
		
					if allow and !in_range and !in_chars return chr(this_ord);
					}
	
				return true;
			}
			
			var __string_split = function(str, substr) {
	
				var sub_len = string_length(substr);
				var n = string_count(substr, str);
				var arr = [];
	
				if n == 0 return [str];

				for(var i = 0; i < n; i++)
				    {
				    var p = string_pos(substr, str) - 1;
				    arr[i] = string_copy(str, 1, p);
				    str = string_delete(str, 1, p + sub_len);
				    }
				if str != "" arr[i] = str;
	
				return arr;
			}
	
			var separator = string_pos("==", cond_str) != 0 ? "==" : "!=";
			var split = __string_split(cond_str, separator);
	
			var first_part = string_pos(",", split[0]) != 0 ? __string_split(split[0], ",") : [split[0]];
			var second_part = split[1];
	
			var first_part_fixed = [];	
			var positions = [];
	
			var arr_len = array_length(first_part); 
			for(var i = 0, i2 = 0; i < arr_len; i++)
				{
				var s = first_part[i];
				if string_char_at(s, 1) == "<" and i+1 < arr_len
					{
					if string_pos("|>", first_part[i+1]) != 0
						{
						first_part_fixed[i2++] = s + "," + first_part[i+1];
						i++;
						continue;
						}
					}
				first_part_fixed[i2++] = s;
				}
	
			var arr_len = array_length(first_part_fixed); 
			for(var i = 0; i < arr_len; i++)
				{
				var s = first_part_fixed[i];
				var firstchar = string_char_at(s, 1);
		
				if firstchar == "<"
					{
					var str_btw = "";
					var str_len = string_length(s);
					for(var i2 = str_len; i2 > 0; i2--)
						{
						var ch = string_char_at(s, i2);
						if ch == "+" or ch == "-"
							{
							var btw = __string_get_between(s, "<|", "|>", 0);
					
							var _chars = "";
							var _char_str_len = string_length(btw);
							for(var _i = 1; _i < _char_str_len+1; _i++)
								{
								var _ch = string_char_at(btw, _i);
								var _ch1 = string_char_at(btw, _i+1);
								var _ch2 = string_char_at(btw, _i+2);
						
								if _ch1 == "-" and _ch2 != "" and string_lettersdigits(_ch) == _ch and string_lettersdigits(_ch2) == _ch2
									{
									_i += 2;
									var ord1 = ord(_ch);
									var ord2 = ord(_ch2);
									for(var rang = ord1; rang < ord2+1; rang++) { _chars += chr(rang); }
									}
								else _chars += _ch;
								}
					
							positions[array_length(positions)] = [_chars, ch, str_btw];
							break;
							}
						str_btw = ch + str_btw;
						}
					}
				else if firstchar == "n"
					{
					var ch1 = string_char_at(s, 2);
					var ch2 = string_char_at(s, 3);
					if ch1 == "-" and ch2 != "" and string_digits(ch2) == ch2
						{
						positions[array_length(positions)] = string_length(str) - real(ch2);
						}
					else positions[array_length(positions)] = string_length(str);
					}
				else if string_digits(s) == s positions[array_length(positions)] = real(s);
				}
	
			var comparing_with = __string_get_between(second_part, "<|", "|>", 0);
			var arr_len = array_length(positions);
			for(var i = 0; i < arr_len; i++)
				{
				var p = positions[i];
				if is_real(p)
					{
					var ch_at = string_char_at(str, p);
					var result = __check_chars_and_char_ranges(comparing_with, ch_at, separator == "==");
					if result != true return "forbidden_char: '"+result+"' in: '"+str+"' at_pos: '"+string(p)+"' condition: :("+cond_str;
					}
				else
					{
					var ranges = [];
					var chars = "";
					var char_str_len = string_length(p[0]);
	
					for(var i = 1; i < char_str_len+1; i++)
						{
						var ch = string_char_at(p[0], i);
						var ch1 = string_char_at(p[0], i+1);
						var ch2 = string_char_at(p[0], i+2);
		
						if ch1 == "-" and ch2 != "" and string_lettersdigits(ch) == ch and string_lettersdigits(ch2) == ch2
							{
							var arr_l = array_length(ranges);
							ranges[arr_l] = ord(ch);
							ranges[arr_l+1] = ord(ch2);
							i += 2;
							}
						else chars += ch;
						}
				
					var chars = p[0];
					var chars_len = string_length(chars);
					for(var i2 = 1; i2 < chars_len+1; i2++)
						{
						var curr_chr = string_char_at(chars, i2);			
						var str_len = string_length(str);
						for(var i3 = 1; i3 < str_len+1; i3++)
							{
							//show_debug_message(curr_chr+ " " +string_char_at(str, i3))
							if curr_chr == string_char_at(str, i3)
								{
								if string_digits(p[2]) == "" continue;
						
								var pos = p[1] == "+" ? i3 + p[2] : i3 - p[2];
								if pos > 0 and pos < str_len+1
									{
									//show_debug_message("CHECK: "+string_char_at(str, pos));
									var result = __check_chars_and_char_ranges(comparing_with, string_char_at(str, pos), separator == "==");
									if result != true return "forbidden_char: '"+result+"' in: '"+str+"' at_pos: '"+string(pos)+"' condition: :("+cond_str+")";
									}
								}
							}
						}
					}
				}
	
			return true;
		}
		
		var __check_chars_and_char_ranges = function(char_str, str, allow) {

			var ranges = [];
			var chars = "";
			var char_str_len = string_length(char_str);
	
			for(var i = 1; i < char_str_len+1; i++)
				{
				var ch = string_char_at(char_str, i);
				var ch1 = string_char_at(char_str, i+1);
				var ch2 = string_char_at(char_str, i+2);
		
				if ch1 == "-" and ch2 != "" and string_lettersdigits(ch) == ch and string_lettersdigits(ch2) == ch2
					{
					var arr_l = array_length(ranges);
					ranges[arr_l] = ord(ch);
					ranges[arr_l+1] = ord(ch2);
					i += 2;
					}
				else chars += ch;
				}
	
			var ranges_len = array_length(ranges);
			var chars_len = string_length(chars);
			var str_len = string_length(str);
	
			for(var i = 1; i < str_len+1; i++)
				{
				var this_ord = string_ord_at(str, i);
				var in_range = false;
				var in_chars = false;
		
				for(var i2 = 1; i2 < chars_len+1; i2++)
					{
					in_chars = string_ord_at(chars, i2) == this_ord;
					if in_chars { if allow break; else return chr(this_ord); }
					}
		
				for(var r = 0; r < ranges_len; r += 2)
					{
					in_range = clamp(this_ord, ranges[r], ranges[r+1]) == this_ord;
					if in_range { if allow break; else return chr(this_ord); }
					}
		
				if allow and !in_range and !in_chars return chr(this_ord);
				}
	
			return true;
		}
		
		var __check_str_length_is_in_range = function(range, str) {
			
			var __string_get_between = function(str, substr1, substr2, skip) {
	
				var first_found = false;
				var str_between = "";
	
				var times_found = 0;
	
				var str_length = string_length(str);
				var substr1_length = string_length(substr1);
				var substr2_length = string_length(substr2);
	
				for(var i = 1; i < str_length+1; i++)
					{
					if !first_found
						{
						if string_ord_at(str, i) == string_ord_at(substr1, 1)
							{
							var same_chars = true;
							for(var s = 0; s < substr1_length-1; s++)
								{
								same_chars = string_ord_at(str, i+s) == string_ord_at(substr1, s);
								if !same_chars break;
								}
				
							if same_chars
								{
								if is_undefined(skip) or (!is_undefined(skip) and times_found++ == skip)
									{
									first_found = true;
									i += substr1_length-1;
									}
								}
							}
						}
					else
						{
						if string_ord_at(str, i) == string_ord_at(substr2, 1)
							{
							var same_chars = true;
							for(var s = 0; s < substr2_length-1; s++)
								{
								same_chars = string_ord_at(str, i+s) == string_ord_at(substr2, s);
								if !same_chars break;
								}
							if same_chars return str_between;
							}
			
						str_between += string_char_at(str, i);
						}
					}
	
				if first_found str_between += string_char_at(str, i);
				return str_between;
			}
	
			var val_min = real(string_digits(__string_get_between(range, "(", "-", 0)));
			var val_max = real(string_digits(__string_get_between(range, "-", ")", 0)));
			var len = string_length(str);
	
			if val_min != 0 and val_max != 0 return clamp(len, val_min, val_max) == len;
			else
				{
				if val_max != 0 return len <= val_max;
				else return len >= val_min;
				}
		}
	
		var cond_str_len = string_length(cond_str);
		var str_btw = "";
	
		var getting_allowed_chars = false;
		var getting_not_allowed_chars = false;
		var getting_length_range = false
		var getting_custom_condition = false;
		var skipping_string = false;
	
		for(var i = 1; i < cond_str_len+1; i++)
			{
			var ch = string_char_at(cond_str, i);
			var ch1 = string_char_at(cond_str, i+1);
			var ch2 = string_char_at(cond_str, i+2);
		
			var is_collecting_str = getting_not_allowed_chars or getting_allowed_chars or getting_length_range or getting_custom_condition;
		
			switch(ch)
				{
				case "<":
					if !is_collecting_str
						{
						if ch1 == "!" and ch2 == "|"
							{
							getting_not_allowed_chars = true;
							str_btw = "";
							i += 2;
							continue;
							}
						else if ch1 == "|"
							{
							getting_allowed_chars = true;
							str_btw = "";
							i++;
							continue;
							}
						}
				
					if ch1 == "|" and getting_custom_condition skipping_string = true;
				break;
			
				case "|":
					if getting_allowed_chars and ch1 == ">"
						{
						getting_allowed_chars = false;
						var result = __check_chars_and_char_ranges(str_btw, str, true);
						if result != true return "forbidden_char: '"+result+"' in: '"+str+"' condition: '<|"+str_btw+"|>'";
						i++;
						continue;
						}
					else if getting_not_allowed_chars and ch1 == ">"
						{
						getting_not_allowed_chars = false;
						var result = __check_chars_and_char_ranges(str_btw, str, false);
						if result != true return "forbidden_char: '"+result+"' in: '"+str+"' condition: '<!|"+str_btw+"|>'";
						i++;
						continue;
						}
				
					if ch1 == ">" and getting_custom_condition skipping_string = false;
				break;
			
				case "L":
					if !is_collecting_str and ch1 == "("
						{
						getting_length_range = true;
						str_btw = "";
						continue;
						}
				break;
			
				case ")":
					if getting_length_range
						{
						getting_length_range = false;
						var result = __check_str_length_is_in_range(str_btw+")", str);
						if result != true return "str_length_not_in_range: '"+str+"'("+string(string_length(str))+") condition: L'"+str_btw+")'";
						continue;
						}
					else if getting_custom_condition and !skipping_string
						{
						getting_custom_condition = false;
						var result = __check_custom_condition(str_btw, str);
						if result != true return result;
						continue;
						}
				break;
			
				case ":":
					if !is_collecting_str and ch1 == "("
						{
						getting_custom_condition = true;
						str_btw = "";
						i++;
						continue;
						}
				break;
				}
		
			if is_collecting_str str_btw += ch;
			}
	
		return true;
	}
	
	var __divide_match_and_strings = function(m, str) {
	
		var m_len = string_length(m);
		var m_arr = [];
		var is_condition = false;
		var is_str = false;
		var str_btw = "";
	
		// SEPARATE MATCH ARRAYS AND STRINGS
		for(var i = 1; i < m_len+1; i++)
			{
			var ch = string_char_at(m, i);
			var ch1 = string_char_at(m, i+1);
		
			switch(ch)
				{
				case "[":
					if !is_condition and !is_str
						{
						is_condition = true;
						str_btw = "";
						continue;
						}
				break;
				case "]":
					if is_condition and (ch1 == "$" or ch1 == "")
						{
						is_condition = false;
						m_arr[array_length(m_arr)] = [str_btw];
						continue;
						}
				break;
				case "$":
					if !is_condition and !is_str
						{
						is_str = true;
						str_btw = "";
						continue;
						}
					else if is_str and (ch1 == "[" or ch1 == "")
						{
						is_str = false;
						m_arr[array_length(m_arr)] = str_btw;
						continue;
						}
				break;
				}
		
			if is_condition or is_str str_btw += ch;
			}
	
		// SPLIT STRINGS
		var m_arr_len = array_length(m_arr);
		var str_arr = [];

		for(var i = 0; i < m_arr_len; i++)
			{
			var c = m_arr[i];
			if is_array(m_arr[i]) continue;
		
			var p = string_pos(c, str)-1;
			var copy = string_copy(str, 1, p);
		
			if copy != "" str_arr[array_length(str_arr)] = copy;
			str = string_delete(str, 1, p + string_length(c));
			}
		if str != "" str_arr[array_length(str_arr)] = str;
	
		return [m_arr, str_arr];
	}
	
	var divided = __divide_match_and_strings(match, str);
	var match_arr = divided[0];
	var str_arr = divided[1];
	
	if array_length(match_arr) == 0 or array_length(str_arr) == 0
		{
		if show_err return "str_is_empty";
		return false;
		}

	var m_len = array_length(match_arr);
	var i2 = 0;
	
	for(var i = 0; i < m_len; i++)
		{
		var m = match_arr[i];
		if is_array(m)
			{
			if array_length(m) == 0
				{
				if show_err return "str_is_empty";
				return false;
				}
			
			if i2 >= array_length(str_arr) or str_arr[i2] == ""
				{
				if show_err return "string_not_complete";
				else return false;
				}
			
			var result = __compare_conditions_with_string(m[0], str_arr[i2++]);
			if result != true
				{
				if show_err return result;
				else return false;
				}
			}
		}
	
	return true;
}

txFF8nc.png

Board footer

Powered by FluxBB