summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile26
-rw-r--r--def.h9
-rw-r--r--enemy.c198
-rw-r--r--enemy.h68
-rw-r--r--game-state.c150
-rw-r--r--game-state.h43
-rw-r--r--main.c180
-rw-r--r--matematik.c16
-rw-r--r--matematik.h11
-rw-r--r--player.c166
-rw-r--r--player.h66
-rw-r--r--sprite-animator.c47
-rw-r--r--sprite-animator.h27
-rw-r--r--textures.c23
-rw-r--r--textures.h23
-rw-r--r--tower.c39
-rw-r--r--tower.h29
17 files changed, 1121 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..16dcbfd
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,26 @@
+# SRC := main.c # player.o sprite-animator.o
+CC := gcc
+OUT := tarla
+OBJ := player.o sprite-animator.o matematik.o enemy.o tower.o game-state.o textures.o main.o
+LIBS := -lraylib -lGL -lm -lpthread -ldl -lrt -lX11
+FLAGS := -Wall
+
+all: $(OUT)
+
+%.o: %.c
+ $(CC) -c $(FLAGS) $< -o $@
+
+$(OUT): $(OBJ)
+ $(CC) $(FLAGS) $(OBJ) -o $(OUT) $(LIBS)
+
+clean:
+ rm -fv $(OUT) *~ *.o
+
+zip:
+ mkdir tarla-src
+ cp -a *.c *.h Makefile tarla-src
+ zip -r tarla.zip tarla-src
+ rm -r tarla-src
+
+count:
+ wc -l *.c *.h
diff --git a/def.h b/def.h
new file mode 100644
index 0000000..889488d
--- /dev/null
+++ b/def.h
@@ -0,0 +1,9 @@
+#ifndef TANIM_H
+#define TANIM_H
+
+#define WINDOW_HEIGHT 600
+#define WINDOW_WIDTH 800
+#define FPS 60
+
+
+#endif
diff --git a/enemy.c b/enemy.c
new file mode 100644
index 0000000..34570e3
--- /dev/null
+++ b/enemy.c
@@ -0,0 +1,198 @@
+#include "enemy.h"
+#include <stdlib.h>
+
+animation_player_t get_enemy_animation(enum enemy_type type){
+ switch (type){
+ case TEST_ENEMY:
+ return make_animation_player(&textures[T_PLAYER], 12, 8, 40, 64, 8, 0);
+ case SKELETON:
+ return make_animation_player(&textures[T_SKELETON_MOVE], 1, 10, 0, 10, 1, 0);
+ default:
+ fprintf(stderr, "get_enemy_animation() error");
+ return (animation_player_t){};
+ }
+}
+
+/* Vector2 get_enemy_size(enum enemy_type type){ */
+/* switch(type){ */
+/* case TEST_ENEMY: */
+/* return (Vector2){ 13, 18 }; */
+/* case SKELETON: */
+/* return (Vector2){ 12, 17 }; */
+/* default: */
+/* return (Vector2){ 0, 0 }; */
+/* } */
+/* } */
+
+Rectangle get_enemy_hitbox(enum enemy_type type){
+ switch(type){
+ case TEST_ENEMY:
+ return (Rectangle){ 1, 5, 13, 18 };
+ case SKELETON:
+ return (Rectangle){ 7, 12, 12, 17 };
+ default:
+ return (Rectangle){0};
+ }
+}
+
+void flip_enemy_y_axis(enemy_t *e){
+ if (e->direction.x < 0){
+ e->animation.frame_rec.width = -abs(e->animation.frame_rec.width);
+ e->hitbox.x = e->position.x + e->animation.frame_width - e->hb_offset.x - e->hitbox.width;
+ } else {
+ e->animation.frame_rec.width = abs(e->animation.frame_rec.width);
+ e->hitbox.x = e->position.x + e->hb_offset.x;
+ }
+}
+
+enemy_t make_enemy(enum enemy_type type, int max_health, int x, int y, int speed){
+ animation_player_t enemy_anim = get_enemy_animation(type);
+ Rectangle hitbox = get_enemy_hitbox(type);
+ Vector2 offset = (Vector2){ hitbox.x, hitbox.y };
+ hitbox.x += x;
+ hitbox.y += y;
+ return (enemy_t) {
+ .animation = enemy_anim,
+ .type = type,
+ .health = max_health,
+ .health_max = max_health,
+ .position = (Vector2){ x, y },
+ .hitbox = hitbox,
+ .hb_offset = offset,
+ /* .size = get_enemy_size(type), */
+ /* .center = (Vector2){ x + enemy_anim.frame_width / 2, y + enemy_anim.frame_height / 2}, */
+ .speed = speed,
+ .active = 1
+ };
+}
+
+void move_enemy(enemy_t* e, float dt){
+ float x_move = e->direction.x * e->speed * dt;
+ float y_move = e->direction.y * e->speed * dt;
+ e->position.x += x_move;
+ e->position.y += y_move;
+ e->hitbox.x += x_move;
+ e->hitbox.y += y_move;
+ /* e->center.x = e->position.x + e->size.x / 2; */
+ /* e->center.y = e->position.y + e->size.y / 2; */
+}
+
+void update_enemy_basic(enemy_t *e, Vector2 target, float dt){
+ float old = e->direction.x;
+ e->direction = normalize_vector(vector_diff(target, e->position));
+ if (signbit(old) != signbit(e->direction.x)){
+ flip_enemy_y_axis(e);
+ }
+}
+
+void update_test_enemy(enemy_t *e, Vector2 target, float dt){
+ e->direction = normalize_vector(vector_diff(target, e->position));
+ int new_offset = get_dir(floorf(e->direction.x + 1) + 3 * floorf(e->direction.y + 1));
+ if (new_offset != e->animation.index_offset){
+ animation_player_t *anim = &e->animation;
+ anim->index_offset = new_offset;
+ anim->frame_current = anim->index_start + anim->index_offset;
+ }
+}
+
+void update_enemy(enemy_t *e, Vector2 target, float dt){
+ update_frame(&e->animation);
+ switch (e->type){
+ case TEST_ENEMY:
+ update_test_enemy(e, target, dt);
+ break;
+ case SKELETON:
+ update_enemy_basic(e, target, dt);
+ break;
+ default:
+ break;
+ }
+ if (distance(target, e->position) > 1.0f){
+ move_enemy(e, dt);
+ }
+}
+
+/* animation_player_t *anim = &enemy->animation; */
+/* DrawTextureRec(*anim->sheet, anim->frame_rec, enemy->position, WHITE); */
+void draw_enemy(enemy_t *enemy){
+ draw_frame(&enemy->animation, enemy->position, WHITE);
+
+ DrawRectangleLinesEx(enemy->hitbox, 1, LIGHTGRAY);
+
+ Rectangle hb = enemy->hitbox;
+ Rectangle health_box = { hb.x, hb.y + hb.height + 5, hb.width, 5};
+ float per_health_width = health_box.width / enemy->health_max;
+ DrawRectangleLinesEx(health_box, 1, BLACK);
+ DrawRectangleRec((Rectangle){health_box.x, health_box.y, per_health_width * enemy->health, 5}, RED);
+
+ /* animation_player_t *anim = &enemy->animation; */
+ /* Vector2 pos = enemy->position; */
+ /* DrawRectangleLines(pos.x, pos.y, anim->frame_width, anim->frame_height, SKYBLUE); */
+ draw_frame_box(&enemy->animation, enemy->position);
+}
+
+/* Vector2 p = enemy->position; */
+/* Vector2 s = enemy->size; */
+/* Vector2 pos = (Vector2) {p.x, p.y + s.y + 5}; */
+/* Rectangle rec = (Rectangle) { pos.x, pos.y, s.x, 5 }; */
+/* float box_width = s.x / enemy->health_max; */
+/* /\* box_width * enemy->health *\/ */
+/* DrawRectangleLinesEx(rec, 1, BLACK); */
+/* DrawRectangleV(pos, (Vector2){box_width * enemy->health, 5}, RED); */
+/* /\* DrawRectangleLinesEx((Rectangle){ p.x, p.y, s.x, s.y }, 2, LIGHTGRAY); *\/ */
+/* /\* DrawRectangleV(enemy->position, enemy->size, LIGHTGRAY); *\/ */
+
+enemy_t spawn_enemy(enemy_spawner_t* spawner){
+ Rectangle *s = &spawner->border;
+ int x = s->x, y = s->y;
+ if (rand() % 2){
+ x += rand() % 2 ? 0 : s->width;
+ y += rand() % (int)s->height;
+ } else {
+ x += rand() % (int)s->width;
+ y += rand() % 2 ? 0 : s->height;
+ }
+ start_cooldown(spawner);
+ return make_enemy(rand() % ENEMY_TYPE_COUNT, 3, x, y, 40);
+}
+
+void update_spawner(enemy_spawner_t* spawner, float dt){
+ spawner->cooldown -= dt;
+}
+
+void start_cooldown(enemy_spawner_t* spawner){
+ spawner->cooldown = spawner->cooldown_max;
+}
+
+bool can_spawn(const enemy_spawner_t* spawner){
+ return spawner->cooldown <= 0;
+}
+
+/* animation_player_t enemy_anim = { */
+/* .sheet = texture, */
+/* .frame_counter = 0, */
+/* .frame_speed = 5, */
+/* .frame_width = texture->width / 8.0, */
+/* .frame_height = texture->height / 12.0, */
+/* .index_start = 40, */
+/* .index_end = 64, */
+/* .index_increment = 8, */
+/* .index_offset = 0 */
+/* }; */
+/* enemy_anim.frame_current = enemy_anim.index_start; */
+/* enemy_anim.frame_rec = (Rectangle){ 0, 0, enemy_anim.frame_width, enemy_anim.frame_height}; */
+
+/* enemy_t make_test_enemy(Texture2D *texture, int max_health, int x, int y, int speed){ */
+/* /\* animation_player_t enemy_anim = make_animation_player(texture, 12, 8, 40, 64, 8, 0); *\/ */
+/* animation_player_t enemy_anim = get_enemy_animation(TEST_ENEMY); */
+/* return (enemy_t) { */
+/* .animation = enemy_anim, */
+/* .type = TEST_ENEMY, */
+/* .health = max_health, */
+/* .health_max = max_health, */
+/* .position = (Vector2){ x, y }, */
+/* .size = (Vector2){ enemy_anim.frame_width, enemy_anim.frame_height }, */
+/* .center = (Vector2){ x + enemy_anim.frame_width / 2, y + enemy_anim.frame_height / 2}, */
+/* .speed = speed */
+/* }; */
+/* } */
diff --git a/enemy.h b/enemy.h
new file mode 100644
index 0000000..ae362bd
--- /dev/null
+++ b/enemy.h
@@ -0,0 +1,68 @@
+#ifndef DUSMAN_H
+#define DUSMAN_H
+
+#include <raylib.h>
+#include <math.h>
+#include <stdbool.h>
+#include "sprite-animator.h"
+#include "matematik.h"
+#include "player.h"
+#include "textures.h"
+
+enum enemy_type {
+ TEST_ENEMY,
+ SKELETON,
+ ENEMY_TYPE_COUNT
+};
+
+typedef struct Enemy {
+ animation_player_t animation;
+ enum enemy_type type;
+ int health;
+ int health_max;
+ Vector2 position;
+ Vector2 size;
+ /* Vector2 center; */
+ Vector2 direction;
+ Rectangle hitbox;
+ Vector2 hb_offset;
+ int speed;
+ int active;
+} enemy_t;
+
+animation_player_t get_enemy_animation(enum enemy_type type);
+Rectangle get_enemy_hitbox(enum enemy_type type);
+
+/* enemy_t make_test_enemy(Texture2D *texture, int max_health, int x, int y, int speed); */
+enemy_t make_enemy(enum enemy_type type, int max_health, int x, int y, int speed);
+void draw_enemy(enemy_t *enemy);
+
+void move_enemy(enemy_t* e, float dt);
+void flip_enemy_y_axis(enemy_t *enemy);
+
+void update_test_enemy(enemy_t *e, Vector2 target, float dt);
+void update_enemy_basic(enemy_t *e, Vector2 target, float dt);
+void update_enemy(enemy_t *e, Vector2 target, float dt);
+
+typedef struct EnemySpawner {
+ Rectangle border;
+ float cooldown_max;
+ float cooldown;
+} enemy_spawner_t;
+
+enemy_t spawn_enemy(enemy_spawner_t* spawner);
+void update_spawner(enemy_spawner_t* spawner, float dt);
+void start_cooldown(enemy_spawner_t* spawner);
+bool can_spawn(const enemy_spawner_t* spawner);
+
+/* Vector2 enemy_health = (Vector2){ */
+/* .x = 5, */
+/* .y = 5 */
+/* }; */
+
+/* void draw_enemy(enemy_t *enemy){ */
+/* DrawRectangleV(enemy->position, enemy->size, RED); */
+/* /\* DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color) *\/ */
+/* } */
+
+#endif
diff --git a/game-state.c b/game-state.c
new file mode 100644
index 0000000..420c6d9
--- /dev/null
+++ b/game-state.c
@@ -0,0 +1,150 @@
+#include "game-state.h"
+#include <stdlib.h>
+
+/* const int ENEMY_COUNT_MAX = 1000; */
+/* const int TOWER_COUNT_MAX = 5; */
+const enemy_spawner_t DEFAULT_ENEMY_SPAWNER = {
+ .border = { .x = 0, .y = 0, .width = WINDOW_WIDTH, .height = WINDOW_HEIGHT },
+ .cooldown = 100.0f,
+ .cooldown_max = 100.0f
+};
+
+float mouse_x(gs_t *gs){
+ Camera2D *cam = &gs->player.camera;
+ return cam->target.x + GetMouseX() - cam->offset.x;
+}
+
+float mouse_y(gs_t *gs){
+ Camera2D *cam = &gs->player.camera;
+ return cam->target.y + GetMouseY() - cam->offset.y;
+}
+
+void add_tower(gs_t *gs, tower_t tower){
+ if (gs->tower_count == TOWER_COUNT_MAX){
+ return;
+ }
+ gs->towers[gs->tower_count++] = tower;
+}
+
+void add_enemy(gs_t *gs, enemy_t enemy){
+ if (gs->enemy_count == ENEMY_COUNT_MAX){
+ return;
+ }
+ gs->enemies[gs->enemy_count++] = enemy;
+}
+
+void add_beam(gs_t *gs, tower_beam_t beam){
+ if (gs->beam_count == MAX_BEAM_COUNT){
+ return;
+ }
+ gs->beams[gs->beam_count++] = beam;
+}
+
+void kill_enemy(gs_t *gs, int index){
+ /* enemy_t temp = gs->enemies[index]; */
+ gs->enemies[index].active = 0;
+ gs->enemies[index] = gs->enemies[gs->enemy_count - 1];
+ /* gs->enemies[gs->enemy_count] = temp; */
+ gs->enemy_count--;
+ /* printf("enemy <%d> killed\n", index); */
+}
+
+void update_game(gs_t *gs, float dt){
+ update_player(&gs->player, dt);
+ for (int i = 0; i < gs->enemy_count; i++){
+ update_enemy(gs->enemies + i, gs->player.position, dt);
+ }
+ for (int i = 0; i < gs->tower_count; i++){
+ update_tower(&gs->towers[i], dt);
+ }
+ for (int i = 0; i < gs->tower_count; i++){
+ tower_t *tow = &gs->towers[i];
+ if (tow->cooldown <= 0){
+ for (int j = 0; j < gs->enemy_count; j++){
+ enemy_t *e = &gs->enemies[j];
+ if /* (distance(gs->towers[i].center, gs->enemies[j].center) */
+ /* < gs->towers[i].range) */
+ (e->active && CheckCollisionCircleRec(tow->center, tow->range, e->hitbox)){
+ add_beam(gs, make_beam(tow->position,
+ /* gs->enemies[j].center */
+ e->position, 0.1));
+ e->health -= tow->damage;
+ tow->cooldown = tow->cooldown_max;
+ PlaySound(BEAM_SOUND);
+ if (e->health <= 0){
+ kill_enemy(gs, j);
+ }
+ break;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < gs->enemy_count; i++){
+ if (CheckCollisionRecs(gs->enemies[i].hitbox, gs->player.hitbox)){
+ int remaining = take_damage(&gs->player, 1);
+ if (remaining == 0){
+ Camera2D camera = gs->player.camera;
+ gs->player = make_player(&textures[T_PLAYER]);
+ gs->player.camera.zoom = camera.zoom;
+ }
+ /* printf("collision\n"); */
+ }
+ }
+ for (int i = 0; i < gs->beam_count; i++){
+ gs->beams[i].live_time -= dt;
+ if (gs->beams[i].live_time <= 0){
+ gs->beams[i] = gs->beams[gs->beam_count];
+ gs->beam_count--;
+ }
+ }
+
+ if (can_spawn(&gs->spawner) && gs->enemy_count < ENEMY_COUNT_MAX){
+ add_enemy(gs, spawn_enemy(&gs->spawner));
+ }
+ update_spawner(&gs->spawner, dt);
+
+ if (IsKeyPressed(KEY_T)){
+ add_tower(gs, make_tower(1, 100, mouse_x(gs), mouse_y(gs), 20, 30, &textures[T_TOWER]));
+ }
+ if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
+ paint_tile(gs);
+ }
+}
+
+void draw_game(gs_t *gs){
+ draw_player(&gs->player);
+ for (int i = 0; i < gs->tower_count; i++){
+ draw_tower(gs->towers + i);
+ }
+ for (int i = 0; i < gs->enemy_count; i++){
+ draw_enemy(gs->enemies + i);
+ }
+ for (int i = 0; i < gs->beam_count; i++){
+ DrawLineV(gs->beams[i].src, gs->beams[i].dest, MAGENTA);
+ }
+ /* if (IsKeyDown(KEY_E)){ */
+
+ /* } */
+ DrawCircle(mouse_x(gs), mouse_y(gs), 5, VIOLET);
+}
+
+void paint_tile(gs_t *gs){
+ float zoom = gs->player.camera.zoom;
+ float edge_len = 16 / zoom;
+ Camera2D *c = &gs->player.camera;
+ int edge = edge_len * c->zoom;
+ int x = mouse_x(gs) / edge;
+ int y = (WINDOW_HEIGHT - mouse_y(gs)) / edge;
+ /* int y = (GetMouseY() - gs.player.camera.target.y) / edge_len; */
+ printf("drawing tile(%d, %d) at %d %d\n", x, y, x * edge, y * edge);
+ if /* (x >= 0 && x < WINDOW_WIDTH && y >= 0 && y < WINDOW_HEIGHT) */
+ (1){
+ BeginTextureMode(gs->bg_tiles);
+ DrawTextureRec(gs->textures[T_DIRT], (Rectangle){ 0, 0, 16, 16 },
+ (Vector2){x * edge /* / zoom */, y * edge /* / zoom */}
+ /* (Vector2){0, edge_len * yy} */
+ , WHITE);
+ EndTextureMode();
+ /* yy++; */
+ }
+}
diff --git a/game-state.h b/game-state.h
new file mode 100644
index 0000000..78e423f
--- /dev/null
+++ b/game-state.h
@@ -0,0 +1,43 @@
+#ifndef GAME_STATE_H
+#define GAME_STATE_H
+
+#include <raylib.h>
+
+#include "player.h"
+#include "def.h"
+#include "enemy.h"
+#include "tower.h"
+
+extern const enemy_spawner_t DEFAULT_ENEMY_SPAWNER;
+#define ENEMY_COUNT_MAX 1000
+#define TOWER_COUNT_MAX 100
+#define MAX_BEAM_COUNT 100
+/* extern const int ENEMY_COUNT_MAX; */
+/* extern const int TOWER_COUNT_MAX; */
+
+typedef struct GameState {
+ player_t player;
+ tower_t towers[TOWER_COUNT_MAX];
+ int tower_count;
+ enemy_t enemies[ENEMY_COUNT_MAX];
+ int enemy_count;
+ tower_beam_t beams[MAX_BEAM_COUNT];
+ int beam_count;
+ enemy_spawner_t spawner;
+ Texture2D *textures;
+ RenderTexture2D bg_tiles;
+} gs_t;
+
+float mouse_x(gs_t *gs);
+float mouse_y(gs_t *gs);
+
+void add_tower(gs_t *gs, tower_t tower);
+void add_enemy(gs_t *gs, enemy_t enemy);
+void add_beam(gs_t *gs, tower_beam_t beam);
+void kill_enemy(gs_t *gs, int index);
+void update_game(gs_t *gs, float dt);
+void draw_game(gs_t *gs);
+
+void paint_tile();
+
+#endif
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..417ba5f
--- /dev/null
+++ b/main.c
@@ -0,0 +1,180 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <time.h>
+
+#include <raylib.h>
+#include "game-state.h"
+#include "textures.h"
+
+/* Texture2D sheet = LoadTexture("asset/8-dir.png"); */
+/* Texture2D tile_texture = LoadTexture("asset/grass_tiles.png"); */
+/* Texture2D dirt_texture = LoadTexture("asset/dirt-path.png"); */
+/* Texture2D tower_texture = LoadTexture("asset/tower.png"); */
+/* Image dust_gif = LoadImageAnim("asset/dust.gif"); */
+
+
+
+char _frame_info_buf[128];
+void _draw_info(gs_t *gs);
+
+int main(){
+ srand(time(NULL));
+ InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "oyun");
+ InitAudioDevice();
+ HideCursor();
+ SetTargetFPS(FPS);
+
+ load_textures();
+ BEAM_SOUND = LoadSound("asset/laser.wav");
+
+ gs_t gs = {
+ .tower_count = 0,
+ .enemy_count = 0,
+ .textures = &textures[0],
+ .spawner = DEFAULT_ENEMY_SPAWNER
+ };
+ add_tower(&gs, make_tower(1, 100, WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2, 20, 30, &textures[T_TOWER]));
+ /* add_enemy(&gs, make_enemy(&sheet, 3, WINDOW_WIDTH / 2 + 80, WINDOW_HEIGHT / 2 - 80, 30)); */
+ /* add_enemy(&gs, make_enemy(&sheet, 3, WINDOW_WIDTH / 2 - 80, WINDOW_HEIGHT / 2 - 80, 30)); */
+ gs.player = make_player(&textures[T_PLAYER]);
+
+ Rectangle grass_tile1_rect = (Rectangle){ 0, 0, 16, 16 };
+ RenderTexture2D bg_tiles = LoadRenderTexture(WINDOW_WIDTH, WINDOW_HEIGHT);
+ gs.bg_tiles = bg_tiles;
+ // https://stackoverflow.com/questions/78609155/what-is-the-difference-between-begintexturemode-and-drawtexture-in-raylib
+ BeginTextureMode(bg_tiles);
+ int edge_len = 16;
+ for (int i = 0; i < WINDOW_WIDTH; i += edge_len)
+ for (int j = 0; j < WINDOW_HEIGHT; j += edge_len){
+ DrawTextureRec(textures[T_GRASS], grass_tile1_rect, (Vector2){i, j}, WHITE);
+ /* char buf[32]; */
+ /* snprintf(buf, 32, "%d", (i + j) % 10); */
+ /* DrawText(buf, i, j + 2, 4, BLACK); */
+ }
+ EndTextureMode();
+
+ /* int yy = 0; */
+ int paused = 0;
+ while (!WindowShouldClose()){
+
+ if (IsKeyPressed(KEY_P)){
+ paused = !paused;
+ } /* else if (IsKeyReleased(KEY_P)){ */
+ /* paused = 0; */
+ /* } */
+
+ if (!paused){
+ update_game(&gs, GetFrameTime());
+ }
+ /* update_frame(&anim); */
+
+ BeginDrawing();
+
+ ClearBackground(WHITE);
+
+ /* draw_frame(&anim); */
+ BeginMode2D(gs.player.camera);
+
+ /* for (int i = 0; i < WINDOW_WIDTH; i += edge_len) */
+ /* DrawLine(i, 0, i, WINDOW_HEIGHT, LIGHTGRAY); */
+ /* for (int i = 0; i < WINDOW_HEIGHT; i += edge_len) */
+ /* DrawLine(0, i, WINDOW_WIDTH, i, LIGHTGRAY); */
+ /* for (int i = 0; i < WINDOW_WIDTH; i += edge_len) */
+ /* for (int j = 0; j < WINDOW_HEIGHT; j += edge_len) */
+ /* DrawTextureRec(tile_texture, grass_tile1_rect, (Vector2){i, j}, WHITE); */
+ DrawTexture(bg_tiles.texture, 0, 0, WHITE);
+ /* DrawTextureRec(bg_tiles.texture, (Rectangle){ 0, 0, bg_tiles.texture.width, -bg_tiles.texture.height }, (Vector2){0, 0}, WHITE); */
+ draw_game(&gs);
+
+ EndMode2D();
+
+ _draw_info(&gs);
+ if (paused){
+ DrawText("PAUSED", WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2, 10, BLACK);
+ }
+
+ EndDrawing();
+ }
+
+ unload_textures();
+ /* UnloadTexture(sheet); */
+ /* UnloadTexture(tile_texture); */
+ /* UnloadTexture(tower_texture); */
+ /* UnloadTexture(dirt_texture); */
+ UnloadSound(BEAM_SOUND);
+ UnloadRenderTexture(bg_tiles);
+
+ CloseAudioDevice();
+ CloseWindow();
+ return EXIT_SUCCESS;
+}
+
+
+void _draw_info(gs_t *gs){
+ player_t *p = &gs->player;
+ sprintf(_frame_info_buf, "player (%.2f, %.2f)", p->position.x, p->position.y);
+ DrawText(_frame_info_buf, 20, 20, 20, BLACK);
+
+ sprintf(_frame_info_buf, "mouse (%d, %d)", GetMouseX(), GetMouseY());
+ DrawText(_frame_info_buf, 20, 50, 20, BLACK);
+
+ sprintf(_frame_info_buf, "camera target (%.2f, %.2f)", p->camera.target.x, p->camera.target.y);
+ DrawText(_frame_info_buf, 20, 80, 20, BLACK);
+
+ sprintf(_frame_info_buf, "camera offset (%.2f, %.2f)", p->camera.offset.x, p->camera.offset.y);
+ DrawText(_frame_info_buf, 20, 110, 20, BLACK);
+
+ sprintf(_frame_info_buf, "enemy count %d / %d", gs->enemy_count, ENEMY_COUNT_MAX);
+ DrawText(_frame_info_buf, 20, 140, 20, BLACK);
+
+ sprintf(_frame_info_buf, "tower count %d / %d", gs->tower_count, TOWER_COUNT_MAX);
+ DrawText(_frame_info_buf, 20, 170, 20, BLACK);
+
+
+ /* animation_player_t *anim = p->animation; */
+ /* sprintf(_frame_info_buf, "counter %d", anim->frame_counter); */
+ /* DrawText(_frame_info_buf, 20, 20, 20, BLACK); */
+
+ /* sprintf(_frame_info_buf, "current %d", anim->frame_current); */
+ /* DrawText(_frame_info_buf, 20, 50, 20, BLACK); */
+
+ /* sprintf(_frame_info_buf, "frec.x %f", anim->frame_rec.x); */
+ /* DrawText(_frame_info_buf, 20, 80, 20, BLACK); */
+
+ /* sprintf(_frame_info_buf, "frec.y %f", anim->frame_rec.y); */
+ /* DrawText(_frame_info_buf, 20, 110, 20, BLACK); */
+
+ /* sprintf(_frame_info_buf, "pos.x %f", p->position.x); */
+ /* DrawText(_frame_info_buf, 20, 140, 20, BLACK); */
+
+ /* sprintf(_frame_info_buf, "pos.y %f", p->position.y); */
+ /* DrawText(_frame_info_buf, 20, 170, 20, BLACK); */
+
+ /* if (gs->enemy_count > 0){ */
+ /* sprintf(_frame_info_buf, "(%f, %f)", */
+ /* gs->enemies[0].direction.x, */
+ /* gs->enemies[0].direction.y); */
+ /* DrawText(_frame_info_buf, 20, 200, 20, BLACK); */
+
+ /* sprintf(_frame_info_buf, "hp %d", gs->enemies[0].health); */
+ /* DrawText(_frame_info_buf, 20, 230, 20, BLACK); */
+
+ /* sprintf(_frame_info_buf, "dist %f", distance(gs->towers[0].center, gs->enemies[0].center)); */
+ /* DrawText(_frame_info_buf, 20, 260, 20, BLACK); */
+
+ /* animation_player_t *anim = &gs->enemies[0].animation; */
+ /* sprintf(_frame_info_buf, "counter %d", anim->frame_counter); */
+ /* DrawText(_frame_info_buf, 20, 20, 20, BLACK); */
+
+ /* sprintf(_frame_info_buf, "current %d", anim->frame_current); */
+ /* DrawText(_frame_info_buf, 20, 50, 20, BLACK); */
+
+ /* sprintf(_frame_info_buf, "frec.x %f", anim->frame_rec.x); */
+ /* DrawText(_frame_info_buf, 20, 80, 20, BLACK); */
+
+ /* sprintf(_frame_info_buf, "frec.y %f", anim->frame_rec.y); */
+ /* DrawText(_frame_info_buf, 20, 110, 20, BLACK); */
+
+ /* } */
+}
diff --git a/matematik.c b/matematik.c
new file mode 100644
index 0000000..c5e36b2
--- /dev/null
+++ b/matematik.c
@@ -0,0 +1,16 @@
+#include "matematik.h"
+
+float distance(Vector2 a, Vector2 b){
+ return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
+}
+
+Vector2 normalize_vector(Vector2 vec){
+ float len = sqrt(vec.x * vec.x + vec.y * vec.y);
+ vec.x /= len;
+ vec.y /= len;
+ return vec;
+}
+
+Vector2 vector_diff(Vector2 a, Vector2 b){
+ return (Vector2) { a.x - b.x, a.y - b.y };
+}
diff --git a/matematik.h b/matematik.h
new file mode 100644
index 0000000..e042d2f
--- /dev/null
+++ b/matematik.h
@@ -0,0 +1,11 @@
+#ifndef MATEMATIK_H
+#define MATEMATIK_H
+
+#include <raylib.h>
+#include <math.h>
+
+float distance(Vector2 a, Vector2 b);
+Vector2 normalize_vector(Vector2 vec);
+Vector2 vector_diff(Vector2 a, Vector2 b);
+
+#endif
diff --git a/player.c b/player.c
new file mode 100644
index 0000000..d9d0ad0
--- /dev/null
+++ b/player.c
@@ -0,0 +1,166 @@
+#include "player.h"
+
+void input_player_direction(player_t *p){
+ Vector2 v = {0, 0};
+ if (IsKeyDown(KEY_W)){
+ v.y = -1;
+ } else if (IsKeyDown(KEY_S)) {
+ v.y = 1;
+ }
+ if (IsKeyDown(KEY_D)){
+ v.x = 1;
+ } else if (IsKeyDown(KEY_A)){
+ v.x = -1;
+ }
+ int new_offset = get_dir((v.x + 1) + 3 * (v.y + 1));
+ if (new_offset != p->animation.index_offset){
+ animation_player_t *anim = &p->animation;
+ anim->index_offset = new_offset;
+ anim->frame_current = anim->index_start + anim->index_offset;
+ }
+ if (v.x != 0 || v.y != 0){
+ v = normalize_vector(v);
+ }
+ p->direction = v;
+}
+
+void update_player(player_t *p, float dt){
+ if (p->state.iframe_active){
+ p->iframe_duration -= dt;
+ if (p->iframe_duration <= 0){
+ p->state.iframe_active = 0;
+ p->iframe_duration = 0;
+ }
+ }
+
+ input_player_direction(p);
+ update_frame(&p->animation);
+
+ if (p->direction.x == 0 && p->direction.y == 0){
+ p->state.move = PS_STANDING;
+ p->animation.frame_speed = 0;
+ p->animation.frame_current = 20;
+ animation_player_t *anim = &p->animation;
+ anim->frame_rec.x = (anim->frame_current % 8) * anim->frame_width;
+ anim->frame_rec.y = (anim->frame_current / 8) * anim->frame_height;
+ } else if (IsKeyDown(KEY_LEFT_SHIFT)){
+ p->speed = PLAYER_SPEED_SPRINT;
+ p->state.move = PS_RUNNING;
+ p->animation.frame_speed = 10;
+ } else /* (IsKeyReleased(KEY_LEFT_SHIFT)) */ {
+ p->speed = PLAYER_SPEED_NORMAL;
+ p->state.move = PS_WALKING;
+ p->animation.frame_speed = 5;
+ }
+
+ float delta_x = p->direction.x * p->speed * dt;
+ float delta_y = p->direction.y * p->speed * dt;
+ p->position.x += delta_x;
+ p->position.y += delta_y;
+ p->hitbox.x += delta_x;
+ p->hitbox.y += delta_y;
+ p->camera.target = p->position;
+
+ p->camera.zoom += ((float)GetMouseWheelMove()*0.05f);
+
+ if (p->camera.zoom > 3.0f) p->camera.zoom = 3.0f;
+ else if (p->camera.zoom < 0.1f) p->camera.zoom = 0.1f;
+
+}
+
+void draw_player(player_t *p){
+ draw_frame(&p->animation, p->position, p->state.iframe_active ? RED : WHITE);
+ draw_frame_box(&p->animation, p->position);
+ DrawRectangleLinesEx(p->hitbox, 1, LIGHTGRAY);
+
+ Rectangle hb = p->hitbox;
+ Rectangle health_box = { hb.x, hb.y + hb.height + 5, hb.width, 5};
+ float per_health_width = health_box.width / p->health_max;
+ DrawRectangleLinesEx(health_box, 1, BLACK);
+ DrawRectangleRec((Rectangle){health_box.x, health_box.y, per_health_width * p->health, 5}, RED);
+
+ /* animation_player_t *anim = &p->animation; */
+ /* /\* draw_frame(anim); *\/ */
+ /* DrawTextureRec(*anim->sheet, anim->frame_rec, p->position, WHITE); */
+ /* if (p->state == RUNNING){ */
+ /* /\* DrawTexture(Texture2D texture, int posX, int posY, Color tint); *\/ */
+ /* } */
+}
+
+player_t make_player(Texture2D *texture){
+ animation_player_t anim = {
+ .sheet = texture,
+ .frame_counter = 0,
+ .frame_speed = 5,
+ .frame_width = texture->width / 8.0,
+ .frame_height = texture->height / 12.0,
+ .index_start = 8,
+ .index_end = 32,
+ .index_increment = 8,
+ .index_offset = 0
+ };
+ anim.frame_current = anim.index_start;
+ anim.frame_rec = (Rectangle){0, 0, anim.frame_width, anim.frame_height};
+
+ player_t player = {
+ .position = (Vector2){ WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2 - 100 },
+ .speed = PLAYER_SPEED_NORMAL,
+ .animation = anim,
+ .health = 3,
+ .health_max = 3,
+ .iframe_duration = 0,
+ .iframe_duration_max = 1,
+ .state = (player_state_t){
+ .move = PS_STANDING,
+ .can_move = 1,
+ .iframe_active = 0,
+ .alive = 1
+ }
+ };
+ player.hitbox = (Rectangle){
+ player.position.x,
+ player.position.y + 5,
+ anim.frame_rec.width,
+ anim.frame_height - 5
+ };
+ player.camera = (Camera2D){
+ .target = (Vector2){ player.position.x, player.position.y },
+ .offset = (Vector2){ WINDOW_WIDTH/2.0f, WINDOW_HEIGHT /2.0f },
+ .rotation = 0.0f,
+ .zoom = 1.0f
+ };
+ return player;
+}
+
+sprite_dir_enum _directions[9] = {
+ SP_NORTH_WEST,
+ SP_NORTH,
+ SP_NORTH_EAST,
+ SP_WEST,
+ SP_SOUTH,
+ SP_EAST,
+ SP_SOUTH_WEST,
+ SP_SOUTH,
+ SP_SOUTH_EAST,
+};
+
+sprite_dir_enum get_dir(int index){
+ return _directions[index];
+}
+
+int take_damage(player_t *p, int amount){
+ if (p->state.iframe_active){
+ return p->health;
+ }
+
+ p->health -= amount;
+ p->state.iframe_active = 1;
+ p->iframe_duration = p->iframe_duration_max;
+
+ if (p->health <= 0){
+ p->state.alive = 0;
+ return 0;
+ } else {
+ return p->health;
+ }
+}
diff --git a/player.h b/player.h
new file mode 100644
index 0000000..1a08a71
--- /dev/null
+++ b/player.h
@@ -0,0 +1,66 @@
+#ifndef OYUNCU_H
+#define OYUNCU_H
+
+#include <raylib.h>
+#include "sprite-animator.h"
+#include "def.h"
+#include "matematik.h"
+
+#define PLAYER_SPEED_NORMAL 40
+#define PLAYER_SPEED_SPRINT 65
+
+/* enum player_states { */
+/* PS_STANDING = 1, */
+/* PS_WALKING = 2, */
+/* PS_RUNNING = 4, */
+/* PS_MOVE_STATE = PS_STANDING | PS_WALKING | PS_RUNNING, */
+/* PS_CAN_MOVE = 8, */
+/* PS_IFRAME_ACTIVE = 16 */
+/* }; */
+
+#define PS_STANDING 0
+#define PS_WALKING 1
+#define PS_RUNNING 2
+
+typedef struct {
+ unsigned int move : 2;
+ unsigned int can_move : 1;
+ unsigned int iframe_active : 1;
+ unsigned int alive : 1;
+} player_state_t;
+
+typedef struct Player {
+ Vector2 position;
+ Vector2 direction;
+ int speed;
+ int health;
+ int health_max;
+ Rectangle hitbox;
+ float iframe_duration;
+ float iframe_duration_max;
+ animation_player_t animation;
+ Camera2D camera;
+ player_state_t state;
+} player_t;
+
+void input_player_direction(player_t *p);
+void update_player(player_t *p, float dt);
+void draw_player(player_t *p);
+player_t make_player(Texture2D *texture);
+int take_damage(player_t *p, int amount);
+
+
+typedef enum SpriteDirections {
+ SP_NORTH,
+ SP_NORTH_EAST,
+ SP_EAST,
+ SP_SOUTH_EAST,
+ SP_SOUTH,
+ SP_SOUTH_WEST,
+ SP_WEST,
+ SP_NORTH_WEST,
+} sprite_dir_enum;
+
+sprite_dir_enum get_dir(int index);
+
+#endif
diff --git a/sprite-animator.c b/sprite-animator.c
new file mode 100644
index 0000000..b77d225
--- /dev/null
+++ b/sprite-animator.c
@@ -0,0 +1,47 @@
+#include "sprite-animator.h"
+
+void update_frame(animation_player_t *anim){
+ if (anim->frame_speed == 0){
+ return;
+ }
+
+ anim->frame_counter++;
+ if (anim->frame_counter >= (FPS / anim->frame_speed)){
+ anim->frame_counter = 0;
+
+ anim->frame_current = anim->frame_current + anim->index_increment;
+ if (anim->frame_current >= anim->index_end) {
+ anim->frame_current = anim->index_start + anim->index_offset;
+ }
+
+ anim->frame_rec.x = (anim->frame_current % 8) * anim->frame_width;
+ anim->frame_rec.y = (anim->frame_current / 8) * anim->frame_height;
+ }
+}
+
+void draw_frame(animation_player_t *anim, Vector2 pos, Color tint){
+ DrawTextureRec(*anim->sheet, anim->frame_rec, pos, tint);
+}
+
+animation_player_t make_animation_player
+(Texture2D *texture, int row, int col, int start, int end, int inc, int offset){
+ float w = texture->width / col;
+ float h = texture->height / row;
+ return (animation_player_t){
+ .sheet = texture,
+ .frame_counter = 0,
+ .frame_speed = 5,
+ .frame_width = w,
+ .frame_height = h,
+ .index_start = start,
+ .index_end = end,
+ .index_increment = inc,
+ .index_offset = offset,
+ .frame_current = start,
+ .frame_rec = (Rectangle){ 0, 0, w, h }
+ };
+}
+
+void draw_frame_box(animation_player_t *anim, Vector2 pos){
+ DrawRectangleLines(pos.x, pos.y, anim->frame_width, anim->frame_height, SKYBLUE);
+}
diff --git a/sprite-animator.h b/sprite-animator.h
new file mode 100644
index 0000000..ab61dd7
--- /dev/null
+++ b/sprite-animator.h
@@ -0,0 +1,27 @@
+#ifndef SPRITE_SHEET_ANIMATOR_H
+#define SPRITE_SHEET_ANIMATOR_H
+
+#include <raylib.h>
+#include "def.h"
+
+typedef struct AnimationPlayer {
+ Texture2D *sheet;
+ int frame_counter;
+ int frame_speed;
+ int frame_current;
+ Rectangle frame_rec;
+ float frame_width;
+ float frame_height;
+
+ int index_start;
+ int index_offset;
+ int index_end;
+ int index_increment;
+} animation_player_t;
+
+animation_player_t make_animation_player(Texture2D *texture, int row, int col, int start, int end, int inc, int offset);
+void update_frame(animation_player_t *anim);
+void draw_frame(animation_player_t *anim, Vector2 pos, Color tint);
+void draw_frame_box(animation_player_t *anim, Vector2 pos);
+
+#endif
diff --git a/textures.c b/textures.c
new file mode 100644
index 0000000..52a745b
--- /dev/null
+++ b/textures.c
@@ -0,0 +1,23 @@
+#include "textures.h"
+
+Texture2D textures[T_COUNT];
+
+Texture2D load_texture(const char *path){
+ char buf[128];
+ snprintf(buf, 128, ASSET_ROOT "%s", path);
+ return LoadTexture(buf);
+}
+
+void load_textures(){
+ textures[T_PLAYER] = load_texture("8-dir.png");
+ textures[T_GRASS] = load_texture("grass_tiles.png");
+ textures[T_DIRT] = load_texture("dirt-path.png");
+ textures[T_TOWER] = load_texture("tower.png");
+ textures[T_SKELETON_MOVE] = load_texture("skeleton1/movement.png");
+}
+
+void unload_textures(){
+ for (size_t i = 0; i < T_COUNT; i++){
+ UnloadTexture(textures[i]);
+ }
+}
diff --git a/textures.h b/textures.h
new file mode 100644
index 0000000..01b8873
--- /dev/null
+++ b/textures.h
@@ -0,0 +1,23 @@
+#ifndef ASSET_LOADER_H
+#define ASSET_LOADER_H
+
+#include <raylib.h>
+#include <stdio.h>
+
+enum texture_enum {
+ T_PLAYER,
+ T_GRASS,
+ T_DIRT,
+ T_TOWER,
+ T_SKELETON_MOVE,
+ T_COUNT
+};
+
+extern Texture2D textures[T_COUNT];
+
+#define ASSET_ROOT "asset/"
+Texture2D load_texture(const char *path);
+void load_textures();
+void unload_textures();
+
+#endif
diff --git a/tower.c b/tower.c
new file mode 100644
index 0000000..882fc60
--- /dev/null
+++ b/tower.c
@@ -0,0 +1,39 @@
+#include "tower.h"
+
+Sound BEAM_SOUND;
+
+tower_t make_tower(int damage, int range, int x, int y, int width, int height, Texture2D *texture){
+ return (tower_t) {
+ .texture = texture,
+ .damage = damage,
+ .range = range,
+ .position = (Vector2){ x, y },
+ .size = (Vector2){ width, height },
+ .center = (Vector2){ x + width / 2, y + height / 2},
+ .cooldown = 0.0,
+ .cooldown_max = 1.0
+ };
+}
+
+tower_beam_t make_beam(Vector2 src, Vector2 dest, float live_time){
+ return (tower_beam_t){ .src = src, .dest = dest, .live_time = live_time };
+}
+
+char _tower_cooldown_text[16];
+void draw_tower(tower_t *tower){
+ DrawTexture(*tower->texture, tower->position.x, tower->position.y, WHITE);
+ /* DrawRectangleV(tower->position, tower->size, BLUE); */
+ DrawCircleLinesV(tower->center, tower->range, LIGHTGRAY);
+ const Vector2 p = tower->position;
+ const Vector2 s = tower->size;
+ DrawRectangle(p.x, p.y - 15, s.x * ((tower->cooldown_max - tower->cooldown) / tower->cooldown_max), 5, BLUE);
+ DrawRectangleLines(p.x, p.y - 15, s.x, 5, BLACK);
+ /* snprintf(_tower_cooldown_text, 16, "%.2f", tower->cooldown);
+ DrawText(_tower_cooldown_text, tower->position.x + (tower->size.x / 2), tower->position.y - 25, 10, LIGHTGRAY); */
+}
+
+void update_tower(tower_t *t, float dt){
+ if (t->cooldown > 0){
+ t->cooldown -= dt;
+ }
+}
diff --git a/tower.h b/tower.h
new file mode 100644
index 0000000..e5801c1
--- /dev/null
+++ b/tower.h
@@ -0,0 +1,29 @@
+#ifndef KULE_H
+#define KULE_H
+
+#include <raylib.h>
+
+typedef struct Tower {
+ Texture2D* texture;
+ int damage;
+ int range;
+ Vector2 position;
+ Vector2 size;
+ Vector2 center;
+ float cooldown;
+ float cooldown_max;
+} tower_t;
+
+extern Sound BEAM_SOUND;
+typedef struct TowerBeam {
+ Vector2 dest;
+ Vector2 src;
+ float live_time;
+} tower_beam_t;
+
+tower_t make_tower(int damage, int range, int x, int y, int width, int height, Texture2D *texture);
+tower_beam_t make_beam(Vector2 src, Vector2 dest, float live_time);
+void draw_tower(tower_t *tower);
+void update_tower(tower_t *t, float dt);
+
+#endif