Indie Dev

Hello Guest!. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, sell your games, upload content, as well as connect with other members through your own private inbox!

Combining anonymous function with plugin container

DoubleX

Adventurer
Xy$
1.12
We all know what anonymous functions are as almost all of us work with them all the time and the majority of us use them all the time.
Many of us have also heard of plugin container, which is an object containing all new functions added by the plugin, or similar concepts.

But how about using them together? DoubleX RMMv Item Triggers gives an example for this experiment:
JavaScript:
(function(IT) {

    IT.DataManager = {};
    var DM = IT.DataManager;

    DM.isDatabaseLoaded = DataManager.isDatabaseLoaded;
    DataManager.isDatabaseLoaded = function() {
        // Rewritten
        return DM.isDatabaseLoaded.apply(this, arguments) && DM.loadAllNotes();
        //
    }; // DataManager.isDatabaseLoaded

    DM.loadAllNotes = function() {
        [$dataSkills, $dataItems].forEach(function(type) {
            type.forEach(function(data) {
                if (data) { DM.loadItemNotes(data); }
            });
        });
        return true;
    }; // DM.loadAllNotes

    // data: The data to have its notetags read
    DM.loadItemNotes = function(data) {
        var regExp = /< *(\w+) +item +trigger *: *(\w+) *, *(\w+) *>/i;
        var timing, triggers;
        data.meta.itemTriggers = {};
        triggers = data.meta.itemTriggers;
        data.note.split(/[\r\n]+/).forEach(function(line) {
            if (!line.match(regExp)) { return; }
            timing = RegExp.$1;
            triggers[timing] = triggers[timing] || [];
            triggers[timing].push([RegExp.$2, RegExp.$3]);
        });
    }; // DM.loadItemNotes

    IT.BattleManager = {};
    var BM = IT.BattleManager;

    BM.startAction = BattleManager.startAction;
    BattleManager.startAction = function() {
        // Added
        var item = this._subject.currentAction().item();
        GBB.execItemTriggers.call(this._subject, item, "preBattle");
        //
        BM.startAction.apply(this, arguments);
    }; // BattleManager.startAction

    BM.endAction = BattleManager.endAction;
    BattleManager.endAction = function() {
        BM.endAction.apply(this, arguments);
        // Added
        var item = this._action ? this._action.item() : null;
        if (!item) { return; }
        GBB.execItemTriggers.call(this._subject, item, "postBattle");
        //
    }; // BattleManager.endAction

    IT.Game_BattlerBase = {};
    var GBB = IT.Game_BattlerBase;

    /*------------------------------------------------------------------------
     *    Triggers each item action when each respective condition's met    
     *------------------------------------------------------------------------*/
    // timing: The timing of the item triggering its actions
    GBB.execItemTriggers = function(item, timing) {
        var triggers = item.meta.itemTriggers[timing];
        if (!triggers) { return; }
        // Calls each ITCX to see if its ITAX should be called as well
        triggers.forEach(function(trigger) {
            if (IT[trigger[0]].call(this)) { IT[trigger[1]].call(this); }
        }, this);
        //
    }; // GBB.execItemTriggers

    IT.Scene_ItemBase = {};
    var SIB = IT.Scene_ItemBase;

    SIB.useItem = Scene_ItemBase.prototype.useItem;
    Scene_ItemBase.prototype.useItem = function() {
        GBB.execItemTriggers.call(this.user(), this.item(), "preMap"); // Added
        SIB.useItem.apply(this, arguments);
        GBB.execItemTriggers.call(this.user(), this.item(), "postMap"); // Added
    }; // Scene_ItemBase.prototype.useItem

})(DoubleX_RMMV.Item_Triggers);
Note that extended functions always use apply only, while new functions always use call only.

This style exhibits at least the below 3 traits:
1. Almost the whole plugin's implementation's(except declaring new classes which will be done before calling the anonymous function) wrapped by a single anonymous function
2. Every new function's wrapped by a single container - the plugin object(DoubleX_RMMV.Item_Triggers in this case)
3. Every new function declared and extended function in the anonymous function's accessible via the plugin object as the container(like DoubleX_RMMV.Item_Triggers.Scene_ItemBase.useItem.apply(sceneItemBasePrototype, arguments) for calling the extended function SIB.useItem and DoubleX_RMMV.Item_Triggers.Game_BattlerBase.execItemTriggers.call(battler, item, timing) for calling the new function GBB.execItemTriggers)

Of course this style also has at least 3 issues:
1. Using call to call a function is at least slightly less performant than directly calling it
2. It makes other plugins calling new or extended functions in mine much, much more painful(just compare DoubleX_RMMV.Item_Triggers.Scene_ItemBase.useItem.apply(this, arguments) with this.useItem() and DoubleX_RMMV.Item_Triggers.Game_BattlerBase.execItemTriggers.call(this, item, timing) with this.execItemTriggers(item, timing))
3. It doesn't allow the use of polymorphism via subtyping.
For example, without this style, I can use that trick this way(DoubleX RMMV Intercept Item):
JavaScript:
BattleManager.successIntercept = function(interceptor, subject, act, item) {
    // Some codes irrelevant with this issue
    //
    if (interceptor.canLearnInterceptItem(item)) {
        // Some codes irrelevant with this issue
        //
    }
    // Some codes irrelevant with this issue
    //
}; // BattleManager.successIntercept

// item: The skill/item to be intercepted
Game_Battler.prototype.canLearnInterceptItem = function(item) {
    return false;
}; // Game_Battler.prototype.canLearnInterceptItem

// item: The skill/item to be intercepted
Game_Actor.prototype.canLearnInterceptItem = function(item) {
    if (!DataManager.isSkill(item)) { return false; }
    if (!item.meta.learnInterceptItem) { return false; }
    return this.interceptNote("learnInterceptItem");
}; // Game_Actor.prototype.canLearnInterceptItem
With this style, that trick no longer works. I'll have to choose either of the below:
JavaScript:
/* interceptor: The battler intercepting the skill/item
* item: The skill/item to be intercepted
*/
II.BattleManager.successIntercept =function(interceptor, item){
    // Some codes irrelevant with this issue
    //
    if (II.Game_Battler.canLearnInterceptItem.call(interceptor, item)) {
        // Some codes irrelevant with this issue
        //
    }
    // Some codes irrelevant with this issue
    //
}; // II.BattleManager.successIntercept

// item: The skill/item to be intercepted
II.Game_Battler.canLearnInterceptItem = function(item) {
    if (!this.isActor() || !DataManager.isSkill(item)) { return false; }
    if (!item.meta.learnInterceptItem) { return false; }
    return II.Game_Battler.interceptNote.call(this, "learnInterceptItem");
}; // II.Game_Battler.canLearnInterceptItem
JavaScript:
/* interceptor: The battler intercepting the skill/item
* item: The skill/item to be intercepted
*/
II.BattleManager.successIntercept =function(interceptor, item){
    // Some codes irrelevant with this issue
    //
    if (interceptor.isActor()) {
        if (II.Game_Actor.canLearnInterceptItem.call(interceptor, item)) {
            // Some codes irrelevant with this issue
            //
        }
    }
    // Some codes irrelevant with this issue
    //
}; // II.BattleManager.successIntercept

// item: The skill/item to be intercepted
II.Game_Actor.canLearnInterceptItem = function(item) {
    if (!DataManager.isSkill(item)) { return false; }
    if (!item.meta.learnInterceptItem) { return false; }
    return II.Game_Battler.interceptNote.call(this, "learnInterceptItem");
}; // II.Game_Actor.canLearnInterceptItem
On a side note: It means an extra checking(whether interceptor is an actor) has to be added now, when it can be replaced by polymorphism via subtying without my new experimental setup.

What do you think about the combination of anonymous function with plugin container? Let's drop your 2 cents here :)
 
Last edited:

eivl

Local Hero
Xy$
0.00
This is a great way to manage potential conflicts in a large project and this is something that jQuery have been doing for a long time now.
If you are going to make something that works in a complex system then this is something to consider.
 
Top