GMLscripts.com

Discuss and collaborate on GML scripts
Invert

You are not logged in.

#1 2021-12-16 05:58:17

maras
Member
Registered: 2021-04-25
Posts: 24
Website

foreach [v2]

A better and much simpler (to use) version of whetever I've committed in the past here
It's using macros so keywords "foreach", "in" and "as_*" are reserved by the script
+ one global variable called "fed"

All you have to do is to run this script once when the game starts or in some create event
(if you run it multiple times it will scream at you anyway)

update 17. 12. 2021 - added range data type
foreach "number" in [from*, to, step*] as_range { }

Expand/// foreach
//
// usage:
//     foreach <args> in <data> as_<data_type> 
// 
// possible args:
//      ["val"] (or just "val")   	 	-returns only values
//      ["val", step]     				-returns values and uses custom counter
//      ["val", step, startfrom]     	-returns values and uses custom counter and custom start position
//      ["key", "val"]     				-returns keys and values
//      ["key", "val", step]     		-returns keys, values and uses custom counter
//      ["key", "val", step, startfrom] -returns keys, values and uses custom counter and start position
// 
//      "key", "val"     (string)
//           - you can name these variables as you want
//                (choose unused variable names that do not exists yet)
// 	  - if both are used:
// 		  - the first string will always return index/key
// 		  - the second string will always return value
// 	  - "key":
//         - list, array, string 	returns index (real)
// 	       - map, struct 		returns keys (string)
// 	       - grid 			returns [xpos, ypos] (array of reals)
// 
//     step, startfrom     (real)
//           - both optional
//           - step is what is added to the values used for iteration (i += step)
//           - startfrom is where the iterator starts (i = startfrom)
// 
// possible data types:
//      as_array, as_list, as_map, as_struct, as_grid, as_string, as_range
// 
//  note:
//      the system is using a single global variable "fed" (ForEachData)
//      you can rename it, just hit ctrl+f in the script and replace it with whatever name you want
//      
//      there is also a fed.set(val) function for lazy typers so you can simply change the value in the data while looping
// 
//      when you type the args name it should be saying that the variable is used only once
//      which is fine because the variable will be created at runtime
//      DO NOT create these string args variables using the "var" keyword because it's not possible to overwrite variables //      created like this
//      
//      it's still just a loop so you can use break; and continue;
//     
/// GMLscripts.com/license

function foreach_init() {
	
	globalvar fed;
	fed = {
		stacks: ds_stack_create(),
		cs : -1, // current stack in loop
		
		break_arr: function(stack) {
			if is_array(stack.name) { 
				var name_overwrite = "";
				var len = array_length(stack.name);
				
				if len == 1 name_overwrite = stack.name[0];
				else if len > 1 {
					if is_string(stack.name[0]) and is_string(stack.name[1]) {
						stack.iname = stack.name[0];
						name_overwrite = stack.name[1];
						if len >= 3 stack.step = stack.name[2];
						if len == 4 stack.startfrom = stack.name[3];
					}
					else if is_string(stack.name[0]) and is_real(stack.name[1]) {
						name_overwrite = stack.name[0];
						stack.step = stack.name[1];
						if len == 3 stack.startfrom = stack.name[2];
					}
				}
				stack.name = name_overwrite;
			}
		},
		
		break_real_arr: function(stack) {
			if is_array(stack.input) {
				var len = array_length(stack.input);
				if len == 1 stack.to = stack.input[0];
				else if len >= 2 {
					stack.from = stack.input[0];
					stack.to = stack.input[1];
					if len == 3 stack.step = (stack.input[2] == 0) ? 1 : abs(stack.input[2]);
				}
			}
			else stack.to = stack.input;
		},
		
		set: function(val) { 
			switch(fed.cs.data_type) {
				case 0: fed.cs.input[@ fed.cs.i] = val; break;
				case 1: fed.cs.input[| fed.cs.i] = val; break;
				case 2: fed.cs.input[? fed.cs.key] = val; break;
				case 3: fed.cs.input[# fed.cs.xpos, fed.cs.ypos] = val; break;
				case 4: fed.cs.input = string_delete(fed.cs.input, fed.cs.i+1, 1);
						fed.cs.input = string_insert(string(val), fed.cs.input, fed.cs.i+1); break;
				case 5: fed.cs.input[$ fed.cs.key] = val; break;
			}
			return val;
		}
	};
	
	#macro foreach \
		ds_stack_push(fed.stacks, { name: "", iname: "", input: undefined, i: 0, startfrom: 0, step: 1, len: 0, xpos: 0, xlen: 0, ypos: 0, ylen: 0, key: "", keys: [], data_type: -1, from: 0, to: 0 }); \
		ds_stack_top(fed.stacks).name =
	
	#macro in \
		ds_stack_top(fed.stacks).input =
	
	#macro as_array \
		fed.cs = ds_stack_top(fed.stacks); \
		fed.cs.data_type = 0; \
		fed.break_arr(fed.cs); \
		fed.cs.len = array_length(fed.cs.input); \
		if fed.cs.name != "" variable_instance_set(id, fed.cs.name, fed.cs.input[@ fed.cs.startfrom]); \
		if fed.cs.iname != "" variable_instance_set(id, fed.cs.iname, fed.cs.startfrom); \
		for(fed.cs.i = fed.cs.startfrom; true; \
			{ \
				fed.cs.i += fed.cs.step; \
				if fed.cs.i < fed.cs.len { \
					if fed.cs.name != "" variable_instance_set(id, fed.cs.name, fed.cs.input[@ fed.cs.i]); \
					if fed.cs.iname != "" variable_instance_set(id, fed.cs.iname, fed.cs.i); \
				} else { ds_stack_pop(fed.stacks); fed.cs = ds_stack_top(fed.stacks); break; } \
			})
	
	#macro as_list \
		fed.cs = ds_stack_top(fed.stacks); \
		fed.cs.data_type = 1; \
		fed.break_arr(fed.cs); \
		fed.cs.len = ds_list_size(fed.cs.input); \
		if fed.cs.name != "" variable_instance_set(id, fed.cs.name, fed.cs.input[| fed.cs.startfrom]); \
		if fed.cs.iname != "" variable_instance_set(id, fed.cs.iname, fed.cs.startfrom); \
		for(fed.cs.i = fed.cs.startfrom; true; \
			{ \
				fed.cs.i += fed.cs.step; \
				if fed.cs.i < fed.cs.len { \
					if fed.cs.name != "" variable_instance_set(id, fed.cs.name, fed.cs.input[| fed.cs.i]); \
					if fed.cs.iname != "" variable_instance_set(id, fed.cs.iname, fed.cs.i); \
				} else { ds_stack_pop(fed.stacks); fed.cs = ds_stack_top(fed.stacks); break; } \
			})
	
	#macro as_map \
		fed.cs = ds_stack_top(fed.stacks); \
		fed.cs.data_type = 2; \
		fed.break_arr(fed.cs); \
		fed.cs.key = ds_map_find_first(fed.cs.input); \
		repeat(fed.cs.startfrom) { fed.cs.key = ds_map_find_next(fed.cs.input, fed.cs.key); } \
		if fed.cs.name != "" variable_instance_set(id, fed.cs.name, fed.cs.input[? fed.cs.key]); \
		if fed.cs.iname != "" variable_instance_set(id, fed.cs.iname, fed.cs.key); \
		for(; true; \
			{ \
				repeat(fed.cs.step) { fed.cs.key = ds_map_find_next(fed.cs.input, fed.cs.key); } \
				if !is_undefined(fed.cs.key) { \
					if fed.cs.name != "" variable_instance_set(id, fed.cs.name, fed.cs.input[? fed.cs.key]); \
					if fed.cs.iname != "" variable_instance_set(id, fed.cs.iname, fed.cs.key); \
				} else { ds_stack_pop(fed.stacks); fed.cs = ds_stack_top(fed.stacks); break; } \
			})
	
	#macro as_grid \
		fed.cs = ds_stack_top(fed.stacks); \
		fed.cs.data_type = 3; \
		fed.break_arr(fed.cs); \
		fed.cs.xlen = ds_grid_width(fed.cs.input); \
		fed.cs.ylen = ds_grid_height(fed.cs.input); \
		fed.cs.len = fed.cs.xlen * fed.cs.ylen; \
		if fed.cs.name != "" variable_instance_set(id, fed.cs.name, fed.cs.input[# fed.cs.startfrom, fed.cs.startfrom]); \
		if fed.cs.iname != "" variable_instance_set(id, fed.cs.iname, [fed.cs.startfrom, fed.cs.startfrom]); \
		for(; true; \
			{ \
				fed.cs.i += fed.cs.step; \
				if fed.cs.i < fed.cs.len { \
					fed.cs.ypos += fed.cs.step; \
					if fed.cs.ypos > fed.cs.ylen-1 { \
						fed.cs.ypos += fed.cs.step; \
						fed.cs.ypos = 0; \
						fed.cs.xpos++; \
					} \
					if fed.cs.name != "" variable_instance_set(id, fed.cs.name, fed.cs.input[# fed.cs.xpos, fed.cs.ypos]); \
					if fed.cs.iname != "" variable_instance_set(id, fed.cs.iname, [fed.cs.xpos, fed.cs.ypos]); \
				} else { ds_stack_pop(fed.stacks); fed.cs = ds_stack_top(fed.stacks); break; } \
			})
	
	#macro as_string \
		fed.cs = ds_stack_top(fed.stacks); \
		fed.cs.data_type = 4; \
		fed.break_arr(fed.cs); \
		fed.cs.len = string_length(fed.cs.input); \
		if fed.cs.name != "" variable_instance_set(id, fed.cs.name, string_char_at(fed.cs.input, fed.cs.startfrom+1)); \
		if fed.cs.iname != "" variable_instance_set(id, fed.cs.iname, fed.cs.startfrom); \
		for(fed.cs.i = fed.cs.startfrom; true; \
			{ \
				fed.cs.i += fed.cs.step; \
				if fed.cs.i < fed.cs.len { \
					if fed.cs.name != "" variable_instance_set(id, fed.cs.name, string_char_at(fed.cs.input, fed.cs.i+1)); \
					if fed.cs.iname != "" variable_instance_set(id, fed.cs.iname, fed.cs.i); \
				} else { ds_stack_pop(fed.stacks); fed.cs = ds_stack_top(fed.stacks); break; } \
			})
	
	#macro as_struct \
		fed.cs = ds_stack_top(fed.stacks); \
		fed.cs.data_type = 5; \
		fed.break_arr(fed.cs); \
		fed.cs.keys = variable_struct_get_names(fed.cs.input); \
		fed.cs.len = array_length(fed.cs.keys); \
		fed.cs.key = fed.cs.keys[fed.cs.startfrom]; \
		if fed.cs.name != "" variable_instance_set(id, fed.cs.name, fed.cs.input[$ fed.cs.key]); \
		if fed.cs.iname != "" variable_instance_set(id, fed.cs.iname, fed.cs.key); \
		for(fed.cs.i = fed.cs.startfrom; true; \
			{ \
				fed.cs.i += fed.cs.step; \
				if fed.cs.i < fed.cs.len { \
					fed.cs.key = fed.cs.keys[fed.cs.i]; \
					if fed.cs.name != "" variable_instance_set(id, fed.cs.name, fed.cs.input[$ fed.cs.key]); \
					if fed.cs.iname != "" variable_instance_set(id, fed.cs.iname, fed.cs.key); \
				} else { ds_stack_pop(fed.stacks); fed.cs = ds_stack_top(fed.stacks); break; } \
			})
	
	#macro as_range \
		fed.cs = ds_stack_top(fed.stacks); \
		fed.cs.data_type = 6; \
		fed.break_arr(fed.cs); \
		fed.break_real_arr(fed.cs) \
		if fed.cs.name != "" variable_instance_set(id, fed.cs.name, fed.cs.from); \
		for(fed.cs.i = fed.cs.from; true; \
			{ \
				fed.cs.i += sign(fed.cs.to - fed.cs.from) * fed.cs.step; \
				if (fed.cs.from < fed.cs.to and fed.cs.i < fed.cs.to) \
				or (fed.cs.from > fed.cs.to and fed.cs.i > fed.cs.to) { \
					if fed.cs.name != "" variable_instance_set(id, fed.cs.name, fed.cs.i); \
				} else { ds_stack_pop(fed.stacks); fed.cs = ds_stack_top(fed.stacks); break; } \
			})
}

here's some examples

Expandvar arr = ["Bob", "Julie", "John", "Mark"];

foreach "name" in arr as_array {
	show_debug_message(name);
}
/* >>
"Bob"
"Julie"
"John"
"Mark"
*/
Expandvar arr = ["Bob", "Julie", "John", "Mark"];

foreach ["index", "name"] in arr as_array {
	show_debug_message(string(index) + " " + string(name));
}
/* >>
"0 Bob"
"1 Julie"
"2 John"
"3 Mark"
*/
Expandvar arr = ["Bob", "Julie", "John", "Mark"];

foreach ["index", "name", 1, 2] in arr as_array {
	show_debug_message(string(index) + " " + string(name));
}
/* >>
"2 John"
"3 Mark"
*/

you can access any variable from the struct containing all the settings in the current loop
(see how the set function uses this)

Expandfed.cs.<var>;

the foreach is stackable
also if you want to return only the key or index just leave the second string empty

Expandvar inventory = [
	{ weapon: "gun", ammo: 20 },
	{ weapon: "sword", ammo: 0 },
	{ weapon: "something_else", ammo: 500 },
];

foreach ["index", ""] in inventory as_array {
	foreach ["key", "info"] in inventory[index] as_struct {
		show_debug_message([index, info]);
	}
}

fed.set example

Expandvar arr = [1, 2, 3, 4];

foreach "num" in arr as_array {
	fed.set(num + 1);
}
// after foreach >> [2, 3, 4, 5]

// which is the same as
foreach ["index", "num"] in arr as_array {
	arr[@ index] = num + 1;
}

as_range

Expandforeach "n" in 3 as_range {
	show_debug_message(n);
}
/* >>
0
1
2
*/

foreach "n" in [2, -2, 0.5] as_range {
	show_debug_message(n);
}
/* >>
2
1.5
1
0.5
0
-0.5
-1
-1.5
*/

if you try to reuse an existing variable it will NOT overwrite the existing variable
variable_instance_set simply can't do that
this is the biggest problem with this foreach and idk how to solve it

Expandvar index = 100;

var arr = ["Bob", "Julie", "John", "Mark"];

foreach ["index", "name"] in arr as_array {
	show_debug_message(string(index) + " " + string(name));
}
/* >>
"100 Bob"
"100 Julie"
"100 John"
"100 Mark"
*/

you can reuse a variable created by another foreach tho

Expandforeach ["index", "name"] in arr as_array {
	show_debug_message(string(index) + " " + string(name));
}
foreach ["index", "char"] in str as_string {
	show_debug_message(string(index) + " " + string(char));
}

Last edited by maras (2022-01-15 13:14:03)


I'm on the official GM discord > maras#5104

Offline

#2 2022-01-10 16:35:20

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: foreach [v2]

Whoa! I'll have to think about this.


Abusing forum power since 1986.

Offline

Board footer

Powered by FluxBB