#ifndef systems_h #define systems_h #include #include "components.h" #include "config.h" #include "fx.h" #include "game_config.h" #include "rect.h" #include "sound.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, Entity player ) { const CPosition* pos; pos = &world->positions[bullet]; player_take_damage(world, &world->player, 1); destroy_entity(world, bullet); new_enemy_bullet_explosion(world, pos->x, pos->y); } static void handle_bullet_vs_enemy( World* world, Entity bullet, Entity enemy ) { CEnemy* e; const CPosition* pos; pos = &world->positions[bullet]; e = &world->enemies[enemy]; e->being_damaged = enemy_hit_frames; e->hp--; destroy_entity(world, bullet); new_player_bullet_explosion(world, pos->x, pos->y); play_beep(50, 500); } static void handle_bullet_vs_solid( World* world, Entity bullet, Entity solid ) { const CPosition* pos; unsigned bbits; bbits = world->bitmask[bullet]; pos = &world->positions[bullet]; if (bbits & ctype_enemy_bullet) { new_enemy_bullet_explosion(world, pos->x, pos->y); } else if (bbits & ctype_player_bullet) { new_player_bullet_explosion(world, pos->x, pos->y); } play_beep(50, 100); 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]; bb = world->bitmask[b]; if ((ab & ctype_enemy_bullet) && (bb & ctype_player)) { handle_bullet_vs_player(world, a, b); return; } if ((ab & ctype_player) && (bb & ctype_enemy_bullet)) { handle_bullet_vs_player(world, b, a); return; } if ((ab & ctype_player_bullet) && (bb & ctype_enemy)) { 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, k, s, s2, p0x, p0y, r, l, t, b, dx, dy, d; int ax, ay, aw, ah, bx, by, bw, bh; int overlap[4]; unsigned bits; CPosition* pos0, * pos1; const CCollider* col0, * col1; Collision_Side side; for (i = 0; i < world->entity_count; i++) { bits = world->bitmask[i]; if ( !(bits & ctype_collider) || !(bits & ctype_position) ) { continue; } pos0 = &world->positions[i]; col0 = &world->colliders[i]; p0x = pos0->x + col0->x; p0y = pos0->y + col0->y; for (j = i + 1; j < world->entity_count; j++) { bits = world->bitmask[j]; if ( !(bits & ctype_collider) || !(bits & ctype_position) ) { continue; } 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( ax, ay, aw, ah, bx, by, bw, bh )) { 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); } } } for (i = 0; i < world->entity_count; i++) { bits = world->bitmask[i]; if ( !(bits & ctype_enemy) || !(bits & ctype_position) ) { continue; } pos0 = &world->positions[i]; for (j = i + 1; j < world->entity_count; j++) { bits = world->bitmask[j]; if ( !(bits & ctype_enemy) || !(bits & ctype_position) ) { continue; } pos1 = &world->positions[j]; dx = (pos0->x - pos1->x) >> 4; dy = (pos0->y - pos1->y) >> 4; d = ((dx * dx) + (dy * dy)) >> fbits; if (d < enemy_min_distance) { pos0->x += dx; pos1->x -= dx; pos0->y += dy; pos1->y -= dy; } } } } #endif