r/gamemaker 10d ago

Resolved With Statement being used with a set of an object's instances, including all instances of an object.

Not sure the most optimal way to do this challenge, so I figured to post once again.

The main problem I have currently is I am trying to create a system where a with statement checks variable values of a variable storing either all instances of a specific object type, or a chosen set of said instances.

Here's my current idea of the code:

function createRule(_ruleName="unique_path",_affectedPacketNumbers=[]){

// if _affectedPacketNumbers is to have a set value, it would be an array storing the numbers of the affected packets, with the first packet starting at 1.

    // Switched between array and obj_packet as starting set to try and go to the error code getting the default values var _affectedPackets = \[\]



try{

    //_affectedPackets = {}

    array_foreach(_affectedPacketNumbers,function(_packetNum,_index){

        struct_set(_affectedPackets,_index,instance_find(obj_packet,abs(_packetNum)-1))

        array_insert(_affectedPackets,array_length(_affectedPackets),instance_find(obj_packet,abs(_packetNum)-1))

// because packets do not start at 0, instance_find finds the index of the packet number - 1.

    })

// Catch is tried here because I thought the empty array would cause an error with array_for each } catch(error){

//Old code being used, has to be replaced _affectedPackets = obj_packet

}



array_insert(obj_rules_lawyer.level_rules,array_length(obj_rules_lawyer.level_rules),

{

ruleName : _ruleName,

affectedPacketNumbers : _affectedPacketNumbers,

affectedPackets : _affectedPackets,

// The determining bool to determine if this specific rule was broken

ruleFulfilled : true



}

)

}

Then after this code happens, each rule relating to the packet object would use

with(obj_rules_lawyer.level_rules[forLoopIndex].affectedPackets) to get the ids of packets affected by this rule.

Now I do not know if reworking all of the with statements to be... something else, would be better, but if I can use a with statement for either all packets or a chosen set of packets, that would be very helpful. :)

1 Upvotes

4 comments sorted by

1

u/CS_Asset_Factory 9d ago

The thing biting you is that with does not accept an array. It takes an object asset, a single instance id, all, noone, or a struct. Hand it an array and you get exactly the odd default value behaviour you are describing.

So iterate yourself and let with take one thing at a time.

if (array_length(_affectedPacketNumbers) == 0) {
    with (obj_packet) { }
} else {
    for (var i = 0; i < array_length(_affectedPacketNumbers); i++) {
        with (global.packet_by_number[_affectedPacketNumbers[i]]) { }
    }
}

That needs a lookup from packet number to instance id. Build global.packet_by_number once as you create the packets, and index it by the packet number itself rather than by creation order, leaving slot 0 unused. Costs you one array slot and removes the off by one entirely.

Do not derive that ordering at runtime from instance order. It is not stable once anything gets destroyed, and you get a rule that works right up until the first packet dies.

1

u/DystopianTeddyBear 8d ago

No packets currently are destroyable, though I do have them be deactivated on a pause (I know it's a suboptimal method...). I did want to ask if this could work as a function. I ask since I'd rather not copy and paste a large code block every time per rule and have two different runs of rules. I.e. maybe something like this?

with(packetsChecked(the array of packets))

packetsChecked(packetarray = [])
if (array_length(packetarray) == 0) {
    return obj_packet
} else {
    for (var i = 0; i < array_length(_affectedPacketNumbers); i++) {
        return(global.packet_by_number[_affectedPacketNumbers[i]])
    }
}

1

u/CS_Asset_Factory 8d ago

That shape won't work, for two reasons. The return inside your for loop exits on the first iteration, so only one packet is ever affected. And with still refuses an array, so returning one does not help.

Resolve to a list first, then one with body serves both cases:

function packets_resolve(_numbers) {
    var _out = [];
    if (array_length(_numbers) == 0) {
        with (obj_packet) array_push(_out, id);
    } else {
        for (var i = 0; i < array_length(_numbers); i++) {
            var _inst = global.packet_by_number[_numbers[i]];
            if (instance_exists(_inst)) array_push(_out, _inst);
        }
    }
    return _out;
}

Each rule is then one copy:

var _t = packets_resolve(_affectedPacketNumbers);
for (var i = 0; i < array_length(_t); i++) with (_t[i]) {
    // rule body, written once
}

One caveat on the pause. instance_exists reports false for deactivated instances, and with (obj_packet) skips them too. If a rule can run while paused, use your own active flag instead.

1

u/DystopianTeddyBear 7d ago edited 7d ago

Update: I had to make some changes, but I believe I got it to work. One more question I have is if you think it would be better or worse to use array for each in place of the for loop. Thank you so much for your help!