Plugin - Perfect Battlers Alignment in battle

Soulrender

Jack Of All Trades
Veteran
Joined
Jan 12, 2019
Messages
589
Reaction score
314
First Language
Poland
Primarily Uses
RMMV
I never had a solid interest in this matter, but it turns out I need to solve it...
Default battler alignment in database is very poor - at least for me - and does not solve
all problems with aligning battlers in battle and aligning all troops takes a lot of time
wich I cannot afford at the moment... Anyway, I wrote small plugin that will align battlers
for me in all battles, regardless of troop squad I choose and so far it did very well, but
I need something more, you know... Maybe a "higher level coding" plugin...

So... What I'm looking for is a plugin that will align battlers in as perfect alignment as
possible, like in image shown below, but with following restrictions like in this schematic picture:

- Must automaticly calculate positions both as well as party members and enemies in troop
- Must check numbers of enemies in troop group and in party
- Must include game window resolution.

I know in Yanfly's plugin I can do that but it in some (visual) way collides between themselves:
- Yanfly's Battle Engine Core
- Yanfly's Row Formation.

If this may help... I'm looking for a plugin that will align battlers like dots on playing dice, but battlers will be placed within
gray zone from image.

Thank you in advance.
 

Attachments

caethyril

^_^
Veteran
Joined
Feb 21, 2018
Messages
1,712
Reaction score
1,162
First Language
EN
Primarily Uses
RMMV
- Must automaticly calculate positions both as well as party members and enemies in troop
Maybe clarify what you mean by this? Do you want all enemies/actors to be positioned in their respective zones according to some formula? If so: Yanfly's Row Formation can do that, the only tricky bit is working out an appropriate formula...

If this may help... I'm looking for a plugin that will align battlers like dots on playing dice, but battlers will be placed within gray zone from image.
So you want the array of positions to be based on the number of party members currently in battle, right? Sounds like it could get fairly complex; writing your own little plugin was probably a smart move (particularly if you don't need the row formation stuff). :kaopride:

Maybe try something like this (untested):
JavaScript:
var myPluginNamespace = {};  // just for demonstration purposes
(function($) {
'use strict';

  // Position arrays (from 0 to 1) for X/Y coordinates of dots on a d6
  $.diceX = function(num) {
    switch (num) {
      case 2: return [0, 1];
      case 3: return [0, 0.5, 1];
      case 4: return [0, 1, 0, 1];
      case 5: return [0, 1, 0.5, 0, 1];
      case 6: return [0.3, 0.7, 0.3, 0.7, 0.3, 0.7];
      default: return [0.5];
    }
  };
  $.diceY = function(num) {
    switch (num) {
      case 2: return [0, 1];
      case 3: return [0, 0.5, 1];
      case 4: return [0, 0, 1, 1];
      case 5: return [0, 0, 0.5, 1, 1];
      case 6: return [0, 0, 0.5, 0.5, 1, 1];
      default: return [0.5];
    }
  };

  // adjust values as needed to restrict area
  $.horizon = 200;  // upper Y limit
  $.maxY = 500;     // lower Y limit
  $.fullX = 100;    // maximum width of area (bottom)
  $.leftX = 400;    // left edge of actor display area

  // Scale width of actor home area based on Y-coordinate
  $.width = function(y) { return $.fullX * (y - $.horizon) / ($.maxY - $.horizon); };

  // Get positions!
  $.getY = function(partySize, index) {
    return $.diceY(partySize)[index] * ($.maxY - $.horizon) + $.horizon;
  };
  $.getX = function(partySize, index, y) {
    return $.diceX(partySize)[index] * $.width(y) + $.leftX;
  };

  // Override actor home position with new values~
  Sprite_Actor.prototype.setActorHome = function(index) {
    let size = $gameParty.battleMembers().length;
    let y = $.getY(size, index);
    let x = $.getX(size, index, y);
    this.setHome(x, y);
  };

})(myPluginNamespace);
Basically, this fetches X and Y values from an array. The array used depends on the number of actors in battle. The battler's X value is then scaled down depending on their Y, to simulate the perspective you mentioned. :kaophew:

Hope that helps! :kaothx:

[Edit: corrected/tidied the code a bit.]
 
Last edited:

Soulrender

Jack Of All Trades
Veteran
Joined
Jan 12, 2019
Messages
589
Reaction score
314
First Language
Poland
Primarily Uses
RMMV
@caethyril Yes, That's 99.99% what I need!
It miss few things, but I believe after 2 mugs of coffee I manage to do it.
However, look at the lines that represents perspective. I want to place at these points both enemies and party members as dots on dice so each battler is facing each other in straight line
 
Last edited:

caethyril

^_^
Veteran
Joined
Feb 21, 2018
Messages
1,712
Reaction score
1,162
First Language
EN
Primarily Uses
RMMV
Yea, I feel like I've probably messed up the numbers somewhere... :kaoslp:

Happy to help, though! :kaojoy:
 

Soulrender

Jack Of All Trades
Veteran
Joined
Jan 12, 2019
Messages
589
Reaction score
314
First Language
Poland
Primarily Uses
RMMV
@caethyril - check my edit, might we miss something (that 0.01% :D )

I made preview images to picture my intentions :D

Plugin checks how many battlers and actors and enemies entered battle and based on that
number, position them as on images:

When battle is 1vs1 then place them like this - https://ibb.co/FnDJTWh
If battle is 2vs2, then position them as this - https://ibb.co/CHkbqW7
If battle is 3vs3, then place battlers like that - https://ibb.co/y5Cmrt7
4vs4 then - https://ibb.co/Hr6rWsP
5vs5 then - https://ibb.co/9w7x2Mp
6vs6 then - https://ibb.co/zrN5TLM

There's only one matter left, what if number of troops differs from number of party members?
Let the plugin still position them as shown on images :p
 
Last edited:

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,409
Reaction score
3,390
First Language
English
I'm interested in this problem because I need to solve it as well for my own battle alignment.
Except I need to do it for every square on the screen and every battler sprite as well.

1596392408273.png

Hardcoding the positions works. See this game:



if you don't need to scale the sprites based on how "far" they are from the camera (we assume the camera is positioned where we're sitting). This is probably ok for side-view battle system, but for front-view where the depth of view is much different, now we actually need to scale the sprites.

However then you have to consider things like, what about animations? Do animations that occur "further" from he screen have the same size as animations "closer" to the screen?

Currently I've been playing around with the idea of projection, with the idea that maybe I'll just turn the entire battlefield into a 2D plane. I don't really know the math so I'm trying to understand how 3D and camera works lol but hopefully it will address all the issues I have regarding distance and scaling.


And hey maybe you can then "rotate" the battlefield or something.

There's only one matter left, what if number of troops differs from number of party members?
Let the plugin still position them as shown on images :p
I would define actor and enemy positions separately. If you have say up to 8 enemy positions, and you only have 4 enemies, then you would use the first 4 positions or something.

Then it wouldn't matter how many actors or enemies there are because they'd be separate.
 
Last edited:

Soulrender

Jack Of All Trades
Veteran
Joined
Jan 12, 2019
Messages
589
Reaction score
314
First Language
Poland
Primarily Uses
RMMV
@Tsukihime your grid system looks very neat, a quite interesting way to do it. Since my last post in this topic I tried to adapt @caethyril code to my needings, but I failed, although his code works excelent.
So I took MY old code and simply hardcoded coordinates in arrays and now it covers with my needs.
However this is not a good code, but the result is what I need. - https://prnt.sc/tssl2a
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,409
Reaction score
3,390
First Language
English
@Tsukihime your grid system looks very neat, a quite interesting way to do it. Since my last post in this topic I tried to adapt @caethyril code to my needings, but I failed, although his code works excelent.
So I took MY old code and simply hardcoded coordinates in arrays and now it covers with my needs.
However this is not a good code, but the result is what I need. - https://prnt.sc/tssl2a
As long as it works. You can refactor the code later lol.
I might hardcode coordinates myself for now just so I can get better looking screenshots.

How did you go about getting the coordinates?
 

Soulrender

Jack Of All Trades
Veteran
Joined
Jan 12, 2019
Messages
589
Reaction score
314
First Language
Poland
Primarily Uses
RMMV
@Tsukihime
#1: I took as a base a frame size of a battler, wich is 64x64 pixels :)
#2: I took game resolution (in my case it's 960x540 pixels width/height) and divided it by 64 (wich gives 15 and 8,4375 (round to 8)). With those data I created in Photoshop empty image 960x540 and I put grid with cell 64 pixels size and simply by adjusting a square (64x64) representing sprite I read coordinates.

Now adjusting offset was very simple, because frame size is 64x64 only half of it is enough to make
look battlers alignemt very strict, clean and in order, so for each battler I simply added 32 to x-coordinate.
But in Y-Coordinate I had to be more careful, at some point y-offset needed to be reset to 0, have a look at my arrays ;]

Code:
var TwoEnemiesPos    =
                    [
                        [0, 0],
                        [-32, 32]
                    ];                   
var ThreeEnemiesPos    = [
                        [0, 0],
                        [-32, 32],
                        [-64, 64]
                    ];                   
var FourEnemiesPos    = [
                        [0, 0],
                        [-32, 32],
                        [-64, 64],
                        [-96, 0]
                    ];               
var FiveEnemiesPos    = [
                        [0, 0],
                        [-32, 32],
                        [-64, 64],
                        [-96, 0],
                        [-128, 32]
                    ];                   
var SixEnemiesPos    = [
                        [0, 0],
                        [-32, 32],
                        [-64, 64],
                        [-96, 0],
                        [-128, 32],
                        [-160, 64]
                    ];
See, first element of arrays continues by 32, second element in every 3rd step is reset to 0 to form second row of enemies, as shown in image in previous post.
 
Last edited:

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,409
Reaction score
3,390
First Language
English
Thanks, I tried it out. Instead of hardcoding I just used the WIDTH + (ROW * OFFSET).
So the row is offset a bit to give it that fake perspective.
Height might also need to be offset a bit I think...

1596396613716.png

I think the offset between each row gets smaller the closer to the middle of the screen, and larger when you're closer to the edge of the screen.

Wonder if there's a formula that will capture that observation.

Looking at the picture, the middle of the screen acts like a "mirror" where left half and right half are basically flipped. Maybe I just need to include a calculation based on distance between the tile position and the middle of the screen.
 

Soulrender

Jack Of All Trades
Veteran
Joined
Jan 12, 2019
Messages
589
Reaction score
314
First Language
Poland
Primarily Uses
RMMV
@Tsukihime you could try define custom step scale e.g 0.2 and in loop increase/decrease scale depending on enemy index, to be more precise in method
Code:
Game_Troop.prototype.placeEnemies = function(){

    var BattlerSize = this._enemies.length;
    var Battler = this._enemies;
(...)
I'm not sure, but some trygonometry methods in Math class can do it already
 

caethyril

^_^
Veteran
Joined
Feb 21, 2018
Messages
1,712
Reaction score
1,162
First Language
EN
Primarily Uses
RMMV
@Soulrender: oh whoops, I forgot about the enemies! :kaoblush:

I rewrote my previous code a bit. In this version all the positions/offsets are taken from plugin parameters; I tried to make the default parameter values match your six diagrams, and tested it a bit this time too. :)

Not sure whether you'll find it helpful since you've already got something working, but here's the code anyway~ :kaopride:
JavaScript:
// ==================================================
// Cae_AlignBattlers.js
// ==================================================

/*:
 * @plugindesc v1.0 - Specify troop/party battler positioning based on unit size. Includes basic perspective scaling (1 vanishing point).
 * @author Caethyril
 * @help Help:
 *   In the following, values written in "Quotes" are plugin parameters.
 * 
 *   Perspective is applied using a vanishing point at:
 *     X = (centre screen) = (screen width) * 0.5
 *     Y = "Horizon"
 * 
 *   "Positions" is a set of values for deploying to a square of size 1.
 *   These values are scaled to fit the true deployment area.
 *   The X values are then adjusted based on the corresponding Y value.
 *     (X values become closer together when nearer the horizon.)
 * 
 *   The deployment area is a scalene triangle with corners at:
 *     1) Vanishing point
 *     2) X = (centre screen) + "Skew Offset"
 *        Y = "Lower Y"
 *     3) X = (centre screen) + "Skew Offset" + "Deploy Width"
 *        Y = "Lower Y"
 * 
 *   "Inner X Margin" = distance between screen centre and "Position" X = 0.
 *   "Top Margin"     = distance between the horizon   and "Position" Y = 0.
 * 
 * Terms of use:
 *   Free to use and modify.
 * 
 * @param Positions
 * @text Positions
 * @type struct<CaeScalePointsType>[]
 * @desc Values used for positioning (mirrored for enemies). Units of size 1 use the first array; size 2, second array; etc.
 * @default ["{\"X\":\"[\\\"0\\\"]\",\"Y\":\"[\\\"0.5\\\"]\"}","{\"X\":\"[\\\"0\\\",\\\"0\\\"]\",\"Y\":\"[\\\"0\\\",\\\"1\\\"]\"}","{\"X\":\"[\\\"0\\\",\\\"0\\\",\\\"0\\\"]\",\"Y\":\"[\\\"0\\\",\\\"0.5\\\",\\\"1\\\"]\"}","{\"X\":\"[\\\"0\\\",\\\"0\\\",\\\"1\\\",\\\"1\\\"]\",\"Y\":\"[\\\"0\\\",\\\"1\\\",\\\"0\\\",\\\"1\\\"]\"}","{\"X\":\"[\\\"0\\\",\\\"0\\\",\\\"1\\\",\\\"1\\\",\\\"1\\\"]\",\"Y\":\"[\\\"0\\\",\\\"1\\\",\\\"0\\\",\\\"0.5\\\",\\\"1\\\"]\"}","{\"X\":\"[\\\"0\\\",\\\"0\\\",\\\"0\\\",\\\"1\\\",\\\"1\\\",\\\"1\\\"]\",\"Y\":\"[\\\"0\\\",\\\"0.5\\\",\\\"1\\\",\\\"0\\\",\\\"0.5\\\",\\\"1\\\"]\"}"]
 * 
 * @param --- X ---
 * @text --- X ---
 * @type select
 * @desc Scaling settings for X coordinates!
 * 
 * @param Deploy Width
 * @text Deploy Width
 * @parent --- X ---
 * @type number
 * @min 1
 * @desc Full width of the areas in which enemies/actors are positioned. Default = 180 px.
 * @default 180
 * 
 * @param Skew Offset
 * @text Skew Offset
 * @parent --- X ---
 * @type number
 * @desc Distance between centre of screen and front edge of each deploy area. Default = 100 px.
 * @default 100
 * 
 * @param Inner X Margin
 * @text Inner X Margin
 * @parent --- X ---
 * @type number
 * @desc Distance (px) from the centre edge of the deployment area left empty. Default: 50 px.
 * @default 50
 * 
 * @param --- Y ---
 * @text --- Y ---
 * @type select
 * @desc Scaling settings for Y coordinates!
 * 
 * @param Horizon
 * @text Horizon
 * @parent --- Y ---
 * @type number
 * @desc Vertical distance between the top of the screen and the horizon. Default: 100 px.
 * @default 100
 * 
 * @param Lower Y
 * @text Lower Y
 * @parent --- Y ---
 * @type number
 * @desc Distance (px) between the bottom of the deployable area and the bottom of the screen. Default = 200 px.
 * @default 200
 * 
 * @param Top Margin
 * @text Top Margin
 * @parent --- Y ---
 * @type number
 * @desc Distance (px) from the top edge of the deployment area left empty. Default: 175 px.
 * @default 175
 */
// =========================
/*~struct~CaeScalePointsType:
 * @param X
 * @text X
 * @type number[]
 * @max 1
 * @decimals 3
 * @desc A list of values in [0, 1] representing X positions within the deployable area.
 * @default []
 * 
 * @param Y
 * @text Y
 * @type number[]
 * @max 1
 * @decimals 3
 * @desc A list of 0 to 1 values specifying Y positions within the deployable area.
 * @default []
 */

var Imported = Imported || {};
Imported.Cae_AlignBattlers = 1.0;

var CAE = CAE || {};
CAE.AlignBattlers = CAE.AlignBattlers || {};

(function(_) {
'use strict';

    const PLUGIN_NAME = 'Cae_AlignBattlers';
    const ERR_PRE     = PLUGIN_NAME + '.js ';
    const ERR_NOPARAM = ERR_PRE + 'could not find its parameters!\nCheck the plugin file is named correctly and try again.';
    const ERR_BADPAR  = ERR_PRE + 'could not parse parameter "%1".\nCheck the value in the Plugin Manager and try again.';

// ======= Parameter stuff ======= //

    _.params = PluginManager.parameters(PLUGIN_NAME);
    if (!_.params) throw new Error(ERR_NOPARAM);

    try {
        // String > array > object > array > number => 3 object parses, 1 Number parse
        _.positions = JSON.parse(_.params['Positions']).map(JSON.parse).map(function(o) {
            return { x: JSON.parse(o.X).map(Number), y: JSON.parse(o.Y).map(Number) };
        });
    } catch (ex) {
        throw new Error(ERR_BADPAR.format('Positions') + '\n\n' + ex);
    }
    _.deX = parseInt(_.params['Deploy Width'], 10) || 0;
    _.skX = parseInt(_.params['Skew Offset'], 10) || 0;
    _.loY = parseInt(_.params['Lower Y'], 10) || 0;
    _.hoz = parseInt(_.params['Horizon'], 10) || 0;
    _.mgX = parseInt(_.params['Inner X Margin'], 10) || 0;
    _.mgY = parseInt(_.params['Top Margin'], 10) || 0;

// =========== Utility =========== //

    // Fetch/scale functions: convert unit size/index into X/Y positions
    _.get = {
        arr: function(size, index) {
            // Values in range [0, 1], representing relative positioning
            let pos = _.positions[size-1];
            return { x: pos.x[index] || 0, y: pos.y[index] || 0 };
        },
        margin: function(p, margin, max) {
            // Scale input to current range - margin, then offset by margin
            if (!margin || !max) return p;
            return p * (max - margin) / max + margin;
        },
        height: function() {
            // Deployment area height
            return Math.max(Graphics.boxHeight - _.loY - _.hoz, 0);
        },
        scaleX: function(y) { 
            // Distance from current Y to horizon, divided by height of deployable area; min 0, max 1
            return ((y - _.hoz) / this.height()).clamp(0, 1);
        },
        x: function(rawX, y) {
            // ((input * area width * perspective scale) adjusted for margin + offset * perspective scale
            let scale = this.scaleX(y);
            return this.margin(rawX * scale * _.deX, _.mgX, _.deX) + _.skX * scale;
        },
        y: function(rawY) {
            // input * area height + minimum
            return this.margin(rawY * this.height(), _.mgY, this.height()) + _.hoz;
        },
        p: function(size, index) {
            // Get X and Y coordinates from unit size and member index
            let arr = this.arr(size, index);
            let res = {};
            res.y = this.y(arr.y);
            res.x = this.x(arr.x, res.y);
            return res;
        }
    };

// ========= Alterations ========= //

    // Override actor home position with new values~
    Sprite_Actor.prototype.setActorHome = function(index) {         // Override!
        let size = this._battler.friendsUnit().members().length;
        let p = _.get.p(size, index);
        this.setHome(Graphics.boxWidth / 2 + p.x, p.y);     // X is relative to screen centre
    };

    // Reapply enemy home position with new values~
    _.Sprite_Enemy_setBattler = Sprite_Enemy.prototype.setBattler;  // Alias
    Sprite_Enemy.prototype.setBattler = function(battler) {
        _.Sprite_Enemy_setBattler.apply(this, arguments);
        let size = battler.friendsUnit().members().length;
        let index = battler.index();
        let p = _.get.p(size, index);
        this.setHome(Graphics.boxWidth / 2 - p.x, p.y);     // X is relative to screen centre
        this._stateIconSprite.setup(battler);
    };

})(CAE.AlignBattlers);
Also, since I've seen people get confused over this before, these two snippets have the same result:
JavaScript:
var a = {
  cheese: 5,
  cake: function() { return 'tasty'; }
};
JavaScript:
var a = {};
a.cheese = 5;
a.cake = function() { return 'tasty'; };
 

Soulrender

Jack Of All Trades
Veteran
Joined
Jan 12, 2019
Messages
589
Reaction score
314
First Language
Poland
Primarily Uses
RMMV
@caethyril As always great job! Everything you shared over this community I have saved on special pendrive wich I always keep close to my heart :)
 

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

Latest Threads

Latest Profile Posts

I'm sure this question's been asked before, but I can't find an answer: is there any way to run the Steam version of RPGMaker *without* being logged into Steam? It's a frustrating extra step sometimes.
Been seeing bright flashes in the sky for a while, and thought it was my eyes playing tricks on me. Nope! Lightning. Now it's raining heavily. Seeing lightning, but not hearing thunder, is weird.
Small Update for myself:
>Two Dungeon maps are done, Dungeon 3 is in progress. Dungeon 4 needs work.
>4 characters set up out of a possible 6 but those come later.
>Hub area needs work.
>Game completion..., 20-25% maybe?
Does anyone else find writing dialogue hard?
You guys want more tutorials? Well not only are we doing new video tutorials with Driftwood, we've also brought in two contributing authors for the blog to get a new tutorial every week! First one here from @Avery
https://blog.rpgmakerweb.com/tutorials/tutorial-mapping-interior/

Next week we'll get something from @hiddenone

Forum statistics

Threads
100,713
Messages
978,650
Members
132,325
Latest member
ighjku
Top