RMMZ [RPG Maker MZ] Where do those Area-of-Effect Damage delays come from?

Raith

Squire
Veteran
Joined
Oct 28, 2019
Messages
79
Reaction score
64
First Language
Indonesian
Primarily Uses
RMMZ
The default RM MZ still has the same, head-scratching Area-of-Effect damage delay problem, already seen before in previous RPG Maker versions: if you use skill with All Enemies targeting and hit all enemies on the battlefield, the damage will be executed to the enemies one by one instead all at once. That few hundred milliseconds of significant delay between damage executions are small, but may ruin the immersion and battle pace in some games.

So, I'm trying to understand the function that determine the length of delay, and finally create plugin that removes that delay.

The problem is, I'm stuck on finding that root cause of delay. My search stopped in the meaning of this.updateLastTarget(target) in the following code:

JavaScript:
Game_Action.prototype.apply = function(target) {
    const result = target.result();
    this.subject().clearResult();
    result.clear();
    result.used = this.testApply(target);
    result.missed = result.used && Math.random() >= this.itemHit(target);
    result.evaded = !result.missed && Math.random() < this.itemEva(target);
    result.physical = this.isPhysical();
    result.drain = this.isDrain();
    if (result.isHit()) {
        if (this.item().damage.type > 0) {
            result.critical = Math.random() < this.itemCri(target);
            const value = this.makeDamageValue(target, result.critical);
            this.executeDamage(target, value);
        }
        for (const effect of this.item().effects) {
            this.applyItemEffect(target, effect);
        }
        this.applyItemUserEffect(target);
    }
    this.updateLastTarget(target);

};

How it was updated? How they set the value between target update? How the "apply" function is executed?

From there, I can't trace back where that delay come from, or why it was made.
Looks like there's some array/sequence execution ahead that code that I don't understand.

So, anyone can give me idea/clue? Any help will be appreciated

PS: And no, I won't use external plugins. Thank you. I'm using like active three dozens of self-made plugins. Any external plugin will mostly creates headache than helps.

Disclaimer: I actually don't know where to post this, since the plugin doesn't have any form yet, making it ineligible to ask/publish in "Plugin In Development" section. Hence I post this here.
 

Andar

Veteran
Veteran
Joined
Mar 5, 2013
Messages
32,369
Reaction score
8,092
First Language
German
Primarily Uses
RMMV
the damage will be executed to the enemies one by one instead all at once.
that is intentional, which is why it was never changed in any maker.
in all cases before there have been scripts or plugins that gave the option to change that behaviour (mostly for animation), but basically all skills that hit multiple targets are turned into muiltiple single-hits because that is neccessary to handle the damage formula correctly - it does usually include the different target defenses after all.

if you want to change that behaviour, you'll first have to think of what you want to use to replace it. Because there is a reason why previous scripts and plugins made that change optional:
As long as the damage formula may contain different target values and especially if you want to allow full javascript code options inside the damage formula (like checking if a target has a state and using different calculations based on the results), you have to allow processing the target damage calculation once per target on group targets.
 

TheoAllen

Self-proclaimed jack of all trades
Veteran
Joined
Mar 16, 2012
Messages
6,137
Reaction score
7,328
First Language
Indonesian
Primarily Uses
RMVXA
if you use skill with All Enemies targeting and hit all enemies on the battlefield, the damage will be executed to the enemies one by one instead all at once. That few hundred milliseconds of significant delay between damage executions are small, but may ruin the immersion and battle pace in some games.
Before we start solving this problem. Let me ask you a question.
If we have two operands that do this.
Code:
enemy1_hp -= 10
enemy2_hp -= 10
How do we make both operands executed together?
No, you can't.

All multiple hit damage is executed one-by-one. It is not about "a few milliseconds" and it ruins the immersion. It won't, as long as all of them is executed in a single frame that it looks instant.

Now your problem might be something else. I assume that you are using a default RMMZ without any other plugins installed. In RMMZ code, there is indeed a delay, especially when playing an animation. And it is only the animation part probably due to the fact that loading Effekseer animation at once takes resources.

These lines specifically from rpg_sprites.js
JavaScript:
Spriteset_Base.prototype.animationBaseDelay = function() {
    return 8;
};

Spriteset_Base.prototype.animationNextDelay = function() {
    return 12;
};
Copy these code, save as a new plugin file. Then change the number to 0, it might solve your animation-delay problem that might ruin the immersion. Do note that it might put your game on a burden when loading the animation at once.

Second, the battle message and the popup.
Under the hood, the action effect (damage) is executed at the "same time", however the action result (damage popup and display report in the message log) is delayed. It is necessary to put a delay so the battle is more readable and you can read the report from the battle log. Changing this behavior may require heavy editing in the window battle log.

For reference, here is the piece of the code from rpg_windows.js
JavaScript:
Window_BattleLog.prototype.displayActionResults = function(subject, target) {
    if (target.result().used) {
        this.push("pushBaseLine");
        this.displayCritical(target);
        this.push("popupDamage", target);
        this.push("popupDamage", subject);
        this.displayDamage(target);
        this.displayAffectedStatus(target);
        this.displayFailure(target);
        this.push("waitForNewLine");
        this.push("popBaseLine");
    }
};
You can try to search "waitForNewLine" and where it is used then change the behavior accordingly.
 

Raith

Squire
Veteran
Joined
Oct 28, 2019
Messages
79
Reaction score
64
First Language
Indonesian
Primarily Uses
RMMZ
I guess seeing Yanfly's Battle Engine Core video here will clears some things up. Compare the damage delay between 0:35 and 2:28. That's what I'm talking about.

@Andar I'll take note on that

@TheoAllen Yes, I'm fully aware that you can't execute more than 1 operand at once.

The problem is not a few milliseconds delay (of calculation?). But hundreds of millisecond. Multiply it by 4 enemies and you may got a solid second.

And sir, we also know that a few millisecond delay between each calculation is equal to "all at once" in human-eye perception, right? That few millisecond is like bomb blast timing. The shockwave still needs time to travel from the blast source. But from people eyes, the blast sweep anything in the blast radius all at once. So, perhaps we can alter the code to do that?

I'm confident that the problem is not the animation. I'm familiar with that AnimationDelay functions since MV. YEP_BattleEngineCore in MV also modded those number. So, sorry, that's not what I'm looking for.

About the damage log and popups. I'm probably will check the popups one. Thank you for this clue.
 

TheoAllen

Self-proclaimed jack of all trades
Veteran
Joined
Mar 16, 2012
Messages
6,137
Reaction score
7,328
First Language
Indonesian
Primarily Uses
RMVXA
Compare the damage delay between 0:35 and 2:28. That's what I'm talking about.
If you mean
0:35 is the damage delay
And
2:28 as it is executed at once

The window battle log in the default code is designed to show "blink and pops up the damage with delayed effect". Think the chronology like this

- Start action
- Play animation (with delay)
- Apply the skill (at "once"), but the result of the skill apply is not yet reported.
- Report the result to the player (with delay)

Reporting it to the player includes popup damage and write it in the battle log. This is where you need to change the code from the default.

The problem is not a few milliseconds delay (of calculation?). But hundreds of millisecond. Multiply it by 4 enemies and you may got a solid second.

And sir, we also know that a few millisecond delay between each calculation is equal to "all at once" in human-eye perception, right? That few millisecond is like bomb blast timing. The shockwave still needs time to travel from the blast source. But from people eyes, the blast sweep anything in the blast radius all at once. So, perhaps we can alter the code to do that?
Then you clearly don't know how the game loop works and what it means by frame per second (fps). The game graphic is not being updated in real time (or at least in the context of RPG Maker). But a frame by frame. Your analogy of bomb blasting with game graphics is not a direct analogy that can be compared. That is just not how it works.

Let me clear this up.
Suppose that you want to move an object from x = 0 to x = 10 instantly.
- in frame 1, the player sees the object in x = 0
- You instantly update the object.x = 10
- game screen updated, in frame 2, the player sees the object in x =10, it teleported instantly.

Or, if you want to do it in a dumb way
- in frame 1, the player sees the object in x = 0
- You make a code that loops 10 times to add x += 1
- game screen updated, in frame 2, the player sees the object in x =10, it teleported instantly.

Both yield the same result despite the process.

Now, replace the code with this.
- in frame 1, the player inputs a command to damage an enemy
- One enemy damaged
- game screen updated, in frame 2, the player sees one enemy damaged.

Let's make it an area of effect
- in frame 1, the player inputs a command to damage all enemies
- Iterate through all enemies to damage one by one.
- game screen updated, in frame 2, the player sees all of the enemy damaged.

What is probably going on in your mind, probably, like this:
- The player input command to damage all of the enemies, the screen updated.
- iterate through all of the enemies, one damaged, the screen updated.
- the enemy gets damaged in a factor of sub-seconds.

Again, sir. That is not how game loop or frame-based update (fps) works in most games.
 

Raith

Squire
Veteran
Joined
Oct 28, 2019
Messages
79
Reaction score
64
First Language
Indonesian
Primarily Uses
RMMZ
Reporting it to the player includes popup damage and write it in the battle log. This is where you need to change the code from the default.
I probably will dig that battleLog codes more. I already messing with messageSpeed and waitCount with unsatisfying result.

Your analogy of bomb blasting with game graphics is not a direct analogy that can be compared. That is just not how it works.
"A frame is a spatial information within a quantified time interval"
I've made a lot of frame-by-frame animations, so I facepalmed when this bomb analogue is failing
  1. A bomb blast needs time to travel to reach its target.
  2. Things happening due to that blast on each update of time unit is called happening within a timeframe.
  3. Imagine that bomb in a 10 m room, the shockwave travels at 1 m/ms.
  4. In 1st ms, item 1 meter near blast center is destroyed => this is newly happen in frame 1
  5. Then in 2nd ms, item 2 meter near blast center is destroyed => this newly happen in frame 2
  6. Sadly, our eyes only update the frame after, say, 1000 ms
  7. Hence, in that 10 m room, you will see everything newly destroyed in 1-1000 ms timeframe as if it was destroyed at once
  8. We can exploit this for the sake of game coding.
  9. That because game screen is updated frame-by-frame.
I wont talk about fps here, since it concern about frame update rate than the what we discussed here.

The other good example of frame is going to be light travel. But Wikipedia will have better explanation for that.


What is probably going on in your mind, probably, like this:
Well, I know people will look at me like that... *sigh*

What's in my mind is like this actually:
- The player input command to damage all of the enemies
- Enemy is listed (through array, etc)
- Iterate through all of the enemies using a cyclic function on regular interval
- enemy A damaged, the screen updated in frame = 1, SFX played, animation is done (if any), enemy A delisted, the function wait 1 frame , then return to the function cycle
- enemy B damaged, the screen updated in frame = 3, SFX played, animation is done (if any), enemy B delisted, the function wait 1 frame, then return to the function cycle
- enemy C damaged, the screen updated in frame = 3, SFX played, animation is done (if any), enemy C delisted, the function wait 1 frame, then return to the function cycle
- repeat until the rest of enemies receive the damage

So finally you get update in frame 1-3-5... That frame interval went by fast and giving the illusion of simultaneous damage.

At first I think that interval is a setTimeout in a For/Loop function, built intentionally as @Andar said, to wait the SFX done playing and collapse animation finishing.

But I can't find one in the code. So I suppose RPG Maker don't use that.

Hence I need to know how to modify the regular interval in this case,
Which is the timing of the delay, exactly just like in this quote:


The window battle log in the default code is designed to show "blink and pops up the damage with delayed effect".
 
Last edited:

Kenen

Veteran
Veteran
Joined
Apr 3, 2012
Messages
301
Reaction score
226
First Language
English
Primarily Uses
RMMZ
"A frame is a spatial information within a quantified time interval"

It seems like you are way more fixated on coming up with arbitrary terms and the desire for your contrived analogies to be correct than you are about understanding the way MZ actually works and how to adapt it to what you're trying to accomplish.

Theo has already told you at least twice where to start looking.

Here's the reality: nearly everything that happens in battle in MZ (and MV) gets routed through Window_BattleLog. If you want to change the pace or the flow of those events, you need to look there.

Inventing analogies about exploding bombs that are completely unrelated to how MZ processes battle information is not going to help you solve your problem.
 

Raith

Squire
Veteran
Joined
Oct 28, 2019
Messages
79
Reaction score
64
First Language
Indonesian
Primarily Uses
RMMZ
It seems like you are way more fixated on coming up with arbitrary terms and the desire for your contrived analogies to be correct than you are about understanding the way MZ actually works and how to adapt it to what you're trying to accomplish.

Theo has already told you at least twice where to start looking.

Here's the reality: nearly everything that happens in battle in MZ (and MV) gets routed through Window_BattleLog. If you want to change the pace or the flow of those events, you need to look there.

Inventing analogies about exploding bombs that are completely unrelated to how MZ processes battle information is not going to help you solve your problem.
Sorry, looks like I'm just too frustrated from being judged like that.

Since MV, I've been digging the Window_BattleLog functions, modifying setWaitCounts, messageSpeed function, adding new functional identity just for damage popups control, separating and/or parallleling the available damage popups function, examining the Game_Action, even trying to reverse-engineer Yanfly's MV Yep_battleenginecore for months... All just to understand why there is a delay even when I set the BattleLog's messageSpeed to 0?

That was when I'm using MV. And now I'm in MZ - with codes that not changing much. Looking at current answer, it seems I'm returning at the same confusion I've met before.
 

Kenen

Veteran
Veteran
Joined
Apr 3, 2012
Messages
301
Reaction score
226
First Language
English
Primarily Uses
RMMZ
Theo gave you the exact function to look at. It was in his first reply to your request. The problem is that you were more worried about your analogies and your line of thinking being correct than you were about taking his advice.

Let's look at what he said:

For reference, here is the piece of the code from rpg_windows.js
JavaScript:
Window_BattleLog.prototype.displayActionResults = function(subject, target) {
if (target.result().used) {
this.push("pushBaseLine");
this.displayCritical(target);
this.push("popupDamage", target);
this.push("popupDamage", subject);
this.displayDamage(target);
this.displayAffectedStatus(target);
this.displayFailure(target);
this.push("waitForNewLine");
this.push("popBaseLine");
}
};

And let's look at the function:

JavaScript:
Window_BattleLog.prototype.displayActionResults = function(subject, target) {
    if (target.result().used) {
        this.push("pushBaseLine"); // Updates the battle log text line(s)
        this.displayCritical(target); // Displays a critical hit message
        this.push("popupDamage", target); // Calls the popup damage for the target
        this.push("popupDamage", subject); // Calls the popup damage for the user
        this.displayDamage(target); // Calls the displayDamage function, which displays the miss/evade/damage text in the battle log
        this.displayAffectedStatus(target); // Displays state changes, including target death (called Collapse) in the battle log
        this.displayFailure(target); // Displays failures in the battle log
        this.push("waitForNewLine"); // Updates the battle log text line(s)
        this.push("popBaseLine"); // Updates the battle log text line(s)
    }
};

Most of these items feed text to the battle log, and in turn, cause delays. Just because you set the messageSpeed to 0 doesn't mean these messages stop being processed.

You can disable some of these messages. However, some of the actual effects (such as enemy collapse) are tied to those message functions. This is because, as I said earlier, the majority of things that occur in battle in MZ/MV go through Window_BattleLog.

JavaScript:
Window_BattleLog.prototype.displayActionResults = function(subject, target) {
    if (target.result().used) {
        //this.push("pushBaseLine");
        //this.displayCritical(target);
        this.push("popupDamage", target);
        this.push("popupDamage", subject);
        //this.displayDamage(target, subject);
        this.displayAffectedStatus(target);
        //this.displayFailure(target);
        //this.push("waitForNewLine");
        //this.push("popBaseLine");
    }
};

Changing displayActionResults like this will remove the vast majority of delay between damage popups when hitting multiple targets. However, if an enemy dies (collapses), there will still be a delay for that, because those messages are controlled by additional functions.

I'm not going to do all of the legwork for you, because this is the Learning Javascript forum. Between what Theo has already tried to explain to you multiple times and the info here, hopefully you can get started working out the solution to your problem.

Just be advised that if you eliminate all delays entirely by removing messages, you may need to reintroduce some delays elsewhere or else the pace of battle will be too fast. Similarly, if you remove all of the messages and don’t plan for other ways to display that information, you’ll have an entirely different problem on your hands.

Edit: updated my code tags to use javascript formatting.
 
Last edited:

Andar

Veteran
Veteran
Joined
Mar 5, 2013
Messages
32,369
Reaction score
8,092
First Language
German
Primarily Uses
RMMV
@Raith
perhaps you'll understand it better if I use a different example to explain it.

all RMs always had several rather pecular quirks in their programming, just because the original programmer in japan likes to work in that way and refuses to change its ways.

For example despite the fact that the trait is called HIT%, you'll never find any mention of any hit-chance in the entire skill sequence. Instead the code is about missed and NOT-missed. No matter how many times someone would try to find a hit-chance, they'll never succeed, and several important codes would have to be rewritten and replaced to create any response to a hit-chance.

and it's the same with what you're trying. What you are looking for simply doesn't exist at all, because the engine handles the mechanics in an entirely different way. And that different way cannot work the way you expect it to work without a major rewrite of the code.

So your choice is either to forget your own idea and work along the way the engine handles the data, or you can decide to delete most of the existing functions to rewrite the engine into working the way you expect it to work.

It's basic fact of programming that there are many different ways to program something that looks similar on the screen for the player. And everyone here will agree that some of the ways how the RM engine handles things are rather strange to most programmers. But that doesn't change the fact that the engine works in other ways than you expect it to work, and your failure to find what you want is simply because it isn't there.
 

Kenen

Veteran
Veteran
Joined
Apr 3, 2012
Messages
301
Reaction score
226
First Language
English
Primarily Uses
RMMZ
@Raith since you indicated that this is causing you some frustration, here is a very simple plugin that should address your issue. This was tested in a blank project.

JavaScript:
//=============================================================================
// RPG Maker MZ - Instant Enemy Damage
//=============================================================================
/*:
 * @target MZ
 * @plugindesc Removes the Window_BattleLog messages and associated delays when dealing damage to enemies.
 *
 * @help instantEnemyDamage.js
 * 
 * Removes Window_BattleLog messages and associated delays when dealing damage to enemies.
 * Enemies that are killed will collapse immediately without causing further message-related delays.
 * Enemies inflicted with states will no longer display an initial state message, but subsequent (end of turn) state messages will display normally.
 * 
 */
(() => {
var _Window_Battlelog_displayActionResults = Window_BattleLog.prototype.displayActionResults;
Window_BattleLog.prototype.displayActionResults = function(subject, target) {
    if (target.result().used && target.isEnemy()) {
        this.displayEnemyDamage(subject, target);
    } else {
        _Window_Battlelog_displayActionResults.call(this, subject, target);
    }
}
Window_BattleLog.prototype.displayEnemyDamage = function(subject, target) {
    this.push("popupDamage", target);
    this.push("popupDamage", subject);
    if (target.isDead()) target.performCollapse();
    if (subject.isDead()) subject.performCollapse();
}
// Spriteset_Base
Spriteset_Base.prototype.animationBaseDelay = function() {
    return 0;
};
Spriteset_Base.prototype.animationNextDelay = function() {
    return 0;
};
})();

giphy.gif


This makes the assumption that you are using the standard battle system in front view, and only affects enemies as a result. If you want to expand on this to affect everyone, it should be fairly straightforward for you to modify.

I also made some assumptions regarding what you were trying to accomplish. If this isn't 100% it, maybe it will at least put you in the right direction.
 

TheoAllen

Self-proclaimed jack of all trades
Veteran
Joined
Mar 16, 2012
Messages
6,137
Reaction score
7,328
First Language
Indonesian
Primarily Uses
RMVXA
My take in this matter.
JavaScript:
(() => {
    Window_BattleLog.prototype.displayActionResults = function(subject, target) {
        if (target.isDead()) target.performCollapse();
        if (subject.isDead()) subject.performCollapse();
    }

    // Spriteset_Battle
    Spriteset_Battle.prototype.animationBaseDelay = function() {
        return 0;
    };
    Spriteset_Battle.prototype.animationNextDelay = function() {
        return 0;
    };

    // Remove wavy effect
    Sprite_Damage.prototype.createDigits = function(value) {
        const string = Math.abs(value).toString();
        const h = this.fontSize();
        const w = Math.floor(h * 0.75);
        for (let i = 0; i < string.length; i++) {
            const sprite = this.createChildSprite(w, h);
            sprite.bitmap.drawText(string[i], 0, 0, w, h, "center");
            sprite.x = (i - (string.length - 1) / 2) * w;
            sprite.dy = -1;
        }
    };

    // Popup the damage right after hitting
    BattleManager.updateAction = function() {
        let target = this._targets.shift();
        while (target) {
            this.invokeAction(this._subject, target);
            this._logWindow.popupDamage(target)
            this._logWindow.popupDamage(this._subject)
            if (target.result().hpDamage > 0) this._logWindow.performDamage(target);
            this._logWindow.displayChangedStates(target)
            target = this._targets.shift();
        }
        this.endAction();
    };
})();
damageatthesametime.gif
 

Kenen

Veteran
Veteran
Joined
Apr 3, 2012
Messages
301
Reaction score
226
First Language
English
Primarily Uses
RMMZ
Theo is putting me to shame! Theo's version is a much better solution because it keeps the state application message whereas mine doesn't. Use Theo's!
 

TheoAllen

Self-proclaimed jack of all trades
Veteran
Joined
Mar 16, 2012
Messages
6,137
Reaction score
7,328
First Language
Indonesian
Primarily Uses
RMVXA
My solution is not perfect.
Mine assumes that the target is not changed (substitute). And does not proc counterattack or magic reflection.
Which is rolled inside the invoke action function.

A lot of changes need to be made in the function and I'm too lazy to think of the solution.
But yeah, that is the pointer.
 

Latest Threads

Latest Posts

Latest Profile Posts

Why is parallexing so time-consuming? Ive been at it for 8hrs and i havent even finished the map yet :/
It seems MogHunter Twitter is back...
1618970067646.png
Feedback would be appreciated. To me it feels like the Dad does look a bit too normal and boring.
I think he's missing some features that make him more unique. Opinions?
recovering from surgery that covid caused me to have, im back trying to work on the newer engine on my game. We will see how this goes. I have no idea what to do about conversions

Forum statistics

Threads
110,589
Messages
1,054,486
Members
143,715
Latest member
natzma
Top