From 937edea9599718e959f8ed135e97c68728855975 Mon Sep 17 00:00:00 2001 From: quou Date: Sat, 6 May 2023 12:22:50 +1000 Subject: Add solid environment collisions. --- Makefile | 1 + collision_system.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++---- components.h | 4 +- enemy.c | 3 +- game.c | 3 +- map.c | 40 ++++++++++++- map.h | 4 +- player.c | 24 +++++--- player.h | 1 + solid.c | 27 +++++++++ solid.h | 8 +++ 11 files changed, 261 insertions(+), 26 deletions(-) create mode 100644 solid.c create mode 100644 solid.h diff --git a/Makefile b/Makefile index c4c00e2..25e0cec 100644 --- a/Makefile +++ b/Makefile @@ -101,6 +101,7 @@ sources = \ player.c \ rect.c \ render.c \ + solid.c \ sprite.c \ sprite_system.c \ standard.c \ diff --git a/collision_system.c b/collision_system.c index 68e3b09..2a1ac03 100644 --- a/collision_system.c +++ b/collision_system.c @@ -1,11 +1,43 @@ #ifndef systems_h #define systems_h +#include + +#include "components.h" #include "config.h" #include "fx.h" #include "rect.h" +#include "standard.h" #include "world.h" +#ifdef DEBUG +#include "error.h" +#include "platform.h" +#endif + +typedef enum { + collision_side_left = 0, + collision_side_right, + collision_side_top, + collision_side_bottom +} Collision_Side; + +static Collision_Side invert_side(Collision_Side side) { + switch (side) { + case collision_side_left: return collision_side_right; + case collision_side_right: return collision_side_left; + case collision_side_bottom: return collision_side_top; + case collision_side_top: return collision_side_bottom; + } + +#if DEBUG + platform_err("Invalid collision side.\n"); + platform_abort(error_gameplay_error); +#endif + + return -1; +} + static void handle_bullet_vs_player( World* world, Entity bullet, @@ -32,7 +64,66 @@ static void handle_bullet_vs_enemy( destroy_entity(world, bullet); } -static void handle(World* world, Entity a, Entity b) { +static void handle_bullet_vs_solid( + World* world, + Entity bullet, + Entity solid +) { + const CPosition* pos; + unsigned bbits; + + bbits = world->bitmask[bullet]; + + if (bbits & ctype_enemy_bullet) { + pos = &world->positions[bullet]; + new_enemy_bullet_explosion(world, pos->x, pos->y); + } + + destroy_entity(world, bullet); +} + +static void handle_moveable_vs_solid( + World* world, + Entity moveable, + Entity solid, + Collision_Side side +) { + const CCollider* scol, * mcol; + const CPosition* spos; + CPosition* mpos; + + scol = &world->colliders[solid]; + mcol = &world->colliders[moveable]; + spos = &world->positions[solid]; + mpos = &world->positions[moveable]; + + switch (side) { + case collision_side_top: + mpos->y = spos->y + scol->y + scol->h - mcol->y; + return; + case collision_side_bottom: + mpos->y = (spos->y + scol->y) - (mcol->y + mcol->h); + return; + case collision_side_left: + mpos->x = spos->x + scol->x + scol->w - mcol->x; + return; + case collision_side_right: + mpos->x = (spos->x + scol->x) - (mcol->x + mcol->w); + return; + } + +#if DEBUG + platform_err("Invalid collision side.\n"); + platform_abort(error_gameplay_error); +#endif +} + +static void handle( + World* world, + Entity a, + Entity b, + Collision_Side side +) { unsigned ab, bb; ab = world->bitmask[a]; @@ -42,7 +133,6 @@ static void handle(World* world, Entity a, Entity b) { handle_bullet_vs_player(world, a, b); return; } - if ((ab & ctype_player) && (bb & ctype_enemy_bullet)) { handle_bullet_vs_player(world, b, a); return; @@ -52,18 +142,39 @@ static void handle(World* world, Entity a, Entity b) { handle_bullet_vs_enemy(world, a, b); return; } - if ((ab & ctype_enemy) && (bb & ctype_player_bullet)) { handle_bullet_vs_enemy(world, b, a); return; } + + if ((ab & ctype_bullet) && (bb & ctype_solid)) { + handle_bullet_vs_solid(world, a, b); + return; + } + if ((ab & ctype_solid) && (bb & ctype_bullet)) { + handle_bullet_vs_solid(world, b, a); + return; + } + + if ((ab & ctype_moveable) && (bb & ctype_solid)) { + handle_moveable_vs_solid(world, a, b, side); + return; + } + if ((ab & ctype_solid) && (bb & ctype_moveable)) { + side = invert_side(side); + handle_moveable_vs_solid(world, b, a, side); + return; + } } void collision_system(World* world) { - int i, j, p0x, p0y; + int i, j, k, s, s2, p0x, p0y, r, l, t, b; + int ax, ay, aw, ah, bx, by, bw, bh; + int overlap[4]; unsigned bits; const CPosition* pos0, * pos1; const CCollider* col0, * col1; + Collision_Side side; for (i = 0; i < world->entity_count; i++) { bits = world->bitmask[i]; @@ -87,17 +198,52 @@ void collision_system(World* world) { pos1 = &world->positions[j]; col1 = &world->colliders[j]; + ax = p0x; + ay = p0y; + aw = col0->w; + ah = col0->h; + bx = pos1->x + col1->x; + by = pos1->y + col1->y; + bw = col1->w; + bh = col1->h; + + /* Can you tell I made this while I had a migraine? */ if (rects_overlap2( - p0x, - p0y, - col0->w, - col0->h, - pos1->x + col1->x, - pos1->y + col1->y, - col1->w, - col1->h + ax, + ay, + aw, + ah, + bx, + by, + bw, + bh )) { - handle(world, i, j); + r = (ax + aw) - bx; + l = (bx + bw) - ax; + t = (by + bh) - ay; + b = (ay + ah) - by; + + overlap[0] = r; + overlap[1] = l; + overlap[2] = t; + overlap[3] = b; + s = INT32_MAX; + for (k = 0; k < 4; k++) { + s2 = overlap[k]; + if (s2 < s) { s = s2; } + } + + if (s == absolute(r)) { + side = collision_side_right; + } else if (s == absolute(l)) { + side = collision_side_left; + } else if (s == absolute(b)) { + side = collision_side_bottom; + } else if (s == absolute(t)) { + side = collision_side_top; + } + + handle(world, i, j, side); } } } diff --git a/components.h b/components.h index f23b35e..63a62c5 100644 --- a/components.h +++ b/components.h @@ -55,7 +55,9 @@ typedef enum { ctype_debris = 1 << 8, ctype_collider = 1 << 9, ctype_player = 1 << 10, - ctype_destroy_on_anim_done = 1 << 11 + ctype_destroy_on_anim_done = 1 << 11, + ctype_solid = 1 << 12, + ctype_moveable = 1 << 13 } CType; #endif diff --git a/enemy.c b/enemy.c index 6729862..8b2642f 100644 --- a/enemy.c +++ b/enemy.c @@ -20,7 +20,8 @@ Entity new_skull(World* world, int x, int y) { ctype_sprite | ctype_enemy | ctype_skull | - ctype_collider + ctype_collider | + ctype_moveable ); pos = &world->positions[e]; sprite = &world->sprites[e]; diff --git a/game.c b/game.c index 00e4265..b3d40fb 100644 --- a/game.c +++ b/game.c @@ -87,7 +87,7 @@ static void menu_deinit(Game* game) { static void gameplay_init(Game* game) { init_world(&game->world); - init_map(&game->world.map); + init_map(&game->world.map, &game->world); init_player(&game->world.player, &game->world); new_skull(&game->world, 0, 0); } @@ -103,6 +103,7 @@ static void gameplay_update(Game* game) { collision_system(&game->world); animation_system(&game->world); render_map(&game->world.map, cx, cy); + update_camera(&game->world.player, &game->world); sprite_system(&game->world); } diff --git a/map.c b/map.c index 2b10abd..d8b6e1c 100644 --- a/map.c +++ b/map.c @@ -1,8 +1,9 @@ #include "map.h" #include "sprite.h" #include "standard.h" +#include "solid.h" -void init_map(Map* map) { +void init_map(Map* map, World* world) { const Sprite* floor, * bricks; const Bitmap* fbmp, * bbmp; Bitmap bitmap; @@ -67,6 +68,36 @@ void init_map(Map* map) { ); } + /* Bounds */ + new_solid( + world, + 0, + 0, + (map_width * map_tile_size) << fbits, + map_tile_size << fbits + ); + new_solid( + world, + 0, + ((map_height - 1) * map_tile_size) << fbits, + (map_width * map_tile_size) << fbits, + map_tile_size << fbits + ); + new_solid( + world, + 0, + 0, + map_tile_size << fbits, + (map_height * map_tile_size) << fbits + ); + new_solid( + world, + ((map_width - 1) * map_tile_size) << fbits, + 0, + map_tile_size << fbits, + (map_height * map_tile_size) << fbits + ); + for (i = 0; i < 20; i++) { x = rand_range(0, map_width - 1) * map_tile_size; y = rand_range(0, map_height - 1) * map_tile_size; @@ -77,6 +108,13 @@ void init_map(Map* map) { y, &bricks->rect ); + new_solid( + world, + x << fbits, + y << fbits, + map_tile_size << fbits, + map_tile_size << fbits + ); } } diff --git a/map.h b/map.h index 35e97f2..5b79195 100644 --- a/map.h +++ b/map.h @@ -4,6 +4,8 @@ #include "game_config.h" #include "render.h" +struct World; + typedef struct { Colour pixels[ map_width * @@ -13,7 +15,7 @@ typedef struct { ]; } Map; -void init_map(Map* map); +void init_map(Map* map, struct World* world); void render_map(Map* map, int cx, int cy); #endif diff --git a/player.c b/player.c index d79beed..4a82cff 100644 --- a/player.c +++ b/player.c @@ -26,7 +26,8 @@ void init_player(Player* player, World* world) { ctype_position | ctype_animated | ctype_collider | - ctype_player + ctype_player | + ctype_moveable ); pos = &world->positions[e]; sprite = &world->sprites[e]; @@ -55,7 +56,7 @@ void init_player(Player* player, World* world) { } void update_player(Player* player, World* world) { - int dx, dy, cbx, cby; + int dx, dy; int face, moving = 0; Entity e; CPosition* pos; @@ -132,6 +133,19 @@ void update_player(Player* player, World* world) { game_change_state(&game, game_state_dead); } + player->shoot_countdown--; +} + +void player_take_damage(Player* player, int dmg) { + player->hp -= dmg; +} + +void update_camera(Player* player, World* world) { + const CPosition* pos; + int cbx, cby; + + pos = &world->positions[player->entity]; + world->cam_x = pos->x - ((renderer_w / 2) << fbits); world->cam_y = pos->y - ((renderer_h / 2) << fbits); @@ -151,10 +165,4 @@ void update_player(Player* player, World* world) { if (world->cam_y > cby) { world->cam_y = cby; } - - player->shoot_countdown--; -} - -void player_take_damage(Player* player, int dmg) { - player->hp -= dmg; } diff --git a/player.h b/player.h index 0e84b9e..0fb3e4d 100644 --- a/player.h +++ b/player.h @@ -15,5 +15,6 @@ typedef struct { void init_player(Player* player, struct World* world); void update_player(Player* player, struct World* world); void player_take_damage(Player* player, int dmg); +void update_camera(Player* player, struct World* world); #endif diff --git a/solid.c b/solid.c new file mode 100644 index 0000000..40c3656 --- /dev/null +++ b/solid.c @@ -0,0 +1,27 @@ +#include "solid.h" + +Entity new_solid(World* world, int x, int y, int w, int h) { + Entity e; + CCollider* col; + CPosition* pos; + + e = new_entity(world); + add_components( + world, + e, + ctype_position | + ctype_collider | + ctype_solid + ); + pos = &world->positions[e]; + col = &world->colliders[e]; + + pos->x = x; + pos->y = y; + col->x = 0; + col->y = 0; + col->w = w; + col->h = h; + + return e; +} diff --git a/solid.h b/solid.h new file mode 100644 index 0000000..e861e6d --- /dev/null +++ b/solid.h @@ -0,0 +1,8 @@ +#ifndef solid_h +#define solid_h + +#include "world.h" + +Entity new_solid(World* world, int x, int y, int w, int h); + +#endif -- cgit v1.2.3-54-g00ecf