diff --git a/src/dusk/rpg/battle/battleability.h b/src/dusk/rpg/battle/battleability.h new file mode 100644 index 00000000..6b2297e4 --- /dev/null +++ b/src/dusk/rpg/battle/battleability.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 "error/error.h" + +typedef enum { + BATTLE_ABILITY_NULL, + + BATTLE_ABILITY_FIRE, + + BATTLE_ABILITY_COUNT +} battleability_t; diff --git a/src/dusk/rpg/battle/battlefighter.h b/src/dusk/rpg/battle/battlefighter.h index af032331..1b3674b1 100644 --- a/src/dusk/rpg/battle/battlefighter.h +++ b/src/dusk/rpg/battle/battlefighter.h @@ -7,6 +7,9 @@ #pragma once #include "error/error.h" +#include "battleability.h" + +#define BATTLE_FIGHTER_ABILITY_COUNT_MAX 8 // An empty status means the slot in BATTLE.fighters is unused. typedef enum { @@ -55,6 +58,7 @@ typedef struct { uint16_t mpMax; battlefighterstats_t stats; + battleability_t abilities[BATTLE_FIGHTER_ABILITY_COUNT_MAX]; } battlefighter_t; /** diff --git a/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.c b/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.c index 338fd62d..73ee5843 100644 --- a/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.c +++ b/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.c @@ -10,7 +10,9 @@ #include "scene/scene.h" #include "save/save.h" #include "rpg/cutscene/item/json/cutscenejsonbattleencounter.h" +#include "rpg/cutscene/item/json/cutscenejsonbattleability.h" #include "rpg/cutscene/item/json/cutscenejsonfield.h" +#include "util/memory.h" void cutsceneStartBattleStart( const cutsceneitem_t *item, @@ -34,14 +36,18 @@ void cutsceneStartBattleStart( fighter->health = member->health; fighter->mp = member->mp; fighter->status = member->status; + memoryCopy(fighter->abilities, member->abilities, sizeof(fighter->abilities)); } for(uint8_t i = 0; i < config->enemyCount; i++) { const cutscenestartbattleenemy_t *enemy = &config->enemies[i]; - battleAddFighter( + battlefighter_t *fighter = battleAddFighter( BATTLE_FIGHTER_TEAM_ENEMY, BATTLE_FIGHTER_CONTROLLER_AI, enemy->stats, enemy->healthMax, enemy->mpMax ); + if(fighter == NULL) continue; + + memoryCopy(fighter->abilities, enemy->abilities, sizeof(fighter->abilities)); } battleStart(config->encounterType, config->fleeAvailable); @@ -162,6 +168,37 @@ errorret_t cutsceneStartBattleLoad( (uint16_t)yyjson_get_int(yyjson_obj_get(enemyVal, "healthMax")); enemy->mpMax = (uint16_t)yyjson_get_int(yyjson_obj_get(enemyVal, "mpMax")); + + yyjson_val *abilities = yyjson_obj_get(enemyVal, "abilities"); + if(abilities != NULL) { + size_t abilityCount = yyjson_arr_size(abilities); + if(!yyjson_is_arr(abilities) || + abilityCount > BATTLE_FIGHTER_ABILITY_COUNT_MAX) { + errorThrow( + "Cutscene item (START_BATTLE): enemies[%u].abilities must have " + "0-%u entries", + (uint32_t)e, (uint32_t)BATTLE_FIGHTER_ABILITY_COUNT_MAX + ); + } + + yyjson_arr_iter abilityIter = yyjson_arr_iter_with(abilities); + yyjson_val *abilityVal; + size_t a = 0; + while((abilityVal = yyjson_arr_iter_next(&abilityIter)) != NULL) { + battleability_t ability; + if(!cutsceneJsonLookupBattleAbility( + yyjson_get_str(abilityVal), &ability + )) { + errorThrow( + "Cutscene item (START_BATTLE): enemies[%u].abilities[%u] is " + "invalid", + (uint32_t)e, (uint32_t)a + ); + } + enemy->abilities[a] = ability; + a++; + } + } e++; } diff --git a/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.h b/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.h index 8bf740f3..007f16d3 100644 --- a/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.h +++ b/src/dusk/rpg/cutscene/item/battle/cutscenestartbattle.h @@ -16,6 +16,7 @@ typedef struct { battlefighterstats_t stats; uint16_t healthMax; uint16_t mpMax; + battleability_t abilities[BATTLE_FIGHTER_ABILITY_COUNT_MAX]; } cutscenestartbattleenemy_t; typedef struct { diff --git a/src/dusk/rpg/cutscene/item/json/CMakeLists.txt b/src/dusk/rpg/cutscene/item/json/CMakeLists.txt index d15a1d9e..2afa3112 100644 --- a/src/dusk/rpg/cutscene/item/json/CMakeLists.txt +++ b/src/dusk/rpg/cutscene/item/json/CMakeLists.txt @@ -16,6 +16,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} cutscenejsonentitytype.c cutscenejsonentitydir.c cutscenejsonbattleencounter.c + cutscenejsonbattleability.c cutscenejsonbattlestate.c cutscenejsonscenetype.c cutscenejsonuiscreentype.c diff --git a/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.c b/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.c new file mode 100644 index 00000000..adb9dc39 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.c @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "cutscenejsonbattleability.h" +#include "cutscenejsonenum.h" + +static const cutscenejsonenumentry_t CUTSCENE_JSON_BATTLE_ABILITY_TABLE[] = { + { "FIRE", BATTLE_ABILITY_FIRE }, +}; +#define CUTSCENE_JSON_BATTLE_ABILITY_TABLE_COUNT \ + (sizeof(CUTSCENE_JSON_BATTLE_ABILITY_TABLE) / \ + sizeof(cutscenejsonenumentry_t)) + +bool_t cutsceneJsonLookupBattleAbility( + const char_t *name, + battleability_t *out +) { + int32_t value; + if(!cutsceneJsonLookupEnumInsensitive( + CUTSCENE_JSON_BATTLE_ABILITY_TABLE, + CUTSCENE_JSON_BATTLE_ABILITY_TABLE_COUNT, name, &value + )) { + return false; + } + *out = (battleability_t)value; + return true; +} diff --git a/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.h b/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.h new file mode 100644 index 00000000..bc6abd95 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/json/cutscenejsonbattleability.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" +#include "rpg/battle/battleability.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. + * @return true if a matching entry was found, false otherwise. + */ +bool_t cutsceneJsonLookupBattleAbility( + const char_t *name, + battleability_t *out +);