Compare commits
6 commits
notashelf/
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b6aa958413 | |||
|
0410db95bb |
|||
|
a315b14dd1 |
|||
|
23dead8c0d |
|||
| d7a2e81f24 | |||
| 1f65d406cd |
18 changed files with 104 additions and 40 deletions
BIN
assets/sounds/block1.wav
Normal file
BIN
assets/sounds/block1.wav
Normal file
Binary file not shown.
BIN
assets/sounds/block2.wav
Normal file
BIN
assets/sounds/block2.wav
Normal file
Binary file not shown.
BIN
assets/sounds/block3.wav
Normal file
BIN
assets/sounds/block3.wav
Normal file
Binary file not shown.
BIN
assets/sounds/crit.wav
Normal file
BIN
assets/sounds/crit.wav
Normal file
Binary file not shown.
BIN
assets/sounds/dodge1.wav
Normal file
BIN
assets/sounds/dodge1.wav
Normal file
Binary file not shown.
BIN
assets/sounds/dodge2.wav
Normal file
BIN
assets/sounds/dodge2.wav
Normal file
Binary file not shown.
BIN
assets/sounds/dodge3.wav
Normal file
BIN
assets/sounds/dodge3.wav
Normal file
Binary file not shown.
BIN
assets/sounds/itempickup.wav
Normal file
BIN
assets/sounds/itempickup.wav
Normal file
Binary file not shown.
BIN
assets/sounds/levelcomplete.wav
Normal file
BIN
assets/sounds/levelcomplete.wav
Normal file
Binary file not shown.
BIN
assets/sounds/sword1.wav
Normal file
BIN
assets/sounds/sword1.wav
Normal file
Binary file not shown.
BIN
assets/sounds/sword2.wav
Normal file
BIN
assets/sounds/sword2.wav
Normal file
Binary file not shown.
BIN
assets/sounds/sword3.wav
Normal file
BIN
assets/sounds/sword3.wav
Normal file
Binary file not shown.
BIN
assets/sounds/uiclick1.wav
Normal file
BIN
assets/sounds/uiclick1.wav
Normal file
Binary file not shown.
BIN
assets/sounds/uiclick2.wav
Normal file
BIN
assets/sounds/uiclick2.wav
Normal file
Binary file not shown.
62
src/audio.c
62
src/audio.c
|
|
@ -1,5 +1,6 @@
|
|||
#include "audio.h"
|
||||
#include "raylib.h"
|
||||
#include "common.h"
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
|
@ -70,50 +71,79 @@ void audio_play_move(void) {
|
|||
play_tone(200.0f, 0.05f, 0.3f);
|
||||
}
|
||||
|
||||
void audio_play_attack(void) {
|
||||
void audio_play_attack(GameState *gs) {
|
||||
// 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
|
||||
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
|
||||
play_tone(300.0f, 0.1f, 0.5f);
|
||||
play_tone(150.0f, 0.15f, 0.4f);
|
||||
}
|
||||
|
||||
void audio_play_player_damage(void) {
|
||||
void audio_play_player_damage(GameState *gs) {
|
||||
// Harsh damage sound
|
||||
play_tone(150.0f, 0.1f, 0.6f);
|
||||
play_tone(100.0f, 0.1f, 0.4f);
|
||||
}
|
||||
|
||||
void audio_play_stairs(void) {
|
||||
void audio_play_stairs(GameState *gs) {
|
||||
// Ascending stairs sound
|
||||
play_tone(400.0f, 0.1f, 0.3f);
|
||||
play_tone(600.0f, 0.1f, 0.3f);
|
||||
play_tone(800.0f, 0.15f, 0.3f);
|
||||
PlaySound(gs->sounds.staircase);
|
||||
}
|
||||
|
||||
void audio_play_dodge(void) {
|
||||
void audio_play_dodge(GameState *gs) {
|
||||
// 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
|
||||
play_tone(250.0f, 0.06f, 0.5f);
|
||||
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
|
||||
play_tone(600.0f, 0.05f, 0.7f);
|
||||
play_tone(900.0f, 0.1f, 0.5f);
|
||||
audio_play_attack(gs);
|
||||
PlaySound(gs->sounds.crit);
|
||||
}
|
||||
|
||||
void audio_play_proc(void) {
|
||||
|
|
|
|||
17
src/audio.h
17
src/audio.h
|
|
@ -1,5 +1,6 @@
|
|||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
#include "common.h"
|
||||
|
||||
// Initialize audio system
|
||||
void audio_init(void);
|
||||
|
|
@ -11,28 +12,28 @@ void audio_close(void);
|
|||
void audio_play_move(void);
|
||||
|
||||
// Play attack sound
|
||||
void audio_play_attack(void);
|
||||
void audio_play_attack(GameState *gs);
|
||||
|
||||
// Play item pickup sound
|
||||
void audio_play_item_pickup(void);
|
||||
void audio_play_item_pickup(GameState *gs);
|
||||
|
||||
// Play enemy death sound
|
||||
void audio_play_enemy_death(void);
|
||||
void audio_play_enemy_death(GameState *gs);
|
||||
|
||||
// Play player damage sound
|
||||
void audio_play_player_damage(void);
|
||||
void audio_play_player_damage(GameState *gs);
|
||||
|
||||
// Play stairs/level change sound
|
||||
void audio_play_stairs(void);
|
||||
void audio_play_stairs(GameState *gs);
|
||||
|
||||
// Play dodge sound
|
||||
void audio_play_dodge(void);
|
||||
void audio_play_dodge(GameState *gs);
|
||||
|
||||
// Play block sound
|
||||
void audio_play_block(void);
|
||||
void audio_play_block(GameState *gs);
|
||||
|
||||
// Play critical hit sound
|
||||
void audio_play_crit(void);
|
||||
void audio_play_crit(GameState *gs);
|
||||
|
||||
// Play status effect proc sound
|
||||
void audio_play_proc(void);
|
||||
|
|
|
|||
12
src/common.h
12
src/common.h
|
|
@ -2,6 +2,7 @@
|
|||
#define COMMON_H
|
||||
|
||||
#include "settings.h"
|
||||
#include <raylib.h>
|
||||
|
||||
// Tile types
|
||||
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
|
||||
} 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
|
||||
typedef struct {
|
||||
Player player;
|
||||
|
|
@ -139,6 +149,8 @@ typedef struct {
|
|||
int screen_shake; // frames of screen shake remaining
|
||||
int shake_x;
|
||||
int shake_y;
|
||||
AudioAssets sounds;
|
||||
} GameState;
|
||||
|
||||
|
||||
#endif // COMMON_H
|
||||
|
|
|
|||
53
src/main.c
53
src/main.c
|
|
@ -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) {
|
||||
switch (effect) {
|
||||
case EFFECT_POISON: return "POISON!";
|
||||
case EFFECT_BLEED: return "BLEED!";
|
||||
case EFFECT_BURN: return "BURN!";
|
||||
case EFFECT_STUN: return "STUN!";
|
||||
case EFFECT_WEAKEN: return "WEAKEN!";
|
||||
default: return "";
|
||||
case EFFECT_POISON:
|
||||
return "POISON!";
|
||||
case EFFECT_BLEED:
|
||||
return "BLEED!";
|
||||
case EFFECT_BURN:
|
||||
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()) {
|
||||
spawn_floating_label(gs, ex, ey, "DODGE", EFFECT_NONE);
|
||||
audio_play_dodge();
|
||||
audio_play_dodge(gs);
|
||||
} else {
|
||||
if (combat_get_last_damage() > 0)
|
||||
spawn_floating_text(gs, ex, ey, combat_get_last_damage(), combat_was_critical());
|
||||
audio_play_attack(gs);
|
||||
if (combat_was_blocked()) {
|
||||
spawn_floating_label(gs, ex, ey - 10, "BLOCK", EFFECT_NONE);
|
||||
audio_play_block();
|
||||
audio_play_block(gs);
|
||||
}
|
||||
if (combat_was_critical()) {
|
||||
spawn_floating_label(gs, ex + 8, ey - 10, "CRIT!", EFFECT_NONE);
|
||||
audio_play_crit();
|
||||
audio_play_crit(gs);
|
||||
}
|
||||
StatusEffectType applied = combat_get_applied_effect();
|
||||
if (applied != EFFECT_NONE) {
|
||||
|
|
@ -200,7 +207,7 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) {
|
|||
}
|
||||
if (!attacked_enemy->alive) {
|
||||
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
|
||||
if (combat_was_player_damage() && combat_get_last_damage() > 0) {
|
||||
audio_play_player_damage();
|
||||
audio_play_player_damage(gs);
|
||||
gs->screen_shake = 8;
|
||||
spawn_floating_text(gs, gs->player.x * TILE_SIZE + 8, gs->player.y * TILE_SIZE, combat_get_last_damage(),
|
||||
combat_was_critical());
|
||||
|
|
@ -342,7 +349,7 @@ static int handle_inventory_input(GameState *gs) {
|
|||
static int handle_descend_input(GameState *gs) {
|
||||
if (IsKeyPressed(KEY_Y)) {
|
||||
if (gs->player.floor < NUM_FLOORS) {
|
||||
audio_play_stairs();
|
||||
audio_play_stairs(gs);
|
||||
init_floor(gs, gs->player.floor + 1);
|
||||
gs->last_message = "Descended to next floor!";
|
||||
gs->message_timer = 60;
|
||||
|
|
@ -381,7 +388,7 @@ static int handle_movement_input(GameState *gs) {
|
|||
add_log(gs, pickup_msg);
|
||||
gs->last_message = "Picked up item!";
|
||||
gs->message_timer = 60;
|
||||
audio_play_item_pickup();
|
||||
audio_play_item_pickup(gs);
|
||||
} else {
|
||||
gs->last_message = "Inventory full!";
|
||||
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)) {
|
||||
gs->last_message = "Used potion!";
|
||||
gs->message_timer = 60;
|
||||
audio_play_item_pickup();
|
||||
audio_play_item_pickup(gs);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -461,11 +468,24 @@ static int handle_input(GameState *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
|
||||
static void game_loop(void) {
|
||||
GameState gs;
|
||||
memset(&gs, 0, sizeof(GameState));
|
||||
|
||||
load_audio_assets(&gs);
|
||||
// Initialize first floor
|
||||
rng_seed(12345);
|
||||
init_floor(&gs, 1);
|
||||
|
|
@ -553,7 +573,8 @@ static void game_loop(void) {
|
|||
int main(void) {
|
||||
// Initialize audio
|
||||
audio_init();
|
||||
|
||||
// Initialize random number generator
|
||||
SetRandomSeed(88435);
|
||||
// Initialize window
|
||||
InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT + 60, "Roguelike");
|
||||
SetTargetFPS(60);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue