[RMMV] How to flip (mirror) SV Battlers Left/Right with Yanfly Plugins

Moon_Haven

Veteran
Veteran
Joined
May 5, 2020
Messages
81
Reaction score
34
First Language
English
Primarily Uses
RMMV
[EDIT: This seems to be working, so I've changed the title from "need help" to "how to". I kept the rest of my post intact so future dwellers can learn about the process and have the code]



Hello. I need help.

So far, I've been able to reposition actors to the left of the screen, and the enemies on the right. In order to make all of them attack in the correct direction, I have changed the parameters of this.setMirror to true in the CODE of YEP_BattleEngineCore

JavaScript:
Game_Battler.prototype.spriteFaceForward = function() {
    this.setMirror(true);
};

Game_Battler.prototype.spriteFaceBackward = function() {
    this.setMirror(true);
This makes the battlers face the targets properly, swing weapons in the right direction, etc.

Where I'm stuck: The only thing that I'm not able to figure out is that at the beginning of the battle, every battler is looking in the wrong direction (picture below).

mirror.png

But once the battle begins, things come to normal. For actors: they all turn in the right direction after the first action input (so when they move back to their home position). For the enemies, when the last actor has set its action, all the enemies turn around all at once.

I have the feeling I need to inject some kind of a refresh at the very beginning of the battle (force a this.spriteFaceForward() on each battler), or set a default parameter when the battlers are initialized. I just do not know where. Been trying for a couple of days :(

flipped.png

Here's basically a step-by-step how I got here:

Notes:
- I'm using Yanfly plugins.
- I'm using screen size of 1280 x 720. You can use pretty much any size that you want, but you will need to change the hard values (150, 26, 52...) to fit your game screen size.

STEP 1 - Position our actors and enemies.
I used YEP_RowFormation to automate the positions. My game will use 3 rows for actors and enemies. Row1 (fighters) is at the front, Row2 (support) is middle, Row3 (casters) is at the back.

rows.png

The values below assume that the sprites are 64x64. If they are any bigger, you will want to change the values of 16, 32 and 64.
The value 150 is the distance from the edge of the screen.
The values 26 and 52 are for the staggering. Simply put, we add (or retract) these values for each sprite on a row, so that it gives a sense of perspective. These will need to be changed (trial and error) if you use a different screen size than 1280 x 720. If you are using default RMMV size, you can use 16 and 32 as a starting point. This is what it would look like.

In YEP_RowFormation:
Maximum Rows
: 3
Maximum Row X: screenWidth - 150 + 26
Maximum Row Y: screenHeight - statusHeight - 16
Minimum Row Y: screenHeight - statusHeight - 16 - ((maxRows + 1) * 64)
Center Row Y: (maxRowY + minRowY) / 2 + 16

Row 1 Home X: 150 - 32 + $gameParty.rowSize(maxRows) * 26 + (maxRows - rowId) * 150 - $gameParty.rowSize(rowId) * 26 + rowIndex * 52
Row 1 Home Y: centerY - ((rowSize / -2 + 0.5) + rowIndex) * 64
Row 2 Home X: 150 - 32 + $gameParty.rowSize(maxRows) * 26 + (maxRows - rowId) * 150 - $gameParty.rowSize(rowId) * 26 + rowIndex * 52
Row 2 Home Y: centerY - ((rowSize / -2 + 0.5) + rowIndex) * 64
Row 3 Home X: 150 - 32 + $gameParty.rowSize(maxRows) * 26 + (maxRows - rowId) * 150 - $gameParty.rowSize(rowId) * 26 + rowIndex * 52
Row 3 Home Y: centerY - ((rowSize / -2 + 0.5) + rowIndex) * 64
Enemy Row X: screenWidth - 150 + 32 - $gameTroop.rowSize(maxRows) * 26 - (maxRows - rowId) * 150 + $gameTroop.rowSize(rowId) * 26 - rowIndex * 52
Enemy Row Y: centerY - ((rowSize / -2 + 0.5) + rowIndex) * 64


STEP 2 - Flip (mirror) the sprites
Now that I have positioned the actors where the enemies should be, and vice-versa, the sprites are looking in the wrong direction. To fix this:

In YEP_BattleEngineCore:

Step Distance
: -32
Flinch Distance: -16

In the *CODE* of YEP_BattleEngineCore.js:
I switched the values for mirroring. Find the following lines:

JavaScript:
Game_Battler.prototype.spriteFaceForward = function() {
    this.setMirror(false);
};
Game_Battler.prototype.spriteFaceBackward = function() {
    this.setMirror(true);
};
Change the false to true, and true to false

JavaScript:
Game_Battler.prototype.spriteFaceForward = function() {
    this.setMirror(true);  // Default: false
};
Game_Battler.prototype.spriteFaceBackward = function() {
    this.setMirror(false);   // Default: true
};

STEP 3 - Action Sequence (no longer needed, code in 3rd post of this thread fixes all of this)
Everything is now reversed, even action sequence. For instance, moving an actor to the front of an enemy was: move user: target, front, 30 but is it now: move user: target, back, 30

A simple attack will now look like this:
Code:
<setup action>
    clear battle log
    display action
    immortal: targets, true
    cast animation
    wait for animation
</setup action>

<whole action>
    face user: target
    if user.attackMotion() !== 'missile'
        move user: target, back, 30
        wait for movement
    else
        perform start
        wait for movement
    end
</whole action>

<target action>
    motion attack: user
    wait: 10
    action effect
    if target.result().missed || target.result().evaded
    else
        attack animation: target
        wait for animation
    end
</target action>

<finish action>
    immortal: targets, false
    wait for new line
    clear battle log
    perform finish
    wait for movement
    wait for effect
</finish action>
 
Last edited:

Moon_Haven

Veteran
Veteran
Joined
May 5, 2020
Messages
81
Reaction score
34
First Language
English
Primarily Uses
RMMV
I've modified a line of code in the YEP_RowFormation, which effectively forces mirroring.


In the *CODE* YEP_RowFormation.js:
Look for this:
JavaScript:
BattleManager.refreshAllBattlers = function() {
  var members = $gameParty.members().concat($gameTroop.members());
  var length = members.length;
  for (var i = 0; i < length; ++i) {
    var member = members[i];
    if (member){
      member.refresh();
    }
  }
};
... and add member.setMirror(true); under the member.refresh(), like so:

JavaScript:
BattleManager.refreshAllBattlers = function() {
  var members = $gameParty.members().concat($gameTroop.members());
  var length = members.length;
  for (var i = 0; i < length; ++i) {
    var member = members[i];
    if (member){
      member.refresh();
      member.setMirror(true);
    }
  }
};
 
Last edited:

Moon_Haven

Veteran
Veteran
Joined
May 5, 2020
Messages
81
Reaction score
34
First Language
English
Primarily Uses
RMMV
I did some additional work on this as the battlers were mixing up FRONT and BACK. The additional changes you need to do are:

In the *CODE* of YEP_X_ActSeqPack2.js:
Replace the whole BattleManager.actionFace with this:

JavaScript:
BattleManager.actionFace = function(name, actionArgs) {
    var movers = this.makeActionTargets(name);
    if (movers.length < 1) return true;
    var cmd = actionArgs[0].toUpperCase();
    if (['FORWARD', 'NORMAL'].contains(cmd)) {
      movers.forEach(function(mover) {
        mover.spriteFaceForward();
      });
    } else if (['BACKWARD', 'MIRROR'].contains(cmd)) {
      movers.forEach(function(mover) {
        mover.spriteFaceBackward();
      });
    } else if (['HOME', 'ORIGIN'].contains(cmd)) {
      movers.forEach(function(mover) {
        mover.spriteFaceHome();
      });
    } else if (['AWAY FROM HOME', 'AWAY FROM ORIGIN'].contains(cmd)) {
      movers.forEach(function(mover) {
        mover.spriteFaceAwayHome();
      });
    } else if (['POINT', 'POSITION', 'COORDINATE', 'SCREEN', 'SCREEN POS',
    'COORDINATES'].contains(cmd)) {
      var destX = eval(actionArgs[1]) || 0;
      var destY = eval(actionArgs[2]) || 0;
      movers.forEach(function(mover) {
        // mover.spriteFacePoint(destX, destY);
        mover.spriteFaceAwayPoint(destX, destY);
      });
    } else if (['AWAY FROM POINT', 'AWAY FROM POSITION', 'AWAY FROM COORDINATE',
    'AWAY FROM SCREEN', 'AWAY FROM SCREEN POS',
    'AWAY FROM COORDINATES'].contains(cmd)) {
      var destX = eval(actionArgs[1]) || 0;
      var destY = eval(actionArgs[2]) || 0;
      movers.forEach(function(mover) {
        // mover.spriteFaceAwayPoint(destX, destY);
        mover.spriteFacePoint(destX, destY);
      });
    } else if (cmd.match(/AWAY[ ]FROM[ ](.*)/i)) {
      var targets = this.makeActionTargets(String(RegExp.$1));
      if (targets.length < 1) return false;
      var destX = 0;
      var destY = 0;
      targets.forEach(function(target) {
        destX += target.spritePosX();
        destY += target.spritePosY();
      }, this);
      destX /= targets.length;
      destY /= targets.length;
      movers.forEach(function(mover) {
        // mover.spriteFaceAwayPoint(destX, destY);
        mover.spriteFacePoint(destX, destY);
      }, this);
    } else {
      var targets = this.makeActionTargets(actionArgs[0]);
      if (targets.length < 1) return false;
      var destX = 0;
      var destY = 0;
      targets.forEach(function(target) {
        destX += target.spritePosX();
        destY += target.spritePosY();
      }, this);
      destX /= targets.length;
      destY /= targets.length;
      movers.forEach(function(mover) {
        // mover.spriteFacePoint(destX, destY);
        mover.spriteFaceAwayPoint(destX, destY);
      }, this);
    }
    return false;
};


Replace the whole BattleManager.actionMove with this:

JavaScript:
BattleManager.actionMove = function(name, actionArgs) {
    if (!$gameSystem.isSideView()) return true;
    var movers = this.makeActionTargets(name);
    if (movers.length < 1) return true;
    var cmd = actionArgs[0].toUpperCase();

    if (['HOME', 'ORIGIN'].contains(cmd)) {
      var frames = actionArgs[1] || 12;
      movers.forEach(function(mover) {
        mover.battler().startMove(0, 0, frames);
        mover.requestMotion('walk');
        mover.spriteFaceHome();
      });
    } else if (['RETURN'].contains(cmd)) {
      var frames = actionArgs[1] || 12;
      movers.forEach(function(mover) {
        mover.battler().startMove(0, 0, frames);
        mover.requestMotion('evade');
        mover.spriteFaceForward();
      });
    } else if (['FORWARD', 'FORWARDS', 'BACKWARD',
    'BACKWARDS'].contains(cmd)) {
      var distance = actionArgs[1] || Yanfly.Param.BECStepDist;
      if (['BACKWARD', 'BACKWARDS'].contains(cmd)) distance *= -1;
      var frames = actionArgs[2] || 12;
      movers.forEach(function(mover) {
        mover.battler().moveForward(distance, frames);
        mover.requestMotion('walk');
        if (['FORWARD', 'FORWARDS'].contains(cmd)) {
          mover.spriteFaceForward();
        } else {
          mover.spriteFaceBackward();
        }
      });
    } else if (['POINT', 'POSITION', 'COORDINATE', 'SCREEN', 'SCREEN POS',
    'COORDINATES'].contains(cmd)) {
      var destX = eval(actionArgs[1]) || 0;
      var destY = eval(actionArgs[2]) || 0;
      var frames = actionArgs[3] || 12;
      movers.forEach(function(mover) {
        var offsetX = BattleManager.actionMoveOffsetX(actionArgs, mover, mover);
        var offsetY = BattleManager.actionMoveOffsetY(actionArgs, mover, mover);
        mover.battler().moveToPoint(destX + offsetX, destY + offsetY, frames);
        mover.requestMotion('walk');
        mover.spriteFacePoint(destX, destY);      
      });
    } else {
      var targets = this.makeActionTargets(actionArgs[0]);
      var frames = actionArgs[2] || 12;
      var type = actionArgs[1].toUpperCase();
      if (targets.length < 1) return false;
      for (var i = 0; i < movers.length; ++i) {
          var mover = movers[i];
          if (!mover) continue;
          if (['BASE', 'FOOT', 'FEET'].contains(type)) {
            var destX = this.actionMoveX(mover, targets, 'center');
            var destY = this.actionMoveY(mover, targets, 'foot');
          } else if (['CENTER', 'MIDDLE'].contains(type)) {
            var destX = this.actionMoveX(mover, targets, 'center');
            var destY = this.actionMoveY(mover, targets, 'center');
          } else if (['HEAD', 'TOP'].contains(type)) {
            var destX = this.actionMoveX(mover, targets, 'center');
            var destY = this.actionMoveY(mover, targets, 'head');
          } else if (['FRONT BASE', 'FRONT FOOT', 'FRONT FEET',
          'FRONT'].contains(type)) {
            var destX = this.actionMoveX(mover, targets, 'back'); // FRONT <-> BACK -- WORKS
            var destY = this.actionMoveY(mover, targets, 'foot');
          } else if (['BACK BASE', 'BACK FOOT', 'BACK FEET',
          'BACK'].contains(type)) {
              var destX = this.actionMoveX(mover, targets, 'front'); // FRONT <-> BACK -- WORKS
            var destY = this.actionMoveY(mover, targets, 'foot');
          } else if (['FRONT CENTER', 'FRONT MIDDLE'].contains(type)) {
            var destX = this.actionMoveX(mover, targets, 'back'); // FRONT <-> BACK -- WORKS
            var destY = this.actionMoveY(mover, targets, 'center');
          } else if (['BACK CENTER', 'BACK MIDDLE',].contains(type)) {
            var destX = this.actionMoveX(mover, targets, 'front'); // FRONT <-> BACK -- WORKS
            var destY = this.actionMoveY(mover, targets, 'center');
          } else if (['FRONT HEAD', 'FRONT TOP'].contains(type)) {
            var destX = this.actionMoveX(mover, targets, 'back'); // FRONT <-> BACK -- WORKS
            var destY = this.actionMoveY(mover, targets, 'head');
          } else if (['BACK HEAD', 'BACK TOP'].contains(type)) {
            var destX = this.actionMoveX(mover, targets, 'front'); // FRONT <-> BACK -- WORKS
            var destY = this.actionMoveY(mover, targets, 'head');
          }
        var offsetX = this.actionMoveOffsetX(actionArgs, mover, targets[0]);
        var offsetY = this.actionMoveOffsetY(actionArgs, mover, targets[0]);
          mover.battler().moveToPoint(destX + offsetX, destY + offsetY, frames);
        // mover.spriteFacePoint(destX, destY);
        mover.spriteFaceAwayPoint(destX, destY);
      }
    }
    return true;
};
 

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

Latest Threads

Latest Profile Posts

And back. Trying to work online with no internet for 1.5 days was...interesting. I felt like a character in a old Sierra game trying to find internet due to all the hoops I had to jump through just to get online for work.
grief... if MV didn't have certain plugins already, I think I'd upgrade to MZ. seeing like 10 MV plugins in 1 MZ one is hilariously convenient lol.
Have you already checkes out my mapping tutorial on the blog? I would love to hear your feedback and things you'd like to see in the future!

one of character for my upcoming game idk how to design o_O

Designing patches that can 'augmented' to clothing based armor. Such good fun. A bit time consuming due to the need to create image displays, but the patches are limited so it's not too tiring.
Note: lol, posting this made me find a couple mistakes.

Forum statistics

Threads
100,736
Messages
978,940
Members
132,359
Latest member
PhilKeepItReal
Top