DOUBLEX'S PROFILE

Just a nameless weakling who's incredibly nub at everything

Search

DoubleX Music Room

I never thought even I could make some music, but I don't know if mine would be too unpleasant to hear, so it takes me some courage to share them here :)

DoubleX - Everyone Turning Against You

[RMMZ]Basic knowledge to the default RMMZ TPBS battle flow implementations

This topic aims to share the basic knowledge on what the default RMMZ TPBS battle flow implementations do in general, but you're still assumed to have at least:
1. Some plugin development proficiency(having written several easy, simple and small battle-related plugins up to 1k LoC scale)
2. Basic knowledge on what the default RMMZ turn based battle flow implementations do in general
3. Basic knowledge on what the default RMMZ TPBS battle flow does in general on the user level(At least you need to know what's going on on the surface when playing it as a player)

Simplified Flowchart

Please note that this flowchart only includes the most important elements of the battle flow, to avoid making it too complicated and convoluted for the intended targeting audience ;)

Battle Start
It's almost exactly the same as that in the turn based counterpart, so the phase will change to "start" after finished starting the battle.

Input Action Slots
It's actually quite similar to that in the turn based counterpart, except that:
1. As nothing other than inputting action slots can happen during the "input" phase in the turn based counterpart, the exact input sequence is always fixed there, whereas in the TPBS some party members can become inputable or uninputable at anytime, especially when it comes to active TPBS.
2. At any given frame in TPBS, players are still supposed to start from inputting the 1st action slot of the 1st inputable party member to inputting the last action slot of the last party member.
3. If players are inputting the action slots of a party member, and then they cancel inputting all those action slots of of that party member, among all the other inputable party members, those whose party member indices are greater rather than less than the previously inputting one will be selected first, and the party command window will be setup in case no other inputable party member exists.
This is because BattleManager.selectPreviousCommand will call BattleManager.selectPreviousActor, which will still call BattleManager.changeCurrentActor with argument forward as true, which is the same as what BattleManager.selectNextActor, which is called by BattleManager.selectNextCommand, does:
BattleManager.selectPreviousCommand = function() {
    if (this._currentActor) {
        if (this._currentActor.selectPreviousCommand()) {
            return;
        }
        this.cancelActorInput();
    }
    this.selectPreviousActor();
};
4. Also, unlike the turn based counterpart, if another inputable party member is selected for inputting actions due to cancelling inputs of another inputable party member, the newly selected one will still input his/her/its 1st action slot first, then proceed the same sequence until the last action slot is inputted.
5. As a corollary, once an inputable party member has inputted all his/her/its action slots, there's no way to cancel those input because he/she/it'll proceed to casting and then executing those actions, and this is unlike the turn based counterpart, where players can cancel party members who've inputted all their action slots, as long as the phase is still "input".
6. In the turn based counterpart, the only way to activate or deactivate any input window is by the ok and cancel commands issued by players, whereas in TPBS this can also be affected by whether the currently inputting party member becomes not inputable, due to Scene_Battle.prototype.changeInputWindow, which will only be called if some input windows need to be activated/deactivated:
Scene_Battle.prototype.changeInputWindow = function() {
    this.hideSubInputWindows();
    if (BattleManager.isInputting()) {
        if (BattleManager.actor()) {
            this.startActorCommandSelection();
        } else {
            this.startPartyCommandSelection();
        }
    } else {
        this.endCommandSelection();
    }
};
In short, other than hiding the skill, item, actor and enemy windows:
- If there are inputable party members and 1 of them becomes selected to input action slots, the actor command window will be setup with the status window selecting that actor
- If there are inputable party members and all of them become not selected to input action slots, the party command window will be setup with the status window deselected
- If there are no more inputable party members, all the input windows will be closed and the status window will be deselected
Bear in mind that how the above points work in details are too advanced to be covered here.

Thinking In Frames
Unlike the default turn based battle system, thinking in frames are vital even in just trying to have a basic knowledge on what the default RMMZ TPBS battle flow implementations do in general, especially when it comes to active TPBS, so the flowchart is drawn quite differently from the turn based counterpart.
To be able to think in frames, one first need to know the starting point of a frame and all the possible ending points of that frame, then one can sequentially grasp the summary of each path, until a vague impression of a frame can be formed.
To make the task even easier, simpler and smaller, one can first try to briefly read the codes without thinking about active TPBS, which is more complicated and convoluted, especially when it comes to edge cases that are hard but still possible to reach.
When one becomes familiar with thinking in frames, he/she should be able to at least partially simulate what a frame does in general in his/her mind, and eventually roughly visualize the TPBS battle flow implementations mentally.

Frame Start
A frame starts from Scene_Battle.prototype.update, which is a vital part of the scene life cycle(too advanced to be covered here):
Scene_Battle.prototype.update = function() {
    const active = this.isActive();
    $gameTimer.update(active);
    $gameScreen.update();
    this.updateVisibility();
    if (active && !this.isBusy()) {
        this.updateBattleProcess();
    }
    Scene_Message.prototype.update.call(this);
};
Then Scene_Battle.prototype.updateBattleProcess will be called to use the result of Scene_Battle.prototype.isTimeActive as the argument of BattleManager.update, which is being called immediately afterwards:
Scene_Battle.prototype.updateBattleProcess = function() {
    BattleManager.update(this.isTimeActive());
};
Scene_Battle.prototype.isTimeActive = function() {
    if (BattleManager.isActiveTpb()) {
        return !this._skillWindow.active && !this._itemWindow.active;
    } else {
        return !this.isAnyInputWindowActive();
    }
};
BattleManager.update = function(timeActive) {
    if (!this.isBusy() && !this.updateEvent()) {
        this.updatePhase(timeActive);
    }
    if (this.isTpb()) {
        this.updateTpbInput();
    }
};
Because of Scene_Battle.prototype.isTimeActive, the active TPBS will keep the TPB running unless the skill or item window's active, while the non-active TPBS will only keep the TPB running when there are no active input windows(party or actor command, or skill, item, actor or enemy window), meaning that there are no inputable party members.
(On a side note: Strictly speaking, the way the TPBS battle flow's implemented won't let plugin developers change the active TPBS to keep the TPB running even when battlers are executing actions, unless those plugin developers rewrite the whole TPBS from scratch, but these details are way, way too advanced and complex to be elaborated here)
BattleManager.isBusy and BattleManager.updateEvent will be called to only call BattleManager.updatePhase when the TPB can technically keep running(the details of these underlying technical limitations are way, way too advanced and complex to be elaborated here):
BattleManager.isBusy = function() {
    return (
        $gameMessage.isBusy() ||
        this._spriteset.isBusy() ||
        this._logWindow.isBusy()
    );
};
BattleManager.updateEvent = function() {
    switch (this._phase) {
        case "start":
        case "turn":
        case "turnEnd":
            if (this.isActionForced()) {
                this.processForcedAction();
                return true;
            } else {
                return this.updateEventMain();
            }
    }
    return this.checkAbort();
};
BattleManager.updatePhase = function(timeActive) {
    switch (this._phase) {
        case "start":
            this.updateStart();
            break;
        case "turn":
            this.updateTurn(timeActive);
            break;
        case "action":
            this.updateAction();
            break;
        case "turnEnd":
            this.updateTurnEnd();
            break;
        case "battleEnd":
            this.updateBattleEnd();
            break;
    }
};
While Game_Message.prototype.isBusy and Spriteset_Battle.prototype.isBusy are self-explanatory enough, Window_BattleLog.prototype.isBusy is a lot more complicated and convoluted(too advanced to be covered here), but it's still about whether the TPB needs to stop to let the visual coordination running, like the battle log, animations, battler sprites, etc.
The main function of interest inside BattleManager.updateEvent is BattleManager.updateEventMain(too advanced to be covered here), and what makes it interesting here is that it'll check whether the battle needs to end by checking whether it's aborted, victorious or defeated, and will change the phase to "battleEnd" if any of those conditions are met.
As for BattleManager.updatePhase, it's mainly about picking the function to call according to the current phase of the battle, while the argument timeActive is the result of Scene_Battle.prototype.isTimeActive.

Start Phase
There's not much in this phase, as all BattleManager.updateStart does in TPBS is to change to phase to "turn":
BattleManager.updateStart = function() {
    if (this.isTpb()) {
        this._phase = "turn";
    } else {
        this.startInput();
    }
};

Turn Phase
The "turn" phase is the majority of the difference between the TPBS battle flow and the turn based counterpart.
First, BattleManager.updateTurn will be called to use the argument timeActive as the result of Scene_Battle.prototype.isTimeActive to determine if BattleManager.updateTpb should be called as well:
BattleManager.updateTurn = function(timeActive) {
    $gameParty.requestMotionRefresh();
    if (this.isTpb() && timeActive) {
        this.updateTpb();
    }
    if (!this._subject) {
        this._subject = this.getNextSubject();
    }
    if (this._subject) {
        this.processTurn();
    } else if (!this.isTpb()) {
        this.endTurn();
    }
};
BattleManager.updateTpb = function() {
    $gameParty.updateTpb();
    $gameTroop.updateTpb();
    this.updateAllTpbBattlers();
    this.checkTpbTurnEnd();
};
Assuming that timeActive is true -
Now, Game_Unit.prototype.updateTpb will be called to call Game_Battler.prototype.updateTpb for all battlers:
Game_Battler.prototype.updateTpb = function() {
    if (this.canMove()) {
        this.updateTpbChargeTime();
        this.updateTpbCastTime();
        this.updateTpbAutoBattle();
    }
    if (this.isAlive()) {
        this.updateTpbIdleTime();
    }
};
So, if a battler can move, he/she/it'll update the TPB and action casting bars, as well as start casting all the autobattle actions that are just made in case he/she/it's in Auto Battle.
If he/she/it's alive, he/she/it'll update the idle TPB bar as well.
If his/her/its TPB becomes fully charged, he/she/it'll become available for inputting action slots.
If his/her/its action casting bar becomes full, he/she/it'll become available for executing valid actions.
BattleManager.updateAllTpbBattlers will call BattleManager.updateTpbBattler for all battle members:
BattleManager.updateTpbBattler = function(battler) {
    if (battler.isTpbTurnEnd()) {
        battler.onTurnEnd();
        battler.startTpbTurn();
        this.displayBattlerStatus(battler, false);
    } else if (battler.isTpbReady()) {
        battler.startTpbAction();
        this._actionBattlers.push(battler);
    } else if (battler.isTpbTimeout()) {
        battler.onTpbTimeout();
        this.displayBattlerStatus(battler, true);
    }
};
First, if the turn of the battler involved becomes ended, the old turn will be ended and the new one will be started here, with the latest battler status displayed on the battle log window.
Second, if the battler involved becomes available for executing actions, that battler will be pushed into the back of the action execution subject queue, so later BattleManager.updateTurn can call BattleManager.getNextSubject to pickup that battler to be the action execution subject.
Third, if the battler involved has become idled for so long that a turn has passed, that battler will be in the new battler turn, with the latest battler status displayed on the battle log window.
BattleManager.checkTpbTurnEnd will be covered in "Turn End Phase".
Regardless of whether BattleManager.updateTpb is called, the rest of BattleManager.updateTurn is exactly the same as the turn based counterpart.

Action Phase
It's almost the same as the turn based counterpart, as least when only the battle flow is concerned.

Turn End Phase
It's quite similar to the "turn" phase in TPBS, except that, after calling BattleManager.checkTpbTurnEnd, if Game_Troop.prototype.isTpbTurnEnd returns true, BattleManager.endTurn will be called to change the phase to "turnEnd" as well:
BattleManager.checkTpbTurnEnd = function() {
    if ($gameTroop.isTpbTurnEnd()) {
        this.endTurn();
    }
};
Game_Troop.prototype.isTpbTurnEnd = function() {
    const members = this.members();
    const turnMax = Math.max(...members.map(member => member.turnCount()));
    return turnMax > this._turnCount;
};
BattleManager.endTurn = function() {
    this._phase = "turnEnd";
    this._preemptive = false;
    this._surprise = false;
};
Note that this doesn't always mean that the phase at the next frame will be "turnEnd", because as shown in the flowchart, it's still possible that BattleManager.startAction will be called to change the phase to "action" before proceeding to the next frame(the proof of this possibility is too advanced to be covered here), meaning that the battle turn count can trigger later than expected, and thus potentially surprising effects on the subsequent action executions before all the queued action execution subjects have executed all their valid actions.

Battle End Phase
It's exactly the same as the turn based counterpart as well, since BattleManager.updateBattleEnd is the absolute last stop of both of the battle flows:
BattleManager.updateBattleEnd = function() {
    if (this.isBattleTest()) {
        AudioManager.stopBgm();
        SceneManager.exit();
    } else if (!this._escaped && $gameParty.isAllDead()) {
        if (this._canLose) {
            $gameParty.reviveBattleMembers();
            SceneManager.pop();
        } else {
            SceneManager.goto(Scene_Gameover);
        }
    } else {
        SceneManager.pop();
    }
    this._phase = "";
};
Note that SceneManager here is to change the scene from Scene_Battle to something else:
1. Exits the game in the case of battle test
2. Goes to the last scene(the one before this battle) if it's not a game over
3. Goes to the game over scene(Scene_GameOver)

Update TPB Input
It's always run at the end of a frame in TPBS, regardless of what the current phase of the battle is.
Basically, if there's at least 1 inputable party members, BattleManager.updateTpbInput will call BattleManager.checkTpbInputClose, otherwise it'll call BattleManager.checkTpbInputOpen:
BattleManager.updateTpbInput = function() {
    if (this._inputting) {
        this.checkTpbInputClose();
    } else {
        this.checkTpbInputOpen();
    }
};
BattleManager.checkTpbInputClose = function() {
    if (!this.isPartyTpbInputtable() || this.needsActorInputCancel()) {
        this.cancelActorInput();
        this._currentActor = null;
        this._inputting = false;
    }
};
BattleManager.checkTpbInputOpen = function() {
    if (this.isPartyTpbInputtable()) {
        if (this._tpbNeedsPartyCommand) {
            this._inputting = true;
            this._tpbNeedsPartyCommand = false;
        } else {
            this.selectNextCommand();
        }
    }
};
In the case of running BattleManager.checkTpbInputClose, it's to void the currently inputting party member(the one whose action slots are being inputted by players) and the party inputability flag if the currently inputting party member becomes not inputable(BattleManager.isPartyTpbInputtable is mainly for handling edge cases here).
In the case of BattleManager.checkTpbInputOpen, the gist is that(the details are too advanced to be covered here), when at least 1 of the party members become inputable, the party inputability flag will be raised if it's the 1st time the party becomes inputable(to show the party command window instead of the actor command window), otherwise BattleManager.selectNextCommand will be called:
BattleManager.selectNextCommand = function() {
    if (this._currentActor) {
        if (this._currentActor.selectNextCommand()) {
            return;
        }
        this.finishActorInput();
    }
    this.selectNextActor();
};
While the exact mechanism of raising the inputability flag and setting up the actor command window are too advanced to be covered here, the point is that BattleManager.selectNextActor will call BattleManager.changeCurrentActor, which will call BattleManager.startActorInput, and thus raise the inputability flag if the players are already inputting the action slots of an inputable party member.

Summary
First, the battle will start, and the phase will change to "start" upon fully starting the battle, with the catch that this phase will only trigger once, which is at the start of the battle.
Then, the phase will quickly change to "turn".
After that, all battlers will charge their TPB, and will become inputable when theirs become full.
In the case of actors, the party command window will be setup for the 1st time such event triggers in this battle, otherwise the actor command window corresponding to the inputable party member with the smallest party index at that frame will be setup.
Whenever a battler becomes restricted, his/her/its TPB and cast bars will be cleared.
Players will input from the 1st action slot of the 1st inputable party member to the last action slot of the last inputable party member at any given frame.
Whenever the party becomes to have at least 1 inputable party member, the actor command window will be setup if an actor's selected for inputting action slots, otherwise the party command window will be setup.
Whenever the party becomes to have no inputable party members, all the input windows will be closed.
When battlers finish inputting all their action slots, they'll start casting those actions, until they're fully cast, and this will cause those battlers to be pushed at the back of the action execution subject queue.
As long as no actions are already executing, the most up front battler in that queue will be picked up as the action execution subject to execute will be cast valid actions, and the phase will be changed to "action".
When that action execution subject has executed all those cast valid actions, that battler will have the TPB and cast bars emptied, and the above process of picking up new action execution subject will be repeated, until there are no more battlers available as action execution subjects, in which the phase will be changed to "turn".
If a battle turn's supposed to be ended, the phase will be changed to "turnEnd", but it'll be immediately changed to "action" at the same frame if there are still action execution subjects to execute actions.
If a battle's supposed to be ended, the phase will be changed to "battleEnd", and the scene will be changed from the battle scene to something else, followed by changing the phase to empty.

That's all for now. I hope this can help you grasp these basic knowledge. For those thoroughly comprehending the essence of the default RMMZ TPBS battle flow implementations, feel free to correct me if there's anything wrong :)
For those wanting to have a solid understanding to the default RMMZ TPBS battle flow implementations, I might open a more advanced topic for that later :D

[RMMZ]Basic knowledge to the default RMMZ turn based battle flow implementations

This topic aims to share the basic knowledge on what the default RMMZ turn based battle flow implementations do in general, but you're still assumed to have at least:
1. Little javascript coding proficiency(barely okay with writing rudimentary Javascript codes up to 300 LoC scale)
2. Basic knowledge on what the default RMMZ turn based battle flow does on the user level(At least you need to know what's going on on the surface when playing it as a player)

Simplified Flowchart

Please note that this flowchart only includes the most important elements of the battle flow, to avoid making it too complicated and convoluted for the intended targeting audience :)

Start Battle
This is the phase handling the start of the battle(with the empty phase only being run once), until BattleManager.startInput will be called to change the phase to "input".
To keep things simple here, only the parts that are common to all the 3 different ways(battle tests, normal encounters and event encounters) of starting battles will be covered.
The common parts start from BattleManager.setup, which initializes the battle phase as empty in BattleManager.initMembers:
BattleManager.initMembers = function() {
    this._phase = "";
    this._inputting = false;
    this._canEscape = false;
    this._canLose = false;
    this._battleTest = false;
    this._eventCallback = null;
    this._preemptive = false;
    this._surprise = false;
    this._currentActor = null;
    this._actionForcedBattler = null;
    this._mapBgm = null;
    this._mapBgs = null;
    this._actionBattlers = [];
    this._subject = null;
    this._action = null;
    this._targets = [];
    this._logWindow = null;
    this._spriteset = null;
    this._escapeRatio = 0;
    this._escaped = false;
    this._rewards = {};
    this._tpbNeedsPartyCommand = true;
};
Then, regardless of how the battles are started, either of the following will be called to start the battle scene:
SceneManager.goto(Scene_Battle);
SceneManager.push(Scene_Battle);
At the beginning of the scene life cycle(too advanced to be covered here), Scene_Battle.prototype.create will be called to call Scene_Battle.prototype.createDisplayObjects as well, which calls Scene_Battle.prototype.createAllWindows:
Scene_Battle.prototype.createAllWindows = function() {
    this.createLogWindow();
    this.createStatusWindow();
    this.createPartyCommandWindow();
    this.createActorCommandWindow();
    this.createHelpWindow();
    this.createSkillWindow();
    this.createItemWindow();
    this.createActorWindow();
    this.createEnemyWindow();
    Scene_Message.prototype.createAllWindows.call(this);
};
Here, Scene_Battle.prototype.createPartyCommandWindow and Scene_Battle.prototype.createActorCommandWindow are methods creating the party and actor command windows respectively, and these 2 are the windows that are parts of the battle flow.
As the scene life cycle reaches Scene_Battle.prototype.start, BattleManager.startBattle will be called to change the phase to "start":
BattleManager.startBattle = function() {
    this._phase = "start";
    $gameSystem.onBattleStart();
    $gameParty.onBattleStart(this._preemptive);
    $gameTroop.onBattleStart(this._surprise);
    this.displayStartMessages();
};
When the events that are supposed to be run upon battle start are finished(how this is exactly done is too advanced to be covered here), Scene_Battle.prototype.update will call Scene_Battle.prototype.updateBattleProcess, which will call BattleManager.update, causing BattleManager.updatePhase to be called as well(frame updates in battle flow are too advanced to be covered here), and so BattleManager.updateStart will be called to call BattleManager.startInput, in order to change the phase to "input", meaning that the battle is finally fully started:
BattleManager.startInput = function() {
    this._phase = "input";
    this._inputting = true;
    $gameParty.makeActions();
    $gameTroop.makeActions();
    this._currentActor = null;
    if (this._surprise || !$gameParty.canInput()) {
        this.startTurn();
    }
};

Input Actions
This phase starts from BattleManager.startInput and ends with BattleManager.startTurn, and it's the phase where players can either try to escape the battle, or input all actions for all inputable party members, until BattleManager.startTurn is called to change the phase to "turn", or BattleManager.processAbort is called to abort the battle.
First, Game_Unit.prototype.makeActions will be called, causing Game_Battler.prototype.makeActions to be called to make all battlers have their respective number of action slots determined by their respective Action Times+ at that moment:
Game_Battler.prototype.makeActions = function() {
    this.clearActions();
    if (this.canMove()) {
        const actionTimes = this.makeActionTimes();
        this._actions = [];
        for (let i = 0; i < actionTimes; i++) {
            this._actions.push(new Game_Action(this));
        }
    }
};
Note that Auto Battle and/or confused actors will automatically input all their action slots without player inputs because of Game_Actor.prototype.makeActions:
Game_Actor.prototype.makeActions = function() {
    Game_Battler.prototype.makeActions.call(this);
    if (this.numActions() > 0) {
        this.setActionState("undecided");
    } else {
        this.setActionState("waiting");
    }
    if (this.isAutoBattle()) {
        this.makeAutoBattleActions();
    } else if (this.isConfused()) {
        this.makeConfusionActions();
    }
};
Whereas all enemies will always automatically input all their action slots:
Game_Enemy.prototype.makeActions = function() {
    Game_Battler.prototype.makeActions.call(this);
    if (this.numActions() > 0) {
        const actionList = this.enemy().actions.filter(a =>
            this.isActionValid(a)
        );
        if (actionList.length > 0) {
            this.selectAllActions(actionList);
        }
    }
    this.setActionState("waiting");
};
As for inputability, an actor's inputable if he/she/it's alive, in battle and has no Restrictions enforced by states and no special flag Auto Battle.
As long as the party can input actions in the current turn(determined by whether it's the 1st turn with a surprise battle start and whether any party member can input actions), this phase will proceed as follows:
1. Scene_Battle.prototype.changeInputWindow will call Scene_Battle.prototype.startPartyCommandSelection(its reasons are too advanced to be covered here), which setups the party command window:
Scene_Battle.prototype.changeInputWindow = function() {
    this.hideSubInputWindows();
    if (BattleManager.isInputting()) {
        if (BattleManager.actor()) {
            this.startActorCommandSelection();
        } else {
            this.startPartyCommandSelection();
        }
    } else {
        this.endCommandSelection();
    }
};
Scene_Battle.prototype.startPartyCommandSelection = function() {
    this._statusWindow.deselect();
    this._statusWindow.show();
    this._statusWindow.open();
    this._actorCommandWindow.setup(null);
    this._actorCommandWindow.close();
    this._partyCommandWindow.setup();
};
2a. If those players choose to escape, then Scene_Battle.prototype.commandEscape as the corresponding handler of the party command window(handlers are too advanced to be covered here) will be called to call BattleManager.processEscape.
The escape attempt will always succeed upon the 1st turn if the battle starts with preemptive, otherwise its probability of success is determined by the escape ratio at that moment, which is initially set as 0.5 * the average of agi of all party members in battle / the average of agi of all enemies in battle, and will increase by 0.1 per failed attempt.
2a(i). If the escape attempt succeeded, then BattleManager.onEscapeSucces will be called to call BattleManager.processAbort, which calls BattleManager.endBattle which the result argument as 1 to abort the battle(how battles are ended are too advanced to be covered here):
BattleManager.processAbort = function() {
    $gameParty.removeBattleStates();
    this._logWindow.clear();
    this.replayBgmAndBgs();
    this.endBattle(1);
};
2a(ii). If the escape attempt failed, then BattleManager.onEscapeFailure will be called to call BattleManager.startTurn, which starts the current turn and will be covered in the later parts of this post:
BattleManager.onEscapeFailure = function() {
    $gameParty.onEscapeFailure();
    this.displayEscapeFailureMessage();
    this._escapeRatio += 0.1;
    if (!this.isTpb()) {
        this.startTurn();
    }
};
Note that Game_Party.prototype.onEscapeFailure will call Game_Actor.prototype.onEscapeFailure for all party members, and so Game_Actor.prototype.clearActions will be called to void all action slots:
Game_Actor.prototype.onEscapeFailure = function() {
    if (BattleManager.isTpb()) {
        this.applyTpbPenalty();
    }
    this.clearActions();
    this.requestMotionRefresh();
};
Game_Actor.prototype.clearActions = function() {
    Game_Battler.prototype.clearActions.call(this);
    this._actionInputIndex = 0;
};
2b. If those players choose to fight, then Scene_Battle.prototype.commandFight use select next command(covered in the later parts of this post) to let those players input all actions of all inputable party members sequentially.
3. Those players will first input the 1st action slot of the 1st inputable party member, then the 2nd, 3rd, and finally the last action slot of that member, and those players will proceed with the same sequence for the 2nd inputable party member, finally those players will repeat this sequence until the last action slot of the last inputable party member is inputted, with the restriction that those players can never break nor escape this sequence without inputting all action slots of all inputable party members.
4. When inputting actions for inputable party members, players can use the ok command to proceed to the next action slot of the current member, or from the last action slot of that member to the 1st action slot of the next member(or start the current turn upon finish inputting the last action slot of the last member).
The ok command is handled by the actor command window(how it's setup is too advanced to be covered here) using the handler Scene_Battle.prototype.selectNextCommand, which calls BattleManager.selectNextCommand to call Game_Actor.prototype.selectNextCommand:
Scene_Battle.prototype.selectNextCommand = function() {
    BattleManager.selectNextCommand();
    this.changeInputWindow();
};
BattleManager.selectNextCommand = function() {
    if (this._currentActor) {
        if (this._currentActor.selectNextCommand()) {
            return;
        }
        this.finishActorInput();
    }
    this.selectNextActor();
};
Game_Actor.prototype.selectNextCommand = function() {
    if (this._actionInputIndex < this.numActions() - 1) {
        this._actionInputIndex++;
        return true;
    } else {
        return false;
    }
};
5. Similarly, players can use the cancel command to proceed to the previous action slot of the current member, or from the 1st action slot of that member to the last action slot of the previous member(or setup the party command window upon cancelling the input of the 1st action slot of the 1st member).
The cancel command is handled by the actor command window using the handler Scene_Battle.prototype.selectPreviousCommand, which calls BattleManager.selectPreviousCommand to call Game_Actor.prototype.selectPreviousCommand:
Scene_Battle.prototype.selectPreviousCommand = function() {
    BattleManager.selectPreviousCommand();
    this.changeInputWindow();
};
BattleManager.selectPreviousCommand = function() {
    if (this._currentActor) {
        if (this._currentActor.selectPreviousCommand()) {
            return;
        }
        this.cancelActorInput();
    }
    this.selectPreviousActor();
};
Game_Actor.prototype.selectPreviousCommand = function() {
    if (this._actionInputIndex > 0) {
        this._actionInputIndex--;
        return true;
    } else {
        return false;
    }
};
6. While the exact traversals of this doubly linked list are too advanced to be covered here, the party command window(head sentinel node) and the turn start(tail sentinel node) are the start and end of this sequence respectively, while the ok and cancel commands are single steps for moving forward(next pointer) and backward(previous pointer) respectively.

Process Turns
To be precise, there are 2 phases in this part, which are "turn" and "turnEnd".
The "turn" phase starts from having BattleManager.startTurn called upon inputting the last action slot of the last inputable party member, and ends with calling BattleManager.startAction to change the phase to "action", or BattleManager.endTurn to change the phase to "turnEnd":
BattleManager.startTurn = function() {
    this._phase = "turn";
    $gameTroop.increaseTurn();
    $gameParty.requestMotionRefresh();
    if (!this.isTpb()) {
        this.makeActionOrders();
        this._logWindow.startTurn();
        this._inputting = false;
    }
};
This changes the phase to "turn" and increases the battle turn count by 1.
Then, by calling BattleManager.makeActionOrders, the action order queue descendingly sorted by the speed of all battlers(the faster the battlers are the more up front they're on this queue) at this moment will have all the inputted actions of all those battlers, with the ordering of all actions among the same battler unchanged from his/her/its action slot input sequence:
BattleManager.makeActionOrders = function() {
    const battlers = [];
    if (!this._surprise) {
        battlers.push(...$gameParty.battleMembers());
    }
    if (!this._preemptive) {
        battlers.push(...$gameTroop.members());
    }
    for (const battler of battlers) {
        battler.makeSpeed();
    }
    battlers.sort((a, b) => b.speed() - a.speed());
    this._actionBattlers = battlers;
};
Note that the speed of a battler is determined by the fastest action slot inputted by that battler(the skill/item with the lowest Speed).
After that, all battle events which should be run upon turn start will be run(how this is exactly done is too advanced to be covered here), and then BattleManager.updatePhase will call BattleManager.updateTurn:
BattleManager.updateTurn = function(timeActive) {
    $gameParty.requestMotionRefresh();
    if (this.isTpb() && timeActive) {
        this.updateTpb();
    }
    if (!this._subject) {
        this._subject = this.getNextSubject();
    }
    if (this._subject) {
        this.processTurn();
    } else if (!this.isTpb()) {
        this.endTurn();
    }
};
If there's no action execution subject, BattleManager.getNextSubject will be used as a try to find the most up front alive battler in the action order queue at that moment, with all the dead ones removed from the queue before an alive one is found, and having found such a battler means that battler will be removed from the queue as well:
BattleManager.getNextSubject = function() {
    for (;;) {
        const battler = this._actionBattlers.shift();
        if (!battler) {
            return null;
        }
        if (battler.isBattleMember() && battler.isAlive()) {
            return battler;
        }
    }
};
If no such battler's found, then the phase will change to "turnEnd" by calling BattleManager.endTurn(which will be covered in the later parts of this post).
If such a battler's found, he/she/it'll be the new action subject, and BattleManager.processTurn will be called to try to execute inputted actions of that battler:
BattleManager.processTurn = function() {
    const subject = this._subject;
    const action = subject.currentAction();
    if (action) {
        action.prepare();
        if (action.isValid()) {
            this.startAction();
        }
        subject.removeCurrentAction();
    } else {
        this.endAction();
        this._subject = null;
    }
};
An action is said to be valid upon execution if it's forced or its user meets all the conditions of the corresponding skill/item upon execution.
If the action execution subject has no valid actions to execute, then BattleManager.endAction will be called(this will be covered in the later parts of this post), which will cause BattleManager.updateTurn to be called again later, meaning that another action execution subject has to be found, or the turn will just end.
If the action execution subject has valid actions to execute, then all the invalid ones will be discarded and the valid ones will be executed sequentially, by calling BattleManager.startAction, which makes the target queue of the current action to be executed and changes the phase to "action":
BattleManager.startAction = function() {
    const subject = this._subject;
    const action = subject.currentAction();
    const targets = action.makeTargets();
    this._phase = "action";
    this._action = action;
    this._targets = targets;
    subject.cancelMotionRefresh();
    subject.useItem(action.item());
    this._action.applyGlobal();
    this._logWindow.startAction(subject, action, targets);
};
The "turnEnd" phase starts from having BattleManager.endTurn called upon having no more action execution subject to be found nor execute valid actions not yet executed, and ends with later calling BattleManager.updatePhase, which calls BattleManager.updateTurnEnd to change the phase to "start":
BattleManager.endTurn = function() {
    this._phase = "turnEnd";
    this._preemptive = false;
    this._surprise = false;
};
BattleManager.updateTurnEnd = function() {
    if (this.isTpb()) {
        this.startTurn();
    } else {
        this.endAllBattlersTurn();
        this._phase = "start";
    }
};

Execute Actions
It basically starts from BattleManager.startAction and ends with BattleManager.endAction to change the phase to "turn".
It's the phase where the current action execution subject will, after discarding all the invalid actions, execute all the valid ones sequentially, which the ordering unchanged from the action slot input sequence of that subject.
As far as only the current action execution subject is concerned, this phase will proceed as follows:
1. The 1st valid action that aren't executed yet will be executed after showing its start using the battle log window(how Window_BattleLog works is too advanced to be covered here).
2. Upon the start of the execution, BattleManager.updatePhase will call BattleManager.updateAction, which tries to find the 1st target in the target queue of the action to be executed:
BattleManager.updateAction = function() {
    const target = this._targets.shift();
    if (target) {
        this.invokeAction(this._subject, target);
    } else {
        this.endAction();
    }
};
If such target's found, that target will be removed from the queue and BattleManager.invokeAction will be called:
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");
};
For now, you just need to know that Window_BattleLog is way more than just a battle log window, as it actually coordinates all the visual display sequences in the battle flow(also too advanced to be covered here).
If no such target's found, then BattleManager.endAction will be called to change the phase to "turn":
BattleManager.endAction = function() {
    this._logWindow.endAction(this._subject);
    this._phase = "turn";
    if (this._subject.numActions() === 0) {
        this.endBattlerActions(this._subject);
        this._subject = null;
    }
};

Summary
The battle first starts with the empty phase.
The empty phase will always change to "start" after finished preparing to start the battle.
The "start" phase will always change to "input" after finished starting the battle to try to let players input actions for all inputable party members.
All battlers will use their respective Action Times+ at that moment to determine their respectively number of action slots they can have in the current turn.
If there's no such member or the battle starts with surprise, the battle phase will immediately change to "turn".
Otherwise players will either try to escape the battle, or sequentially input from the 1st action slot of the 1st inputable party member to the last one of the last such member.
A sucess escape attempt will abort the battle while a failed one will change the phase to "turn" without inputting any action slot of any inputable party member.
The battle phase will change to "turn" when all actions slots of all inputable party members are inputted.
The "turn" phase will try to sequentially execute actions from the 1st inputted valid ones of the fastest alive battler at that moment to the last inputted valid ones of the slowest alive battler at that moment.
Whenever the 1st inputted valid action not executed yet by the current action execution subject is to be executed now, the phase will change to "action".
When there are no more alive battlers left to execute valid actions that aren't executed yet, the phase will change to "turnEnd", which will soon change to "start".
The "action" phase will try to sequentially invoke the currently valid action to be executed into each of its targets and remove that target from the action target queue.
When that action target queue has no more targets, the phase will change to "turn".

That's all for now. I hope this can help you grasp these basic knowledge. For those thoroughly comprehending the essence of the default RMMZ turn based battle flow implementations, feel free to correct me if there's anything wrong :D
For those wanting to have a solid understanding to the default RMMZ turn based battle flow implementations, I might open a more advanced topic for that later ;)

Beware of scams like this...(pretending to be Paypal)

I just received an email like this:
Title: Notification Case #(Some random numbers)
Sender: (Non-Paypal logo)service@paypal.com.(My PayPal account location) <(Non-PayPal email used by the real scammers)>
Recipients: (My email), (The email of an innocent straw man used by the real scammers)
Contents(With UI styles copying those in real PayPal emails):
Someone has logged into your account
We noticed a new login with your PayPal account associated with (The email of an innocent straw man used by the real scammers) from a device we don't recognize. Because of that we've temporarily limited your account until you renew and verify your identity.
Please click the button below to login into your account for verify your account.
(Login button copying that in real Paypal emails)
If this was you, please disregard this email.
(Footers copying those in real PayPal emails)

I admit that I'm incredibly stupid, because I almost believed that it's a real PayPal email, and I only realized that it's a scam right after I've clicked the login button, because it links to a URL that's completely different from the login page of the real PayPal(so fortunately I didn't input anything there).
While I've faced many old-schooled phishing emails and can figure them all out right from the start, I've never seen phishing emails like this, and what makes me feel even more dumb is that I already have 2FA applied to my PayPal account before receiving this scam email, meaning that my phone would've a PayPal verification SMS out of nowhere if there was really an unauthorized login to my account.

Of course, that straw man email owner is completely innocent, and I believe that owner already received the same scam email with me being the straw man, so that owner might think that I really performed unauthorized login into his/her PayPal account, if he/she didn't realize that the whole email's just a scam.
Before I realized that it's just a scam, I thought he/she really done what the email claims as well, so I just focused on logging into my PayPal accounts to assess the damages done and evaluate countermeasures to be taken, and if I didn't realize that it's just a scam, I'd already have given the password of my PayPal account to the scammers in their fake PayPal login page.

I suspect that many more PayPal users might have already received/are going to receive such scam emails, and I think this way of phishing can work for many other online payment gateways as well, so I think I can do some good by sharing my case, to hope that only I'll be this dumb(even though I didn't give the scammers my Paypal password at the end).

What can be done to make RMMZ have overwhelming positive reviews?

While RMMZ has already released for about a month, the reviews are still mixed(64% positive among 225 reviews), at least according to Steam(I failed to find any other quantified reviews of comparable significance yet).
Although the overall score shows a slight upward trend overall, it still doesn't seem to look good, at least compared to RMMV, where it received 108 positives and 27 negatives, netting a 80% score, with a much more obvious upward trend later on.

The most obvious thing that can be done for those having bought RMMZ via Steam, is to give a positive review with solid reasons, like what I've done quite a while ago :)
But, maybe more can be done to further boost the review scores legitimately, apart from another obvious thing that resource makers can continue to make more and more awesome resources for the community :D

I've also briefly checked the NEGATIVE REVIEWS FROM STEAM, and here are the summary of all the points I've collected(which is by no means exhaustive):
1. The base price of RMMZ is too expensive compared to what it added and improved over RMMV(But they'd be positive for RMMZ if there will be a big sale)
2. RMMZ isn't even RMMVA, but just RMMV 1.7, because everything "new" in RMMZ are already provided by plugins in RMMV
3. RMMZ improved too little from RMMV, meaning that the RM franchise has become trapped in the past
4. The 3D effekseer doesn't work well with the RMMZ animations, because it's a 2D engine
5. The RMMZ RTP is worse than that of RMMV(images are ugly, audios are unpleasant to listen to, etc), and the former isn't compatible with the latter
6. There are still some unnecessarily hardcoded assumptions, like the tileset size still being fixed to 48
7. RMMZ doesn't work well when the screen size isn't 816 * 624(1280 * 720 can already cause the side view battler sprites to be mis-positioned to the left of the screen), like, ugly scaling of battler sprites
8. The RMMZ character generator still sucks so hard that the generated adults look like kids
9. There's no way to make porting RMMV animations to RMMZ an easy, simple and small task, because of effekseer
10. RMMZ still doesn't make good use of multiple CPU cores nor the GPU, causing inferior performance even with the Pixi v5 upgrades and other performance optimizations over RMMV
11. RMMZ only lets you use 4 map layers, which is far from being enough
12. The hardcoded limit of 2000 items per database type isn't high enough
13. The sprites are still hardcoded to using 3 frame per action
14. There's no way to make exporting to mobile platforms an easy, simple and small task, when RMMZ is advertised for optimized for such platforms
15. Many features that are provided by RMMZ plugins should've been done by RMMZ itself
16. The audio loading still isn't optimized in RMMZ, causing large audio files to have too long to load(and this issue already exists in RMMV and is well-known by the community for years)
17. The amount of resources including by the RTP still isn't large enough, and should've been much, much larger
18. While this engine advertise itself as easy enough for a child, it's not the case when you've to learn effekseer, which is a must to use RMMZ
19. An exported project can be as big as way more than 200MB in RMMZ, while similar projects in RMMV would've been just around 50MB, and 50MB is already too much
20. Sometimes the engine can just suddenly crash, like when clicking edit on a show text event, causing unexpected loss of progresses
21. The default RMMZ JavaScript codebase is so poorly-documented that documentation there is effectively nonexistent
22. The RMMZ is obviously very, very unpolished and just rushed for launch, and the current state of RMMZ is clearly an incomplete product
23. In general, the RMMZ plugins aren't as generous as the RMMV counterparts, because of the pricing, terms of use, and source code accessibility, and RMMZ is nothing without plugins
24. Enemy skill animations aren't displayed in front view battles
25. The battler sprites aren't animated enough, and they're behaving like static ones(no shadows, floating motions, etc)
26. RMMZ just isn't worth it before it has as many plugins as that of RMMV
27. Many improvements in RMMZ should've been implemented in RMMV
28. RMMZ should let you use either effekseer or the traditional RMMV like animations
29. The RM franchise has been working in the form of buying the engine with the use of many scripts/plugins from the same scripter/plugin developer(without him/her RM would be nothing), with tweaks of those scripts/plugins made, and some other scripts/plugins(that are compatible to those made by that particular scripter/plugin developer) from the other scripters/plugin developers to make games, but this formula no longer works in RMMZ, because of the successor of that particular scripter/plugin developers start making closed-source plugins in RMMZ(and some of them are paid too)​
Please note that I'm just trying to summarize the points I've collected from negative Steam reviews, and it has nothing to do with whether I agree or disagree with any of them.

So, I'd like to open this post to collect ideas on some possible ways to make RMMZ to have overwhelming positive reviews, and this doesn't have to be limited to Steam, because I think later on there will be some other quantified reviews of comparable significance ;)

[RMMV] Testing The Idea On Revealing Some Original Codes From The Obfuscated Version

​Disclaimer: This post doesn't mean that I support the idea of releasing plugins with obfuscated implementation codes, and in fact I'm personally against it in most cases(but my feeling on this isn't the point of this post at all), it's just that there might be some really extreme and rare cases making this approach at least a considerable backup plan for some plugin developers(but making this move should still never be taken lightly).

I've obfuscated 1 of my RMMV plugins(an advanced complex battle system plugin with way more than 3K LoC of implementation codes), which isn't published yet and is an older version, and I've posted it in my github repository: RMMV Plugin Obfuscation Test
Please feel free to deobfuscate this plugin(and please submit a pull request with your fully deobfuscated version), as it's a very, very old and outdated version anyway, and the current version I'm working on already has much, much more contents than that highly deprecated version(and it's still far from being complete).
I've tried to deobfuscate this obfuscated version using several online deobfuscators, but none of them can fully deobfuscate it, even though they do help quite a bit, so I don' think deobfuscating the whole thing all at once will be an easy, simple and small task(unless you're a deobsucation experts of course).

The supposed workflow of revealing parts of codes is as follows:
1. Users asks plugin developers to make some tunes and tweaks on the plugins but failed to do so(goes to 2)
2. If such tunes and tweaks can be done by editing parameters/configurations/notetags/script calls/plugin commands, then teach those users to do so, otherwise go to 3
3. If these tunes and tweaks are(after considering all factors) worthwhile to be supported by the plugins directly, then upgrade the plugins to support them(or at least announce such plans), otherwise go to 4
4. If these tunes and tweaks can be independent from the plugins, then teach those users to make those workarounds, otherwise go to 5
5. Gives the minimum amount of relevant codes(minified then deminified) to the users, and ensures those codes won't work without the rest of the plugins
Then scammers trying to steal the whole thing will have to ask for tunes and tweaks for tons of different plugin parts very quickly, and this will make their true nature very obvious, especially when it's clear that they aren't even taking time to actually implement their tunes and tweaks with the help of those revealed codes(plugin developers can also setup a policy to restrict the same user to ask, say, only 1 small group of revealed code parts per day per plugin or even per week per plugin).

I've also added 3 examples on some users requesting some parts of the plugins to be tuned and tweaked, even though I know that real plugins really using this approach will have way more than 3 groups of revealed code parts:
Add Delay Between Becoming Able To Act And Actually Inputting Actions
Change ATB Bar Window Opacity And Padding
Set The Actor And Enemy Starting ATB Values
I've tried to use them to help further deobfuscating what's already deobfuscated by online deobfuscators, but I can't even recognize which parts of the still highly deobfuscated implementation codes correspond to the revealed original versions(being minified then deminified before being revealed), so again, I feel that deobfuscating the whole thing will still be complicated and convoluted, even with the help of these revealed codes(please exclude the deobfuscation experts here).

Please STRICTLY FOCUS ON THESE 2 ASPECTS:
1. Does this approach let users effectively and efficiently tune and tweak the parts of the plugins they wanted, and what obstacles will they face if the reality isn't as ideal as it should be(possibly other than the cheap one time payment/periodic subscription needed to have such accesses)?
2. Will this approach help a scammer deobfuscate the whole plugin implementation codes, or at least enough parts of them to make a standalone plugin that can make profits way more than what those scammers costed(I'm specifically excluding the case of a highly coordinated team of scammers with many teammates)?
But not anything involving the pros and cons/rights and wrongs of obfuscating plugin implementation codes, because I've made this example as a little experiment to test whether this idea can indeed let users tune and tweak the parts of the plugins they want while still stopping scammers from stealing plugins, and I don't want this post to be severely derailed or even closed :)

Unable To Submit RPG Maker MZ Scripts

In short, the submission demands the Category to be selected, but now there's no category and thus nothing to be selected, causing submissions of RPG Maker MZ plugins to be impossible right now :)

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

I think such a plugin has at least the following potential advantages:
1. Those avoiding direct prototyping like a plague can work with an ES6 version
2. Those not being familiar with ES5 don't have to learn this outdated approach, especially when it's just for writing RMMZ plugins
3. As this plugin can be directly maintained by the RMMZ plugin developer community, it might address their needs more effectively and efficiently(as long as such changes won't break plugins not written with this plugin in mind)

And at least the following potential disadvantages:
1. This plugin will need to be kept in sync with the default RMMZ codebase changes, which can be very complicated and convoluted for the plugin developers involved to detect all the changes in the latter(while letting the plugin users/other plugin developers know if the plugin's outdated is an easy, simple and small task)
2. This can further "encourage and recommend" ES5 vs ES6 flame wars among some plugin developers(and can be a serious community trouble if it becomes a widespread drama), as some will write plugins in the ES6 standard with this plugin and some will continue to write ES5 with direct prototyping without this plugin
3. Some plugin users touching plugin implementations will have to deal with both ES5 and ES6 styles, as some plugins will be written in the former and some will be written in the latter(or even mixed styles in the same plugin in some rare extreme cases)

To make this post more effective and efficient, I've used nearly 35 hours in 3 days to write 1 such plugin(0.9.5 with just the core parts).
While it might break some plugins not written with this plugin in mind despite the fact that I've checked about that several times already, I want the discussion to focus on whether THIS DIRECTION AS A WHOLE is a good idea, so I hope it's not so poorly written that it'll defeat its purpose in this post :)

[RMMZ]Using ES6 Class Inheritance With Extensions For Aliasing Without Direct Prototyping

With ES6 classes, trying to extend the parent class in plugins can lead to its children still using the old parent codes.
For instance, you can place this in a JavaScript sandbox and verify the console output yourselves:
// Default RMMZ codebase
class DefaultMZParentClass {

    method() { return "Parent method"; }

}

class DefaultMZChildClass extends DefaultMZParentClass {

    method() { return `${super.method()} Child method`; }

}
//

// Plugin codebase
const PluginAParentClass = DefaultMZParentClass;
DefaultMZParentClass = class extends DefaultMZParentClass {

    method() { return `Plugin ${super.method()}`; }

};
//

// It's "Parent method Child method" but should be "Plugin Parent method Child method"
console.info("new DefaultMZChildClass().method()", new DefaultMZChildClass().method());
//

So normally, you'll still have to directly type prototypes:
// Default RMMZ codebase
class DefaultMZParentClass {

    method() { return "Parent method"; }

}

class DefaultMZChildClass extends DefaultMZParentClass {

    method() { return `${super.method()} Child method`; }

}
//

// Plugin codebase
const parentProto = DefaultMZParentClass.prototype, pluginAMethod = parentProto.method;
parentProto.method = function() { return `Plugin ${pluginAMethod.call(this)}`; };
//

// It's "Plugin Parent method Child method" which is the intended output
console.info("new DefaultMZChildClass().method()", new DefaultMZChildClass().method());
//


But I wanted to offer an alternative for those not being familiar with ES5 or avoiding prototypes like a plague despite the fact that many RMMZ codes are written that way, and this is what I've come up with:
/*
 * Do these 2 additional things when using ES6 class inheritance aliasing
 * without directly typing prototypes:
 * 1. Add the following code right below a new class inheriting another one:
 *    - ExtendedClassAlias.inherit(Klass);
 *    Where Klass is the new class inheriting another one
 * 2. Add the following code right below extending an existing class as a way
 *    to alias its methods:
 *    - ExtendedClassAlias.updateClass(Klass);
 *    Where Klass is the existing class being extended as a way to alias its
 *    methods
 * Right now it doesn't work well with inheriting static functions in classes,
 * so those in children classes should use ParentClass.staticFunc.call(this)
 * instead of super.staticFunc()
 */

// Codes allowing ES6 class inheritance aliasing without direct prototyping
class ExtendedClassAlias {

    static inherit(Child) {
        const childProto = Child.prototype;
        const parentName = Object.getPrototypeOf(childProto).constructor.name;
        this._inherit(Child, parentName);
    }

    static updateClass(Parent) {
        const parentName = Parent.prototype.constructor.name;
        // There's no need to update anything if the passed class's no children
        if (!this._inheritances.has(parentName)) return;
        this._updateClass(this._inheritances.get(parentName), Parent);
        //
    }

    static _inherit(Child, parentName) {
        // So the parent class will know which classes are its children
        if (this._inheritances.has(parentName)) {
            const oldChildProtos = this._inheritances.get(parentName);
            const newChildProtos = oldChildProtos.concat([Child]);
            this._inheritances.set(parentName, newChildProtos);
        } else {
            this._inheritances.set(parentName, [Child]);
        }
        //
    }

    static _updateClass(children, Parent) {
        this._updateProtoMethods(children, Parent.prototype);
        this._updateStaticFuncs(children, Parent);
    }

    static _updateProtoMethods(children, parentProto) {
        // So all the children will inherit the new rather than the old parent
        children.forEach(Child => Child.prototype.__proto__ = parentProto);
        //
    }

    static _updateStaticFuncs(children, Parent) {
        // So all children will inherit all new static functions from new parent
        Object.getOwnPropertyNames(Parent).forEach(name => {
            const desc = Object.getOwnPropertyDescriptor(Parent, name);
            if (!desc || typeof desc.value !== "function") return;
            children.forEach(Child => {
                Child[name] = Child[name] || Parent[name];
            });
        });
        //
    }

}
ExtendedClassAlias._inheritances = new Map();
//

I've tested that it works for adding new instance variables and prototype methods in base classes, and extending and overriding existing prototype methods there(you can place the linked snippet into a JavaScript sandbox and verify the console output yourselves).
While I failed to inherit the changes in the static functions of the base classes from plugins as well, this can be easily mitigated by using BaseClass.staticFunc.call(this) instead of super.staticFunc().

Basically, the essence of the issue when aliasing ES6 class inheritance without direct prototyping is this:
1. The original child class inherits the original base class
2. A plugin extends the original base class to alias some of its prototype methods
3. The child class still inherits the original base class
So to solve this, simply store the linkage between the child class and the base class right after creating that child class, then points the parent of the child class to the extended base class right after extending it.

As for the static functions in classes, while I tried to use the linkage to let the exiting children class use the new static functions from the extended parent class, I failed to cover the case for aliasing existing parent class static functions, because it's just impossible:
1. The super in the static functions of the child class always points to the original parent class
2. The super in the static functions of the extened parent class always points to the original parent class
3. The super in the static functions of the child class's supposed to always point to the extended parent class
Clearly, combining 1 and 2 will contradict with 3, which is the goal I've trying to achieve.

For those not being familiar with ES5 or avoiding prototypes like a plague, I hope using ExtendedClassAlias won't be too demanding for you, as all you need to do is sticking to these:
/*
 * Do these 2 additional things when using ES6 class inheritance aliasing
 * without directly typing prototypes:
 * 1. Add the following code right below a new class inheriting another one:
 *    - ExtendedClassAlias.inherit(Klass);
 *    Where Klass is the new class inheriting another one
 * 2. Add the following code right below extending an existing class as a way
 *    to alias its methods:
 *    - ExtendedClassAlias.updateClass(Klass);
 *    Where Klass is the existing class being extended as a way to alias its
 *    methods
 * Right now it doesn't work well with inheriting static functions in classes,
 * so those in children classes should use ParentClass.staticFunc.call(this)
 * instead of super.staticFunc()
 */

P.S.: I've spent almost 24 hours on this and I enjoyed the process a lot, even though this might not be practical enough to be used in MZ :)

[SCRIPTING]Will you deliberately write uncommon bad codes with strange poor designs to make plagiarisms easier to prove?

Disclaimer: Plagiarisms should never ever be countered by this idea alone, as this idea, even if it's to be used, should be used along with some other plagiarism counters, otherwise one would fall into the "security through obscurity" application pitfall(but not the idea itself).

While in most cases, only those having high popularity and excellent reputations will have to worry about plagiarisms on a regular basis, sometimes even a dumb noob like me can be a victim on this(and unfortunately a long time ago some of my free plugins are sold by someone else without my permissions, even when it was such a blatant scam that I don't think anyone will really lose a penny there).
So I've read how some of the best and most successful plugin developers deal with plagiarisms, and I noticed that sometimes some uncommon bad codes and strange poor designs in some plugins made by the original plugin developers can actually help proving the plagiarisms, as I've seen a plugin developer listing some of them as evidences.

While I don't think those bad codes/poor designs are intentional(they're likely due to the desire/need to make tons of plugins very quickly), that case of that plugin developer inspired me to think about whether it'd be at least considerable to intentionally write uncommon bad codes with strange poor designs to make plagiarisms easier to prove(of course better implementation alternatives shouldn't be obvious nor easy/simple to swap in or it'd only stop those trying to hide their plagiarisms but are too lazy/stupid to do it well).
The reasoning behind this practice is that, while there won't be many different ways to do the exact same thing and it's likely to fall into the very same common programming pitfalls, if a scammer plagiarizes a plugin using tons of uncommon bad codes and strange poor designs without removing all those rare programming pitfalls, it'd be an easy, simple and small task to prove that it's indeed plagiarism, or at least the case would be so probable that it can be assumed as plagiarisms until proven otherwise(sometimes even beyond reasonable doubt), all due to the fact that making the same rare programming pitfalls when trying to do the same things is effectively a black swan.
On a side note: This also implies that, sometimes prioritizing plugin development speed over code quality can surprisingly help countering plagiarisms, as scammers will be harder to hide their plagiarisms in those unintended cases.

As a bonus, the plugins written this way can also be so apparently(but not actually if you know the "secret") cryptic and fragile that others can't change anything meaningful without breaking at least something else significant(but not so cryptic that plugin users can't report meaningful errors), so those trying to hide their plagiarisms will instead keep encountering serious bugs after serious bugs, therefore they'll eventually just give up plagiarizing or change to plagiarizing without hiding(and will therefore be caught very quickly).
On the other hand, the plugin developers can keep some private test suites and documentation files(of course maintaining them needs tons of extra work) that can help them work well with those apparently cryptic and fragile codes, and let the sufficiently trustworthy fellows to have somewhat controlled, limited and recorded accesses on them in case they've to solve some compatibility issues and need such accesses.

Of course, in order to keep the negative side-effects under control, at least the following conditions must be met:
1. The plugins will still keep working well on the user level without much nontrivial bugs that can't be fixed quickly(it needs the plugin developers to keep tons of codes inside their heads)
2. The plugin developers will still keep working with the plugins effectively and efficiently despite the extremely low code qualities(private test suites can help a lot here in the long term)
3. At least some fellow plugin developers will still want to address compatibility issues with said plugins even though it'd be a hellish nightmare in general(provided that those compatibility issues must be addressed on their sides)
Otherwise those plugins won't be plagiarized not because doing so would be too hard without being caught, but because there would be so few users using them that it's simply not worth plagiarizing them.

Also, bad junior programmers like me, those having next to no plugin users or those not being harassed by plagiarisms that are hard to prove shouldn't even think about actually doing this, because:
1. If you're not at least about to be a seasoned professional software engineer, it's almost certain that deliberately writing uncommon bad codes with strange poor designs will shoot yourself in your foot so hard that your plugins won't even work at all
2. If you've next to no plugin users, it probably means your popularity and reputations are so low that nearly no scammer will think that your plugins will be worth plagiarizing(there will always be exceptions though and I'm one of the few), because even the most brazen plagiarisms demand "actual work" in the views of those scammers(otherwise no one would be shameless enough to sell others' plugins without permissions using "charging for searching services" as a pathetic excuse)
3. If all you've faced are obvious plagiarisms, then the whole idea here will only do harm to everyone except the scammers and no good to anyone, as those scammers would just continue to make obvious plagiarisms anyway
In short, the whole approach might only payoff for very proficient plugin developers having tons of plugins, popularity, reputations and users, especially when they're already being severely harassed by plagiarisms that are all very hard to prove otherwise.

While I feel bad for those being harassed by plagiarisms that are hard to prove on a regular basis, I'd like to know what you'll think about incorporating this idea with your existing plagiarism counters, as I'm wondering whether this additional option can indeed further help stopping such plagiarisms :)
Pages: first 12 next last