Player: Battle System Refactors

Created on 15 Dec 2018  路  12Comments  路  Source: EasyRPG/Player

This issue is to list the large scale battle system refactors that need to be done.

  • [x] Refactor the battle 2k message window

Remove the hacks from the battle PRs and have a more streamlined and simple battle console.

  • [ ] Do a comprehensive retest of battle algorithms

I did some sampling and noticed some small differences in battle action results. Maybe rounding of variant is wrong? Maybe our RNG behaves differently?

First verify results with no variance, then add variance.

  • [ ] Consolidate skill effects

Consolidate skill effects and other battle related algorithms into one place. In particular, one code path should be used for skills and items in battle and in the menu.

  • [ ] Remove the Game_Battler class inheritance heirarchy

Game_Actor should stand alone and not inherit from Game_Battler. The problem is that Game_Battler contains battle state, and this should not exist outside of battle. Instead Game_Battler objects should be created on battle start and maintain a pointer to an actor or enemy. When the battle ends, all Game_Battler's will be destroyed.

The benefits are:

  • Guaranteed cleanup of all battle state when battle ends.
  • Better separation of battle vs non-battle concerns
  • De-virtualization of all battler methods, improving performance
  • Separate battler types for 2k/2k3 battles as needed (maybe with a common base)
  • Easily allows custom battler types for games that want to write custom battle systems.
  • [ ] Refactor SourceAnimationState concept

Animation state is 2k3 specific. It should live in some 2k3 specific entity. Enemy Death and Self-Destruct in 2k will need to be moved to their own special place.

Battle Duplicate Refactor

All 12 comments

@CherryDT

I did some testing and noticed that for RPG_RT, the variance applied to damage calculations may not use a uniform distribution. I found the that the edges (X-20%,X+20%) seem to occur more rarely than other values.

Is RPG_RT using any special distribution here?

(From Discord)

Cherry, what distribution is used for variance and m battles? It seems not uniform as the min max are rare

Hm but I think it's uniform, at least in 2k... should be like this:

function applyVariance(value, variance) {
  let range = Math.floor(value * variance / 10)
  if (range < 1) range = 1
  return value + Math.floor(Math.random() * (range + 1)) - Math.floor(range / 2)
}

_Maybe_ it's different in 2k3...

@CherryDT

Another one I just remembered.

Do you know the frame timings in between battle messages in the rm2k battle system? We now have 2k battles nearly feature complete but I think we are still off for the delay timings on some of those messages.

It will be very time consuming to research and list them here, there are so many different times used for various things in the battle.

You are better off testing it yourself.

There is a function, let's just call it Wait(min, max). It will wait for max frames. Enter or Shift (regardless of testplay) will skip the wait if pressed after min frames. If escape is pressed, it will extend the wait until escape is released. Clarification: The triggering doesn't matter, only the current state, so holding down Enter or Shift will skip everything as soon as possible all the time.

_Note: especially in older versions the escape key feature wouldn't work indefinitely as soon as the game ran on more than one processor, due to the same race condition that caused key input processing to randomly return 0 for one frame even thought the key was continuously held down._

On RM2kE 1.62, this function is at virtual address 0x479487 0x479484 (_EDIT: doesn't matter here though, this guide will work either way_), and min and max are passed in registers edx and ecx.

So, to log the calls, you can do this: (Windows)

  1. Download x64dbg
  2. Start x32dbg.exe (yes, 32)
  3. Attach to the game using File->Attach (or open the RPG_RT.exe and start it) - must be RM2kE v1.62!
  4. Press Ctrl+G ("Follow Expression"), enter the address 479487, click "OK"
    image
  5. Press F2 to set a breakpoint
  6. Right-click the breakpoint which now shows in red, use Breakpoint->Edit
    image
  7. Set "Break Condition" to 0 and "Log Text" to Wait({d:edx}, {d:ecx})
    image
  8. Switch to "Log" tab
  9. Resume game in case it was paused, using F9
  10. Do a battle and watch how all the calls to the Wait function are logged in the debugger!
    image

(Correction: Shift also skips only after min frames.)

By the way, if you _don't_ set the "Break Condition" to 0, but leave it empty or set to 1, you will actually break every time the wait function is called. May also be useful to understand more exactly when it is called. You can resume with F9.

By the way, if you need this for RM2k3 too, on RM2k3 v1.09a+ (that is, both inofficial v1.09a+ and official v1.10+, since they are all just huge hacks on top of the v1.09a binary) the address is 4966B8.

(Also, I realized the address is actually 479484 and not 479487 in RM2k, it just happens to not make a difference to log at 479787 :D)

@CherryDT

Here's a debugger case that would be very useful. I'd like to get a printout of all damage numbers that occur from attacks, skills, healing, etc.. actions in battle.

Basically what i want to do is some distribution testing of damage to ensure the numbers from RPG_RT match Player. For this I need a lot of samples.

For example, put the hero on autobattle, record damage numbers for 200 attacks, and compare the distributions from RPG_RT and Player.

I noticed some differences before so this is important for difficulty scaling to match.

Self Destruct Damage Algo:

   v8 = (*RPG::Inventory)->party.size;
    v9 = 0;
    do
    {
      v10 = LookupActorByPartyIndex(*RPG::Inventory, v9);
      if ( BattlerExists(v10) )
      {
        v2->actionMessageLineAmount = 1;
        sub_47AFC8(v2);
        DisplayBattleMessage(v2, 4, 4);
        v11 = LookupActorByPartyIndex(*RPG::Inventory, v9);
        v17 = GetActorDef(v11) / 2;
        v12 = GetBattlerAtk(&v25->battler);
        v24 = max(0, v12 - v17);
        if ( v24 > 0 )
        {
          v23 = max(1, 4 * v24 / 10);
          v13 = RPG_RandExclusive(v23 + 1);
          v24 += v13 - v23 / 2;
        }
        v17 = 100;
        v14 = LookupActorByPartyIndex(*RPG::Inventory, v9);
        ProcessDamage(v2, v14, v24, v9, v8, v2, 10, v17);
      }
      ++v9;
      --v8;
    }
    while ( v8 );

Superseded by #2399

anything of interest in here for the release blog?

If yes: Milestone it
If not: Label it with "Duplicate", thx

Labeled it, we can use #2399 for release blog

Was this page helpful?
0 / 5 - 0 ratings

Related issues

s9060666 picture s9060666  路  14Comments

zell180 picture zell180  路  23Comments

Ghabry picture Ghabry  路  13Comments

s9060666 picture s9060666  路  12Comments

jenes2 picture jenes2  路  13Comments