diff --git a/assets/battle/moves.jsonc b/assets/battle/moves.jsonc index f6870e77..1ab6a329 100644 --- a/assets/battle/moves.jsonc +++ b/assets/battle/moves.jsonc @@ -1,11 +1,21 @@ { "moves": { "fire": { - "ability": "FIRE", - "power": 12, - "mpCost": 4, - "target": "SINGLE_ENEMY", - "cutscene": "cutscenes/battle/fire.jsonc" + // Target flags + "enemies": true, + "allies": true, + "multiple": false, + "self": true, + "default": "enemy", + "target": "SINGLE_ENEMY" + }, + + "slash": { + "enemies": true, + "allies": true, + "self": false, + "default": "enemy", + "target": "SINGLE_ENEMY" } } } diff --git a/assets/cutscenes/battle/moves/fire.jsonc b/assets/cutscenes/battle/moves/fire.jsonc new file mode 100644 index 00000000..0b455d28 --- /dev/null +++ b/assets/cutscenes/battle/moves/fire.jsonc @@ -0,0 +1,9 @@ +{ + "items": [ + { + "type": "MODAL", + "title": "BATTLE TEST", + "message": "BATTLE TEST" + } + ] +} \ No newline at end of file diff --git a/assets/cutscenes/battle/moves/slash.jsonc b/assets/cutscenes/battle/moves/slash.jsonc new file mode 100644 index 00000000..0b455d28 --- /dev/null +++ b/assets/cutscenes/battle/moves/slash.jsonc @@ -0,0 +1,9 @@ +{ + "items": [ + { + "type": "MODAL", + "title": "BATTLE TEST", + "message": "BATTLE TEST" + } + ] +} \ No newline at end of file diff --git a/assets/locale/en_US.po b/assets/locale/en_US.po index d45ecd33..9f1242b0 100644 --- a/assets/locale/en_US.po +++ b/assets/locale/en_US.po @@ -266,6 +266,10 @@ msgstr "loading" msgid "ui.autosave.saving" msgstr "SAVING" + +# +# Items +# msgid "item.potion.name" msgstr "Potion" @@ -273,4 +277,13 @@ msgid "item.potato.name" msgstr "Potato" msgid "item.apple.name" -msgstr "Apple" \ No newline at end of file +msgstr "Apple" + +# +# Moves +# +msgid "move.fire.name" +msgstr "Fire" + +msgid "move.slash.name" +msgstr "Slash" \ No newline at end of file diff --git a/src/dusk/asset/assetdsk.h b/src/dusk/asset/assetdsk.h index 6be8644a..efb28800 100644 --- a/src/dusk/asset/assetdsk.h +++ b/src/dusk/asset/assetdsk.h @@ -12,13 +12,16 @@ // "DSK2" - distinct from a plain zip's "PK\x03\x04" so a stray plain zip // never gets misread as a valid dusk.dsk. #define ASSET_DSK_MAGIC_SIZE 4 -#define ASSET_DSK_MAGIC "DSK2" +#define ASSET_DSK_MAGIC "DSK" #define ASSET_DSK_VERSION 2 -// magic(4) + version(4) + size(4) + checksum(4), all little-endian -// regardless of host - see assetDskParseHeader. The zip archive itself -// starts immediately after, at ASSET_DSK_HEADER_SIZE. -#define ASSET_DSK_HEADER_SIZE 16 +// Magic + version + size + checksum +#define ASSET_DSK_HEADER_SIZE ( \ + ASSET_DSK_MAGIC_SIZE + \ + sizeof(uint32_t) + \ + sizeof(uint32_t) + \ + sizeof(uint32_t) \ +) /** * Parsed dusk.dsk (DSK2 format) header. dusk.dsk is a single uncompressed diff --git a/src/dusk/rpg/battle/CMakeLists.txt b/src/dusk/rpg/battle/CMakeLists.txt index 5b0a546c..dc397b16 100644 --- a/src/dusk/rpg/battle/CMakeLists.txt +++ b/src/dusk/rpg/battle/CMakeLists.txt @@ -7,6 +7,8 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC battle.c - battlefighter.c party.c ) + +add_subdirectory(fighter) +add_subdirectory(move) diff --git a/src/dusk/rpg/battle/battle.c b/src/dusk/rpg/battle/battle.c index 0a3ec94f..0d9ad040 100644 --- a/src/dusk/rpg/battle/battle.c +++ b/src/dusk/rpg/battle/battle.c @@ -10,10 +10,14 @@ #include "util/memory.h" #include "rpg/cutscene/cutscenesystem.h" +// TEMPORARY: hardcoded pending a real moves.jsonc-backed registry lookup. +#define BATTLE_MOVE_ID_ATTACK ((battlemoveid_t)1) + battle_t BATTLE; void battleInit(void) { memoryZero(&BATTLE, sizeof(battle_t)); + for(uint8_t i = 0; i < BATTLE_FIGHTER_COUNT_MAX; i++) { BATTLE.fighters[i].id = i; } @@ -103,18 +107,14 @@ battleresult_t battleCheckResult(void) { return BATTLE.result; } -void battleQueueAction( +void battleQueueMove( const uint8_t fighterIndex, - const battleactiontype_t type, + const battlemoveid_t move, const uint8_t targetIndex ) { - battleaction_t *action = &BATTLE.actions[fighterIndex]; - action->type = type; + battlemoveaction_t *action = &BATTLE.moves[fighterIndex]; + action->move = move; action->targetIndex = targetIndex; - - if(BATTLE.onActionDecided != NULL) { - BATTLE.onActionDecided(&BATTLE.fighters[fighterIndex], action); - } } void battlePlayerAttack(const uint8_t targetIndex) { @@ -123,7 +123,7 @@ void battlePlayerAttack(const uint8_t targetIndex) { if(targetIndex >= BATTLE_FIGHTER_COUNT_MAX) return; if(!battleFighterIsAlive(&BATTLE.fighters[targetIndex])) return; - battleQueueAction(fighter->id, BATTLE_ACTION_ATTACK, targetIndex); + battleQueueMove(fighter->id, BATTLE_MOVE_ID_ATTACK, targetIndex); BATTLE.selectionIndex++; battleAdvanceSelection(); } @@ -220,9 +220,11 @@ void battleMoveTeamFirst(const battlefighterteam_t team) { } battlefighter_t *battleAIChooseTarget(const battlefighter_t *fighter) { - const battlefighterteam_t enemyTeam = + const battlefighterteam_t enemyTeam = ( fighter->team == BATTLE_FIGHTER_TEAM_ALLY ? - BATTLE_FIGHTER_TEAM_ENEMY : BATTLE_FIGHTER_TEAM_ALLY; + BATTLE_FIGHTER_TEAM_ENEMY : + BATTLE_FIGHTER_TEAM_ALLY + ); battlefighter_t *weakest = NULL; for(uint8_t i = 0; i < BATTLE_FIGHTER_COUNT_MAX; i++) { @@ -238,9 +240,7 @@ battlefighter_t *battleAIChooseTarget(const battlefighter_t *fighter) { } void battleSetState(const battlestate_t next) { - const battlestate_t previous = BATTLE.state; BATTLE.state = next; - if(BATTLE.onStateChanged != NULL) BATTLE.onStateChanged(previous, next); } void battleSetResult(const battleresult_t result) { @@ -254,7 +254,7 @@ bool_t battleFighterNeedsDecision( ) { return battleFighterIsAlive(&BATTLE.fighters[fighterIndex]) && BATTLE.fighters[fighterIndex].controller == controller - && BATTLE.actions[fighterIndex].type == BATTLE_ACTION_NONE; + && BATTLE.moves[fighterIndex].move == BATTLE_MOVE_ID_NULL; } void battleAdvanceSelection(void) { @@ -272,10 +272,10 @@ void battleAdvanceSelection(void) { } void battleUpdatePreRound(void) { - // No need to clear BATTLE.actions here: every living fighter's action is - // unconditionally reset to BATTLE_ACTION_NONE as it's processed in + // No need to clear BATTLE.moves here: every living fighter's move is + // unconditionally reset to BATTLE_MOVE_ID_NULL as it's processed in // battleUpdateMovesExecuting, and round 1 starts pre-zeroed by - // battleInit(). Clearing it here would also wipe out any action a + // battleInit(). Clearing it here would also wipe out any move a // cutscene force-queued while parked at BATTLE_STATE_PRE_ROUND. battleBuildExecutionOrder(BATTLE.round == 1); @@ -295,7 +295,7 @@ void battleUpdateAiSelection(void) { battleAIChooseTarget(&BATTLE.fighters[fighterIndex]); if(target == NULL) continue; - battleQueueAction(fighterIndex, BATTLE_ACTION_ATTACK, target->id); + battleQueueMove(fighterIndex, BATTLE_MOVE_ID_ATTACK, target->id); } BATTLE.executionIndex = 0; @@ -312,12 +312,12 @@ void battleUpdateMovesExecuting(void) { battlefighter_t *fighter = &BATTLE.fighters[fighterIndex]; if(!battleFighterIsAlive(fighter)) return; - battleaction_t *action = &BATTLE.actions[fighterIndex]; - if(action->type == BATTLE_ACTION_ATTACK) { + battlemoveaction_t *action = &BATTLE.moves[fighterIndex]; + if(action->move == BATTLE_MOVE_ID_ATTACK) { battlefighter_t *target = &BATTLE.fighters[action->targetIndex]; if(battleFighterIsAlive(target)) battleResolveAttack(fighter, target); } - action->type = BATTLE_ACTION_NONE; + action->move = BATTLE_MOVE_ID_NULL; battleCheckResult(); } diff --git a/src/dusk/rpg/battle/battle.h b/src/dusk/rpg/battle/battle.h index 6bdfee25..306bef7e 100644 --- a/src/dusk/rpg/battle/battle.h +++ b/src/dusk/rpg/battle/battle.h @@ -6,8 +6,8 @@ */ #pragma once -#include "battlefighter.h" -#include "battleaction.h" +#include "rpg/battle/fighter/battlefighter.h" +#include "rpg/battle/move/battlemove.h" #define BATTLE_FIGHTER_COUNT_MAX 8 @@ -43,15 +43,10 @@ typedef enum { BATTLE_STATE_COUNT } battlestate_t; -typedef void (*battlestatechangedcallback_t)( - const battlestate_t previous, - const battlestate_t next -); - typedef struct { battlestate_t state; battlefighter_t fighters[BATTLE_FIGHTER_COUNT_MAX]; - battleaction_t actions[BATTLE_FIGHTER_COUNT_MAX]; + battlemoveaction_t moves[BATTLE_FIGHTER_COUNT_MAX]; battleencountertype_t encounterType; bool_t fleeAvailable; @@ -66,9 +61,6 @@ typedef struct { // Cursor into executionOrder used by BATTLE_STATE_PLAYER_SELECTION to find // the next player-controlled fighter that still needs a decision. uint8_t selectionIndex; - - battlestatechangedcallback_t onStateChanged; - battleactiondecidedcallback_t onActionDecided; } battle_t; extern battle_t BATTLE; @@ -163,25 +155,24 @@ void battleResolveAttack( battleresult_t battleCheckResult(void); /** - * Queues an action for a fighter to perform once BATTLE_STATE_MOVES_EXECUTING - * reaches them this round, overwriting any action already queued for that - * fighter. Fires BATTLE.onActionDecided. + * Queues a move for a fighter to perform once BATTLE_STATE_MOVES_EXECUTING + * reaches them this round, overwriting any move already queued for that + * fighter. * * @param fighterIndex Index into BATTLE.fighters of the deciding fighter. - * @param type The type of action to perform. - * @param targetIndex Index into BATTLE.fighters of the target, meaningful - * for BATTLE_ACTION_ATTACK. + * @param move The move to perform. + * @param targetIndex Index into BATTLE.fighters of the target. */ -void battleQueueAction( +void battleQueueMove( const uint8_t fighterIndex, - const battleactiontype_t type, + const battlemoveid_t move, const uint8_t targetIndex ); /** * Submits the currently-selecting fighter's attack against a target, if the * battle is in BATTLE_STATE_PLAYER_SELECTION and awaiting a decision. - * Queues the action and advances the selection cursor. + * Queues the move and advances the selection cursor. * * @param targetIndex Index into BATTLE.fighters of the target. */ @@ -230,8 +221,7 @@ void battleMoveTeamFirst(const battlefighterteam_t team); battlefighter_t *battleAIChooseTarget(const battlefighter_t *fighter); /** - * Sets BATTLE.state and fires BATTLE.onStateChanged with the previous and - * new state. + * Sets BATTLE.state. * * @param next The state to transition to. */ @@ -247,13 +237,13 @@ void battleSetResult(const battleresult_t result); /** * Checks whether a fighter is a live, undecided candidate for the given * controller -- i.e. whether PLAYER_SELECTION or AI_SELECTION should still - * be deciding an action for it this round. + * be deciding a move for it this round. * * @param fighterIndex Index into BATTLE.fighters to check. * @param controller The controller PLAYER_SELECTION/AI_SELECTION is * currently deciding for. * @return True if the fighter is alive, matches controller, and has no - * action queued yet. + * move queued yet. */ bool_t battleFighterNeedsDecision( const uint8_t fighterIndex, @@ -280,7 +270,7 @@ void battleUpdatePreRound(void); void battleUpdateAiSelection(void); /** - * Handles BATTLE_STATE_MOVES_EXECUTING: resolves one queued action from + * Handles BATTLE_STATE_MOVES_EXECUTING: resolves one queued move from * BATTLE.executionOrder per call, or transitions to BATTLE_STATE_POST_ROUND * once every fighter this round has been processed. */ diff --git a/src/dusk/rpg/battle/battleaction.h b/src/dusk/rpg/battle/battleaction.h deleted file mode 100644 index c786bcfd..00000000 --- a/src/dusk/rpg/battle/battleaction.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" -#include "battlefighter.h" - -typedef enum { - BATTLE_ACTION_NONE, // No action decided yet for this fighter this round. - BATTLE_ACTION_ATTACK, - - BATTLE_ACTION_COUNT -} battleactiontype_t; - -typedef struct { - battleactiontype_t type; - uint8_t targetIndex; // Meaningful for BATTLE_ACTION_ATTACK. -} battleaction_t; - -typedef void (*battleactiondecidedcallback_t)( - const battlefighter_t *fighter, - const battleaction_t *action -); diff --git a/src/dusk/rpg/battle/fighter/CMakeLists.txt b/src/dusk/rpg/battle/fighter/CMakeLists.txt new file mode 100644 index 00000000..bc721bd2 --- /dev/null +++ b/src/dusk/rpg/battle/fighter/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + battlefighter.c +) diff --git a/src/dusk/rpg/battle/battlefighter.c b/src/dusk/rpg/battle/fighter/battlefighter.c similarity index 100% rename from src/dusk/rpg/battle/battlefighter.c rename to src/dusk/rpg/battle/fighter/battlefighter.c diff --git a/src/dusk/rpg/battle/battlefighter.h b/src/dusk/rpg/battle/fighter/battlefighter.h similarity index 59% rename from src/dusk/rpg/battle/battlefighter.h rename to src/dusk/rpg/battle/fighter/battlefighter.h index 1b3674b1..fb24db73 100644 --- a/src/dusk/rpg/battle/battlefighter.h +++ b/src/dusk/rpg/battle/fighter/battlefighter.h @@ -7,46 +7,15 @@ #pragma once #include "error/error.h" -#include "battleability.h" +#include "rpg/battle/move/battlemove.h" +#include "rpg/battle/fighter/battlefighterstatus.h" +#include "rpg/battle/fighter/battlefighterteam.h" +#include "rpg/battle/fighter/battlefightercontroller.h" +#include "rpg/battle/fighter/battlefighterstats.h" #define BATTLE_FIGHTER_ABILITY_COUNT_MAX 8 -// An empty status means the slot in BATTLE.fighters is unused. -typedef enum { - BATTLE_FIGHTER_STATUS_NULL, - - BATTLE_FIGHTER_STATUS_NORMAL, - BATTLE_FIGHTER_STATUS_DEAD, - - BATTLE_FIGHTER_STATUS_COUNT -} battlefighterstatus_t; - -typedef enum { - BATTLE_FIGHTER_TEAM_ALLY, - BATTLE_FIGHTER_TEAM_ENEMY, - - BATTLE_FIGHTER_TEAM_COUNT -} battlefighterteam_t; - -typedef enum { - BATTLE_FIGHTER_CONTROLLER_PLAYER, - BATTLE_FIGHTER_CONTROLLER_AI, - - BATTLE_FIGHTER_CONTROLLER_COUNT -} battlefightercontroller_t; - -// Base combat stats, kept separate from the resource pools (health/mp) on -// battlefighter_t so that equipment/buffs can later modify them without -// touching current health/mp state. -typedef struct { - uint16_t attack; - uint16_t defense; - uint16_t magic; - uint16_t speed; - uint16_t luck; -} battlefighterstats_t; - -typedef struct { +typedef struct battlefighter_s { uint8_t id; battlefighterstatus_t status; battlefighterteam_t team; @@ -58,7 +27,7 @@ typedef struct { uint16_t mpMax; battlefighterstats_t stats; - battleability_t abilities[BATTLE_FIGHTER_ABILITY_COUNT_MAX]; + battlemoveid_t abilities[BATTLE_FIGHTER_ABILITY_COUNT_MAX]; } battlefighter_t; /** diff --git a/src/dusk/rpg/battle/fighter/battlefightercontroller.h b/src/dusk/rpg/battle/fighter/battlefightercontroller.h new file mode 100644 index 00000000..5e0389d0 --- /dev/null +++ b/src/dusk/rpg/battle/fighter/battlefightercontroller.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" + +typedef enum { + BATTLE_FIGHTER_CONTROLLER_NONE, + + BATTLE_FIGHTER_CONTROLLER_PLAYER, + BATTLE_FIGHTER_CONTROLLER_AI, + + BATTLE_FIGHTER_CONTROLLER_COUNT +} battlefightercontroller_t; diff --git a/src/dusk/rpg/battle/fighter/battlefighterstats.h b/src/dusk/rpg/battle/fighter/battlefighterstats.h new file mode 100644 index 00000000..1d9820e1 --- /dev/null +++ b/src/dusk/rpg/battle/fighter/battlefighterstats.h @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef struct { + uint16_t attack; + uint16_t defense; + uint16_t magic; + uint16_t speed; + uint16_t luck; +} battlefighterstats_t; diff --git a/src/dusk/rpg/battle/fighter/battlefighterstatus.h b/src/dusk/rpg/battle/fighter/battlefighterstatus.h new file mode 100644 index 00000000..c24e496d --- /dev/null +++ b/src/dusk/rpg/battle/fighter/battlefighterstatus.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef enum { + BATTLE_FIGHTER_STATUS_NULL, + + BATTLE_FIGHTER_STATUS_NORMAL, + BATTLE_FIGHTER_STATUS_DEAD, + + BATTLE_FIGHTER_STATUS_COUNT +} battlefighterstatus_t; diff --git a/src/dusk/rpg/battle/battleability.h b/src/dusk/rpg/battle/fighter/battlefighterteam.h similarity index 57% rename from src/dusk/rpg/battle/battleability.h rename to src/dusk/rpg/battle/fighter/battlefighterteam.h index 6b2297e4..85abda07 100644 --- a/src/dusk/rpg/battle/battleability.h +++ b/src/dusk/rpg/battle/fighter/battlefighterteam.h @@ -6,12 +6,11 @@ */ #pragma once -#include "error/error.h" +#include "dusk.h" typedef enum { - BATTLE_ABILITY_NULL, + BATTLE_FIGHTER_TEAM_ALLY, + BATTLE_FIGHTER_TEAM_ENEMY, - BATTLE_ABILITY_FIRE, - - BATTLE_ABILITY_COUNT -} battleability_t; + BATTLE_FIGHTER_TEAM_COUNT +} battlefighterteam_t; diff --git a/src/dusk/rpg/battle/move/CMakeLists.txt b/src/dusk/rpg/battle/move/CMakeLists.txt new file mode 100644 index 00000000..cdbe40d2 --- /dev/null +++ b/src/dusk/rpg/battle/move/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + battlemove.c +) diff --git a/src/dusk/rpg/battle/move/battlemove.c b/src/dusk/rpg/battle/move/battlemove.c new file mode 100644 index 00000000..1c33cb96 --- /dev/null +++ b/src/dusk/rpg/battle/move/battlemove.c @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "battlemove.h" +#include "asset/asset.h" +#include "asset/loader/assetloader.h" +#include "assert/assert.h" +#include "util/memory.h" +#include "util/string.h" + +battleability_t BATTLE_MOVES[BATTLE_MOVE_COUNT_MAX]; +uint8_t BATTLE_MOVE_COUNT; + +errorret_t battleMoveInit(void) { + memoryZero(BATTLE_MOVES, sizeof(BATTLE_MOVES)); + BATTLE_MOVE_COUNT = BATTLE_MOVE_ID_NULL; + + // Get moves JSON. + assetentry_t *entry = assetLock( + "battle/moves.jsonc", ASSET_LOADER_TYPE_JSON, NULL + ); + errorret_t ret = assetRequireLoaded(entry); + if(errorIsNotOk(ret)) { + assetUnlockEntry(entry); + errorChain(ret); + } + + yyjson_val *root = yyjson_doc_get_root(entry->data.json); + yyjson_val *moves = yyjson_obj_get(root, "moves"); + yyjson_obj_iter iter = yyjson_obj_iter_with(moves); + yyjson_val *key; + while((key = yyjson_obj_iter_next(&iter))) { + assertTrue( + BATTLE_MOVE_COUNT < BATTLE_MOVE_COUNT_MAX, + "Too many battle moves defined." + ); + + yyjson_val *val = yyjson_obj_iter_get_val(key); + battlemoveid_t id = BATTLE_MOVE_COUNT++; + battleability_t *move = &BATTLE_MOVES[id]; + + stringCopy(move->name, yyjson_get_str(key), BATTLE_MOVE_NAME_MAX); + + battletarget_t target = 0; + if(yyjson_get_bool(yyjson_obj_get(val, "enemies"))) { + target |= BATTLE_TARGET_ENEMIES; + } + if(yyjson_get_bool(yyjson_obj_get(val, "allies"))) { + target |= BATTLE_TARGET_ALLIES; + } + if(yyjson_get_bool(yyjson_obj_get(val, "multiple"))) { + target |= BATTLE_TARGET_MULTIPLE; + } + if(yyjson_get_bool(yyjson_obj_get(val, "self"))) { + target |= BATTLE_TARGET_SELF; + } + move->target = target; + + const char_t *defaultTarget = yyjson_get_str( + yyjson_obj_get(val, "default") + ); + if(defaultTarget != NULL && stringEquals(defaultTarget, "enemy")) { + move->targetDefault = BATTLE_TARGET_ENEMIES; + } else if(defaultTarget != NULL && stringEquals(defaultTarget, "ally")) { + move->targetDefault = BATTLE_TARGET_ALLIES; + } else if(defaultTarget != NULL && stringEquals(defaultTarget, "self")) { + move->targetDefault = BATTLE_TARGET_SELF; + } else { + move->targetDefault = 0; + } + } + + assetUnlockEntry(entry); + errorOk(); +} diff --git a/src/dusk/rpg/battle/move/battlemove.h b/src/dusk/rpg/battle/move/battlemove.h new file mode 100644 index 00000000..46b23a5d --- /dev/null +++ b/src/dusk/rpg/battle/move/battlemove.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "asset/asset.h" +#include "battletarget.h" + +#define BATTLE_MOVE_NAME_MAX 16 +#define BATTLE_MOVE_COUNT_MAX 250 + +typedef uint8_t battlemoveid_t; +#define BATTLE_MOVE_ID_NULL ((battlemoveid_t)0) + +extern uint8_t BATTLE_MOVE_COUNT; + +typedef struct { + char_t name[BATTLE_MOVE_NAME_MAX]; + battletarget_t target; + battletarget_t targetDefault; +} battleability_t; + +typedef struct { + battlemoveid_t move; + uint8_t targetIndex; +} battlemoveaction_t; + +extern battleability_t BATTLE_MOVES[BATTLE_MOVE_COUNT_MAX]; + +/** + * Initializes the battle move subsystem, loading assets/battle/moves.jsonc. + * + * @return Any error that occurs. + */ +errorret_t battleMoveInit(void); diff --git a/src/dusk/rpg/battle/move/battletarget.h b/src/dusk/rpg/battle/move/battletarget.h new file mode 100644 index 00000000..73abfb38 --- /dev/null +++ b/src/dusk/rpg/battle/move/battletarget.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef uint8_t battletarget_t; + +#define BATTLE_TARGET_ENEMIES (1 << 0) +#define BATTLE_TARGET_ALLIES (1 << 1) +#define BATTLE_TARGET_MULTIPLE (1 << 2) +#define BATTLE_TARGET_SELF (1 << 3) \ No newline at end of file diff --git a/src/dusk/rpg/battle/party.h b/src/dusk/rpg/battle/party.h index 3e8f4e8f..9e83485e 100644 --- a/src/dusk/rpg/battle/party.h +++ b/src/dusk/rpg/battle/party.h @@ -6,7 +6,7 @@ */ #pragma once -#include "battlefighter.h" +#include "rpg/battle/fighter/battlefighter.h" #define PARTY_MEMBER_COUNT_MAX 4 #define PARTY_ACTIVE_SIZE_MAX 3 @@ -14,12 +14,8 @@ typedef struct { battlefighter_t members[PARTY_MEMBER_COUNT_MAX]; - - // Maps an active battle slot to the roster member filling it, or - // PARTY_ORDER_EMPTY if the slot is unfilled. Only the first - // PARTY_ACTIVE_SIZE_MAX of the PARTY_MEMBER_COUNT_MAX roster members - // can be in the active lineup at once. uint8_t order[PARTY_ACTIVE_SIZE_MAX]; + // TODO: Whether party members are "front" or "back". } party_t; /** diff --git a/src/dusk/rpg/cutscene/item/battle/CMakeLists.txt b/src/dusk/rpg/cutscene/item/battle/CMakeLists.txt index 60a5f108..715334ca 100644 --- a/src/dusk/rpg/cutscene/item/battle/CMakeLists.txt +++ b/src/dusk/rpg/cutscene/item/battle/CMakeLists.txt @@ -7,6 +7,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC cutscenestartbattle.c cutscenebattlewaitstate.c - cutscenebattleforceaction.c + cutscenebattleforcemove.c cutsceneregularbattle.c ) diff --git a/src/dusk/rpg/cutscene/item/battle/cutscenebattleforceaction.c b/src/dusk/rpg/cutscene/item/battle/cutscenebattleforcemove.c similarity index 55% rename from src/dusk/rpg/cutscene/item/battle/cutscenebattleforceaction.c rename to src/dusk/rpg/cutscene/item/battle/cutscenebattleforcemove.c index 34a497fe..dca48c23 100644 --- a/src/dusk/rpg/cutscene/item/battle/cutscenebattleforceaction.c +++ b/src/dusk/rpg/cutscene/item/battle/cutscenebattleforcemove.c @@ -7,30 +7,33 @@ #include "rpg/cutscene/item/cutsceneitem.h" -void cutsceneBattleForceActionStart( +// TEMPORARY: hardcoded pending a real moves.jsonc-backed registry lookup. +#define BATTLE_MOVE_ID_ATTACK ((battlemoveid_t)1) + +void cutsceneBattleForceMoveStart( const cutsceneitem_t *item, cutsceneitemdata_t *data ) { - battleQueueAction( - item->battleForceAction.fighterIndex, BATTLE_ACTION_ATTACK, - item->battleForceAction.targetIndex + battleQueueMove( + item->battleForceMove.fighterIndex, BATTLE_MOVE_ID_ATTACK, + item->battleForceMove.targetIndex ); } -bool_t cutsceneBattleForceActionUpdate( +bool_t cutsceneBattleForceMoveUpdate( const cutsceneitem_t *item, cutsceneitemdata_t *data ) { return true; } -errorret_t cutsceneBattleForceActionLoad( +errorret_t cutsceneBattleForceMoveLoad( yyjson_val *itemObj, cutsceneitem_t *item ) { - item->battleForceAction.fighterIndex = + item->battleForceMove.fighterIndex = (uint8_t)yyjson_get_int(yyjson_obj_get(itemObj, "fighterIndex")); - item->battleForceAction.targetIndex = + item->battleForceMove.targetIndex = (uint8_t)yyjson_get_int(yyjson_obj_get(itemObj, "targetIndex")); errorOk(); diff --git a/src/dusk/rpg/cutscene/item/battle/cutscenebattleforceaction.h b/src/dusk/rpg/cutscene/item/battle/cutscenebattleforcemove.h similarity index 72% rename from src/dusk/rpg/cutscene/item/battle/cutscenebattleforceaction.h rename to src/dusk/rpg/cutscene/item/battle/cutscenebattleforcemove.h index 4acb73dc..1889b126 100644 --- a/src/dusk/rpg/cutscene/item/battle/cutscenebattleforceaction.h +++ b/src/dusk/rpg/cutscene/item/battle/cutscenebattleforcemove.h @@ -12,41 +12,41 @@ typedef struct { uint8_t fighterIndex; uint8_t targetIndex; -} cutscenebattleforceaction_t; +} cutscenebattleforcemove_t; /** - * Starts a battle force-action step: immediately queues an attack for the + * Starts a battle force-move step: immediately queues an attack for the * given fighter against the given target, bypassing normal player/AI * selection for that fighter this round. * * @param item The cutscene item. * @param data Runtime data storage. */ -void cutsceneBattleForceActionStart( +void cutsceneBattleForceMoveStart( const cutsceneitem_t *item, cutsceneitemdata_t *data ); /** - * Updates a battle force-action step (always completes immediately). + * Updates a battle force-move step (always completes immediately). * * @param item The cutscene item. * @param data Runtime data storage. * @returns true always. */ -bool_t cutsceneBattleForceActionUpdate( +bool_t cutsceneBattleForceMoveUpdate( const cutsceneitem_t *item, cutsceneitemdata_t *data ); /** - * Parses a BATTLE_FORCE_ACTION cutscene item's JSON fields into item. + * Parses a BATTLE_FORCE_MOVE cutscene item's JSON fields into item. * * @param itemObj The item's JSON object. * @param item Destination item, already zeroed by the caller. * @return Error code indicating success or failure of the parse. */ -errorret_t cutsceneBattleForceActionLoad( +errorret_t cutsceneBattleForceMoveLoad( yyjson_val *itemObj, cutsceneitem_t *item ); diff --git a/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.c b/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.c index 3cd12e7b..38abb5cb 100644 --- a/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.c +++ b/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.c @@ -183,7 +183,7 @@ errorret_t cutsceneStartBattleLoad( yyjson_val *abilityVal; size_t a = 0; while((abilityVal = yyjson_arr_iter_next(&abilityIter)) != NULL) { - battleability_t ability; + battlemoveid_t ability; if(!cutsceneJsonLookupBattleAbility( yyjson_get_str(abilityVal), &ability )) { diff --git a/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.h b/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.h index 405cbfbd..dab96d8b 100644 --- a/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.h +++ b/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.h @@ -16,7 +16,7 @@ typedef struct { battlefighterstats_t stats; uint16_t healthMax; uint16_t mpMax; - battleability_t abilities[BATTLE_FIGHTER_ABILITY_COUNT_MAX]; + battlemoveid_t abilities[BATTLE_FIGHTER_ABILITY_COUNT_MAX]; } cutscenestartbattleenemy_t; typedef struct { diff --git a/src/dusk/rpg/cutscene/item/cutsceneitem.c b/src/dusk/rpg/cutscene/item/cutsceneitem.c index 149b9c04..98fcad25 100644 --- a/src/dusk/rpg/cutscene/item/cutsceneitem.c +++ b/src/dusk/rpg/cutscene/item/cutsceneitem.c @@ -17,27 +17,7 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = { [CUTSCENE_ITEM_TYPE_NULL] = { 0 }, - [CUTSCENE_ITEM_TYPE_TEXT] = { - .init = cutsceneTextStart, - .update = cutsceneTextUpdate, - .jsonName = "TEXT", - .load = cutsceneTextLoad - }, - - [CUTSCENE_ITEM_TYPE_TEXT_MINI] = { - .init = cutsceneTextMiniStart, - .update = cutsceneTextMiniUpdate, - .jsonName = "TEXT_MINI", - .load = cutsceneTextMiniLoad - }, - - [CUTSCENE_ITEM_TYPE_TEXT_MINI_HIDE] = { - .init = cutsceneTextMiniHideStart, - .update = cutsceneTextMiniHideUpdate, - .jsonName = "TEXT_MINI_HIDE", - .load = cutsceneTextMiniHideLoad - }, - + // Control [CUTSCENE_ITEM_TYPE_CALLBACK] = { .init = cutsceneCallbackStart, .update = cutsceneCallbackUpdate @@ -57,25 +37,11 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = { .load = cutsceneCutsceneLoad }, - [CUTSCENE_ITEM_TYPE_ENTITY_TELEPORT] = { - .init = cutsceneEntityTeleportStart, - .update = cutsceneEntityTeleportUpdate, - .jsonName = "ENTITY_TELEPORT", - .load = cutsceneEntityTeleportLoad - }, - - [CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO] = { - .init = cutsceneEntityWalkToStart, - .update = cutsceneEntityWalkToUpdate, - .jsonName = "ENTITY_WALK_TO", - .load = cutsceneEntityWalkToLoad - }, - - [CUTSCENE_ITEM_TYPE_FADE] = { - .init = cutsceneFadeStart, - .update = cutsceneFadeUpdate, - .jsonName = "FADE", - .load = cutsceneFadeLoad + [CUTSCENE_ITEM_TYPE_INSERT] = { + .init = cutsceneInsertStart, + .update = cutsceneInsertUpdate, + .jsonName = "INSERT", + .load = cutsceneInsertLoad }, [CUTSCENE_ITEM_TYPE_SET_PAUSE] = { @@ -90,122 +56,6 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = { .update = cutsceneConcurrentUpdate }, - [CUTSCENE_ITEM_TYPE_ITEM_GIVE] = { - .init = cutsceneItemGiveStart, - .update = cutsceneItemGiveUpdate, - .jsonName = "ITEM_GIVE", - .load = cutsceneItemGiveLoad - }, - - [CUTSCENE_ITEM_TYPE_ENTITY_REMOVE] = { - .init = cutsceneEntityRemoveStart, - .update = cutsceneEntityRemoveUpdate, - .jsonName = "ENTITY_REMOVE", - .load = cutsceneEntityRemoveLoad - }, - - [CUTSCENE_ITEM_TYPE_ENTITY_ADD] = { - .init = cutsceneEntityAddStart, - .update = cutsceneEntityAddUpdate, - .jsonName = "ENTITY_ADD", - .load = cutsceneEntityAddLoad - }, - - [CUTSCENE_ITEM_TYPE_ENTITY_TURN] = { - .init = cutsceneEntityTurnStart, - .update = cutsceneEntityTurnUpdate, - .jsonName = "ENTITY_TURN", - .load = cutsceneEntityTurnLoad - }, - - [CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO_ENTITY] = { - .init = cutsceneEntityWalkToEntityStart, - .update = cutsceneEntityWalkToEntityUpdate, - .jsonName = "ENTITY_WALK_TO_ENTITY", - .load = cutsceneEntityWalkToEntityLoad - }, - - [CUTSCENE_ITEM_TYPE_MAP_AREA_ADD] = { - .init = cutsceneMapAreaAddStart, - .update = cutsceneMapAreaAddUpdate - }, - - [CUTSCENE_ITEM_TYPE_MAP_AREA_REMOVE] = { - .init = cutsceneMapAreaRemoveStart, - .update = cutsceneMapAreaRemoveUpdate, - .jsonName = "MAP_AREA_REMOVE", - .load = cutsceneMapAreaRemoveLoad - }, - - [CUTSCENE_ITEM_TYPE_MAP_AREA_WAIT] = { - .init = cutsceneMapAreaWaitStart, - .update = cutsceneMapAreaWaitUpdate, - .jsonName = "MAP_AREA_WAIT", - .load = cutsceneMapAreaWaitLoad - }, - - [CUTSCENE_ITEM_TYPE_START_BATTLE] = { - .init = cutsceneStartBattleStart, - .update = cutsceneStartBattleUpdate, - .jsonName = "START_BATTLE", - .load = cutsceneStartBattleLoad - }, - - [CUTSCENE_ITEM_TYPE_EMOJI] = { - .init = cutsceneEmojiStart, - .update = cutsceneEmojiUpdate, - .jsonName = "EMOJI", - .load = cutsceneEmojiLoad - }, - - [CUTSCENE_ITEM_TYPE_SHAKE] = { - .init = cutsceneShakeStart, - .update = cutsceneShakeUpdate, - .jsonName = "SHAKE", - .load = cutsceneShakeLoad - }, - - [CUTSCENE_ITEM_TYPE_BATTLE_WAIT_STATE] = { - .update = cutsceneBattleWaitStateUpdate, - .jsonName = "BATTLE_WAIT_STATE", - .load = cutsceneBattleWaitStateLoad - }, - - [CUTSCENE_ITEM_TYPE_BATTLE_FORCE_ACTION] = { - .init = cutsceneBattleForceActionStart, - .update = cutsceneBattleForceActionUpdate, - .jsonName = "BATTLE_FORCE_ACTION", - .load = cutsceneBattleForceActionLoad - }, - - [CUTSCENE_ITEM_TYPE_MODAL] = { - .init = cutsceneModalStart, - .update = cutsceneModalUpdate, - .jsonName = "MODAL", - .load = cutsceneModalLoad - }, - - [CUTSCENE_ITEM_TYPE_MODAL_OPTIONS_MARKERS] = { - .init = cutsceneModalOptionsMarkersStart, - .update = cutsceneModalOptionsMarkersUpdate, - .jsonName = "MODAL_OPTIONS_MARKERS", - .load = cutsceneModalOptionsMarkersLoad - }, - - [CUTSCENE_ITEM_TYPE_MODAL_CLOSE] = { - .init = cutsceneModalCloseStart, - .update = cutsceneModalCloseUpdate, - .jsonName = "MODAL_CLOSE", - .load = cutsceneItemLoadNoop - }, - - [CUTSCENE_ITEM_TYPE_PRINT] = { - .init = cutscenePrintStart, - .update = cutscenePrintUpdate, - .jsonName = "PRINT", - .load = cutscenePrintLoad - }, - [CUTSCENE_ITEM_TYPE_MARKER] = { .init = cutsceneMarkerStart, .update = cutsceneMarkerUpdate, @@ -234,6 +84,194 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = { .load = cutsceneSceneLoad }, + [CUTSCENE_ITEM_TYPE_IDLE] = { + .update = cutsceneIdleUpdate, + .jsonName = "IDLE", + .load = cutsceneItemLoadNoop + }, + + // Entity + [CUTSCENE_ITEM_TYPE_ENTITY_TELEPORT] = { + .init = cutsceneEntityTeleportStart, + .update = cutsceneEntityTeleportUpdate, + .jsonName = "ENTITY_TELEPORT", + .load = cutsceneEntityTeleportLoad + }, + + [CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO] = { + .init = cutsceneEntityWalkToStart, + .update = cutsceneEntityWalkToUpdate, + .jsonName = "ENTITY_WALK_TO", + .load = cutsceneEntityWalkToLoad + }, + + [CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO_ENTITY] = { + .init = cutsceneEntityWalkToEntityStart, + .update = cutsceneEntityWalkToEntityUpdate, + .jsonName = "ENTITY_WALK_TO_ENTITY", + .load = cutsceneEntityWalkToEntityLoad + }, + + [CUTSCENE_ITEM_TYPE_ENTITY_REMOVE] = { + .init = cutsceneEntityRemoveStart, + .update = cutsceneEntityRemoveUpdate, + .jsonName = "ENTITY_REMOVE", + .load = cutsceneEntityRemoveLoad + }, + + [CUTSCENE_ITEM_TYPE_ENTITY_ADD] = { + .init = cutsceneEntityAddStart, + .update = cutsceneEntityAddUpdate, + .jsonName = "ENTITY_ADD", + .load = cutsceneEntityAddLoad + }, + + [CUTSCENE_ITEM_TYPE_ENTITY_TURN] = { + .init = cutsceneEntityTurnStart, + .update = cutsceneEntityTurnUpdate, + .jsonName = "ENTITY_TURN", + .load = cutsceneEntityTurnLoad + }, + + // UI + [CUTSCENE_ITEM_TYPE_TEXT] = { + .init = cutsceneTextStart, + .update = cutsceneTextUpdate, + .jsonName = "TEXT", + .load = cutsceneTextLoad + }, + + [CUTSCENE_ITEM_TYPE_TEXT_MINI] = { + .init = cutsceneTextMiniStart, + .update = cutsceneTextMiniUpdate, + .jsonName = "TEXT_MINI", + .load = cutsceneTextMiniLoad + }, + + [CUTSCENE_ITEM_TYPE_TEXT_MINI_HIDE] = { + .init = cutsceneTextMiniHideStart, + .update = cutsceneTextMiniHideUpdate, + .jsonName = "TEXT_MINI_HIDE", + .load = cutsceneTextMiniHideLoad + }, + + [CUTSCENE_ITEM_TYPE_PRINT] = { + .init = cutscenePrintStart, + .update = cutscenePrintUpdate, + .jsonName = "PRINT", + .load = cutscenePrintLoad + }, + + [CUTSCENE_ITEM_TYPE_FADE] = { + .init = cutsceneFadeStart, + .update = cutsceneFadeUpdate, + .jsonName = "FADE", + .load = cutsceneFadeLoad + }, + + [CUTSCENE_ITEM_TYPE_EMOJI] = { + .init = cutsceneEmojiStart, + .update = cutsceneEmojiUpdate, + .jsonName = "EMOJI", + .load = cutsceneEmojiLoad + }, + + [CUTSCENE_ITEM_TYPE_SHAKE] = { + .init = cutsceneShakeStart, + .update = cutsceneShakeUpdate, + .jsonName = "SHAKE", + .load = cutsceneShakeLoad + }, + + [CUTSCENE_ITEM_TYPE_MODAL] = { + .init = cutsceneModalStart, + .update = cutsceneModalUpdate, + .jsonName = "MODAL", + .load = cutsceneModalLoad + }, + + [CUTSCENE_ITEM_TYPE_MODAL_OPTIONS_MARKERS] = { + .init = cutsceneModalOptionsMarkersStart, + .update = cutsceneModalOptionsMarkersUpdate, + .jsonName = "MODAL_OPTIONS_MARKERS", + .load = cutsceneModalOptionsMarkersLoad + }, + + [CUTSCENE_ITEM_TYPE_MODAL_CLOSE] = { + .init = cutsceneModalCloseStart, + .update = cutsceneModalCloseUpdate, + .jsonName = "MODAL_CLOSE", + .load = cutsceneItemLoadNoop + }, + + [CUTSCENE_ITEM_TYPE_KEYBOARD] = { + .init = cutsceneKeyboardStart, + .update = cutsceneKeyboardUpdate + }, + + [CUTSCENE_ITEM_TYPE_UI_SHOW] = { + .init = cutsceneUIShowStart, + .update = cutsceneUIShowUpdate, + .jsonName = "UI_SHOW", + .load = cutsceneUIShowLoad + }, + + // Item + [CUTSCENE_ITEM_TYPE_ITEM_GIVE] = { + .init = cutsceneItemGiveStart, + .update = cutsceneItemGiveUpdate, + .jsonName = "ITEM_GIVE", + .load = cutsceneItemGiveLoad + }, + + // Map area + [CUTSCENE_ITEM_TYPE_MAP_AREA_ADD] = { + .init = cutsceneMapAreaAddStart, + .update = cutsceneMapAreaAddUpdate + }, + + [CUTSCENE_ITEM_TYPE_MAP_AREA_REMOVE] = { + .init = cutsceneMapAreaRemoveStart, + .update = cutsceneMapAreaRemoveUpdate, + .jsonName = "MAP_AREA_REMOVE", + .load = cutsceneMapAreaRemoveLoad + }, + + [CUTSCENE_ITEM_TYPE_MAP_AREA_WAIT] = { + .init = cutsceneMapAreaWaitStart, + .update = cutsceneMapAreaWaitUpdate, + .jsonName = "MAP_AREA_WAIT", + .load = cutsceneMapAreaWaitLoad + }, + + // Battle + [CUTSCENE_ITEM_TYPE_START_BATTLE] = { + .init = cutsceneStartBattleStart, + .update = cutsceneStartBattleUpdate, + .jsonName = "START_BATTLE", + .load = cutsceneStartBattleLoad + }, + + [CUTSCENE_ITEM_TYPE_BATTLE_WAIT_STATE] = { + .update = cutsceneBattleWaitStateUpdate, + .jsonName = "BATTLE_WAIT_STATE", + .load = cutsceneBattleWaitStateLoad + }, + + [CUTSCENE_ITEM_TYPE_BATTLE_FORCE_MOVE] = { + .init = cutsceneBattleForceMoveStart, + .update = cutsceneBattleForceMoveUpdate, + .jsonName = "BATTLE_FORCE_MOVE", + .load = cutsceneBattleForceMoveLoad + }, + + [CUTSCENE_ITEM_TYPE_REGULAR_BATTLE] = { + .update = cutsceneRegularBattleUpdate, + .jsonName = "REGULAR_BATTLE", + .load = cutsceneItemLoadNoop + }, + + // Save [CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK] = { .init = cutsceneSaveDeviceCheckStart, .update = cutsceneSaveDeviceCheckUpdate, @@ -248,11 +286,7 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = { .load = cutsceneSaveLoadAllSlotsLoad }, - [CUTSCENE_ITEM_TYPE_KEYBOARD] = { - .init = cutsceneKeyboardStart, - .update = cutsceneKeyboardUpdate - }, - + // Audio [CUTSCENE_ITEM_TYPE_AUDIO_PLAY] = { .init = cutsceneAudioPlayStart, .update = cutsceneAudioPlayUpdate, @@ -315,32 +349,7 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = { .load = cutsceneAudioSetLoad }, - [CUTSCENE_ITEM_TYPE_IDLE] = { - .update = cutsceneIdleUpdate, - .jsonName = "IDLE", - .load = cutsceneItemLoadNoop - }, - - [CUTSCENE_ITEM_TYPE_UI_SHOW] = { - .init = cutsceneUIShowStart, - .update = cutsceneUIShowUpdate, - .jsonName = "UI_SHOW", - .load = cutsceneUIShowLoad - }, - - [CUTSCENE_ITEM_TYPE_REGULAR_BATTLE] = { - .update = cutsceneRegularBattleUpdate, - .jsonName = "REGULAR_BATTLE", - .load = cutsceneItemLoadNoop - }, - - [CUTSCENE_ITEM_TYPE_INSERT] = { - .init = cutsceneInsertStart, - .update = cutsceneInsertUpdate, - .jsonName = "INSERT", - .load = cutsceneInsertLoad - }, - + // Story [CUTSCENE_ITEM_TYPE_STORY_SET_FLAG] = { .init = cutsceneStorySetFlagStart, .update = cutsceneStorySetFlagUpdate, @@ -506,8 +515,9 @@ void cutsceneInsertResolveAndSplice(const char_t *name) { // nothing needs to keep the doc itself resident past the parse. cutsceneitem_t items[CUTSCENE_INSERT_ITEMS_MAX]; cutscene_t scene = { .items = items }; - errorret_t parseResult = - cutsceneParseDoc(entry->data.json, &scene, items, CUTSCENE_INSERT_ITEMS_MAX); + errorret_t parseResult = cutsceneParseDoc( + entry->data.json, &scene, items, CUTSCENE_INSERT_ITEMS_MAX + ); assetUnlockEntry(entry); if(errorIsNotOk(parseResult)) { errorCatch(errorPrint(parseResult)); diff --git a/src/dusk/rpg/cutscene/item/cutsceneitem.h b/src/dusk/rpg/cutscene/item/cutsceneitem.h index 006b79a0..872271a6 100644 --- a/src/dusk/rpg/cutscene/item/cutsceneitem.h +++ b/src/dusk/rpg/cutscene/item/cutsceneitem.h @@ -42,7 +42,7 @@ #include "maparea/cutscenemapareawait.h" #include "battle/cutscenestartbattle.h" #include "battle/cutscenebattlewaitstate.h" -#include "battle/cutscenebattleforceaction.h" +#include "battle/cutscenebattleforcemove.h" #include "battle/cutsceneregularbattle.h" #include "save/cutscenesavedevicecheck.h" #include "save/cutscenesaveloadallslots.h" @@ -120,7 +120,7 @@ struct cutsceneitem_s { cutsceneemoji_t emoji; cutsceneshake_t shake; cutscenebattlewaitstate_t battleWaitState; - cutscenebattleforceaction_t battleForceAction; + cutscenebattleforcemove_t battleForceMove; cutscenemodal_t modal; cutscenemodaloptionsmarkers_t modalOptionsMarkers; cutsceneprint_t print; diff --git a/src/dusk/rpg/cutscene/item/cutsceneitembase.h b/src/dusk/rpg/cutscene/item/cutsceneitembase.h index c19815a4..f2cd7518 100644 --- a/src/dusk/rpg/cutscene/item/cutsceneitembase.h +++ b/src/dusk/rpg/cutscene/item/cutsceneitembase.h @@ -16,41 +16,60 @@ typedef union cutsceneitemdata_u cutsceneitemdata_t; typedef enum { CUTSCENE_ITEM_TYPE_NULL, - CUTSCENE_ITEM_TYPE_TEXT, - CUTSCENE_ITEM_TYPE_TEXT_MINI, - CUTSCENE_ITEM_TYPE_TEXT_MINI_HIDE, + // Control CUTSCENE_ITEM_TYPE_CALLBACK, CUTSCENE_ITEM_TYPE_WAIT, CUTSCENE_ITEM_TYPE_CUTSCENE, - CUTSCENE_ITEM_TYPE_ENTITY_TELEPORT, - CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, - CUTSCENE_ITEM_TYPE_FADE, + CUTSCENE_ITEM_TYPE_INSERT, CUTSCENE_ITEM_TYPE_SET_PAUSE, CUTSCENE_ITEM_TYPE_CONCURRENT, - CUTSCENE_ITEM_TYPE_ITEM_GIVE, - CUTSCENE_ITEM_TYPE_ENTITY_REMOVE, - CUTSCENE_ITEM_TYPE_ENTITY_ADD, - CUTSCENE_ITEM_TYPE_ENTITY_TURN, - CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO_ENTITY, - CUTSCENE_ITEM_TYPE_MAP_AREA_ADD, - CUTSCENE_ITEM_TYPE_MAP_AREA_REMOVE, - CUTSCENE_ITEM_TYPE_MAP_AREA_WAIT, - CUTSCENE_ITEM_TYPE_START_BATTLE, - CUTSCENE_ITEM_TYPE_EMOJI, - CUTSCENE_ITEM_TYPE_SHAKE, - CUTSCENE_ITEM_TYPE_BATTLE_WAIT_STATE, - CUTSCENE_ITEM_TYPE_BATTLE_FORCE_ACTION, - CUTSCENE_ITEM_TYPE_MODAL, - CUTSCENE_ITEM_TYPE_MODAL_OPTIONS_MARKERS, - CUTSCENE_ITEM_TYPE_MODAL_CLOSE, - CUTSCENE_ITEM_TYPE_PRINT, CUTSCENE_ITEM_TYPE_MARKER, CUTSCENE_ITEM_TYPE_RESTART, CUTSCENE_ITEM_TYPE_QUIT_GAME, CUTSCENE_ITEM_TYPE_SCENE, + CUTSCENE_ITEM_TYPE_IDLE, + + // Entity + CUTSCENE_ITEM_TYPE_ENTITY_TELEPORT, + CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO, + CUTSCENE_ITEM_TYPE_ENTITY_WALK_TO_ENTITY, + CUTSCENE_ITEM_TYPE_ENTITY_REMOVE, + CUTSCENE_ITEM_TYPE_ENTITY_ADD, + CUTSCENE_ITEM_TYPE_ENTITY_TURN, + + // UI + CUTSCENE_ITEM_TYPE_TEXT, + CUTSCENE_ITEM_TYPE_TEXT_MINI, + CUTSCENE_ITEM_TYPE_TEXT_MINI_HIDE, + CUTSCENE_ITEM_TYPE_PRINT, + CUTSCENE_ITEM_TYPE_FADE, + CUTSCENE_ITEM_TYPE_EMOJI, + CUTSCENE_ITEM_TYPE_SHAKE, + CUTSCENE_ITEM_TYPE_MODAL, + CUTSCENE_ITEM_TYPE_MODAL_OPTIONS_MARKERS, + CUTSCENE_ITEM_TYPE_MODAL_CLOSE, + CUTSCENE_ITEM_TYPE_KEYBOARD, + CUTSCENE_ITEM_TYPE_UI_SHOW, + + // Item + CUTSCENE_ITEM_TYPE_ITEM_GIVE, + + // Map area + CUTSCENE_ITEM_TYPE_MAP_AREA_ADD, + CUTSCENE_ITEM_TYPE_MAP_AREA_REMOVE, + CUTSCENE_ITEM_TYPE_MAP_AREA_WAIT, + + // Battle + CUTSCENE_ITEM_TYPE_START_BATTLE, + CUTSCENE_ITEM_TYPE_BATTLE_WAIT_STATE, + CUTSCENE_ITEM_TYPE_BATTLE_FORCE_MOVE, + CUTSCENE_ITEM_TYPE_REGULAR_BATTLE, + + // Save CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK, CUTSCENE_ITEM_TYPE_SAVE_LOAD_ALL_SLOTS, - CUTSCENE_ITEM_TYPE_KEYBOARD, + + // Audio CUTSCENE_ITEM_TYPE_AUDIO_PLAY, CUTSCENE_ITEM_TYPE_AUDIO_STOP, CUTSCENE_ITEM_TYPE_AUDIO_PAUSE, @@ -60,10 +79,8 @@ typedef enum { CUTSCENE_ITEM_TYPE_AUDIO_SET_PAN, CUTSCENE_ITEM_TYPE_AUDIO_SET_LOOP, CUTSCENE_ITEM_TYPE_AUDIO_SET, - CUTSCENE_ITEM_TYPE_IDLE, - CUTSCENE_ITEM_TYPE_UI_SHOW, - CUTSCENE_ITEM_TYPE_REGULAR_BATTLE, - CUTSCENE_ITEM_TYPE_INSERT, + + // Story CUTSCENE_ITEM_TYPE_STORY_SET_FLAG, CUTSCENE_ITEM_TYPE_STORY_WAIT_FLAG, diff --git a/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.c b/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.c index adb9dc39..01c64077 100644 --- a/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.c +++ b/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.c @@ -8,8 +8,12 @@ #include "cutscenejsonbattleability.h" #include "cutscenejsonenum.h" +// TEMPORARY: hardcoded pending a real moves.jsonc-backed registry lookup +// (see BATTLE_MOVE_COUNT). +#define BATTLE_MOVE_ID_FIRE ((battlemoveid_t)2) + static const cutscenejsonenumentry_t CUTSCENE_JSON_BATTLE_ABILITY_TABLE[] = { - { "FIRE", BATTLE_ABILITY_FIRE }, + { "FIRE", BATTLE_MOVE_ID_FIRE }, }; #define CUTSCENE_JSON_BATTLE_ABILITY_TABLE_COUNT \ (sizeof(CUTSCENE_JSON_BATTLE_ABILITY_TABLE) / \ @@ -17,7 +21,7 @@ static const cutscenejsonenumentry_t CUTSCENE_JSON_BATTLE_ABILITY_TABLE[] = { bool_t cutsceneJsonLookupBattleAbility( const char_t *name, - battleability_t *out + battlemoveid_t *out ) { int32_t value; if(!cutsceneJsonLookupEnumInsensitive( @@ -26,6 +30,6 @@ bool_t cutsceneJsonLookupBattleAbility( )) { return false; } - *out = (battleability_t)value; + *out = (battlemoveid_t)value; return true; } diff --git a/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.h b/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.h index bc6abd95..ec2d92c7 100644 --- a/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.h +++ b/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.h @@ -7,16 +7,16 @@ #pragma once #include "dusk.h" -#include "rpg/battle/battleability.h" +#include "rpg/battle/move/battlemove.h" /** * Looks up name (case-insensitively) against the battle ability table. * * @param name Name to search for. - * @param out Set to the matching ability on success. + * @param out Set to the matching move id on success. * @return true if a matching entry was found, false otherwise. */ bool_t cutsceneJsonLookupBattleAbility( const char_t *name, - battleability_t *out + battlemoveid_t *out ); diff --git a/src/dusk/rpg/entity/global/entitygloballist.h b/src/dusk/rpg/entity/global/entitygloballist.h index c1437ce4..c2a83e79 100644 --- a/src/dusk/rpg/entity/global/entitygloballist.h +++ b/src/dusk/rpg/entity/global/entitygloballist.h @@ -19,7 +19,7 @@ ENTITY_GLOBAL_CALLBACK(3) { ENTITY_GLOBAL_CALLBACK(4) { create->entity->interact.type = ENTITY_INTERACT_CUTSCENE_NAME; - create->entity->interact.data.cutsceneName = "battletest"; + create->entity->interact.data.cutsceneName = "cutscenes/battletest.jsonc"; } static const entityglobaldef_t ENTITY_GLOBAL_LIST[] = { diff --git a/src/dusk/rpg/rpg.c b/src/dusk/rpg/rpg.c index d500dcda..56115801 100644 --- a/src/dusk/rpg/rpg.c +++ b/src/dusk/rpg/rpg.c @@ -13,6 +13,7 @@ #include "rpg/item/item.h" #include "rpg/item/backpack.h" #include "rpg/battle/party.h" +#include "rpg/battle/move/battlemove.h" #include "save/save.h" #include "ui/rpg/textbox/uitextboxminilist.h" #include "time/time.h" @@ -30,6 +31,7 @@ errorret_t rpgInit(void) { errorChain(storyInit()); errorChain(itemInit()); + errorChain(battleMoveInit()); backpackInit(); partyInit(); cutsceneSystemInit(); diff --git a/test/rpg/battle/test_battle.c b/test/rpg/battle/test_battle.c index abf19f10..30956300 100644 --- a/test/rpg/battle/test_battle.c +++ b/test/rpg/battle/test_battle.c @@ -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); diff --git a/tools/asset/pack/__main__.py b/tools/asset/pack/__main__.py index c549ea0e..932346c8 100644 --- a/tools/asset/pack/__main__.py +++ b/tools/asset/pack/__main__.py @@ -49,7 +49,7 @@ import zipfile import zlib import io -MAGIC = b'DSK2' +MAGIC = b'DSK' VERSION = 2 HEADER_FORMAT = '<4sIII' HEADER_SIZE = struct.calcsize(HEADER_FORMAT)