Is a plugin rewriting RMMZ codebase into ES6 standard a good idea?

Is a plugin rewriting RMMZ codebase into ES6 standard a good idea?


  • Total voters
    18

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,555
Reaction score
3,808
First Language
English
(Previously overlooked this part of your reply)I personally agree with this and actually, I'm completely fine with direct ES5 prototyping, but as I saw quite some potential MZ plugin developers having rather strong opinions against sticking with that style for the default RMMZ codebase, I wonder if some of them will so go far to ask their users to use a plugin just to let them conform with the ES6 standard more effectively and efficiently :)
I'll be coding to default codebase. If they want to use an ES6 SDK, unless everyone suddenly decides to use it because some plugin dev makes everything on SDK, all error messages will need to be resolved by the SDK dev.

I won't be explicitly building patches just cause someone wants to type in ES6.
 

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,726
Reaction score
875
First Language
Chinese
Primarily Uses
N/A
I won't be explicitly building patches just cause someone wants to type in ES6.
In my case, it depends on how many potential MZ plugin developers think that it's a good idea.
If most think that it's a bad idea, I'll simply public declare my example as a deprecated and obsolete failure, so those using it are at their own risk(all announced in the plugin itself).
If most think it's a good idea, I'll probably continue to work on this example, and I'll actually expect at least a fellow to help(they'll be credited in the plugin of course and they can even request me to request the plugin users to give them credits), because it should be very clear that it's just too dangerous for a plugin having 30k+ LoC(the default MV codebase has around 34k LoC) to be kept in sync with the default MZ codebase by just 1 plugin developer, and it's likely that at least some of them will understand that.
Of course, if almost all potential MZ plugin developers think it's a good idea but none are going to help, then I deserve to suffer all the consequences from my bad decisions lol
P.S.: Actually, if I first keep a copy of each version of the default MZ codebase, then use a VCS to show the difference between the older version and the latest version, keeping the plugin in sync with the codebase should be feasible, even when it's still tons of work that has to be done quickly :p
 

Zeriab

Huggins!
Veteran
Joined
Mar 20, 2012
Messages
1,264
Reaction score
1,408
First Language
English
Primarily Uses
RMXP
This feels a lot like the RPG Maker XP SDK.
I recommend looking into the history of this thing. Both what it did well and what problems it had, and also why it eventually died out and people stopped working with it.

If you have fun working on this project, by all means continue :D
 

ImaginaryVillain

Resident of Silent Hill... Apparently
Veteran
Joined
Jun 22, 2019
Messages
712
Reaction score
3,697
First Language
Absurdism
Primarily Uses
RMMV
@DoubleX
Honestly I think you should do with this exactly what I did with Community Lighting. Put up a thread with what you've already accomplished. Add a mission statement explaining your goal, and then invite anybody to join in and help. Seemed to work out for Community Lighting, maybe it will work out for you as well. :LZSexcite:
 

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,726
Reaction score
875
First Language
Chinese
Primarily Uses
N/A
This feels a lot like the RPG Maker XP SDK.
I recommend looking into the history of this thing. Both what it did well and what problems it had, and also why it eventually died out and people stopped working with it.
I've briefly googled that and I think it's quite a different thing, as it aims to establish a foundation for high quality xp scripts to improve their compatibility by replacing the default xp codebase, which is just an utterly pathetic mess, with one following a strict coding standard.
As scripts claimed to have written with the SDK needs to be verified by the SDK team, it'll certainly pose at least 2 fatal obstacles:
1. Needless to say, the SDK team will be very overworked if they've to verify the vast number of scripts making such claims, provided that the SDK's indeed that popular
2. A large amount of control and freedom will be taken away from the xp scripters trying to conform with the SDK requirements, and this alone can already scare off many such xp scripters(especially when code quality isn't as prioritized at that time as what's been right now)
Therefore, I'd actually be surprised if it indeed works.

On the other hand, the whole idea of this post and the example is to gives another option for JavaScript programmers not being familiar with ES5 or those avoiding direct prototyping like a plague, and of course I won't even try to verify any of those plugins written with my example in mind, as there's just no need nor advantage to do so.
All the MZ plugin developers need to do is to make a decision between these 3 choices:
1. Use my example to help them write plugins into the ES6 standard more effectively and efficiently
2. Copy the ES6ExtendedClassAlias class from this plugin into theirs and use it to write their own ES6 codes
3. Stick to the outdated but still definitely working ES5 direct prototyping
Because my example's supposed to not break any plugin not written with my example in mind.
All I've to do is to provide a ES6 standard version of the default RMMZ codebase, and keep the former in sync with the latter(I've already tried GitHub Desktop and it's working for me when it comes to detecting codebase changes among versions by setting each version as a branch).
Of course, it doesn't mean the whole idea in this post nor my example's good, but I think my experiment is different enough from the XP SDK that I want to wait for the community response on this(I think I'll make a decision on whether I'll continue several days after MZ's released) :D

@DoubleX
Honestly I think you should do with this exactly what I did with Community Lighting. Put up a thread with what you've already accomplished. Add a mission statement explaining your goal, and then invite anybody to join in and help. Seemed to work out for Community Lighting, maybe it will work out for you as well. :LZSexcite:
I don't think my whole idea here can already go to this step yet, because I still don't know if it'll even work.
On the other hand, the community lighting works because its functionality itself works and the demand of such functionality is already proven to be vast, meaning that many plugin developers will be willing to just jump in quickly, and the community lighting will have many users.
If I try to do what you did in my example, I could risk convincing some potential MZ plugin developers to join, only to find that the whole idea doesn't work, and thus I'd do a very, very great disservice towards those tried to help, and obviously this won't do any good to anyone involved.
In short, I'd rather be on my own and do all the tedious work(actually I can consider asking for help after this idea's proved to be working), than to causing those helping me to have put effort and energy into something that doesn't work at all :)
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,555
Reaction score
3,808
First Language
English
I think if you can get it to work and make it easier for people that refuse to write code if they need to use prototype, might be alright.

I mean, options are never bad.
It's just people have to maintain it, and if it turns out there's some huge bug in the end, how do you mitigate the risks.

Maybe they can just run their code through a converter that "unrolls" their class/extend etc into prototype code.
 

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,726
Reaction score
875
First Language
Chinese
Primarily Uses
N/A
It's just people have to maintain it, and if it turns out there's some huge bug in the end, how do you mitigate the risks.
I'm thinking of making a RMMZ project as a manual test suite of my example(with a list of things to be tested), but I need to make the decision of whether the whole idea in this post's going to work first.
While I've also thought of making an automatic test suite, it seems to me that(ignoring the fact that this will demand tons of extra work) either I still don't know how to write high quality testable code yet, or it's just infeasible to do so in RMMZ without rewriting it from scratch, which will break tons of plugins of course.
So I think my focus on this issue will be on prevention, because it's generally very hard to cure once the symptoms already manifest :)
 

Solar_Flare

Veteran
Veteran
Joined
Jun 6, 2020
Messages
525
Reaction score
230
First Language
English
Primarily Uses
RMMV
On the other hand, the whole idea of this post and the example is to gives another option for JavaScript programmers not being familiar with ES5 or those avoiding direct prototyping like a plague, and of course I won't even try to verify any of those plugins written with my example in mind, as there's just no need nor advantage to do so.
All I've to do is to provide a ES6 standard version of the default RMMZ codebase, and keep the former in sync with the latter(I've already tried GitHub Desktop and it's working for me when it comes to detecting codebase changes among versions by setting each version as a branch).
Maybe they can just run their code through a converter that "unrolls" their class/extend etc into prototype code.
I get the feeling that there might be a big misunderstanding here? I don't think there's any reason to have a version of the MZ codebase converted to use ES6 class syntax. Plugin developers are free to use either ES6 class syntax or the older prototype syntax as they prefer; the two are compatible. In other words, the following will work:

JavaScript:
function Game_Battler() {
    this.initialize.apply(this, arguments);
}

Game_Battler.prototype = Object.create(Game_BattlerBase.prototype);
Game_Battler.prototype.constructor = Game_Battler;

class Game_MyCustomBattler extends Game_Battler {
    constructor() {
        this.initialize.apply(arguments);
    }
};
And so will this:

JavaScript:
class Game_BattlerBase {
    constructor() {
        this.initialize.apply(this, arguments);
    }
};

function Game_Battler() {
    this.initialize.apply(this, arguments);
}

Game_Battler.prototype = Object.create(Game_BattlerBase.prototype);
Game_Battler.prototype.constructor = Game_Battler;
Those aren't very good examples (and they only show straightforward inheritance, not aliasing), but hopefully they get the point across?

All the MZ plugin developers need to do is to make a decision between these 3 choices:
1. Use my example to help them write plugins into the ES6 standard more effectively and efficiently
2. Copy the ES6ExtendedClassAlias class from this plugin into theirs and use it to write their own ES6 codes
3. Stick to the outdated but still definitely working ES5 direct prototyping
Because my example's supposed to not break any plugin not written with my example in mind.
This seems fine to me. Offering a class for people to use to make their aliasing easier is definitely worthwhile. I don't think that maintaining a converted version of the MZ codebase is worth it though, unless you can automate it with a script; and I definitely don't recommend using such a codebase as anything other than a reference.
 

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,726
Reaction score
875
First Language
Chinese
Primarily Uses
N/A
This seems fine to me. Offering a class for people to use to make their aliasing easier is definitely worthwhile. I don't think that maintaining a converted version of the MZ codebase is worth it though, unless you can automate it with a script; and I definitely don't recommend using such a codebase as anything other than a reference.
Agreed, maybe the following's what's most of those writing plugins into ES6 standard will do:
1. Copy the ES6ExtendedClassAlias class into each of their plugins(or into their own core engines)
2. Reference my example instead of the default MZ codebase to help them write ES6 codes more effectively and efficiently(without using it)
This should be feasible because I've explicitly differentiated functions/methods added by me and those being the same as the default MZ codebase :)

But in the MV era, it seems to me that some plugin developers are very, very upset with their(accurate or not) feeling that MV almost never listened to their change requests on the default MV codebase, so if the same happens to MZ, such plugin developers will have some incentives to use my example and some of them might even become a contributor, because my example can be maintained by the community directly(obviously with the strict restriction that no pull requests breaking plugins not written with this example in mind will be even considered).

Personally, I don't have much complaint with the default MV codebase, and I still expect that the MZ one's drastically improved from the former(I haven't read the rest of the MZ codebase yet), but leaving the options of letting some MZ plugin developer use my examples can indeed fulfill those needs, provided that it'll arise again in MZ :D
 

Dr.Yami

。◕‿◕。
Developer
Joined
Mar 5, 2012
Messages
1,003
Reaction score
757
First Language
Vietnamese
Primarily Uses
Other
IMO just go with anything you feel comfortable with. There are many nice features in modern JS (such as arrow functions, spreading, destructuring, const/let...), use at your own benefits :kaojoy:
 

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,726
Reaction score
875
First Language
Chinese
Primarily Uses
N/A
Judging from the community response so far, it seems to me that the number of those not thinking that the whole idea's good's slightly larger than those thinking that it's good(I've made this post on some other forums as well but there are still less than 20 votes in total which isn't much yet), so I'm less and less inclined to continue with my example as an experiment of the whole idea, even though I'll make my decisions several days after the MZ release rather than right now.

However, after reading some parts of the default RMMZ codebase, I'm a bit disappointed with the code qualities of some functions/methods, including those making some plugins hard to add/edit/remove some functionality without rewriting, some obviously redundant allocations and calculations, some blatantly ineffective and inefficient algorithms, and some excessive duplication that can be easily removed by using some ES6 constructs.

So it becomes more and more likely that my example will become some sort of the core engine of my MZ plugins, and that core engine still shouldn't break any plugins not written with that core engine in mind(of course I still welcome any other plugin developer to use my core engine in that case).
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,555
Reaction score
3,808
First Language
English
Do you have examples of things that could be more efficient in ES6, and functions that are hard to alias without rewrite?
 

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,726
Reaction score
875
First Language
Chinese
Primarily Uses
N/A
Do you have examples of things that could be more efficient in ES6, and functions that are hard to alias without rewrite?
For instance:
Code:
BattleManager.onEscapeFailure = function() {
    $gameParty.onEscapeFailure();
    this.displayEscapeFailureMessage();
    this._escapeRatio += 0.1;
    if (!this.isTpb()) {
        this.startTurn();
    }
}
If you want to change the escape ratio increment, you'll be in trouble, even though rewrite isn't absolutely necessary here(like adding this._escapeRatio += this._pluginEscRatioIncrement() - 0.1, but that's clearly undesirable).
I've rewritten this function into this:
Code:
    static onEscapeFailure() {
        $gameParty.onEscapeFailure();
        this.displayEscapeFailureMessage();
        // Edited to help plugins alter escape ratio increment in better ways
        this._escapeRatio += this._escRatioIncrement();
        //
        if (!this.isTpb()) this.startTurn();
    } // onEscapeFailure
   
    /**
     * Nullipotent
     * @author DoubleX @since 0.9.5 @version 0.9.5
     * @returns {number} The party escape attempt success probability increment
     */
    static _escRatioIncrement() { return 0.1; }
Now changing the increment is as simple as returning a different value in _escRatioIncrement :D

Similarly:
Code:
    BattleManager.invokeAction = function(subject, target) {
        this._logWindow.push("pushBaseLine");
        if (Math.random() < this._action.itemCnt(target)) {
            this.invokeCounterAttack(subject, target);
        } else if (Math.random() < this._action.itemMrf(target)) {
            this.invokeMagicReflection(subject, target);
        } else {
            this.invokeNormalAction(subject, target);
        }
        subject.setLastTarget(target);
        this._logWindow.push("popBaseLine");
    } // invokeAction
If there are plugins wanting to make some special cases bypasing cnt and/or mrf, then some workarounds will have to be done.
Currently, itemCnt and itemMrf can be extended to handle such special cases, but that would change the value of cnt and mrf in those cases, when all those plugin want is to bypass them.
This can be problematic if some other plugins use cnt and mrf for any other uses, meaning that there can be unintended compatibility issues.
So I've rewritten this function into this:
JavaScript:
    static invokeAction(subject, target) {
        this._logWindow.push("pushBaseLine");
        // Edited to help plugins alter invoke action behaviors in better ways
        if (this._isInvokeCnt(target)) {
            this.invokeCounterAttack(subject, target);
        } else if (this._isInvokeMrf(target)) {
            this.invokeMagicReflection(subject, target);
        } else this.invokeNormalAction(subject, target);
        //
        subject.setLastTarget(target);
        this._logWindow.push("popBaseLine");
    } // invokeAction

    /**
     * Nullipotent
     * @author DoubleX @since 0.9.5 @version 0.9.5
     * @returns {boolean} Whether the battle should be aborted
     */
    static _isInvokeCnt(target) {
        return Math.random() < this._action.itemCnt(target);
    } // _isInvokeCnt

    /**
     * Nullipotent
     * @author DoubleX @since 0.9.5 @version 0.9.5
     * @returns {boolean} Whether the battle should be aborted
     */
    static _isInvokeMrf(target) {
        return Math.random() < this._action.itemMrf(target)
    } // _isInvokeMrf
Now plugins wanting to add some special cases to bypass cnt and mrf have an even more desirable seam to work with :)

So far it's mainly the TouchInput class, where many functions have the arguments x and y, which are always equal to Graphics.pageToCanvasX(eventTouch.pageX) and Graphics.pageToCanvasY(eventTouch.pageY), meaning that these are redundant duplication due to them representing the identical knowledge.
In ES6, removing these duplication is as simple as making this function:
JavaScript:
    /**
     * Potential Hotspot/Nullipotent
     * @author DoubleX @since 0.9.5 @version 0.9.5
     * @param {Event|Touch} eventTouch - The specified event or its touch
     * @returns {[number]} The converted x and y canvas positions
     */
    static _pageToCanvasXY(eventTouch) {
        return [
            Graphics.pageToCanvasX(eventTouch.pageX),
            Graphics.pageToCanvasY(eventTouch.pageY)
        ];
    } // _pageToCanvasXY
And using it this way for all functions originally repeating Graphics.pageToCanvasX(eventTouch.pageX) and Graphics.pageToCanvasY(eventTouch.pageY) again and again:
JavaScript:
    static _onRightButtonDown(event) {
        // Edited to dry up codes essentially being the identical knowledge
        const xy = this._pageToCanvasXY(event);
        if (Graphics.isInsideCanvas(...xy)) this._onCancel(...xy);
        //
    } // _onRightButtonDown

    static _onMouseMove(event) {
        // Edited to dry up codes essentially being the identical knowledge
        const xy = this._pageToCanvasXY(event);
        if (this._mousePressed) return this._onMove(...xy);
        if (Graphics.isInsideCanvas(...xy)) this._onHover(...xy);
        //
    } // _onMouseMove

    static _onTouchStart(event) {
        const { changedTouches, touches } = event;
        changedTouches.forEach(touch => {
            // Edited to help plugins alter touches inside canvas in better ways
            const xy = this._pageToCanvasXY(touch);
            if (!Graphics.isInsideCanvas(...xy)) return;
            this._onTouchStartInsideCanvas(touches, ...xy);
            //
        });
        /** @todo Extracts this conditional into a well-named static function */
        if (!window.cordova && !window.navigator.standalone) return;
        //
        event.preventDefault();
    } // _onTouchStart

    /**
     * Triggers the specified the left button up event
     * Idempotent
     * @author DoubleX @since 0.9.5 @version 0.9.5
     * @param {Event} event - The specified left button up event
     */
    static _onLeftButtonUp(event) {
        this._mousePressed = false;
        this._onRelease(...this._pageToCanvasXY(event));
    } // _onLeftButtonUp

    /**
     * Triggers the touch move event for the specified touch
     * Potential Hotspot/Idempotent
     * @author DoubleX @since 0.9.5 @version 0.9.5
     * @param {Touch} touch - The specified touch from the specified event
     */
    static _onMoveTouch(touch) { this._onMove(...this._pageToCanvasXY(touch)); }

    /**
     * Triggers the touch release event for the specified touch
     * Idempotent
     * @author DoubleX @since 0.9.5 @version 0.9.5
     * @param {Touch} touch - The specified touch from the specified event
     */
    static _onReleaseTouch(touch) {
        this._screenPressed = false;
        this._onRelease(...this._pageToCanvasXY(touch));
    } // _onReleaseTouch
While the same duplication removal can be done in ES5 using func.apply(this, this._pageToCanvasXY(event)), applying the ES6 spread operator in the form of func(...this._pageToCanvasXY(event)) will make the duplication removal even more worthwhile ;)
Although it's unlikely for any plugin to change these codes from using Graphics.pageToCanvasX(eventTouch.pageX) and Graphics.pageToCanvasY(eventTouch.pageY) to anything else, removing duplication representing identical knowledge is still generally doing more good than harm, especially when anyone being familiar with ES6 should've no problem with the spread operators.
Of course, these are just some examples, and I hope I won't face more and more of these in the rest of the codebase :p
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,555
Reaction score
3,808
First Language
English
You should compile a list of suggested code changes and see if they can merge it with RMMZ before launch. Just small refactors that would make aliasing easier.

Like ya what if I wanted to customize the escape ratio? Explicitly subtracting 0.1 doesn't work if you and I both try to do the same thing cause now we're subtracting 0.2

And if we try to say "oh well let's just have a community plugin that rewrites a bunch of methods to make it easier to alias" now we have XP SDK all over again.
 

Solar_Flare

Veteran
Veteran
Joined
Jun 6, 2020
Messages
525
Reaction score
230
First Language
English
Primarily Uses
RMMV
However, after reading some parts of the default RMMZ codebase, I'm a bit disappointed with the code qualities of some functions/methods, including those making some plugins hard to add/edit/remove some functionality without rewriting, some obviously redundant allocations and calculations, some blatantly ineffective and inefficient algorithms, and some excessive duplication that can be easily removed by using some ES6 constructs.
I've definitely noticed a number of things in the RMMV codebase that are quite hard to customize. The codebase is also very hostile to localization - it concatenates strings together, and it also substitutes the names of objects (skills, items, etc) into formatted strings, making correct localization pretty much impossible for certain target languages (anything with some form of grammatical agreement rules).

Do you have examples of things that could be more efficient in ES6, and functions that are hard to alias without rewrite?
Here's another example of something that's really hard to customize (from MV):

JavaScript:
Game_Actor.prototype.levelUp = function() {
    this._level++;
    this.currentClass().learnings.forEach(function(learning) {
        if (learning.level === this._level) {
            this.learnSkill(learning.skillId);
        }
    }, this);
};
What if you want to add more prerequisites to certain skills? For example, maybe you have items that grow ATK and you need a certain ATK before you can learn one of the skills. You'd have to rewrite the function to something like this:

JavaScript:
Game_Actor.prototype.levelUp = function() {
    this._level++;
    this.currentClass().learnings.forEach(function(learning) {
        if (learning.level <= this._level && learning.meta.atk <= this.atk) {
            this.learnSkill(learning.skillId);
        }
    }, this);
};
(That's illustrative but won't actually work on its own, since learnings don't have a meta attribute. It'll work if you use SFG_Utils.)

If it had instead been implemented like this, it would be possible to get that effect without rewriting the whole function:

JavaScript:
Game_Actor.prototype.levelUp = function() {
    this._level++;
    this.currentClass().learnings.forEach(function(learning) {
        if (this.canLearnSkillOnLevelUp(learning)) {
            this.learnSkill(learning.skillId);
        }
    }, this);
};

Game_Actor.prototype.canLearnSkillOnLevelUp = function(learning) {
    return learning.level === this._level;
};
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,555
Reaction score
3,808
First Language
English
Oh ya that learning thing has really annoyed me forever. I had to do exactly that to create class learning conditions in Ace. Hasn't changed since...

We should all complain.
 

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,726
Reaction score
875
First Language
Chinese
Primarily Uses
N/A
I think I'll do both to play safe.
When the MZ corescript's exposed in GitHub that accepts pull requests(just like what the MV corescript's doing), I'll try to make some pull requests suggesting some codebase quality improvements.
I've even thought of opening yet another post to focus on the default RMMZ codebase issues and proposed improvements, but I don't want to give the wrong impression that the RMMZ implementations' just an utterly pathetic mess(and thus possibly harm the sales before MZ's even released), so I'm still thinking whether I'll make such a post before we can formally make pull requests in GitHub.
Meanwhile, I'll likely continue to work with my rewritten MZ codebase, because I'm not optimistic on having many accepted pull requests improving codebase quality, and I can actually understand that:
1. They've to ensure none of those pull requests will break any plugin
2. They've something else even more important to do(fixing bugs, improving performance, correcting documentations, developing new features, etc)
On a side note: I've not totally given up the idea of having a version of the default RMMZ codebase written in the ES6 standard yet :D

On the root cause of many problems I've found in the MZ codebase, I think it's because of not respecting the abstraction levels, and this causes at least the following problems:
1. Some codes are less readable than they can be, because you don't know what those implementation details are trying to achieve. For instance -
JavaScript:
Less readable codes mixing different abstraction levels
Input._updateGamepadState = function(gamepad) {
    const lastState = this._gamepadStates[gamepad.index] || [];
    const newState = [];
    const buttons = gamepad.buttons;
    const axes = gamepad.axes;
    // Exposing these implementation details makes this function less readable
    const threshold = 0.5;
    newState[12] = false;
    newState[13] = false;
    newState[14] = false;
    newState[15] = false;
    for (let i = 0; i < buttons.length; i++) {
        newState[i] = buttons[i].pressed;
    }
    if (axes[1] < -threshold) {
        newState[12] = true; // up
    } else if (axes[1] > threshold) {
        newState[13] = true; // down
    }
    if (axes[0] < -threshold) {
        newState[14] = true; // left
    } else if (axes[0] > threshold) {
        newState[15] = true; // right
    }
    //
    for (let j = 0; j < newState.length; j++) {
        if (newState[j] !== lastState[j]) {
            const buttonName = this.gamepadMapper[j];
            if (buttonName) {
                this._currentState[buttonName] = newState[j];
            }
        }
    }
    this._gamepadStates[gamepad.index] = newState;
};
//

// More readable codes respecting abstraction levels more
    static _updateGamepadState(gamepad) {
        const { index, buttons, axes } = gamepad;
        const lastState = this._gamepadStates[index] || [];
        // Edited to help plugins alter new gamepad states in better ways
        this._gamepadStates[index] = this._newGamepadStates(buttons, axes);
        //
        this._gamepadStates[index].forEach((ns, i) => {
            if (ns === lastState[i]) return;
            const buttonName = this.gamepadMapper[i];
            if (buttonName) this._currentState.set(buttonName, ns);
        });
    } // _updateGamepadState

        /**
     * Hotspot/Nullipotent
     * @author DoubleX @since 0.9.5 @version 0.9.5
     * @enum @param {[GamepadButton]} buttons - The list of the gamepad buttons
     * @enum @param {[number]} axes - The amounts of the gamepad axes directions
     * @returns {[boolean]} The list of new gamepad button states
     */
    static _newGamepadStates(buttons, axes) {
        // They're hidden behind a well-named function to increase readability
        const [newState, threshold] = [[], 0.5];
        newState[12] = newState[13] = newState[14] = newState[15] = false;
        buttons.forEach((button, i) => newState[i] = button.pressed);
        if (axes[1] < -threshold) {
            newState[12] = true; // up
        } else if (axes[1] > threshold) newState[13] = true; // down
        if (axes[0] < -threshold) {
            newState[14] = true; // left
        } else if (axes[0] > threshold) newState[15] = true; // right
        return newState;
        //
    } // _newGamepadStates

2. Some codes have bundled what they're trying to do with some particular implementation details, because there are no seams to edit those implementation details without rewriting them all. For instance -
JavaScript:
// Codes with wrong abstraction levels
BattleManager.processTurn = function() {
    const subject = this._subject;
    const action = subject.currentAction();
    if (action) {
        // It bundles the "process turn with action" case with these implementation details
        action.prepare();
        if (action.isValid()) {
            this.startAction();
        }
        subject.removeCurrentAction();
        //
    } else {
        // It bundles the "process turn without action" case with these implementation details
        this.endAction();
        this._subject = null;
        //
    }
};
//

// Codes with right abstraction levels
    static processTurn() {
        const subject = this._subject, action = subject.currentAction();
        // Edited to help plugins alter process turn behaviors in better ways
        if (action) return this._procTurnWithAct(action);
        this._procTurnWithoutAct();
        //
    } // processTurn

    /**
     * Processes the battle turn with an action to be executed
     * @author DoubleX @since 0.9.5 @version 0.9.5
     * @param {Game_Action} action - The action to be executed
     */
    static _procTurnWithAct(action) {
        // These implementation details are no longer bundled with processTurn
        action.prepare();
        if (action.isValid()) this.startAction();
        subject.removeCurrentAction();
        //
    } // _procTurnWithAct

    /**
     * Processes the battle turn without an action to be executed
     * @author DoubleX @since 0.9.5 @version 0.9.5
     */
    static _procTurnWithoutAct() {
        // These implementation details are no longer bundled with processTurn
        this.endAction();
        this._subject = null;
        //
    } // _procTurnWithoutAct
//

P.S.: Many readable codes generally have some of the following forms:
JavaScript:
function abstractionLevelX() {
    abstractionLevelXMinus1Func1();
    abstractionLevelXMinus1Func2();
    abstractionLevelXMinus1Func3();
    abstractionLevelXMinus1Func4();
    abstractionLevelXMinus1Func5();
    abstractionLevelXMinus1Func6();
    abstractionLevelXMinus1Func7();
    abstractionLevelXMinus1Func8();
}
JavaScript:
function abstractionLevelX() {
    if (abstractionLevelXMinus1Func1()) {
        abstractionLevelXMinus1Func2();
    } else if (abstractionLevelXMinus1Func3()) {
        abstractionLevelXMinus1Func4();
    } else {
        abstractionLevelXMinus1Func5();
    }
}
JavaScript:
function abstractionLevelX() {
    abstractionLevelXMinus1Func1();
    abstractionLevelXMinus1Func2().forEach(abstractionLevelXMinus1Func3);
    abstractionLevelXMinus1Func4();
}
JavaScript:
function abstractionLevelX() {
    try {
        abstractionLevelXMinus1Func1();
    } catch (err) {
        abstractionLevelXMinus1Func2(err);
    } finally {
        abstractionLevelXMinus1Func3(err);
    }
}
JavaScript:
function abstractionLevelX() {
    this._var1.abstractionLevelXMinus1Func1();
    this._var1 = abstractionLevelXMinus1Func2();
    this._var1.abstractionLevelXMinus1Func3();
}
Of course it's by no means exhaustive, and sometimes those codes can end up having worse code quality because of such forms, but the point remains to "program to interfaces, not implementations" as the big direction.
If the default MZ codebase tried harder to respecting abstraction levels, I think the codebase quality will improve even more drastically, even though writing ideal codes isn't always feasible and it'll definitely cost those devs more time and thus more budget even when it's indeed feasible(meaning that the ROI might not be worth it for them) :)
 
Last edited:

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,555
Reaction score
3,808
First Language
English
I wouldn't worry about sales. It's better to get these changes in before launch.

Otherwise we end up all just doing our own thing.

Splitting up a method into multiple methods generally should be much easier to test especially if they're intended to be private methods or at least not referenced anywhere else.
 

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,726
Reaction score
875
First Language
Chinese
Primarily Uses
N/A
With RMMZ released for a while, it seems to me that there's not much demand on applying the ES6 standard to RMMZ, so I decided to drop this idea.
However, those wanting to continue on this whole idea can freely take over my failed experiment :)
 

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Posts

Latest Profile Posts

if you want learn free cool stuff for game dev .
Your jumping works great, self. Now onward! Let's add to some of those dungeon tilesets that are looking a tad sparse.
Gotta vent a minute: "Move routes" have been unintuitive and clunky since the 90's and I absolutely cannot believe there is no built-in functionality to direct an event to an x, y coordinate. Thank you for coming to my TED talk.
I watched a local Amish community hold a cart and buggy presidential rally that passed by my worksite this afternoon and it really got me thinking...how do the Amish keep up with national news?
trying to wrap up the boat/ship/castaway tilesets with a great galleon - no promises

Forum statistics

Threads
103,106
Messages
997,364
Members
134,577
Latest member
ProDudeFilms
Top