-2
\$\begingroup\$

I have a problem spawning a object twice throughout the game. This is my code:

alarm[0]event of Obj_game
randomize()
var wrongitems,righitems,xpos,ypos;

xpos = random_range(100,room_width-100);   
ypos = -100  

if irandom(50)=1
{
  wrongitems = instance_create(xpos, ypos, obj_stew) 
  wrongitems.sprite_index= choose(
    sprCarrot,
    spr_bread,
    spr_apple,
    spr_aubergine,
    spr_cabbage,
    spr_cake,
    spr_orange,
    spr_banana,
  )
}
if irandom(50)=1
{
  wrongitems = instance_create(xpos, ypos, obj_stew)
  wrongitems.sprite_index= choose(
    spr_onion,
    spr_pepper,
    spr_oil,
    spr_beef,
    spr_salt,
    spr_tomatoes,
  )
}

This spawns the right items randomly an infinite number of times as long as nothing is happening but how can I make it spawn the right objects just 2 times each

\$\endgroup\$
0
\$\begingroup\$

Thanks guys! A swell developer helped me out with this.

He approached the problem using ds lists to pick the sprites used for spawning the objects then using the for statement to specify how many times each sprite should be used in an object.

This is what he did.

wrongfruit = ds_list_create();
item_number = 5; 
for(var i = 0; i < item_number; i++)
{
    ds_list_add(wrongfruit, sprCarrot);
    ds_list_add(wrongfruit, spr_bread);
    ds_list_add(wrongfruit, spr_apple);
    ds_list_add(wrongfruit, spr_aubergine);
    ds_list_add(wrongfruit, spr_cabbage);
    ds_list_add(wrongfruit, spr_cake);
    ds_list_add(wrongfruit, spr_orange);
    ds_list_add(wrongfruit, spr_banana);
}
\$\endgroup\$
  • 1
    \$\begingroup\$ As a mild note this answer will catastrophically fail if you use save files at all. Data structures get flushed from memory. If you'd like, I can show you a cheap solution for how to get working lists that don't get flushed? \$\endgroup\$ – user64742 Jun 6 '17 at 4:55
-1
\$\begingroup\$

WARNING: REALLY BIG POST. THERE'S A LOT OF CODE HERE.

Now if you are familiar a lot with the language C, this answer will make a lot of sense to you at the low level. Otherwise, do not worry about the exact functions. Some of them are fairly complicated.

Ok so basically going off of your self answer that isn't just a failed commenting attempt (whoops), I can see you want some kind of list data structure for your thingy. I should be quite clear at this point and clarify that sprites are the actual internal image files. So generating them isn't quite what you think. Spawning them doesn't really make sense as a good term either. So perhaps what you should say is that you want to randomly draw images around a room, but never use the same one more than so many times. Anyway, that's a minor rant on your terminology. At this point, it's probably irrelevant...

Now there are two built-in ways to make a linked list* in game maker.

  1. The built in "DS" functions.

  2. Manually by using objects as nodes and a main linked list class (I'd suggest googling the architecture of a linked list in the Java language to understand what I mean)

Both of these attempts will fundamentally fail. Here is why.

In the documentation for data structures it states the following in the 4th paragraph:

Please note that data structures and their content are not saved when you save the game using the actions or functions for that. If you use data structures and want to allow for saves you have to create your own mechanism for that.

Considering that I've taken quite a few computer science classes and used a lot of languages other than Game Maker, I can confidently say that avoiding to write a custom save mechanism is quite desirable if we can do it without compromising quality.

Regarding the use of object IDs being used to create complex linked pointer structures the issue can be summed up in the documentation on objects.

Note that the assignment of the instances to the instance id's changes every step so you cannot use values from previous steps.

So you're basically dealing with shuffling memory locations, which is a pretty horrible setup, imo. I don't know of any other language with such a concept. It's laziness on part of the developers.

These all lead us to the only conclusion I can think of to resolve these issues:

Write a dynamic memory allocator within an array.

You only need the functions of malloc and free which will work within the context of an object. This basically means that data structures made with these will only exist in the context of a particular instance (as they reside within a local array). What follows below are the scripts I wrote to implement such an allocator. Be sure to put them each individually within a script with the name given in the header. These will all be supplementary and necessary only for the purpose of allocating the array indices used for the linked list.


mm_init

HEAD_NODE = 0;
/*no need to align 0*/
HEAP_SPACE[HEAD_NODE] = 0;
LINKED_LIST_LENGTH = 0;
TAIL_NODE = HEAD_NODE;
return 0;

mm_malloc

newsize = argument[0] + (argument[0] mod 2);
freeP = HEAD_NODE;

if (mm_getLength(freeP) - newsize > 0 && !mm_getFree(freeP))
{
    mm_setFree(freeP,true);
    mm_insert(freeP, newsize);
    return freeP + 1;
}
while (mm_getNext(freeP) < TAIL_NODE)
{
    freeP=mm_getNext(freeP);
    if (mm_getLength(freeP) - newsize > 0 && !mm_getFree(freeP))
    {
        mm_setFree(freeP,true);
        mm_insert(freeP, newsize);
        return freeP + 1;
    }
}

p = TAIL_NODE; /*add to the heap and return the starting position*/
//p = mem_sbrk(newsize + 8); /*add to the heap and return the starting position*/
if (p == -1)
{
    return NULL;
} else
{
    HEAP_SPACE[p] = (newsize) + (1 << 31);
    HEAP_SPACE[mm_getNext(p) - 1] = newsize;
    if (LINKED_LIST_LENGTH == 0)
    {
        HEAD_NODE = p;
    }
    TAIL_NODE = mm_getNext(p);
    LINKED_LIST_LENGTH += 1;
    return (p + 1);
}

mm_free

/* adjust so that ptr points
 * to the node for this block
 */
p = argument[0] - 1;
HEAP_SPACE[p] = mm_getLength(p);

/*did the pointer originate from the heap*/
if (p > HEAD_NODE && p < TAIL_NODE)
{
    if (!mm_getFree(mm_getPrevious(p)))
    {
        /* the previous node
         * can be coalesced
         */
        mm_removeNode(p);
        p = mm_getPrevious(p);
    }

    if (!mm_getFree(mm_getNext(p)) && mm_getNext(p) <= TAIL_NODE)
    {
        /* the next node
         * can be coalesced
         */
        if (mm_getNext(p) == TAIL_NODE)
        {
            TAIL_NODE = p;
        }
        mm_removeNode(mm_getNext(p));
    }
}

mm_getNext

return (argument[0] + mm_getLength(argument[0]) + 2);

mm_getPrevious

return (argument[0] - mm_getLength(argument[0]-1) - 2);

mm_getLength

/*zero out the leftmost bit, reserved for the free value*/
return HEAP_SPACE[argument[0]] & (~(1 << 31));

mm_getFree

return ((HEAP_SPACE[argument[0]] & (1 << 31)) > 0);

mm_setLength

HEAP_SPACE[argument[0]] = argument[1] + (mm_getFree(argument[0]) << 31);
HEAP_SPACE[mm_getNext(argument[0]) - 1] = argument[1];

mm_setFree

HEAP_SPACE[argument[0]] = mm_getLength(argument[0]) + (argument[1] << 31);

mm_removeNode

mm_setLength(mm_getPrevious(argument[0]),mm_getLength(mm_getPrevious(argument[0]))+mm_getLength(argument[0])+2);
LINKED_LIST_LENGTH -= 1;
/*length is in terms of integer words*/

mm_insert

/* the node has to fit to avoid distorting
 * the linked list's heap property
 */
if (argument[1] + 2 < mm_getLength(argument[0]))
{
    node = argument[0] + argument[1] + 2;
    /*ensure the free field is 0*/
    HEAP_SPACE[node] = 0;
    mm_setLength(node,mm_getLength(argument[0])-argument[1]-2);
    mm_setLength(argument[0],argument[1]);
} else
{
    /* all blocks and nodes are 2 byte aligned, so a difference of one
     * would only exist in the case of a system flaw
     */
    HEAP_SPACE[argument[0]] = mm_getLength(argument[0]) + (1 << 31);
}

That is all of the functions and little utility scripts needed to implement malloc and free. If you understand the wikipedia article I linked or have used malloc and free then you will be able to use them to allocate memory for structs which are basically objects but with nothing but data and no events or methods attached. Note that this is technically a slapped on interface we are just honoring to give the illusion of allocation. Game Maker is oblivious and just presumes this to be array manipulation. The mm_init script at the beginning basically resets the heap space which is the name conventionally used to describe a region wherein memory is allocated in this manner or something similar. Be sure to call it before allocating or weird things will happen. In fact, call it before any of the scripts I list here are called. Just call it in the creation event of your stew manager thingy mentioned in the question.

Anyways, to build a linked list we need node structs. Here are the scripts used to define generic nodes. These are a sub-object used to build larger things, but basically you can give them a script that will act as a manner of deleting whatever is held within the list, tree, or what-have-you that the nodes are building. You're holding integers AKA sprite indexes so don't worry about it. It will only be useful for more advanced things (better to put it in and not use it then to not put it in and get a memory leak later).

Same principle as before applies:


node_createNode

argument[0] = mm_malloc(4);
node_setData(argument[0],-1);
node_setNext(argument[0],-1);
node_setPrevious(argument[0],-1);
node_setDataDestructor(argument[0],node_byteDataDestructor);
return argument[0];

node_setData

HEAP_SPACE[argument[0]] = argument[1];

node_setNext

HEAP_SPACE[argument[0]+1] = argument[1];

node_setPrevious

HEAP_SPACE[argument[0]+2] = argument[1];

node_setDataDeconstructor

HEAP_SPACE[argument[0]+3] = argument[1];

node_getData

return HEAP_SPACE[argument[0]];

node_getNext

return HEAP_SPACE[argument[0]+1];

node_getPrevious

return HEAP_SPACE[argument[0]+2];

node_getDataDeconstructor

return HEAP_SPACE[argument[0]+3];

node_deleteNode

script_execute(node_getDataDestructor(argument[0]),node_getData(argument[0]));
mm_free(argument[0]);

node_byteDataDestructor

/*this script intentionally left empty as a destructor for built-in numerical data types*/

So you have nodes and an allocator. The actual linked list will take a while to write as I haven't actually written it yet. These are all scripts I have in my personal library and have no problem sharing. The linked list coincidentally needs to be written for some stuff of my own anyways. When it is finished I will edit this post with the other scripts and the rewritten version of that segment you had in your self-written answer.

Technically not a complete answer at this time. I still have to write the linked list functions as well, but this is a start. I should have the rest done very soon (it's not trivial to write and I have a lot to get done).


*You don't want me to try writing a true arrayList like in Java. I honestly suck at writing that version. We'd be here a while... as in more than a week. Linked lists can also be more efficient at removal. The difference is marginal for you since the list is so tiny.

\$\endgroup\$
  • \$\begingroup\$ To those reading this post, it is technically an answer in that it says how to do this "use the nodes and dynamic allocation I have provided to write a linked list data structure for use in the algorithm the OP provided in their partial answer". I'm just aware that asking one to do that is not a simple feat and opting to err on the side of writing more to finish up all the scripts they need at the level of abstraction they were working at. \$\endgroup\$ – user64742 Jun 8 '17 at 19:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.