Passing local variables to scripts without 16 argument limitation? Is there a workaround? (solved)

B

BlackAura

Guest
Ok, long question. I'm rebuilding a project of mine from the ground up. I'm designing a system where all game world objects can interact with one another. Therefore, there's a kind of hierarchy of variables needed for each quality of an object. I'm trying to do this using scripts with as little repetition as possible so when I change a couple universal scripts, nearly all objects are affected.

for example:
is an object collectible? if so does it go to inventory?
is an object carryable? if so can it be thrown? is it heavy to lift?
does an object emit light? if so, where? what color? radius? sprite? cast shadows? etc etc etc.

You see, I can create variables to describe all of these aspects. however, you can see that more fundamental "parent" variables would need subcategories of variables, and if that more fundamental variable is false (like if the object does not cast light with the lighting engine) then I've created a ton of "sub-variables" that just take up memory.

My original solution was this. Every object has a big block of initialization code. (I'll post this for an example.) Then, I would edit this block to define almost any object in the game. This would be so virtually all objects can share a universal logical system and I could mix and match the behavioral dna if you will of any object. A simple parent and child object system is not enough. I've tried. I run into situations where I want an object with the qualities of various parents and to be checked for when either parent is checked for by other objects. To avoid creating a bunch of instance variables each specialized object would have no use of, I thought to use local variables for all variables that would only be used with certain objects, then call a script that would turn the local variables into instance variables if their "parent" variable was set to true. (this script would additionally create more specialized variables.) However, I wasn't thinking and forgot that a script cannot read the local variables in the same event calling it.

Well, this is frustrating. Could I pass local variables into the script with arguments? Absolutely, that's the point of arguments. But here's the problem. I have like... 30 local variables. far more than the 16 argument limitation.

I could just move the (turn relevant local variables into instance variables) script's code into the create event, but then if there's a modification I want to make, i no longer can modify a script and have to manually edit all my objects. Not going to happen, and completely destroys the point of having this kind of hierarchical, universal variable system. However I need all my objects to share a ton of code as objects will be endlessly reading each other's code and interacting with each other so I need to use scripts to ensure darn-near perfect interactive behavior using strict naming conventions and matching variable names throughout.

Here are my two ideas.

1: use instance variables from the start. is there a downside to having like 50 variables in every object, when each object only uses a handful of them? I thought "oh hey, is there a way to free/delete instance variables if I know for sure I won't use them like I can free a surface or data structure?" It doesn't appear so.

2: my only possible workaround. Create a ton of global variables to act like arguments, to get around the 16 argument limitation. for example. create something like a global.arg at the beginning of the game, then in my code basically do this:

global.arg[0]=var
global.arg[1]=var
global.arg[2]=var
global.arg[3]=var
customscript()
and the customscript will reference that global array instead of the built in argument[0-15] variables.

My question is this. What's the more efficient solution? Am I missing something that would be a lot more elegant?

EDIT: Third idea. Place the script's code into the parent's create code (I have a parent object called OBancestor_obj. it is essentially the parent of all parents.), then in all create events, have this initialization block run, then at the end call an event_inherited()? will the local variables from the child objects continue into the code coming in from the parent object unlike it would if that code were coming from a script?

Here's an example of that initialization code.

Code:
//Init Universal Variables: Main
    xv=x                                //x origin. can be different from object's x position but usually isn't.
    yv=y                                //y origin. can be different from object's x position but usually isn't.
    xspeed=0                            //
    yspeed=0                            //
    mobile=1                            //if object can change position.
var i_physical=1                        //if object interacts with solids.
var i_mask=mask_index                    //mask of object. must be set here and not in object properties.
var i_grav=1                            //gravitational factor. 1:normal
var i_rollable=1                        //if object rolls when moved           
var i_topple=1                            //if object is bottom-heavy and balances self.
var i_breakable=0                        //if object is broken by being thrown.
var i_weight=16                            //weight of object, controls how wind, water, etc affect it.

//Init Universal Variables: Material Properties
    material=Material.mat_metal            //material of object.                0:null|1:particle|2:organic|3:stone|4:metal|5:wood|6:grass|7:sand|8:snow|9:ice|10:waterbody|11:waterbodyelement|12:ghost
    mat_solid=0                            //if object is a solid.
    mat_buoyant=1                        //if object floats in water.        0:null|1:alwaysfloats|2:canbesunken
    mat_burnable=0                        //if object can be lit on fire.
    mat_shockable=1                        //if object can conduct electricity.
    mat_freezable=0                        //if object can be frozen in ice.
    mat_blowable=1                        //if object can be blown by the wind.
    mat_wettable=1                        //if object can get wet.

//Init Universal Variables: Interactivity
    carriable=1                            //if object can be picked up.
    collectable=0                        //if object can be collected.
    container=1                            //if object can contain another object.
var i_inst_contain=-1                    //handle for contained object.

//Init Universal Variables: HP and DP
    hazardous=0                            //if object can deal damage.
var i_dp=0                                //damage points (amount of dealt damage).
    destructable=0                        //if object can be destroyed
var i_respawn=0                            //if object creates respawner object upon destruction
var i_hp=100                            //health points (resistance to recieved damage)
var i_hp_max=i_hp                        //maximum hp.
var i_vv_iframe=0                        //invincibility frames.
var i_damage_beam=1                        //factor by which damage to object is multiplied. 0:invulnerable -- 1:normal vulnerability
var i_damage_bomb=1                        //factor by which damage to object is multiplied. 0:invulnerable -- 1:normal vulnerability
var i_damage_burn=1                        //factor by which damage to object is multiplied. 0:invulnerable -- 1:normal vulnerability
var i_damage_shock=1                    //factor by which damage to object is multiplied. 0:invulnerable -- 1:normal vulnerability
var i_damage_freeze=1                    //factor by which damage to object is multiplied. 0:invulnerable -- 1:normal vulnerability
var i_damage_wet=1                        //factor by which damage to object is multiplied. 0:invulnerable -- 1:normal vulnerability
var i_damage_blade=1                    //factor by which damage to object is multiplied. 0:invulnerable -- 1:normal vulnerability
var i_damage_lantern=1                    //factor by which damage to object is multiplied. 0:invulnerable -- 1:normal vulnerability

//Init Universal Variables: Lightcast
    lightcast=0
var i_spr_lightcast=-1
var i_ff_lightcast=0
var i_xv_lightcast=x
var i_yv_lightcast=y
var i_xx_lightcast=1
var i_yy_lightcast=1
var i_rr_lightcast=0
var i_cc_lightcast=-1
var i_aa_lightcast=1

This is the code I wanted to run after to "clean up" my variable usage. I had this in a script but for some reason totally forgot about local variable scopes when using scripts. as you can see it does a little more than just that.

Code:
//Create Instance Variables From Local Variables
if mobile=1
    {
    physical=i_physical
    mask=i_mask
    if physical=1
        {
        grounded=0
        slope=0
        weight=i_weight   
        waterlevel=0
        submerged=0
        rollable=i_rollable
        breakable=i_breakable
        grav=i_grav
        if rollable=1 {topple=i_topple rr_roll=0 rs_roll=0 vv_rollsettle=0}
        //event notifiers.
        event_bounce=0
        //universal sound timers
        tt_snd_lap=0
        }
    }
    
if mat_buoyant=1
    {sv_buo=0      ss_buo=0        vv_waterfill=0}
if pausable=1 paused=0
    
if mat_burnable=1
    {vv_burn=0     vd_burn=0}
if mat_shockable=1
    {vv_shock=0    vd_shock=0}
if mat_freezable=1
    {vv_freeze=0   vd_freeze=0}
if mat_wettable=1
    {vv_wet=0      vd_wet=0}

if container=1
    inst_contain=i_inst_contain
    
if carriable=1
    carry=0

if hazardous=1
    dp=i_dp

if destructable=1
    {
    respawn=i_respawn
    hp=i_hp
    hm_max=i_hp_max
    vv_hp=0
    vv_iframe=i_vv_iframe
    damage_beam=i_damage_beam
    damage_bomb=i_damage_bomb
    damage_burn=i_damage_burn
    damage_shock=i_damage_shock
    damage_freeze=i_damage_freeze
    damage_wet=i_damage_wet
    damage_blade=i_damage_blade
    damage_lantern=i_damage_lantern
    }
    
if lightcast=1
    {
    spr_lightcast=i_spr_lightcast
    ff_lightcast=i_ff_lightcast
    xv_lightcast=i_xv_lightcast
    yv_lightcast=i_yv_lightcast
    xx_lightcast=i_xx_lightcast
    yy_lightcast=i_yy_lightcast
    rr_lightcast=i_rr_lightcast
    cc_lightcast=i_cc_lightcast
    aa_lightcast=i_aa_lightcast
    }
 
Last edited by a moderator:

YellowAfterlife

ᴏɴʟɪɴᴇ ᴍᴜʟᴛɪᴘʟᴀʏᴇʀ
Forum Staff
Moderator
Are you sure that 16-argument limitation is in effect? As far as I'm concerned, it have been fixed a while ago, so shouldn't be the case unless you are using a much older version of GM.

Splitting the script into several (one setting some global variables for the other) or storing these values in an array/list would be the usual solution.
 

Electros

Member
Create a ton of global variables to act like arguments, to get around the 16 argument limitation
Just a note that you can pass an array in as a single argument if you did want to circumvent the limit, then access the array elements individually. It may be if you are hitting the limit that it would be cleaner to break it down through data structures or other methods to try and simplify somehow, but it is an option.
 
B

BlackAura

Guest
Are you sure that 16-argument limitation is in effect? As far as I'm concerned, it have been fixed a while ago, so shouldn't be the case unless you are using a much older version of GM.

Splitting the script into several (one setting some global variables for the other) or storing these values in an array/list would be the usual solution.
I'm using GMS 2. The limit is still 16 unfortunately. I suppose I could create a couple scripts. Not the best solution for organization but a good idea!

Just a note that you can pass an array in as a single argument if you did want to circumvent the limit, then access the array elements individually. It may be if you are hitting the limit that it would be cleaner to break it down through data structures or other methods to try and simplify somehow, but it is an option.
Of course! That's an excellent idea. That should work beautifully! I don't think I've ever used arrays as arguments in scripts, that's pretty clever, and more organized as well. I think that's the solution :)
 

NeZvers

Member
You can definitely pass in more than 16 arguments. There are only 16 built-in argument calls (argument0 - argument15) but that doesn't mean you can't have more.
You need to use argument_count, so script counts all inputs and GM:S doesn't throw red line, because of wrong argument count, and read the argument as an array - argument.
it works GM:S 1.4 AND GM:S 2.

Code:
//input
array_leng(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20);

//Script
for(var i=0; i<argument_count; i++){
    show_debug_message(string(argument[i]));
}

//Output
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
Top