diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ec985e..c133be0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,7 @@ add_executable(breakhack src/db src/settings src/actiontextbuilder + src/animation ) # Sqlite has some warnings that I we don't need to see diff --git a/assets/Extras/sword_swing.png b/assets/Extras/SwordSwing.png similarity index 100% rename from assets/Extras/sword_swing.png rename to assets/Extras/SwordSwing.png diff --git a/src/animation.c b/src/animation.c new file mode 100644 index 0000000..cd63542 --- /dev/null +++ b/src/animation.c @@ -0,0 +1,117 @@ +/* + * BreakHack - A dungeone crawler RPG + * Copyright (C) 2018 Linus Probert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "animation.h" +#include "timer.h" +#include "camera.h" +#include "sprite.h" +#include "util.h" + +Animation * +animation_create(unsigned int clipCount) +{ + Animation *animation = ec_malloc(sizeof(Animation) + + clipCount * sizeof(AnimationClip)); + animation->clipTimer = timer_create(); + animation->clipCount = clipCount; + animation->currentClip = 0; + animation->loop = true; + animation->running = false; + animation->sprite = sprite_create(); + return animation; +} + +void +animation_load_texture(Animation *animation, const char *path, SDL_Renderer *renderer) +{ + sprite_load_texture(animation->sprite, path, 0, renderer); +} + +void +animation_update(Animation *animation) +{ + if (!animation->running) { + return; + } + + if (!timer_started(animation->clipTimer)) { + timer_start(animation->clipTimer); + } + + if (timer_get_ticks(animation->clipTimer) + > animation->clips[animation->currentClip].renderTime) + { + animation->currentClip++; + if (animation->currentClip >= animation->clipCount) { + animation->currentClip = 0; + if (!animation->loop) { + animation_stop(animation); + return; + } + timer_start(animation->clipTimer); + } + } + + animation->sprite->clip = (SDL_Rect) { + animation->clips[animation->currentClip].x, + animation->clips[animation->currentClip].y, + animation->clips[animation->currentClip].w, + animation->clips[animation->currentClip].h + }; +} + +void +animation_render(Animation *animation, Camera *camera) +{ + if (!animation->running) { + return; + } + + sprite_render(animation->sprite, camera); +} + +void +animation_set_frames(Animation *animation, AnimationClip clips[]) +{ + for (size_t i = 0; i < animation->clipCount; i++) { + animation->clips[i] = clips[i]; + } +} + +void +animation_run(Animation *a) +{ + a->running = true; +} + +void +animation_stop(Animation *a) +{ + a->running = false; + a->currentClip = 0; + timer_stop(a->clipTimer); +} + +void +animation_destroy(Animation *animation) +{ + timer_destroy(animation->clipTimer); + sprite_destroy(animation->sprite); + free(animation); +} + diff --git a/src/animation.h b/src/animation.h new file mode 100644 index 0000000..b6f8031 --- /dev/null +++ b/src/animation.h @@ -0,0 +1,75 @@ +/* + * BreakHack - A dungeone crawler RPG + * Copyright (C) 2018 Linus Probert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _ANIMATION_H +#define _ANIMATION_H + +#include +#include + +typedef struct Timer Timer; +typedef struct Camera Camera; +typedef struct Sprite Sprite; + +typedef struct AnimationClip +{ + unsigned int x; + unsigned int y; + unsigned int w; + unsigned int h; + unsigned int renderTime; +} AnimationClip; + +typedef struct Animation +{ + Sprite *sprite; + Timer *clipTimer; + unsigned int currentClip; + bool loop; + unsigned int clipCount; + bool running; + AnimationClip clips[]; +} Animation; + +Animation* +animation_create(unsigned int clipCount); + +void +animation_load_texture(Animation *, const char *path, SDL_Renderer*); + +void +animation_set_frames(Animation*, AnimationClip clips[]); + +void +animation_run(Animation*); + +void +animation_update(Animation*); + +void +animation_render(Animation*, Camera*); + +void +animation_stop(Animation*); + +void +animation_destroy(Animation*); + +#endif // _ANIMATION_H + + diff --git a/src/camera.h b/src/camera.h index 3dbae66..9fb876b 100644 --- a/src/camera.h +++ b/src/camera.h @@ -25,7 +25,7 @@ #include "timer.h" #include "vector2d.h" -typedef struct { +typedef struct Camera { Position pos; Position basePos; Vector2d velocity; diff --git a/src/main.c b/src/main.c index fafd70a..36e76a4 100644 --- a/src/main.c +++ b/src/main.c @@ -516,6 +516,8 @@ run_game(void) map_render_top_layer(gMap, gCamera); + player_render_toplayer(gPlayer, gCamera); + if (gPlayer->class == MAGE || gPlayer->class == PALADIN) roommatrix_render_mouse_square(gRoomMatrix, gCamera); diff --git a/src/player.c b/src/player.c index b33a1a3..fb490d7 100644 --- a/src/player.c +++ b/src/player.c @@ -33,6 +33,7 @@ #include "texturecache.h" #include "vector2d.h" #include "actiontextbuilder.h" +#include "animation.h" #define ENGINEER_STATS { 12, 12, 5, 7, 2, 2, 1 } #define MAGE_STATS { 12, 12, 5, 7, 1, 2, 1 } @@ -133,6 +134,8 @@ has_collided(Player *player, RoomMatrix *matrix, Vector2d direction) monster_hit(space->monster, hit); + animation_run(player->swordAnimation); + if (hit > 0) { gui_log("You hit %s for %u damage", space->monster->lclabel, hit); @@ -246,6 +249,27 @@ handle_next_move(UpdateData *data) ++step; step = step % 4; } + + if (!vector2d_equals(nextDir, VECTOR2D_NODIR)) + player->swordAnimation->sprite->pos = player->sprite->pos; + + if (vector2d_equals(nextDir, VECTOR2D_UP)) { + player->swordAnimation->sprite->pos.y -= 32; + player->swordAnimation->sprite->angle = -90; + player->swordAnimation->sprite->flip = SDL_FLIP_NONE; + } else if (vector2d_equals(nextDir, VECTOR2D_DOWN)) { + player->swordAnimation->sprite->pos.y += 32; + player->swordAnimation->sprite->angle = 90; + player->swordAnimation->sprite->flip = SDL_FLIP_NONE; + } else if (vector2d_equals(nextDir, VECTOR2D_LEFT)) { + player->swordAnimation->sprite->pos.x -= 32; + player->swordAnimation->sprite->angle = 0; + player->swordAnimation->sprite->flip = SDL_FLIP_HORIZONTAL; + } else if (vector2d_equals(nextDir, VECTOR2D_RIGHT)) { + player->swordAnimation->sprite->pos.x += 32; + player->swordAnimation->sprite->angle = 0; + player->swordAnimation->sprite->flip = SDL_FLIP_NONE; + } } static void @@ -327,6 +351,24 @@ check_skill_trigger(UpdateData *data) return true; } +static void +build_sword_animation(Player *p, SDL_Renderer *renderer) +{ + animation_load_texture(p->swordAnimation, "Extras/SwordSwing.png", renderer); + animation_set_frames(p->swordAnimation, (AnimationClip[]) { + { 0, 0, 16, 16, 20 }, + { 16, 0, 16, 16, 20 }, + { 32, 0, 16, 16, 20 }, + { 48, 0, 16, 16, 20 }, + { 64, 0, 16, 16, 20 } + }); + + p->swordAnimation->loop = false; + p->swordAnimation->sprite->dim = GAME_DIMENSION; + p->swordAnimation->sprite->clip = (SDL_Rect) { 0, 0, 16, 16 }; + p->swordAnimation->sprite->rotationPoint = (SDL_Point) { 16, 16 }; +} + Player* player_create(class_t class, SDL_Renderer *renderer) { @@ -349,6 +391,9 @@ player_create(class_t class, SDL_Renderer *renderer) player->state = ALIVE; player->projectiles = linkedlist_create(); player->animationTimer = timer_create(); + player->swordAnimation = animation_create(5); + + build_sword_animation(player, renderer); for (size_t i = 0; i < PLAYER_SKILL_COUNT; ++i) { player->skills[i] = NULL; @@ -451,6 +496,12 @@ player_render(Player *player, Camera *cam) } } +void +player_render_toplayer(Player *player, Camera *camera) +{ + animation_render(player->swordAnimation, camera); +} + void player_reset_steps(Player *p) { @@ -503,6 +554,8 @@ void player_update(UpdateData *data) linkedlist_destroy(&player->projectiles); player->projectiles = remaining; + + animation_update(player->swordAnimation); } void @@ -511,6 +564,7 @@ player_destroy(Player *player) if (player->sprite) sprite_destroy(player->sprite); + animation_destroy(player->swordAnimation); timer_destroy(player->animationTimer); for (size_t i = 0; i < PLAYER_SKILL_COUNT; ++i) { diff --git a/src/player.h b/src/player.h index 352fc3d..03267ba 100644 --- a/src/player.h +++ b/src/player.h @@ -31,7 +31,8 @@ #define PLAYER_SKILL_COUNT 5 // Foward declare -struct UpdateData; +typedef struct UpdateData UpdateData; +typedef struct Animation Animation; typedef enum PlayerClass { ENGINEER, MAGE, PALADIN, ROGUE, WARRIOR } class_t; typedef enum PlayerState { ALIVE, DEAD, FALLING } state_t; @@ -64,6 +65,7 @@ typedef struct Player_t { state_t state; Skill *skills[PLAYER_SKILL_COUNT]; Timer *animationTimer; + Animation *swordAnimation; } Player; Player* @@ -90,6 +92,9 @@ player_update(struct UpdateData *); void player_render(Player*, Camera*); +void +player_render_toplayer(Player*, Camera*); + void player_destroy(Player*); diff --git a/src/roommatrix.h b/src/roommatrix.h index 1cdbe22..979ed57 100644 --- a/src/roommatrix.h +++ b/src/roommatrix.h @@ -26,7 +26,7 @@ #include "map_room_modifiers.h" #include "input.h" -typedef struct Sprite_t Sprite; +typedef struct Sprite Sprite; typedef struct Map_t Map; typedef struct Monster_t Monster; typedef struct Player_t Player; diff --git a/src/sprite.c b/src/sprite.c index 3ba49f9..e1a4040 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -51,7 +51,7 @@ sprite_create(void) void sprite_load_texture(Sprite *sprite, - char *path, + const char *path, int index, SDL_Renderer *renderer) { @@ -68,7 +68,7 @@ sprite_load_texture(Sprite *sprite, sprite->destroyTextures = true; } -void sprite_load_text_texture(Sprite *sprite, char * path, int index, int size, int outline) +void sprite_load_text_texture(Sprite *sprite, const char * path, int index, int size, int outline) { if (index > 1) fatal("in sprite_load_texture() index out of bounds"); @@ -122,7 +122,7 @@ sprite_render(Sprite *s, Camera *cam) cameraPos.x, cameraPos.y, s->dim.width, s->dim.height }; - if ((s->clip.w && s->clip.h) || s->angle != 0 || s->flip != SDL_FLIP_NONE) { + if (s->angle != 0 || s->flip != SDL_FLIP_NONE) { texture_render_clip_ex(s->textures[s->texture_index], &box, &s->clip, diff --git a/src/sprite.h b/src/sprite.h index bbea9ad..38b0c56 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -27,7 +27,7 @@ #include "roommatrix.h" #include "timer.h" -typedef struct Sprite_t { +typedef struct Sprite { Texture* textures[2]; SDL_Rect clip; bool destroyTextures; @@ -46,9 +46,9 @@ typedef struct Sprite_t { Sprite* sprite_create(void); -void sprite_load_texture(Sprite *, char *path, int index, SDL_Renderer *); +void sprite_load_texture(Sprite *, const char *path, int index, SDL_Renderer *); -void sprite_load_text_texture(Sprite *, char *path, int index, int size, int outline); +void sprite_load_text_texture(Sprite *, const char *path, int index, int size, int outline); void sprite_set_texture(Sprite *, Texture *, int index); diff --git a/src/timer.h b/src/timer.h index 98d2e11..86c45c1 100644 --- a/src/timer.h +++ b/src/timer.h @@ -21,7 +21,7 @@ #include -typedef struct { +typedef struct Timer { unsigned int startTime; } Timer;