Bug found? Yanflys BattleSystemATB + Yanflys BattleAiCore = Skip enemies OnTurnStart events after being targeted by actor

romepizza

Veteran
Veteran
Joined
Nov 9, 2015
Messages
45
Reaction score
1
First Language
German
Hello fellow RPG-Makers,

first of all here is a disclaimer: I recently read an article about Yanfly stating that she discontined working on the ATB, so if the bug has something to do with that, noone can help it. But I do not have the feeling that the bug Ipotentially have found is related to that fact.

You can reproduce with a blank project and a bit of setup. What I have found is that when you use ATB and the BattleAiCore, the first onStartTurn events of enemies are not called after an actor has chosen its action to execute (the actual execution does not matter heree). After those one skipped onStartTurn events after an actor has made a choice of actionr, the other onStartTurn events are called in the expected behaviour.

I found it when trying to make the enemy use a skill with a cooldown. The first time, the enemy used the skill normally but then took way too many turns to be ready again. After that I tried calling a simple <Custom Turn Start Effect> with a single console.log inside a state that the enemy was affected by (utilizing Yanflys BuffsStatesCore). This message is not logged. I then made some console.log's inside yanflys scripts and nothing was printed either. In particular, I tried to get a log of the point, in which a battler can decide its action inside the updateATBPhase() method of Yanflys BattleSystemAtb script, but as expected, the log does not show up after the enemy is hit whereas it does show up if it was not previously hit.

I have no clue where is coming from. I have looked through the code of both ATB and AI plugins but found nothing which has to do with being targeted or an actor deciding for an action.

-------------------------------------------------------------------------------------------------------------

I have the minimum specs project already set up, but I don't know if I can or should just upload it here, so let me know if you want me to post it here. Otherwise I will give instructions on how to recreate it:

(1) Create empty project, load in the following plugins (in that order, from top to bottom):
1.1 Yanflys YEP_CoreEngine
1.2 Yanflys YEP_BattleEngineCore
1.3 Yanflys YEP_X_BattleSysATB
1.4 YEP_X_VisualATBGauge (optional, but helpful if you want to track down when the actual onTurnStart should be called)
1.5 YEP_BattleAiCore
1.6 YEP_BattleStatesCore (optionally, so you can add two console.log inside <Custom Turn Start/End Effect> to track down when the OnStartTurn events are called and when not)

(2). In the YEP_BattleEngineCore plugins parameters, chose ATB as default battle system.
(2.1) (optional, easier to debug) In YEP_X_BattleSysATB disable rubberbanding and set Full Gauge and Charge Gauge constaant to something like 2000 and 1000.

(3) Setup an enemy:
(3.1) Create state which implements said <Custom Turn Start/End Effect> console.log("Turn Start/End"); </Custom Turn Start/End Effect> to track down what is going on.
(3.2) Make skill which adds the state from (3) on owner and make enemy learn only that skill (no need to use BattleAiCores special way of making a priority skill list).
(3.3) Set agi to something like 50.

(4) Setup actor: Set agi to about half of enemies agi: 24 (NOTE: I have only tested stuff with a single actor vs a single enemy!)

(5) Playtest and see results.

-------------------------------------------------------------------------------------------------------------

Expected behaviour: The console.log("Turn Start"); should always log to the console as soon as the enemy decides fo an action.
Actual behaviour: The log is not called when the actor has made a decision prior.


If anyone wants take the time and take a look into it, that would be awesome. Otherwise I would have to make some heavy compromises like dropping the AI core :(

Also if anyone has a comment about the way I have structured the bug report, let me know. I have never written a bug report of something such specific. I know it is a wall of text, but I personally like more information more than less information, so here we go ...
 

romepizza

Veteran
Veteran
Joined
Nov 9, 2015
Messages
45
Reaction score
1
First Language
German
Ok, I have found and fixed. As I have found out, while the battle manager is looking for the next battler to enter the phase where the battler can chose his action (which you enter as soon as you atb gauge is full), there is one single if statement in the progress, which checks if the battler already has an action to perform. If this is the case, a certain 'route' of code is skipped, where the battler would actually decide what action to take.

In that skipped route though is the crutial statement which tells the battler to call its onTurnStart() method. So whenever the battler now fills its gauge to 100% and for some reason has already decided for an action prior, the onTurnStart() method will be skipped. To fix this, you can either add the line battler.onTurnStart(); in the route where the battler has already decided a skill prior, OR add this line of code at the point in which it actually transits into the state of charging. If doing so, you will also have to delete the line of code in the route where no action has been decided prior. Otherwise you would trigger two onTurnStart events at once most of the time.

BUT, I do not know why an enemy decides for an action as soon as the player decides for an action. It most likely has something to do with BattleAiCore, which might consider the actions of the player too and decide earlier than at the start of the turn or something. Don't ask me what exactly is going on.

------------------------------------------------------------------------------------------------------

For anyone interested in the actual code and for general educational purposes, I will explain the solution in a bit more detail. Everything takes place in YEP_X_BattleSysATB.

(1)The whole process starts in BattleManager.update():

JavaScript:
Yanfly.ATB.BattleManager_update = BattleManager.update;
BattleManager.update = function() {
    if (this.isATB()) {
      if (this.isBusy()) return;
      if (this.updateEvent()) return;
      if (this._phase === 'battleEnd') {
        return Yanfly.ATB.BattleManager_update.call(this);
      }
      if (this.checkBattleEnd()) return;
      if (this._phase === 'atb') {
        this.updateATBPhase(); // point if interest
      } else {
        Yanfly.ATB.BattleManager_update.call(this);
      }
    } else {
      Yanfly.ATB.BattleManager_update.call(this);
    }
};
Yanfly overrides this method and at some point updates the whole ATB process in which at some point, this.updateATBPhase() is called.


(2) That method updates the atb and charge values of each battler. If before updating there is any battler who has 100% atb gauge or 100% charge gauge, that respective battler will either start chose its action (in case of 100% atb gauge) or perform its prior chosen action (in case of 100% charge gauge):

JavaScript:
BattleManager.updateATBPhase = function() {
    var chargedBattler = this.getChargedATBBattler();
    if (chargedBattler) return this.startATBAction(chargedBattler);
    var readyBattler = this.getReadyATBBattler();
    if (readyBattler) {
      return this.startATBInput(readyBattler);
    } else {
      this.updateATBTicks();
      this.updateBattlerATB();
    }
};
In order to find a full atb gauge battler, it calls getReadyATBBattler(). That method will loop over every battler in the battle and check if it is fully atb gauged. If one has been found, that battler can chose its action. THERE, the battler.onTurnStart() function is called:

Code:
BattleManager.startATBInput = function(battler) {
    if (battler.isDead()) return;
    battler.onTurnStart();
    // ... more code is executed here that I omitted,
    // but 'battler.onTurnStart();' is called right
    // at the beginning
};

(3) Now the crutial part takes place in the line 'var readyBattler = this.getReadyATBBattler();', more specificly in that function:

Code:
BattleManager.isBattlerATBReady = function (battler) {
    if (battler.atbRate() < 1) return false;
    if (battler.isATBCharging()) return false;
    
    if (battler.currentAction() && battler.currentAction().item()) {
    
      // After the player made a decision, battler.currentAction()
      // of a battler is not null, meaning that the enemy does not
      // need to go into the state of startATBInput.
      // This would causes the battler.onTurnStart() call in
      // startATBInput to not be called, so I added it down there.
      // Uncomment 'battler.onTurnStart();' for the whole thing to work.
      
      // battler.onTurnStart();
      battler.makeActions();
      battler.setupATBCharge();
      return false;
    }
    return true;
};
As you can see, if a battler already has an action, the battler will immediately continue with makeActions() and setupATBCharge() and return true, which then in result in updateATBPhase will say that there is no battler who is fully atb gauged, which THEN will result in skipping the function startATBInput () and thus in skipping the line of code 'battler.onTurnStart();'. My solution was to simply add 'battler.onTurnStart();' in case that the battler already has an action.

-------------------------------------------------------------------------------------------------

WARNING: I do NOT guarantee that this is 100% save and will not create any further bugs. Who knows at what other points whatever code might be called that I am missing, but I will go with my solution for now and if I ever see it causing problems, I will come back to this thread and make a reply.
 

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

Latest Threads

Latest Posts

Latest Profile Posts

Couple hours of work. Might use in my game as a secret find or something. Not sure. Fancy though no? :D
Holy stink, where have I been? Well, I started my temporary job this week. So less time to spend on game design... :(
Cartoonier cloud cover that better fits the art style, as well as (slightly) improved blending/fading... fading clouds when there are larger patterns is still somewhat abrupt for some reason.
Do you Find Tilesetting or Looking for Tilesets/Plugins more fun? Personally I like making my tileset for my Game (Cretaceous Park TM) xD
How many parameters is 'too many'??

Forum statistics

Threads
105,862
Messages
1,017,047
Members
137,569
Latest member
Shtelsky
Top