GMLscripts.com

Discuss and collaborate on GML scripts
Invert

You are not logged in.

#1 Script Submission » SHA1 <-> buffer » 2021-06-12 18:03:18

Juju
Replies: 1

For GMS2.3.2

Expand/// Writes a SHA1 string to a buffer as individual bytes, from left to right in the SHA1 string
/// 
/// @return Nothing (undefined)
/// @param buffer      Buffer to write to
/// @param sha1String  SHA1 string to write

function buffer_write_sha1(_buffer, _sha1_string)
{
    var _i = 1;
    repeat(5)
    {
        //Did you know real() worked on hex?
        var _value = real("0x" + string_copy(_sha1_string, _i, 8));
        
        //Reverse bytes in the 32-bit word
        //We do this so that the bytes in the buffer correspond exactly to the bytes in the SHA1 string
        _value = (((_value & 0xFF) << 24) | ((_value & 0xFF00) << 8) | ((_value >> 8) & 0xFF00) | (_value >> 24));
        
        buffer_write(_buffer, buffer_u32, _value);
        
        _i += 8;
    }
}
Expand/// Reads a SHA1 hash from a buffer and converts it into a string
/// 
/// @return SHA1 string extracted from the buffer
/// @param buffer   Buffer to read from
/// @param [lower]  Optional. Whether to force the SHA1 string into lowercase for easier comparison with other SHA1 strings. Defaults to <true>

function buffer_read_sha1()
{
    var _buffer = argument[0];
    var _lower  = ((argument_count > 1) && (argument[1] != undefined))? argument[1] : true;
    
    var _sha1_string = "";
    
    repeat(5)
    {
        var _value = buffer_read(_buffer, buffer_u32);
        
        //Reverse bytes in the 32-bit word we pulled out
        _value = (((_value & 0xFF) << 24) | ((_value & 0xFF00) << 8) | ((_value >> 8) & 0xFF00) | (_value >> 24));
        
        //Checky hack with pointers to output a hex string
        _sha1_string += string(ptr(_value));
    }
    
    //If necessary, force lowercase abcdef
    if (_lower) _sha1_string = string_lower(_sha1_string);
    
    return _sha1_string;
}

#2 Script Submission » tag_get_instance_ids » 2020-04-30 09:29:57

Juju
Replies: 0

As a response to the request on the 2.3.0 Beta forum:

Expand/// @function tag_get_instance_ids(tags, includeChildren)
/// 
/// @param tags             Single asset tag string, or an array with various asset tags
/// @param includeChildren  Include children of objects with a tag
/// 
/// @return An array of all the instances that have the given tags assigned to their object asset

function tag_get_instance_ids(_tags, _include_children)
{
    var _array = [];
    var _count = 0;
    
    var _asset_ids = tag_get_asset_ids(_tags, asset_object);
    var _i = 0;
    repeat(array_length(_asset_ids))
    {
        var _object = _asset_ids[_i];
        with(_object)
        {
            //Skip children of tagged objects
            if ((object_index != _object) && !_include_children) continue;
            
            _array[_count] = id;
            ++_count;
        }
        
        ++_i;
    }
    
    return _array;
}

#3 Script Submission » Rounded triangles » 2019-06-05 16:32:53

Juju
Replies: 0

Scripts that I wrote to solve a problem on the GMC: https://forum.yoyogames.com/index.php?t … way.64010/

The former is fairly efficient, the latter can probably be improved.

Expand/// draw_triangle_rounded()
/// @jujuadams 2019/05/06
/// 
/// @param x1
/// @param y1
/// @param x2
/// @param y2
/// @param x3
/// @param y3
/// @param radius
/// @param outline

#macro __draw_triangle_rounded__max_angle_step  10

var _x1      = argument0;
var _y1      = argument1;
var _x2      = argument2;
var _y2      = argument3;
var _x3      = argument4;
var _y3      = argument5;
var _radius  = argument6;
var _outline = argument7;

//Find the vectors from point to point
var _delta_x21 = _x2 - _x1;
var _delta_y21 = _y2 - _y1;
var _delta_x32 = _x3 - _x2;
var _delta_y32 = _y3 - _y2;
var _delta_x13 = _x1 - _x3;
var _delta_y13 = _y1 - _y3;

//If the winding number is greater than zero then the vertices have been defined counterclockwise
//Swapping p2 and p3 to corrects this
if (_delta_y21*_delta_x32 - _delta_y32*_delta_x21 > 0)
{
    var _temp_x2 = _x2;
    var _temp_y2 = _y2;
    _x2 = _x3;
    _y2 = _y3;
    _x3 = _temp_x2;
    _y3 = _temp_y2;
    
    _delta_x21 = _x2 - _x1;
    _delta_y21 = _y2 - _y1;
    _delta_x32 = _x3 - _x2;
    _delta_y32 = _y3 - _y2;
    _delta_x13 = _x1 - _x3;
    _delta_y13 = _y1 - _y3;
}

//Force the length of each vector to be the radius of the circle
var _f21 = _radius / sqrt(_delta_x21*_delta_x21 + _delta_y21*_delta_y21);
var _f32 = _radius / sqrt(_delta_x32*_delta_x32 + _delta_y32*_delta_y32);
var _f13 = _radius / sqrt(_delta_x13*_delta_x13 + _delta_y13*_delta_y13);
_delta_x21 *= _f21;
_delta_y21 *= _f21;
_delta_x32 *= _f32;
_delta_y32 *= _f32;
_delta_x13 *= _f13;
_delta_y13 *= _f13;

//Find the angle of each vector
var _angle_21 = darctan2(_delta_x21, _delta_y21);
var _angle_32 = darctan2(_delta_x32, _delta_y32);
var _angle_13 = darctan2(_delta_x13, _delta_y13);

//Here's a crafty trick to get a perpendicular line
// x' = x + dy
// y' = y - dx
//We add the direction vectors to their parent points
//By joining A->B for each pair of points we get the outer boundary of the triangle
var _ax21 = _x1 + _delta_y21;
var _ay21 = _y1 - _delta_x21;
var _bx21 = _x2 + _delta_y21;
var _by21 = _y2 - _delta_x21;

var _ax32 = _x2 + _delta_y32;
var _ay32 = _y2 - _delta_x32;
var _bx32 = _x3 + _delta_y32;
var _by32 = _y3 - _delta_x32;

var _ax13 = _x3 + _delta_y13;
var _ay13 = _y3 - _delta_x13;
var _bx13 = _x1 + _delta_y13;
var _by13 = _y1 - _delta_x13;

//Set up the correct kind of primitive
if (_outline)
{
    draw_primitive_begin(pr_linestrip);
}
else
{
    draw_primitive_begin(pr_trianglefan);
    //The first vertex of a triangle fan is the centre of the fan
    //We can choose any point inside the rounded triangle, but we choose the centre for convenience
    draw_vertex((_x1 + _x2 + _x3)/3, (_y1 + _y2 + _y3)/3);
}

//Iterate around the corner of point 1 to create a rounded edge
var _incr  = angle_difference(_angle_21, _angle_13);
var _steps = ceil(abs(_incr) / __draw_triangle_rounded__max_angle_step);
_incr /= _steps;
var _angle = _angle_13;
repeat(_steps)
{
    draw_vertex(_x1 + lengthdir_x(_radius, _angle), _y1 + lengthdir_y(_radius, _angle));
    _angle += _incr;
}

//Add the edge for the point pair P1->P2
draw_vertex(_ax21, _ay21);
draw_vertex(_bx21, _by21);

//Iterate around the corner of point 2...
_incr  = angle_difference(_angle_32, _angle_21);
_steps = ceil(abs(_incr) / __draw_triangle_rounded__max_angle_step);
_incr /= _steps;
_angle = _angle_21;
repeat(_steps)
{
    draw_vertex(_x2 + lengthdir_x(_radius, _angle), _y2 + lengthdir_y(_radius, _angle));
    _angle += _incr;
}

//Add the edge for the point pair P2->P3
draw_vertex(_ax32, _ay32);
draw_vertex(_bx32, _by32);

//Iterate around the corner of point 3...
_incr  = angle_difference(_angle_13, _angle_32);
_steps = ceil(abs(_incr) / __draw_triangle_rounded__max_angle_step);
_incr /= _steps;
_angle = _angle_32;
repeat(_steps)
{
    draw_vertex(_x3 + lengthdir_x(_radius, _angle), _y3 + lengthdir_y(_radius, _angle));
    _angle += _incr;
}

//Add the edge for the point pair P3->P1 to finish the shape
draw_vertex(_ax13, _ay13);
draw_vertex(_bx13, _by13);

//End the primitive and draw it
draw_primitive_end();
Expand/// draw_triangle_rounded_inside()
/// @jujuadams 2019/05/06
/// 
/// @param x1
/// @param y1
/// @param x2
/// @param y2
/// @param x3
/// @param y3
/// @param radius
/// @param outline

#macro __draw_triangle_rounded_inside__max_angle_step  10

var _x1      = argument0;
var _y1      = argument1;
var _x2      = argument2;
var _y2      = argument3;
var _x3      = argument4;
var _y3      = argument5;
var _radius  = argument6;
var _outline = argument7;

//Find the vectors from point to point
var _delta_x21 = _x2 - _x1;
var _delta_y21 = _y2 - _y1;
var _delta_x32 = _x3 - _x2;
var _delta_y32 = _y3 - _y2;
var _delta_x13 = _x1 - _x3;
var _delta_y13 = _y1 - _y3;

//If the winding number is greater than zero then the vertices have been defined counterclockwise
//Swapping p2 and p3 to corrects this
if (_delta_y21*_delta_x32 - _delta_y32*_delta_x21 > 0)
{
    var _temp_x2 = _x2;
    var _temp_y2 = _y2;
    _x2 = _x3;
    _y2 = _y3;
    _x3 = _temp_x2;
    _y3 = _temp_y2;
    
    _delta_x21 = _x2 - _x1;
    _delta_y21 = _y2 - _y1;
    _delta_x32 = _x3 - _x2;
    _delta_y32 = _y3 - _y2;
    _delta_x13 = _x1 - _x3;
    _delta_y13 = _y1 - _y3;
}

//Force the length of each vector to be the radius of the circle
var _f21 = _radius / sqrt(_delta_x21*_delta_x21 + _delta_y21*_delta_y21);
var _f32 = _radius / sqrt(_delta_x32*_delta_x32 + _delta_y32*_delta_y32);
var _f13 = _radius / sqrt(_delta_x13*_delta_x13 + _delta_y13*_delta_y13);
_delta_x21 *= _f21;
_delta_y21 *= _f21;
_delta_x32 *= _f32;
_delta_y32 *= _f32;
_delta_x13 *= _f13;
_delta_y13 *= _f13;

//Find the angle of each vector
var _angle_21 = darctan2(-_delta_y21, _delta_x21);
var _angle_32 = darctan2(-_delta_y32, _delta_x32);
var _angle_13 = darctan2(-_delta_y13, _delta_x13);



var _angle = angle_difference(_angle_13, _angle_21)/2;

var _tan = dtan(_angle);
var _ax21 = _x1 + _tan*_delta_x21;
var _ay21 = _y1 + _tan*_delta_y21;
var _bx13 = _x1 - _tan*_delta_x13;
var _by13 = _y1 - _tan*_delta_y13;

var _dx = _delta_x21 - _delta_x13;
var _dy = _delta_y21 - _delta_y13;
var _d  = (_radius/dcos(_angle)) / sqrt(_dx*_dx + _dy*_dy);
_x1 += _d*_dx;
_y1 += _d*_dy;



var _angle = angle_difference(_angle_21, _angle_32)/2;

var _tan = dtan(_angle);
var _ax32 = _x2 + _tan*_delta_x32;
var _ay32 = _y2 + _tan*_delta_y32;
var _bx21 = _x2 - _tan*_delta_x21;
var _by21 = _y2 - _tan*_delta_y21;

var _dx = _delta_x32 - _delta_x21;
var _dy = _delta_y32 - _delta_y21;
var _d  = (_radius/dcos(_angle)) / sqrt(_dx*_dx + _dy*_dy);
_x2 += _d*_dx;
_y2 += _d*_dy;



var _angle = angle_difference(_angle_32, _angle_13)/2;

var _tan = dtan(_angle);
var _ax13 = _x3 + _tan*_delta_x13;
var _ay13 = _y3 + _tan*_delta_y13;
var _bx32 = _x3 - _tan*_delta_x32;
var _by32 = _y3 - _tan*_delta_y32;

var _dx = _delta_x13 - _delta_x32;
var _dy = _delta_y13 - _delta_y32;
var _d  = (_radius/dcos(_angle)) / sqrt(_dx*_dx + _dy*_dy);
_x3 += _d*_dx;
_y3 += _d*_dy;



//Set up the correct kind of primitive
if (_outline)
{
    draw_primitive_begin(pr_linestrip);
}
else
{
    draw_primitive_begin(pr_trianglefan);
    //The first vertex of a triangle fan is the centre of the fan
    //We can choose any point inside the rounded triangle, but we choose the centre for convenience
    draw_vertex((_x1 + _x2 + _x3)/3, (_y1 + _y2 + _y3)/3);
}

//Iterate around the corner of point 1 to create a rounded edge
var _angle = point_direction(_x1, _y1, _bx13, _by13);
var _incr  = angle_difference(point_direction(_x1, _y1, _ax21, _ay21), _angle);
var _steps = ceil(abs(_incr) / __draw_triangle_rounded_inside__max_angle_step);
_incr /= _steps;
repeat(_steps)
{
    draw_vertex(_x1 + lengthdir_x(_radius, _angle), _y1 + lengthdir_y(_radius, _angle));
    _angle += _incr;
}

//Add the edge for the point pair P1->P2
draw_vertex(_ax21, _ay21);
draw_vertex(_bx21, _by21);

//Iterate around the corner of point 2...
var _angle = point_direction(_x2, _y2, _bx21, _by21);
var _incr  = angle_difference(point_direction(_x2, _y2, _ax32, _ay32), _angle);
var _steps = ceil(abs(_incr) / __draw_triangle_rounded_inside__max_angle_step);
_incr /= _steps;
repeat(_steps)
{
    draw_vertex(_x2 + lengthdir_x(_radius, _angle), _y2 + lengthdir_y(_radius, _angle));
    _angle += _incr;
}

//Add the edge for the point pair P2->P3
draw_vertex(_ax32, _ay32);
draw_vertex(_bx32, _by32);

//Iterate around the corner of point 3...
var _angle = point_direction(_x3, _y3, _bx32, _by32);
var _incr  = angle_difference(point_direction(_x3, _y3, _ax13, _ay13), _angle);
var _steps = ceil(abs(_incr) / __draw_triangle_rounded_inside__max_angle_step);
_incr /= _steps;
repeat(_steps)
{
    draw_vertex(_x3 + lengthdir_x(_radius, _angle), _y3 + lengthdir_y(_radius, _angle));
    _angle += _incr;
}

//Add the edge for the point pair P3->P1 to finish the shape
draw_vertex(_ax13, _ay13);
draw_vertex(_bx13, _by13);

//End the primitive and draw it
draw_primitive_end();

#4 Re: Script Submission » GameMaker Studio 2 style guide discussion » 2018-11-16 06:14:24

I say move entirely to GMS2. It'd be great if it was possible to have a legacy GMS1 version of the site to cover users still on that version, but you've gotta make a clean break at some point.

#5 Script Submission » json_prettify » 2017-12-21 16:19:27

Juju
Replies: 1
Expand/// @param json_string
/// @param [indent_size]
/// @param [newline_char]
/// @param [map_spacing]
//
// 2017/12/08 @jujuadams
// If you use this, shoot me a tweet!
//
// With thanks to yal.cc

if ( argument_count < 1 ) || ( argument_count > 4 ) {
    trace_error( false, "Unsupported number of arguments (", argument_count, ")" );
    return undefined;
} else {
    var _json_string = argument[0];
    var _indent_size  = ((argument_count>1) && (argument[1]!=undefined))? argument[1] : 4;
    var _newline_char = ((argument_count>2) && (argument[2]!=undefined))? argument[2] : chr(13) + chr(10);
    var _map_space    = ((argument_count>3) && (argument[3]!=undefined))? argument[3] : 1;
}

var _in_string        = false;
var _string_escape    = false;
var _string_delimiter = undefined;

var _in_number_string = false;
var _number_string = "";
var _number_string_dot = 0;
var _number_string_last_sig = 0;

var _output = "";
var _indent = 0;

var _index = 0;
repeat( string_length( _json_string ) ) {
    _index++;
    
    var _char = string_char_at( _json_string, _index );
    var _ord = ord( _char );
    var _do_main = true;
	
    if ( _in_string ) {
        
        if ( ( _ord == _string_delimiter ) && !_string_escape ) {
            _in_string = false;
        } else if ( _ord == 92 ) && ( !_string_escape ) {
            _string_escape = true;
        } else {
            _string_escape = false;
        }
        
        _output += _char;
        _do_main = false;
        
    } else if ( _in_number_string ) {
        
        if ( _ord < 45 ) || ( _ord == 47 ) || ( _ord > 57 ) {
			
            _in_number_string = false;
            if ( _number_string_dot >= _number_string_last_sig ) _number_string_last_sig = _number_string_dot-1;
            _output += string_copy( _number_string, 1, _number_string_last_sig );
			
        } else {
            
			_do_main = false;
            _number_string += _char;
            
            if ( _ord == 46 ) {
                _number_string_dot = string_length( _number_string );
                _number_string_last_sig = _number_string_dot;
            } else if ( _number_string_dot == 0 ) || ( _ord != 48 ) {
				_number_string_last_sig++;
			}
            
        }
        
    }
    
    if ( _do_main ) switch( _ord ) {
        
        case 58: // :
            if ( _map_space ) {
                _output += ":";
                repeat( _map_space ) _output += " ";
            } else {
                _output += ":";
            }
        break;
        
        case 34: // "
        case 39: // '
            _string_delimiter = _ord;
            _in_string = true;
            _output += _char;
        break;
        
        case 44: // ,
            _output += _char;
            _output += _newline_char;
            repeat( _indent ) _output += " ";
        break;
        
        case  91: // [
        case 123: // {
            _output += _char;
            _indent += _indent_size;
            _output += _newline_char;
            repeat( _indent ) _output += " ";
        break;
        
        case  93: // ]
        case 125: // }
            _indent -= _indent_size;
            _output += _newline_char;
            repeat( _indent ) _output += " ";
            _output += _char;
        break;
        
        case  9: //remove whitespace
        case 10:
        case 11:
        case 12:
        case 13:
        case 32:
        break;
        
        case  45: // -
        case  46: // .
        case  48: // 0
        case  49: // 1
        case  50: // 2
        case  51: // 3
        case  52: // 4
        case  53: // 5
        case  54: // 6
        case  55: // 7
        case  56: // 8
        case  57: // 9
            _number_string = _char;
            _in_number_string = true;
            _number_string_dot = 0;
            _number_string_last_sig = 1;
        break;
        
        default:
            _output += _char;
        break;
        
    }
    
}

return _output;

#7 Re: Script Submission » csv_to_grid » 2017-05-11 05:30:37

Old GMS1 version.

Expand///csv_to_grid( file, [cell delimiter], [text delimiter] )
//  
//  Loads a .csv file into a ds_grid that is automatically sized to the data.
//  Useful for translations, dialogue trees, mods etc.
//  
//  January 2016
//  @jujuadams

//Collect the filename. You don't strictly have to do this but it's good practice for larger scripts in case you need to change things around

switch( argument_count ) {
    
    case 1:
        var file = argument[0];
        var cellDelimiter = "";
        var textDelimiter = "";
    break;
    
    case 2:
        var file = argument[0];
        var cellDelimiter = argument[1];
        var textDelimiter = "";
    break;
    
    case 3:
        var file = argument[0];
        var cellDelimiter = argument[1];
        var textDelimiter = argument[2];
    break;
    
    default:
        cout( "scr_juju_csv_load_to_grid: Error! Unsupported number of arguments" );
        return noone;
    break;
    
}

if ( cellDelimiter == "" ) cellDelimiter = ",";
if ( textDelimiter == "" ) textDelimiter = chr(34);


var cellDelimiterOrd = ord( cellDelimiter );
var textDelimiterOrd = ord( textDelimiter );

//
var buffer = buffer_create( 1, buffer_grow, 1 );
buffer_load_ext( buffer, file, 0 );
buffer_seek( buffer, buffer_seek_start, 0 );

//Initialise width and height of the spreadsheet
var sheetWidth = 0;
var sheetHeight = 1;

var prevVal = 0;
var nextVal = 0;
var val = 0;
var str = "";
var inText = false;
var grid = noone;

var size = buffer_get_size( buffer );
for( var i = 0; i < size; i++ ) {
    
    prevVal = val;
    var val = buffer_read( buffer, buffer_u8 );
    
    if ( val == 13 ) continue;
    
    if ( val == textDelimiterOrd ) {
        
        var nextVal = buffer_peek( buffer, buffer_tell( buffer ), buffer_u8 );
        
        if ( inText ) {
            if ( nextVal == textDelimiterOrd ) continue;
            if ( prevVal == textDelimiterOrd ) {
                str += textDelimiter;
                continue;
            }
        }
        
        inText = !inText;
        continue;
        
    }
    
    if ( inText ) and ( ( prevVal == 13 ) and ( val == 10 ) ) {   
        str += "#";
        continue;
    }
    
    if ( ( val == cellDelimiterOrd ) or ( ( prevVal == 13 ) and ( val == 10 ) ) ) and ( !inText ) {
        
        sheetWidth++;
        if ( grid == noone ) {
            grid = ds_grid_create( max( 1, sheetWidth ), max( 1, sheetHeight ) );
            ds_grid_clear( grid, "" );
        } else ds_grid_resize( grid, max( sheetWidth, ds_grid_width( grid ) ), sheetHeight );
        
        ds_grid_set( grid, sheetWidth - 1, sheetHeight - 1, str );
        str = "";
        inText = false;
        
        if ( val == 10 ) {
            sheetWidth = 0;
            sheetHeight++;
        }
        
        continue;
    }
    
    str += chr( val );
    
}

buffer_delete( buffer );

sheetWidth = ds_grid_width( grid );
sheetHeight = ds_grid_height( grid );
for( var yy = 0; yy < sheetHeight; yy++ ) {
    for( var xx = 0; xx < sheetWidth; xx++ ) {
        var val = ds_grid_get( grid, xx, yy );
        if ( !is_string( val ) ) ds_grid_set( grid, xx, yy, "" );
    }
}

//Return the grid, ready for use elsewhere
return grid;

#8 Re: Script Submission » Delaunay Triangulation using Bowyer-Watson algorithm » 2017-03-12 06:39:12

I independently solved this a while back and used it for Glitch Witches to triangulate sprites in preparation for Worms-style deformation, spitting out triangle meshes and the Voronoi dual. Source code is available for both but err caveat emptor: your solution looks more efficient to me!

Constrained Delaunay eluded me though... that's obviously the Next Step for a number of things that are, as yet, unsolved in GM (not least navmeshes).

#9 Re: Script Submission » csv_to_grid » 2017-03-05 11:02:07

The "inverse".

Expand///grid_to_csv_string( grid, [field delimiter], [string delimiter], [newline] )
//  
//  arg0   ds_grid   The grid holding the data to be formatted/encoded to CSV
//  arg1   string    The field delimiter used to separate cells (cannot be a decimal point). Defaults to a comma
//  arg2   string    The string delimiter used to define strings in the CSV file. Defaults to a double-quote
//  arg3   string    The newline character(s) used to defined a new row. Defaults to the Windows standard (0D,0A)
//  
//  returns: A string containing a formatted CSV file, including line breaks
//  
//  (c) Juju Adams 5th March 2017 - All Rights Reserved
//  @jujuadams

var _grid             = undefined;
var _field_delimiter  = chr(44); //comma
var _string_delimiter = chr(34); //double-quote
var _newline          = chr(13) + chr(10); //default Windows (Notepad) newline

if ( argument_count <= 0 ) and ( argument_count > 4 ) {
    show_error( "Incorrect number of arguments (" + string( argument_count ) + ")", false );
    return "";
}

if ( argument_count >= 1 ) var _grid             = argument[0];
if ( argument_count >= 2 ) var _field_delimiter  = argument[1];
if ( argument_count >= 3 ) var _string_delimiter = argument[2];
if ( argument_count >= 4 ) var _newline          = argument[3];

if ( _field_delimiter == chr(46) ) { //decimal point
    show_error( "Field delimiter cannot be a decimal point!", false );
    return "";
}

var _str = "";
var _width  = ds_grid_width( _grid );
var _height = ds_grid_height( _grid );

for( var _y = 0; _y < _height; _y++ ) {
    for( var _x = 0; _x < _width; _x++ ) {
        
        var _value = _grid[# _x, _y ];
        if ( is_real( _value ) ) {
            _str += string( _value );
        } else if ( is_string( _value ) ) {
            if ( _value != "" ) { //Catch empty strings
                _str += _string_delimiter + string_replace_all( _value, _string_delimiter, _string_delimiter+_string_delimiter ) + _string_delimiter;
            }
        } else {
            show_debug_message( "ds_grid_to_csv: CAUTION - grid " + string( _grid ) + " value at " + string( _x ) + "," + string( _y ) + " is not a valid datatype!" );
        }
        
        if ( _x < _width-1 ) _str += _field_delimiter;
        
    }
    
    _str += _newline;
    
}

return _str;

#10 Re: Script Submission » csv_to_grid » 2017-03-05 11:01:33

GMS1

Expand///csv_to_grid( file, force strings, cell delimiter, string delimiter, mac newline )
//  
//  CAUTION: Please ensure your files are in UTF-8 encoding.
//  
//  You may pass <undefined> to use the default value for optional arguments.
//  arg0   string   Filename for the source UTF-8 CSV file
//  arg1   bool     Whether to force all cells to be a string. Defaults to false
//  arg2   string   The delimiter used to separate cells. Defaults to a comma
//  arg3   string   The delimiter used to define strings in the CSV file. Defaults to a double-quote
//  arg4   bool     Newline compatibility mode for Mac (0A). Defaults to Windows standard newline (0D,0A)
//  
//  (c) Juju Adams 26th May 2017
//  @jujuadams

//Handle arguments
if ( argument_count < 1 ) or ( argument_count > 5 ) {
	show_error( "Incorrect number of arguments (" + string( argument_count ) + ")", false );
	return undefined;
}

var _filename         = argument[0];
var _force_strings    = false;
var _cell_delimiter   = chr(44); //comma
var _string_delimiter = chr(34); //double-quote
var _newline_alt      = false;

if ( argument_count >= 2 ) and ( !is_undefined( argument[1] ) ) _force_strings    = argument[1];
if ( argument_count >= 3 ) and ( !is_undefined( argument[2] ) ) _cell_delimiter   = argument[2];
if ( argument_count >= 4 ) and ( !is_undefined( argument[3] ) ) _string_delimiter = argument[3];
if ( argument_count >= 5 ) and ( !is_undefined( argument[4] ) ) _newline_alt      = argument[4];

//Check for silliness...
if ( string_length( _cell_delimiter ) != 1 ) or ( string_length( _string_delimiter ) != 1 ) {
	show_error( "Delimiters must be one character", false );
	return undefined;
}

//More variables...
var _cell_delimiter_ord  = ord( _cell_delimiter  );
var _string_delimiter_ord = ord( _string_delimiter );

var _sheet_width  = 0;
var _sheet_height = 1;
var _max_width    = 0;

var _prev_val   = 0;
var _val        = 0;
var _str        = "";
var _in_string  = false;
var _is_decimal = !_force_strings;
var _grid       = ds_grid_create( 1, 1 ); _grid[# 0, 0 ] = "";

//Load CSV file as a buffer
var _buffer = buffer_load( _filename );
var _size = buffer_get_size( _buffer );
buffer_seek( _buffer, buffer_seek_start, 0 );

//Handle byte order marks from some UTF-8 encoders (EF BB BF at the start of the file)
var _bom_a = buffer_read( _buffer, buffer_u8 );
var _bom_b = buffer_read( _buffer, buffer_u8 );
var _bom_c = buffer_read( _buffer, buffer_u8 );
if !( ( _bom_a == 239 ) and ( _bom_b == 187 ) and ( _bom_c == 191 ) ) {
	show_debug_message( "CAUTION: csv_to_grid: " + _filename + ": CSV file might not be UTF-8 encoded (no BOM)" );
	buffer_seek( _buffer, buffer_seek_start, 0 );
} else {
	_size -= 3;
}

//Iterate over the buffer
for( var _i = 0; _i < _size; _i++ ) {

	_prev_val = _val;
	var _val = buffer_read( _buffer, buffer_u8 );

	//Handle UTF-8 encoding
	if ( ( _val & 224 ) == 192 ) { //two-byte

		_val  = (                              _val & 31 ) <<  6;
		_val += ( buffer_read( _buffer, buffer_u8 ) & 63 );
		_i++;

	} else if ( ( _val & 240 ) == 224 ) { //three-byte

		_val  = (                              _val & 15 ) << 12;
		_val += ( buffer_read( _buffer, buffer_u8 ) & 63 ) <<  6;
		_val +=   buffer_read( _buffer, buffer_u8 ) & 63;
		_i += 2;

	} else if ( ( _val & 248 ) == 240 ) { //four-byte

		_val  = (                              _val &  7 ) << 18;
		_val += ( buffer_read( _buffer, buffer_u8 ) & 63 ) << 12;
		_val += ( buffer_read( _buffer, buffer_u8 ) & 63 ) <<  6;
		_val +=   buffer_read( _buffer, buffer_u8 ) & 63;
		_i += 3;

	}

	//If we've found a string delimiter
	if ( _val == _string_delimiter_ord ) {

		//This definitely isn't a decimal number!
		_is_decimal = false;

		//If we're in a string...
		if ( _in_string ) {

			//If the next character is a string delimiter itself, skip this character
			if ( buffer_peek( _buffer, buffer_tell( _buffer ), buffer_u8 ) == _string_delimiter_ord ) continue;

			//If the previous character is a string delimiter itself, add the string delimiter to the working string
			if ( _prev_val == _string_delimiter_ord ) {
			    _str += _string_delimiter;
			    continue;
			}

		}

		//Toggle "we're in a string" behaviour
		_in_string = !_in_string;
		continue;

	}
    
	if ( _newline_alt ) {
		var _newline = ( _val == 10 );
	} else {
		var _newline = ( _prev_val == 13 ) and ( _val == 10 );
        
		//If we've found a newline and we're in a string, skip over the chr(10) character
		if ( _in_string ) and ( _newline ) continue;
	}

	//If we've found a new cell
	if ( ( _val == _cell_delimiter_ord ) or ( _newline ) ) and ( !_in_string ) {

		_sheet_width++;

		//If this cell is now longer than the maximum width of the grid, expand the grid
		if ( _sheet_width > _max_width ) {

			_max_width = _sheet_width;
			ds_grid_resize( _grid, _max_width, _sheet_height );

			//Clear cells vertically above to overwrite the default 0-value
			if ( _sheet_height >= 2 ) ds_grid_set_region( _grid, _max_width-1, 0, _max_width-1, _sheet_height-2, "" );

		}

		//Write the working string to a grid cell
		if ( _is_decimal ) _str = real( _str );
		_grid[# _sheet_width-1, _sheet_height-1 ] = _str;

		_str = "";
		_in_string = false;
		_is_decimal = !_force_strings;

		//A newline outside of a string triggers a new line... unsurprisingly
		if ( _newline ) {

			//Clear cells horizontally to overwrite the default 0-value
			if ( _sheet_width < _max_width ) ds_grid_set_region( _grid, _sheet_width, _sheet_height-1, _max_width-1, _sheet_height-1, "" );

			_sheet_width = 0;
			_sheet_height++;
			ds_grid_resize( _grid, _max_width, _sheet_height );
		}

		continue;

	}

	//Check if we've read a "#" character
	if ( _prev_val == 35 ) {
		_str += chr(13);
		continue;
	}
    
	//No newlines should appear outside of a string delimited cell
	if ( ( _val == 10 ) or ( _val == 13 ) ) and ( !_in_string ) continue;
    
	//Check if this character is outside valid decimal character range
	if ( _val != 45 ) and ( _val != 46 ) and ( ( _val < 48 ) or ( _val > 57 ) ) _is_decimal = false;

	//Finally add this character to the working string!
	_str += chr( _val );

}

//Catch hanging work string on end-of-file
if ( _str != "" ) {
	
	_sheet_width++;
	
	if ( _sheet_width > _max_width ) {
		_max_width = _sheet_width;
		ds_grid_resize( _grid, _max_width, _sheet_height );
		if ( _sheet_height >= 2 ) ds_grid_set_region( _grid, _max_width-1, 0, _max_width-1, _sheet_height-2, "" );
	}
	
	if ( _is_decimal ) _str = real( _str );
	_grid[# _sheet_width-1, _sheet_height-1 ] = _str;
	
}

//If the last character was a newline then we'll have an erroneous extra row at the bottom
if ( _newline ) ds_grid_resize( _grid, _max_width, _sheet_height-1 );

buffer_delete( _buffer );
return _grid;

#11 Re: Script Submission » slerp_3d » 2016-10-27 15:26:13

For bonus points, here's how you do it with quaternions

Expand///quaternion_lerp( quaternion1, quaternion2, t )
//
//  Returns the spherical linear interpolation of two quaternions.
//
//      quaternion1    quaternion, 4-element array
//      quaternion2    quaternion, 4-element array
//      t              paramter, float, 0 <= t <= 1
//
//  Made by @jujuadams.
//
/// GMLscripts.com/license

var _a = argument0;
var _b = argument1;
var _t = argument2;

var _angle = abs( arccos( _a[0]*_b[0] + _a[1]*_b[1] + _a[2]*_b[2] + _a[3]*_b[3] ) );

var _s0 = sin( _angle );

var _result;
if ( abs( _s0 ) < 0.001 ) {
    
    _result[0] = 0.5*_a[0] + 0.5*_b[0];
    _result[1] = 0.5*_a[1] + 0.5*_b[1];
    _result[2] = 0.5*_a[2] + 0.5*_b[2];
    _result[3] = 0.5*_a[3] + 0.5*_b[3];
    
} else {
    
    var _s1 = sin( ( 1 - _t ) * _angle ) / _s0;
    var _s2 = sin( _t * _angle ) / _s0;
    
    _result[0] = _s1*_a[0] + _s2*_b[0];
    _result[1] = _s1*_a[1] + _s2*_b[1];
    _result[2] = _s1*_a[2] + _s2*_b[2];
    _result[3] = _s1*_a[3] + _s2*_b[3];
    
}

return _result;

#12 Script Submission » slerp_3d » 2016-10-27 15:04:59

Juju
Replies: 2

Performs a linear interpolation between two points across the surface of a sphere.

Expand///slerp_3d( vector1, vector2, t )
//
//  Returns the spherical linear interpolation of two 3D vectors.
//
//      vector1    3D vector, 3-element array
//      vector2    3D vector, 3-element array
//      t          paramter, float, 0 <= t <= 1
//
//  Made by @jujuadams.
//
/// GMLscripts.com/license

var _a = argument0;
var _b = argument1;
var _t = argument2;

var _angle = arccos( dot_product_3d_normalised( _a[0], _a[1], _a[2],   _b[0], _b[1], _b[2] ) );

var _s0 = sin( _angle );

var _result;
if ( abs( _s0 ) < 0.001 ) {
    
    _result[0] = 0.5*_a[0] + 0.5*_b[0];
    _result[1] = 0.5*_a[1] + 0.5*_b[1];
    _result[2] = 0.5*_a[2] + 0.5*_b[2];
    
} else {
    
    var _s1 = sin( ( 1 - _t ) * _angle ) / _s0;
    var _s2 = sin( _t * _angle ) / _s0;
    
    _result[0] = _s1*_a[0] + _s2*_b[0];
    _result[1] = _s1*_a[1] + _s2*_b[1];
    _result[2] = _s1*_a[2] + _s2*_b[2];

}

return _result;

#13 Script Submission » matrix_inverse » 2016-10-27 14:49:17

Juju
Replies: 0

It's strange GM provides no native function for this...
Note that this function can return undefined for matrices that cannot be inverted so be a bit careful about using it without checking the output.

Expand///matrix_invert( matrix )
//
//  Returns the mathematical inverse of a matrix.
//  If the matrix is non-invertible, this script returns undefined.
//
//      matrix    4x4 matrix, 16-element array
//
//  Made by @jujuadams. With thanks to Russell Kay.
//
/// GMLscripts.com/license



var _mtx = argument0;
var _inv;                

_inv[ 0] = _mtx[ 5] * _mtx[10] * _mtx[15] - 
           _mtx[ 5] * _mtx[11] * _mtx[14] - 
           _mtx[ 9] * _mtx[ 6] * _mtx[15] + 
           _mtx[ 9] * _mtx[ 7] * _mtx[14] +
           _mtx[13] * _mtx[ 6] * _mtx[11] - 
           _mtx[13] * _mtx[ 7] * _mtx[10];

_inv[ 4] = -_mtx[ 4] * _mtx[10] * _mtx[15] + 
            _mtx[ 4] * _mtx[11] * _mtx[14] + 
            _mtx[ 8] * _mtx[ 6] * _mtx[15] - 
            _mtx[ 8] * _mtx[ 7] * _mtx[14] - 
            _mtx[12] * _mtx[ 6] * _mtx[11] + 
            _mtx[12] * _mtx[ 7] * _mtx[10];

_inv[ 8] = _mtx[ 4] * _mtx[ 9] * _mtx[15] - 
           _mtx[ 4] * _mtx[11] * _mtx[13] - 
           _mtx[ 8] * _mtx[ 5] * _mtx[15] + 
           _mtx[ 8] * _mtx[ 7] * _mtx[13] + 
           _mtx[12] * _mtx[ 5] * _mtx[11] - 
           _mtx[12] * _mtx[ 7] * _mtx[ 9];

_inv[12] = -_mtx[ 4] * _mtx[ 9] * _mtx[14] + 
            _mtx[ 4] * _mtx[10] * _mtx[13] +
            _mtx[ 8] * _mtx[ 5] * _mtx[14] - 
            _mtx[ 8] * _mtx[ 6] * _mtx[13] - 
            _mtx[12] * _mtx[ 5] * _mtx[10] + 
            _mtx[12] * _mtx[ 6] * _mtx[ 9];

var _det = _mtx[0] * _inv[0] + _mtx[1] * _inv[4]
         + _mtx[2] * _inv[8] + _mtx[3] * _inv[12];
if ( _det == 0 ) return undefined;

_inv[ 1] = -_mtx[ 1] * _mtx[10] * _mtx[15] + 
            _mtx[ 1] * _mtx[11] * _mtx[14] + 
            _mtx[ 9] * _mtx[ 2] * _mtx[15] - 
            _mtx[ 9] * _mtx[ 3] * _mtx[14] - 
            _mtx[13] * _mtx[ 2] * _mtx[11] + 
            _mtx[13] * _mtx[ 3] * _mtx[10];

_inv[ 5] = _mtx[ 0] * _mtx[10] * _mtx[15] - 
           _mtx[ 0] * _mtx[11] * _mtx[14] - 
           _mtx[ 8] * _mtx[ 2] * _mtx[15] + 
           _mtx[ 8] * _mtx[ 3] * _mtx[14] + 
           _mtx[12] * _mtx[ 2] * _mtx[11] - 
           _mtx[12] * _mtx[ 3] * _mtx[10];

_inv[ 9] = -_mtx[ 0] * _mtx[ 9] * _mtx[15] + 
            _mtx[ 0] * _mtx[11] * _mtx[13] + 
            _mtx[ 8] * _mtx[ 1] * _mtx[15] - 
            _mtx[ 8] * _mtx[ 3] * _mtx[13] - 
            _mtx[12] * _mtx[ 1] * _mtx[11] + 
            _mtx[12] * _mtx[ 3] * _mtx[ 9];

_inv[13] = _mtx[ 0]  * _mtx[ 9] * _mtx[14] - 
           _mtx[ 0]  * _mtx[10] * _mtx[13] - 
           _mtx[ 8]  * _mtx[ 1] * _mtx[14] + 
           _mtx[ 8]  * _mtx[ 2] * _mtx[13] + 
           _mtx[12] * _mtx[ 1] * _mtx[10] - 
           _mtx[12] * _mtx[ 2] * _mtx[ 9];

_inv[ 2] = _mtx[ 1] * _mtx[ 6] * _mtx[15] - 
           _mtx[ 1] * _mtx[ 7] * _mtx[14] - 
           _mtx[ 5] * _mtx[ 2] * _mtx[15] + 
           _mtx[ 5] * _mtx[ 3] * _mtx[14] + 
           _mtx[13] * _mtx[ 2] * _mtx[ 7] - 
           _mtx[13] * _mtx[ 3] * _mtx[ 6];

_inv[ 6] = -_mtx[ 0] * _mtx[ 6] * _mtx[15] + 
            _mtx[ 0] * _mtx[ 7] * _mtx[14] + 
            _mtx[ 4] * _mtx[ 2] * _mtx[15] - 
            _mtx[ 4] * _mtx[ 3] * _mtx[14] - 
            _mtx[12] * _mtx[ 2] * _mtx[ 7] + 
            _mtx[12] * _mtx[ 3] * _mtx[ 6];

_inv[10] = _mtx[ 0] * _mtx[ 5] * _mtx[15] - 
           _mtx[ 0] * _mtx[ 7] * _mtx[13] - 
           _mtx[ 4] * _mtx[ 1] * _mtx[15] + 
           _mtx[ 4] * _mtx[ 3] * _mtx[13] + 
           _mtx[12] * _mtx[ 1] * _mtx[ 7] - 
           _mtx[12] * _mtx[ 3] * _mtx[ 5];

_inv[14] = -_mtx[ 0] * _mtx[ 5] * _mtx[14] + 
            _mtx[ 0] * _mtx[ 6] * _mtx[13] + 
            _mtx[ 4] * _mtx[ 1] * _mtx[14] - 
            _mtx[ 4] * _mtx[ 2] * _mtx[13] - 
            _mtx[12] * _mtx[ 1] * _mtx[ 6] + 
            _mtx[12] * _mtx[ 2] * _mtx[ 5];

_inv[ 3] = -_mtx[ 1] * _mtx[ 6] * _mtx[11] + 
            _mtx[ 1] * _mtx[ 7] * _mtx[10] + 
            _mtx[ 5] * _mtx[ 2] * _mtx[11] - 
            _mtx[ 5] * _mtx[ 3] * _mtx[10] - 
            _mtx[ 9] * _mtx[ 2] * _mtx[ 7] + 
            _mtx[ 9] * _mtx[ 3] * _mtx[ 6];

_inv[ 7] = _mtx[ 0] * _mtx[ 6] * _mtx[11] - 
           _mtx[ 0] * _mtx[ 7] * _mtx[10] - 
           _mtx[ 4] * _mtx[ 2] * _mtx[11] + 
           _mtx[ 4] * _mtx[ 3] * _mtx[10] + 
           _mtx[ 8] * _mtx[ 2] * _mtx[ 7] - 
           _mtx[ 8] * _mtx[ 3] * _mtx[ 6];

_inv[11] = -_mtx[ 0] * _mtx[ 5] * _mtx[11] + 
            _mtx[ 0] * _mtx[ 7] * _mtx[ 9] + 
            _mtx[ 4] * _mtx[ 1] * _mtx[11] - 
            _mtx[ 4] * _mtx[ 3] * _mtx[ 9] - 
            _mtx[ 8] * _mtx[ 1] * _mtx[ 7] + 
            _mtx[ 8] * _mtx[ 3] * _mtx[ 5];

_inv[15] = _mtx[ 0] * _mtx[ 5] * _mtx[10] - 
           _mtx[ 0] * _mtx[ 6] * _mtx[ 9] - 
           _mtx[ 4] * _mtx[ 1] * _mtx[10] + 
           _mtx[ 4] * _mtx[ 2] * _mtx[ 9] + 
           _mtx[ 8] * _mtx[ 1] * _mtx[ 6] - 
           _mtx[ 8] * _mtx[ 2] * _mtx[ 5];

for( var _i = 0; _i < 16; _i++ ) _inv[_i] /= _det;
return _inv;

#14 Re: Script Submission » wrap(val, min, max) » 2016-09-26 02:58:35

Expand///wrap( value, minimum, maximum )
var _mod = ( argument0 - argument1 ) mod ( argument2 - argument1 );
if ( _mod < 0 ) return _mod + argument2 else return _mod + argument1;

#15 GML Creations » NSIS configuration » 2016-09-04 04:33:51

Juju
Replies: 1

With thanks to Alex Vourtsis.

GMS has some glitches and oversights in the way its NSIS installer is formatted. This script can be used to repair some of these problems, either by editing the NSIS config under GGS>Windows>Installer>Edit NSIS Script or by directly modifying RunnerInstaller.nsi in your project's Config folder.

  • Cleaned up script overall, many unused values from 2013

  • Organised sections better, and inserted comments for developers

  • You can now specify help/support links to be added on the Start Menu and in Add/Remove Programs

  • Fixed the components rollover

  • Game is now added to AppPath so it returns the correct default path on reinstall

  • Fixed uninstaller banner

  • Program lists more professional under Add/Remove (est. filesize, links, version, publisher etc.)

For GMS v1.4.1760 (though it probably works on older versions too)

Expand; RunnerInstaller.nsi
 
;---------------------------------------------------------------------------------------------------
;
; Usable Variables Index
;
; ${PRODUCT_NAME}          = Name of the game, as defined by project name
; ${PRODUCT_PUBLISHER}     = Company name
; ${FILE_DESC}             = Description of the game
; ${FULL_VERSION}          = Full version of the game, e.g. 1.0.0.0
; ${COPYRIGHT_TXT}         = Copyright info
; ${MAKENSIS}              = NSIS directory, usually "<GameMaker Studio Folder>\makensis"
; ${LICENSE_NAME}          = License file
; ${ICON_FILE}             = Executable icon file
; ${PRODUCT_HELP_URL}      = (Optional) Your game's help/distribution website
; ${PRODUCT_HELP_TITLE}    = (Optional) Help url title, used for filename in Start Menu shortcut
; ${PRODUCT_SUPPORT_URL}   = (Optional) Your game's/company's website
; ${PRODUCT_SUPPORT_TITLE} = (Optional) Support url title, used for filename in Start Menu shortcut
;  
;---------------------------------------------------------------------------------------------------
 
; Required Includes
!include MUI2.nsh
 
; Scan for NSIS plugins
!AddPluginDir "."
 
; The default installation directory (Only uncomment one of the following)
!define INSTALL_DIRECTORY "$PROFILE\${PRODUCT_NAME}"                           ; Install in Users/Username/
;!define INSTALL_DIRECTORY "$PROGRAMFILES\${PRODUCT_PUBLISHER}\${PRODUCT_NAME}" ; Install in Program Files/Company Name/Game Name/
;!define INSTALL_DIRECTORY "$PROGRAMFILES\${PRODUCT_NAME}"                      ; Install in Program Files/Game Name/
 
; Optional extra info values
!define PRODUCT_HELP_TITLE    "${PRODUCT_NAME} Website" ; Help/distribution url title
!define PRODUCT_HELP_URL      "" ; Your game's help/distribution url
 
!define PRODUCT_SUPPORT_TITLE "${PRODUCT_PUBLISHER} Website" ; Support url title
!define PRODUCT_SUPPORT_URL   "" ; Your game's url, could be your company's website
 
!define PRODUCT_COMMENTS      "" ; Comments, they appear on the Programs Add and Remove list
 
; The file to write
OutFile "${INSTALLER_FILENAME}"
 
; The name of the installer
Name "${PRODUCT_NAME}"
 
; Text caption for Installer/Uninstaller window title and dialogs
Caption "${PRODUCT_NAME} Setup"
UninstallCaption "Uninstall ${PRODUCT_NAME}"
 
; Branding text, appears on bottom separator
BrandingText "${PRODUCT_NAME}"
 
; Set default installation directory
InstallDir "${INSTALL_DIRECTORY}"
 
; Check the App Path, if the game is already installed, it will use the stored path as default install directory
!define PRODUCT_APPPATH "Software\Microsoft\Windows\CurrentVersion\App Paths\${PRODUCT_NAME}"
InstallDirRegKey HKLM "${PRODUCT_APPPATH}" ""
 
; Request administrator application privileges for Windows Vista and later
RequestExecutionLevel admin
 
; Add Version Information to installer executable
VIProductVersion "${FULL_VERSION}"
VIAddVersionKey /LANG=1033 "FileVersion"     "${FULL_VERSION}"
VIAddVersionKey /LANG=1033 "ProductVersion"  "${FULL_VERSION}"
VIAddVersionKey /LANG=1033 "ProductName"     "${PRODUCT_NAME}"
VIAddVersionKey /LANG=1033 "CompanyName"     "${PRODUCT_PUBLISHER}"
VIAddVersionKey /LANG=1033 "LegalCopyright"  "${COPYRIGHT_TXT}"
VIAddVersionKey /LANG=1033 "FileDescription" "${FILE_DESC}"
 
; Installer options and variables
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_BITMAP_NOSTRETCH
!define MUI_ICON                               "${ICON_FILE}"
!define MUI_UI_HEADERIMAGE_RIGHT               "${IMAGE_FINISHED}"
!define MUI_WELCOMEFINISHPAGE_BITMAP           "${IMAGE_FINISHED}"
!define MUI_HEADERIMAGE_BITMAP                 "${IMAGE_HEADER}"
!define MUI_WELCOMEFINISHPAGE_BITMAP_NOSTRETCH
 
; Custom uninstaller icon
;!define MUI_UNICON "${ICON_FILE}"
 
; Details visible or hidden (show = visible, hide = collapsed)
ShowInstDetails show
ShowUninstDetails show
 
; Variable to hold DirectX Setup error
Var DirectXSetupError
 
;--------------------------------
; Installer Pages
;--------------------------------
 
!insertmacro MUI_PAGE_WELCOME                   ; Introductory page (Optional)
!insertmacro MUI_PAGE_LICENSE "${LICENSE_NAME}" ; License agreement
!insertmacro MUI_PAGE_COMPONENTS                ; Component selection, shortcuts
!insertmacro MUI_PAGE_DIRECTORY                 ; Installation directory selection page
!insertmacro MUI_PAGE_INSTFILES                 ; Install progress page
   
    ; These indented statements modify settings for finish page
    !define MUI_FINISHPAGE_NOAUTOCLOSE ; don't autoclose the installer when done
    !define MUI_FINISHPAGE_RUN_TEXT "Run ${PRODUCT_NAME}" ; option for launching the game afterwards
    !define MUI_FINISHPAGE_RUN "$INSTDIR\${PRODUCT_NAME}.exe" ; path for the executable to run (default is the game exe)
   
!insertmacro MUI_PAGE_FINISH                    ; Installation done page
 
 
;--------------------------------
; Uninstaller Pages
;--------------------------------
 
!insertmacro MUI_UNPAGE_INSTFILES ; Uninstaller progress page
 
 
;--------------------------------
; Language
;--------------------------------
 
!insertmacro MUI_LANGUAGE "English"
 
 
;--------------------------------
; Installer
;--------------------------------
 
; Main Application component (required)
Section "${PRODUCT_NAME}" SEC_APPLICATION
 
    ; Make this component required
    SectionIn RO
 
    ; Set output path to the installation directory
    SetOutPath $INSTDIR
 
    ; Put file in installation directory
    File "${LICENSE_NAME}"
    File /r "${SOURCE_DIR}\*.*"
   
    ; Load up files necessary for getting filesize
    !include "${MAKENSIS}\Include\Util.nsh"
    !include "${MAKENSIS}\Include\FileFunc.nsh"
 
    ; Get estimated filesize
    ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
    IntFmt $0 "0x%08X" $0
   
    ; Create uninstaller executable
    WriteUninstaller "Uninstall.exe"
   
    ; Register the application in the Windows AppPath
    WriteRegStr HKLM "${PRODUCT_APPPATH}" "" "$INSTDIR"
   
    ; Write the uninstall keys for Windows
    WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayName" "${PRODUCT_NAME}"
    WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "Publisher" "${PRODUCT_PUBLISHER}"
    WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe"
    WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayVersion" "${FULL_VERSION}"
    WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayIcon" "$INSTDIR\${PRODUCT_NAME}.exe"
    WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "EstimatedSize" "$0"
    WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "NoModify" 1
    WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "NoRepair" 1
   
    ; Optional info
    ${If} "${PRODUCT_HELP_URL}" != ""
        WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "HelpLink" "${PRODUCT_HELP_URL}"
    ${EndIf}
    ${If} "${PRODUCT_SUPPORT_URL}" != ""
        WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "UrlInfoAbout" "${PRODUCT_SUPPORT_URL}"
    ${EndIf}
    ${If} "${PRODUCT_COMMENTS}" != ""
        WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "Comments" "${PRODUCT_COMMENTS}"
    ${EndIf}
 
SectionEnd
 
; DirectX installer component
Section "DirectX Install" SEC_DIRECTX
 
    ; Make this component required
    SectionIn RO
   
    SetOutPath "$TEMP"
    File "${MAKENSIS}\dxwebsetup.exe"
    DetailPrint "Installing Microsoft DirectX®..."
    ExecWait '"$TEMP\dxwebsetup.exe" /Q' $DirectXSetupError
    DetailPrint "Finished Microsoft DirectX® Setup"
    Delete "$TEMP\dxwebsetup.exe"
    ${If} $DirectXSetupError != "0"
        DetailPrint "Microsoft DirectX® was not installed correctly"
    ${Else}
        DetailPrint "Microsoft DirectX® was successfully installed"       
    ${EndIf}
   
    SetOutPath "$INSTDIR"
 
SectionEnd
 
 
 
; Desktop shortcut component
; !! Note: Add /o to force the shortcut option unchecked, e.g. Section /o "Desktop Shortcut" SEC_DESKTOP_SHORTCUT
Section "Desktop Shortcut" SEC_DESKTOP_SHORTCUT
    CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.exe" ""
SectionEnd
 
; Start menu shortcuts component
; !! Note: Add /o to force the shortcut option unchecked, e.g. Section /o "Start Menu Shortcuts" SEC_STARTMENU_SHORTCUT
Section "Start Menu Shortcuts" SEC_STARTMENU_SHORTCUT
 
  ; Create shortcuts in Start Menu
  CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
  CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0
  CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.exe" "" "$INSTDIR\${PRODUCT_NAME}.exe"
  CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME} License.lnk" "notepad.exe" "$INSTDIR\License.txt"
 
  ; Optional website links (auto-created if you fill in the URL values at the top of this script)
  ${If} "${PRODUCT_HELP_URL}" != ""
    WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_HELP_TITLE}.url" "InternetShortcut" "URL" "${PRODUCT_HELP_URL}"
  ${EndIf}
  ${If} "${PRODUCT_SUPPORT_URL}" != ""
    WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_SUPPORT_TITLE}.url" "InternetShortcut" "URL" "${PRODUCT_SUPPORT_URL}"
  ${EndIf}
 
SectionEnd
 
; Component Descriptions
LangString DESC_SEC01 ${LANG_ENGLISH} "This will install ${PRODUCT_NAME}. Required."
LangString DESC_SEC02 ${LANG_ENGLISH} "This will install Microsoft DirectX®. Required."
LangString DESC_SEC03 ${LANG_ENGLISH} "This will create a shortcut for ${PRODUCT_NAME} on the Desktop."
LangString DESC_SEC04 ${LANG_ENGLISH} "This will create shortcuts for ${PRODUCT_NAME} on the Start Menu."
 
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
    !insertmacro MUI_DESCRIPTION_TEXT ${SEC_APPLICATION} $(DESC_SEC01)
    !insertmacro MUI_DESCRIPTION_TEXT ${SEC_DIRECTX} $(DESC_SEC02)
    !insertmacro MUI_DESCRIPTION_TEXT ${SEC_DESKTOP_SHORTCUT} $(DESC_SEC03)
    !insertmacro MUI_DESCRIPTION_TEXT ${SEC_STARTMENU_SHORTCUT} $(DESC_SEC04)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
 
 
;--------------------------------
; Uninstaller
;--------------------------------
 
Section "Uninstall"
   
    ; Remove registry keys
    DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
    DeleteRegKey HKLM "${PRODUCT_APPPATH}"
   
    ; Remove files and uninstaller
    RMDir /r "$INSTDIR"
   
    ; Remove desktop icon, if any
    Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
   
    ; Remove start menu shortcuts, if any
    Delete "$SMPROGRAMS\${PRODUCT_NAME}\*.*"
   
    ; Remove directories used
    RMDir "$SMPROGRAMS\${PRODUCT_NAME}"
    RMDir "$INSTDIR"
   
SectionEnd
 
; Show confirmation message before uninstall
Function un.onInit
  MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure that you want to completely remove ${PRODUCT_NAME} and all of its components?" IDYES +2
  Abort
FunctionEnd
 
; Show info message after uninstall
Function un.onuninstSuccess
  MessageBox MB_ICONINFORMATION|MB_OK "${PRODUCT_NAME} was successfully removed from your computer."
FunctionEnd

#16 GML Creations » HMAC-SHA1 checksums and ds_map saving/loading » 2016-08-07 07:28:58

Juju
Replies: 1

An example on how to save/load protected maps. Uses a private key (built from character codes rather than using a plaintext string). This is not encryption!

Download here (11kb .gmz, tested in v1.4.1757)

Depends on hmac_sha1()

Expand///ds_map_hmac_sha1_save( id, filename, key )

var _map = argument0;
var _filename = argument1;
var _key = argument2;

var _str = ds_map_write( _map );
var _checksum = hmac_sha1( _key, _str );
var _buffer = buffer_create( string_length( _checksum ) + 1 + string_length( _str ) + 1, buffer_fixed, 1 );
buffer_write( _buffer, buffer_string, _checksum );
buffer_write( _buffer, buffer_string, _str );
buffer_save( _buffer, _filename );
buffer_delete( _buffer );

return true;
Expand///ds_map_hmac_sha1_load( filename, key )

var _filename = argument0;
var _key = argument1;

if ( !file_exists( _filename ) ) {
    return undefined;
    show_debug_message( "ds_map_hmac_sha1_load: Error! File not found" );
}

var _buffer = buffer_load( _filename );

var _checksum = buffer_read( _buffer, buffer_string );
if ( buffer_tell( _buffer ) >= buffer_get_size( _buffer ) ) {
    show_debug_message( "ds_map_hmac_sha1_load: Error! Only one string found (this is usually when someone has editted the file in Notepad)" );
    return undefined;
}

var _str = buffer_read( _buffer, buffer_string );
if ( _checksum != hmac_sha1( _key, _str ) ) {
    show_debug_message( "ds_map_hmac_sha1_load: Error! Checksum does not match expected checksum" );
    return undefined;
} else {
    var _map = ds_map_create();
    ds_map_read( _map, _str );
    return _map;
}

#17 Re: Script Submission » rectangle_on_line » 2016-07-05 18:17:41

Hah! We were both right happy

The issue with my suggested changes is that multiplying through by dx and dy loses sign information. It's possible to fudge it a bit with abs() calls but... eh. The speed improvement is then lost. Certainly replacing min/max makes things faster but the ultimate prize eludes me...

#18 Re: Script Submission » rectangle_on_line » 2016-07-05 04:04:15

...Ok, I couldn't resist. There's still a couple of divisions left that I can't seem to take out. This performs better than all other code so far in VM and YYC. I haven't exhaustively tested this by the way, it might not work in 100% of cases.

No, ignore this code. Broken.

Expand///line_box_intersection( x0, y0, x1, y1, left, top, right, bottom )

var x0     = argument0;
var y0     = argument1;
var x1     = argument2;
var y1     = argument3;
var left   = argument4;
var top    = argument5;
var right  = argument6;
var bottom = argument7;

var dx = x1 - x0;
var f0x = left - x0;
var f1x = right - x0;

if ( f0x > f1x ) { var temp = f0x; f0x = f1x; f1x = temp; }

if ( f1x <= 0 ) || ( f0x >= dx ) return false;
    
if ( f0x >  0 ) var flo = f0x / dx else var flo = 0;
if ( f1x < dx ) var fhi = f1x / dx else var fhi = 1;

if ( flo >= fhi ) return false;
        
var dy = y1 - y0;
flo *= dy;
fhi *= dy;

var f0y = top - y0;
var f1y = bottom - y0;

if ( f0y > f1y ) { var temp = f0y; f0y = f1y; f1y = temp; }

if ( f1y <= flo ) || ( f0y >= fhi ) return false;
    
if ( f0y > flo ) flo = f0y;
if ( f1y < fhi ) fhi = f1y;

// Nearest intersection
//var ix = x0+flo*dx/dy;
//var iy = y0+flo;

return ( flo < fhi );

#19 Re: Script Submission » rectangle_on_line » 2016-07-05 02:49:27

line_intersects_box_test.gmz

This example test four different orientations of line and box to poke at the different early-outs in each script.

xot's array code doesn't appear in this test because it was way slower. Arrays in GM are a bit sluggish, I guess. It's a shame, I would have liked to be able to use arrays as a kind of vec2 datatype.

Under VM, all three scripts compare favourably. xot's code appears to be consistently slightly faster but there's a degree of variance which muddies things a bit. Given that you can get the intersection coordinates (though commented out so it's a fair test), xot's script gets the Double Bacon Cheeseburger Award for tastiest code.

What happens under YYC is altogether different. Juju's code is faster in each of the four tests by a significant margin. Fred's script, which shares a startling similarity to Juju's, is slower than xot's by a small but definite margin. If we open up the Profiler and take a look (bearing in mind that the Profiler only works under VM), we can see where the difference is coming from: it's the use of min and max and that's likely slowing Fred's code down in YYC.

Edit: Thinking about this a bit - why does xot's script not see the same benefits as Juju's under YYC? Are function calls that expensive?

Edit 2: Swapping min and max for simple if branches is a big improvement but, still, xot's code is slower under YYC. The only thing that's left to potentially slow things down are the division operations. Is it possible to multiply through to remove the division operations? Possibly... I'll have a look later (if xot doesn't get to it first).

#20 Re: Script Submission » rectangle_on_line » 2016-07-05 01:24:38

xot, why not return the intersection data as a new array? Seems odd to have to create an array to pass as an argument that actually contains some output.

I didn't get round to performance testing yesterday (was having too much fun with other stuff) so I'll do it now. I'm betting on xot's first script being the fastest!

Board footer

Powered by FluxBB