summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrik@amftech.nl>2022-12-12 09:46:01 +0100
committerAldrik Ramaekers <aldrik@amftech.nl>2022-12-12 09:46:01 +0100
commitec3796faff12ba7bf5f775d757ac25834549903d (patch)
treec403de62f7d53ccfebf5cb431e0d5bf25064ebee /src
parent95c24b38b934f48f7f5cde37182c51857e77eac0 (diff)
src
Diffstat (limited to 'src')
-rw-r--r--src/allocator.c21
-rw-r--r--src/bullets.c208
-rw-r--r--src/list.c102
-rw-r--r--src/map.c245
-rw-r--r--src/math_helper.c124
-rw-r--r--src/objects.c91
-rw-r--r--src/pathfinding.c319
-rw-r--r--src/players.c168
-rw-r--r--src/zombies.c175
9 files changed, 1453 insertions, 0 deletions
diff --git a/src/allocator.c b/src/allocator.c
new file mode 100644
index 0000000..ffb6763
--- /dev/null
+++ b/src/allocator.c
@@ -0,0 +1,21 @@
+#include "../include/allocator.h"
+
+allocator create_allocator(uint64_t size) {
+ allocator allocator;
+ allocator.cursor = 0;
+ allocator.size = size;
+ allocator.memory = mem_alloc(size);
+ return allocator;
+}
+
+void* allocator_alloc(allocator* al, uint64_t size) {
+ if (al->cursor + size < al->size) {
+ al->cursor += size;
+ return al->memory + al->cursor - size;
+ }
+ log_assert(0, "Allocator out of space");
+}
+
+void destroy_allocator(allocator* al) {
+ mem_free(al->memory);
+} \ No newline at end of file
diff --git a/src/bullets.c b/src/bullets.c
new file mode 100644
index 0000000..c2f6d3b
--- /dev/null
+++ b/src/bullets.c
@@ -0,0 +1,208 @@
+#include "../include/bullets.h"
+
+void shoot(platform_window* window, player p) {
+ map_info info = get_map_info(window);
+ float bullet_range = 100.0f;
+
+ float hh = get_height_of_tile_under_coords(window, p.playerx, p.playery);
+
+ float dirx = (_global_mouse.x - (window->width/2));
+ float diry = (_global_mouse.y - (window->height/2));
+ double length = sqrt(dirx * dirx + diry * diry);
+ dirx /= length;
+ diry /= length;
+
+ #define SPRAY_BOUNDS (0.1f)
+ dirx += ((float)rand()/(float)(RAND_MAX/SPRAY_BOUNDS)-(SPRAY_BOUNDS/2));
+ diry += ((float)rand()/(float)(RAND_MAX/SPRAY_BOUNDS)-(SPRAY_BOUNDS/2));
+
+ float bulletx = p.gunx;
+ float bullety = p.guny;
+ float bullet_end_point_x = bulletx+dirx*bullet_range;
+ float bullet_end_point_y = bullety+diry*bullet_range;
+
+ for (int i = 0; i < max_bullets; i++) {
+ bullet b = bullets[i];
+ if (b.active) continue;
+
+ bullets[i] = (bullet){p.id, true, bulletx, bullety, hh + 0.5, bullet_end_point_x, bullet_end_point_y};
+ break;
+ }
+}
+
+bool check_if_bullet_collided_with_section(float* dist_of_closest_intersect, vec2f bstart, vec2f bend, vec2f l1, vec2f l2, vec2f* intersect_point_buf) {
+ if (lines_intersect(bstart, bend, l1, l2)) {
+ vec2f intersect_point = get_intersection_point(bstart, bend, l1, l2);
+
+ float dirx = (bstart.x - intersect_point.x);
+ float diry = (bstart.y - intersect_point.y);
+ double length_of_shot = sqrt(dirx * dirx + diry * diry);
+
+ if (length_of_shot > *dist_of_closest_intersect) {
+ return false;
+ }
+ *dist_of_closest_intersect = length_of_shot;
+ *intersect_point_buf = intersect_point;
+ return true;
+ }
+ return false;
+}
+
+bool check_if_bullet_collided_with_object(bullet* b, platform_window* window) {
+ map_info info = get_map_info(window);
+ float size = get_bullet_size_in_tile(window);
+
+ vec2f bstart = (vec2f){b->position.x, b->position.y};
+ vec2f bend = (vec2f){b->endx, b->endy};
+
+ bool result = false;
+ float dist_of_closest_intersect = __FLT_MAX__;
+
+ for (int i = 0; i < max_objects; i++) {
+ object o = objects[i];
+ if (!o.active) continue;
+ if (b->position.z <= o.h + o.size.z && b->position.z >= o.h) {
+ box obj_box = get_box_of_square(window, (vec3f){o.position.x, o.position.y, o.h}, o.size);
+ vec2f intersect_point;
+ if (check_if_bullet_collided_with_section(&dist_of_closest_intersect, bstart, bend, obj_box.bl_b, obj_box.br_b, &intersect_point)) {
+ result = true;
+ }
+ if (check_if_bullet_collided_with_section(&dist_of_closest_intersect, bstart, bend, obj_box.tl_b, obj_box.tr_b, &intersect_point)) {
+ result = true;
+ }
+ if (check_if_bullet_collided_with_section(&dist_of_closest_intersect, bstart, bend, obj_box.tl_b, obj_box.bl_b, &intersect_point)) {
+ result = true;
+ }
+ if (check_if_bullet_collided_with_section(&dist_of_closest_intersect, bstart, bend, obj_box.tr_b, obj_box.br_b, &intersect_point)) {
+ result = true;
+ }
+
+ if (result) {
+ b->endy = intersect_point.y;
+ b->endx = intersect_point.x;
+ }
+ }
+ }
+
+ return result;
+}
+
+bool check_if_bullet_collided_with_zombie(bullet b, platform_window* window, bool kill_if_collided) {
+ map_info info = get_map_info(window);
+ float size = get_bullet_size_in_tile(window);
+
+ bool result = false;
+ float dist_of_closest_intersect = __FLT_MAX__;
+ int index_of_closest_zombie = -1;
+
+ for (int i = 0; i < max_zombies; i++) {
+ zombie o = zombies[i];
+ if (!o.alive) continue;
+
+ vec2f bstart = (vec2f){b.position.x, b.position.y};
+ vec2f bend = (vec2f){b.endx, b.endy};
+
+ if (b.position.z <= o.position.z + o.size.z && b.position.z >= o.position.z) {
+ vec2f intersect_point;
+ box obj_box = get_box_of_square(window, (vec3f){o.position.x, o.position.y, o.position.z}, o.size);
+ bool this_zombie_collided = false;
+ if (check_if_bullet_collided_with_section(&dist_of_closest_intersect, bstart, bend, obj_box.bl_b, obj_box.br_b, &intersect_point)) {
+ this_zombie_collided = true;
+ index_of_closest_zombie = i;
+ }
+ if (check_if_bullet_collided_with_section(&dist_of_closest_intersect, bstart, bend, obj_box.tl_b, obj_box.tr_b, &intersect_point)) {
+ this_zombie_collided = true;
+ index_of_closest_zombie = i;
+ }
+ if (check_if_bullet_collided_with_section(&dist_of_closest_intersect, bstart, bend, obj_box.tl_b, obj_box.bl_b, &intersect_point)) {
+ this_zombie_collided = true;
+ index_of_closest_zombie = i;
+ }
+ if (check_if_bullet_collided_with_section(&dist_of_closest_intersect, bstart, bend, obj_box.tr_b, obj_box.br_b, &intersect_point)) {
+ this_zombie_collided = true;
+ index_of_closest_zombie = i;
+ }
+
+ if (this_zombie_collided) {
+ result = true;
+ }
+ }
+ }
+
+ if (kill_if_collided && result) {
+ zombies[index_of_closest_zombie].alive = false;
+ return result;
+ }
+
+ return result;
+}
+
+static bool check_if_bullet_collided_with_ground(bullet *b, platform_window* window) {
+ map_info info = get_map_info(window);
+ float dirx = (b->endx - b->position.x);
+ float diry = (b->endy - b->position.y);
+ float length = sqrt(dirx * dirx + diry * diry);
+ dirx /= length;
+ diry /= length;
+ double nr_tiles_to_check = length*4; // check 4 points per tile.
+
+ for (int i = 1; i < nr_tiles_to_check; i++) {
+ float xtocheck = b->position.x + (dirx*i/4);
+ float ytocheck = b->position.y + (diry*i/4);
+ if (!is_in_bounds(xtocheck, ytocheck)) break;
+ tile tile = get_tile_under_coords(window, xtocheck, ytocheck);
+
+ float h = get_height_of_tile_under_coords(window, xtocheck, ytocheck);
+ if (b->position.z <= h) {
+ b->endx = xtocheck;
+ b->endy = ytocheck;
+ return true;
+ }
+ }
+ return false;
+}
+
+void draw_bullets(platform_window* window) {
+ float size = get_bullet_size(window);
+ map_info info = get_map_info(window);
+
+ for (int i = 0; i < max_bullets; i++) {
+ bullet b = bullets[i];
+ if (!b.active) continue;
+ player p = get_player_by_id(b.player_id);
+ bullets[i].position.x = p.gunx;
+ bullets[i].position.y = p.guny;
+ bullets[i].position.z = p.gun_height;
+
+ if (check_if_bullet_collided_with_ground(&b, window)) {
+ bullets[i].endy = b.endy;
+ bullets[i].endx = b.endx;
+ b = bullets[i];
+ }
+
+ if (check_if_bullet_collided_with_object(&b, window)) {
+ bullets[i].endy = b.endy;
+ bullets[i].endx = b.endx;
+ b = bullets[i];
+ }
+
+ if (check_if_bullet_collided_with_zombie(b, window, true)) {
+ bullets[i].active = false;
+ }
+
+ bullets[i].alive_time += update_delta;
+ bullets[i].active = false;
+
+ if (!b.active) continue;
+
+ BULLET_RENDER_DEPTH(b.position.z);
+
+ float bullet_render_x = b.position.x*info.tile_width + (b.position.y*info.px_incline);
+ float bullet_render_y = b.position.y*info.tile_height - (b.position.z*info.px_raised_per_h);
+
+ float bullet_render_x_end = b.endx*info.tile_width + (b.endy*info.px_incline);
+ float bullet_render_y_end = b.endy*info.tile_height - (b.position.z*info.px_raised_per_h);
+
+ renderer->render_line(bullet_render_x, bullet_render_y, bullet_render_x_end, bullet_render_y_end, 2, rgb(0,255,100));
+ }
+} \ No newline at end of file
diff --git a/src/list.c b/src/list.c
new file mode 100644
index 0000000..ae394ec
--- /dev/null
+++ b/src/list.c
@@ -0,0 +1,102 @@
+#include "../include/list.h"
+
+list list_create(u16 size, allocator* al)
+{
+ list l;
+ l.size = size;
+ l.count = 0;
+ l.start = 0;
+ l.al = al;
+
+ return l;
+}
+
+void list_destroy(list* list) {
+ list->start = 0;
+ list->count = 0;
+}
+
+list_item *list_push(list *list, void *data)
+{
+ list_item *item = allocator_alloc(list->al, sizeof(list_item));
+ item->next = 0;
+ item->data = data;
+
+ if (list->count == 0)
+ {
+ list->start = item;
+ }
+ else
+ {
+ list_item *current_item = list->start;
+ while(current_item)
+ {
+ if (!current_item->next)
+ {
+ current_item->next = item;
+ goto done;
+ }
+ else
+ {
+ current_item = current_item->next;
+ }
+ }
+ }
+
+ done:
+ list->count++;
+ return item;
+}
+
+void* list_at(list *list, u32 index)
+{
+ list_item *current_item = list->start;
+ s32 count = 0;
+ while(current_item)
+ {
+ if (count == index)
+ return current_item->data;
+ count++;
+ current_item = current_item->next;
+ }
+ return 0;
+}
+
+void list_remove_at(list *list, u32 index)
+{
+ list_item *prev = 0;
+ list_item *current_item = list->start;
+ s32 count = 0;
+ while(current_item)
+ {
+ if (count == index)
+ {
+ if (prev)
+ prev->next = current_item->next;
+ else
+ list->start = current_item->next;
+
+ goto done;
+ }
+
+ count++;
+ prev = current_item;
+ current_item = current_item->next;
+ }
+
+ done:
+ list->count--;
+}
+
+void list_debug_print(list *list)
+{
+ list_item *current_item = list->start;
+ s32 count = 0;
+ while(current_item)
+ {
+ printf("%d %p\n", count, current_item->data);
+
+ count++;
+ current_item = current_item->next;
+ }
+} \ No newline at end of file
diff --git a/src/map.c b/src/map.c
new file mode 100644
index 0000000..e466f5f
--- /dev/null
+++ b/src/map.c
@@ -0,0 +1,245 @@
+#include "../include/map.h"
+
+static int get_height_of_tile_tl(int current_height, int x, int y) {
+ int highest_point = current_height;
+ if (y > 0) {
+ int tile_above = map[y-1][x];
+ if (tile_above > highest_point) {
+ highest_point = tile_above;
+ }
+ }
+ if (y > 0 && x > 0) {
+ int tile_above = map[y-1][x-1];
+ if (tile_above > highest_point) {
+ highest_point = tile_above;
+ }
+ }
+ if (x > 0) {
+ int tile_above = map[y][x-1];
+ if (tile_above > highest_point) {
+ highest_point = tile_above;
+ }
+ }
+ return highest_point;
+}
+
+
+static int get_height_of_tile_br(int current_height, int x, int y) {
+ int highest_point = current_height;
+ if (x < MAP_SIZE_X-1) {
+ int tile_right = map[y][x+1];
+ if (tile_right > highest_point) {
+ highest_point = tile_right;
+ }
+ }
+ if (y < MAP_SIZE_Y-1 && x < MAP_SIZE_X-1) {
+ int tile_bottom_right = map[y+1][x+1];
+ if (tile_bottom_right > highest_point) {
+ highest_point = tile_bottom_right;
+ }
+ }
+ if (y < MAP_SIZE_Y-1) {
+ int tile_bottom = map[y+1][x];
+ if (tile_bottom > highest_point) {
+ highest_point = tile_bottom;
+ }
+ }
+ return highest_point;
+}
+
+static int get_height_of_tile_bl(int current_height, int x, int y) {
+ int highest_point = current_height;
+ if (y > 0 && x > 0) {
+ int tile_left = map[y][x-1];
+ if (tile_left > highest_point) {
+ highest_point = tile_left;
+ }
+ }
+ if (y < MAP_SIZE_Y-1 && x > 0) {
+ int tile_bottom_left = map[y+1][x-1];
+ if (tile_bottom_left > highest_point) {
+ highest_point = tile_bottom_left;
+ }
+ }
+ if (y < MAP_SIZE_Y-1) {
+ int tile_bottom = map[y+1][x];
+ if (tile_bottom > highest_point) {
+ highest_point = tile_bottom;
+ }
+ }
+ return highest_point;
+}
+
+static int get_height_of_tile_tr(int current_height, int x, int y) {
+ int highest_point = current_height;
+ if (y > 0) {
+ int tile_above = map[y-1][x];
+ if (tile_above > highest_point) {
+ highest_point = tile_above;
+ }
+ }
+ if (y > 0 && x < MAP_SIZE_X-1) {
+ int tile_above_right = map[y-1][x+1];
+ if (tile_above_right > highest_point) {
+ highest_point = tile_above_right;
+ }
+ }
+ if (x < MAP_SIZE_X-1) {
+ int tile_right = map[y][x+1];
+ if (tile_right > highest_point) {
+ highest_point = tile_right;
+ }
+ }
+ return highest_point;
+}
+
+// load hardcoded map.
+void load_map_from_data() {
+ for (int y = 0; y < MAP_SIZE_Y; y++) {
+ for (int x = MAP_SIZE_X-1; x >= 0; x--) {
+ int h = map[y][x];
+ int highest_point_topleft = get_height_of_tile_tl(h, x, y);
+ int highest_point_topright = get_height_of_tile_tr(h, x, y);
+ int highest_point_bottomright = get_height_of_tile_br(h, x, y);
+ int highest_point_bottomleft = get_height_of_tile_bl(h, x, y);
+ map_loaded[y][x] = (tile){h, highest_point_topleft, highest_point_topright, highest_point_bottomleft, highest_point_bottomright};
+ }
+ }
+}
+
+tile get_tile_under_coords(platform_window* window, float x, float y) {
+ return map_loaded[(int)(y)][(int)(x)];
+}
+
+float get_height_of_tile_under_coords(platform_window* window, float tocheckx, float tochecky) {
+ tile tile_under_coords = get_tile_under_coords(window, tocheckx, tochecky);
+
+ int ty = (int)(tochecky);
+ int tx = (int)(tocheckx);
+
+ float percentage_x = tocheckx - tx;
+ float percentage_y = tochecky - ty;
+
+ float topline_h = min(tile_under_coords.topright, tile_under_coords.topleft);
+ float topline_diff = (tile_under_coords.topright - tile_under_coords.topleft) * percentage_x;
+ if (topline_diff < 0.0f) topline_diff = abs(tile_under_coords.topright - tile_under_coords.topleft) + topline_diff;
+ topline_h += topline_diff;
+
+ float bottomline_h = min(tile_under_coords.bottomright, tile_under_coords.bottomleft);
+ float bottomline_diff = (tile_under_coords.bottomright - tile_under_coords.bottomleft) * percentage_x;
+ if (bottomline_diff < 0.0f) bottomline_diff = abs(tile_under_coords.bottomright - tile_under_coords.bottomleft) + bottomline_diff;
+ bottomline_h += bottomline_diff;
+
+ float playerline = min(topline_h, bottomline_h);
+ float playerline_diff = (bottomline_h - topline_h) * percentage_y;
+ if (bottomline_h < topline_h) {
+ playerline_diff = (topline_h - bottomline_h) * (1 -percentage_y);
+ }
+ playerline += playerline_diff;
+
+ return playerline;
+}
+
+inline int get_tile_width(platform_window* window) {
+ int tile_width = window->height / 14;
+ if (window->width > window->height) tile_width = window->width / 14;
+ return tile_width;
+}
+
+inline int get_tile_height(platform_window* window) {
+ int tile_width = get_tile_width(window);
+ int tile_height = tile_width * 1;
+ return tile_height;
+}
+
+bool is_in_bounds(float x, float y) {
+ return (x >= 0 && x <= MAP_SIZE_X && y >= 0 && y < MAP_SIZE_Y);
+}
+
+static float offset = 0.0f;
+static bool dir = true;
+void draw_grid(platform_window* window) {
+ /*if (dir) offset += 0.005f;
+ else offset -= 0.005f;
+ if (offset >= 0.5f) dir = false;
+ if (offset <= -0.5f) dir = true;*/
+
+ map_info info = get_map_info(window);
+
+ for (int y = 0; y < MAP_SIZE_Y; y++) {
+ MAP_RENDER_DEPTH;
+ for (int x = MAP_SIZE_X-1; x >= 0; x--) {
+ tile tile = map_loaded[y][x];
+
+ float xdiff_between_bottom_and_top = info.px_incline;
+ float render_x = (info.tile_width * x) + (xdiff_between_bottom_and_top * y);
+ float render_y = info.tile_height * y;
+ render_y -= tile.height*info.px_raised_per_h;
+
+ vec2f topleft;
+ vec2f bottomleft;
+ vec2f bottomright;
+ vec2f topright;
+
+ int highest_point_topleft = tile.topleft;
+ topleft = (vec2f){render_x, info.tile_width * y - highest_point_topleft*info.px_raised_per_h};
+
+ int highest_point_topright = tile.topright;
+ topright = (vec2f){render_x + info.tile_width, info.tile_height * y - highest_point_topright*info.px_raised_per_h};
+
+ int highest_point_bottomright = tile.bottomright;
+ bottomright = (vec2f){render_x + xdiff_between_bottom_and_top+info.tile_width, info.tile_height * y + info.tile_height - highest_point_bottomright*info.px_raised_per_h};
+
+ int highest_point_bottomleft = tile.bottomleft;
+ bottomleft = (vec2f){render_x + xdiff_between_bottom_and_top, info.tile_height * y + info.tile_height - highest_point_bottomleft*info.px_raised_per_h};
+
+ int r = 220;
+ if (highest_point_topleft > highest_point_bottomleft || highest_point_topright > highest_point_bottomright ||
+ highest_point_topleft > highest_point_bottomright || highest_point_topright > highest_point_bottomleft ||
+ highest_point_topright > highest_point_topleft || highest_point_bottomright > highest_point_bottomleft) r = 180;
+ if (highest_point_topleft < highest_point_bottomleft || highest_point_topright < highest_point_bottomright ||
+ highest_point_topleft > highest_point_topright) r = 240;
+
+ renderer->render_quad(
+ topleft.x, topleft.y,
+ bottomleft.x, bottomleft.y,
+ bottomright.x, bottomright.y,
+ topright.x, topright.y,
+ rgb(r,0,0));
+
+ if (highest_point_topleft != highest_point_bottomright && highest_point_bottomleft == highest_point_topright) {
+ if (highest_point_bottomleft < highest_point_topleft || highest_point_bottomleft < highest_point_bottomright) {
+ renderer->render_tri(topleft.x, topleft.y,
+ bottomleft.x, bottomleft.y,
+ bottomright.x, bottomright.y, rgb(180,0,0));
+ }
+ renderer->render_line(topleft.x, topleft.y, bottomright.x, bottomright.y, 1, rgb(0,0,255)); // diag
+ }
+
+ if (highest_point_bottomleft != highest_point_topright && highest_point_topleft == highest_point_bottomright) {
+ renderer->render_line(topright.x, topright.y, bottomleft.x, bottomleft.y, 1, rgb(0,0,255)); // diag
+ }
+
+ renderer->render_line(topleft.x, topleft.y, topright.x, topright.y, 1, rgb(0,0,255)); // top
+ renderer->render_line(topleft.x, topleft.y, bottomleft.x, bottomleft.y, 1, rgb(0,0,255)); // left
+ renderer->render_line(topright.x, topright.y, bottomright.x, bottomright.y, 1, rgb(0,0,255)); // right
+ renderer->render_line(bottomleft.x, bottomleft.y, bottomright.x, bottomright.y, 1, rgb(0,0,255)); // bottom
+
+ map_loaded[y][x].tl = topleft;
+ map_loaded[y][x].tr = topright;
+ map_loaded[y][x].bl = bottomleft;
+ map_loaded[y][x].br = bottomright;
+ }
+
+ draw_objects_at_row(window, y);
+ }
+}
+
+inline map_info get_map_info(platform_window* window) {
+ map_info info;
+ info.tile_width = get_tile_width(window);
+ info.tile_height = get_tile_height(window);
+ info.px_incline = info.tile_width/3; // info.tile_width/3; // offset*info.tile_width;
+ info.px_raised_per_h = info.tile_height/2.5;
+ return info;
+} \ No newline at end of file
diff --git a/src/math_helper.c b/src/math_helper.c
new file mode 100644
index 0000000..3bbfb29
--- /dev/null
+++ b/src/math_helper.c
@@ -0,0 +1,124 @@
+#include "../include/math_helper.h"
+
+
+bool onSegment(vec2f p, vec2f q, vec2f r)
+{
+ if (q.x <= max(p.x, r.x) && q.x >= min(p.x, r.x) &&
+ q.y <= max(p.y, r.y) && q.y >= min(p.y, r.y))
+ return true;
+
+ return false;
+}
+
+int orientation(vec2f p, vec2f q, vec2f r)
+{
+ int val = (q.y - p.y) * (r.x - q.x) -
+ (q.x - p.x) * (r.y - q.y);
+
+ if (val == 0) return 0; // collinear
+
+ return (val > 0)? 1: 2; // clock or counterclock wise
+}
+
+vec2f get_dir_of_line(vec2f p1, vec2f p2) {
+ float dirx = (p1.x - p2.x);
+ float diry = (p1.y - p2.y);
+ double length = sqrt(dirx * dirx + diry * diry);
+ dirx /= length;
+ diry /= length;
+ return (vec2f){dirx, diry};
+}
+
+bool neg2(float p1, float p2) {
+ return p1 < 0.0f && p2 < 0.0f || abs(p1-p2) < 1.0f;
+}
+
+bool pos2(float p1, float p2) {
+ return (p1 >= 0.0f && p2 >= 0.0f) || abs(p1-p2) < 1.0f;
+}
+
+
+bool lines_intersect(vec2f p1, vec2f q1, vec2f p2, vec2f q2)
+{
+ int o1 = orientation(p1, q1, p2);
+ int o2 = orientation(p1, q1, q2);
+ int o3 = orientation(p2, q2, p1);
+ int o4 = orientation(p2, q2, q1);
+
+ // General case
+ if (o1 != o2 && o3 != o4) {
+ vec2f bdir = get_dir_of_line(p1, q1);
+ vec2f pdir = get_dir_of_line(p1, p2);
+ if (((p1.x <= p2.x && q1.x >= p2.x) || (p1.x >= p2.x && q1.x <= p2.x)) || ((p1.y <= p2.y && q1.y >= p2.y) || (p1.y >= p2.y && q1.y <= p2.y)))
+ if ((neg2(bdir.x, pdir.x) || pos2(bdir.x, pdir.x)) && (neg2(bdir.y, pdir.y) || pos2(bdir.y, pdir.y))) return true;
+ }
+
+ // Special Cases
+ // p1, q1 and p2 are collinear and p2 lies on segment p1q1
+ if (o1 == 0 && onSegment(p1, p2, q1)) return true;
+
+ // p1, q1 and q2 are collinear and q2 lies on segment p1q1
+ if (o2 == 0 && onSegment(p1, q2, q1)) return true;
+
+ // p2, q2 and p1 are collinear and p1 lies on segment p2q2
+ if (o3 == 0 && onSegment(p2, p1, q2)) return true;
+
+ // p2, q2 and q1 are collinear and q1 lies on segment p2q2
+ if (o4 == 0 && onSegment(p2, q1, q2)) return true;
+
+ return false; // Doesn't fall in any of the above cases
+}
+
+vec2f get_intersection_point(vec2f A, vec2f B, vec2f C, vec2f D) {
+ // Line AB represented as a1x + b1y = c1
+ double a1 = B.y - A.y;
+ double b1 = A.x - B.x;
+ double c1 = a1*(A.x) + b1*(A.y);
+
+ // Line CD represented as a2x + b2y = c2
+ double a2 = D.y - C.y;
+ double b2 = C.x - D.x;
+ double c2 = a2*(C.x)+ b2*(C.y);
+
+ double determinant = a1*b2 - a2*b1;
+
+ if (determinant == 0)
+ {
+ // The lines are parallel. This is simplified
+ // by returning a pair of FLT_MAX
+ return (vec2f){__FLT_MAX__, __FLT_MAX__};
+ }
+ else
+ {
+ double x = (b2*c1 - b1*c2)/determinant;
+ double y = (a1*c2 - a2*c1)/determinant;
+ return (vec2f){x, y};
+ }
+}
+
+box get_box_of_square(platform_window* window, vec3f position, vec3f size) {
+ map_info info = get_map_info(window);
+ vec2f rendertl = (vec2f){position.x, position.y};
+ vec2f rendertr = (vec2f){position.x + size.x, position.y};
+ vec2f renderbr = (vec2f){position.x + size.x, position.y + size.y};
+ vec2f renderbl = (vec2f){position.x, position.y + size.y};
+
+ return (box){rendertl, rendertr, renderbl, renderbr, rendertl, rendertr, renderbl, renderbr};
+}
+
+box get_render_box_of_square(platform_window* window, vec3f position, vec3f size) {
+ map_info info = get_map_info(window);
+ float render_x = (info.tile_width * position.x) + (info.px_incline * position.y);
+ vec2f rendertl = (vec2f){render_x, info.tile_width * position.y - position.z*info.px_raised_per_h};
+ vec2f rendertr = (vec2f){render_x + info.tile_width*size.x, info.tile_height * position.y - position.z*info.px_raised_per_h};
+ vec2f renderbr = (vec2f){render_x + (info.px_incline+info.tile_width)*size.x, info.tile_height * position.y + info.tile_height*size.y - position.z*info.px_raised_per_h};
+ vec2f renderbl = (vec2f){render_x + info.px_incline*size.x, info.tile_height * position.y + info.tile_height*size.y - position.z*info.px_raised_per_h};
+
+ position.z += size.z;
+ vec2f rendertl2 = (vec2f){render_x, info.tile_width * position.y - position.z*info.px_raised_per_h};
+ vec2f rendertr2 = (vec2f){render_x + info.tile_width*size.x, info.tile_height * position.y - position.z*info.px_raised_per_h};
+ vec2f renderbr2 = (vec2f){render_x + (info.px_incline+info.tile_width)*size.x, info.tile_height * position.y + info.tile_height*size.y - position.z*info.px_raised_per_h};
+ vec2f renderbl2 = (vec2f){render_x + info.px_incline*size.x, info.tile_height * position.y + info.tile_height*size.y - position.z*info.px_raised_per_h};
+
+ return (box){rendertl, rendertr, renderbl, renderbr, rendertl2, rendertr2, renderbl2, renderbr2};
+} \ No newline at end of file
diff --git a/src/objects.c b/src/objects.c
new file mode 100644
index 0000000..efa128b
--- /dev/null
+++ b/src/objects.c
@@ -0,0 +1,91 @@
+#include "../include/objects.h"
+
+box get_box_of_object(platform_window* window, object o) {
+ return get_render_box_of_square(window, (vec3f){o.position.x, o.position.y, o.h}, o.size);
+}
+
+void render_quad_with_outline(vec2f tl, vec2f tr, vec2f bl, vec2f br) {
+ renderer->render_quad(
+ tl.x, tl.y,
+ bl.x, bl.y,
+ br.x, br.y,
+ tr.x, tr.y,
+ rgb(200,200,0));
+
+ renderer->render_line(tl.x, tl.y, tr.x, tr.y, 1, rgb(0,0,255)); // top
+ renderer->render_line(tl.x, tl.y, bl.x, bl.y, 1, rgb(0,0,255)); // left
+ renderer->render_line(tr.x, tr.y, br.x, br.y, 1, rgb(0,0,255)); // right
+ renderer->render_line(bl.x, bl.y, br.x, br.y, 1, rgb(0,0,255)); // bottom
+}
+
+object get_object_at_tile(int x, int y) {
+ for (int i = 0; i < max_objects; i++) {
+ object o = objects[i];
+ if (!o.active) continue;
+ if (o.position.x == x && o.position.y == y) return o;
+ }
+ return (object){0};
+}
+
+void draw_objects_at_row(platform_window* window, int row) {
+ map_info info = get_map_info(window);
+
+ for (int i = MAP_SIZE_X-1; i >= 0; i--) {
+ object o = get_object_at_tile(i, row);
+
+ OBJECT_RENDER_DEPTH(o.h);
+ draw_players_at_tile(window, i, row);
+
+ draw_zombies_at_tile(window, i, row);
+
+ draw_bullets(window);
+ OBJECT_RENDER_DEPTH(o.h);
+
+ if (!o.active) continue;
+ box box = get_box_of_object(window, o);
+ render_quad_with_outline(box.tl_b, box.tr_b, box.bl_b, box.br_b);
+ render_quad_with_outline(box.tl_u, box.tr_u, box.bl_u, box.br_u);
+ render_quad_with_outline(box.tl_u, box.tl_b, box.bl_u, box.bl_b);
+ render_quad_with_outline(box.bl_u, box.br_u, box.bl_b, box.br_b);
+ }
+}
+
+void create_box(float x, float y, float h) {
+ for (int i = 0; i < max_objects; i++) {
+ object o = objects[i];
+ if (o.active) continue;
+
+ objects[i].active = true;
+ objects[i].position = (vec2f){x, y};
+ objects[i].h = h;
+ objects[i].size = (vec3f){1,1,2};
+ break;
+ }
+}
+
+void create_objects() {
+ // rechts naar links op map.
+
+ for (int i = MAP_SIZE_X-1; i >= 0; i--) {
+ create_box(i, 0, 0);
+ create_box(i, MAP_SIZE_Y-1, 0);
+ }
+
+ for (int i = MAP_SIZE_Y-1; i >= 0; i--) {
+ create_box(0, i, 0);
+ create_box(MAP_SIZE_Y-1, i, 0);
+ }
+
+ create_box(16, 8, 0);
+ create_box(14, 8, 0);
+ create_box(11, 8, 0);
+ create_box(10, 8, 0);
+
+ create_box(15, 10, 0);
+ create_box(14, 10, 0);
+ create_box(13, 10, 0);
+ create_box(11, 10, 0);
+
+ spawn_player(my_id);
+ //spawn_player(my_id+1);
+} \ No newline at end of file
diff --git a/src/pathfinding.c b/src/pathfinding.c
new file mode 100644
index 0000000..16e5261
--- /dev/null
+++ b/src/pathfinding.c
@@ -0,0 +1,319 @@
+#include "../include/pathfinding.h"
+
+static float distance_between(vec2f v1, vec2f v2)
+{
+ return ((v1.x-v2.x)*(v1.x-v2.x)+(v1.y-v2.y)*(v1.y-v2.y));
+}
+
+bool can_walk_at(float x, float y)
+{
+ for (int i = 0; i < max_objects; i++) {
+ object o = objects[i];
+ if (!o.active) continue;
+ if (x >= o.position.x && x < o.position.x + o.size.x && y >= o.position.y && y < o.position.y + o.size.y) return false;
+ }
+ return true;
+}
+
+bool find_path_to(vec2f start_pos, vec2f end_pos, array *to_fill, pathfinding_request* request)
+{
+ uint64_t timestamp = platform_get_time(TIME_PROCESS, TIME_MS);
+ allocator al = create_allocator(500000);
+
+ struct path_node {
+ struct path_node *parent;
+ vec2f position;
+ s32 g;
+ s32 h;
+ s32 f;
+ };
+
+ list open_array = list_create(sizeof(struct path_node), &al);
+ list closed_array = list_create(sizeof(struct path_node), &al);
+
+ struct path_node *start_node = allocator_alloc(&al, sizeof(struct path_node));
+ start_node->g = 0;
+ start_node->h = 0;
+ start_node->f = 0;
+ start_node->parent = 0;
+ start_node->position = start_pos;
+
+ list_push(&open_array, (uint8_t*)start_node);
+
+ struct path_node *current_node = 0;
+ while(open_array.count > 0)
+ {
+ // Get the current node
+ current_node = list_at(&open_array, 0);
+ s32 current_index = 0;
+
+ for (s32 i = 0; i < open_array.count; i++)
+ {
+ struct path_node *item = list_at(&open_array, i);
+ if (item->f < current_node->f)
+ {
+ current_node = item;
+ current_index = i;
+ }
+ }
+
+ //Pop current off open array, add to closed array
+ list_push(&closed_array, (uint8_t*)current_node);
+
+ if (closed_array.count > 1000) return false;
+ if (open_array.count > 1000) return false;
+
+ // Found the goal
+ if (distance_between(current_node->position, end_pos) <= 0)
+ {
+ mutex_lock(&request->mutex);
+
+ if (to_fill)
+ to_fill->length = 0;
+ struct path_node *current = current_node;
+
+ while(current != 0)
+ {
+ if (to_fill)
+ {
+ vec2f v;
+ v.x = current->position.x + 0.5;
+ v.y = current->position.y + 0.5;
+ array_push(to_fill, (uint8_t*)&v);
+ }
+
+ struct path_node *prev = current;
+ current = current->parent;
+ }
+
+ if (to_fill) {
+ array_remove_at(to_fill, to_fill->length-1);
+ //printf("PATHFINDING TOOK: %fms\n", (platform_get_time(TIME_PROCESS, TIME_MS)-timestamp)/1000.0f);
+ }
+ mutex_unlock(&request->mutex);
+
+ list_destroy(&open_array);
+ list_destroy(&closed_array);
+
+ destroy_allocator(&al);
+
+ return true;
+ }
+
+ vec2 adjecent[8] = { {0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1} };
+
+ list children = list_create(sizeof(struct path_node), &al);
+
+ // Generate children
+ for (s32 i = 0; i < 8; i++)
+ {
+ //Get node position
+ vec2f node_position;
+ node_position.x = current_node->position.x + adjecent[i].x;
+ node_position.y = current_node->position.y + adjecent[i].y;
+
+ //Make sure within range
+ if (!is_in_bounds(node_position.x, node_position.y) || !can_walk_at(node_position.x, node_position.y)) continue;
+
+ // if diagonal move, check if not cutting off building
+ if (i >= 4) {
+ if (i == 4) // top left
+ {
+ if (!can_walk_at(node_position.x+1, node_position.y)) continue;
+ if (!can_walk_at(node_position.x, node_position.y+1)) continue;
+ }
+ if (i == 5) // down left
+ {
+ if (!can_walk_at(node_position.x+1, node_position.y)) continue;
+ if (!can_walk_at(node_position.x, node_position.y-1)) continue;
+ }
+ if (i == 6) // top right
+ {
+ if (!can_walk_at(node_position.x-1, node_position.y)) continue;
+ if (!can_walk_at(node_position.x, node_position.y+1)) continue;
+ }
+ if (i == 7) // down right
+ {
+ if (!can_walk_at(node_position.x-1, node_position.y)) continue;
+ if (!can_walk_at(node_position.x, node_position.y-1)) continue;
+ }
+ }
+
+ // Create new node
+ struct path_node *new_node = allocator_alloc(&al, sizeof(struct path_node));
+ new_node->g = 0;
+ new_node->h = 0;
+ new_node->f = 0;
+ new_node->parent = current_node;
+ new_node->position = node_position;
+
+ if (node_position.x == current_node->position.x &&
+ node_position.y == current_node->position.y)
+ continue;
+
+ // Child is on the closed array
+ for (s32 c = 0; c < closed_array.count; c++)
+ {
+ struct path_node *closed_child = list_at(&closed_array, c);
+
+ if (closed_child->position.x == new_node->position.x &&
+ closed_child->position.y == new_node->position.y)
+ {
+ goto skip_adjecent;
+ }
+ }
+
+ list_push(&children,(uint8_t*)new_node);
+
+ skip_adjecent:;
+ }
+
+ // Loop through children
+ for (s32 i = 0; i < children.count; i++)
+ {
+ struct path_node *child = list_at(&children, i);
+
+ // Child is on the closed array
+ for (s32 c = 0; c < closed_array.count; c++)
+ {
+ struct path_node *closed_child = list_at(&closed_array, c);
+
+ if (closed_child->position.x == child->position.x &&
+ closed_child->position.y == child->position.y)
+ {
+ goto skip_child;
+ }
+ }
+
+ // Create the f, g, and h values
+ child->g = current_node->g + 1;
+ child->h = distance_between(child->position, end_pos);
+ child->f = child->g + child->h;
+
+ // Child is already in the open array
+ for (s32 c = 0; c < open_array.count; c++)
+ {
+ struct path_node *open_child = list_at(&open_array, c);
+
+ if ((open_child->position.x == child->position.x &&
+ open_child->position.y == child->position.y) &&
+ child->g >= open_child->g)
+ {
+ goto skip_child;
+ }
+ }
+
+ // Add the child to the open array
+ list_push(&open_array, (uint8_t*)child);
+
+ skip_child:;
+ }
+
+ list_remove_at(&open_array, current_index);
+ }
+
+ destroy_allocator(&al);
+ return false;
+}
+
+vec2f get_open_tile_next_to_target(float x, float y)
+{
+ vec2f adjecent[8] = { {0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1} };
+
+ vec2f v_s;
+ v_s.x = x;
+ v_s.y = y;
+
+ s32 closest_index = -1;
+ s32 closest_dist = 999999;
+ vec2f v;
+ for (s32 i = 0; i < 8; i++)
+ {
+ if (can_walk_at(x + adjecent[i].x, y + adjecent[i].y))
+ {
+ vec2f vv;
+ vv.x = x + adjecent[i].x;
+ vv.y = y + adjecent[i].y;
+
+ s32 dist = distance_between(v_s, vv);
+ if (dist < closest_dist)
+ {
+ v = vv;
+ closest_dist = dist;
+ closest_index = i;
+ }
+ }
+ }
+
+ if (closest_index != -1)
+ {
+ return v;
+ }
+
+ v.x = -1;
+ v.y = -1;
+ return v;
+}
+
+void pathfinding_init()
+{
+ global_pathfinding_queue = array_create(sizeof(pathfinding_request));
+ array_reserve(&global_pathfinding_queue, 1000);
+}
+
+void pathfinding_destroy()
+{
+ array_destroy(&global_pathfinding_queue);
+}
+
+void* pathfinding_thread(void *args)
+{
+ while(1)
+ {
+ mutex_lock(&global_pathfinding_queue.mutex);
+ if (global_pathfinding_queue.length)
+ {
+ pathfinding_request* request = *(pathfinding_request**)array_at(&global_pathfinding_queue, 0);
+ array_remove_at(&global_pathfinding_queue, 0);
+ mutex_unlock(&global_pathfinding_queue.mutex);
+ find_path_to(request->start, request->end, request->to_fill, request);
+ }
+ else
+ {
+ mutex_unlock(&global_pathfinding_queue.mutex);
+ continue;
+ }
+
+ //thread_sleep(100);
+ }
+
+ return 0;
+}
+
+void make_pathfinding_request(vec2f start, vec2f end, array *to_fill, pathfinding_request *request)
+{
+ start.x = (int)start.x;
+ start.y = (int)start.y;
+ end.x = (int)end.x;
+ end.y = (int)end.y;
+
+ mutex_lock(&global_pathfinding_queue.mutex);
+ for (int i = 0; i < global_pathfinding_queue.length; i++) {
+ pathfinding_request* ereq = *(pathfinding_request**)array_at(&global_pathfinding_queue, i);
+ if (request == ereq) {
+ ereq->start = start;
+ ereq->end = end;
+ ereq->timestamp = platform_get_time(TIME_PROCESS, TIME_MS);
+ mutex_unlock(&global_pathfinding_queue.mutex);
+ return;
+ }
+ }
+
+ request->start = start;
+ request->end = end;
+ request->to_fill = to_fill;
+
+ request->timestamp = 0;
+ array_push(&global_pathfinding_queue, (uint8_t*)&request);
+ mutex_unlock(&global_pathfinding_queue.mutex);
+} \ No newline at end of file
diff --git a/src/players.c b/src/players.c
new file mode 100644
index 0000000..132a467
--- /dev/null
+++ b/src/players.c
@@ -0,0 +1,168 @@
+#include "../include/players.h"
+
+float get_bullet_size_in_tile(platform_window* window) {
+ return 1 / 8.0f;
+}
+
+float get_bullet_size(platform_window* window) {
+ return get_tile_width(window) * get_bullet_size_in_tile(window);
+}
+
+float get_player_size_in_tile() {
+ return 0.5;
+}
+
+float get_player_size(platform_window* window) {
+ int player_size = get_tile_width(window) * get_player_size_in_tile();
+}
+
+void spawn_player(int id) {
+ for (int i = 0; i < max_players; i++) {
+ if (players[i].active) continue;
+ players[i].active = true;
+ players[i].sec_since_last_shot = 10.0f;
+ players[i].playerx = 3;
+ players[i].playery = 3;
+ players[i].gunx = 0.0f;
+ players[i].guny = 0.0f;
+ players[i].gun_height = 0.0f;
+ players[i].id = id;
+ return;
+ }
+}
+
+player get_player_by_id(int id) {
+ for (int i = 0; i < max_players; i++) {
+ if (!players[i].active) continue;
+ if (players[i].id == id) return players[i];
+ }
+ return (player){-1};
+}
+
+object check_if_player_collided_with_object(platform_window* window, player p) {
+ map_info info = get_map_info(window);
+ float player_size = get_player_size(window);
+
+ for (int i = 0; i < max_objects; i++) {
+ object o = objects[i];
+ if (!o.active) continue;
+
+ box box = get_box_of_object(window, o);
+ float x_to_check = p.playerx;
+ float player_size_in_tile_px = player_size / (float)info.tile_width;
+ float y_to_check = p.playery + player_size_in_tile_px;
+
+ if (x_to_check+player_size_in_tile_px >= o.position.x && x_to_check <= o.position.x+o.size.x
+ && y_to_check >= o.position.y && y_to_check <= o.position.y+o.size.y) {
+ return o;
+ }
+ }
+
+ return (object){0};
+}
+
+int get_my_player_index() {
+ for (int i = 0; i < max_players; i++) {
+ if (!players[i].active) continue;
+ if (players[i].id == my_id) return i;
+ }
+ return -1;
+}
+
+void take_player_input(platform_window* window) {
+ float speed = 0.1f;
+ float pad_between_player_and_obj = 0.01f;
+
+ int my_index = get_my_player_index();
+ if (my_index == -1) return;
+
+ float old_x = players[my_index].playerx;
+ float old_y = players[my_index].playery;
+
+ if (keyboard_is_key_down(KEY_W)) {
+ float newy = players[my_index].playery - speed;
+ if (is_in_bounds(players[my_index].playerx, newy)) {
+ players[my_index].playery = newy;
+ object o = check_if_player_collided_with_object(window, players[my_index]);
+ if (o.active) players[my_index].playery = o.position.y+o.size.y - get_player_size_in_tile() + pad_between_player_and_obj;
+ }
+ }
+
+ if (keyboard_is_key_down(KEY_S)) {
+ float newy = players[my_index].playery + speed;
+ if (is_in_bounds(players[my_index].playerx, newy)) {
+ players[my_index].playery = newy;
+ object o = check_if_player_collided_with_object(window, players[my_index]);
+ if (o.active) players[my_index].playery = o.position.y - get_player_size_in_tile() - pad_between_player_and_obj;
+ }
+ }
+
+ if (keyboard_is_key_down(KEY_A)) {
+ float newx = players[my_index].playerx - speed;
+ if (is_in_bounds(newx, players[my_index].playery)) {
+ players[my_index].playerx = newx;
+ object o = check_if_player_collided_with_object(window, players[my_index]);
+ if (o.active) players[my_index].playerx = o.position.x+o.size.x + pad_between_player_and_obj;
+ }
+ }
+
+ if (keyboard_is_key_down(KEY_D)) {
+ float newx = players[my_index].playerx + speed;
+ if (is_in_bounds(newx, players[my_index].playery)) {
+ players[my_index].playerx = newx;
+ object o = check_if_player_collided_with_object(window, players[my_index]);
+ if (o.active) players[my_index].playerx = o.position.x-get_player_size_in_tile() - pad_between_player_and_obj;
+ }
+ }
+}
+
+void draw_players_at_tile(platform_window* window, int x, int y) {
+ for (int i = 0; i < max_players; i++) {
+ if (!players[i].active) continue;
+ if ((int)players[i].playerx != x || (int)(players[i].playery+get_player_size_in_tile()) != y) continue;
+
+ int size = get_tile_width(window) / 2;
+ map_info info = get_map_info(window);
+ float height = get_height_of_tile_under_coords(window, players[i].playerx, players[i].playery);
+
+ take_player_input(window);
+
+ players[i].sec_since_last_shot += update_delta;
+ float bullets_per_sec = 10;
+ float time_between_bullets = 1.0f/bullets_per_sec;
+ if (is_left_down()) {
+ if (players[i].sec_since_last_shot > time_between_bullets) {
+ int ix = get_my_player_index();
+ if (ix != -1) {
+ for (int i = 0; i < 3; i++) {
+ shoot(window, players[ix]);
+ players[i].sec_since_last_shot = 0.0f;
+ }
+ }
+ }
+ }
+
+ float player_render_x = players[i].playerx*info.tile_width + (players[i].playery*info.px_incline);
+ float player_render_y = players[i].playery*info.tile_height - (height*info.px_raised_per_h);
+ renderer->render_rectangle(player_render_x, player_render_y, size, size, rgb(200,150,120));
+
+ float dirx = (_global_mouse.x - (window->width/2));
+ float diry = (_global_mouse.y - (window->height/2));
+ double length = sqrt(dirx * dirx + diry * diry);
+ dirx /= length;
+ diry /= length;
+
+ players[i].gunx = players[i].playerx + (get_player_size_in_tile()/2) + dirx/2;
+ players[i].guny = players[i].playery + (get_player_size_in_tile()/2) + diry/2;
+ players[i].gun_height = height+0.5;
+ float gun_render_x = players[i].gunx*info.tile_width + (players[i].guny*info.px_incline);
+ float gun_render_y = players[i].guny*info.tile_height - (players[i].gun_height*info.px_raised_per_h);
+
+ renderer->render_rectangle(gun_render_x, gun_render_y, size/4, size/4, rgb(20,255,20));
+
+ if (players[i].id == my_id) {
+ _global_camera.x = -(window->width / 2) + player_render_x;
+ _global_camera.y = -(window->height / 2) + player_render_y;
+ }
+ }
+}
diff --git a/src/zombies.c b/src/zombies.c
new file mode 100644
index 0000000..4bbcf40
--- /dev/null
+++ b/src/zombies.c
@@ -0,0 +1,175 @@
+#include "../include/zombies.h"
+
+static player get_closest_player_to_tile(int x, int y) {
+ float best_length = 99999;
+ int best_index = -1;
+
+ for (int i = 0; i < max_players; i++) {
+ if (!players[i].active) continue;
+ float dirx = (players[i].playerx - x);
+ float diry = (players[i].playery - y);
+ double length = sqrt(dirx * dirx + diry * diry);
+
+ if (length < best_length) {
+ best_length = length;
+ best_index = i;
+ }
+ }
+ if (best_index == -1) {
+ return (player){-1};
+ }
+ else {
+ return players[best_index];
+ }
+}
+
+void spawn_zombie(int x, int y) {
+ for (int i = 0; i < max_zombies; i++) {
+ zombie o = zombies[i];
+ if (o.alive) continue;
+
+ zombies[i].path = array_create(sizeof(vec2f));
+ zombies[i].next_path = array_create(sizeof(vec2f));
+ zombies[i].alive = true;
+ zombies[i].health = 100.0f;
+ zombies[i].position = (vec3f){x,y, 0};
+ zombies[i].size = (vec3f){0.4, 0.4, 1};
+ zombies[i].time_since_last_path = 0.0f;
+ zombies[i].request.mutex = mutex_create();
+
+ player closest_player = get_closest_player_to_tile(x, y);
+
+ make_pathfinding_request((vec2f){x,y}, (vec2f){closest_player.playerx, closest_player.playery}, &zombies[i].path, &zombies[i].request);
+ break;
+ }
+}
+
+void draw_spawners(platform_window* window) {
+ map_info info = get_map_info(window);
+
+ for (int x = 0; x < 1; x++) {
+ spawner spawner = spawner_tiles[x];
+ int render_x = (info.tile_width * spawner.position.x) + (info.px_incline * spawner.position.y);
+ int render_y = info.tile_height * spawner.position.y;
+
+ tile tile = map_loaded[spawner.position.y][spawner.position.x];
+ renderer->render_quad(
+ tile.tl.x, tile.tl.y,
+ tile.bl.x, tile.bl.y,
+ tile.br.x, tile.br.y,
+ tile.tr.x, tile.tr.y,
+ rgb(100, 150, 50));
+
+ spawner_tiles[x].sec_since_last_spawn += update_delta;
+ if (spawner_tiles[x].sec_since_last_spawn >= 2.0f) {
+ spawn_zombie(spawner.position.x, spawner.position.y);
+ spawner_tiles[x].sec_since_last_spawn = 0;
+ }
+
+ renderer->render_line(tile.tl.x, tile.tl.y, tile.tr.x, tile.tr.y, 1, rgb(0,255,255)); // top
+ renderer->render_line(tile.tl.x, tile.tl.y, tile.bl.x, tile.bl.y, 1, rgb(0,255,255)); // left
+ renderer->render_line(tile.tr.x, tile.tr.y, tile.br.x, tile.br.y, 1, rgb(0,255,255)); // right
+ renderer->render_line(tile.bl.x, tile.bl.y, tile.br.x, tile.br.y, 1, rgb(0,255,255)); // bottom
+ }
+}
+
+static void draw_path_of_zombie(platform_window* window, zombie o) {
+ map_info info = get_map_info(window);
+ vec2f prev_pos = (vec2f){o.position.x, o.position.y};
+
+ for (int i = 0; i < o.path.length; i++) {
+ vec2f* pos = array_at(&o.path, o.path.length-1-i);
+
+ int render_x = (info.tile_width * prev_pos.x) + (info.px_incline * prev_pos.y);
+ int render_y = info.tile_height * prev_pos.y;
+
+ int render_x2 = (info.tile_width * pos->x) + (info.px_incline * pos->y);
+ int render_y2 = info.tile_height * pos->y;
+
+ prev_pos = *pos;
+ if (i == 0) {
+ renderer->render_line(render_x, render_y, render_x2, render_y2, 2, rgb(0,255,255));
+ continue;
+ }
+
+ renderer->render_line(render_x, render_y, render_x2, render_y2, 2, rgb(0,0,255));
+ }
+}
+
+static vec2f get_direction_to_next_tile(zombie o) {
+ vec2f dest = (vec2f){o.position.x, o.position.y};
+ if (o.path.length > 0) {
+ dest = *(vec2f*)array_at(&o.path, o.path.length-1);
+ }
+
+ float dirx = (dest.x - o.position.x);
+ float diry = (dest.y - o.position.y);
+ if (dirx == 0 && diry == 0) return (vec2f){0,0};
+ double length = sqrt(dirx * dirx + diry * diry);
+ dirx /= length;
+ diry /= length;
+ return (vec2f){dirx, diry};
+}
+
+static bool is_within_next_tile(zombie o) {
+ if (o.path.length > 0) {
+ vec2f dest = *(vec2f*)array_at(&o.path, o.path.length-1);
+ if (fabs(o.position.x - dest.x) < 0.05f && fabs(o.position.y - dest.y) < 0.05f) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void draw_zombies_at_tile(platform_window* window, int x, int y) {
+ map_info info = get_map_info(window);
+ float speed = 0.05f;
+
+ for (int i = 0; i < max_zombies; i++) {
+ zombie o = zombies[i];
+ if (!o.alive) continue;
+
+ if ((int)o.position.x != x || (int)ceil(o.position.y) != y) continue;
+
+ zombies[i].time_since_last_path += update_delta;
+ if (zombies[i].time_since_last_path > 0.05f) {
+ player closest_player = get_closest_player_to_tile(x, y);
+ make_pathfinding_request((vec2f){o.position.x,o.position.y},
+ (vec2f){closest_player.playerx, closest_player.playery + get_player_size_in_tile()},
+ &zombies[i].next_path, &zombies[i].request);
+ zombies[i].time_since_last_path = 0;
+ }
+ else {
+ if (mutex_trylock(&zombies[i].request.mutex))
+ {
+ if (zombies[i].request.to_fill->length) {
+ array_destroy(&zombies[i].path);
+ zombies[i].path = array_copy(&zombies[i].next_path);
+ array_clear(&zombies[i].next_path);
+ }
+ }
+ mutex_unlock(&zombies[i].request.mutex);
+ }
+
+ vec2f dir = get_direction_to_next_tile(o);
+
+ float height = get_height_of_tile_under_coords(window, zombies[i].position.x, zombies[i].position.y);
+ zombies[i].position.x += dir.x*speed;
+ zombies[i].position.y += dir.y*speed;
+ zombies[i].position.z = height;
+
+ if (is_within_next_tile(zombies[i])) {
+ array_remove_at(&zombies[i].path, zombies[i].path.length-1);
+ }
+
+ box box = get_render_box_of_square(window, (vec3f){o.position.x, o.position.y, height}, o.size);
+ render_quad_with_outline(box.tl_b, box.tr_b, box.bl_b, box.br_b);
+ render_quad_with_outline(box.tl_u, box.tr_u, box.bl_u, box.br_u);
+ render_quad_with_outline(box.tl_u, box.tl_b, box.bl_u, box.bl_b);
+ render_quad_with_outline(box.bl_u, box.br_u, box.bl_b, box.br_b);
+
+ draw_path_of_zombie(window, o);
+ }
+}
+