commit 81bcb377a82b5ffbacbc150ef3fb17674eee9622 parent 0ad328617881fd26dd8bafb5d661ac05fd94ed3c Author: beep <beep@wimdupont.com> Date: Thu, 30 Jul 2026 10:52:44 +0000 Add adaptive music and combat audio Diffstat:
23 files changed, 319 insertions(+), 9 deletions(-)
diff --git a/Makefile b/Makefile @@ -14,9 +14,17 @@ ITEMS_SRC = assets/vendor/16x16-rpg-items/items.png ITEMS_BMP = $(OBJDIR)/assets/items.bmp CRITTERS_SRC = assets/vendor/16x16-animated-critters/16x16babies.png CRITTERS_BMP = $(OBJDIR)/assets/critters.bmp +AUDIO = \ + assets/vendor/calm-loop/meadow.wav \ + assets/vendor/peacefull-ville/stonehollow.wav \ + assets/vendor/battle-theme/battle.wav \ + assets/vendor/rpg-sound-pack/attack.wav \ + assets/vendor/rpg-sound-pack/hit.wav \ + assets/vendor/rpg-sound-pack/spell.wav SRC = \ src/engine/area.c \ + src/engine/audio.c \ src/engine/battle.c \ src/engine/render.c \ src/engine/world.c \ @@ -53,17 +61,17 @@ endif all: $(BIN) $(MAPBIN) $(TILESET_BMP) $(HERO_BMP) $(ITEMS_BMP) $(CRITTERS_BMP) -$(BIN): $(OBJ) $(MAPBIN) $(TILESET_BMP) $(HERO_BMP) $(ITEMS_BMP) $(CRITTERS_BMP) +$(BIN): $(OBJ) $(MAPBIN) $(TILESET_BMP) $(HERO_BMP) $(ITEMS_BMP) $(CRITTERS_BMP) $(AUDIO) @mkdir -p $(dir $@) $(CC) $(OBJ) -o $@ $(LDLIBS) $(SMOKE): $(CORE_OBJ) $(SMOKE_OBJ) @mkdir -p $(dir $@) - $(CC) $^ -o $@ -lm + $(CC) $^ -o $@ $(LDLIBS) $(RENDER_SMOKE): $(CORE_OBJ) $(OBJDIR)/engine/render.o \ $(OBJDIR)/game/presentation.o $(RENDER_SMOKE_OBJ) $(MAPBIN) \ - $(TILESET_BMP) $(HERO_BMP) $(ITEMS_BMP) $(CRITTERS_BMP) + $(TILESET_BMP) $(HERO_BMP) $(ITEMS_BMP) $(CRITTERS_BMP) $(AUDIO) @mkdir -p $(dir $@) $(CC) $(CORE_OBJ) $(OBJDIR)/engine/render.o $(OBJDIR)/game/presentation.o \ $(RENDER_SMOKE_OBJ) \ @@ -106,7 +114,7 @@ smoke: $(MAPBIN) $(SMOKE) $(SMOKE) render-smoke: $(RENDER_SMOKE) - SDL_VIDEODRIVER=dummy $(RENDER_SMOKE) + SDL_AUDIODRIVER=dummy SDL_VIDEODRIVER=dummy $(RENDER_SMOKE) clean: rm -rf bin $(OBJDIR) diff --git a/assets/vendor/battle-theme/SOURCE.txt b/assets/vendor/battle-theme/SOURCE.txt @@ -0,0 +1,7 @@ +Title: Battle Theme +Author: Wolfgang_ (Theodore Kerr) +Source: https://opengameart.org/content/battle-theme-0 +License: CC0 1.0 + +battle.wav is derived from Battle.mp3 and converted to 22.05 kHz, 16-bit mono +PCM for direct SDL3 playback. diff --git a/assets/vendor/battle-theme/battle.wav b/assets/vendor/battle-theme/battle.wav Binary files differ. diff --git a/assets/vendor/calm-loop/SOURCE.txt b/assets/vendor/calm-loop/SOURCE.txt @@ -0,0 +1,7 @@ +Title: Calm Loop +Author: wipics +Source: https://opengameart.org/content/calm-loop +License: CC0 1.0 + +meadow.wav is derived from Relaxing.mp3 and converted to 22.05 kHz, 16-bit +mono PCM for direct SDL3 playback. diff --git a/assets/vendor/calm-loop/meadow.wav b/assets/vendor/calm-loop/meadow.wav Binary files differ. diff --git a/assets/vendor/peacefull-ville/SOURCE.txt b/assets/vendor/peacefull-ville/SOURCE.txt @@ -0,0 +1,7 @@ +Title: peacefull ville (2023 version) +Author: Bobjt +Source: https://opengameart.org/content/peacefull-ville +License: CC0 1.0 + +stonehollow.wav is derived from peaceful_ville_2023.mp3 and converted to +22.05 kHz, 16-bit mono PCM for direct SDL3 playback. diff --git a/assets/vendor/peacefull-ville/stonehollow.wav b/assets/vendor/peacefull-ville/stonehollow.wav Binary files differ. diff --git a/assets/vendor/rpg-sound-pack/SOURCE.txt b/assets/vendor/rpg-sound-pack/SOURCE.txt @@ -0,0 +1,10 @@ +Title: RPG Sound Pack +Author: artisticdude +Source: https://opengameart.org/content/rpg-sound-pack +License: CC0 1.0 + +Selected and renamed files from the original WAV archive: + +- attack.wav: battle/swing2.wav +- hit.wav: NPC/slime/slime1.wav +- spell.wav: battle/magic1.wav diff --git a/assets/vendor/rpg-sound-pack/attack.wav b/assets/vendor/rpg-sound-pack/attack.wav Binary files differ. diff --git a/assets/vendor/rpg-sound-pack/hit.wav b/assets/vendor/rpg-sound-pack/hit.wav Binary files differ. diff --git a/assets/vendor/rpg-sound-pack/spell.wav b/assets/vendor/rpg-sound-pack/spell.wav Binary files differ. diff --git a/config.def.h b/config.def.h @@ -1,7 +1,7 @@ #ifndef CONFIG_H #define CONFIG_H -#define PH_CONFIG_VERSION 11 +#define PH_CONFIG_VERSION 12 #define PH_VIEW_W 320 #define PH_VIEW_H 240 @@ -9,6 +9,8 @@ #define PH_WINDOW_TITLE "phantasia" #define PH_MAX_FRAME_TIME 0.05f #define PH_FRAME_DELAY_MS 16 +#define PH_MUSIC_GAIN 0.24f +#define PH_EFFECT_GAIN 0.55f #define PH_ENTITY_BLOCK_RADIUS 10 #define PH_INTERACT_RADIUS 20.0f diff --git a/src/engine/audio.c b/src/engine/audio.c @@ -0,0 +1,91 @@ +#include "engine/audio.h" + +#if PH_USE_SDL +#include <string.h> + +static int +ph_audio_stream_clip(SDL_AudioStream *stream, const PhAudioClip *clip, + const SDL_AudioSpec *device_spec) +{ + if (!stream || !clip || !clip->data || !clip->length) return -1; + if (!SDL_ClearAudioStream(stream) || + !SDL_SetAudioStreamFormat(stream, &clip->spec, device_spec) || + !SDL_SetAudioStreamGain(stream, clip->gain) || + !SDL_PutAudioStreamData(stream, clip->data, (int)clip->length)) + return -1; + return 0; +} + +int +ph_audio_init(PhAudio *audio, const PhAudioDef *defs, int count) +{ + int i; + + if (!audio || !defs || count <= 1 || count > PH_AUDIO_CLIP_MAX) return -1; + memset(audio, 0, sizeof(*audio)); + audio->clip_count = count; + for (i = 1; i < count; ++i) { + audio->clips[i].gain = defs[i].gain; + if (!defs[i].path || !SDL_LoadWAV(defs[i].path, &audio->clips[i].spec, + &audio->clips[i].data, &audio->clips[i].length)) goto fail; + } + audio->device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL); + if (!audio->device || !SDL_GetAudioDeviceFormat(audio->device, + &audio->device_spec, NULL)) goto fail; + audio->music_stream = SDL_CreateAudioStream(&audio->clips[1].spec, + &audio->device_spec); + audio->effect_stream = SDL_CreateAudioStream(&audio->clips[1].spec, + &audio->device_spec); + if (!audio->music_stream || !audio->effect_stream || + !SDL_BindAudioStream(audio->device, audio->music_stream) || + !SDL_BindAudioStream(audio->device, audio->effect_stream)) goto fail; + return 0; + +fail: + ph_audio_destroy(audio); + return -1; +} + +void +ph_audio_music(PhAudio *audio, int id) +{ + if (!audio || id <= 0 || id >= audio->clip_count || + id == audio->music_id) return; + if (ph_audio_stream_clip(audio->music_stream, &audio->clips[id], + &audio->device_spec) < 0) return; + audio->music_id = id; +} + +void +ph_audio_effect(PhAudio *audio, int id) +{ + if (!audio || id <= 0 || id >= audio->clip_count) return; + ph_audio_stream_clip(audio->effect_stream, &audio->clips[id], + &audio->device_spec); +} + +void +ph_audio_update(PhAudio *audio) +{ + const PhAudioClip *clip; + + if (!audio || audio->music_id <= 0 || + audio->music_id >= audio->clip_count) return; + clip = &audio->clips[audio->music_id]; + if (SDL_GetAudioStreamQueued(audio->music_stream) < (int)clip->length / 2) + SDL_PutAudioStreamData(audio->music_stream, clip->data, (int)clip->length); +} + +void +ph_audio_destroy(PhAudio *audio) +{ + int i; + + if (!audio) return; + if (audio->music_stream) SDL_DestroyAudioStream(audio->music_stream); + if (audio->effect_stream) SDL_DestroyAudioStream(audio->effect_stream); + if (audio->device) SDL_CloseAudioDevice(audio->device); + for (i = 0; i < audio->clip_count; ++i) SDL_free(audio->clips[i].data); + memset(audio, 0, sizeof(*audio)); +} +#endif diff --git a/src/engine/audio.h b/src/engine/audio.h @@ -0,0 +1,38 @@ +#ifndef PH_ENGINE_AUDIO_H +#define PH_ENGINE_AUDIO_H + +typedef struct { + const char *path; + float gain; +} PhAudioDef; + +#if PH_USE_SDL +#include <SDL3/SDL.h> + +enum { PH_AUDIO_CLIP_MAX = 16 }; + +typedef struct { + SDL_AudioSpec spec; + Uint8 *data; + Uint32 length; + float gain; +} PhAudioClip; + +typedef struct { + SDL_AudioDeviceID device; + SDL_AudioStream *music_stream; + SDL_AudioStream *effect_stream; + SDL_AudioSpec device_spec; + PhAudioClip clips[PH_AUDIO_CLIP_MAX]; + int clip_count; + int music_id; +} PhAudio; + +int ph_audio_init(PhAudio *audio, const PhAudioDef *defs, int count); +void ph_audio_music(PhAudio *audio, int id); +void ph_audio_effect(PhAudio *audio, int id); +void ph_audio_update(PhAudio *audio); +void ph_audio_destroy(PhAudio *audio); +#endif + +#endif diff --git a/src/engine/battle.c b/src/engine/battle.c @@ -369,9 +369,11 @@ ph_battle_resolve(PhBattle *battle, PhWorld *world) def = ph_world_entity_def(world, world->entities[battle->choice.target_index].type_id); if (battle->choice.command == PH_BATTLE_FIGHT) { + battle->event = PH_BATTLE_EVENT_ATTACK; damage = ph_world_physical_attack(world, world->player_index, battle->choice.target_index); } else if (battle->choice.command == PH_BATTLE_MAGICK) { + battle->event = PH_BATTLE_EVENT_SPELL; spell = ph_battle_spell(battle); if (!spell) return; if (spell->battle_use == PH_SPELL_BATTLE_DAMAGE) { @@ -409,6 +411,7 @@ ph_battle_resolve(PhBattle *battle, PhWorld *world) } def = ph_world_entity_def(world, world->entities[battle->enemy_index].type_id); + battle->event = PH_BATTLE_EVENT_HIT; damage = ph_world_physical_attack(world, battle->enemy_index, world->player_index); ph_world_advance_effects(world, battle->enemy_index); snprintf(battle->message, sizeof(battle->message), damage > 0 ? @@ -416,6 +419,17 @@ ph_battle_resolve(PhBattle *battle, PhWorld *world) def && def->name ? def->name : "Enemy", damage); } +PhBattleEvent +ph_battle_take_event(PhBattle *battle) +{ + PhBattleEvent event; + + if (!battle) return PH_BATTLE_EVENT_NONE; + event = battle->event; + battle->event = PH_BATTLE_EVENT_NONE; + return event; +} + PhBattleResult ph_battle_update(PhBattle *battle, PhWorld *world, float dt, int transition) { diff --git a/src/engine/battle.h b/src/engine/battle.h @@ -28,6 +28,13 @@ typedef enum { } PhBattleCommand; typedef enum { + PH_BATTLE_EVENT_NONE, + PH_BATTLE_EVENT_ATTACK, + PH_BATTLE_EVENT_HIT, + PH_BATTLE_EVENT_SPELL, +} PhBattleEvent; + +typedef enum { PH_SPELL_BATTLE_NONE, PH_SPELL_BATTLE_DAMAGE, PH_SPELL_BATTLE_HEAL, @@ -89,6 +96,7 @@ typedef struct { PhBattlePhase phase; char message[64]; PhSpellBook spells; + PhBattleEvent event; } PhBattle; void ph_battle_begin(PhBattle *battle, const PhWorld *world, int enemy_index, @@ -114,5 +122,6 @@ int ph_spellbook_use(const PhSpellBook *spells, PhWorld *world, int entity_index const PhSpellDef *ph_battle_spell(const PhBattle *battle); int ph_spell_battle_usable(const PhSpellDef *spell); float ph_battle_lunge(const PhBattle *battle, PhBattlePhase phase); +PhBattleEvent ph_battle_take_event(PhBattle *battle); #endif diff --git a/src/engine/world.h b/src/engine/world.h @@ -3,7 +3,7 @@ #include <stddef.h> -#define PH_CONFIG_API_VERSION 11 +#define PH_CONFIG_API_VERSION 12 enum { PH_MAP_MAGIC = 0x50484d50, diff --git a/src/game/content.c b/src/game/content.c @@ -1,5 +1,22 @@ #include "game/content.h" +#include "config.h" + +const PhAudioDef ph_audio_defs[PH_AUDIO_COUNT] = { + [PH_MUSIC_MEADOW] = { + "assets/vendor/calm-loop/meadow.wav", PH_MUSIC_GAIN * 0.85f }, + [PH_MUSIC_TOWN] = { + "assets/vendor/peacefull-ville/stonehollow.wav", PH_MUSIC_GAIN }, + [PH_MUSIC_BATTLE] = { + "assets/vendor/battle-theme/battle.wav", PH_MUSIC_GAIN * 0.80f }, + [PH_SOUND_ATTACK] = { + "assets/vendor/rpg-sound-pack/attack.wav", PH_EFFECT_GAIN }, + [PH_SOUND_HIT] = { + "assets/vendor/rpg-sound-pack/hit.wav", PH_EFFECT_GAIN }, + [PH_SOUND_SPELL] = { + "assets/vendor/rpg-sound-pack/spell.wav", PH_EFFECT_GAIN }, +}; + const PhAssetDef ph_asset_defs[PH_ASSET_COUNT] = { [PH_ASSET_TILESET] = { "obj/assets/overworld_tileset_grass.bmp", { 0, 0, 0 } }, [PH_ASSET_HERO] = { "obj/assets/hero.bmp", { 0, 0, 0 } }, @@ -381,3 +398,20 @@ ph_game_interaction_def(int interaction_id) return &ph_interaction_defs[i]; return NULL; } + +int +ph_game_area_music(int area_id) +{ + return area_id == PH_AREA_MEADOW ? PH_MUSIC_MEADOW : PH_MUSIC_TOWN; +} + +int +ph_game_battle_sound(PhBattleEvent event) +{ + switch (event) { + case PH_BATTLE_EVENT_ATTACK: return PH_SOUND_ATTACK; + case PH_BATTLE_EVENT_HIT: return PH_SOUND_HIT; + case PH_BATTLE_EVENT_SPELL: return PH_SOUND_SPELL; + default: return PH_AUDIO_NONE; + } +} diff --git a/src/game/content.h b/src/game/content.h @@ -2,6 +2,7 @@ #define PH_GAME_CONTENT_H #include "engine/area.h" +#include "engine/audio.h" #include "engine/battle.h" #include "engine/render.h" #include "engine/world.h" @@ -34,6 +35,17 @@ typedef struct { } PhInteractionDef; enum { + PH_AUDIO_NONE, + PH_MUSIC_MEADOW, + PH_MUSIC_TOWN, + PH_MUSIC_BATTLE, + PH_SOUND_ATTACK, + PH_SOUND_HIT, + PH_SOUND_SPELL, + PH_AUDIO_COUNT, +}; + +enum { PH_ASSET_NONE, PH_ASSET_TILESET, PH_ASSET_HERO, @@ -97,6 +109,7 @@ enum { }; extern const PhAssetDef ph_asset_defs[PH_ASSET_COUNT]; +extern const PhAudioDef ph_audio_defs[PH_AUDIO_COUNT]; extern const PhAreaDef ph_area_defs[PH_AREA_COUNT]; extern const PhTileDef ph_tile_defs[PH_TILE_DEF_COUNT]; extern const PhEntityDef ph_entity_defs[PH_ENTITY_DEF_COUNT]; @@ -110,5 +123,7 @@ extern const PhInteractionDef ph_interaction_defs[PH_INTERACTION_COUNT]; const PhShopDef *ph_game_shop_def(int shop_id); const PhInteractionDef *ph_game_interaction_def(int interaction_id); +int ph_game_area_music(int area_id); +int ph_game_battle_sound(PhBattleEvent event); #endif diff --git a/src/game/main.c b/src/game/main.c @@ -1,5 +1,6 @@ #include "engine/battle.h" #include "engine/area.h" +#include "engine/audio.h" #include "game/content.h" #include "game/presentation.h" @@ -19,6 +20,13 @@ #else #if PH_USE_SDL +static int +ph_game_music(const PhGameContext *context) +{ + return *context->mode == PH_GAME_WORLD ? + ph_game_area_music(context->world->area_id) : PH_MUSIC_BATTLE; +} + static void ph_update_game(PhGameContext *context, PhInput input, float dt) { @@ -100,6 +108,7 @@ ph_run_game(void) PhGameMode game = PH_GAME_WORLD; PhBattle battle = { 0 }; PhSpellBook spells = ph_player_spellbook; + PhAudio audio = { 0 }; PhGameContext context = { .world = &world, .inventory = &inventory_ui, @@ -111,6 +120,7 @@ ph_run_game(void) }; Uint64 last_ticks; int running = 1; + int audio_ready = 0; PhMenu menu = PH_MENU_NONE; if (!SDL_Init(SDL_INIT_VIDEO)) { @@ -135,6 +145,11 @@ ph_run_game(void) SDL_Quit(); return 1; } + if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) { + fprintf(stderr, "audio disabled: %s\n", SDL_GetError()); + } else if (ph_audio_init(&audio, ph_audio_defs, PH_AUDIO_COUNT) < 0) { + fprintf(stderr, "audio disabled: %s\n", SDL_GetError()); + } else audio_ready = 1; SDL_SetRenderLogicalPresentation(renderer, PH_VIEW_W, PH_VIEW_H, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE); @@ -232,6 +247,18 @@ ph_run_game(void) input = ph_read_input(SDL_GetKeyboardState(NULL)); context.menu = menu; ph_update_game(&context, input, dt); + if (audio_ready) { + int effect = ph_game_battle_sound(ph_battle_take_event(&battle)); + + if (spellbook_ui.spell_used) effect = PH_SOUND_SPELL; + spellbook_ui.spell_used = 0; + ph_audio_music(&audio, ph_game_music(&context)); + if (effect != PH_AUDIO_NONE) ph_audio_effect(&audio, effect); + ph_audio_update(&audio); + } else { + ph_battle_take_event(&battle); + spellbook_ui.spell_used = 0; + } ph_present_frame(renderer, &assets, &context); if (game == PH_GAME_WORLD && menu == PH_MENU_NONE && world.interaction_id > 0) { @@ -241,6 +268,7 @@ ph_run_game(void) SDL_Delay(PH_FRAME_DELAY_MS); } + if (audio_ready) ph_audio_destroy(&audio); ph_render_assets_destroy(&assets); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); diff --git a/src/game/presentation.c b/src/game/presentation.c @@ -503,8 +503,10 @@ ph_spellbook_action(PhSpellbookUi *ui, PhWorld *world, PhSpellBook *spells) amount = ph_spellbook_use(spells, world, world->player_index, spell->id); if (amount < 0) snprintf(ui->status, sizeof(ui->status), "Cannot use %s.", spell->name); - else + else { snprintf(ui->status, sizeof(ui->status), "Restored %d HP.", amount); + ui->spell_used = 1; + } ui->mode = PH_SPELLBOOK_BROWSE; } diff --git a/src/game/presentation.h b/src/game/presentation.h @@ -61,6 +61,7 @@ typedef struct { int selected; int option; PhSpellbookMode mode; + int spell_used; char status[64]; } PhSpellbookUi; diff --git a/src/tests/render.c b/src/tests/render.c @@ -1,5 +1,6 @@ #include "engine/battle.h" #include "engine/area.h" +#include "engine/audio.h" #include "game/content.h" #include "game/presentation.h" @@ -33,6 +34,7 @@ main(void) SDL_Surface *surface; SDL_Renderer *renderer; PhRenderAssets assets; + PhAudio audio = { 0 }; PhWorld world; PhInventoryUi inventory_ui; PhSpellbookUi spellbook_ui; @@ -57,7 +59,7 @@ main(void) int first_item; int second_item; - if (!SDL_Init(SDL_INIT_VIDEO)) { + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) { fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError()); return 1; } @@ -103,6 +105,22 @@ main(void) SDL_Quit(); return 1; } + if (ph_audio_init(&audio, ph_audio_defs, PH_AUDIO_COUNT) < 0) { + fprintf(stderr, "audio smoke test failed: %s\n", SDL_GetError()); + ph_render_assets_destroy(&assets); + ph_area_free(&world.area); + SDL_DestroyRenderer(renderer); + SDL_DestroySurface(surface); + SDL_Quit(); + return 1; + } + ph_audio_music(&audio, PH_MUSIC_MEADOW); + ph_audio_update(&audio); + ph_audio_effect(&audio, PH_SOUND_ATTACK); + ph_audio_effect(&audio, PH_SOUND_HIT); + ph_audio_effect(&audio, PH_SOUND_SPELL); + ph_audio_music(&audio, PH_MUSIC_TOWN); + ph_audio_music(&audio, PH_MUSIC_BATTLE); ph_world_tick(&world, (PhInput){ .interact = 1 }, 0.0f); if (ph_world_item_amount(&world, first_item) != 0) { @@ -220,8 +238,14 @@ main(void) ph_present_frame(renderer, &assets, &context); game = PH_GAME_BATTLE; battle.phase = PH_BATTLE_ENEMY_ACTION; + battle.timer = PH_BATTLE_ACTION_SECONDS; + battle.resolved = 0; + ph_battle_update(&battle, &world, PH_BATTLE_ACTION_SECONDS * 0.5f, 0); + if (ph_battle_take_event(&battle) != PH_BATTLE_EVENT_HIT) { + fprintf(stderr, "render smoke test failed: enemy sound event\n"); + result = 1; + } battle.timer = 0.0f; - battle.resolved = 1; world.entities[world.player_index].vitals.hp = 0; context.menu = PH_MENU_NONE; battle_result = ph_battle_update(&battle, &world, 0.0f, 0); @@ -249,6 +273,10 @@ main(void) ph_present_frame(renderer, &assets, &context); ph_battle_confirm_target(&battle, &world); ph_battle_update(&battle, &world, PH_BATTLE_ACTION_SECONDS * 0.5f, 0); + if (ph_battle_take_event(&battle) != PH_BATTLE_EVENT_SPELL) { + fprintf(stderr, "render smoke test failed: healing sound event\n"); + result = 1; + } if (world.entities[enemy_index].vitals.hp != ph_world_entity_stats(&world, &world.entities[enemy_index]).max_hp || ph_world_player(&world)->vitals.mp != 40) { @@ -293,6 +321,10 @@ main(void) ph_battle_confirm_target(&battle, &world); context.menu = PH_MENU_NONE; ph_battle_update(&battle, &world, PH_BATTLE_ACTION_SECONDS * 0.5f, 0); + if (ph_battle_take_event(&battle) != PH_BATTLE_EVENT_SPELL) { + fprintf(stderr, "render smoke test failed: spell sound event\n"); + result = 1; + } ph_present_frame(renderer, &assets, &context); context.menu = PH_MENU_NONE; ph_battle_update(&battle, &world, PH_BATTLE_ACTION_SECONDS * 0.5f, 0); @@ -310,6 +342,10 @@ main(void) ph_battle_confirm_target(&battle, &world); context.menu = PH_MENU_NONE; ph_battle_update(&battle, &world, PH_BATTLE_ACTION_SECONDS * 0.5f, 0); + if (ph_battle_take_event(&battle) != PH_BATTLE_EVENT_ATTACK) { + fprintf(stderr, "render smoke test failed: attack sound event\n"); + result = 1; + } ph_present_frame(renderer, &assets, &context); context.menu = PH_MENU_NONE; battle_result = ph_battle_update(&battle, &world, @@ -377,6 +413,7 @@ main(void) result = 1; } + ph_audio_destroy(&audio); ph_render_assets_destroy(&assets); ph_area_free(&world.area); SDL_DestroyRenderer(renderer);