Saturday, December 07, 2013

Implementing BattleExecute and IAction questions

Recently I had a twitter question from  @TwitchGamez about the JRPG primer I wrote for GameDevTuts+. Here's the message:

Hi, I was wondering if I could ask you a question regarding your JRPG Primer? Namely, the BattleExecute state. Here's my implementation of it (along with BattleTick) in C#:

    public class BattleTick : FSMState {
            private StateMachine battleFSM;
            private List actions;
     
            public BattleTick(StateMachine battleFSM, List actions) {
                    this.battleFSM = battleFSM;
                    this.actions = actions;
            }
     
            public override void Enter(params object[] info) {
                    foreach (object o in info) {
                            BattleAction action = (BattleAction)o;
                            actions.Add(action);
                    }
            }
     
            public override void Update() {
                    BattleAction topAction = actions[0];
     
                    topAction.Update();
                    if (topAction.IsReady) {
                            actions.RemoveAt(0);
                            battleFSM.ChangeState(StateID.BattleExecute, topAction);
                    }
            }
    }
     
    public class BattleExecute : FSMState {
            private StateMachine battleFSM;
            private List actions;
     
            private BattleAction action;
     
            public BattleExecute(StateMachine battleFSM, List actions) {
                    this.battleFSM = battleFSM;
                    this.actions = actions;
            }
     
            public override void Enter(params object[] info) {
                    action = (BattleAction)info[0];
            }
     
            public override void Update() {
                    action.Update();
     
                    if (action.IsReady) battleFSM.ChangeState(StateID.BattleTick);
            }
     
            public override void Exit() {
                    if (action.unit.playerControlled) actions.Add(new PlayerDecide(action.unit));
                    else actions.Add(new AIDecide(action.unit));
            }
    }

My question is: how do you add new actions to the action queue from the PlayerDecide and AIDecide classes? In your code examples, you don't pass a reference to the queue to those classes. So, if, for example, AIDecide decided it wanted to attack, how would it tell BattleTick it wanted to add an AttackAction to the queue?

Hopefully this question isn't too confusing. Thanks.

Ok, it's been a while, so I've just looked at the code. I'll just reason through it, just like I'm reading it for the first time and we'll see where we get.

There's a state machine with two states; tick and execute. As the battle progresses the context flicks back and forth between these states controlling what happens.

To begin with, all the entities involved in the battle have a decide-action added to the queue.

The statemache is populated with a decide-action for every entity in the battle, with the player controlled characters requiring the player to decide. The game starts in the tick state, looking at the code, it calls Update on all the actions and then checks if the top action is ready. If it is, it changes state and passes the top action through to the execute state.

The code isn't provided for the execute state! But we can get some hints about it from the constructor in the parent battle state:

mBattleStates.Add("execute", new BattleExecute(mBattleStates, mActions));

Execute takes in all the BattleStates which is the statemachine running the combat and the list of actions. So if the action is an AIDecide it will have access to the mActions list via the execute state. If it's a PlayerDecide state, it will have access to the mActions list like the enemy and it can switch to a menu for the player to input choices by using mBattleStates.

The code isn't provided for IAction interface but I imagine it would be need a Run or Execute function that would take in the mActions list and probably the mBattleStates list too. Then any action is free to interact with either of those lists.

Hopefully this answers your question! Actions never directly tell BattleTick anything but along with BattleExecute, it will manipulate the action queue.

I remember the idea of having these two states is to accomodate something like Summon from Final Fantasy; an action that's going to take a long time to run. It uses the Execute state to block combat while all the animations and effects play out.