So, clickbaity title aside, I think I figured out a way that could make GM aspect-oriented with just a relatively small change to the codebase. I'm making this conclusions based off of assumptions that might or might not be correct, and I might be overlooking some obvious flaws in my idea, so this might just be stillborn wishful thinking, of course. Opinions are welcome, logical arguments moreso.
First off, what is aspect-oriented programming? It's a different way to handle inheritance. Traditional inheritance is purely hierarchical in nature: your ghost enemy inherits its behavior from parentFlyingEnemy, which inherits some stuff from parentEnemy, which in turn inherits from parentContactDamage since the player is hurt by just touching it. This works all fine and dandy until you start wanting certain aspects to be inheritable, but with no real logical hierarchical structure. For instance, let's say you also want your ghost to glow in the dark. You have parentLightsource since before, but how would you make a light-emitting enemy using that? If you change the ghost's parent into parentLightsource, it loses all its enemy code and won't be treated as a damage object anymore. You could duplicate the parentFlyingEnemy into a new parentFlyingEnemyLightsource, but now you have code duplication.
The least messy solution is manually executing all the parentLightsource's events using event_perform_object, but it means you need to keep track of every "bastard child" object yourself and add new event_perform_object calls if you ever were to add new events to the parent. And the bastard children won't be covered by with(parentObject) loops, cause collision events with the parent, and so on, which might be desired for a whole lot of inheritances (say, parentCanSetWoodenThingsOnFire).
Aspect oriented programming essentially lets you have multiple unrelated inheritances, picking and mixing the functionality you need without having your objects be a tree of hierarchical definitions.
So what's my proposed solution for this?
Make GM object parents an array.
The idea is as follows:
Apart from being easier to work with in large projects (where you end up with a lot of different systems and no really extensible way to tie them together), this functionality could also help promote code reuse and properly organized projects for new users (and D&D users!) who might not be comfortable setting up the script framework necessary to simulate parenting on their own, and asset pack creators could make systems that are much more easily to slot into existing projects (by adding a new parent to affected objects).
First off, what is aspect-oriented programming? It's a different way to handle inheritance. Traditional inheritance is purely hierarchical in nature: your ghost enemy inherits its behavior from parentFlyingEnemy, which inherits some stuff from parentEnemy, which in turn inherits from parentContactDamage since the player is hurt by just touching it. This works all fine and dandy until you start wanting certain aspects to be inheritable, but with no real logical hierarchical structure. For instance, let's say you also want your ghost to glow in the dark. You have parentLightsource since before, but how would you make a light-emitting enemy using that? If you change the ghost's parent into parentLightsource, it loses all its enemy code and won't be treated as a damage object anymore. You could duplicate the parentFlyingEnemy into a new parentFlyingEnemyLightsource, but now you have code duplication.
The least messy solution is manually executing all the parentLightsource's events using event_perform_object, but it means you need to keep track of every "bastard child" object yourself and add new event_perform_object calls if you ever were to add new events to the parent. And the bastard children won't be covered by with(parentObject) loops, cause collision events with the parent, and so on, which might be desired for a whole lot of inheritances (say, parentCanSetWoodenThingsOnFire).
Aspect oriented programming essentially lets you have multiple unrelated inheritances, picking and mixing the functionality you need without having your objects be a tree of hierarchical definitions.
So what's my proposed solution for this?
Make GM object parents an array.
The idea is as follows:
- Objects have multiple parents (if so desired), and they can be ordered manually. (This is important, since it makes the order conflicting events would resolve well-defined).
- When an event is empty (and the parent event is run instead), parent events of all applicable objects (i.e., most immediate ancestor with a non-empty event) are run, in the order that parents are listed.
- event_inherited() has the same change: all first-nonempty-ancestor events are run, and in the order the parents were listed.
- object_is_ancestor() checks all ancestry branches to see if there's a match on any of them.
- with(parentObj), collision events, and all other loops that implicitly include children will count an object as eligible if any of its parents/ancestors matches the looped-over object.
- The user is responsible to make sure potential ancestry conflicts (e.g. the "diamond problem") doesn't happen in their game. Since GM allows you to create new variables anywhere, and generally is "it happens when the line is executed" instead of "everything exists at compile time", conflicts generally shouldn't be more serious than "two different pieces of code overwrites the same variable with different values"... and that should be taken care of by the priority system.
- The user is also responsible to make shared grandparent code idempotent (i.e., executing it multiple times doesn't cause resource leaks, errors, repeated side effects, or invalid code) for diamond-shaped ancestry where a shared ancestor's code might be executed multiple times thanks to multiple parents having event_inherited()s for the same object's event.
- If the user only uses 1 parent for each object, the system is 100% compatible with the old way of handling parenting. Additionally, converting legacy projects from the old system to the new is trivial.
Apart from being easier to work with in large projects (where you end up with a lot of different systems and no really extensible way to tie them together), this functionality could also help promote code reuse and properly organized projects for new users (and D&D users!) who might not be comfortable setting up the script framework necessary to simulate parenting on their own, and asset pack creators could make systems that are much more easily to slot into existing projects (by adding a new parent to affected objects).
Last edited: