diff --git a/src/defines.h b/src/defines.h index b5f938a..9a600b9 100644 --- a/src/defines.h +++ b/src/defines.h @@ -60,6 +60,10 @@ #define UNUSED(x) (void)(x) +#define UNPACK_COLOR(color) color.r, color.g, color.b, color.a +#define C_WHITE (SDL_Color) { 255, 255, 255, 255 } +#define C_BLACK (SDL_Color) { 0, 0, 0, 255 } + typedef enum Direction_t { UP, DOWN, LEFT, RIGHT } Direction; diff --git a/src/main.c b/src/main.c index 0a05ffe..411d063 100644 --- a/src/main.c +++ b/src/main.c @@ -459,6 +459,7 @@ static void run_game(void) { static UpdateData updateData; + static unsigned int playerLevel = 1; map_clear_dead_monsters(gMap, gPlayer); map_clear_collected_items(gMap); @@ -469,6 +470,10 @@ run_game(void) roommatrix_build_lightmap(gRoomMatrix); populateUpdateData(&updateData, deltaTime); + if (playerLevel != gPlayer->stats.lvl) { + playerLevel = gPlayer->stats.lvl; + skillbar_check_skill_activation(gSkillBar, gPlayer); + } gui_update_player_stats(gGui, gPlayer, gMap, gRenderer); particle_engine_update(deltaTime); player_update(&updateData); @@ -490,7 +495,7 @@ run_game(void) SDL_RenderSetViewport(gRenderer, &gameViewport); map_render(gMap, &gCamera); - particle_engine_render(&gCamera); + particle_engine_render_game(&gCamera); if (!is_player_dead()) player_render(gPlayer, &gCamera); @@ -513,6 +518,7 @@ run_game(void) BOTTOM_GUI_HEIGHT, &gCamera); SDL_RenderSetViewport(gRenderer, NULL); + particle_engine_render_global(&gCamera); if (gGameState == IN_GAME_MENU) { SDL_Rect dimmer = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT }; SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 150); diff --git a/src/particle_engine.c b/src/particle_engine.c index db51487..eb414cb 100644 --- a/src/particle_engine.c +++ b/src/particle_engine.c @@ -32,11 +32,13 @@ typedef struct Particle_t { unsigned int movetime; unsigned int lifetime; bool fixed; + Uint8 blend_mode; SDL_Color color; } Particle; typedef struct Engine_t { - LinkedList *particles; + LinkedList *global_particles; + LinkedList *game_particles; } Engine; static Engine *engine = NULL; @@ -51,6 +53,7 @@ create_particle(void) p->movetime = 100; p->lifetime = 100; p->fixed = false; + p->blend_mode = SDL_BLENDMODE_MOD; p->color = (SDL_Color) { 255, 255, 255, 255 }; return p; } @@ -69,7 +72,8 @@ particle_engine_init(void) fatal("Engine already initiated"); engine = ec_malloc(sizeof(Engine)); - engine->particles = linkedlist_create(); + engine->game_particles = linkedlist_create(); + engine->global_particles = linkedlist_create(); } void @@ -104,7 +108,7 @@ particle_engine_bloodspray(Position pos, Dimension dim, unsigned int count) p->lifetime = lt; p->dim = (Dimension) { w, h }; p->color = (SDL_Color) { 255, 0, 0, 255 }; - linkedlist_append(&engine->particles, p); + linkedlist_append(&engine->game_particles, p); } } @@ -140,7 +144,7 @@ create_explosion(Position pos, Dimension dim, unsigned int c_count, ...) p->lifetime = lt; p->dim = (Dimension) { 2, 2 }; p->color = colors[get_random((unsigned int) c_count-1)]; - linkedlist_append(&engine->particles, p); + linkedlist_append(&engine->game_particles, p); } free(colors); } @@ -193,7 +197,38 @@ particle_engine_speed_lines(Position pos, Dimension dim, bool horizontal) else p->dim = (Dimension) { 2, 20 }; p->color = color; - linkedlist_append(&engine->particles, p); + linkedlist_append(&engine->game_particles, p); + } +} + +void +particle_engine_sparkle(Position pos, Dimension dim) +{ + for (unsigned int i = 0; i < 10; ++i) { + int x, y, yv, alpha; + unsigned int lt; + Particle *p; + + x = get_random(dim.width) + pos.x; + y = get_random(dim.height) + pos.y; + + alpha = get_random(155) + 100; + + yv = (get_random(100) + 100) * -1; + + lt = get_random(20); + + p = create_particle(); + p->pos = (Position) { x, y }; + p->velocity = (Vector2d) { (float) 0, (float) yv }; + p->movetime = lt; + p->lifetime = lt; + p->blend_mode = SDL_BLENDMODE_BLEND; + p->dim = (Dimension) { 2, 2 }; + p->color = C_WHITE; + p->color.a = alpha; + p->fixed = true; + linkedlist_append(&engine->global_particles, p); } } @@ -233,7 +268,7 @@ particle_engine_wind(Vector2d direction) p->dim = (Dimension) { w, h }; p->color = color; p->fixed = true; - linkedlist_append(&engine->particles, p); + linkedlist_append(&engine->game_particles, p); } } @@ -247,40 +282,34 @@ move_particle(Particle *particle, float deltaTime) particle->pos.y += (int) (particle->velocity.y * deltaTime); } -void -particle_engine_update(float deltaTime) +static void +update_particles(LinkedList **particles, float deltaTime) { - check_engine(); - LinkedList *current, *last; - - current = engine->particles; - last = NULL; - - while (current) { - Particle *particle = current->data; - + LinkedList *cleared = linkedlist_create(); + while (*particles) { + Particle *particle = linkedlist_pop(particles); if (particle->movetime) particle->movetime--; if (particle->lifetime > 0) { particle->lifetime--; - move_particle(current->data, deltaTime); - last = current; - current = current->next; + move_particle(particle, deltaTime); + linkedlist_push(&cleared, particle); } else { - if (!last) { - engine->particles = current->next; - free(current->data); - free(current); - current = engine->particles; - } else { - last->next = current->next; - free(current->data); - free(current); - current = last->next; - } + free(particle); } } + + *particles = cleared; +} + +void +particle_engine_update(float deltaTime) +{ + check_engine(); + + update_particles(&engine->global_particles, deltaTime); + update_particles(&engine->game_particles, deltaTime); } static void @@ -292,8 +321,7 @@ render_particle(Particle *p, Camera *cam) else pos = camera_to_camera_position(cam, &p->pos); - // Make the particles look visible on all surfaces - SDL_SetRenderDrawBlendMode(cam->renderer, SDL_BLENDMODE_MOD); + SDL_SetRenderDrawBlendMode(cam->renderer, p->blend_mode); SDL_Rect box = { pos.x, pos.y, p->dim.width, p->dim.height }; SDL_SetRenderDrawColor(cam->renderer, @@ -307,24 +335,37 @@ render_particle(Particle *p, Camera *cam) SDL_SetRenderDrawBlendMode(cam->renderer, SDL_BLENDMODE_BLEND); } -void -particle_engine_render(Camera *cam) +static void +render_particles(LinkedList *particles, Camera *cam) { check_engine(); - LinkedList *particles = engine->particles; - while (particles) { - render_particle(particles->data, cam); - particles = particles->next; + LinkedList *render_list = particles; + + while (render_list) { + render_particle(render_list->data, cam); + render_list = render_list->next; } } +void +particle_engine_render_game(Camera *cam) +{ + render_particles(engine->game_particles, cam); +} + +void +particle_engine_render_global(Camera *cam) +{ + render_particles(engine->global_particles, cam); +} + void particle_engine_clear(void) { check_engine(); - while (engine->particles) - free(linkedlist_pop(&engine->particles)); + while (engine->game_particles) + free(linkedlist_pop(&engine->game_particles)); } void @@ -332,8 +373,8 @@ particle_engine_close(void) { check_engine(); - while (engine->particles) - free(linkedlist_pop(&engine->particles)); + while (engine->game_particles) + free(linkedlist_pop(&engine->game_particles)); free(engine); engine = NULL; diff --git a/src/particle_engine.h b/src/particle_engine.h index 07facef..09c8f28 100644 --- a/src/particle_engine.h +++ b/src/particle_engine.h @@ -40,6 +40,9 @@ particle_engine_eldritch_explosion(Position, Dimension); void particle_engine_speed_lines(Position, Dimension, bool horizontal); +void +particle_engine_sparkle(Position, Dimension); + void particle_engine_wind(Vector2d direction); @@ -47,7 +50,10 @@ void particle_engine_update(float deltatime); void -particle_engine_render(Camera*); +particle_engine_render_game(Camera*); + +void +particle_engine_render_global(Camera*); void particle_engine_clear(void); diff --git a/src/player.c b/src/player.c index e279026..c642681 100644 --- a/src/player.c +++ b/src/player.c @@ -271,8 +271,10 @@ check_skill_activation(Player *player, RoomMatrix *matrix, SDL_Event *event) continue; Skill *skill = player->skills[i]; + if (skill->levelcap > player->stats.lvl) + continue; if (skill->available && !skill->available(player)) - continue; + continue; skill->active = (selected - 1) == i && !skill->active && skill->resetCountdown == 0; if (skill->active && skill->instantUse) { SkillData skillData = { player, matrix, VECTOR2D_NODIR }; diff --git a/src/pointer.c b/src/pointer.c index 4ce7443..97b9e72 100644 --- a/src/pointer.c +++ b/src/pointer.c @@ -49,7 +49,7 @@ pointer_handle_event(Pointer *p, SDL_Event *event) #ifdef DEBUG if (event->type == SDL_MOUSEBUTTONDOWN) { Dimension dim = { 10, 10 }; - particle_engine_eldritch_explosion(p->sprite->pos, dim); + particle_engine_sparkle(p->sprite->pos, dim); } #endif // DEBUG } diff --git a/src/projectile.c b/src/projectile.c index 6810db7..043fcff 100644 --- a/src/projectile.c +++ b/src/projectile.c @@ -90,13 +90,15 @@ projectile_update(Projectile *p, UpdateData *data) return; if (space->monster) { - Uint32 dmg = stats_fight(&data->player->stats, &space->monster->stats); + Stats tmpStats = data->player->stats; + tmpStats.dmg *= 2; + Uint32 dmg = stats_fight(&tmpStats, &space->monster->stats); if (dmg > 0) { gui_log("Your dagger pierced %s for %u damage", space->monster->lclabel, dmg); mixer_play_effect(SWORD_HIT); data->player->hits += 1; } - if (get_random(2) == 0) { + if (get_random(2) >= 1) { Item *item = item_builder_build_item(DAGGER, 1); item->sprite->pos = space->monster->sprite->pos; linkedlist_append(&data->map->items, item); diff --git a/src/skill.c b/src/skill.c index ae24753..da35aec 100644 --- a/src/skill.c +++ b/src/skill.c @@ -59,6 +59,7 @@ create_default(const char *s_label, Sprite *s) skill->active = false; skill->available = NULL; skill->use = NULL; + skill->levelcap = 1; return skill; } @@ -120,6 +121,7 @@ create_flurry(void) s->clip = CLIP32(0, 0); s->fixed = true; Skill *skill = create_default("Flurry", s); + skill->levelcap = 2; skill->use = skill_use_flurry; return skill; } @@ -169,6 +171,7 @@ create_throw_dagger(void) s->clip = CLIP32(64, 0); s->fixed = true; Skill *skill = create_default("Throw dagger", s); + skill->levelcap = 1; skill->instantUse = false; skill->resetTime = 1; skill->available = skill_throw_dagger_available; @@ -201,6 +204,7 @@ create_sip_health(void) s->clip = CLIP16(0, 0); s->fixed = true; Skill *skill = create_default("Sip health", s); + skill->levelcap = 1; skill->instantUse = true; skill->available = skill_sip_health_available; skill->use = skill_sip_health; @@ -297,6 +301,7 @@ create_charge(void) s->clip = CLIP32(32, 0); s->fixed = true; Skill *skill = create_default("Charge", s); + skill->levelcap = 4; skill->use = skill_charge; return skill; } diff --git a/src/skill.h b/src/skill.h index 982aef4..b5421a0 100644 --- a/src/skill.h +++ b/src/skill.h @@ -45,6 +45,7 @@ typedef struct Skill_t { Sprite *icon; unsigned int resetTime; unsigned int resetCountdown; + unsigned int levelcap; bool actionRequired; bool instantUse; bool active; diff --git a/src/skillbar.c b/src/skillbar.c index c63f6fa..f455128 100644 --- a/src/skillbar.c +++ b/src/skillbar.c @@ -24,6 +24,7 @@ #include "sprite.h" #include "keyboard.h" #include "texturecache.h" +#include "particle_engine.h" static void load_texture(SkillBar *bar, const char *path, SDL_Renderer *renderer) @@ -66,12 +67,27 @@ skillbar_create(SDL_Renderer *renderer) SkillBar *bar = ec_malloc(sizeof(SkillBar)); bar->sprites = linkedlist_create(); bar->activationTimer = timer_create(); + bar->skillSparkleTimer = timer_create(); bar->lastActivation = 0; load_texture(bar, "GUI/GUI0.png", renderer); load_countdown_sprites(bar); return bar; } +void +skillbar_check_skill_activation(SkillBar *bar, Player *player) +{ + for (int i = 0; i < PLAYER_SKILL_COUNT; ++i) { + if (!player->skills[i]) + continue; + + if (player->skills[i]->levelcap != player->stats.lvl) + continue; + + timer_start(bar->skillSparkleTimer); + } +} + static void render_frame(Camera *cam) { @@ -169,21 +185,58 @@ render_skill_unavailable(SkillBar *bar, Player *player, Camera *cam) static SDL_Rect unavailableSkillBox = { 0, 0, 32, 32 }; for (int i = 0; i < PLAYER_SKILL_COUNT; ++i) { + bool unavailable = false; + SDL_Color color; + if (!player->skills[i]) continue; Skill *skill = player->skills[i]; - if (skill->resetCountdown || (skill->available && !skill->available(player))) { + if (skill->levelcap > player->stats.lvl) { + unavailable = true; + color = (SDL_Color) { 0, 0, 0, 220 }; + } else if (skill->resetCountdown + || (skill->available && !skill->available(player))) + { + unavailable = true; + color = (SDL_Color) { 255, 0, 0, 70 }; + } + + if (unavailable) { unavailableSkillBox.x = i * 32; - SDL_SetRenderDrawColor(cam->renderer, 255, 0, 0, 70); + SDL_SetRenderDrawColor(cam->renderer, UNPACK_COLOR(color)); SDL_RenderFillRect(cam->renderer, &unavailableSkillBox); if (skill->resetCountdown) { - render_skill_countdown(bar, i, skill->resetCountdown, cam); + render_skill_countdown(bar, + i, + skill->resetCountdown, + cam); } } } } +static void +render_skill_sparkles(SkillBar *bar, Player *player) +{ + if (timer_get_ticks(bar->skillSparkleTimer) > 1500) { + timer_stop(bar->skillSparkleTimer); + return; + } + + Position pos = { 0, GAME_VIEW_HEIGHT }; + Dimension dim = { 32, 32 }; + for (int i = 0; i < PLAYER_SKILL_COUNT; ++i) { + if (!player->skills[i]) + continue; + else if (player->skills[i]->levelcap != player->stats.lvl) + continue; + + pos.x += 32 * i; + particle_engine_sparkle(pos, dim); + } +} + void skillbar_render(SkillBar *bar, Player *player, Camera *cam) { @@ -192,6 +245,8 @@ skillbar_render(SkillBar *bar, Player *player, Camera *cam) render_sprites(bar, cam); render_skill_unavailable(bar, player, cam); render_activation_indicator(bar, cam); + if (timer_started(bar->skillSparkleTimer)) + render_skill_sparkles(bar, player); } void diff --git a/src/skillbar.h b/src/skillbar.h index 3f8a046..103a9ce 100644 --- a/src/skillbar.h +++ b/src/skillbar.h @@ -29,12 +29,16 @@ typedef struct SkillBar_t { LinkedList *sprites; Sprite *countdowns[PLAYER_SKILL_COUNT]; Timer *activationTimer; + Timer *skillSparkleTimer; unsigned int lastActivation; } SkillBar; SkillBar * skillbar_create(SDL_Renderer*); +void +skillbar_check_skill_activation(SkillBar*, Player*); + void skillbar_render(SkillBar*, Player*, Camera*);