Add battle fighter damage/heal cutscene items, wire up battle results
BATTLE_FIGHTER_DAMAGE/BATTLE_FIGHTER_HEAL let a move's own cutscene apply damage/healing to "target" or "user", resolved from BATTLE_FIGHTER_POS_TARGET/USER sentinels against the current action by battleStateExecutingInit. fire/slash now call BATTLE_FIGHTER_DAMAGE on their target. BATTLE_POST_MOVE checks fighter health after each move and sets BATTLE.result (now a real field, previously commented out) to win/loss, transitioning into BATTLE_STATE_WON/LOST; fleeing sets BATTLE_RESULT_FLED. All three end states now show a textbox before moving on to BATTLE_STATE_ENDING. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -11,6 +11,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "MODAL_CLOSE"
|
"type": "MODAL_CLOSE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "BATTLE_FIGHTER_DAMAGE",
|
||||||
|
"fighter": "target",
|
||||||
|
"amount": 12
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "MODAL_CLOSE"
|
"type": "MODAL_CLOSE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "BATTLE_FIGHTER_DAMAGE",
|
||||||
|
"fighter": "target",
|
||||||
|
"amount": 8
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,13 +12,13 @@
|
|||||||
#include "rpg/battle/fighter/battlefighterpos.h"
|
#include "rpg/battle/fighter/battlefighterpos.h"
|
||||||
#include "rpg/battle/state/battlestate.h"
|
#include "rpg/battle/state/battlestate.h"
|
||||||
|
|
||||||
// typedef enum {
|
typedef enum {
|
||||||
// BATTLE_RESULT_NONE,
|
BATTLE_RESULT_NONE,
|
||||||
// BATTLE_RESULT_WIN,
|
BATTLE_RESULT_WIN,
|
||||||
// BATTLE_RESULT_LOSS,
|
BATTLE_RESULT_LOSS,
|
||||||
// BATTLE_RESULT_FLED,
|
BATTLE_RESULT_FLED,
|
||||||
// BATTLE_RESULT_COUNT
|
BATTLE_RESULT_COUNT
|
||||||
// } battleresult_t;
|
} battleresult_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
battlefighter_t *fighters[BATTLE_FIGHTER_POS_COUNT];
|
battlefighter_t *fighters[BATTLE_FIGHTER_POS_COUNT];
|
||||||
@@ -33,6 +33,11 @@ typedef struct {
|
|||||||
|
|
||||||
battlestate_t state;
|
battlestate_t state;
|
||||||
battlestatedata_t stateData;
|
battlestatedata_t stateData;
|
||||||
|
|
||||||
|
// Set once a side has been wiped out (see cutsceneBattlePostMoveStart)
|
||||||
|
// or the player has fled (see battleStateSelectionPlayerFlee). Stays
|
||||||
|
// BATTLE_RESULT_NONE for the rest of the battle otherwise.
|
||||||
|
battleresult_t result;
|
||||||
} battle_t;
|
} battle_t;
|
||||||
|
|
||||||
extern battle_t BATTLE;
|
extern battle_t BATTLE;
|
||||||
|
|||||||
@@ -24,4 +24,12 @@ typedef uint8_t battlefighterpos_t;
|
|||||||
#define BATTLE_FIGHTER_POS_6 BATTLE_FIGHTER_POS(6)
|
#define BATTLE_FIGHTER_POS_6 BATTLE_FIGHTER_POS(6)
|
||||||
#define BATTLE_FIGHTER_POS_7 BATTLE_FIGHTER_POS(7)
|
#define BATTLE_FIGHTER_POS_7 BATTLE_FIGHTER_POS(7)
|
||||||
|
|
||||||
#define BATTLE_FIGHTER_POS_COUNT (BATTLE_FIGHTER_POS_7 + 1)
|
#define BATTLE_FIGHTER_POS_COUNT (BATTLE_FIGHTER_POS_7 + 1)
|
||||||
|
|
||||||
|
// Sentinels resolved by battleStateExecutingInit against the currently
|
||||||
|
// resolving action, for cutscene items authored inside a move's own
|
||||||
|
// cutscene (e.g. cutscenes/battle/moves/slash.jsonc) that need to refer
|
||||||
|
// to "whoever is being hit"/"whoever is attacking" without knowing their
|
||||||
|
// concrete BATTLE.fighters index ahead of time.
|
||||||
|
#define BATTLE_FIGHTER_POS_TARGET BATTLE_FIGHTER_POS(0xFE)
|
||||||
|
#define BATTLE_FIGHTER_POS_USER BATTLE_FIGHTER_POS(0xFD)
|
||||||
@@ -18,6 +18,18 @@
|
|||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
|
||||||
|
|
||||||
|
void battleStateExecutingResolveFighterIndex(
|
||||||
|
battlefighterpos_t *fighterIndex,
|
||||||
|
const uint8_t userIndex,
|
||||||
|
const battlemoveaction_t *action
|
||||||
|
) {
|
||||||
|
if(*fighterIndex == BATTLE_FIGHTER_POS_TARGET) {
|
||||||
|
*fighterIndex = action->targets[0];
|
||||||
|
} else if(*fighterIndex == BATTLE_FIGHTER_POS_USER) {
|
||||||
|
*fighterIndex = userIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void battleStateExecutingInit(void) {
|
void battleStateExecutingInit(void) {
|
||||||
cutsceneitem_t items[BATTLE_STATE_EXECUTING_ITEMS_MAX];
|
cutsceneitem_t items[BATTLE_STATE_EXECUTING_ITEMS_MAX];
|
||||||
uint8_t itemsCount = 0;
|
uint8_t itemsCount = 0;
|
||||||
@@ -60,6 +72,7 @@ void battleStateExecutingInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse out cutscene items and append to the list.
|
// Parse out cutscene items and append to the list.
|
||||||
|
const uint8_t moveItemsStart = itemsCount;
|
||||||
cutscene_t scene;
|
cutscene_t scene;
|
||||||
errorret_t parseResult = cutsceneParseDoc(
|
errorret_t parseResult = cutsceneParseDoc(
|
||||||
entry->data.json, &scene, &items[itemsCount],
|
entry->data.json, &scene, &items[itemsCount],
|
||||||
@@ -75,6 +88,21 @@ void battleStateExecutingInit(void) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve BATTLE_FIGHTER_POS_TARGET/USER sentinels against this
|
||||||
|
// action, now that its user/target fighters are known.
|
||||||
|
for(uint8_t j = moveItemsStart; j < itemsCount; j++) {
|
||||||
|
cutsceneitem_t *moveItem = &items[j];
|
||||||
|
if(moveItem->type == CUTSCENE_ITEM_TYPE_BATTLE_FIGHTER_DAMAGE) {
|
||||||
|
battleStateExecutingResolveFighterIndex(
|
||||||
|
&moveItem->battleFighterDamage.fighterIndex, i, action
|
||||||
|
);
|
||||||
|
} else if(moveItem->type == CUTSCENE_ITEM_TYPE_BATTLE_FIGHTER_HEAL) {
|
||||||
|
battleStateExecutingResolveFighterIndex(
|
||||||
|
&moveItem->battleFighterHeal.fighterIndex, i, action
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Insert end of attack cutscene item.
|
// Insert end of attack cutscene item.
|
||||||
items[itemsCount++] = (cutsceneitem_t){
|
items[itemsCount++] = (cutsceneitem_t){
|
||||||
.type = CUTSCENE_ITEM_TYPE_BATTLE_POST_MOVE
|
.type = CUTSCENE_ITEM_TYPE_BATTLE_POST_MOVE
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
#include "dusk.h"
|
||||||
|
#include "rpg/battle/fighter/battlefighterpos.h"
|
||||||
|
#include "rpg/battle/move/battlemoveaction.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
||||||
@@ -19,6 +21,24 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
void battleStateExecutingInit(void);
|
void battleStateExecutingInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a BATTLE_FIGHTER_POS_TARGET/BATTLE_FIGHTER_POS_USER sentinel
|
||||||
|
* (as parsed onto a BATTLE_FIGHTER_DAMAGE/BATTLE_FIGHTER_HEAL item inside
|
||||||
|
* a move's own cutscene) against the action currently being resolved
|
||||||
|
* into cutscene items. Leaves fighterIndex untouched if it's already a
|
||||||
|
* concrete BATTLE.fighters index.
|
||||||
|
*
|
||||||
|
* @param fighterIndex The field to resolve in place.
|
||||||
|
* @param userIndex Index into BATTLE.fighters of the fighter performing
|
||||||
|
* the action.
|
||||||
|
* @param action The action being resolved.
|
||||||
|
*/
|
||||||
|
void battleStateExecutingResolveFighterIndex(
|
||||||
|
battlefighterpos_t *fighterIndex,
|
||||||
|
const uint8_t userIndex,
|
||||||
|
const battlemoveaction_t *action
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates BATTLE_STATE_EXECUTING for one frame.
|
* Updates BATTLE_STATE_EXECUTING for one frame.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -6,9 +6,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "battlestatefled.h"
|
#include "battlestatefled.h"
|
||||||
|
#include "rpg/cutscene/item/cutsceneitem.h"
|
||||||
|
#include "rpg/cutscene/cutscenesystem.h"
|
||||||
|
|
||||||
void battleStateFledInit(void) {
|
void battleStateFledInit(void) {
|
||||||
|
const cutsceneitem_t items[] = {
|
||||||
|
{
|
||||||
|
.type = CUTSCENE_ITEM_TYPE_TEXT,
|
||||||
|
.text = { .text = "Got away safely!" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = CUTSCENE_ITEM_TYPE_BATTLE_SET_STATE,
|
||||||
|
.battleSetState = { .state = BATTLE_STATE_ENDING }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cutsceneSystemInsertItemsNext(items, sizeof(items) / sizeof(cutsceneitem_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
void battleStateFledUpdate(void) {
|
void battleStateFledUpdate(void) {
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ typedef struct {
|
|||||||
} battlestatefled_t;
|
} battlestatefled_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the battle enters BATTLE_STATE_FLED.
|
* Called when the battle enters BATTLE_STATE_FLED. Queues a textbox
|
||||||
|
* announcing the escape, then transitions to BATTLE_STATE_ENDING once
|
||||||
|
* dismissed.
|
||||||
*/
|
*/
|
||||||
void battleStateFledInit(void);
|
void battleStateFledInit(void);
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "battlestatelost.h"
|
#include "battlestatelost.h"
|
||||||
|
#include "rpg/cutscene/item/cutsceneitem.h"
|
||||||
|
#include "rpg/cutscene/cutscenesystem.h"
|
||||||
|
|
||||||
void battleStateLostInit(void) {
|
void battleStateLostInit(void) {
|
||||||
|
const cutsceneitem_t items[] = {
|
||||||
|
{
|
||||||
|
.type = CUTSCENE_ITEM_TYPE_TEXT,
|
||||||
|
.text = { .text = "You were defeated..." }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = CUTSCENE_ITEM_TYPE_BATTLE_SET_STATE,
|
||||||
|
.battleSetState = { .state = BATTLE_STATE_ENDING }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cutsceneSystemInsertItemsNext(items, sizeof(items) / sizeof(cutsceneitem_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
void battleStateLostUpdate(void) {
|
void battleStateLostUpdate(void) {
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ typedef struct {
|
|||||||
} battlestatelost_t;
|
} battlestatelost_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the battle enters BATTLE_STATE_LOST.
|
* Called when the battle enters BATTLE_STATE_LOST. Queues a textbox
|
||||||
|
* announcing the loss, then transitions to BATTLE_STATE_ENDING once
|
||||||
|
* dismissed.
|
||||||
*/
|
*/
|
||||||
void battleStateLostInit(void);
|
void battleStateLostInit(void);
|
||||||
|
|
||||||
|
|||||||
@@ -101,5 +101,6 @@ void battleStateSelectionPlayerFlee(void) {
|
|||||||
battlefighter_t *fighter = battleStateSelectionGetCurrentFighter();
|
battlefighter_t *fighter = battleStateSelectionGetCurrentFighter();
|
||||||
if(fighter == NULL) return;
|
if(fighter == NULL) return;
|
||||||
|
|
||||||
|
BATTLE.result = BATTLE_RESULT_FLED;
|
||||||
battleSetState(BATTLE_STATE_FLED);
|
battleSetState(BATTLE_STATE_FLED);
|
||||||
}
|
}
|
||||||
@@ -6,9 +6,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "battlestatewon.h"
|
#include "battlestatewon.h"
|
||||||
|
#include "rpg/cutscene/item/cutsceneitem.h"
|
||||||
|
#include "rpg/cutscene/cutscenesystem.h"
|
||||||
|
|
||||||
void battleStateWonInit(void) {
|
void battleStateWonInit(void) {
|
||||||
|
const cutsceneitem_t items[] = {
|
||||||
|
{
|
||||||
|
.type = CUTSCENE_ITEM_TYPE_TEXT,
|
||||||
|
.text = { .text = "You won the battle!" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = CUTSCENE_ITEM_TYPE_BATTLE_SET_STATE,
|
||||||
|
.battleSetState = { .state = BATTLE_STATE_ENDING }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cutsceneSystemInsertItemsNext(items, sizeof(items) / sizeof(cutsceneitem_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
void battleStateWonUpdate(void) {
|
void battleStateWonUpdate(void) {
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ typedef struct {
|
|||||||
} battlestatewon_t;
|
} battlestatewon_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the battle enters BATTLE_STATE_WON.
|
* Called when the battle enters BATTLE_STATE_WON. Queues a textbox
|
||||||
|
* announcing the win, then transitions to BATTLE_STATE_ENDING once
|
||||||
|
* dismissed.
|
||||||
*/
|
*/
|
||||||
void battleStateWonInit(void);
|
void battleStateWonInit(void);
|
||||||
|
|
||||||
|
|||||||
@@ -9,4 +9,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
cutscenebattlesetstate.c
|
cutscenebattlesetstate.c
|
||||||
cutscenebattlewaitselection.c
|
cutscenebattlewaitselection.c
|
||||||
cutscenebattlepostmove.c
|
cutscenebattlepostmove.c
|
||||||
|
cutscenebattlefighterdamage.c
|
||||||
|
cutscenebattlefighterheal.c
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rpg/cutscene/item/cutsceneitem.h"
|
||||||
|
#include "rpg/battle/battle.h"
|
||||||
|
#include "rpg/cutscene/item/json/cutscenejsonfield.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
|
||||||
|
#define CUTSCENE_BATTLE_FIGHTER_REF_MAX 16
|
||||||
|
|
||||||
|
void cutsceneBattleFighterDamageStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
const cutscenebattlefighterdamage_t *damage = &item->battleFighterDamage;
|
||||||
|
assertTrue(
|
||||||
|
damage->fighterIndex < BATTLE_FIGHTER_POS_COUNT,
|
||||||
|
"fighterIndex sentinel was never resolved"
|
||||||
|
);
|
||||||
|
battlefighter_t *fighter = BATTLE.fighters[damage->fighterIndex];
|
||||||
|
assertNotNull(fighter, "Fighter cannot be NULL");
|
||||||
|
|
||||||
|
battleFighterDamage(fighter, damage->amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t cutsceneBattleFighterDamageUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t cutsceneBattleFighterDamageLoad(
|
||||||
|
yyjson_val *itemObj,
|
||||||
|
cutsceneitem_t *item
|
||||||
|
) {
|
||||||
|
char_t fighterRef[CUTSCENE_BATTLE_FIGHTER_REF_MAX];
|
||||||
|
if(!cutsceneJsonCopyString(
|
||||||
|
itemObj, "fighter", NULL, fighterRef, CUTSCENE_BATTLE_FIGHTER_REF_MAX
|
||||||
|
)) {
|
||||||
|
errorThrow("Cutscene item (BATTLE_FIGHTER_DAMAGE): missing 'fighter'");
|
||||||
|
}
|
||||||
|
if(stringEquals(fighterRef, "target")) {
|
||||||
|
item->battleFighterDamage.fighterIndex = BATTLE_FIGHTER_POS_TARGET;
|
||||||
|
} else if(stringEquals(fighterRef, "user")) {
|
||||||
|
item->battleFighterDamage.fighterIndex = BATTLE_FIGHTER_POS_USER;
|
||||||
|
} else {
|
||||||
|
errorThrow(
|
||||||
|
"Cutscene item (BATTLE_FIGHTER_DAMAGE): 'fighter' must be "
|
||||||
|
"'target' or 'user'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32_t amount = cutsceneJsonGetInt(itemObj, "amount", -1);
|
||||||
|
if(amount < 0 || amount > UINT16_MAX) {
|
||||||
|
errorThrow(
|
||||||
|
"Cutscene item (BATTLE_FIGHTER_DAMAGE): invalid/missing 'amount'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
item->battleFighterDamage.amount = (uint16_t)amount;
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "rpg/cutscene/item/cutsceneitembase.h"
|
||||||
|
#include "rpg/battle/fighter/battlefighterpos.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
// Index into BATTLE.fighters of the fighter to damage, or one of
|
||||||
|
// BATTLE_FIGHTER_POS_TARGET/BATTLE_FIGHTER_POS_USER, resolved against
|
||||||
|
// the currently-executing action by battleStateExecutingInit before
|
||||||
|
// this item ever runs.
|
||||||
|
battlefighterpos_t fighterIndex;
|
||||||
|
uint16_t amount;
|
||||||
|
} cutscenebattlefighterdamage_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a BATTLE_FIGHTER_DAMAGE item, applying the configured damage to
|
||||||
|
* BATTLE.fighters[fighterIndex] immediately.
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
*/
|
||||||
|
void cutsceneBattleFighterDamageStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a BATTLE_FIGHTER_DAMAGE item (always completes immediately).
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
* @returns true always.
|
||||||
|
*/
|
||||||
|
bool_t cutsceneBattleFighterDamageUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a BATTLE_FIGHTER_DAMAGE 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 cutsceneBattleFighterDamageLoad(
|
||||||
|
yyjson_val *itemObj,
|
||||||
|
cutsceneitem_t *item
|
||||||
|
);
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rpg/cutscene/item/cutsceneitem.h"
|
||||||
|
#include "rpg/battle/battle.h"
|
||||||
|
#include "rpg/cutscene/item/json/cutscenejsonfield.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
|
||||||
|
#define CUTSCENE_BATTLE_FIGHTER_REF_MAX 16
|
||||||
|
|
||||||
|
void cutsceneBattleFighterHealStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
const cutscenebattlefighterheal_t *heal = &item->battleFighterHeal;
|
||||||
|
assertTrue(
|
||||||
|
heal->fighterIndex < BATTLE_FIGHTER_POS_COUNT,
|
||||||
|
"fighterIndex sentinel was never resolved"
|
||||||
|
);
|
||||||
|
battlefighter_t *fighter = BATTLE.fighters[heal->fighterIndex];
|
||||||
|
assertNotNull(fighter, "Fighter cannot be NULL");
|
||||||
|
|
||||||
|
battleFighterHeal(fighter, heal->amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t cutsceneBattleFighterHealUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t cutsceneBattleFighterHealLoad(
|
||||||
|
yyjson_val *itemObj,
|
||||||
|
cutsceneitem_t *item
|
||||||
|
) {
|
||||||
|
char_t fighterRef[CUTSCENE_BATTLE_FIGHTER_REF_MAX];
|
||||||
|
if(!cutsceneJsonCopyString(
|
||||||
|
itemObj, "fighter", NULL, fighterRef, CUTSCENE_BATTLE_FIGHTER_REF_MAX
|
||||||
|
)) {
|
||||||
|
errorThrow("Cutscene item (BATTLE_FIGHTER_HEAL): missing 'fighter'");
|
||||||
|
}
|
||||||
|
if(stringEquals(fighterRef, "target")) {
|
||||||
|
item->battleFighterHeal.fighterIndex = BATTLE_FIGHTER_POS_TARGET;
|
||||||
|
} else if(stringEquals(fighterRef, "user")) {
|
||||||
|
item->battleFighterHeal.fighterIndex = BATTLE_FIGHTER_POS_USER;
|
||||||
|
} else {
|
||||||
|
errorThrow(
|
||||||
|
"Cutscene item (BATTLE_FIGHTER_HEAL): 'fighter' must be "
|
||||||
|
"'target' or 'user'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32_t amount = cutsceneJsonGetInt(itemObj, "amount", -1);
|
||||||
|
if(amount < 0 || amount > UINT16_MAX) {
|
||||||
|
errorThrow(
|
||||||
|
"Cutscene item (BATTLE_FIGHTER_HEAL): invalid/missing 'amount'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
item->battleFighterHeal.amount = (uint16_t)amount;
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "rpg/cutscene/item/cutsceneitembase.h"
|
||||||
|
#include "rpg/battle/fighter/battlefighterpos.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
// Index into BATTLE.fighters of the fighter to heal, or one of
|
||||||
|
// BATTLE_FIGHTER_POS_TARGET/BATTLE_FIGHTER_POS_USER, resolved against
|
||||||
|
// the currently-executing action by battleStateExecutingInit before
|
||||||
|
// this item ever runs.
|
||||||
|
battlefighterpos_t fighterIndex;
|
||||||
|
uint16_t amount;
|
||||||
|
} cutscenebattlefighterheal_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a BATTLE_FIGHTER_HEAL item, applying the configured heal to
|
||||||
|
* BATTLE.fighters[fighterIndex] immediately.
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
*/
|
||||||
|
void cutsceneBattleFighterHealStart(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a BATTLE_FIGHTER_HEAL item (always completes immediately).
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
* @param data Runtime data storage.
|
||||||
|
* @returns true always.
|
||||||
|
*/
|
||||||
|
bool_t cutsceneBattleFighterHealUpdate(
|
||||||
|
const cutsceneitem_t *item,
|
||||||
|
cutsceneitemdata_t *data
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a BATTLE_FIGHTER_HEAL 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 cutsceneBattleFighterHealLoad(
|
||||||
|
yyjson_val *itemObj,
|
||||||
|
cutsceneitem_t *item
|
||||||
|
);
|
||||||
@@ -6,15 +6,44 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "rpg/cutscene/item/cutsceneitem.h"
|
#include "rpg/cutscene/item/cutsceneitem.h"
|
||||||
|
#include "rpg/battle/battle.h"
|
||||||
|
#include "rpg/cutscene/cutscenesystem.h"
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
|
|
||||||
void cutsceneBattlePostMoveStart(
|
void cutsceneBattlePostMoveStart(
|
||||||
const cutsceneitem_t *item,
|
const cutsceneitem_t *item,
|
||||||
cutsceneitemdata_t *data
|
cutsceneitemdata_t *data
|
||||||
) {
|
) {
|
||||||
// TODO: check fighter health etc. here to short-circuit into a
|
|
||||||
// win/loss (BATTLE_STATE_WON/LOST) once a side has been wiped out.
|
|
||||||
consolePrint("BATTLE_POST_MOVE\n");
|
consolePrint("BATTLE_POST_MOVE\n");
|
||||||
|
|
||||||
|
// Once a result has already been decided (e.g. the player fled), there
|
||||||
|
// is nothing left to check.
|
||||||
|
if(BATTLE.result != BATTLE_RESULT_NONE) return;
|
||||||
|
|
||||||
|
bool_t allyAlive = false;
|
||||||
|
bool_t enemyAlive = false;
|
||||||
|
for(uint8_t i = 0; i < BATTLE_FIGHTER_POS_COUNT; i++) {
|
||||||
|
battlefighter_t *fighter = BATTLE.fighters[i];
|
||||||
|
if(fighter == NULL) continue;
|
||||||
|
if(!battleFighterIsAlive(fighter)) continue;
|
||||||
|
|
||||||
|
if(fighter->team == BATTLE_FIGHTER_TEAM_ALLY) allyAlive = true;
|
||||||
|
else enemyAlive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allyAlive && enemyAlive) return;
|
||||||
|
|
||||||
|
BATTLE.result = allyAlive ? BATTLE_RESULT_WIN : BATTLE_RESULT_LOSS;
|
||||||
|
|
||||||
|
const cutsceneitem_t items[] = {
|
||||||
|
{
|
||||||
|
.type = CUTSCENE_ITEM_TYPE_BATTLE_SET_STATE,
|
||||||
|
.battleSetState = {
|
||||||
|
.state = allyAlive ? BATTLE_STATE_WON : BATTLE_STATE_LOST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cutsceneSystemInsertItemsNext(items, sizeof(items) / sizeof(cutsceneitem_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool_t cutsceneBattlePostMoveUpdate(
|
bool_t cutsceneBattlePostMoveUpdate(
|
||||||
|
|||||||
@@ -10,10 +10,11 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a BATTLE_POST_MOVE item. Runs after each queued move's cutscene
|
* Starts a BATTLE_POST_MOVE item. Runs after each queued move's cutscene
|
||||||
* finishes - eventually this is where we'll check things like fighter
|
* finishes: checks whether every ally or every enemy fighter has been
|
||||||
* health to short-circuit into a win/loss, but for now it just prints to
|
* knocked out and, if so, sets BATTLE.result and splices in a
|
||||||
* the console. Not JSON-authorable -- constructed dynamically by
|
* BATTLE_SET_STATE to BATTLE_STATE_WON/LOST. No-ops the health check if
|
||||||
* battleStateExecutingInit.
|
* BATTLE.result is already set (e.g. the player fled). Not
|
||||||
|
* JSON-authorable -- constructed dynamically by battleStateExecutingInit.
|
||||||
*
|
*
|
||||||
* @param item The cutscene item.
|
* @param item The cutscene item.
|
||||||
* @param data Runtime data storage.
|
* @param data Runtime data storage.
|
||||||
|
|||||||
@@ -270,6 +270,20 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = {
|
|||||||
.update = cutsceneBattlePostMoveUpdate
|
.update = cutsceneBattlePostMoveUpdate
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[CUTSCENE_ITEM_TYPE_BATTLE_FIGHTER_DAMAGE] = {
|
||||||
|
.init = cutsceneBattleFighterDamageStart,
|
||||||
|
.update = cutsceneBattleFighterDamageUpdate,
|
||||||
|
.jsonName = "BATTLE_FIGHTER_DAMAGE",
|
||||||
|
.load = cutsceneBattleFighterDamageLoad
|
||||||
|
},
|
||||||
|
|
||||||
|
[CUTSCENE_ITEM_TYPE_BATTLE_FIGHTER_HEAL] = {
|
||||||
|
.init = cutsceneBattleFighterHealStart,
|
||||||
|
.update = cutsceneBattleFighterHealUpdate,
|
||||||
|
.jsonName = "BATTLE_FIGHTER_HEAL",
|
||||||
|
.load = cutsceneBattleFighterHealLoad
|
||||||
|
},
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
[CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK] = {
|
[CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK] = {
|
||||||
.init = cutsceneSaveDeviceCheckStart,
|
.init = cutsceneSaveDeviceCheckStart,
|
||||||
|
|||||||
@@ -44,6 +44,8 @@
|
|||||||
#include "battle/cutscenebattlesetstate.h"
|
#include "battle/cutscenebattlesetstate.h"
|
||||||
#include "battle/cutscenebattlewaitselection.h"
|
#include "battle/cutscenebattlewaitselection.h"
|
||||||
#include "battle/cutscenebattlepostmove.h"
|
#include "battle/cutscenebattlepostmove.h"
|
||||||
|
#include "battle/cutscenebattlefighterdamage.h"
|
||||||
|
#include "battle/cutscenebattlefighterheal.h"
|
||||||
#include "save/cutscenesavedevicecheck.h"
|
#include "save/cutscenesavedevicecheck.h"
|
||||||
#include "save/cutscenesaveloadallslots.h"
|
#include "save/cutscenesaveloadallslots.h"
|
||||||
#include "audio/cutsceneaudioplay.h"
|
#include "audio/cutsceneaudioplay.h"
|
||||||
@@ -118,6 +120,8 @@ struct cutsceneitem_s {
|
|||||||
cutscenemapareawait_t mapAreaWait;
|
cutscenemapareawait_t mapAreaWait;
|
||||||
cutscenebattlestart_t battleStart;
|
cutscenebattlestart_t battleStart;
|
||||||
cutscenebattlesetstate_t battleSetState;
|
cutscenebattlesetstate_t battleSetState;
|
||||||
|
cutscenebattlefighterdamage_t battleFighterDamage;
|
||||||
|
cutscenebattlefighterheal_t battleFighterHeal;
|
||||||
cutsceneemoji_t emoji;
|
cutsceneemoji_t emoji;
|
||||||
cutsceneshake_t shake;
|
cutsceneshake_t shake;
|
||||||
cutscenemodal_t modal;
|
cutscenemodal_t modal;
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ typedef enum {
|
|||||||
CUTSCENE_ITEM_TYPE_BATTLE_SET_STATE,
|
CUTSCENE_ITEM_TYPE_BATTLE_SET_STATE,
|
||||||
CUTSCENE_ITEM_TYPE_BATTLE_WAIT_SELECTION,
|
CUTSCENE_ITEM_TYPE_BATTLE_WAIT_SELECTION,
|
||||||
CUTSCENE_ITEM_TYPE_BATTLE_POST_MOVE,
|
CUTSCENE_ITEM_TYPE_BATTLE_POST_MOVE,
|
||||||
|
CUTSCENE_ITEM_TYPE_BATTLE_FIGHTER_DAMAGE,
|
||||||
|
CUTSCENE_ITEM_TYPE_BATTLE_FIGHTER_HEAL,
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK,
|
CUTSCENE_ITEM_TYPE_SAVE_DEVICE_CHECK,
|
||||||
|
|||||||
Reference in New Issue
Block a user