Compare commits

..

6 commits

Author SHA1 Message Date
raf
b6aa958413 Merge pull request 'audio: refactor audio asset loading, add crit sound' (#9) from amr/rogged:audio into main
Reviewed-on: #9
Reviewed-by: raf <raf@notashelf.dev>
2026-04-07 17:56:35 +00:00
0410db95bb
audio: remove duplicate sound 2026-04-07 13:54:29 -04:00
a315b14dd1
audio: refactor audio asset loading, add crit sound 2026-04-06 17:51:07 -04:00
23dead8c0d
audio: add item pickup sound 2026-04-06 14:03:39 -04:00
d7a2e81f24 audio: add various sound assets (#7)
Reviewed-on: #7
Co-authored-by: A.M. Rowsell <amr@frzn.dev>
Co-committed-by: A.M. Rowsell <amr@frzn.dev>
2026-04-06 15:12:08 +00:00
raf
1f65d406cd Merge pull request 'docs: add a README' (#6) from notashelf/push-nrzuupynktom into main
Reviewed-on: #6
2026-04-05 21:15:01 +00:00
18 changed files with 104 additions and 40 deletions

BIN
assets/sounds/block1.wav Normal file

Binary file not shown.

BIN
assets/sounds/block2.wav Normal file

Binary file not shown.

BIN
assets/sounds/block3.wav Normal file

Binary file not shown.

BIN
assets/sounds/crit.wav Normal file

Binary file not shown.

BIN
assets/sounds/dodge1.wav Normal file

Binary file not shown.

BIN
assets/sounds/dodge2.wav Normal file

Binary file not shown.

BIN
assets/sounds/dodge3.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/sword1.wav Normal file

Binary file not shown.

BIN
assets/sounds/sword2.wav Normal file

Binary file not shown.

BIN
assets/sounds/sword3.wav Normal file

Binary file not shown.

BIN
assets/sounds/uiclick1.wav Normal file

Binary file not shown.

BIN
assets/sounds/uiclick2.wav Normal file

Binary file not shown.

View file

@ -1,5 +1,6 @@
#include "audio.h" #include "audio.h"
#include "raylib.h" #include "raylib.h"
#include "common.h"
#include <math.h> #include <math.h>
#include <stddef.h> #include <stddef.h>
@ -70,50 +71,79 @@ void audio_play_move(void) {
play_tone(200.0f, 0.05f, 0.3f); play_tone(200.0f, 0.05f, 0.3f);
} }
void audio_play_attack(void) { void audio_play_attack(GameState *gs) {
// Mid-range hit sound // Mid-range hit sound
play_tone(400.0f, 0.1f, 0.5f); // play_tone(400.0f, 0.1f, 0.5f);
int choice = GetRandomValue(1, 3);
switch (choice) {
case 1:
PlaySound(gs->sounds.attack1);
break;
case 2:
PlaySound(gs->sounds.attack2);
break;
case 3:
PlaySound(gs->sounds.attack3);
break;
default:
PlaySound(gs->sounds.attack1);
break;
}
} }
void audio_play_item_pickup(void) { void audio_play_item_pickup(GameState *gs) {
// High-pitched pickup sound // High-pitched pickup sound
play_tone(800.0f, 0.15f, 0.4f); PlaySound(gs->sounds.pickup);
} }
void audio_play_enemy_death(void) { void audio_play_enemy_death(GameState *gs) {
// Descending death sound // Descending death sound
play_tone(300.0f, 0.1f, 0.5f); play_tone(300.0f, 0.1f, 0.5f);
play_tone(150.0f, 0.15f, 0.4f); play_tone(150.0f, 0.15f, 0.4f);
} }
void audio_play_player_damage(void) { void audio_play_player_damage(GameState *gs) {
// Harsh damage sound // Harsh damage sound
play_tone(150.0f, 0.1f, 0.6f); play_tone(150.0f, 0.1f, 0.6f);
play_tone(100.0f, 0.1f, 0.4f); play_tone(100.0f, 0.1f, 0.4f);
} }
void audio_play_stairs(void) { void audio_play_stairs(GameState *gs) {
// Ascending stairs sound // Ascending stairs sound
play_tone(400.0f, 0.1f, 0.3f); PlaySound(gs->sounds.staircase);
play_tone(600.0f, 0.1f, 0.3f);
play_tone(800.0f, 0.15f, 0.3f);
} }
void audio_play_dodge(void) { void audio_play_dodge(GameState *gs) {
// High-pitched whoosh // High-pitched whoosh
play_tone(900.0f, 0.08f, 0.3f); // play_tone(900.0f, 0.08f, 0.3f);
int choice = GetRandomValue(1, 3);
switch (choice) {
case 1:
PlaySound(gs->sounds.dodge1);
break;
case 2:
PlaySound(gs->sounds.dodge2);
break;
case 3:
PlaySound(gs->sounds.dodge3);
break;
default:
PlaySound(gs->sounds.dodge1);
break;
}
return;
} }
void audio_play_block(void) { void audio_play_block(GameState *gs) {
// Low-then-mid metallic clang // Low-then-mid metallic clang
play_tone(250.0f, 0.06f, 0.5f); play_tone(250.0f, 0.06f, 0.5f);
play_tone(350.0f, 0.04f, 0.3f); play_tone(350.0f, 0.04f, 0.3f);
} }
void audio_play_crit(void) { void audio_play_crit(GameState *gs) {
// Sharp crack with high-pitched follow // Sharp crack with high-pitched follow
play_tone(600.0f, 0.05f, 0.7f); audio_play_attack(gs);
play_tone(900.0f, 0.1f, 0.5f); PlaySound(gs->sounds.crit);
} }
void audio_play_proc(void) { void audio_play_proc(void) {

View file

@ -1,5 +1,6 @@
#ifndef AUDIO_H #ifndef AUDIO_H
#define AUDIO_H #define AUDIO_H
#include "common.h"
// Initialize audio system // Initialize audio system
void audio_init(void); void audio_init(void);
@ -11,28 +12,28 @@ void audio_close(void);
void audio_play_move(void); void audio_play_move(void);
// Play attack sound // Play attack sound
void audio_play_attack(void); void audio_play_attack(GameState *gs);
// Play item pickup sound // Play item pickup sound
void audio_play_item_pickup(void); void audio_play_item_pickup(GameState *gs);
// Play enemy death sound // Play enemy death sound
void audio_play_enemy_death(void); void audio_play_enemy_death(GameState *gs);
// Play player damage sound // Play player damage sound
void audio_play_player_damage(void); void audio_play_player_damage(GameState *gs);
// Play stairs/level change sound // Play stairs/level change sound
void audio_play_stairs(void); void audio_play_stairs(GameState *gs);
// Play dodge sound // Play dodge sound
void audio_play_dodge(void); void audio_play_dodge(GameState *gs);
// Play block sound // Play block sound
void audio_play_block(void); void audio_play_block(GameState *gs);
// Play critical hit sound // Play critical hit sound
void audio_play_crit(void); void audio_play_crit(GameState *gs);
// Play status effect proc sound // Play status effect proc sound
void audio_play_proc(void); void audio_play_proc(void);

View file

@ -2,6 +2,7 @@
#define COMMON_H #define COMMON_H
#include "settings.h" #include "settings.h"
#include <raylib.h>
// Tile types // Tile types
typedef enum { TILE_WALL, TILE_FLOOR, TILE_STAIRS } TileType; typedef enum { TILE_WALL, TILE_FLOOR, TILE_STAIRS } TileType;
@ -112,6 +113,15 @@ typedef struct {
StatusEffectType effect_type; // used to pick color for proc labels StatusEffectType effect_type; // used to pick color for proc labels
} FloatingText; } FloatingText;
// AudioAssets
typedef struct {
Sound attack1, attack2, attack3;
Sound pickup;
Sound staircase;
Sound dodge1, dodge2, dodge3;
Sound crit;
} AudioAssets;
// GameState - encapsulates all game state for testability and save/load // GameState - encapsulates all game state for testability and save/load
typedef struct { typedef struct {
Player player; Player player;
@ -139,6 +149,8 @@ typedef struct {
int screen_shake; // frames of screen shake remaining int screen_shake; // frames of screen shake remaining
int shake_x; int shake_x;
int shake_y; int shake_y;
AudioAssets sounds;
} GameState; } GameState;
#endif // COMMON_H #endif // COMMON_H

View file

@ -65,12 +65,18 @@ static void spawn_floating_label(GameState *gs, int x, int y, const char *label,
static const char *proc_label_for(StatusEffectType effect) { static const char *proc_label_for(StatusEffectType effect) {
switch (effect) { switch (effect) {
case EFFECT_POISON: return "POISON!"; case EFFECT_POISON:
case EFFECT_BLEED: return "BLEED!"; return "POISON!";
case EFFECT_BURN: return "BURN!"; case EFFECT_BLEED:
case EFFECT_STUN: return "STUN!"; return "BLEED!";
case EFFECT_WEAKEN: return "WEAKEN!"; case EFFECT_BURN:
default: return ""; return "BURN!";
case EFFECT_STUN:
return "STUN!";
case EFFECT_WEAKEN:
return "WEAKEN!";
default:
return "";
} }
} }
@ -181,17 +187,18 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) {
if (combat_was_dodged()) { if (combat_was_dodged()) {
spawn_floating_label(gs, ex, ey, "DODGE", EFFECT_NONE); spawn_floating_label(gs, ex, ey, "DODGE", EFFECT_NONE);
audio_play_dodge(); audio_play_dodge(gs);
} else { } else {
if (combat_get_last_damage() > 0) if (combat_get_last_damage() > 0)
spawn_floating_text(gs, ex, ey, combat_get_last_damage(), combat_was_critical()); spawn_floating_text(gs, ex, ey, combat_get_last_damage(), combat_was_critical());
audio_play_attack(gs);
if (combat_was_blocked()) { if (combat_was_blocked()) {
spawn_floating_label(gs, ex, ey - 10, "BLOCK", EFFECT_NONE); spawn_floating_label(gs, ex, ey - 10, "BLOCK", EFFECT_NONE);
audio_play_block(); audio_play_block(gs);
} }
if (combat_was_critical()) { if (combat_was_critical()) {
spawn_floating_label(gs, ex + 8, ey - 10, "CRIT!", EFFECT_NONE); spawn_floating_label(gs, ex + 8, ey - 10, "CRIT!", EFFECT_NONE);
audio_play_crit(); audio_play_crit(gs);
} }
StatusEffectType applied = combat_get_applied_effect(); StatusEffectType applied = combat_get_applied_effect();
if (applied != EFFECT_NONE) { if (applied != EFFECT_NONE) {
@ -200,7 +207,7 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) {
} }
if (!attacked_enemy->alive) { if (!attacked_enemy->alive) {
spawn_floating_label(gs, ex, ey - 20, "SLAIN", EFFECT_NONE); spawn_floating_label(gs, ex, ey - 20, "SLAIN", EFFECT_NONE);
audio_play_enemy_death(); audio_play_enemy_death(gs);
} }
} }
} }
@ -210,7 +217,7 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) {
// Check if player took damage // Check if player took damage
if (combat_was_player_damage() && combat_get_last_damage() > 0) { if (combat_was_player_damage() && combat_get_last_damage() > 0) {
audio_play_player_damage(); audio_play_player_damage(gs);
gs->screen_shake = 8; gs->screen_shake = 8;
spawn_floating_text(gs, gs->player.x * TILE_SIZE + 8, gs->player.y * TILE_SIZE, combat_get_last_damage(), spawn_floating_text(gs, gs->player.x * TILE_SIZE + 8, gs->player.y * TILE_SIZE, combat_get_last_damage(),
combat_was_critical()); combat_was_critical());
@ -342,7 +349,7 @@ static int handle_inventory_input(GameState *gs) {
static int handle_descend_input(GameState *gs) { static int handle_descend_input(GameState *gs) {
if (IsKeyPressed(KEY_Y)) { if (IsKeyPressed(KEY_Y)) {
if (gs->player.floor < NUM_FLOORS) { if (gs->player.floor < NUM_FLOORS) {
audio_play_stairs(); audio_play_stairs(gs);
init_floor(gs, gs->player.floor + 1); init_floor(gs, gs->player.floor + 1);
gs->last_message = "Descended to next floor!"; gs->last_message = "Descended to next floor!";
gs->message_timer = 60; gs->message_timer = 60;
@ -381,7 +388,7 @@ static int handle_movement_input(GameState *gs) {
add_log(gs, pickup_msg); add_log(gs, pickup_msg);
gs->last_message = "Picked up item!"; gs->last_message = "Picked up item!";
gs->message_timer = 60; gs->message_timer = 60;
audio_play_item_pickup(); audio_play_item_pickup(gs);
} else { } else {
gs->last_message = "Inventory full!"; gs->last_message = "Inventory full!";
gs->message_timer = 60; gs->message_timer = 60;
@ -395,7 +402,7 @@ static int handle_movement_input(GameState *gs) {
if (gs->player.inventory_count > 0 && player_use_first_item(&gs->player)) { if (gs->player.inventory_count > 0 && player_use_first_item(&gs->player)) {
gs->last_message = "Used potion!"; gs->last_message = "Used potion!";
gs->message_timer = 60; gs->message_timer = 60;
audio_play_item_pickup(); audio_play_item_pickup(gs);
return 1; return 1;
} }
} }
@ -461,11 +468,24 @@ static int handle_input(GameState *gs) {
return handle_movement_input(gs); return handle_movement_input(gs);
} }
void load_audio_assets(GameState *gs) {
gs->sounds.attack1 = LoadSound("./assets/sounds/sword1.wav");
gs->sounds.attack2 = LoadSound("./assets/sounds/sword2.wav");
gs->sounds.attack3 = LoadSound("./assets/sounds/sword3.wav");
gs->sounds.pickup = LoadSound("./assets/sounds/itempickup.wav");
gs->sounds.staircase = LoadSound("./assets/sounds/levelcomplete.wav");
gs->sounds.dodge1 = LoadSound("./assets/sounds/dodge1.wav");
gs->sounds.dodge2 = LoadSound("./assets/sounds/dodge2.wav");
gs->sounds.dodge3 = LoadSound("./assets/sounds/dodge3.wav");
gs->sounds.crit = LoadSound("./assets/sounds/crit.wav");
return;
}
// Main game loop // Main game loop
static void game_loop(void) { static void game_loop(void) {
GameState gs; GameState gs;
memset(&gs, 0, sizeof(GameState)); memset(&gs, 0, sizeof(GameState));
load_audio_assets(&gs);
// Initialize first floor // Initialize first floor
rng_seed(12345); rng_seed(12345);
init_floor(&gs, 1); init_floor(&gs, 1);
@ -553,7 +573,8 @@ static void game_loop(void) {
int main(void) { int main(void) {
// Initialize audio // Initialize audio
audio_init(); audio_init();
// Initialize random number generator
SetRandomSeed(88435);
// Initialize window // Initialize window
InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT + 60, "Roguelike"); InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT + 60, "Roguelike");
SetTargetFPS(60); SetTargetFPS(60);