Battle changes, first pass.
This commit is contained in:
+23
-119
@@ -9,7 +9,10 @@
|
||||
#include "rpg/battle/battle.h"
|
||||
#include "rpg/cutscene/cutscenesystem.h"
|
||||
|
||||
// Fighter slots as added by test_battleCutsceneForceActionOverridesTarget:
|
||||
// TEMPORARY: hardcoded pending a real moves.jsonc-backed registry lookup.
|
||||
#define BATTLE_MOVE_ID_ATTACK ((battlemoveid_t)1)
|
||||
|
||||
// Fighter slots as added by test_battleCutsceneForceMoveOverridesTarget:
|
||||
// 0 = allyA, 1 = allyB, 2 = enemy.
|
||||
static cutsceneitem_t CUTSCENE_TEST_SCRATCH_ITEMS[] = {
|
||||
// Waiting BEFORE pausing is the correct order: pausing first would
|
||||
@@ -22,8 +25,8 @@ static cutsceneitem_t CUTSCENE_TEST_SCRATCH_ITEMS[] = {
|
||||
{ .type = CUTSCENE_ITEM_TYPE_SET_PAUSE, .setPause = CUTSCENE_PAUSE_BATTLE },
|
||||
// enemy (slot 2) forced onto allyA (slot 0)
|
||||
{
|
||||
.type = CUTSCENE_ITEM_TYPE_BATTLE_FORCE_ACTION,
|
||||
.battleForceAction = { .fighterIndex = 2, .targetIndex = 0 }
|
||||
.type = CUTSCENE_ITEM_TYPE_BATTLE_FORCE_MOVE,
|
||||
.battleForceMove = { .fighterIndex = 2, .targetIndex = 0 }
|
||||
},
|
||||
{ .type = CUTSCENE_ITEM_TYPE_SET_PAUSE, .setPause = CUTSCENE_PAUSE_NONE }
|
||||
};
|
||||
@@ -101,13 +104,13 @@ static void test_battlePlayerSelectionAdvancesAndSkipsDecidedFighters(
|
||||
assert_int_equal(BATTLE.state, BATTLE_STATE_PLAYER_SELECTION);
|
||||
assert_ptr_equal(battleGetCurrentFighter(), ally1);
|
||||
|
||||
// A forced action (as a scripted-battle cutscene item would push) marks
|
||||
// A forced move (as a scripted-battle cutscene item would push) marks
|
||||
// ally2 as already decided, so PLAYER_SELECTION must skip it.
|
||||
battleQueueAction(ally2->id, BATTLE_ACTION_ATTACK, enemy->id);
|
||||
battleQueueMove(ally2->id, BATTLE_MOVE_ID_ATTACK, enemy->id);
|
||||
|
||||
battlePlayerAttack(enemy->id);
|
||||
assert_int_equal(BATTLE.actions[ally1->id].type, BATTLE_ACTION_ATTACK);
|
||||
assert_int_equal(BATTLE.actions[ally1->id].targetIndex, enemy->id);
|
||||
assert_int_equal(BATTLE.moves[ally1->id].move, BATTLE_MOVE_ID_ATTACK);
|
||||
assert_int_equal(BATTLE.moves[ally1->id].targetIndex, enemy->id);
|
||||
|
||||
// ally2 already had a decision queued, and enemy is AI-controlled, so
|
||||
// there's nothing left for PLAYER_SELECTION -- it should have moved on.
|
||||
@@ -134,8 +137,8 @@ static void test_battleAiSelectionAutoQueuesUndecidedAiFighters(void **state) {
|
||||
|
||||
battleUpdate();// AI_SELECTION auto-decides for enemy
|
||||
assert_int_equal(BATTLE.state, BATTLE_STATE_MOVES_EXECUTING);
|
||||
assert_int_equal(BATTLE.actions[enemy->id].type, BATTLE_ACTION_ATTACK);
|
||||
assert_int_equal(BATTLE.actions[enemy->id].targetIndex, ally->id);
|
||||
assert_int_equal(BATTLE.moves[enemy->id].move, BATTLE_MOVE_ID_ATTACK);
|
||||
assert_int_equal(BATTLE.moves[enemy->id].targetIndex, ally->id);
|
||||
}
|
||||
|
||||
static void test_battleMovesExecutingResolvesInSpeedOrderAndFizzles(
|
||||
@@ -278,107 +281,12 @@ static void test_battleFleeUnavailableIsIgnored(void **state) {
|
||||
assert_int_equal(BATTLE.result, BATTLE_RESULT_NONE);
|
||||
}
|
||||
|
||||
static uint8_t stateChangedCount;
|
||||
static battlestate_t stateChangedLog[32][2];
|
||||
|
||||
static void recordStateChanged(
|
||||
const battlestate_t previous,
|
||||
const battlestate_t next
|
||||
) {
|
||||
if(stateChangedCount >= 32) return;
|
||||
stateChangedLog[stateChangedCount][0] = previous;
|
||||
stateChangedLog[stateChangedCount][1] = next;
|
||||
stateChangedCount++;
|
||||
}
|
||||
|
||||
static void test_battleOnStateChangedFiresAcrossFullRound(void **state) {
|
||||
|
||||
|
||||
battleInit();
|
||||
stateChangedCount = 0;
|
||||
BATTLE.onStateChanged = recordStateChanged;
|
||||
|
||||
addFighter(
|
||||
BATTLE_FIGHTER_TEAM_ALLY, BATTLE_FIGHTER_CONTROLLER_PLAYER, 1, 0, 10, 100
|
||||
);
|
||||
battlefighter_t *enemy = addFighter(
|
||||
BATTLE_FIGHTER_TEAM_ENEMY, BATTLE_FIGHTER_CONTROLLER_AI, 1, 0, 5, 100
|
||||
);
|
||||
|
||||
battleStart(BATTLE_ENCOUNTER_REGULAR, true);// fires NONE -> OPENING
|
||||
battleUpdate();// OPENING -> PRE_ROUND
|
||||
battleUpdate();// PRE_ROUND -> PLAYER_SELECTION
|
||||
battlePlayerAttack(enemy->id);// PLAYER_SELECTION -> AI_SELECTION
|
||||
battleUpdate();// AI_SELECTION -> MOVES_EXECUTING
|
||||
battleUpdate();// MOVES_EXECUTING: ally's attack resolves (still executing)
|
||||
battleUpdate();// MOVES_EXECUTING: enemy's attack resolves (still executing)
|
||||
battleUpdate();// both slots processed -> POST_ROUND
|
||||
battleUpdate();// POST_ROUND -> PRE_ROUND, round advances
|
||||
|
||||
assert_int_equal(stateChangedLog[0][0], BATTLE_STATE_NONE);
|
||||
assert_int_equal(stateChangedLog[0][1], BATTLE_STATE_OPENING);
|
||||
assert_int_equal(stateChangedLog[1][1], BATTLE_STATE_PRE_ROUND);
|
||||
assert_int_equal(stateChangedLog[2][1], BATTLE_STATE_PLAYER_SELECTION);
|
||||
assert_int_equal(stateChangedLog[3][1], BATTLE_STATE_AI_SELECTION);
|
||||
assert_int_equal(stateChangedLog[4][1], BATTLE_STATE_MOVES_EXECUTING);
|
||||
assert_int_equal(stateChangedLog[5][1], BATTLE_STATE_POST_ROUND);
|
||||
assert_int_equal(stateChangedLog[6][1], BATTLE_STATE_PRE_ROUND);
|
||||
|
||||
BATTLE.onStateChanged = NULL;
|
||||
}
|
||||
|
||||
static uint8_t actionDecidedCount;
|
||||
|
||||
static void recordActionDecided(
|
||||
const battlefighter_t *fighter,
|
||||
const battleaction_t *action
|
||||
) {
|
||||
actionDecidedCount++;
|
||||
}
|
||||
|
||||
static void test_battleOnActionDecidedFiresPerFighterRegardlessOfSource(
|
||||
void **state
|
||||
) {
|
||||
|
||||
|
||||
battleInit();
|
||||
actionDecidedCount = 0;
|
||||
BATTLE.onActionDecided = recordActionDecided;
|
||||
|
||||
battlefighter_t *ally1 = addFighter(
|
||||
BATTLE_FIGHTER_TEAM_ALLY, BATTLE_FIGHTER_CONTROLLER_PLAYER, 1, 0, 10, 100
|
||||
);
|
||||
battlefighter_t *ally2 = addFighter(
|
||||
BATTLE_FIGHTER_TEAM_ALLY, BATTLE_FIGHTER_CONTROLLER_PLAYER, 1, 0, 9, 100
|
||||
);
|
||||
battlefighter_t *enemy = addFighter(
|
||||
BATTLE_FIGHTER_TEAM_ENEMY, BATTLE_FIGHTER_CONTROLLER_AI, 1, 0, 5, 100
|
||||
);
|
||||
|
||||
battleStart(BATTLE_ENCOUNTER_REGULAR, true);
|
||||
battleUpdate();// OPENING -> PRE_ROUND
|
||||
battleUpdate();// PRE_ROUND -> PLAYER_SELECTION
|
||||
|
||||
// A forced action, as CUTSCENE_BATTLE_FORCE_ACTION would push, counts too.
|
||||
battleQueueAction(ally2->id, BATTLE_ACTION_ATTACK, enemy->id);
|
||||
assert_int_equal(actionDecidedCount, 1);
|
||||
|
||||
battlePlayerAttack(enemy->id);// ally1's real decision
|
||||
assert_int_equal(actionDecidedCount, 2);
|
||||
assert_int_equal(BATTLE.state, BATTLE_STATE_AI_SELECTION);
|
||||
|
||||
battleUpdate();// AI_SELECTION decides for enemy
|
||||
assert_int_equal(actionDecidedCount, 3);
|
||||
|
||||
BATTLE.onActionDecided = NULL;
|
||||
}
|
||||
|
||||
// Exercises CUTSCENE_BATTLE_WAIT_STATE + CUTSCENE_SET_PAUSE(BATTLE) +
|
||||
// CUTSCENE_BATTLE_FORCE_ACTION together, driving the cutscene and battle
|
||||
// CUTSCENE_BATTLE_FORCE_MOVE together, driving the cutscene and battle
|
||||
// systems side by side the way rpg.c's main loop does, to prove a
|
||||
// partially-scripted round actually works end-to-end (not just that each
|
||||
// item type compiles).
|
||||
static void test_battleCutsceneForceActionOverridesTarget(void **state) {
|
||||
static void test_battleCutsceneForceMoveOverridesTarget(void **state) {
|
||||
|
||||
|
||||
battleInit();
|
||||
@@ -411,22 +319,22 @@ static void test_battleCutsceneForceActionOverridesTarget(void **state) {
|
||||
assert_int_equal(BATTLE.state, BATTLE_STATE_PRE_ROUND);
|
||||
assert_true(CUTSCENE_SYSTEM.pause & CUTSCENE_PAUSE_BATTLE);
|
||||
|
||||
cutsceneSystemUpdate();// SET_PAUSE done -> FORCE_ACTION queues enemy's move
|
||||
cutsceneSystemUpdate();// SET_PAUSE done -> FORCE_MOVE queues enemy's move
|
||||
battleUpdate();// still paused: no-op
|
||||
|
||||
assert_int_equal(BATTLE.actions[enemy->id].type, BATTLE_ACTION_ATTACK);
|
||||
assert_int_equal(BATTLE.actions[enemy->id].targetIndex, allyA->id);
|
||||
assert_int_equal(BATTLE.moves[enemy->id].move, BATTLE_MOVE_ID_ATTACK);
|
||||
assert_int_equal(BATTLE.moves[enemy->id].targetIndex, allyA->id);
|
||||
assert_int_equal(BATTLE.state, BATTLE_STATE_PRE_ROUND);// still frozen
|
||||
|
||||
cutsceneSystemUpdate();// FORCE_ACTION done -> SET_PAUSE(NONE) lifts it
|
||||
cutsceneSystemUpdate();// FORCE_MOVE done -> SET_PAUSE(NONE) lifts it
|
||||
battleUpdate();// unpaused: PRE_ROUND -> PLAYER_SELECTION, allyA first
|
||||
|
||||
assert_false(CUTSCENE_SYSTEM.pause & CUTSCENE_PAUSE_BATTLE);
|
||||
assert_int_equal(BATTLE.state, BATTLE_STATE_PLAYER_SELECTION);
|
||||
assert_ptr_equal(battleGetCurrentFighter(), allyA);
|
||||
// The forced action from three frames ago survived untouched.
|
||||
assert_int_equal(BATTLE.actions[enemy->id].type, BATTLE_ACTION_ATTACK);
|
||||
assert_int_equal(BATTLE.actions[enemy->id].targetIndex, allyA->id);
|
||||
// The forced move from three frames ago survived untouched.
|
||||
assert_int_equal(BATTLE.moves[enemy->id].move, BATTLE_MOVE_ID_ATTACK);
|
||||
assert_int_equal(BATTLE.moves[enemy->id].targetIndex, allyA->id);
|
||||
|
||||
battlePlayerAttack(enemy->id);// allyA -> AI_SELECTION would be next...
|
||||
assert_ptr_equal(battleGetCurrentFighter(), allyB);
|
||||
@@ -437,7 +345,7 @@ static void test_battleCutsceneForceActionOverridesTarget(void **state) {
|
||||
// AI_SELECTION: enemy already decided, skipped -> MOVES_EXECUTING
|
||||
battleUpdate();
|
||||
// untouched
|
||||
assert_int_equal(BATTLE.actions[enemy->id].targetIndex, allyA->id);
|
||||
assert_int_equal(BATTLE.moves[enemy->id].targetIndex, allyA->id);
|
||||
|
||||
battleUpdate();// allyA attacks enemy
|
||||
battleUpdate();// allyB attacks enemy
|
||||
@@ -458,11 +366,7 @@ int main(int argc, char** argv) {
|
||||
cmocka_unit_test(test_battleLossEndsInStateEnded),
|
||||
cmocka_unit_test(test_battleFleeEndsInStateEndedImmediately),
|
||||
cmocka_unit_test(test_battleFleeUnavailableIsIgnored),
|
||||
cmocka_unit_test(test_battleOnStateChangedFiresAcrossFullRound),
|
||||
cmocka_unit_test(
|
||||
test_battleOnActionDecidedFiresPerFighterRegardlessOfSource
|
||||
),
|
||||
cmocka_unit_test(test_battleCutsceneForceActionOverridesTarget),
|
||||
cmocka_unit_test(test_battleCutsceneForceMoveOverridesTarget),
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
|
||||
Reference in New Issue
Block a user