GMLscripts.com

Discuss and collaborate on GML scripts
Invert

You are not logged in.

#1 Script Submission » convert a sprite mask to binded fixtures (with example) » 2020-09-09 21:36:11

lostdarkwolf
Replies: 0

Here is another example and script. This script can scan a sprite and convert its sprite mask into a bunch of physics fixtures that are built like scanlines. if you want full detail, it can be a bit laggy, but you can reduce detail by increasing the point_precision argument in "physics_fixture_multibind_sprite_lines".

I may end up trying to improve this script in the future.

Physics Destructable Terrain Example (Hosted at host-a.net)

NOTE: I've discovered that the example has bugs, but I'm not sure what the problem is yet.

Expand///physics_fixture_multibind_sprite_lines(point_precision, density*, restitution*, angular_damping*, linear_damping*, friction*, scan_x*, scan_y*)
//
// Binds multiple fixtures to the calling instance.
// The overall shape of the fixtures will roughly match the mask of the local sprite.
// Returns a ds_list that contains all of the individual shapes that were binded to the local instance.
//
// point_precision   precision of scaning for fixtures
// density           physics density for the entire shape.
// restitution       physics restitution for the entire shape.
// angular_damping   physics angular damping for the entire shape.
// linear_damping    physics linear damping for the entire shape.
// friction          physics friction for the entire shape.
// scan_x            where to start scanlines for x.
// scan_y            where to start scanlines for y.
//
// If you don't know where to start scanlines, then omit the last two arguments.
// This script works as scanlines do.
// every individual shape generated by this script has four points.
//
/// GMLscripts.com/license

var var_memory_fixture, var_edge_start_x, var_edge_start_y, var_point_precision, var_cancel_shape, 
    var_density, var_restitution, var_angular_damping, var_linear_damping, var_friction;

var_point_precision=2;
if argument_count>0 var_point_precision=argument[0];
var_density=0
if argument_count>1 var_density=argument[1];
var_restitution=0.1
if argument_count>2 var_restitution=argument[2];
var_angular_damping=0.6
if argument_count>3 var_angular_damping=argument[3];
var_linear_damping=0.6
if argument_count>4 var_linear_damping=argument[4];
var_friction=0.2
if argument_count>5 var_friction=argument[5];
var_edge_start_x=0;
if argument_count>6 var_edge_start_x=argument[6];
var_edge_start_y=0;
if argument_count>7 var_edge_start_y=argument[7];

var var_return_dslist; var_return_dslist=ds_list_create();

fixture_top_offset=floor(sprite_get_xoffset(sprite_index)*image_xscale);
fixture_left_offset=floor(sprite_get_yoffset(sprite_index)*image_yscale);

// if edge_start location is invalid
if not position_meeting(x+var_edge_start_x, y+var_edge_start_y, id) 
or position_meeting(x+var_edge_start_x, y+var_edge_start_y-1, id) { 
 // find a valid edge_start location
 var ii_seen_self;
 ii_seen_self=false
 var_edge_start_y=fixture_left_offset;
 while var_edge_start_y<sprite_width-fixture_top_offset and ii_seen_self=false {
  var_edge_start_x=-fixture_top_offset;
  while var_edge_start_x<sprite_height-fixture_left_offset and ii_seen_self=false {
   if position_meeting(x+var_edge_start_x, y+var_edge_start_y, id) ii_seen_self=true;
   var_edge_start_x+=var_point_precision;
  }
  var_edge_start_y+=var_point_precision;
 }
}
if var_edge_start_y>=sprite_width-fixture_top_offset
and var_edge_start_x>=sprite_height-fixture_left_offset {
 return -1; // sprite mask is empty!
}

scan_point_x=0
scan_point_y=0
var var_x_goto; var_x_goto=0;
var_cancel_shape=false
var var_lose_meeting;

while scan_point_y<sprite_height-var_point_precision {
 var_memory_fixture = physics_fixture_create();
 physics_fixture_set_polygon_shape(var_memory_fixture);
 
 // point 1 and 2
 while (not position_meeting(x+scan_point_x, y+scan_point_y+var_point_precision, id)
        or not position_meeting(x+scan_point_x, y+scan_point_y, id))
 and scan_point_x<=sprite_width+var_point_precision {
  scan_point_x+=var_point_precision
 }
 
 physics_fixture_add_point(var_memory_fixture,scan_point_x, scan_point_y+var_point_precision);
 physics_fixture_add_point(var_memory_fixture,scan_point_x, scan_point_y);
 
 // point 3 and 4
 scan_point_x+=(var_point_precision*2)
 while (position_meeting(x+scan_point_x, y+scan_point_y+var_point_precision, id)
        and position_meeting(x+scan_point_x, y+scan_point_y, id))
 and scan_point_x<=sprite_width+var_point_precision {
  scan_point_x+=var_point_precision
 }
 if scan_point_x<sprite_width scan_point_x-=var_point_precision
 
 physics_fixture_add_point(var_memory_fixture,scan_point_x, scan_point_y);
 physics_fixture_add_point(var_memory_fixture,scan_point_x, scan_point_y+var_point_precision);
 scan_point_x+=(var_point_precision)
 
 while (not position_meeting(x+scan_point_x, y+scan_point_y+var_point_precision, id)
        or not position_meeting(x+scan_point_x, y+scan_point_y, id))
 and scan_point_x<=sprite_width+var_point_precision {
  scan_point_x+=var_point_precision
 }

 if scan_point_x>sprite_width+var_point_precision {
  scan_point_y+=var_point_precision;
  scan_point_x=0;
 }
 
 // finalization
 physics_fixture_set_density(var_memory_fixture, var_density);
 physics_fixture_set_restitution(var_memory_fixture, var_restitution);
 physics_fixture_set_angular_damping(var_memory_fixture, var_angular_damping);
 physics_fixture_set_linear_damping(var_memory_fixture, var_linear_damping);
 physics_fixture_set_friction(var_memory_fixture, var_friction);
 ds_list_add(var_return_dslist, physics_fixture_bind(var_memory_fixture, id))
 physics_fixture_delete(var_memory_fixture);
}

return var_return_dslist;

This is a required script.

Expand///physics_remove_fixture_list(inst, ds_list)
// 
// removes multiple binded fixtures, all stored in a ds_list.
//
// inst      ID of the instance to remove the fixtures from
// ds_list   List of binded fixture IDs that are to be removed from the instance
//
/// GMLscripts.com/license

var ii; ii=0;
repeat ds_list_size(argument1) {
 physics_remove_fixture(argument0, ds_list_find_value(argument1, ii));
 ii+=1;
}
ds_list_destroy(argument1);

#2 Re: Script Submission » generate a random cave for a ds_grid (Update 9/2/20) » 2020-09-02 03:16:08

Feel free to post and/or edit whatever, if you want. I don't mind. I will work on an example, but it wont be ready for about 12 to 48 hours (if nothing unexpected happens with my day plans).

#3 Re: Script Submission » good car physics, old physics engine » 2020-09-02 01:40:17

I mage an example, although it could use a bit more polish. there will be a 2D example as well, but not yet. Example Link GMZ host-a.net
Anyone can feel free to use whatever resources. This includes the provided hover car model. Be aware that I may still use the hover car model in my own project as well. Sharing is caring.

#4 Script Submission » custom coded multiline text entry form » 2020-04-11 02:23:50

lostdarkwolf
Replies: 0

The main reason I made the fixhash scripts, was to try and make good multi-line text fields. The UI for my text fields isn't very pretty, but it is discreet. The functionality is very nice as well, and it doesn't use surfaces.

Some of the required scripts are found here: https://www.gmlscripts.com/forums/viewtopic.php?id=2475
The external required scripts are string_length_line, string_width_fixhash, string_height_fixhash, and chr_width.

It works just like a normal textbox, except that there is no scroll bars. Instead, there is a scroll indicator that only works with the mouse wheel. Note that you may also scroll automatically with arrow key navigation.

Example of use for two text fields:
Create event:
text_field_init()
text_field_prime(0,'ABC')
text_field_prime(1,'123')

step event:
text_field_step(0,0,true,true,x,y,250,100,true);
text_field_step(1,0,true,true,x,y+120,250,100,true);

draw event:
text_field_draw(0,x,y,250,100,$DDDDDD,c_lime,$1D1D1D);
text_field_draw(1,x,y+120,250,100,$DDDDDD,c_lime,$1D1D1D);

get text field content like this:
your_variable=global.SCR_text_field_string[text_field_id]
EDIT: 4-11-20 added missed script, string_line_pos.

Expand///string_line_pos(string, pos, front)
//
// returns the line where pos is at.
// the hash symbol does not produce a newline.
//
// string        the string to get length from, string
// line_number   the line number starting from one, real (integer >0)
//
/// GMLscripts.com/license

var var_back_stop, var_front_stop;
var_back_stop=argument1;
var_front_stop=argument1;
while string_char_at(argument0,var_back_stop-1)!=chr(10)
and string_char_at(argument0,var_back_stop-1)!=chr(13) 
and var_back_stop>=1 {
 var_back_stop-=1;
}
if argument2 {
 while string_char_at(argument0,var_front_stop)!=chr(10)
 and string_char_at(argument0,var_front_stop)!=chr(13) 
 and var_front_stop<=string_length(argument0) {
  var_front_stop+=1;
 }
}
return string_copy(argument0,var_back_stop,var_front_stop-var_back_stop);
Expand///draw_text_fixhash_cropped(x,y,text,left,top,width,height)
//
// draws only the cropped area of the specified string
// the hash symbol does not produce a newline
// this script requires the script string_width_fixhash.
//
// x        x position of the string, real
// y        y position of the string, real
// text     string to draw, string
// halign   horizontal alignment, real (0 = left; 1 = center; 2 = right)
// valign   vertical alignment, real (0 = top; 1 = middle; 2 = bottem)
//
// justification is always left
//
/// GMLscripts.com/license

var var_main_string, var_width, var_letter, var_ii, var_draw_x, var_draw_y, var_line_height, var_space_size;
var_main_string=argument2;
var_main_string=string_replace_all(var_main_string,chr(13)+chr(10),chr(10))
var_ii=1;
var_line_height=string_height('A');
var_space_size=string_width(' ');
var_width=string_width_fixhash(var_main_string);
var_draw_x=-argument3;
var_draw_y=-argument4;
var_spacing=0;
var_line_number=1 // current line number

repeat string_length(var_main_string) {
 var_letter=ord(string_char_at(var_main_string,var_ii))
 switch var_letter {
  case 9: // tab
   var_draw_x+=var_space_size*4; // left
  break;
  case 10: // newline
   var_line_number+=1;
   var_draw_y+=var_line_height;
   var_draw_x=-argument3
  break;
  case 13: // newline
   var_line_number+=1;
   var_draw_y+=var_line_height;
   var_draw_x=-argument3
  break;
  case 32: // space
   var_draw_x+=var_space_size;
  break;
 }
 // if letter is drawable
 if var_letter>=33 and var_letter<=126 {
  // if letter = "#" 
   if var_letter=35 {
    if var_draw_x>=0 and var_draw_y>=0 
    and var_draw_x+string_width('\#')<argument5 and  var_draw_y+var_line_height<argument6 {
     draw_text(argument0+var_draw_x,argument1+var_draw_y,'\#')
    }
    var_draw_x+=string_width('\#')+var_spacing;
   }
   else {
    if var_draw_x>=0 and var_draw_y>=0 
    and var_draw_x+string_width(chr(var_letter))<=argument5 and  var_draw_y+var_line_height<=argument6 {
     draw_text(argument0+var_draw_x,argument1+var_draw_y,chr(var_letter))
    }
    var_draw_x+=string_width(chr(var_letter))+var_spacing;
   }
 }
 var_ii+=1;
}
Expand///draw_text_pos(x,y,text,halign,valign,pos,char)
//
// draws a position indicator for the specified string
// the hash symbol does not produce a newline
// this script requires the scripts string_length_line, string_width_fixhash, and string_height_fixhash.
//
// x        x position of the string, real
// y        y position of the string, real
// text     string to draw, string
// halign   horizontal alignment, real (0 = left; 1 = center; 2 = right)
// valign   vertical alignment, real (0 = top; 1 = middle; 2 = bottem)
//
/// GMLscripts.com/license

var_justify=argument3;
var_justify_vert=argument4;
var_pos=argument5;
var_char=argument6;

var var_main_string, var_width, var_letter, var_ii, var_draw_x, var_draw_y, var_line_height, var_space_size;
var_main_string=argument2;
var_ii=0;
var_line_height=string_height('A');
var_space_size=string_width(' ');
var_width=string_width_fixhash(var_main_string);
if var_justify=0 { // left
 var_draw_x=0;
}
if var_justify=1 { // center
 var_draw_x=-string_width_fixhash(string_copy(var_main_string,1,string_length_line(var_main_string,1)))/2;
}
if var_justify=2 { // right
 var_draw_x=-string_width_fixhash(string_copy(var_main_string,1,string_length_line(var_main_string,1)));
}
if var_justify_vert=0 { // top
 var_draw_y=0;
}
if var_justify_vert=1 { // middle
 var_draw_y=-(string_height_fixhash(var_main_string))/2;
}
if var_justify_vert=2 { // bottem
 var_draw_y=-(string_height_fixhash(var_main_string));
}
var_spacing=0;
var_line_number=1 // current line number

repeat string_length(var_main_string)+1 {
 var_letter=ord(string_char_at(var_main_string,var_ii+1))
 
 // if letter = "#" 
 if var_letter=35 {
  if var_ii=var_pos {
   draw_text(argument0+var_draw_x-(string_width(var_char)/2),argument1+var_draw_y,var_char) // draw char
   return var_ii;
  }
  if var_justify=0 var_draw_x+=string_width('\#')+var_spacing;
  if var_justify=1 var_draw_x+=string_width('\#')+var_spacing;
  if var_justify=2 var_draw_x+=string_width('\#')+var_spacing;
 }
 else {
  if var_ii=var_pos {
   draw_text(argument0+var_draw_x-(string_width(var_char)/2),argument1+var_draw_y,var_char) // draw char
   return var_ii;
  }
  if var_justify=0 var_draw_x+=string_width(chr(var_letter))+var_spacing;
  if var_justify=1 var_draw_x+=string_width(chr(var_letter))+var_spacing;
  if var_justify=2 var_draw_x+=string_width(chr(var_letter))+var_spacing;
 }
 
 if var_letter=9 {
  if var_justify=0 var_draw_x+=var_space_size*4; // left
  if var_justify=1 var_draw_x+=var_space_size*2; // center
  if var_justify=2 var_draw_x+=var_space_size*4; // right
 }
 if var_letter=10 { // newline
  var_line_number+=1;
  var_draw_y+=var_line_height;
  if var_justify=0 var_draw_x=0
  if var_justify=1 var_draw_x=-string_width_fixhash(string_copy(var_main_string, var_ii+1,string_length_line(var_main_string,var_line_number)))/2;
  if var_justify=2 var_draw_x=-string_width_fixhash(string_copy(var_main_string,var_ii+1,string_length_line(var_main_string,var_line_number)));
 }
 if var_letter=13 { // newline
  var_line_number+=1;
  var_draw_y+=var_line_height;
  if var_justify=0 var_draw_x=0
  if var_justify=1 var_draw_x=-string_width_fixhash(string_copy(var_main_string, var_ii+1,string_length_line(var_main_string,var_line_number)))/2;
  if var_justify=2 var_draw_x=-string_width_fixhash(string_copy(var_main_string,var_ii+1,string_length_line(var_main_string,var_line_number)));
 }
 var_ii+=1;
}
Expand///string_mouse_pos_drawcrop_fixhash(x,y,text,left,top,width,height)
//
// returns a position in the specified string where the mouse is located
// returns -1 if no such charactor is found
// the hash symbol does not produce a newline
// this script requires the script string_width_fixhash
//
// x        x position of the string, real
// y        y position of the string, real
// text     string to draw, string
// halign   horizontal alignment, real (0 = left; 1 = center; 2 = right)
// valign   vertical alignment, real (0 = top; 1 = middle; 2 = bottem)
//
// justification is always left
//
/// GMLscripts.com/license


var var_main_string, var_width, var_letter, var_ii, var_draw_x, var_draw_y, var_line_height, var_space_size;
var_main_string=argument2;
var_main_string=string_replace_all(var_main_string,chr(13)+chr(10),chr(10))
var_ii=1;
var_line_height=string_height('A');
var_space_size=string_width(' ');
var_width=string_width_fixhash(var_main_string);
var_draw_x=-argument3;
var_draw_y=-argument4;
var_spacing=0;
var_line_number=1 // current line number

repeat string_length(var_main_string) {
 var_letter=ord(string_char_at(var_main_string,var_ii))
 switch var_letter {
  case 9: // tab
   var_draw_x+=var_space_size*4; // left
  break;
  case 10: // newline
   var_line_number+=1;
   var_draw_y+=var_line_height;
   var_draw_x=-argument3
  break;
  case 13: // newline
   var_line_number+=1;
   var_draw_y+=var_line_height;
   var_draw_x=-argument3
  break;
  case 32: // space
   var_draw_x+=var_space_size;
  break;
 }
 // if letter is drawable
 if var_letter>=33 and var_letter<=126 {
  // if letter = "#" 
   if var_letter=35 {
    if var_draw_x>=0 and var_draw_y>=0 
    and var_draw_x+string_width('\#')<argument5 and  var_draw_y+var_line_height<argument6 {
     if mouse_x=median(var_draw_x,var_draw_x+string_width('\#'),mouse_x)
     and mouse_y=median(var_draw_y,var_draw_y+string_height('\#'),mouse_y) {
      return var_ii;
     }
    }
    var_draw_x+=string_width('\#')+var_spacing;
   }
   else {
    if var_draw_x>=0 and var_draw_y>=0 
    and var_draw_x+string_width(chr(var_letter))<argument5 and  var_draw_y+var_line_height<argument6 {
     if mouse_x=median(argument0+var_draw_x,argument0+var_draw_x+string_width(chr(var_letter)),mouse_x)
     and mouse_y=median(argument1+var_draw_y,argument1+var_draw_y+string_height(chr(var_letter)),mouse_y) {
      return var_ii;
     }
    }
    var_draw_x+=string_width(chr(var_letter))+var_spacing;
   }
 }
 var_ii+=1;
}
return -1;
Expand///chr_printable(keycode, input_format, use_backspace, use_space)
//
// returns true if the keycode produces a printable charactor.
//
// keycode         virtual key code, real
// input_format    format of charactors to consider printable, real
// use_backspace   true if backspace is considered printable, real
// use_space       true if space is considered printable, real
//
/// GMLscripts.com/license

// List of values for input_format:
// 0 = all charactors access (US keyboard)
// 1 = letters only access
// 2 = real number access
// 3 = natural number access

if argument1=0 { // access all (US keyboard)
 if argument2 and argument0=8 return true; // backspace
 if argument3 and argument0=32 return true; // space
 switch argument0 {
  case 106: // numberpad *
  case 107: // numberpad +
  case 109: // numberpad -
  case 110: // numberpad .
  case 111: // numberpad /
  return true;
 }
 if argument0=median(48,57,argument0) return true; // number line 0 to 9
 if argument0=median(65,90,argument0) return true; // letters A to Z
 if argument0=median(96,105,argument0) return true; // numberpad numbers 0 to 9
 if argument0=median(187,192,argument0) return true; // OEMs  =+  ,<  -_  .>  /|  `~
 if argument0=median(219,222,argument0) return true; // OEMs  [{  \|  ]}  '"
}
if argument1=1 { // letter access
 if argument2 and argument0=8 return true; // backspace
 if argument3 and argument0=32 return true; // space
 if argument0=median(65,90,argument0) return true; // letters A to Z
}
if argument1=2 { // real number access
 if argument2 and argument0=8 return true; // backspace
 if argument0=189 and keyboard_check(vk_shift) return true;// 189 + shift = minus (OEM)
 if argument0=190 and not keyboard_check(vk_shift) return true;// 190 + no shift = point (OEM)
 if argument2 and argument0=8 return true; // backspace
 switch argument0 {
  case 109: // numberpad -
  case 110: // numberpad .
  return true;
 }
 if argument0=median(48,57,argument0) return true; // number line 0 to 9
 if argument0=median(96,105,argument0) return true; // numberpad numbers 0 to 9
}
if argument1=2 { // natural number access
 if argument2 and argument0=8 return true; // backspace
 if argument0=median(48,57,argument0) return true; // number line 0 to 9
 if argument0=median(96,105,argument0) return true; // numberpad numbers 0 to 9
}
Expand///text_field_prime()
//
// This script simply initializes the text field system
//
/// GMLscripts.com/license

global.SCR_text_field_repeat_time=0.09;
global.SCR_text_field_repeat_wait=0.4;
global.SCR_text_field_repeat_timer=ceil(room_speed*global.SCR_text_field_repeat_wait);
global.SCR_text_field_pos=1;
global.SCR_text_field_selpos=1;
global.SCR_text_field_active=-1;
global.SCR_text_field_scroll_x=0;
global.SCR_text_field_scroll_y=0;
global.SCR_text_field_edit_switch=false;
Expand///text_field_prime(text_field_id, default_string)
//
// This script can create a text field, as well as reset it.
// Provide your own id, use caution.
//
// text_field_id    desired text field ID to set. real (integer)
// default_string   default string for the text field, string
//
//
//
/// GMLscripts.com/license

global.SCR_text_field_string[argument0]=argument1;
global.SCR_text_field_scroll_y[argument0]=0;
global.SCR_text_field_scroll_x[argument0]=-8;
Expand///text_field_step(text_field_id, text_field_type, allow_backspace, allow_space, x, y, width, height, allow_enter)
//
// Runs per-step functions for a single textbox
// the hash symbol does not produce a newline
// Required scripts: chr_printable, string_width_fixhash, string_height_fixhash, string_line_pos, string_mouse_pos_drawcrop_fixhash.
// 
// text_field_id     integer ID for a single text field, real (integer)
// text_field_type   text field input allowance type, real (integer, see list below)
// allow_backspace   true if use of backspace is allowed, real (boolean)
// allow_space       true if use of space is allowed, real (boolean)
// x                 x location for the left side of the text field, real
// y                 y location for the top of the text field, real
// width             width of the text field in pixels, real
// height            height of the text field in pixels, real
// allow_enter       true if use of space is allowed, real (boolean)
//
/// GMLscripts.com/license

// List of values for text_field_type:
// 0 = all charactors access (US keyboard)
// 1 = letters only access
// 2 = real number access
// 3 = natural number access

// -- manage active text field --
if mouse_check_button_pressed(mb_left) {
 if mouse_x=median(argument4,argument4+argument6,mouse_x)
 and mouse_y=median(argument5,argument5+argument7,mouse_y) {
  // set edit switch for showing the blinker
  global.SCR_text_field_edit_switch=true;
  if global.SCR_text_field_active!=argument0 global.SCR_text_field_active=argument0;
  // set pos to mouse pos on text click
  global.SCR_text_field_pos=string_mouse_pos_drawcrop_fixhash(argument4,argument5,global.SCR_text_field_string[argument0],global.SCR_text_field_scroll_x[argument0],global.SCR_text_field_scroll_y[argument0],argument6,argument7);
  // set pos to random pos on empty-space click
  if global.SCR_text_field_pos=-1 global.SCR_text_field_pos=1+irandom(string_length(global.SCR_text_field_string[argument0]))
 }
 if global.SCR_text_field_active=argument0 
 and mouse_x!=median(argument4,argument4+argument6,mouse_x)
 and mouse_y!=median(argument5,argument5+argument7,mouse_y) {
  global.SCR_text_field_active=-1;
 }
 if not keyboard_check(vk_shift) global.SCR_text_field_selpos=global.SCR_text_field_pos;
}
if mouse_check_button(mb_left) { // selpos update
 if mouse_x=median(argument4,argument4+argument6,mouse_x)
 and mouse_y=median(argument5,argument5+argument7,mouse_y) {
  if global.SCR_text_field_active!=argument0 global.SCR_text_field_active=argument0;
  // set pos to random pos on click
  // global.SCR_text_field_pos=1+irandom(string_length(global.SCR_text_field_string[argument0]))
  // set pos to mouse pos on click
  global.SCR_text_field_selpos=string_mouse_pos_drawcrop_fixhash(argument4,argument5,global.SCR_text_field_string[argument0],global.SCR_text_field_scroll_x[argument0],global.SCR_text_field_scroll_y[argument0],argument6,argument7);
  if global.SCR_text_field_selpos=-1 global.SCR_text_field_selpos=global.SCR_text_field_pos
 }
}
if global.SCR_text_field_active!=argument0 return 0;

if keyboard_check(vk_anykey) {
 // set edit switch for showing the blinker
 global.SCR_text_field_edit_switch=true;
 // scroll ending type limit
 global.SCR_text_field_scroll_x[argument0]=max(global.SCR_text_field_scroll_x[argument0], -argument6+string_width_fixhash(string_line_pos(global.SCR_text_field_string[argument0],global.SCR_text_field_pos,false))+24)
 global.SCR_text_field_scroll_y[argument0]=max(global.SCR_text_field_scroll_y[argument0], -argument7+string_height_fixhash(string_copy(global.SCR_text_field_string[argument0],1,global.SCR_text_field_pos-1)))
 // scroll begining type limit
 global.SCR_text_field_scroll_x[argument0]=min(global.SCR_text_field_scroll_x[argument0], string_width_fixhash(string_line_pos(global.SCR_text_field_string[argument0],global.SCR_text_field_pos,false))-24)
 global.SCR_text_field_scroll_y[argument0]=min(global.SCR_text_field_scroll_y[argument0], string_height_fixhash(string_copy(global.SCR_text_field_string[argument0],1,global.SCR_text_field_pos-1))-string_height('A'))
}
// -- scroll zero limit --
global.SCR_text_field_scroll_x[argument0]=max(-8,global.SCR_text_field_scroll_x[argument0])
global.SCR_text_field_scroll_y[argument0]=max(0,global.SCR_text_field_scroll_y[argument0])

// -- extra navigation --
if keyboard_check(vk_end) {
 global.SCR_text_field_pos=string_length(global.SCR_text_field_string[argument0])+1
 if not keyboard_check(vk_shift) global.SCR_text_field_selpos=global.SCR_text_field_pos;
}
if keyboard_check(vk_home) {
 global.SCR_text_field_pos=1;
 if not keyboard_check(vk_shift) global.SCR_text_field_selpos=global.SCR_text_field_pos;
}

// -- cut, copy, and paste --
if keyboard_check(vk_control) {
 if keyboard_check_pressed(ord("X")) {
  var var_min; var_min=min(global.SCR_text_field_selpos,global.SCR_text_field_pos); 
  clipboard_set_text(string_copy(global.SCR_text_field_string[argument0],var_min,max(global.SCR_text_field_selpos,global.SCR_text_field_pos)-var_min))
  
  // delete selection, if present
  if global.SCR_text_field_selpos!=global.SCR_text_field_pos {
   var_min=min(global.SCR_text_field_selpos,global.SCR_text_field_pos); 
   global.SCR_text_field_string[argument0]=string_delete(global.SCR_text_field_string[argument0], var_min, max(global.SCR_text_field_selpos,global.SCR_text_field_pos)-var_min);
   global.SCR_text_field_selpos=min(global.SCR_text_field_selpos,global.SCR_text_field_pos);
   global.SCR_text_field_pos=min(global.SCR_text_field_selpos,global.SCR_text_field_pos);
  }
 }
 if keyboard_check_pressed(ord("C")) {
  var var_min; var_min=min(global.SCR_text_field_selpos,global.SCR_text_field_pos); 
  clipboard_set_text(string_copy(global.SCR_text_field_string[argument0],var_min,max(global.SCR_text_field_selpos,global.SCR_text_field_pos)-var_min))
 }
 if keyboard_check_pressed(ord("V")) {
  // delete selection, if present
  if global.SCR_text_field_selpos!=global.SCR_text_field_pos {
   var_min=min(global.SCR_text_field_selpos,global.SCR_text_field_pos); 
   global.SCR_text_field_string[argument0]=string_delete(global.SCR_text_field_string[argument0], var_min, max(global.SCR_text_field_selpos,global.SCR_text_field_pos)-var_min);
   global.SCR_text_field_selpos=min(global.SCR_text_field_selpos,global.SCR_text_field_pos);
   global.SCR_text_field_pos=min(global.SCR_text_field_selpos,global.SCR_text_field_pos);
  }

  var var_add_me; var_add_me=clipboard_get_text();
  if is_string(var_add_me) {
   global.SCR_text_field_string[argument0]=string_insert(var_add_me,global.SCR_text_field_string[argument0],global.SCR_text_field_pos)
  }
  global.SCR_text_field_pos+=string_length(var_add_me); // ?? NOT RIGHT
  global.SCR_text_field_selpos=global.SCR_text_field_pos
 }
}

// -- key repeat --
if keyboard_key=vk_nokey or keyboard_check_pressed(keyboard_key) global.SCR_text_field_repeat_timer=ceil(room_speed*global.SCR_text_field_repeat_wait);
if keyboard_key!=vk_nokey and not keyboard_check_pressed(keyboard_key) global.SCR_text_field_repeat_timer-=1;

// -- mouse scrolling --
if mouse_x=median(argument4,argument4+argument6,mouse_x)
and mouse_y=median(argument5,argument5+argument7,mouse_y) {
 // set edit switch for hiding the blinker
 if mouse_wheel_up() or mouse_wheel_down() global.SCR_text_field_edit_switch=false;
 if mouse_y-(argument5+argument7)<mouse_x-(argument4+argument6) {
  global.SCR_text_field_scroll_y[argument0]+=(-mouse_wheel_up()+mouse_wheel_down())*8;
 }
 else {
  global.SCR_text_field_scroll_x[argument0]+=(-mouse_wheel_up()+mouse_wheel_down())*8;
 }
}

// -- arrow key navigation --
if keyboard_check_pressed(vk_right) or (global.SCR_text_field_repeat_timer=0 and keyboard_check(vk_right)) {
 if keyboard_check(vk_control) { // jump to space
  while string_char_at(global.SCR_text_field_string[argument0],global.SCR_text_field_pos)!=' ' 
  and global.SCR_text_field_pos<=string_length(global.SCR_text_field_string[argument0]) {
   global.SCR_text_field_pos+=1;
  }
 }
 else {
  if global.SCR_text_field_pos<=string_length(global.SCR_text_field_string[argument0]) global.SCR_text_field_pos+=1;
 }
 if not keyboard_check(vk_shift) global.SCR_text_field_selpos=global.SCR_text_field_pos;
}
if keyboard_check_pressed(vk_left) or (global.SCR_text_field_repeat_timer=0 and keyboard_check(vk_left)) {
 if keyboard_check(vk_control) { // jump to space
  while string_char_at(global.SCR_text_field_string[argument0],global.SCR_text_field_pos)!=' ' 
  and global.SCR_text_field_pos>1 {
   global.SCR_text_field_pos-=1;
  }
 }
 else {
  if global.SCR_text_field_pos>1 global.SCR_text_field_pos-=1;
 }
 if not keyboard_check(vk_shift) global.SCR_text_field_selpos=global.SCR_text_field_pos;
}
if keyboard_check_pressed(vk_up) or (global.SCR_text_field_repeat_timer=0 and keyboard_check(vk_up)) {
 // control to scroll and go up
 if keyboard_check(vk_control){
  global.SCR_text_field_scroll_y[argument0]=max(0,global.SCR_text_field_scroll_y[argument0]-string_height('A'))
 }
 
 var var_newline_pos, var_new_pos_x;
 var_newline_pos=max(global.SCR_text_field_pos-1,1);
 while (string_char_at(global.SCR_text_field_string[argument0],var_newline_pos)!=chr(10)
 and    string_char_at(global.SCR_text_field_string[argument0],var_newline_pos)!=chr(13)) 
 and   var_newline_pos>1 {
  var_newline_pos-=1;
 }
 
 var_new_pos_x=string_width_fixhash(string_copy(global.SCR_text_field_string[argument0],var_newline_pos+1,max(global.SCR_text_field_pos-var_newline_pos-2,1)))-3
 
 global.SCR_text_field_pos=var_newline_pos;
 while string_width_fixhash(string_line_pos(global.SCR_text_field_string[argument0],global.SCR_text_field_pos-1,false))>var_new_pos_x
 and   global.SCR_text_field_pos>1 {
  global.SCR_text_field_pos-=1;
 }
 if not keyboard_check(vk_shift) global.SCR_text_field_selpos=global.SCR_text_field_pos;
}
if keyboard_check_pressed(vk_down) or (global.SCR_text_field_repeat_timer=0 and keyboard_check(vk_down)) {
 // control to scroll and go down
 if keyboard_check(vk_control){
  global.SCR_text_field_scroll_y[argument0]=min(string_height_fixhash(global.SCR_text_field_string[argument0])-string_height('A'),global.SCR_text_field_scroll_y[argument0]+string_height('A'))
 }

 var var_newline_pos, var_newline_pos2, var_old_pos, var_new_pos_x, var_find_me;
 var_newline_pos=global.SCR_text_field_pos;
 while (string_char_at(global.SCR_text_field_string[argument0],var_newline_pos)!=chr(10)
 and    string_char_at(global.SCR_text_field_string[argument0],var_newline_pos)!=chr(13)) 
 and   var_newline_pos<=string_length(global.SCR_text_field_string[argument0]) {
  var_newline_pos+=1;
 }
 var_newline_pos2=global.SCR_text_field_pos-1;
 while (string_char_at(global.SCR_text_field_string[argument0],var_newline_pos2)!=chr(10)
 and    string_char_at(global.SCR_text_field_string[argument0],var_newline_pos2)!=chr(13)) 
 and   var_newline_pos2>0 {
  var_newline_pos2-=1;
 }
 
 var_new_pos_x=string_width(string_copy(global.SCR_text_field_string[argument0],var_newline_pos2+1,global.SCR_text_field_pos-var_newline_pos2-1))-3
 
 global.SCR_text_field_pos=min(var_newline_pos+1,1+string_length(global.SCR_text_field_string[argument0]));

 while string_width(string_line_pos(global.SCR_text_field_string[argument0],global.SCR_text_field_pos,false))<var_new_pos_x
 and   global.SCR_text_field_pos<=string_length(global.SCR_text_field_string[argument0]) 
 and   string_char_at(global.SCR_text_field_string[argument0],global.SCR_text_field_pos)!=chr(10)
 and   string_char_at(global.SCR_text_field_string[argument0],global.SCR_text_field_pos)!=chr(13) {
  
  global.SCR_text_field_pos+=1;
 }
 if not keyboard_check(vk_shift) global.SCR_text_field_selpos=global.SCR_text_field_pos;
}

// -- text entry --
if keyboard_check_pressed(keyboard_key) or (global.SCR_text_field_repeat_timer=0 and keyboard_check(keyboard_key)) 
and chr_printable(keyboard_key,argument1,false,argument3) 
and not keyboard_check(vk_control) {
 // delete selection, if present
 if global.SCR_text_field_selpos!=global.SCR_text_field_pos {
  var var_min; var_min=min(global.SCR_text_field_selpos,global.SCR_text_field_pos); 
  global.SCR_text_field_string[argument0]=string_delete(global.SCR_text_field_string[argument0], var_min, max(global.SCR_text_field_selpos,global.SCR_text_field_pos)-var_min);
  global.SCR_text_field_selpos=min(global.SCR_text_field_selpos,global.SCR_text_field_pos);
  global.SCR_text_field_pos=min(global.SCR_text_field_selpos,global.SCR_text_field_pos);
 }
 // add last typed charactor to text field at edit position
 global.SCR_text_field_string[argument0]=string_insert(keyboard_lastchar,global.SCR_text_field_string[argument0],global.SCR_text_field_pos);
 // advance pos and selpos
 global.SCR_text_field_pos+=1;
 global.SCR_text_field_selpos+=1;
}
if (keyboard_check_pressed(vk_backspace) or (global.SCR_text_field_repeat_timer=0 and keyboard_check(vk_backspace))) and argument2 {
 // if no selection, remove current charactor (if possible)
 if global.SCR_text_field_selpos=global.SCR_text_field_pos {
  if global.SCR_text_field_pos>1 {
   global.SCR_text_field_string[argument0]=string_delete(global.SCR_text_field_string[argument0],global.SCR_text_field_pos-1,1);
   // subtract from pos and selpos
   global.SCR_text_field_pos-=1;
   global.SCR_text_field_selpos-=1;
  }
 }
 // else, delete selection.
 else {
  var var_min; var_min=min(global.SCR_text_field_selpos,global.SCR_text_field_pos); 
  global.SCR_text_field_string[argument0]=string_delete(global.SCR_text_field_string[argument0], var_min, max(global.SCR_text_field_selpos,global.SCR_text_field_pos)-var_min);
  global.SCR_text_field_selpos=min(global.SCR_text_field_selpos,global.SCR_text_field_pos);
  global.SCR_text_field_pos=min(global.SCR_text_field_selpos,global.SCR_text_field_pos);
 }
}

// -- delete key --
if keyboard_check_pressed(vk_delete) or (global.SCR_text_field_repeat_timer=0 and keyboard_check(vk_delete)) {
 // if no selection, remove back charactor (if possible)
 if global.SCR_text_field_selpos=global.SCR_text_field_pos {
  if global.SCR_text_field_pos<=string_length(global.SCR_text_field_string[argument0]) global.SCR_text_field_string[argument0]=string_delete(global.SCR_text_field_string[argument0],global.SCR_text_field_pos,1);
 }
 // else, delete selection.
 else {
  var var_min; var_min=min(global.SCR_text_field_selpos,global.SCR_text_field_pos); 
  global.SCR_text_field_string[argument0]=string_delete(global.SCR_text_field_string[argument0], var_min, max(global.SCR_text_field_selpos,global.SCR_text_field_pos)-var_min);
  global.SCR_text_field_selpos=min(global.SCR_text_field_selpos,global.SCR_text_field_pos);
  global.SCR_text_field_pos=min(global.SCR_text_field_selpos,global.SCR_text_field_pos);
 }
}
// -- enter key --
if (keyboard_check_pressed(vk_enter) or (global.SCR_text_field_repeat_timer=0 and keyboard_check(vk_enter))) and argument8 {
 // delete selection, if present
 if global.SCR_text_field_selpos!=global.SCR_text_field_pos {
  var var_min; var_min=min(global.SCR_text_field_selpos,global.SCR_text_field_pos); 
  global.SCR_text_field_string[argument0]=string_delete(global.SCR_text_field_string[argument0], var_min, max(global.SCR_text_field_selpos,global.SCR_text_field_pos)-var_min);
  global.SCR_text_field_selpos=min(global.SCR_text_field_selpos,global.SCR_text_field_pos);
  global.SCR_text_field_pos=min(global.SCR_text_field_selpos,global.SCR_text_field_pos);
 }
 // add newline charactor to text field at edit position
 global.SCR_text_field_string[argument0]=string_insert(chr(10),global.SCR_text_field_string[argument0],global.SCR_text_field_pos);
 // advance pos and selpos
 global.SCR_text_field_pos+=1;
 global.SCR_text_field_selpos+=1;
}

// -- reset the repeat timer (if needed) --
if global.SCR_text_field_repeat_timer=0 {
 global.SCR_text_field_repeat_timer=ceil(room_speed*global.SCR_text_field_repeat_time);
}
Expand///text_field_draw(id, x, y, width, height, col_front, col_highlight, col_back)
//
// draws a single text field.
// required scripts: draw_text_pos, draw_text_fixhash_cropped.
//
// id              integer ID for a single text field, real (integer)
// x               x location for the left side of the text field, real
// y               y location for the top of the text field, real
// width           width of the text field in pixels, real
// height          height of the text field in pixels, real
// col_front       color of outline, text, and blinker, real (color)
// col_highlight   color of highlights and mouse scroll indicator, real (color)
// col_back        color of the text field's background, real (color)
//
/// GMLscripts.com/license

color_1=argument5;
color_2=argument6;
color_3=argument7;

// outline
draw_set_color(color_1)
draw_rectangle(argument1,argument2,argument1+argument3,argument2+argument4,true);

// backdrop
draw_set_color(color_3)
draw_rectangle(argument1,argument2,argument1+argument3,argument2+argument4,false);

// mouse scroll indicator
if mouse_x=median(argument1,argument1+argument3,mouse_x)
and mouse_y=median(argument2,argument2+argument4,mouse_y) 
and irandom(8) {
 draw_set_color(color_2)
 if mouse_y-(argument2+argument4)<mouse_x-(argument1+argument3) {
  draw_line(argument1+argument3-1,argument2,argument1+argument3-1,argument2+argument4)
 }
 else {
  draw_line(argument1,argument2+argument4-1,argument1+argument3,argument2+argument4-1)
 }
}

// text blinker cursor
if irandom(8) and global.SCR_text_field_edit_switch=true and global.SCR_text_field_active=argument0 {
 draw_set_color(color_1)
 draw_text_pos(argument1-global.SCR_text_field_scroll_x[argument0],argument2-global.SCR_text_field_scroll_y[argument0],global.SCR_text_field_string[argument0],0,0,global.SCR_text_field_pos-1,"|")
 if global.SCR_text_field_selpos!=global.SCR_text_field_pos {
  draw_set_color(color_2)
  draw_text_pos(argument1-global.SCR_text_field_scroll_x[argument0],argument2-global.SCR_text_field_scroll_y[argument0],global.SCR_text_field_string[argument0],0,0,global.SCR_text_field_selpos-1,"|")
 }
}

// text feild contents
draw_set_color(color_1)
draw_text_fixhash_cropped(argument1,argument2,global.SCR_text_field_string[argument0],global.SCR_text_field_scroll_x[argument0],global.SCR_text_field_scroll_y[argument0],argument3,argument4)

#5 Script Submission » Draw text with formatting and no # as newline » 2020-04-04 18:57:29

lostdarkwolf
Replies: 0

To me, it seems that sometimes the functionality that turns hash symbols into newlines can be counter-productive. That's why I made this set of scripts. "draw_text_fixhash_coded" is just an extra idea that I was able to implement.

"draw_text_fixhash_coded" requires hex_to_dec. Find it here: https://www.gmlscripts.com/script/hex_to_dec

edit: 4-6-20 bugfix in draw_text_fixhash_coded and added "string_width_fixhash_coded".
edit: 4-7-20 minor and non-cosmetic bugfix in draw_text_fixhash_coded and string_width_fixhash_coded. Also added support for commas and spaces in coding between calls. Also, added some missed variabes to var statements with most, if not all scripts.

Expand///chr_width(string)
//
// returns the width of the charactor.
// the hash symbol is not considered a newline.
//
// string   string to get width from, string
//
/// GMLscripts.com/license

if argument0=9 return string_width(' ')*4
if argument0=35 return string_width('\#');
if argument0>=32 and argument0<=126 return string_width(chr(argument0));
Expand///string_length_line(string,line_number)
//
// returns the charactor length of the specified line in string.
// the hash symbol does not produce a newline.
//
// string        the string to get length from, string
// line_number   the line number starting from one, real (integer >0)
//
/// GMLscripts.com/license

var var_ii, var_text; 

var_text=argument0;
repeat argument1 {
var_ii=string_pos(chr(13),var_text)-1 
if var_ii=-1 var_ii=string_pos(chr(10),var_text)-1 
if var_ii=-1 var_ii=string_length(var_text); // end of string
var_text=string_delete(var_text,1,var_ii+1)
if var_text='' return var_ii;
}
return var_ii;
Expand///string_width_fixhash(string)
//
// returns the width of the string.
// the hash symbol does not produce a newline.
// requires script chr_width.
//
// string   string to get width from, string
//
/// GMLscripts.com/license

var var_string_length, var_letter, var_letter_last, var_ii, var_recorded_max, var_recorded_current;
var_ii=1;
var_recorded_current=0;
var_recorded_max=0;
var_string_length=string_length(argument0);

var_letter=string_char_at(argument0,var_ii)
var_letter_last=var_letter;

while var_ii<var_string_length {
 var_recorded_current+=chr_width(ord(var_letter));
 if var_recorded_current>var_recorded_max var_recorded_max=var_recorded_current;
 var_ii+=1;
 var_letter_last=var_letter;
 var_letter=string_char_at(argument0,var_ii)
 if var_letter=chr(10) or var_letter_last=chr(13) var_recorded_current=0;
}
var_recorded_current+=chr_width(ord(var_letter));
if var_recorded_current>var_recorded_max var_recorded_max=var_recorded_current;
return var_recorded_max;
Expand///string_height_fixhash(string)
//
// returns the height of the string.
// the hash symbol does not produce a newline.
//
// string   string to get width from, string
//
/// GMLscripts.com/license

if argument0='' return 0;
var var_tally_1, var_tally_2, var_tally_3;
var_tally_1=string_count(chr(10)+chr(13),argument0);
var_tally_2=string_count(chr(10),argument0)-var_tally_1;
var_tally_3=string_count(chr(13),argument0)-var_tally_1;

return (var_tally_1+var_tally_2+var_tally_3+1)*string_height('A');
Expand///draw_text_fixhash(x,y,text,halign,valign)
//
// draws the specified string
// the hash symbol does not produce a newline
// this script requires the scripts chr_width, string_width_fixhash, and string_height_fixhash.
//
// x        x position of the string, real
// y        y position of the string, real
// text     string to draw, string
// halign   horizontal alignment, real (0 = left; 1 = center; 2 = right)
// valign   vertical alignment, real (0 = top; 1 = middle; 2 = bottem)
//
/// GMLscripts.com/license

var_justify=argument3;
var_justify_vert=argument4;

var var_main_string, var_width, var_letter, var_ii, var_draw_x, var_draw_y, var_line_height;
var_main_string=argument2;
var_main_string=string_replace_all(var_main_string,chr(13)+chr(10),chr(10))
var_ii=1;
var_line_height=string_height('A');
var_space_size=string_width(' ');
var_width=string_width_fixhash(var_main_string);
if var_justify=0 { // left
 var_draw_x=0;
}
if var_justify=1 { // center
 var_draw_x=-string_width_fixhash(string_copy(var_main_string,1,string_length_line(var_main_string,1)))/2;
}
if var_justify=2 { // right
 var_draw_x=-string_width_fixhash(string_copy(var_main_string,1,string_length_line(var_main_string,1)));
}
if var_justify_vert=0 { //top
 var_draw_y=0;
}
if var_justify_vert=1 { //middle
 var_draw_y=-(string_height_fixhash(var_main_string))/2;
}
if var_justify_vert=2 { //bottem
 var_draw_y=-(string_height_fixhash(var_main_string));
}
var_spacing=0;
var_line_number=1//current line number
repeat string_length(var_main_string) {
 var_letter=ord(string_char_at(var_main_string,var_ii))
 switch var_letter {
  case 9: // tab
   if var_justify=0 var_draw_x+=var_space_size*4; // left
   if var_justify=1 var_draw_x+=var_space_size*2; // center
   if var_justify=2 var_draw_x+=var_space_size*4; // right
  break;
  case 10: // newline
   var_line_number+=1;
   var_draw_y+=var_line_height;
   if var_justify=0 var_draw_x=0
   if var_justify=1 var_draw_x=-string_width_fixhash(string_copy(var_main_string, var_ii+1,string_length_line(var_main_string,var_line_number)))/2;
   if var_justify=2 var_draw_x=-string_width_fixhash(string_copy(var_main_string,var_ii+1,string_length_line(var_main_string,var_line_number)));
  break;
  case 13: // newline
   var_line_number+=1;
   var_draw_y+=var_line_height;
   if var_justify=0 var_draw_x=0
   if var_justify=1 var_draw_x=-string_width_fixhash(string_copy(var_main_string, var_ii+1,string_length_line(var_main_string,var_line_number)))/2;
   if var_justify=2 var_draw_x=-string_width_fixhash(string_copy(var_main_string,var_ii+1,string_length_line(var_main_string,var_line_number)));
  break;
  case 32: // space
   if var_justify=0 var_draw_x+=var_space_size;
   if var_justify=1 var_draw_x+=var_space_size;
   if var_justify=2 var_draw_x+=var_space_size;
  break;
 }
 // if letter is drawable
 if var_letter>=33 and var_letter<=126 {
  // if letter = "#" 
  if var_letter=35 {
   draw_text(argument0+var_draw_x,argument1+var_draw_y,'\#')
   if var_justify=0 var_draw_x+=string_width('\#')+var_spacing;
   if var_justify=1 var_draw_x+=string_width('\#')+var_spacing;
   if var_justify=2 var_draw_x+=string_width('\#')+var_spacing;
  }
  else {
   draw_text(argument0+var_draw_x,argument1+var_draw_y,chr(var_letter))
   if var_justify=0 var_draw_x+=string_width(chr(var_letter))+var_spacing;
   if var_justify=1 var_draw_x+=string_width(chr(var_letter))+var_spacing;
   if var_justify=2 var_draw_x+=string_width(chr(var_letter))+var_spacing;
  }
 }
 var_ii+=1;
}
Expand//string_width_fixhash_coded(string)
//
// returns the width of the string.
// the hash symbol does not produce a newline.
// requires script chr_width.
//
// string   string to get width from, string
//
/// GMLscripts.com/license

var var_string_length, var_xscale, var_code_out, var_code_in, var_code_mode, var_letter, var_letter_last, var_ii, var_recorded_max, var_recorded_current;
var_ii=1;
var_recorded_current=0;
var_recorded_max=0;
var_string_length=string_length(argument0);

var_letter=string_char_at(argument0,var_ii)
var_letter_last=var_letter;
var_code_mode=false;
var_code_in="/*" /* Coding works like this */
var_code_out="*/"
var_xscale=1;

while var_ii<var_string_length {
 // 2 = length of code in tag
 if string_copy(argument0,var_ii,2)=var_code_in {
  var_code_mode=true
  var_ii+=2
 }
 // 2 = length of code out tag
 if string_copy(argument0,var_ii,2)=var_code_out {
  var_code_mode=false
  var_ii+=2
 }
 if var_code_mode=false {
  if keyboard_check_direct(vk_space) show_message(var_xscale)
  var_letter_last=var_letter;
  var_letter=string_char_at(argument0,var_ii)
  if var_letter=chr(10) or var_letter_last=chr(13) var_recorded_current=0;
  var_recorded_current+=chr_width(ord(var_letter))*var_xscale;
  if var_recorded_current>var_recorded_max var_recorded_max=var_recorded_current;
  var_ii+=1;
 }
 else {
  var var_is_inknown; var_is_inknown=true;
  if string_copy(argument0,var_ii,7)='xscale:' {
   var var_ii2; var_ii2=0;
   while string_digits(string_char_at(argument0,var_ii+7+var_ii2))=string_char_at(argument0,var_ii+7+var_ii2) 
   or string_char_at(argument0,var_ii+7+var_ii2)='.' {
    var_ii2+=1;
   }
   var_xscale=real(string_copy(argument0,var_ii+7,var_ii2))
   var_ii+=7+var_ii2;
   var_is_inknown=false;
  }
  if var_is_inknown=true var_ii+=1;
 }
}
return var_recorded_max;

This script requires hex_to_dec. Find it here: https://www.gmlscripts.com/script/hex_to_dec

Expand///draw_text_fixhash_coded(x,y,text,halign,valign)
//
// draws the specified string
// the hash symbol does not produce a newline
// uses in-string coding to change text appearance mid-string. (see paramiters below)
// Do NOT put spaces in the in-string code.
// this script requires the scripts hex_to_dec, chr_width, string_width_fixhash_coded, and string_height_fixhash.
//
// x        x position of the string, real
// y        y position of the string, real
// text     string to draw, string
// halign   horizontal alignment, real (0 = left; 1 = center; 2 = right)
// valign   vertical alignment, real (0 = top; 1 = middle; 2 = bottem)
//
/// GMLscripts.com/license

// in-string code paramiters: (Dont forget ":")
// color    color of text, real or hex
// alpha    alpha of text, real
// xscale   text horizontal scaling, real
// yscale   text vertical scaling, real
//
// examples:
// "default text /*color:$FF0000, alpha:0.5*/Blue transparent text"
// "default text /*xscale:1.4*/WIDE TEXT"
// "default text /*yscale:1.3*/TALL TEXT"

var_justify=argument3;
var_justify_vert=argument4;

var var_ii2, var_draw_yscale, var_draw_xscale, var_code_mode, var_code_out, var_space_size, var_main_length, var_main_string, var_width, var_letter, var_ii, var_draw_x, var_draw_y, var_line_height;
var_main_string=argument2;
var_main_string=string_replace_all(var_main_string,chr(13)+chr(10),chr(10))
var_main_length=string_length(var_main_string);
var_ii=1;
var_line_height=string_height('A');
var_space_size=string_width(' ');
var_width=string_width_fixhash_coded(var_main_string);
var_code_in="/*" /* Coding works like this */
var_code_out="*/"
var_code_mode=false;
var_draw_xscale=1
var_draw_yscale=1

if var_justify=0 { // left
 var_draw_x=0;
}
if var_justify=1 { // center
 var_draw_x=-string_width_fixhash_coded(string_copy(var_main_string,1,string_length_line(var_main_string,1)))/2;
}
if var_justify=2 { // right
 var_draw_x=-string_width_fixhash_coded(string_copy(var_main_string,1,string_length_line(var_main_string,1)));
}
if var_justify_vert=0 { //top
 var_draw_y=0;
}
if var_justify_vert=1 { //middle
 var_draw_y=-(string_height_fixhash(var_main_string))/2;
}
if var_justify_vert=2 { //bottem
 var_draw_y=-(string_height_fixhash(var_main_string));
 }
var_spacing=0;
var_line_number=1//current line number
while var_ii<=var_main_length {
 // 2 = length of the code_in tag
 if string_copy(argument2,var_ii,2)=var_code_in {
  var_code_mode=true;
  var_ii+=2;
 }
 // 2 = length of the code_out tag
 if string_copy(argument2,var_ii,2)=var_code_out {
  var_code_mode=false;
  var_ii+=2;
 }
 if var_code_mode=false {
  var_letter=ord(string_char_at(var_main_string,var_ii))
  switch var_letter {
   case 9: // tab
    if var_justify=0 var_draw_x+=var_space_size*4; // left
    if var_justify=1 var_draw_x+=var_space_size*2; // center
    if var_justify=2 var_draw_x+=var_space_size*4; // right
   break;
   case 10: // newline
   case 13: // newline
    var_line_number+=1;
    var_draw_y+=var_line_height;
    if var_justify=0 var_draw_x=0
    if var_justify=1 var_draw_x=(-string_width_fixhash_coded(string_copy(var_main_string, var_ii+1,string_length_line(var_main_string,var_line_number)))/2)*var_draw_xscale;
    if var_justify=2 var_draw_x=(-string_width_fixhash_coded(string_copy(var_main_string,var_ii+1,string_length_line(var_main_string,var_line_number))))*var_draw_xscale;
    
   break;
   case 32: // space
    if var_justify=0 var_draw_x+=var_space_size;
    if var_justify=1 var_draw_x+=var_space_size;
    if var_justify=2 var_draw_x+=var_space_size;
   break;
  }
  // if letter is drawable
  if var_letter>=33 and var_letter<=126 {
   // if letter = "#" 
   if var_letter=35 {
    draw_text_transformed(argument0+var_draw_x,argument1+var_draw_y,'\#',var_draw_xscale,var_draw_yscale,0);
    var_draw_x+=string_width('\#')+var_spacing;
   }
   else {
    draw_text_transformed(argument0+var_draw_x,argument1+var_draw_y,chr(var_letter),var_draw_xscale,var_draw_yscale,0)
    var_draw_x+=(string_width(chr(var_letter))*var_draw_xscale)+var_spacing;
   }
  }
  var_ii+=1;
 }
 else {
  if string_copy(argument2,var_ii,6)="color:" {
   if string_char_at(argument2,var_ii+6)='$' {
    draw_set_color(hex_to_dec(string_copy(argument2,var_ii+7,6)))
   }
   else {
    var_ii2=0;
    while string_digits(string_char_at(argument2,var_ii+6+var_ii2))=string_char_at(argument2,var_ii+6+var_ii2) {
     var_ii2+=1;
    }
    draw_set_color(real(string_copy(argument2,var_ii+6,var_ii2)))
   }
   var_ii+=6+var_ii2;
  }
  if string_copy(argument2,var_ii,6)="alpha:" {
   var_ii2=0;
   while string_digits(string_char_at(argument2,var_ii+6+var_ii2))=string_char_at(argument2,var_ii+6+var_ii2) 
   or string_char_at(argument2,var_ii+6+var_ii2)='.' {
    var_ii2+=1;
   }
   draw_set_alpha(real(string_copy(argument2,var_ii+6,var_ii2)))
   var_ii+=6+var_ii2;
  }
  if string_copy(argument2,var_ii,7)="xscale:" {
   var_ii2=0;
   while string_digits(string_char_at(argument2,var_ii+7+var_ii2))=string_char_at(argument2,var_ii+7+var_ii2) 
   or string_char_at(argument2,var_ii+7+var_ii2)='.' {
    var_ii2+=1;
   }
   var_draw_xscale=real(string_copy(argument2,var_ii+7,var_ii2))
   var_ii+=7+var_ii2;
  }
  if string_copy(argument2,var_ii,7)="yscale:" {
   var_ii2=0;
   while string_digits(string_char_at(argument2,var_ii+7+var_ii2))=string_char_at(argument2,var_ii+7+var_ii2) 
   or string_char_at(argument2,var_ii+7+var_ii2)='.' {
    var_ii2+=1;
   }
   var_draw_yscale=real(string_copy(argument2,var_ii+7,var_ii2))
   var_ii+=7+var_ii2;
  }
  if string_char_at(argument2,var_ii)=" " // stuff to ignore
  or string_char_at(argument2,var_ii)="," {
   var_ii+=1;
  }
 }
}

#6 Re: Script Submission » generate a random cave for a ds_grid (Update 9/2/20) » 2020-02-28 12:49:36

Update! This new script is more efficient and has zoning capabilities. If you aim to replace the old script, know that returned values do work differently. Zero is always solid, and three is the first zone value. Every number beyond 3 is for more zones. With zones, you can generate a biome-filled overworld map.

EDIT 9/2/20: There is now an example showcasing this collection of generation scripts, and more. Maze, Dungeon, Cave, and Overworld Example GMZ. Hosted at Host-a.net.
There has been a recent bugfix in the example. (Dungeon door refresh bug.)
EDIT 9/4/20: simple bugfix for proper room refreshing when pressing F5 in game presentation.

My device isnt that great, but this should give you an idea of how long this script can take.
with full smoothing
500 x 500 = 17.83 seconds
400 x 400 = 12.22 seconds
300 X 300 = 7.53 seconds
200 X 200 = 3.73 seconds
100 X 100 = 1.01 seconds
without smoothing
500 X 500 = 3.03 seconds
100 X 100 = 0.93 seconds

Expand///generate_cave(ds_grid, free_cells*, edge_limit*, smooth_power*, smooth_block*, zones*, xn_start*, yn_start*, convex_smooth_scope*, concave_smooth_scope*)
// * arguments are optional
// 
// Generates a cave-like splotch.
// See value key for ds_grid below.
// 
// ds_grid                ds_grid to splotch, or undefined  a new grid, real (ds_grid)
// free_cells             the number of free cells within the unsmoothed cave splotch, real (integer)
// edge_limit             size of the boundry that suppresses cave generation, real (integer)
// smooth_power           power of smoothing for the cave splotch, real (bool)
// smooth_block           blocks concave smoothing at 1, and convex smoothing at 2, real (integer)
// zones                  number of zones to generate within the splotch, real (integer)
// xn_start               starting x position of splotch generation, real (normal)
// yn_start               starting y position of splotch generation, real (normal)
// convex_smooth_scope    scope size for convex smoothing, ranges from 0 to 7, real (integer)
// concave_smooth_scope   scope size for concave smoothing, ranges from 1 to 8, real (integer)
// 
// The first zone (last generated) occors at 3. Other zones occor subsequently.
// Set argument smooth_power to -1 to smooth until smoothing becomes ineffective.
// - This causes significant slowdown with large ds_grids.
// If argument concave_smooth_scope is 6 or more, passages won't be blocked.
// Zones may not be fully connected.
//
/// GMLscripts.com/license

// value key for ds_grid:
// NOTE: Values 1 and 2 should never be seen.
// 0 = solid cell
// 1 = blocked generation cell (solid) (removed during generation cleanup)
// 2 = zoneless cell (free) (presence implies unsuccessful zone eyedrop during smoothing)
// 3 and up = zones (free)

var var_xsn, var_ysn, var_buddy_count, var_dsg, var_reps, var_chance_edge_limit, 
var_smooth_power, var_smoothing_block, var_dsg_width, var_dsg_height, 
var_potential_list_x, var_potential_list_y, var_iix, var_iiy, var_buddy_count, 
var_differ_check, var_concave_smooth_scope, var_convex_smooth_scope, var_zones, 
var_zone_wrap, var_eyedrop, var_grid_get;

var_dsg=argument[0];

var_reps=500;
if argument_count>1 var_reps=argument[1];

var_chance_edge_limit=12;
if argument_count>2 var_chance_edge_limit=argument[2];

var_smooth_power=1
if argument_count>3 var_smooth_power=argument[3];

var_smoothing_block=0
if argument_count>4 var_smoothing_block=argument[4];

var_zones=1
if argument_count>5 var_zones=argument[5]

var_xsn=0.5
if argument_count>6 var_xsn=argument[6]

var_ysn=0.5
if argument_count>7 var_ysn=argument[7]

var_convex_smooth_scope=3
if argument_count>8 var_convex_smooth_scope=median(0,7,argument[8])

var_concave_smooth_scope=6
if argument_count>9 var_concave_smooth_scope=median(1,8,argument[9])


var_dsg_width=ds_grid_width(var_dsg)
var_dsg_height=ds_grid_height(var_dsg)
var_potential_list_x=ds_list_create();
var_potential_list_y=ds_list_create();
var_iix=median(1,var_dsg_width-2,floor(var_dsg_width*var_xsn))
var_iiy=median(1,var_dsg_height-2,floor(var_dsg_height*var_ysn))

ds_grid_clear(var_dsg,0);
ds_grid_add(var_dsg,var_iix,var_iiy,3);

ds_list_add(var_potential_list_x,var_iix-1)
ds_list_add(var_potential_list_y,var_iiy)

ds_list_add(var_potential_list_x,var_iix)
ds_list_add(var_potential_list_y,var_iiy-1)

ds_list_add(var_potential_list_x,var_iix+1)
ds_list_add(var_potential_list_y,var_iiy)

ds_list_add(var_potential_list_x,var_iix)
ds_list_add(var_potential_list_y,var_iiy+1)

var_initial_limit=min(irandom_range(100, 1000),var_reps*0.2)
var_zone_wrap=ceil(var_reps/var_zones);
while var_reps>0 {
 // choose cell to free
 var_potential_list_size=ds_list_size(var_potential_list_x)
 if var_potential_list_size>var_initial_limit var_list_value=(var_potential_list_size-1)-irandom(  max(floor((var_potential_list_size*0.005)),3)  )
 else var_list_value=(var_potential_list_size-1)-irandom(  max(floor((var_potential_list_size*0.1)),3)  )
 
 var_iix=ds_list_find_value(var_potential_list_x,var_list_value)
 var_iiy=ds_list_find_value(var_potential_list_y,var_list_value)
 
 if ds_grid_get(var_dsg,var_iix,var_iiy)=0 {
  ds_list_delete(var_potential_list_x,var_list_value);
  ds_list_delete(var_potential_list_y,var_list_value);
  ds_grid_set(var_dsg,var_iix,var_iiy,3+floor(var_reps/var_zone_wrap));
  var_reps-=1;
 }
 else {
  ds_list_delete(var_potential_list_x,var_list_value);
  ds_list_delete(var_potential_list_y,var_list_value);
 }
 
 // potentialize neighbor cells
 if var_iix>1 { // west cell
  if ds_grid_get(var_dsg, var_iix-1, var_iiy)=0 {
   if var_iix>irandom(var_chance_edge_limit) {
    ds_list_add(var_potential_list_x,var_iix-1)
    ds_list_add(var_potential_list_y,var_iiy)
   }
   else {
    ds_grid_set(var_dsg, var_iix-1, var_iiy, 1)
   }
  }
 }

 if var_iiy>1 { // north cell
  if ds_grid_get(var_dsg, var_iix, var_iiy-1)=0 {
   if var_iiy>irandom(var_chance_edge_limit) {
    ds_list_add(var_potential_list_x,var_iix)
    ds_list_add(var_potential_list_y,var_iiy-1)
   }
   else {
    ds_grid_set(var_dsg, var_iix, var_iiy-1, 1)
   }
  }
 }

 if var_iix<var_dsg_width-2 { // south cell
  if ds_grid_get(var_dsg, var_iix+1, var_iiy)=0 {
   if var_iix<(var_dsg_width-1)-irandom(var_chance_edge_limit) {
    ds_list_add(var_potential_list_x,var_iix+1)
    ds_list_add(var_potential_list_y,var_iiy)
   }
   else {
    ds_grid_set(var_dsg, var_iix+1, var_iiy, 1)
   }
  }
 }

 if var_iiy<var_dsg_height-2 { // east cell
  if ds_grid_get(var_dsg, var_iix, var_iiy+1)=0 {
   if var_iiy<(var_dsg_height-1)-irandom(var_chance_edge_limit) {
    ds_list_add(var_potential_list_x,var_iix)
    ds_list_add(var_potential_list_y,var_iiy+1)
   }
   else {
    ds_grid_set(var_dsg, var_iix, var_iiy+1, 1)
   }
  }
 }
}

// generation cleanup
ds_list_destroy(var_potential_list_x);
ds_list_destroy(var_potential_list_y);
var_iix=1;
while var_iix<var_dsg_width-1 {
 var_iiy=1;
 while var_iiy<var_dsg_height-1 {
  if ds_grid_get(var_dsg, var_iix, var_iiy)=1 ds_grid_set(var_dsg, var_iix, var_iiy, 0)
  
  var_iiy+=1;
 }
 var_iix+=1;
}

// smoothing
if var_smooth_power=-1 var_smooth_power=9007199254740991;
while var_smooth_power>0 {
 var_smooth_count=0;
 var_iix=1;
 while var_iix<var_dsg_width-1 {
  var_iiy=1;
  while var_iiy<var_dsg_height-1 {
   var_buddy_count=0;
   var_eyedrop=2;
   var_grid_get=ds_grid_get(var_dsg, var_iix-1, var_iiy-1)
   if var_grid_get<=1 {
    var_buddy_count+=1
   }
   else var_eyedrop=var_grid_get
   var_grid_get=ds_grid_get(var_dsg, var_iix, var_iiy-1)
   if var_grid_get<=1 {
    var_buddy_count+=1
   }
   else var_eyedrop=var_grid_get
   var_grid_get=ds_grid_get(var_dsg, var_iix+1, var_iiy-1)
   if var_grid_get<=1 {
    var_buddy_count+=1
   }
   else var_eyedrop=var_grid_get
   var_grid_get=ds_grid_get(var_dsg, var_iix-1, var_iiy)
   if var_grid_get<=1 {
    var_buddy_count+=1
   }
   else var_eyedrop=var_grid_get
   var_grid_get=ds_grid_get(var_dsg, var_iix+1, var_iiy)
   if var_grid_get<=1 {
    var_buddy_count+=1
   }
   else var_eyedrop=var_grid_get
   var_grid_get=ds_grid_get(var_dsg, var_iix-1, var_iiy+1)
   if var_grid_get<=1 {
    var_buddy_count+=1
   }
   else var_eyedrop=var_grid_get
   var_grid_get=ds_grid_get(var_dsg, var_iix, var_iiy+1)
   if var_grid_get<=1 {
    var_buddy_count+=1
   }
   else var_eyedrop=var_grid_get
   var_grid_get=ds_grid_get(var_dsg, var_iix+1, var_iiy+1)
   if var_grid_get<=1 {
    var_buddy_count+=1
   }
   else var_eyedrop=var_grid_get
   
   if var_buddy_count<var_convex_smooth_scope and var_smoothing_block!=2 {
    var_differ_check=ds_grid_get(var_dsg, var_iix, var_iiy)
    if (var_differ_check<=1) != (var_eyedrop<=1) {
     ds_grid_set(var_dsg, var_iix, var_iiy, var_eyedrop)
     var_smooth_count+=1;
    }
   }
   if var_buddy_count>var_concave_smooth_scope and var_smoothing_block!=1 {
    var_differ_check=ds_grid_get(var_dsg, var_iix, var_iiy)
    if var_differ_check!=0 {
     ds_grid_set(var_dsg, var_iix, var_iiy, 0)
     var_smooth_count+=1;
    }
   }
   
   var_iiy+=1;
  }
  var_iix+=1;
 }
 if var_smooth_power>0 {
  if var_smooth_power=9007199254740991 {
   if var_smooth_count=0 var_smooth_power=0;
  }
  else {
   if var_smooth_count=0 var_smooth_power=0;
   else var_smooth_power-=1;
  }
 }
}

return var_dsg;

#7 Script Submission » "pie move" wander system » 2020-02-22 02:17:06

lostdarkwolf
Replies: 0

This is a set of scripts i call the "pie move" wander system. It uses "scan rays" aligned like pie slices or tire spokes. It scans for a suitable and desirable direction, then travels there. repeating this process as quickly as possible. Scanning is automatically done only when the instance is stationary, as per intentional limitations. if the instance scans outside the station, it must try to get back. The station location can be unbound, enabling unrestricted wandering.

The "pie move" wander system consists of five scripts, but only the top four are required.
The fifth script is to visualize the data so you can see what the script is doing.

Example of use: (also in init script)
Create event:
  pie_move_init(48)
Step event:
  var var_wander_speed; var_wander_speed=3;
  pie_move_update(4,140,2)
  pie_move_point(xstart,ystart,100,var_wander_speed)
  mp_potential_step(loc_ret_x,loc_ret_y,var_wander_speed,false);
Draw event: (optional, for visualization only)
  pie_move_draw(xstart,ystart,100)

EDIT 2/22/20: Bugfix in pie_move_init.
EDIT 4/6/20: added pie_move_reset and mp_grid_add_solids

Only needed if you want to use pathfinding

Expand///mp_grid_add_solids(id, x, y, grid_width, grid_height, cell_width, cell_height);
// 
// Checks for collision in an area, and sets an mp_grid accordingly
// Collisions are checked with the calling instance's sprite mask.
//
// id            id of mp_grid, real
// x             x position of the mp_grid, real
// y             y position of the mp_grid, real
// grid_width    width of the grid in cells, real
// grid_height   height of the grid in cells, real
// grid_width    width of a single cell, real
// grid_height   height of a single cell, real
//
///gmlscripts.com/license

var var_id, var_iix, var_iiy, var_x, var_y, var_grid_width, var_grid_height, var_cell_width, var_cell_height, var_solid_revert;
var_id=argument[0];
var_x=argument[1];
var_y=argument[2];
var_grid_width=argument[3];
var_grid_height=argument[4];
var_cell_width=16;
if argument_count>5 var_cell_width=argument[5];
var_cell_height=16;
if argument_count>6 var_cell_height=argument[6];

var_iiy=0;
while var_iiy<var_grid_height {
 var_iix=0;
 while var_iix<var_grid_width {
  if not place_free(var_x+(var_iix*var_cell_width),var_y+(var_iiy*var_cell_height)) {
   mp_grid_add_cell(var_id,var_iix,var_iiy);
   //show_message('cell added at '+string(var_iix)+', '+string(var_iiy))
  }
  var_iix+=1;
 }
 var_iiy+=1;
}

Only needed of you want to use pathfinding

Expand///pie_move_reset()
// * arguments are optinal
// 
// resets the pie_move system
// call this when restarting an idle pie_move system
// 
// used for a wander system called "pie move"
// 
///gmlscripts.com/license

loc_startup_timer=0;
loc_pie_offset=0;
loc_ret_x=x;
loc_ret_y=y;

REQUIRED

Expand///distance_to_solid(maxdist,direction,not_me,precision*,x*,y*)
//
// returns distance until solid from x and y
// 
// maxdist     maximum distance
// direction   direction to check
// not_me      set to true to exclude local instance
// precision   distance precision for checking
// x           x location to scan from
// y           y location to scan from
//
///gmlscripts.com/license

var Amaxdist=argument[0];
var Adirection=argument[1];
var Anot_me=1;
if argument_count>2 Anot_me=argument[2];
var Aprec=1;
if argument_count>3 Aprec=argument[3];
var Ax=x;
if argument_count>5 Ax=argument[5];
var Ay=y;
if argument_count>6 Ay=argument[6];
var seen_solid=false;

var solid_revert; solid_revert=solid;
if Anot_me solid=0;
var ii_len=0;
while (ii_len<Amaxdist and seen_solid=false) {
 if not place_free(Ax+lengthdir_x(ii_len,Adirection),Ay+lengthdir_y(ii_len,Adirection)) {
  seen_solid=true;
 }
 ii_len+=Aprec;
}
ii_len-=Aprec;

solid=solid_revert;

return ii_len;

REQUIRED

Expand///pie_move_init(raw_slice_count, goal_reach_distance, ray_distance, x, y)
// all arguments are optional
//
// initializes a wander system
// should be executed only once
//
// raw_slice_count   total spoke-styled scan rays, real (integer)
// ray_distance      maximum scan ray distance, real
// x                 x position of wandering instance
// y                 y position of wandering instance
//
// used for a wander system called "pie move"
// raw_slice_count should be a multiple of "sub_slice_count"
// - "sub_slice_count" is an argument specified in the script "pie_move_update"
// see below for an example of use of the "pie-move" wander system.
//
///gmlscripts.com/license

/* example of "pie-move" use:
Create event:
  pie_move_init(48)
Step event:
  var var_wander_speed; var_wander_speed=3;
  pie_move_update(4,140,2)
  pie_move_point(xstart,ystart,100,var_wander_speed)
  mp_potential_step(loc_ret_x,loc_ret_y,var_wander_speed,false);
Draw event: (optional, for visualization only)
  pie_move_draw(xstart,ystart,100)
*/

var var_y, var_x, var_ii;

loc_stop_timer=-1;
loc_raw_slice_count=90
if argument_count>0 loc_raw_slice_count=argument[0]
loc_goal_reach_distance=9
if argument_count>1 loc_goal_reach_distance=argument[1]
loc_ray_distance=120;
if argument_count>2 loc_ray_distance=argument[2]
var_x=x
if argument_count>3 var_x=argument[3]
var_y=y
if argument_count>4 var_y=argument[4]
loc_startup_timer=-1;
loc_pie_offset=0;
var_ii=0;
repeat loc_raw_slice_count {
 loc_pie_data[var_ii]=0;
 var_ii+=1;
}
loc_ret_x=var_x;
loc_ret_y=var_y;
loc_dir=direction;
loc_interesting_direction_threshold=17

REQUIRED

Expand///pie_move_update(precision, ray_distance*, sub_slice_count*, x*, y*)
// * arguments are optinal
// 
// updates scan rays ONLY.
// should be used in a step event.
// 
// precision         precision to use for rays, real
// sub_slice_count   number of rays to scan per step, real (integer)
// ray_distance      maximum scan ray distance, real
// x                 x position of wandering instance, real
// y                 y position of wandering instance, real
// 
// used for a wander system called "pie move"
// sub_slice_count must devide into "raw_slice_count" without remainder
// - "raw_slice_count" is an argument in the script "pie_move_init"
// see script "pie_move_update" for an example of use
// 
///gmlscripts.com/license

var var_x, var_y, var_slice_space, var_raw_slice_size;

loc_sub_slice_count=6;
if argument_count>2 loc_sub_slice_count=argument[2];

if argument_count>1 loc_ray_distance=argument[1];
var_x=x;
if argument_count>3 var_x=argument[3];
var_y=y;
if argument_count>4 var_y=argument[4];
var var_slice_space;
var_slice_space=loc_raw_slice_count/loc_sub_slice_count;
var_raw_slice_size=360/loc_raw_slice_count;

if loc_startup_timer<360 loc_startup_timer+=1;

if point_distance(loc_ret_x,loc_ret_y,var_x,var_y)=0 {
 // sub slice update onto raw slices
 var var_slice_ii, var_subslice_index; var_slice_ii=0
 repeat loc_sub_slice_count {
  var_subslice_index=(var_slice_ii*var_slice_space)+loc_pie_offset;
  loc_pie_data[var_subslice_index]=distance_to_solid(loc_ray_distance,var_subslice_index*var_raw_slice_size,true,argument[0]);
  var_slice_ii+=1;
 }
  
 // update sub slice pie offset location
 loc_pie_offset=(loc_pie_offset+1) mod var_slice_space;
}

REQUIRED

Expand///pie_move_point(station_x, station_y, travel_distance, speed, x*, y*)
// * arguments are optional
//
// reacts to "scan ray" data and will "point" to the chosen "interesting direction"
// instance is allowed to walk outside station, but must walk toward station after leaving it
// should be used in a step event
// 
// station_x         x location of station to wander around
// station_y         y location of station to wander around
// travel_distance   station's unrestricted movement distance
// speed             speed by which to wander
// x                 x position of wandering instance
// y                 y position of wandering instance
// 
// if station_x is set to undefined, wandering will be unbound, and station_y will be ignored
// see script "pie_move_update" for an example of use
// customization notes below (marked "Custom:")
// 
///gmlscripts.com/license

var var_sx, var_sy, var_speed, var_go_distance, var_x, var_y, var_exception;

var_sx=argument[0];
var_sy=argument[1];
var_go_distance=argument[2];
var_speed=argument[3];
var_x=x;
if argument_count>4 var_x=argument[4];
var_y=y;
if argument_count>5 var_y=argument[5];

var var_station_direction, var_chosen_direction_raw_index, var_station_distance_exceeded,
var_goal_distance;

var_station_direction=point_direction(var_x,var_y,var_sx,var_sy);

if var_sx!=undefined var_station_distance_exceeded=point_distance(var_x,var_y,var_sx,var_sy)>var_go_distance
else var_station_distance_exceeded=false;

var_goal_distance=point_distance(loc_ret_x,loc_ret_y,var_x,var_y);


if var_goal_distance>=loc_goal_reach_distance {
 loc_startup_timer=-1;
}
else {
 loc_ret_x=x;
 loc_ret_y=y;
}

if loc_startup_timer>=loc_raw_slice_count/loc_sub_slice_count 
and var_goal_distance<loc_goal_reach_distance {
  var var_raw_slice_size, var_ii;
  var_raw_slice_size=360/loc_raw_slice_count;
  var_ii=0;
  var var_align_direction_min_diff, var_align_direction, var_align_direction_diff;
  if var_station_distance_exceeded var_align_direction=var_station_direction;
  else {
   // Custom: chance of trying to maintain approximate direction
   // |----------|
   if irandom(4)=0 var_align_direction=irandom(359);
   // Custom: feild of random direction offset for approximation
   //                                |-------------|
   else var_align_direction=direction-30+irandom(60);
  }
  var_align_direction_min_diff=1000;
  repeat loc_raw_slice_count {
   var var_last_slice; var_last_slice=var_ii-1;
   if var_last_slice<0 var_last_slice+=loc_raw_slice_count;
   var var_last_slice2; var_last_slice2=var_ii-2;
   if var_last_slice2<0 var_last_slice2+=loc_raw_slice_count;
   var var_next_slice; var_next_slice=var_ii+1;
   if var_next_slice>=loc_raw_slice_count var_next_slice-=loc_raw_slice_count;
   var var_next_slice2; var_next_slice2=var_ii+2;
   if var_next_slice2>=loc_raw_slice_count var_next_slice2-=loc_raw_slice_count;
   // dont walk away from the station for an "interesting direction"
   if var_station_distance_exceeded var_exception=abs(angle_difference(var_ii*var_raw_slice_size,var_align_direction+180))>75;
   else var_exception=true;
   // Custom: 30 is the far neighbor "interesting direction" threshold
   if (loc_pie_data[var_last_slice2]<=loc_pie_data[var_ii]-(loc_interesting_direction_threshold*2) 
   or loc_pie_data[var_next_slice2]<=loc_pie_data[var_ii]-(loc_interesting_direction_threshold*2)) 
   // Custom: 17 is the close neighbor "interesting direction" threshold
   if (loc_pie_data[var_last_slice]<=loc_pie_data[var_ii]-loc_interesting_direction_threshold  
   or loc_pie_data[var_next_slice]<=loc_pie_data[var_ii]-loc_interesting_direction_threshold) 
   // Custom: 40 is the minimum acceptable scan ray distance.
   and loc_pie_data[var_ii]>40 
   and var_exception {
    var_align_direction_diff=abs(angle_difference(var_align_direction,var_ii*var_raw_slice_size));
    if var_align_direction_min_diff>var_align_direction_diff {
     var_align_direction_min_diff=var_align_direction_diff;
     var_chosen_direction_raw_index=var_ii;
    }
   }
   var_ii+=1
  }
  if var_align_direction_min_diff=1000 {
   if var_station_distance_exceeded direction=floor(var_station_direction/var_raw_slice_size)*var_raw_slice_size;
   else direction=irandom(loc_raw_slice_count-1)*var_raw_slice_size;
   var_chosen_direction_raw_index=direction/var_raw_slice_size
  }
  else direction=var_chosen_direction_raw_index*var_raw_slice_size;
  var var_max_dist, var_chosen_length;
  var_max_dist=loc_pie_data[var_chosen_direction_raw_index];
  // Custom: 0.6 is a multiple of a scan-ray's distance for the chosen direction
  // Custom: 16 is padding to avoid having the wander target go into a solid
  // they both specify walk distance randomization paramiters
  var_chosen_length=irandom_range(var_max_dist*0.6,var_max_dist-16)
  loc_ret_x=var_x+lengthdir_x(var_chosen_length,direction);
  loc_ret_y=var_y+lengthdir_y(var_chosen_length,direction);
}

optional

Expand///pie_move_draw(station_x, station_y, travel_distance)
//
// this script is only intended for visualizing how "pie move" works
// - This script is not synced with any data from the "pie_move_point" script
// this script should be called in a draw event
//
// station_x         x location of station to wander around
// station_y         y location of station to wander around
// travel_distance   station's unrestricted movement distance
// 
// see script "pie_move_update" for an example of use
//
///gmlscripts.com/license

var_sx=argument[0]
var_sy=argument[1]
var_go_distance=argument[2]

draw_set_color(c_fuchsia);
draw_circle(var_sx,var_sy,var_go_distance,1);
draw_circle(var_sx,var_sy,3,1);
var_raw_slice_size=360/loc_raw_slice_count;
var var_ii; var_ii=0;
repeat loc_raw_slice_count {
 var var_last_slice; var_last_slice=var_ii-1;
 if var_last_slice<0 var_last_slice+=loc_raw_slice_count;
 var var_last_slice2; var_last_slice2=var_ii-2;
 if var_last_slice2<0 var_last_slice2+=loc_raw_slice_count;
 var var_next_slice; var_next_slice=var_ii+1;
 if var_next_slice>=loc_raw_slice_count var_next_slice-=loc_raw_slice_count;
 var var_next_slice2; var_next_slice2=var_ii+2;
 if var_next_slice2>=loc_raw_slice_count var_next_slice2-=loc_raw_slice_count;
 
  // 30 is the far neighbor "interesting direction" threshold
  //if (loc_pie_data[var_last_slice2]<=loc_pie_data[var_ii]-30 
  //or loc_pie_data[var_next_slice2]<=loc_pie_data[var_ii]-30) 
  // 17 is the close neighbor "interesting direction" threshold
  if (loc_pie_data[var_last_slice]<=loc_pie_data[var_ii]-17 
  or loc_pie_data[var_next_slice]<=loc_pie_data[var_ii]-17) 
  // 40 is the minimum acceptable scan ray distance.
  and loc_pie_data[var_ii]>40 {
  if loc_startup_timer>(loc_raw_slice_count/loc_sub_slice_count) {
   draw_set_color(c_red)
  }
  else draw_set_color(c_yellow)
 }
 else draw_set_color(c_aqua)
 draw_line(x,y,x+lengthdir_x(loc_pie_data[var_ii],var_ii*var_raw_slice_size),y+lengthdir_y(loc_pie_data[var_ii],var_ii*var_raw_slice_size))
 
 var_ii+=1;
}
draw_set_color(c_lime);
draw_rectangle(loc_ret_x-1,loc_ret_y-1,loc_ret_x+1,loc_ret_y+1,0)

#8 Script Submission » 3 scripts that each determine collision location » 2019-10-28 22:46:11

lostdarkwolf
Replies: 0

I have made a set of scripts that each scan for collision with another instance in a unique way. Each script scans the edge of the local instance, looking for another specified instance. if the other instance is found, you are able to get each end of the collision, as well as the middle of the collision. all of these points can/will also be on the edge of the local instance (if possible). The ends of the collision will always be on the edge of the local instance.
I have made this set of scripts in order to make a raster physics engine. I'm not sure if I can make the engine, but I'll try. If I am successful, it will be a script, and I will post it on gmlscripts.

The first script is collision_edge_scan_first. This script is the fastest, but it also gives the least information. This script should be used if you only need ANY point in the collision of instances, or if you just want to check if two instance borders collide.

The second script is collision_edge_scan_middle. This script only returns the middle point of the collision (optionally on the edge of the local instance).

the last script is collision_edge_scan_region. This script is the slowest, but it also gives the most information. This script is the one that should be used for a physics engine. This script returns the region of collision as well as the middle point of the collision.

There are no required scripts.
EDIT 10/29/2019: Bugfix. Setting edge_correction to false now functions correctly on middle and region scripts.

Expand///collision_edge_scan_first(other_inst, edge_x_start*, edge_y_start*, edge_correction*)
// * arguments are optional
//
// Finds any location on the local instances collision with other_inst, and returns it as variables.
// Edge point arguments are relative to local position.
// The return variables are listed below.
//
// other_inst        The other instance to look for. real (instance id)
// edge_x_start      Optional x-coordinate of any edge point on the sprite. real
// edge_y_start      Optional y-coordinate of any edge point on the sprite. real
// edge_correction   Boolean switch for edge correction. real (bool)
//
// Enable edge correction to ensure that returned points are on an edge of the local instance.
// Supply a correct edge point for faster calculation.
// If an incorrect edge point is supplied, this script will automatically find a correct one.
// Enable line 75 to correct edge points.
//
/// GMLscripts.com/license
/// -- RETURN VARIABLES: --
// ces_ret_x
// ces_ret_y

var var_other_inst, var_edge_x_start, var_edge_y_start, var_edge_x, var_edge_y, 
    var_edge_x_old, var_edge_y_old, var_potential_travel_direction, var_done, 
    var_ret_dist, var_ret_dir, var_edge_correction;
var_other_inst=argument[0];
var_edge_x_start=undefined;
if argument_count>1 var_edge_x_start=x+argument[1];
var_edge_y_start=undefined;
if argument_count>2 var_edge_y_start=y+argument[2];
var_edge_correction=true;
if argument_count>3 var_edge_correction=argument[3];

// find free pixel
var_potential_travel_direction=undefined;
if var_edge_x_start!=undefined {
 if instance_position(var_edge_x_start,var_edge_y_start,id)=id {
  if instance_position(var_edge_x_start,var_edge_y_start-1,id)!=id {
   var_potential_travel_direction=90;
  }
  if instance_position(var_edge_x_start,var_edge_y_start+1,id)!=id {
   var_potential_travel_direction=270;
  }
  if instance_position(var_edge_x_start-1,var_edge_y_start,id)!=id {
   var_potential_travel_direction=180;
  }
  if instance_position(var_edge_x_start+1,var_edge_y_start,id)!=id {
   var_potential_travel_direction=0;
  }
 }
}
if var_potential_travel_direction=undefined {
 // start point is invalid or undefined
 var var_iix=-sprite_xoffset;
 var var_iiy=-sprite_yoffset;
 var var_iix_stop=sprite_width-sprite_xoffset;
 var var_iiy_stop=sprite_height-sprite_yoffset;
 while var_iiy<=var_iiy_stop and var_potential_travel_direction=undefined {
  var var_iix=-sprite_xoffset;
  while var_iix<=var_iix_stop and var_potential_travel_direction=undefined {
   if instance_position(x+var_iix,y+var_iiy,id)=id {
    var_edge_x_start=x+var_iix;
    var_edge_y_start=y+var_iiy;
    var_potential_travel_direction=180;
   }
   var_iix+=1;
  }
  var_iiy+=1;
 }
 if var_potential_travel_direction=undefined {
  if not keyboard_check_direct(ord('Q')) show_message('Crucial error on script "collision_edge_scan_first", Invalid start point, and auto-fix has failed.#Hold Q to keep this message from showing up. It is advised to hold Q when closing the program as well.');
  return 0;
 }
 else {
  //if not keyboard_check_direct(ord('Q')) show_message('Noncrucial error on script "collision_edge_scan_first" used by '+string(object_get_name(object_index))+', Invalid edge start point at '+string(argument[1])+', '+string(argument[2])+'. A valid start point was found at '+string(var_edge_x_start-x)+', '+string(var_edge_y_start-y)+'.#Hold Q to keep this message from showing up. It is advised to hold Q when closing the program as well.');
 }
}

if var_edge_correction!=false {
 ces_ret_x=undefined;
 ces_ret_y=undefined;
 if instance_position(var_edge_x_start,var_edge_y_start,var_other_inst)=var_other_inst {
  ces_ret_x=var_edge_x_start;
  ces_ret_y=var_edge_y_start;
 }
 
 var_edge_x=var_edge_x_start;
 var_edge_y=var_edge_y_start;
 var_done=false;
 while var_done=false {
  var var_ii; var_ii=0;
  while instance_position(var_edge_x+round(lengthdir_x(1,var_potential_travel_direction)),var_edge_y+round(lengthdir_y(1,var_potential_travel_direction)),id)!=id {
   var_potential_travel_direction+=45;
  }
  var_edge_x_old=var_edge_x;
  var_edge_y_old=var_edge_y;
  var_edge_x=var_edge_x+round(lengthdir_x(1.3,var_potential_travel_direction));
  var_edge_y=var_edge_y+round(lengthdir_y(1.3,var_potential_travel_direction));
  if instance_position(var_edge_x,var_edge_y,var_other_inst)=var_other_inst {
   ces_ret_x=var_edge_x;
   ces_ret_y=var_edge_y;
   var_done=true;
  }
  if var_edge_x=var_edge_x_start 
  and var_edge_y=var_edge_y_start {
   var_done=true;
  }
  var_potential_travel_direction=45+point_direction(var_edge_x,var_edge_y,var_edge_x_old,var_edge_y_old);
 }
}
Expand///collision_edge_scan_middle(other_inst, edge_x_start*, edge_y_start*, edge_correction*)
// * arguments are optional
//
// Finds the middle location of the collision with other_inst, and returns it as variables.
// Edge point arguments are relative to local position.
// The return variables are listed below.
//
// other_inst        The other instance to look for. real (instance id)
// edge_x_start      Optional x-coordinate of any edge point on the sprite. real
// edge_y_start      Optional y-coordinate of any edge point on the sprite. real
// edge_correction   Boolean switch for edge correction. real (bool)
//
// Enable edge correction to ensure that returned points are on an edge of the local instance.
// Supply a correct edge point for faster calculation.
// If an incorrect edge point is supplied, this script will automatically find a correct one.
// Enable line 81 to correct edge points.
// Returns -1 if the specified local instance boundary is fully enveloped by other_inst.
//
/// GMLscripts.com/license
/// -- RETURN VARIABLES: --
// ces_ret_x
// ces_ret_y

var var_other_inst, var_edge_x_start, var_edge_y_start, var_edge_x, var_edge_y, 
    var_edge_x_old, var_edge_y_old, var_potential_travel_direction, var_done, 
    var_ret_dist, var_ret_dir, var_points_found, var_edge_correction, var_is_enveloped;
var_other_inst=argument[0];
var_edge_x_start=undefined;
if argument_count>1 var_edge_x_start=x+argument[1];
var_edge_y_start=undefined;
if argument_count>2 var_edge_y_start=y+argument[2];
var_edge_correction=true;
if argument_count>3 var_edge_correction=argument[3];

var_is_enveloped=true;
ces_ret_x=undefined;
ces_ret_y=undefined;
var_points_found=0;

// find free pixel
var_potential_travel_direction=undefined;
if var_edge_x_start!=undefined {
 if instance_position(var_edge_x_start,var_edge_y_start,id)=id {
  if instance_position(var_edge_x_start,var_edge_y_start-1,id)!=id {
   var_potential_travel_direction=90;
  }
  if instance_position(var_edge_x_start,var_edge_y_start+1,id)!=id {
   var_potential_travel_direction=270;
  }
  if instance_position(var_edge_x_start-1,var_edge_y_start,id)!=id {
   var_potential_travel_direction=180;
  }
  if instance_position(var_edge_x_start+1,var_edge_y_start,id)!=id {
   var_potential_travel_direction=0;
  }
 }
}
if var_potential_travel_direction=undefined {
 // start point is invalid or undefined
 var var_iix=-sprite_xoffset;
 var var_iiy=-sprite_yoffset;
 var var_iix_stop=sprite_width-sprite_xoffset;
 var var_iiy_stop=sprite_height-sprite_yoffset;
 while var_iiy<=var_iiy_stop and var_potential_travel_direction=undefined {
  var var_iix=-sprite_xoffset;
  while var_iix<=var_iix_stop and var_potential_travel_direction=undefined {
   if instance_position(x+var_iix,y+var_iiy,id)=id {
    var_edge_x_start=x+var_iix;
    var_edge_y_start=y+var_iiy;
    var_potential_travel_direction=180;
   }
   var_iix+=1;
  }
  var_iiy+=1;
 }
 if var_potential_travel_direction=undefined {
  if not keyboard_check_direct(ord('Q')) show_message('Crucial error on script "collision_edge_scan_middle", Invalid start point, and auto-fix has failed.#Hold Q to keep this message from showing up. It is advised to hold Q when closing the program as well.');
  return 0;
 }
 else {
  //if not keyboard_check_direct(ord('Q')) show_message('Noncrucial error on script "collision_edge_scan_middle" used by '+string(object_get_name(object_index))+', Invalid edge start point at '+string(argument[1])+', '+string(argument[2])+'. A valid start point was found at '+string(var_edge_x_start-x)+', '+string(var_edge_y_start-y)+'.#Hold Q to keep this message from showing up. It is advised to hold Q when closing the program as well.');
 }
}

var_edge_x=var_edge_x_start;
var_edge_y=var_edge_y_start;
var_done=false;
while var_done=false {
 var var_ii; var_ii=0;
 while instance_position(var_edge_x+round(lengthdir_x(1,var_potential_travel_direction)),var_edge_y+round(lengthdir_y(1,var_potential_travel_direction)),id)!=id {
  var_potential_travel_direction+=45;
 }
 var_edge_x_old=var_edge_x;
 var_edge_y_old=var_edge_y;
 if var_edge_x=undefined {
  var_edge_x=var_edge_x_start+round(lengthdir_x(1.3,var_potential_travel_direction));
  var_edge_y=var_edge_y_start+round(lengthdir_y(1.3,var_potential_travel_direction));
 }
 else {
  var_edge_x=var_edge_x+round(lengthdir_x(1.3,var_potential_travel_direction));
  var_edge_y=var_edge_y+round(lengthdir_y(1.3,var_potential_travel_direction));
 }
 if instance_position(var_edge_x,var_edge_y,var_other_inst)=var_other_inst {
  if ces_ret_x!=undefined {
   ces_ret_x+=var_edge_x;
   ces_ret_y+=var_edge_y;
  }
  else {
   ces_ret_x=var_edge_x;
   ces_ret_y=var_edge_y;
  }
  var_points_found+=1;
 }
 else {
  var_is_enveloped=false;
 }
 if var_edge_x=var_edge_x_start 
 and var_edge_y=var_edge_y_start {
  var_done=true;
 }
 
 var_potential_travel_direction=45+point_direction(var_edge_x,var_edge_y,var_edge_x_old,var_edge_y_old);
}

if var_is_enveloped=true {
 ces_ret_x=undefined;
 ces_ret_y=undefined;
 return -1;
}

if var_points_found>0 {
 ces_ret_x=ces_ret_x/var_points_found;
 ces_ret_y=ces_ret_y/var_points_found;
 if var_edge_correction!=false {
  // this finds an edge on a normal vector starting from the 
  //  local position, and facing toward the average point. if 
  //  the local mask is shaped in such a way that the vector
  //  has no collision with the local mask, the return point
  //  will be the average point of all points found.
  var_ret_dist=point_distance(x,y,ces_ret_x,ces_ret_y);
  var_ret_dir=point_direction(x,y,ces_ret_x,ces_ret_y);
 
  var var_ii_dist;
  var_ii_dist=undefined;
  // collision ahead
  if instance_position(ces_ret_x,ces_ret_y,id)!=id {
   var_ii_dist=var_ret_dist;
   while var_ii_dist<=var_ret_dist*2 and instance_position(x+lengthdir_x(var_ii_dist,var_ret_dir),y+lengthdir_y(var_ii_dist,var_ret_dir),id)!=id {
    var_ii_dist+=1;
   }
   if var_ii_dist>var_ret_dist*2 var_ii_dist=undefined;
  }
  // collision here
  else {
   var_ii_dist=point_distance(x,y,ces_ret_x,ces_ret_y);
   var var_stop_dist=distance_to_point(sprite_width,sprite_height);
   while var_ii_dist<=var_stop_dist and instance_position(x+lengthdir_x(var_ii_dist,var_ret_dir),y+lengthdir_y(var_ii_dist,var_ret_dir),id)=id {
    var_ii_dist+=1;
   }
   if var_ii_dist>var_stop_dist var_ii_dist=undefined;
   else var_ii_dist-=1;
  }
  // apply edge finding results to return variables
  if var_ii_dist!=undefined {
   ces_ret_x=round(x+lengthdir_x(var_ii_dist,var_ret_dir));
   ces_ret_y=round(y+lengthdir_y(var_ii_dist,var_ret_dir));
  }
 }
}
Expand///collision_edge_scan_region(other_inst, edge_x_start*, edge_y_start*, edge_correction*)
// * arguments are optional
//
// Finds the end points of the area where the local instance collides with other_inst and returns both points as variables.
// Also finds the middle location of the collision and returns it as variables.
// Edge point arguments are relative to local position.
// The return variables are listed below.
//
// other_inst        The other instance to look for. real (instance id)
// edge_x_start      Optional x-coordinate of any edge point on the sprite. real
// edge_y_start      Optional y-coordinate of any edge point on the sprite. real
// edge_correction   Boolean switch for edge correction. real (bool)
//
// Enable edge correction to ensure that returned points are on an edge of the local instance.
// Supply a correct edge point for faster calculation.
// If an incorrect edge point is supplied, this script will automatically find a correct one.
// Enable line 95 to correct edge points.
// Returns -1 if the specified local instance boundary is fully enveloped by other_inst.
//
/// GMLscripts.com/license
/// -- RETURN VARIABLES: --
// ces_ret_x
// ces_ret_y
// ces_ret_start_x
// ces_ret_start_y
// ces_ret_end_x
// ces_ret_end_y

var var_other_inst, var_edge_x_start, var_edge_y_start, var_edge_x, var_edge_y, 
    var_edge_x_old, var_edge_y_old, var_potential_travel_direction, var_done, 
    var_ret_dist, var_ret_dir, var_points_found, var_edge_correction, var_ii,
    var_is_enveloped;
var_other_inst=argument[0];
var_edge_x_start=undefined;
if argument_count>1 var_edge_x_start=x+argument[1];
var_edge_y_start=undefined;
if argument_count>2 var_edge_y_start=y+argument[2];
var_edge_correction=true;
if argument_count>3 var_edge_correction=argument[3];

var_is_enveloped=true;
ces_ret_x=undefined;
ces_ret_y=undefined;
var_points_found=0;

var_ii=0;
repeat 360 {
 var_empty_region[var_ii]=true;
 var_angle_point_x[var_ii]=undefined;
 var_angle_point_y[var_ii]=undefined;
 var_ii+=1;
}

// find free pixel
var_potential_travel_direction=undefined;
if var_edge_x_start!=undefined {
 if instance_position(var_edge_x_start,var_edge_y_start,id)=id {
  if instance_position(var_edge_x_start,var_edge_y_start-1,id)!=id {
   var_potential_travel_direction=90;
  }
  if instance_position(var_edge_x_start,var_edge_y_start+1,id)!=id {
   var_potential_travel_direction=270;
  }
  if instance_position(var_edge_x_start-1,var_edge_y_start,id)!=id {
   var_potential_travel_direction=180;
  }
  if instance_position(var_edge_x_start+1,var_edge_y_start,id)!=id {
   var_potential_travel_direction=0;
  }
 }
}
if var_potential_travel_direction=undefined {
 // start point is invalid or undefined
 var var_iix=-sprite_xoffset;
 var var_iiy=-sprite_yoffset;
 var var_iix_stop=sprite_width-sprite_xoffset;
 var var_iiy_stop=sprite_height-sprite_yoffset;
 while var_iiy<=var_iiy_stop and var_potential_travel_direction=undefined {
  var var_iix=-sprite_xoffset;
  while var_iix<=var_iix_stop and var_potential_travel_direction=undefined {
   if instance_position(x+var_iix,y+var_iiy,id)=id {
    var_edge_x_start=x+var_iix;
    var_edge_y_start=y+var_iiy;
    var_potential_travel_direction=180;
   }
   var_iix+=1;
  }
  var_iiy+=1;
 }
 if var_potential_travel_direction=undefined {
  if not keyboard_check_direct(ord('Q')) show_message('Crucial error on script "collision_edge_scan_region", Invalid start point, and auto-fix has failed.#Hold Q to keep this message from showing up. It is advised to hold Q when closing the program as well.');
  return 0;
 }
 else {
  //if not keyboard_check_direct(ord('Q')) show_message('Noncrucial error on script "collision_edge_scan_region" used by '+string(object_get_name(object_index))+', Invalid edge start point at '+string(argument[1])+', '+string(argument[2])+'. A valid start point was found at '+string(var_edge_x_start-x)+', '+string(var_edge_y_start-y)+'.#Hold Q to keep this message from showing up. It is advised to hold Q when closing the program as well.');
 }
}
if instance_position(var_edge_x_start,var_edge_y_start,var_other_inst)=var_other_inst {
 ces_ret_x=var_edge_x_start;
 ces_ret_y=var_edge_y_start;
 var_points_found+=1;
 var_empty_region[(round(point_direction(x,y,var_edge_x_start,var_edge_y_start))+720) mod 360]=false;
 if var_angle_point_x[(round(point_direction(x,y,var_edge_x_start,var_edge_y_start))+720) mod 360]!=undefined {
  if point_distance(x,y,var_edge_x_start,var_edge_y_start)>point_distance(x,y,var_angle_point_x[(round(point_direction(x,y,var_edge_x_start,var_edge_y_start))+720) mod 360],var_angle_point_y[(round(point_direction(x,y,var_edge_x_start,var_edge_y_start))+720) mod 360]) {
   var_angle_point_x[(round(point_direction(x,y,var_edge_x_start,var_edge_y_start))+720) mod 360]=var_edge_x_start;
   var_angle_point_y[(round(point_direction(x,y,var_edge_x_start,var_edge_y_start))+720) mod 360]=var_edge_y_start;
  }
 }
 else {
  var_angle_point_x[(round(point_direction(x,y,var_edge_x_start,var_edge_y_start))+720) mod 360]=var_edge_x_start;
  var_angle_point_y[(round(point_direction(x,y,var_edge_x_start,var_edge_y_start))+720) mod 360]=var_edge_y_start;
 }
}

var_edge_x=var_edge_x_start;
var_edge_y=var_edge_y_start;
var_done=false;
while var_done=false {
 var var_ii; var_ii=0;
 while instance_position(var_edge_x+round(lengthdir_x(1,var_potential_travel_direction)),var_edge_y+round(lengthdir_y(1,var_potential_travel_direction)),id)!=id {
  var_potential_travel_direction+=45;
 }
 var_edge_x_old=var_edge_x;
 var_edge_y_old=var_edge_y;
 if var_edge_x=undefined {
  var_edge_x=var_edge_x_start+round(lengthdir_x(1.3,var_potential_travel_direction));
  var_edge_y=var_edge_y_start+round(lengthdir_y(1.3,var_potential_travel_direction));
 }
 else {
  var_edge_x=var_edge_x+round(lengthdir_x(1.3,var_potential_travel_direction));
  var_edge_y=var_edge_y+round(lengthdir_y(1.3,var_potential_travel_direction));
 }
 if instance_position(var_edge_x,var_edge_y,var_other_inst)=var_other_inst {
  if ces_ret_x!=undefined {
   ces_ret_x+=var_edge_x;
   ces_ret_y+=var_edge_y;
  }
  else {
   ces_ret_x=var_edge_x;
   ces_ret_y=var_edge_y;
  }
  var_points_found+=1;
  var_empty_region[(round(point_direction(x,y,var_edge_x,var_edge_y))+720) mod 360]=false;
  if var_angle_point_x[(round(point_direction(x,y,var_edge_x,var_edge_y))+720) mod 360]!=undefined {
   if point_distance(x,y,var_edge_x,var_edge_y)>point_distance(x,y,var_angle_point_x[(round(point_direction(x,y,var_edge_x,var_edge_y))+720) mod 360],var_angle_point_y[(round(point_direction(x,y,var_edge_x,var_edge_y))+720) mod 360]) {
    var_angle_point_x[(round(point_direction(x,y,var_edge_x,var_edge_y))+720) mod 360]=var_edge_x;
    var_angle_point_y[(round(point_direction(x,y,var_edge_x,var_edge_y))+720) mod 360]=var_edge_y;
   }
  }
  else {
   var_angle_point_x[(round(point_direction(x,y,var_edge_x,var_edge_y))+720) mod 360]=var_edge_x;
   var_angle_point_y[(round(point_direction(x,y,var_edge_x,var_edge_y))+720) mod 360]=var_edge_y;
  }
 }
 else {
  var_is_enveloped=false;
 }
 if var_edge_x=var_edge_x_start 
 and var_edge_y=var_edge_y_start {
  var_done=true;
 }
 
 var_potential_travel_direction=45+point_direction(var_edge_x,var_edge_y,var_edge_x_old,var_edge_y_old);
}

if var_is_enveloped=true {
 ces_ret_x=undefined;
 ces_ret_y=undefined;
 return -1;
}

if var_points_found>0 {
 ces_ret_x=ces_ret_x/var_points_found;
 ces_ret_y=ces_ret_y/var_points_found;
 if var_edge_correction!=false {
  // this finds an edge on a normal vector starting from the 
  //  local position, and facing toward the average point. if 
  //  the local mask is shaped in such a way that the vector
  //  has no collision with the local mask, the return point
  //  will be the average point of all points found.
  var_ret_dist=point_distance(x,y,ces_ret_x,ces_ret_y);
  var_ret_dir=point_direction(x,y,ces_ret_x,ces_ret_y);
 
  var var_ii_dist;
  var_ii_dist=undefined;
  // collision ahead
  if instance_position(ces_ret_x,ces_ret_y,id)!=id {
   var_ii_dist=var_ret_dist;
   while var_ii_dist<=var_ret_dist*2 and instance_position(x+lengthdir_x(var_ii_dist,var_ret_dir),y+lengthdir_y(var_ii_dist,var_ret_dir),id)!=id {
    var_ii_dist+=1;
   }
   if var_ii_dist>var_ret_dist*2 var_ii_dist=undefined;
  }
  // collision here
  else {
   var_ii_dist=point_distance(x,y,ces_ret_x,ces_ret_y);
   var var_stop_dist=distance_to_point(sprite_width,sprite_height);
   while var_ii_dist<=var_stop_dist and instance_position(x+lengthdir_x(var_ii_dist,var_ret_dir),y+lengthdir_y(var_ii_dist,var_ret_dir),id)=id {
    var_ii_dist+=1;
   }
   if var_ii_dist>var_stop_dist var_ii_dist=undefined;
   else var_ii_dist-=1;
  }
  // apply edge finding results to return variables
  if var_ii_dist!=undefined {
   ces_ret_x=round(x+lengthdir_x(var_ii_dist,var_ret_dir));
   ces_ret_y=round(y+lengthdir_y(var_ii_dist,var_ret_dir));
  }
 }
}

if var_points_found>0 {
 // find largest free region
 var_ii=0;
 while var_empty_region[var_ii]=true {
  var_ii=(var_ii+1) mod 360;
 }
 var var_current_streak, var_record_streak, var_current_start, var_record_start, var_current_end, var_record_end;
 var_current_streak=0;
 var_record_streak=0;
 var_current_start=var_ii;
 var_record_start=var_ii;
 var_current_end=var_ii;
 var_record_end=var_ii;
 repeat 360 {
  if var_empty_region[var_ii]=true {
   if var_current_streak=0 {
    var_current_start=var_ii;
   }
   var_current_end=var_ii;
   var_current_streak+=1;
  }
  else {
   var_current_streak=0;
  }
  if var_current_streak>var_record_streak {
   var_record_streak=var_current_streak;
   var_record_start=var_current_start;
   var_record_end=var_current_end;
  }
  var_ii=(var_ii+1) mod 360;
 }
 while var_angle_point_x[var_record_start mod 360]=undefined {
  var_record_start=var_record_start-1;
  if var_record_start<0 var_record_start+=360;
 }
 while var_angle_point_x[var_record_end mod 360]=undefined {
  var_record_end=(var_record_end+1) mod 360;
 }
 ces_ret_start_x=var_angle_point_x[var_record_start mod 360];
 ces_ret_start_y=var_angle_point_y[var_record_start mod 360];
 ces_ret_end_x=var_angle_point_x[var_record_end mod 360];
 ces_ret_end_y=var_angle_point_y[var_record_end mod 360];
}

#9 Script Submission » Import obj files in color » 2019-09-19 13:48:57

lostdarkwolf
Replies: 0

This code will convert an obj file into a gamemaker model with support for diffuse color. The argument inputs are strings containing the entire obj or mtl file. There is one required script, called scireal.

Expand///obj_to_model(obj_data,mtl_data)
// 
// Converts OBJ and optional MTL data to a model.
// 
// obj_data   The contents of an entire OBJ file, string.
// mtl_data   The contents of an entire MTL file, string.
// 
// Supports diffuse color.
// Textures are not supported, but UV mapping is maintained.
// Requires a script called "scireal".
// 
/// GMLscripts.com/license

/*
Supported OBJ Calls:
v vt vn f usemtl
Supported MTL Calls:
Kd
*/

var var_done, var_ii_pos;

var var_work_string_a1=argument[0];
var var_work_string_b1='null_data';
if argument_count>1 var_work_string_b1=argument[1];

// remove carrage return charactors, and add a newline at the end.
var_work_string_a1=string_replace(var_work_string_a1,chr(10)+chr(12),chr(10));
var_work_string_a1=string_replace(var_work_string_a1,chr(12),chr(10))+chr(10);

if var_work_string_b1!='null_data' {
 var_work_string_b1=string_replace(var_work_string_b1,chr(10)+chr(12),chr(10));
 var_work_string_b1=string_replace(var_work_string_b1,chr(12),chr(10))+chr(10);
}

var var_total_faces=string_count('f ',var_work_string_a1)

dsg_v=ds_grid_create(3,var_total_faces*3);
current_v=0;
dsg_vt=ds_grid_create(2,var_total_faces*2);
current_vt=0;
dsg_vn=ds_grid_create(3,var_total_faces*3);
current_vn=0
dsg_f=ds_grid_create(10,var_total_faces);
current_f=0;
var dsm_mtl=ds_map_create();

var current_mtl='null_material';
var_done=0;
var var_start_pos=1;
var var_end_pos=string_pos(chr(10),var_work_string_a1);
if var_end_pos=0 var_end_pos=string_length(var_work_string_a1);
var var_work_string_a2=string_copy(var_work_string_a1,var_start_pos,(var_end_pos-var_start_pos)-1);
while string_char_at(var_work_string_a2,1)=' ' {
 var_work_string_a2=string_delete(var_work_string_a2,1,1)
}
while var_done<=1 { // convert obj string to model data (in ds_grids)
 if string_pos("v ",var_work_string_a2)=1 {
  var_work_string_a2=string_delete(var_work_string_a2,1,2)
  var_ii_pos=string_pos(" ",var_work_string_a2);
  ds_grid_set(dsg_v,0,current_v,scireal(string_copy(var_work_string_a2,1,var_ii_pos-1)));
  var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
  var_ii_pos=string_pos(" ",var_work_string_a2);
  ds_grid_set(dsg_v,1,current_v,scireal(string_copy(var_work_string_a2,1,var_ii_pos-1)));
  var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
  ds_grid_set(dsg_v,2,current_v,scireal(var_work_string_a2));
  current_v+=1;
 }
 if string_pos("vt ",var_work_string_a2)=1 {
  var_work_string_a2=string_delete(var_work_string_a2,1,3)
  var_ii_pos=string_pos(" ",var_work_string_a2);
  ds_grid_set(dsg_vt,0,current_vt,scireal(string_copy(var_work_string_a2,1,var_ii_pos-1)));
  var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
  ds_grid_set(dsg_vt,1,current_vt,scireal(var_work_string_a2));
  current_vt+=1;
 }
 if string_pos("vn ",var_work_string_a2)=1 {
  var_work_string_a2=string_delete(var_work_string_a2,1,3)
  var_ii_pos=string_pos(" ",var_work_string_a2);
  ds_grid_set(dsg_vn,0,current_vn,scireal(string_copy(var_work_string_a2,1,var_ii_pos-1)));
  var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
  var_ii_pos=string_pos(" ",var_work_string_a2);
  ds_grid_set(dsg_vn,1,current_vn,scireal(string_copy(var_work_string_a2,1,var_ii_pos-1)));
  var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
  ds_grid_set(dsg_vn,2,current_vn,scireal(var_work_string_a2));
  current_vn+=1;
 }
 if string_pos("f ",var_work_string_a2)=1 {
  var_work_string_a2=string_delete(var_work_string_a2,1,2)
  var var_slash_count=string_count("/",var_work_string_a2)
  var var_ii1, var_ii2, var_ii3, var_ii4, var_ii5, var_ii6, var_ii7, var_ii8, var_ii9;
  if var_slash_count=0 {
   var_ii_pos=string_pos(" ",var_work_string_a2)
   var_ii1=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii_pos=string_pos(" ",var_work_string_a2)
   var_ii3=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii6=scireal(var_work_string_a2)
  }
  if var_slash_count=3 {
   var_ii_pos=string_pos("/",var_work_string_a2)
   var_ii1=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii_pos=string_pos(" ",var_work_string_a2)
   var_ii2=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii_pos=string_pos("/",var_work_string_a2)
   var_ii4=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii_pos=string_pos(" ",var_work_string_a2)
   var_ii5=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii_pos=string_pos("/",var_work_string_a2)
   var_ii7=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii8=scireal(var_work_string_a2)
  }
  if var_slash_count=6 {
   var_ii_pos=string_pos("/",var_work_string_a2)
   var_ii1=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii_pos=string_pos("/",var_work_string_a2)
   var_ii2=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii_pos=string_pos(" ",var_work_string_a2)
   var_ii3=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii_pos=string_pos("/",var_work_string_a2)
   var_ii4=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii_pos=string_pos("/",var_work_string_a2)
   var_ii5=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii_pos=string_pos(" ",var_work_string_a2)
   var_ii6=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii_pos=string_pos("/",var_work_string_a2)
   var_ii7=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii_pos=string_pos("/",var_work_string_a2)
   var_ii8=scireal(string_copy(var_work_string_a2,1,var_ii_pos-1));
   var_work_string_a2=string_delete(var_work_string_a2,1,var_ii_pos)
   var_ii9=scireal(var_work_string_a2)
  }
  ds_grid_set(dsg_f,0,current_f,var_ii1);
  ds_grid_set(dsg_f,1,current_f,var_ii2);
  ds_grid_set(dsg_f,2,current_f,var_ii3);
  ds_grid_set(dsg_f,3,current_f,var_ii4);
  ds_grid_set(dsg_f,4,current_f,var_ii5);
  ds_grid_set(dsg_f,5,current_f,var_ii6);
  ds_grid_set(dsg_f,6,current_f,var_ii7);
  ds_grid_set(dsg_f,7,current_f,var_ii8);
  ds_grid_set(dsg_f,8,current_f,var_ii9);
  ds_grid_set(dsg_f,9,current_f,current_mtl);
  current_f+=1;
 }
 if string_pos("usemtl ",var_work_string_a2)=1 {
  current_mtl=string_delete(var_work_string_a2,1,7)
 }
 var_work_string_a1=string_delete(var_work_string_a1,1,var_end_pos)
 var_end_pos=string_pos(chr(10),var_work_string_a1);
 if var_done=1 {
  var_done=2;
  var_total_faces=current_f;
 }
 if var_end_pos=0 and var_done=0 {
  var_end_pos=string_length(var_work_string_a1);
  var_done=1;
 }
 else {
  var_work_string_a2=string_copy(var_work_string_a1,var_start_pos,(var_end_pos-var_start_pos)-1);
  while string_char_at(var_work_string_a2,1)=' ' {
   var_work_string_a2=string_delete(var_work_string_a2,1,1)
  }
 }
}

if var_work_string_b1!='null_data' { // get material data
 var_done=0;
 current_mtl='invalid_material';
 var var_start_pos=1;
 var var_end_pos=string_pos(chr(10),var_work_string_b1);
 if var_end_pos=0 var_end_pos=string_length(var_work_string_b1);
 var var_work_string_b2=string_copy(var_work_string_b1,var_start_pos,(var_end_pos-var_start_pos)-1);
 while string_char_at(var_work_string_b2,1)=' ' {
  var_work_string_b2=string_delete(var_work_string_b2,1,1)
 }
 while var_done<=1 { // convert mtl string to material data (in ds_grids)
  if string_pos("newmtl ",var_work_string_b2)=1 {
   current_mtl=string_delete(var_work_string_b2,1,7);
  }
  if string_pos("Kd ",var_work_string_b2)=1 { // color
   var_work_string_b2=string_delete(var_work_string_b2,1,3)
   var_ii_pos=string_pos(" ",var_work_string_b2)
   var_ii1=scireal(string_copy(var_work_string_b2,1,var_ii_pos-1)); // R
   var_work_string_b2=string_delete(var_work_string_b2,1,var_ii_pos)
   var_ii_pos=string_pos(" ",var_work_string_b2)
   var_ii2=scireal(string_copy(var_work_string_b2,1,var_ii_pos-1)); // G
   var_work_string_b2=string_delete(var_work_string_b2,1,var_ii_pos)
   var_ii3=scireal(var_work_string_b2); // B
   ds_map_replace(dsm_mtl,'Kd_'+current_mtl,make_colour_rgb(var_ii1*255,var_ii2*255,var_ii3*255));
  }
  var_work_string_b1=string_delete(var_work_string_b1,1,var_end_pos)
  var_end_pos=string_pos(chr(10),var_work_string_b1);
  if var_done=1 var_done=2;
  if var_end_pos=0 and var_done=0 {
   var_end_pos=string_length(var_work_string_b1);
   var_done=1;
  }
  else {
   var_work_string_b2=string_copy(var_work_string_b1,var_start_pos,(var_end_pos-var_start_pos)-1);
   while string_char_at(var_work_string_b2,1)=' ' {
    var_work_string_b2=string_delete(var_work_string_b2,1,1)
   }
  }
 }
}

// convert collected data to model

var var_current_face=0;
var current_vertex=0;
var current_normal=0;
var current_texture=0;
var var_color_value='null_material';
var current_model_gm_index=d3d_model_create();
d3d_model_primitive_begin(current_model_gm_index,pr_trianglelist)
 while var_current_face<var_total_faces {
  var_color_value=ds_map_find_value(dsm_mtl,'Kd_'+ds_grid_get(dsg_f,9,var_current_face))
  if var_color_value=undefined var_color_value=c_white;
  var_current_vertex=ds_grid_get(dsg_f,0,var_current_face)-1
  var_current_texture=ds_grid_get(dsg_f,1,var_current_face)-1
  var_current_normal=ds_grid_get(dsg_f,2,var_current_face)-1
  d3d_model_vertex_normal_texture_colour
   (current_model_gm_index
   ,ds_grid_get(dsg_v,0,var_current_vertex)
   ,ds_grid_get(dsg_v,1,var_current_vertex)
   ,ds_grid_get(dsg_v,2,var_current_vertex)
   ,ds_grid_get(dsg_vn,0,var_current_normal)
   ,ds_grid_get(dsg_vn,1,var_current_normal)
   ,ds_grid_get(dsg_vn,2,var_current_normal)
   ,ds_grid_get(dsg_vt,0,var_current_texture)
   ,ds_grid_get(dsg_vt,1,var_current_texture)
   ,var_color_value
   ,1
  )
  // // //
  var_current_vertex=ds_grid_get(dsg_f,3,var_current_face)-1
  var_current_texture=ds_grid_get(dsg_f,4,var_current_face)-1
  var_current_normal=ds_grid_get(dsg_f,5,var_current_face)-1
  d3d_model_vertex_normal_texture_colour
   (current_model_gm_index
   ,ds_grid_get(dsg_v,0,var_current_vertex)
   ,ds_grid_get(dsg_v,1,var_current_vertex)
   ,ds_grid_get(dsg_v,2,var_current_vertex)
   ,ds_grid_get(dsg_vn,0,var_current_normal)
   ,ds_grid_get(dsg_vn,1,var_current_normal)
   ,ds_grid_get(dsg_vn,2,var_current_normal)
   ,ds_grid_get(dsg_vt,0,var_current_texture)
   ,ds_grid_get(dsg_vt,1,var_current_texture)
   ,var_color_value
   ,1
  )
  // // //
  var_current_vertex=ds_grid_get(dsg_f,6,var_current_face)-1
  var_current_texture=ds_grid_get(dsg_f,7,var_current_face)-1
  var_current_normal=ds_grid_get(dsg_f,8,var_current_face)-1
  d3d_model_vertex_normal_texture_colour
   (current_model_gm_index
   ,ds_grid_get(dsg_v,0,var_current_vertex)
   ,ds_grid_get(dsg_v,1,var_current_vertex)
   ,ds_grid_get(dsg_v,2,var_current_vertex)
   ,ds_grid_get(dsg_vn,0,var_current_normal)
   ,ds_grid_get(dsg_vn,1,var_current_normal)
   ,ds_grid_get(dsg_vn,2,var_current_normal)
   ,ds_grid_get(dsg_vt,0,var_current_texture)
   ,ds_grid_get(dsg_vt,1,var_current_texture)
   ,var_color_value
   ,1
  )
  var_current_face+=1;
 }
d3d_model_primitive_end(current_model_gm_index);

ds_grid_destroy(dsg_v);
ds_grid_destroy(dsg_vt);
ds_grid_destroy(dsg_vn);
ds_grid_destroy(dsg_f);
ds_map_destroy(dsm_mtl);

return current_model_gm_index;

Below is the required script.

Expand///scireal(string)
//
// converts a string to a real value, with scientific notation.
//
// string   number to convert to a real value, string
//
// scientific notation is used by adding an "e", such as "1.23652e-016".
//
/// GMLscripts.com/license

var var_e=0;
var var_work_value=string_lower(argument[0]);
var var_e_pos=string_pos('e',var_work_value);
if var_e_pos!=0 {
 var_e=real(string_copy(var_work_value,var_e_pos+1,string_length(var_work_value)-(var_e_pos+1)));
 var_work_value=real(string_delete(var_work_value,var_e_pos,string_length(var_work_value)-var_e_pos))*power(10,var_e);
}
else var_work_value=real(var_work_value);
return var_work_value;

#10 Script Submission » good car physics, old physics engine » 2019-08-27 14:47:27

lostdarkwolf
Replies: 2

I made a nice car physics script. The script inputs are what control the car. There are many adjustable settings in the script. The script includes reactions to objects flagged as solid.
EDIT: bugfix. (position correction needed to come before collision detection.)

Expand///car_physics(do_handbrake, turn_left, turn_right, gas, brake)
//
// Simulates car physics for an object.
// Has 2 required variables (listed below).
// Should be called in a step event.
//
// do_handbrake   true if using handbrake, real (boolean)
// turn_left      magnitude of turning left, real (normal)
// turn_right     magnitude of turning right, real (normal)
// gas            magnitude of acceleration, real (normal)
// brake          magnitude of brake, real (normal)
//
/// GMLscripts.com/license
/* 
 - - - ABOUT crash_damage: - - -
- "crash_damage" ranges from 0 to 2.
It is not used for anything, just
provided to help with calculating
crash damage, should you need it.
- "crash_damage" does not account for 
the motion of the object that is
crashed into. 
- "crash_damage" is a local variable.
- "crash_damage" can exceed 2 if speed
is somehow greater than max_speed.
 - - - REQUIRED VARIABLES: - - -
All listed variables are local.
- input_deadzone
- is_skidding (set to false)
*/
// - - - HANDLING - - -

// vehicle stats:
var veer_return_strength, acceleration, max_speed, braking_strength, 
reverse_acceleration, max_reverse_speed, handbrake_slowdown_rate, 
natural_deceleration_rate, veer_left_strength, drift_left_strength, 
drift_left_angular_bonus, veer_right_strength, drift_right_strength, 
drift_right_angular_bonus, drift_return_strength, veer_return_strength,
drift_angle_pullback, veer_angle_pullback, minumum_drifting_speed, 
crash_bounce_back, skid_start_threshold, ii;
veer_return_strength=0.75; // normal (0-1)
acceleration=1;
max_speed=12;
braking_strength=2;
reverse_acceleration=1;
max_reverse_speed=4;
handbrake_slowdown_rate=0.5;
natural_deceleration_rate=0.08; // normal (0-1)
veer_left_strength=3;
drift_left_strength=1.7;
drift_left_angular_bonus=2;
veer_right_strength=3;
drift_right_strength=1.7;
drift_right_angular_bonus=2;
drift_return_strength=0.2; // normal (0-1)
veer_return_strength=0.75; // normal (0-1)
drift_angle_pullback=0.04; // normal (0-1)
veer_angle_pullback=0.2; // normal (0-1)
minumum_drifting_speed=6;
crash_bounce_back=0.25; // normal (0-1)
skid_start_threshold=1.2;

// ricochet settings:
var ricochet_precision, ricochet_speed_loss, max_ricochet_angle;
ricochet_precision=5;
ricochet_speed_loss=0.3; // normal (0-1)
max_ricochet_angle=55;

// position correction settings:
var pos_corr_distance_precision, pos_corr_distance,
pos_corr_angular_precision, pos_corr_distance_precision;
pos_corr_distance_precision=1;
pos_corr_distance=18;
pos_corr_angular_precision=2;
pos_corr_distance_precision=1;

var do_handbrake=argument0;
var turn_left_magnitude=argument1;
var turn_right_magnitude=argument2;
var gas_magnitude=argument3;
var brake_magnitude=argument4;

// "skid_threshold" is a precise angualar threshold that the veering rate can meet, but not pass.
var skid_threshold = (1/(-veer_return_strength+1))*max(veer_right_strength,veer_left_strength);
if speed=0 direction=image_angle;
if do_handbrake is_skidding=true;
var do_gas=gas_magnitude>input_deadzone
var do_brake=brake_magnitude>input_deadzone
var do_left=turn_left_magnitude>input_deadzone;
var do_right=turn_right_magnitude>input_deadzone;
// if angular difference is somehow in the range of skidding, enable skidding.
if abs(angle_difference(direction,image_angle))>skid_threshold*skid_start_threshold is_skidding=true;
// acceleration
if not do_handbrake and do_gas {
 var raw_speed_gain=lerp(gas_magnitude*acceleration,0,min(abs(angle_difference(direction,image_angle)),90)/90);
 speed+=lerp(raw_speed_gain,0,abs(speed)/max_speed);
}
// brake and reverse
if not do_handbrake and do_brake {
 if speed>0 speed-=brake_magnitude*braking_strength;
 else { // reverse
  speed-=lerp(reverse_acceleration,0,abs(speed)/max_reverse_speed);
 }
}
if do_handbrake {
 speed+=-sign(speed)*handbrake_slowdown_rate;
}
// natural deceleration
if not do_brake and not do_gas {
 speed+=-sign(speed)*natural_deceleration_rate;
 if abs(speed)<=natural_deceleration_rate speed=0;
}

if do_left and do_right do_left=false;
// turn left
if do_left {
 if is_skidding {
  image_angle+=drift_left_angular_bonus+min((speed/max_speed)*(drift_left_strength*turn_left_magnitude),drift_left_strength)
 }
 else image_angle+=min((speed/max_speed)*(veer_left_strength*turn_left_magnitude),veer_left_strength)
}
// turn right
if do_right {
 if is_skidding {
  image_angle-=drift_right_angular_bonus+min((speed/max_speed)*(drift_right_strength*turn_right_magnitude),drift_right_strength)
 }
 else image_angle-=min((speed/max_speed)*(veer_right_strength*turn_right_magnitude),veer_right_strength)
}
// veer return and skid return
if not do_left and not do_right {
 if is_skidding direction=lerp(direction,direction-angle_difference(direction,image_angle),drift_return_strength)
 else direction=lerp(direction,direction-angle_difference(direction,image_angle),veer_return_strength)
}
// veering and drifting
else { 
 if is_skidding direction=direction-lerp(0,angle_difference(direction,image_angle),drift_angle_pullback)
 else direction=direction-lerp(0,angle_difference(direction,image_angle),veer_angle_pullback)
}
// stop skidding
if (not do_handbrake and abs(angle_difference(direction,image_angle))<=skid_threshold) or abs(speed)<minumum_drifting_speed {
 is_skidding=false;
}

// - - - POSITION CORRECTION - - -

if not place_free(x,y) {
 ii=pos_corr_distance_precision;
 var ii2=0;
 while ii<=pos_corr_distance {
  while ii2<360 {
   if place_free(x+lengthdir_x(ii,ii2),y+lengthdir_y(ii,ii2)) {
    x=x+lengthdir_x(ii,ii2)
    y=y+lengthdir_y(ii,ii2)
    ii=9999;
    ii2=9999;
   }
   ii2+=pos_corr_angular_precision;
  }
  ii2=0;
  ii+=pos_corr_distance_precision;
 }
}

// - - - COLLISION DETECTION - - -

// ricochet and head-on collision
if abs(speed)>0 {
 // ricochet
 if speed>0 {
  ii=ricochet_precision;
  if not place_free(x+hspeed,y+vspeed) move_contact_solid(direction,speed);
  while not place_free(x+hspeed,y+vspeed) and ii<=max_ricochet_angle {
   var free_minus = place_free(x+lengthdir_x(speed,direction-ii),y+lengthdir_y(speed,direction-ii))
   var free_plus = place_free(x+lengthdir_x(speed,direction+ii),y+lengthdir_y(speed,direction+ii))
   if free_minus and free_plus { // if both places are free, choose one randomly.
    if irandom(1) free_minus=false;
    else free_plus=false;
   }
   if free_plus and not free_minus {
    direction+=ii;
    speed=lerp(speed,speed*ricochet_speed_loss,ii/max_ricochet_angle);
    ii=9999;
    crash_damage=ii/max_ricochet_angle;
   }
   if free_minus and not free_plus {
    direction-=ii;
    speed=lerp(speed,speed*ricochet_speed_loss,ii/max_ricochet_angle);
    ii=9999;
    crash_damage=ii/max_ricochet_angle;
   }
   ii+=ricochet_precision;
  }
 }
 // head-on collision
 if not place_free(x+lengthdir_x(speed,direction),y+lengthdir_y(speed,direction)) {
  speed=-speed*crash_bounce_back;
  crash_damage=1+(abs(speed)/max_speed);
  //stop skidding
  direction=image_angle+median(skid_threshold,-skid_threshold,angle_difference(image_angle,direction))
  is_skidding=false;
 }
}

#11 Re: Script Submission » Is_Prime » 2019-08-11 05:00:50

Sorry to grave dig, but I noticed that yourself's script, and xDanielx's modification of yourself's script, are both flawed. According to both scripts, the following numbers are incorrectly considered as prime numbers. 25, 49, 121, and 169 (first four only). atarian's script can handle negative numbers, and it can be seemingly flawless with the following inclusion:

Expand_p = argument0 // existing line
if (_p=2) return true // new line
val = true // existing line 

#12 Re: Script Submission » Vector graphics kit » 2019-07-23 12:09:54

bump. (Six new scripts added to main post)

#13 Re: Script Submission » no vk platformer physics (BIG UPDATE 3/13/19). Uses GM's variables. » 2019-03-13 22:40:07

Sorry if this is dual-posting, but this is a pretty big update with a whole new set of scripts.
Each instance calling collision_platformer_list functions should have its own object list.
Each object list is a list of objects and/or instances to be considered solid only by the instance calling the script.
The solid flag is ignored.
The ability to specify an object list is useful to be able to combine psudo-solids and local-multiplayer.

Please let me know if you notice any problems or have any questions.

Expand///collision_platformer_list_react(object_list, stair_height, gravity);
//
// Provides platformer physics reactions for an instance without using input.
// Integrates GM's native variables into its code. (Including but not limited to direction, speed, and gravity.)
// Requires scripts "move_contact_list" and "place_meeting_list".
// 
// stair_height   Maximum height of stairs, real.
// gravity        Default gravity for the current step, real.
// object_list    List of specific instances and/or object indexes to treat as solid.
// 
// Should be placed in the step event of any platformer instance.
// All arguments are optional.
//
/// GMLscripts.com/license

var CPS_obj_list;
CPS_obj_list=argument[0];

var CPS_stair_height; CPS_stair_height=13;
if argument_count>1 CPS_stair_height=argument[1];

var CPS_gravity; CPS_gravity=1;
if argument_count>2 CPS_gravity=argument[2];

// Check for collision with ground
//CPS_on_ground=0;
if (place_meeting_list(x+4,y+1,CPS_obj_list) or place_meeting_list(x-4,y+1,CPS_obj_list)) and place_meeting_list(x,y+1,CPS_obj_list) {
 gravity=0;
 if vspeed>0 vspeed=0
 //CPS_on_ground=1;
 move_contact_list(CPS_obj_list,270,max(speed,2,CPS_stair_height))
}
else gravity=CPS_gravity;
Expand///collision_platformer_list_correct(object_list, stair_height);
//
// Provides platformer physics reactions for an instance without using input.
// Integrates GM's native variables into its code. (Including but not limited to direction, speed, and gravity.)
// Requires scripts "place_meeting_list", "wiggle_list", "move_outside_list", "move_contact_list", and "direction_fix_list".
// 
// object_list    List of specific instances and/or object indexes to treat as solid, real (ds_list).
// stair_height   Maximum height of stairs, real.
// 
// Should be placed in the step event of any platformer instance.
// All arguments are optional.
// The object list contains a list of objects and/or instances that should be treated as solid by the instance calling the script.
//
/// GMLscripts.com/license

var CPS_obj_list;
CPS_obj_list=argument[0];

var CPS_stair_height; CPS_stair_height=13;
if argument_count>1 CPS_stair_height=argument[1];

var var_placemeet_current=place_meeting_list(x,y,CPS_obj_list)

// var_a represents 
var var_a=median(CPS_stair_height,abs(hspeed)*2.2,1);
var var_can_stair=var_placemeet_current
                  and (not place_meeting_list(x+(hspeed),y-var_a,CPS_obj_list)
                  or not place_meeting_list(x+(hspeed),y-(var_a*0.8),CPS_obj_list)
                  or not place_meeting_list(x+(hspeed),y-(var_a*0.6),CPS_obj_list)
                  or not place_meeting_list(x+(hspeed),y-(var_a*0.4),CPS_obj_list)
                  or not place_meeting_list(x+(hspeed),y-(var_a*0.2),CPS_obj_list))
                  and hspeed!=0;

var var_stair_up=0;

//go up stairs
move_outside_list(CPS_obj_list,90,var_a);

//get unstuck
var ii; ii=1;
while ii<ceil(var_a*2) and place_meeting_list(x,y,CPS_obj_list) {
 if place_meeting_list(x,y,CPS_obj_list) wiggle_list(CPS_obj_list,ii); // Dont let the instance get stuck
 ii+=2;
}

// Upward vertical collisions and bumps (like with a sloped roof)
if place_meeting_list(x,y+vspeed,CPS_obj_list) and vspeed<0 {
 var var_dir_fix_result=0;
 var var_old_dir=direction
 var_dir_fix_result=direction_fix_list(CPS_obj_list,5,52);
 if abs(angle_difference(90,direction))>=90 {
  direction=var_old_dir;
  var_dir_fix_result=0;
 }
 else {
  // speed loss for hitting an angled roof
  speed=speed*0.9
 }
 if place_meeting_list(x,y+vspeed,CPS_obj_list) and not var_dir_fix_result {
  move_contact_list(CPS_obj_list,((sign(vspeed)+1)*90)+90,speed);
  vspeed=0;
 }
}

//horizontal collisions
if place_meeting_list(x+hspeed,y,CPS_obj_list) 
and place_meeting_list(x+hspeed,y-(CPS_stair_height*0.25),CPS_obj_list) 
and place_meeting_list(x+hspeed,y+(CPS_stair_height*0.25),CPS_obj_list) 
and place_meeting_list(x+hspeed,y-(CPS_stair_height*0.5),CPS_obj_list) 
and place_meeting_list(x+hspeed,y+(CPS_stair_height*0.5),CPS_obj_list) 
and place_meeting_list(x+hspeed,y-(CPS_stair_height*0.75),CPS_obj_list) 
and place_meeting_list(x+hspeed,y+(CPS_stair_height*0.75),CPS_obj_list) 
and place_meeting_list(x+hspeed,y-CPS_stair_height,CPS_obj_list) 
and place_meeting_list(x+hspeed,y+CPS_stair_height,CPS_obj_list) 
and hspeed!=0 {
 hspeed=0;
}

// This part is optional and can be safely erased
// check if on wall (should be done after corrective translations)
if not place_meeting_list(x,y+1,CPS_obj_list) {
 CPS_on_wall=0 // this is a local variable
 if place_meeting_list(x+1,y,CPS_obj_list) CPS_on_wall=1;
 if place_meeting_list(x-1,y,CPS_obj_list) CPS_on_wall=-1;
}

REQUIRED SCRIPTS:

Expand///place_meeting_list(x, y, obj_list)
//
// Checks for a collision with a list of instances and/or objects.
// Does the same thing as place_meeting, but with a list of objects instead of just one.
//
// x          X position to check.
// y          Y position to check.
// obj_list   list of instances and/or objects to check for collision with.
//
/// GMLscripts.com/license

var var_arg_x, var_arg_y, var_arg_ds, var_return, ii;
var_arg_x=argument[0];
var_arg_y=argument[1];
var_arg_ds=argument[2];

//show_message('place_meeting_list called')
var_return=0;
var ii=ds_list_size(var_arg_ds)-1;
while ii>=0 and var_return=0 {
 //show_message('ii is '+string(ii))
 if place_meeting(var_arg_x,var_arg_y,ds_list_find_value(var_arg_ds,ii)) var_return=1;
 ii-=1;
}

return var_return;
Expand///wiggle_list(obj_list, max_dist*)
// * arguments are optional
//
// tries to wiggle an object free of a listed instance.
// returns true if successful.
//
// obj_list    list of instances and/or objects to move out of, real (ds_list)
// max_dist    ammount in pixels of the position change, real
//
///gmlscripts.com

var ii_distance, ii_inst, var_obj_list;
var_obj_list=argument[0];
ii_distance=1000000000;
if argument_count>1 ii_distance=argument[1];
ii_inst=-1;
if argument_count>2 ii_inst=argument[2];

// overhead view, you are X.
// 7 8 9
// 4 X 6
// 1 2 3
// order is X64283719

if !place_meeting_list(x,y,var_obj_list) {return 1;} // X
if !place_meeting_list(x+ii_distance,y,var_obj_list) {x+=ii_distance; return 1;} // 6
if !place_meeting_list(x-ii_distance,y,var_obj_list) {x-=ii_distance; return 1;} // 4
if !place_meeting_list(x,y+ii_distance,var_obj_list) {y+=ii_distance; return 1;} // 2
if !place_meeting_list(x,y-ii_distance,var_obj_list) {y-=ii_distance; return 1;} // 8
if !place_meeting_list(x+ii_distance,y+ii_distance,var_obj_list) {y+=ii_distance; x+=ii_distance; return 1;} // 3
if !place_meeting_list(x-ii_distance,y-ii_distance,var_obj_list) {y-=ii_distance; x-=ii_distance; return 1;} // 7
if !place_meeting_list(x-ii_distance,y+ii_distance,var_obj_list) {y+=ii_distance; x-=ii_distance; return 1;} // 1
if !place_meeting_list(x+ii_distance,y-ii_distance,var_obj_list) {y-=ii_distance; x+=ii_distance; return 1;} // 9
Expand///move_outside_list(obj_list,dir,max_dist,step_size*)
//
// moves the instance running this script a set number of pixels 
//    in the specified direction until it meets a listed instance.
//
// obj_list    a list of instances and/or objects to collide with, real (ds_list with instances and/or objects)
// dir         direction of travel, real
// max_dist    maximum distance of travel, real
// step_size   size of steps to travel, real
//
// Returns successful if collided.
// Useful for having psudo-solids with local multiplayer.
// Ignores the "solid" flag.
//
/// GMLscripts.com/license

var var_arg_obj_list;
var_arg_obj_list=argument[0];
var_arg_dir=argument[1];
var_arg_maxdist=argument[2];

step_size=0.01;
if argument_count>3 step_size=argument[3];

var var_start_x=x;
var var_start_y=y;

var_dist_traveled=0;
if place_meeting_list(x,y,var_arg_obj_list) {
 while place_meeting_list(x+lengthdir_x(step_size,var_arg_dir),y+lengthdir_y(step_size,var_arg_dir),var_arg_obj_list) 
       and var_dist_traveled<=var_arg_maxdist {
  x+=lengthdir_x(step_size,var_arg_dir);
  y+=lengthdir_y(step_size,var_arg_dir);
  var_dist_traveled+=step_size;
 }
 if var_dist_traveled<=var_arg_maxdist {
  x+=lengthdir_x(step_size,var_arg_dir);
  y+=lengthdir_y(step_size,var_arg_dir);
 }
}

// return successful if outside
if var_dist_traveled<=var_arg_maxdist return 1;
else {
 x=var_start_x;
 y=var_start_y;
}
Expand///move_contact_list(obj_list,dir,max_dist,step_size*)
//
// moves the instance running this script a set number of pixels 
//    in the specified direction until it meets a listed instance.
//
// obj_list    a list of instances and/or objects to collide with, real (ds_list with instances and/or objects)
// dir         direction of travel, real
// max_dist    maximum distance of travel, real
// step_size   size of steps to travel, real
//
// Returns successful if collided.
// With this script, instance A can collide with instance B, while 
//    instance C does not (if it is set up appropriately).
// Useful for having psudo-solids with local multiplayer.
// Ignores the "solid" flag.
//
/// GMLscripts.com/license

var var_arg_obj_list;
var_arg_obj_list=argument[0];
var_arg_dir=argument[1];
var_arg_maxdist=argument[2];

step_size=0.01;
if argument_count>3 step_size=argument[3];

var var_start_x=x;
var var_start_y=y;

var_dist_traveled=0;
if not place_meeting_list(x,y,var_arg_obj_list) {
 while not place_meeting_list(x+lengthdir_x(step_size,var_arg_dir),y+lengthdir_y(step_size,var_arg_dir),var_arg_obj_list) 
       and var_dist_traveled<=var_arg_maxdist {
  x+=lengthdir_x(step_size,var_arg_dir);
  y+=lengthdir_y(step_size,var_arg_dir);
  var_dist_traveled+=step_size;
 }
}
// return successful if collided
if var_dist_traveled<=var_arg_maxdist return 1;
else {
 x=var_start_x;
 y=var_start_y;
}
Expand///direction_fix_list(obj_list, angular_precision*, direction_flux_cap*)
// * arguments are optional
//
// corrects the direction of the calling instance to not collide with the target instance (or object).
// returns weather successful
//
// obj_list    a list of instances and/or objects to collide with, real (ds_list with instances and/or objects)
// inst                  instance to avoid, set to -1 to avoid solids.
// angular_precision     precision for checking for empty spots
// direction_flux_cap    cap for direction change
//
///gmlscripts.com

var SCRobj_list, SCRprec, SCRlimit;
SCRobj_list=argument[0];
SCRprec=2;
if argument_count>1 SCRprec=argument[1];
SCRlimit=90;
if argument_count>2 SCRlimit=argument[2];


if place_meeting_list(x+lengthdir_x(speed,direction),y+lengthdir_y(speed,direction),SCRobj_list) {
 //smooth movement
 for(ii=0; ii<=SCRlimit; ii+=SCRprec;){
  if(!place_meeting_list(x+lengthdir_x(speed,direction+ii),y+lengthdir_y(speed,direction+ii),SCRobj_list)) {
   direction+=ii;
   ii=90+1;
   return true;
  }
  if(!place_meeting_list(x+lengthdir_x(speed,direction-ii),y+lengthdir_y(speed,direction-ii),SCRobj_list) and
     ii<=90) {
   direction-=ii;
   ii=90+1;
   return true;
  }
 }
}
return false;

#14 Script Submission » convert a real number to a binary float string, and back » 2019-01-25 16:37:29

lostdarkwolf
Replies: 0

The following scripts can convert a real number into a binary string that represents a floating-point number. Multiple bit depths are supported, as long as you know the format. 32-bit is default. Useful for file data.

Expand///float_to_dec(binary_string, mantissa_bits*, hidden_mant_bit*, exp_bias*)
// * not required
//
// Converts a binary string representing a floating-point number, into a real number.
//
// binary_string     a binary string representing a floating-point number, string (binary sequence)
// fraction_bits     number of bits in the fraction portion of the float, real (integer)
// hidden_mant_bit   true if the first bit of the mantissa is hidden, real (binary)
// exp_bias          the bias of the exponent, real (integer)
//
// 32-bit float format is default.
//
/// GMLscripts.com/license

//result is a binary string
var var_arg_bin_str=argument[0];
var exp_bits=8;
if argument_count>1 exp_bits=argument[1];
var fraction_bits=23;
if argument_count>2 fraction_bits=argument[2];
var hidden_mant_bit=1;
if argument_count>3 hidden_mant_bit=argument[3];
var var_exp_bias=power(2,exp_bits-1)-1;
if argument_count>4 var_exp_bias=argument[4];

// get sign
var var_sign=power(-1,real(string_char_at(var_arg_bin_str,1)))

// get exponent
var var_exp=real(base_convert(string_copy(var_arg_bin_str,2,exp_bits),2,exp_bits+2))-var_exp_bias;

// get mantissa
var ii=0;
var var_mantissa=hidden_mant_bit;
repeat fraction_bits {
 if string_char_at(var_arg_bin_str,exp_bits+2+ii)='1' var_mantissa+=power(2,-(ii+1))
 ii+=1;
}

// return decimal
return (var_sign*var_mantissa*power(2,var_exp));
Expand///dec_to_float(dec, mantissa_bits*, hidden_mant_bit*, exp_bias*)
// * not required
//
// Converts a real number into a binary string representing a floating-point number.
//
// dec               the number to convert into float, real
// fraction_bits     number of bits in the fraction portion of the float, real (integer)
// hidden_mant_bit   true if the first bit of the mantissa is hidden, real (binary)
// exp_bias          the bias of the exponent, real (integer)
//
// 32-bit float format is default.
//
/// GMLscripts.com/license

var var_arg_decimal=argument[0];
var exp_bits=8;
if argument_count>1 exp_bits=argument[1];
var fraction_bits=23;
if argument_count>2 fraction_bits=argument[2];
var hidden_mant_bit=1; // one is hidden
if argument_count>3 hidden_mant_bit=argument[3];
var var_exp_bias=power(2,exp_bits-1)-1;
if argument_count>4 var_exp_bias=argument[4];

// set sign
var return_string=string(!(min(sign(var_arg_decimal),0)+1));

var var_decimal_whole=floor(var_arg_decimal);

// convert integer of "dec" to binary.
var var_decimal_whole_bin=base_convert(string(var_decimal_whole),exp_bits+2,2);
var_decimal_whole_bin=string_copy(var_decimal_whole_bin,2,string_length(var_decimal_whole_bin)-1);
// set exponent
return_string+=base_convert(string(var_exp_bias+string_length(var_decimal_whole_bin)),exp_bits+2,2,exp_bits)
// set the mantissa
return_string+=var_decimal_whole_bin;

// convert decimal of "dec" to binary and finish the fraction bits
var var_decimal_fraction=var_arg_decimal-var_decimal_whole;
for (var ii=string_length(var_decimal_whole_bin)+hidden_mant_bit; ii<=fraction_bits; ii+=1;) {
 if var_decimal_fraction*2>1 return_string+="1"
 else return_string+="0"
 var_decimal_fraction=(var_decimal_fraction*2 mod 1);
}

return return_string;

#15 Script Submission » Xot's base_convert script modded to support a higher base (update 3) » 2019-01-25 00:57:05

lostdarkwolf
Replies: 0

I've modified Xot's base_convert script to support a higher base depth. Original functionality retained.
Original: https://www.gmlscripts.com/script/base_convert
I've also made a set of scripts that does almost the same thing as the base_convert script. They are below the base_convert script.

This version of base_convert seems to work without error as far as i have tested, but it can be slow if the base depth is a big number, depending on the system. update 3: displayable character set extended to 94.

Expand/// base_convert(number,oldbase,newbase,tot)
//
//  Returns a string of digits representing the
//  given number converted form one base to another.
//  The largest base supported is unknown.
//  After base94, the character set changes to one that is not meant to be displayed.
//
//      number      integer value to be converted, string
//      oldbase     base of the given number, integer
//      newbase     base of the returned value, integer
//      tot         Total number of places of the returned value to be shown. Use -1 to disable. integer
//
//  The first character in the character set will be inserted
//  to match the number of places specified by "tot".
//  This script will hang temporarily if "oldbase" or
//  "newbase" is set to a high number. 
//
/// GMLscripts.com/license
{
    var number, oldbase, newbase, out, format;
    number = string_upper(argument[0]);
    oldbase = argument[1];
    newbase = argument[2];
    format = -1;
    if (argument_count > 3) format = argument[3];
    out = "";
 
    var len, tab_in, tab_out;
    len = string_length(number);
    tab_in = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz"+'!"'+'$%&'+"'"+"()*+,-./:;<=>?@[]^_`{|}~ "
    if oldbase>94 {
        tab_in="";
        var ii=0;
        do {
            tab_in+=chr(ii);
            ii+=1;
        }
        until string_length(tab_in)=oldbase
    }
    tab_out = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz"+'!"'+'$%&'+"'"+"()*+,-./:;<=>?@[]^_`{|}~ "
    if newbase>94 {
        tab_out="";
        var ii=0;
        do {
            tab_out+=chr(ii);
            ii+=1;
        }
        until string_length(tab_out)=newbase
    }
 
    var i, num;
    for (i=0; i<len; i+=1) {
        num[i] = string_pos(string_char_at(number, i+1), tab_in) - 1;
    }
 
    do {
        var divide, newlen;
        divide = 0;
        newlen = 0;
        for (i=0; i<len; i+=1) {
            divide = divide * oldbase + num[i];
            if (divide >= newbase) {
                num[newlen] = divide div newbase;
                newlen += 1;
                divide = divide mod newbase;
            } else if (newlen  > 0) {
                num[newlen] = 0;
                newlen += 1;
            }
        }
        len = newlen;
        out = string_char_at(tab_out, divide+1) + out;
    } until (len == 0 or string_length(out) == format);
 
    if (format > 0) {
        while (string_length(out) < format) {
            out = string_char_at(tab_out, 0) + out
        }
    }
    return out;
}

My goal was to convert a number into a string that can be safely copied to the clipboard in the most space efficient way. I was successful, but I was unable to optimize the script above. So, after a bit of focus, I've come up with the following scripts instead. To ensure safe operation, I've decided to only use printable characters. Unfortunately, the number sign is excluded because gamemaker converts it into a newline, and it cannot be represented alone.

This script efficiently converts an integer into a clipboard friendly printable string.

Expand///dec_to_print(dec, base*, tot*)
// * not required
//
// Converts an integer into a printable string.
// Printable characters, include space, but exclude number sign.
// Base94 is the highest base supported.
//
// dec    decimal to convert, real (integer)
// base   base depth to use, real (integer)
// tot    total number of places, real (integer)
//
/// GMLscripts.com/license

var var_arg_decimal=argument[0];
var_arg_base_depth=94
if argument_count>1 var_arg_base_depth+=argument[1];
var var_arg_tot=-1;
if argument_count>2 var_arg_tot=argument[2];
var Return='';

var var_key_str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz"+'!"'+'$%&'+"'"+"()*+,-./:;<=>?@[]^_`{|}~ ";
while var_arg_decimal>0 {
 if var_arg_tot>=0 {
  if string_length(Return)<var_arg_tot {
   Return=string_char_at(var_key_str,(var_arg_decimal mod var_arg_base_depth)+1)+Return;
  }
 }
 else {
  Return=string_char_at(var_key_str,(var_arg_decimal mod var_arg_base_depth)+1)+Return;
 }
 var_arg_decimal=floor(var_arg_decimal/var_arg_base_depth);
}

while string_length(Return)<max(var_arg_tot,1) Return='0'+Return;

return Return;

This script efficiently converts a printable string into an integer.

Expand///print_to_dec(clip, base*)
// * not required
//
// Converts a printable string into a decimal.
// Output only uses printable characters, including space.
// Base94 is the highest base supported.
//
// clip   96-bit string to convert, real (integer)
// base   base depth to use, real (integer)
//
/// GMLscripts.com/license

var var_arg_clip=argument[0];
var var_arg_base_depth=94;
if argument_count>1 var_arg_base_depth+=argument[1];

var_key_str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz"+'!"'+'$%&'+"'"+"()*+,-./:;<=>?@[]^_`{|}~ "
var ii=1;
var ii2=string_length(var_arg_clip);
var var_char_num=0;
while ii2>0 {
 var_char_num+=(string_pos(string_char_at(var_arg_clip,ii),var_key_str)-1)*power(var_arg_base_depth,ii2-1);;
 ii+=1;
 ii2-=1;
}

return var_char_num;

#16 Re: Script Submission » RNG with random, uneven, and shifting distribution. » 2018-12-28 04:46:41

Cool! happy

This script is a way of getting random numbers where you have no idea what the likelihood distribution is, because the likelihood distribution is also random. Furthermore, the likelihood distribution is capable of (evenly) randomly changing every time a random number is requested. I realize that many may not want to pull random numbers from just one distribution identity, so that's what the sets are for. In the example, I only needed one distribution set, so that's all I made. This script uses local variables, and thus it will only apply to the object that is calling it.

This tallys the occorance of 0 through 9, with the script.
Create event:
(you want the deviation to be high unless you want to start with a more even distribution. If I had also wanted to tally 0 through 14, then max_n would be at least 14.)

Expandirandom_uneven_setup(9,0,0.7)

testval[0]=0
testval[1]=0
testval[2]=0
testval[3]=0
testval[4]=0
testval[5]=0
testval[6]=0
testval[7]=0
testval[8]=0
testval[9]=0

Step event:
(you want the diviation to be low, otherwise the range values would jump around so much, that it would likely risk losing some of this script's uneven nature.)

Expandrepeat 100 {
 testval[ irandom_uneven(9,0,0.015) ]+=1;
}

Draw event:

Expanddraw_text(10,10,'0: '+string(testval[0])+'#1: '+string(testval[1])+'#2: '+string(testval[2])+'#3: '+string(testval[3])+'#4: '+string(testval[4])+'#5: '+string(testval[5])+'#6: '+string(testval[6])+'#7: '+string(testval[7])+'#8: '+string(testval[8])+'#9: '+string(testval[9]))

- - - - - - - -
To compare, here is how you tally 0 through 9 without the script (and with even distribution).
Create event:

Expandtally[0]=0;
tally[1]=0;
tally[2]=0;
tally[3]=0;
tally[4]=0;
tally[5]=0;
tally[6]=0;
tally[7]=0;
tally[8]=0;
tally[9]=0;

Step event:

Expandrepeat 100 {
 tally[irandom(9)]+=1;
}

Draw event:

Expanddraw_text(200,10,'0: '+string(tally[0])+'#1: '+string(tally[1])+'#2: '+string(tally[2])+'#3: '+string(tally[3])+'#4: '+string(tally[4])+'#5: '+string(tally[5])+'#6: '+string(tally[6])+'#7: '+string(tally[7])+'#8: '+string(tally[8])+'#9: '+string(tally[9]))

#17 Script Submission » RNG with random, uneven, and shifting distribution. » 2018-12-26 20:18:17

lostdarkwolf
Replies: 2

Random number generation with uneven distribution.

Both scripts are required.

Expand///irandom_uneven_setup(n_max,range_set_max,deviation)
//
// Performs initial setup for "irandom_uneven".
// This should be executed only once per calling object, such as in a create event.
//
// n_max           the highest integer used for RNG. 
// range_set_max   maximum number of sets of range distributions.
// deviation       initial random deviation from 0.5.
//
/// GMLscripts.com/license

var nn, devi;
nn=argument0;
set_max=argument1;
devi=median(argument2,0,1);

irandom_uneven_n_max=nn;
irandom_uneven_range_grid=ds_grid_create(set_max+1,nn+1);

//create distribution (raw data)
var ii_set,ii_n;
ii_set=0;
while ii_set<=set_max {
 ii_n=0;
 while ii_n<=nn {
  ds_grid_add(irandom_uneven_range_grid,ii_set,ii_n,median(0.5+((-0.5*devi)+random(devi)),1,0));
  ii_n+=1;
 }
 ii_set+=1;
}

the wording on the script below has been corrected as of 1/6/19.

Expand///irandom_uneven(n,range_set,deviation)
//
// Generates and returns a random number with random, uneven, and shifting distribution.
// This should be called on-demand.
// returns a number between zero and n.
//
// n           Max number. Result will be between zero and n. real integer.
// range_set    Deviation set to use. real integer.
// deviation   Amount of (even distribution) random flux for range data. real normal.
//
/// GMLscripts.com/license

var nn=argument[0];
var range_set=argument[1];
var devi=argument[2];
var Return=0;

//create distribution chart from distribution (raw data).
var ii, irandom_uneven_range_chart;
ii=0;
irandom_uneven_range_chart=ds_grid_create(1,nn+1)
var var_pr=0; // pr = previous range
repeat nn+1 {
 ds_grid_set(irandom_uneven_range_chart,0,ii,var_pr+ds_grid_get(irandom_uneven_range_grid,range_set,ii))
 var_pr=ds_grid_get(irandom_uneven_range_chart,0,ii)
 ii+=1;
}

//pick a number from the distribution chart
var chosen_spot=random(ds_grid_get(irandom_uneven_range_chart,0,ii-1)-0.00001);
ii=0;
var done=0;
while done=0 {
 if chosen_spot>=ds_grid_get(irandom_uneven_range_chart,0,ii) ii+=1;
 else {
  Return=ii;
  done=1;
 }
 if ii>nn { // happens when all distribution ranges are zero.
  Return=random(nn);
  done=1;
 }
}

//deviate distribution
if devi>0 {
 ii=0;
 repeat irandom_uneven_n_max {
  ds_grid_set(irandom_uneven_range_grid,range_set,ii,median(ds_grid_get(irandom_uneven_range_grid,range_set,ii)+((-0.5*devi)+random(devi)),0.00001,0.99999));
  ii+=1
 }
}

//finalize
ds_grid_destroy(irandom_uneven_range_chart);
return Return;

#18 Re: Off Topic » Is there spam in script submission? » 2018-10-19 18:45:55

Oh, I didn't know that there was a report button since I usually surf as a guest. tongue Sorry, my bad. laugh thank you.

#19 Off Topic » Is there spam in script submission? » 2018-10-18 04:34:13

lostdarkwolf
Replies: 2

Whats going on with the script submission section? Are some of the more recent topics that are currently in script submission spam? Why is there posts about stuff like shoes? I've tried not to reply to it thinking that it is spam, but I would've thought that the older ones would've been deleted by now. Why have they not? I would love to see more moderation, or even be willing to help moderate myself if the help is really needed. Of course I may just be being impatient, or maybe even misunderstanding the situation. Just trying to figure this out.

#20 Script Submission » generate a random cave for a ds_grid (Update 9/2/20) » 2018-07-09 05:35:22

lostdarkwolf
Replies: 6

See the updated script in the post below this one.
- - -
No required scripts. Enjoy!
EDIT: The last script worked okay, but it took me a while to realize that the generation didn't exactly do what I wanted it to do. This script has been corrected in that regard, and is more flexible.

Expand/// generate_cave(ds_grid, path_repetitions*, expansion_repetitions*, smooth_repetitions*, safe_smoothing*, expansion_size_limit*, tries*, boundary_wall_width*)
// * argument not required
// 
// Turns a ds_grid into a simple boolean cave.
// The bigger the ds_grid is, the better the cave will look.
// 
// ds_grid                 The ds_grid to turn into a data-based cave. real
// path_repetitions        Approximate length of the initial cave path. real
// expansion_repetitions   How many times to randomly expand the cave. real
// smooth_repetitions      How many times to smooth the cave. If this value is -1, the script will then smooth until smoothing becomes redundant. real
// safe_smoothing          If true, smoothing will not create solids, just remove them. real
// expansion_size_limit    upper limit for the expansion radius, the lower limit is 1. real
// tries                   amount of tries for expansion. real
// boundry_wall_width      width of the solid edge around the ds_grid. Use zero for none. real
// 
// Generation avoids using noise.
// If safe_smoothing is on, every open point within the cave should be accessible.
// Expansion does not respect the ds_grid boundaries. Use it respectfully.
// Cell values are either one for solid, or zero for open.
//
/// GMLscripts.com/license

MAV_grid=argument[0];
var var_width; var_width=ds_grid_width(MAV_grid)-1;
var var_height; var_height=ds_grid_height(MAV_grid)-1;

var MAV_repetitions1; MAV_repetitions1=sqr(mean(var_width,var_height));
if argument_count>1 MAV_repetitions1=argument[1];
var MAV_repetitions2; MAV_repetitions2=mean(var_width,var_height);
if argument_count>2 MAV_repetitions2=argument[2];
var MAV_repetitions3; MAV_repetitions3=-1;
if argument_count>3 MAV_repetitions3=argument[3];
var var_safe_smoothing; var_safe_smoothing=1;
if argument_count>4 var_safe_smoothing=argument[4];
var MAV_expansion_size; MAV_expansion_size=floor(mean(var_width,var_height)/50);
if argument_count>5 MAV_expansion_size=argument[5];
var MAV_tries; MAV_tries=max(mean(var_width,var_height),100);
if argument_count>6 MAV_tries=argument[6];
var MAV_renew_boundary_wall_width; MAV_renew_boundary_wall_width=1;
if argument_count>7 MAV_renew_boundary_wall_width=argument[7];

//make the initial cave path
ds_grid_set_region(MAV_grid,0,0,var_width,var_height,1)
var iix, iiy, var_dir;
iix=floor(var_width/2);
iiy=floor(var_height/2);
repeat MAV_repetitions1 {
 ds_grid_set(MAV_grid,iix,iiy,0)
 var_dir=irandom(4);
 switch var_dir {
  case 0:
   iix+=1;
  break;
  case 1:
   iiy-=1;
  break;
  case 2:
   iix-=1;
  break;
  case 3:
   iiy+=1;
  break;
 }
 if iix<var_width*0.1 and irandom(var_width*0.1)=0 iix+=1;
 if iix>var_width*0.9 and irandom(var_width*0.1)=0 iix-=1;
 if iiy<var_height*0.1 and irandom(var_width*0.1)=0 iiy+=1;
 if iiy>var_height*0.9 and irandom(var_width*0.1)=0 iiy-=1;
 iix=median(round(iix),1,var_width-1);
 iiy=median(round(iiy),1,var_height-1);
}

// randomly expand the cave path
var iix, iiy, var_buddy_count;
if MAV_repetitions2>0 {
 var var_source_value, var_expand_way;
 var var_tries; var_tries=0;
 var var_runs_done; var_runs_done=0;
 var var_done; var_done=0;
 while var_done=0 {
  iix=median(2,irandom(var_width),var_width-2);
  iiy=median(2,irandom(var_height),var_width-2);
  var_source_value=ds_grid_get(MAV_grid,iix,iiy)
  if iix=2 or iix=var_width-2 var_source_value=1
  if iiy=2 or iiy=var_height-2 var_source_value=1
  var_buddy_count=ds_grid_get(MAV_grid,iix+1,iiy)+ds_grid_get(MAV_grid,iix,iiy-1)+ds_grid_get(MAV_grid,iix-1,iiy)+ds_grid_get(MAV_grid,iix,iiy+1)
  if not (var_source_value=1 and var_buddy_count=4 or var_source_value=0 and var_buddy_count=0) and var_source_value=0 {
   var_runs_done+=1;
   repeat irandom(2) {
    var_expand_way=irandom(3);
    var_expansion_size=1+random(MAV_expansion_size-1);
    if var_expand_way=0 ds_grid_set_disk(MAV_grid,iix+(var_expansion_size*0.75),iiy,var_expansion_size,var_source_value);
    if var_expand_way=1 ds_grid_set_disk(MAV_grid,iix,iiy-(var_expansion_size*0.75),var_expansion_size,var_source_value);
    if var_expand_way=2 ds_grid_set_disk(MAV_grid,iix-(var_expansion_size*0.75),iiy,var_expansion_size,var_source_value);
    if var_expand_way=3 ds_grid_set_disk(MAV_grid,iix,iiy+(var_expansion_size*0.75),var_expansion_size,var_source_value);
   }
  }
  else {
   var_tries+=1;
  }
  if var_tries>=MAV_tries var_done=1;
  if var_runs_done>=MAV_repetitions2 var_done=1;
 }
}

//smooth the cave
repeat max(MAV_repetitions3,0) {
 for (iix=var_width-1; iix>=1; iix-=1;) {
  for (iiy=var_height-1; iiy>=1; iiy-=1;) {
   var_buddy_count = ds_grid_get(MAV_grid,iix+1,iiy)+
                     ds_grid_get(MAV_grid,iix,iiy-1)+
                     ds_grid_get(MAV_grid,iix-1,iiy)+
                     ds_grid_get(MAV_grid,iix,iiy+1)+
                     ds_grid_get(MAV_grid,iix+1,iiy-1)+
                     ds_grid_get(MAV_grid,iix-1,iiy-1)+
                     ds_grid_get(MAV_grid,iix-1,iiy+1)+
                     ds_grid_get(MAV_grid,iix+1,iiy+1);
   if var_buddy_count<4 ds_grid_set(MAV_grid,iix,iiy,0)
   if var_buddy_count>5 and var_safe_smoothing=false ds_grid_set(MAV_grid,iix,iiy,1)
  }
 }
}
if MAV_repetitions3=-1 {
 var var_orig_val;
 var var_changes;
 var_changes=1;
 while var_changes>0 {
  var_changes=0;
  for (iix=var_width-1; iix>=1; iix-=1;) {
   for (iiy=var_height-1; iiy>=1; iiy-=1;) {
    var_buddy_count = ds_grid_get(MAV_grid,iix+1,iiy)+
                      ds_grid_get(MAV_grid,iix,iiy-1)+
                      ds_grid_get(MAV_grid,iix-1,iiy)+
                      ds_grid_get(MAV_grid,iix,iiy+1)+
                      ds_grid_get(MAV_grid,iix+1,iiy-1)+
                      ds_grid_get(MAV_grid,iix-1,iiy-1)+
                      ds_grid_get(MAV_grid,iix-1,iiy+1)+
                      ds_grid_get(MAV_grid,iix+1,iiy+1);
    if var_buddy_count<4 {
     var_orig_val=ds_grid_get(MAV_grid,iix,iiy);
     if var_orig_val!=0 {
      ds_grid_set(MAV_grid,iix,iiy,0)
      var_changes+=1;
     }
    }
    if var_buddy_count>5 and var_safe_smoothing=false {
     var_orig_val=ds_grid_get(MAV_grid,iix,iiy);
     if var_orig_val!=1 {
      ds_grid_set(MAV_grid,iix,iiy,1)
      var_changes+=1;
     }
    }
   }
  }
 }
}

// renew boundary wall
if MAV_renew_boundary_wall_width>0 {
 var var_line_number; var_line_number=0;
 repeat MAV_renew_boundary_wall_width {
  for (iix=var_width-1-var_line_number; iix>=1+var_line_number; iix-=1;) {
   ds_grid_set(MAV_grid,iix,var_line_number,1)
   ds_grid_set(MAV_grid,iix,var_height-1-var_line_number,1)
  }
  for (iiy=var_height-1-var_line_number; iiy>=1+var_line_number; iiy-=1;) {
   ds_grid_set(MAV_grid,var_line_number,iiy,1)
   ds_grid_set(MAV_grid,var_width-1-var_line_number,iiy,1)
  }
  var_line_number+=1;
 }
 for (iix=var_width-1-var_line_number; iix>=1+var_line_number; iix-=1;) {
  if irandom(1) ds_grid_set(MAV_grid,iix,var_line_number,1)
  if irandom(1) ds_grid_set(MAV_grid,iix,var_height-1-var_line_number,1)
 }
 for (iiy=var_height-1-var_line_number; iiy>=1+var_line_number; iiy-=1;) {
  if irandom(1) ds_grid_set(MAV_grid,var_line_number,iiy,1)
  if irandom(1) ds_grid_set(MAV_grid,var_width-1-var_line_number,iiy,1)
 }
}

Board footer

Powered by FluxBB