#include #include #include #include #include #include "raylib.h" #include "Sprite2D.h" Texture2D background; Texture2D entity_texture; Texture2D player_texture; Texture2D enemy_texture; Texture2D obstacle_texture; Sound background_music; const int GAME_WIDTH = 800; const int GAME_HEIGHT = GAME_WIDTH * (3.0f / 5.0f); const int SIDE_WALK1 = 40; const int SIDE_WALK1_W = 50; const int SIDE_WALK2_W = SIDE_WALK1_W; const int SIDE_WALK2 = GAME_HEIGHT - SIDE_WALK2_W; const int AGRO_BUFFER_DISTANCE = 10; const double RIGHT_MARGIN = (3.0f / 5.0f); float v; #include "util.h" #include "my_random.hpp" #include "main_menu.h" #include "Effect.hpp" #include "Entity.hpp" #include "death_manager.h" using namespace std; float absolute_x; Spawner enemy_spawner = (Spawner) { .min_distance = 400, .denominator = 10, }; Spawner obstacle_spawner = (Spawner) { .min_distance = 1200, .denominator = 1000, }; const float max_v = 3.4f; list enemies; vector ordered_enemies; Obstacle obstacle; const float MOVEMENT_SPEED = 3.2; float move_player(bool left, bool right, bool up, bool down) { static const float mod = MOVEMENT_SPEED; float x_mod = 0; float y_mod = 0; if (left) { x_mod -= mod; } if (right) { x_mod += mod; } if (up) { y_mod -= mod; } if (down) { y_mod += mod; } if (player.x + x_mod > 0 && player.x + x_mod < GAME_WIDTH * RIGHT_MARGIN) { player.x += x_mod; } if (player.y + y_mod > SIDE_WALK1 && player.y < GAME_HEIGHT) { player.y += y_mod; } return x_mod; } void update_scroll(float mod) { if (mod > 0 && v + 0.01f < max_v) { v += 0.01f; } else if (v - 0.1f > 0.0f) { v -= 0.1f; } else { v = 0; } absolute_x += v*v; } int game_loop(void) { // Init enum { GAME_RUNNING, GAME_OVER, } game_state = GAME_RUNNING; double gameover_time; enemy_spawner.reset(); obstacle_spawner.reset(); reset_deaths(); enemies.clear(); ordered_enemies.clear(); obstacle.regen(); player.x = 100; player.y = 100; absolute_x = 0; v = 0.0f; // Work PlaySound(background_music); float mod = 0; while (!WindowShouldClose()) { input: if (game_state == GAME_OVER) { if ((GetTime() - gameover_time) > 0.5 && GetKeyPressed() != KEY_NULL) { return 0; } mod = 0; goto simulate; } mod = move_player( IsKeyDown(KEY_A) | IsKeyDown(KEY_LEFT) | IsKeyDown(KEY_H), IsKeyDown(KEY_D) | IsKeyDown(KEY_RIGHT) | IsKeyDown(KEY_L), IsKeyDown(KEY_W) | IsKeyDown(KEY_UP) | IsKeyDown(KEY_K), IsKeyDown(KEY_S) | IsKeyDown(KEY_DOWN) | IsKeyDown(KEY_J) ); simulate: // Map movement update_scroll(mod); // Player animation if (((int)absolute_x / 10) % 2) { player.frame = player.frame xor 0x01; } // Spawn if (enemy_spawner.blaze(absolute_x)) { enemies.emplace_back(); } if (obstacle_spawner.blaze(absolute_x)) { obstacle.regen(); } // Obstacle movement obstacle.x -= v*v + obstacle.speed; // Player death if (CheckCollisionRecs((Rectangle)obstacle, (Rectangle)player)) { add_death((Vector2) { player.x, player.y }); gameover_time = GetTime(); game_state = GAME_OVER; } // Enemy Death vector to_erase; for (auto e = enemies.begin(); e != enemies.end(); e++) { if (CheckCollisionRecs((Rectangle)*e, (Rectangle)obstacle)) { add_death((Vector2) { e->x, e->y }); to_erase.push_back(e); } } for (auto e : to_erase) { enemies.erase(e); } // Enemy movement ordered_enemies.clear(); for (auto &e : enemies) { ordered_enemies.push_back(&e); } sort(ordered_enemies.begin(), ordered_enemies.end(), [](Enemy * a, Enemy * b) { return distance(a->x, a->y, player.x, player.y) < distance(b->x, b->y, player.x, player.y); }); for (auto &e : ordered_enemies) { // non-agro if (not e->is_agrod) { e->x -= v*v; if (e->x - AGRO_BUFFER_DISTANCE < player.x) { e->is_agrod = true; } continue; } // murder player if (CheckCollisionRecs((Rectangle)*e, (Rectangle)player)) { add_death((Vector2) { player.x, player.y }); gameover_time = GetTime(); game_state = GAME_OVER; } // move Rectangle would_be_rect = (Rectangle)*e; float diff = player.y - e->y; would_be_rect.y += diff * 0.05f; float mod = -(v*v) + (MOVEMENT_SPEED*0.9f); if (e->x + mod > 0) { would_be_rect.x += mod; } would_be_rect.y += random_wiggle(64, 6); would_be_rect.y += -abs(random_wiggle(32, 3)); bool would_collide_x = false; bool would_collide_y = false; for (const auto &r : enemies) { if (would_collide_x && would_collide_y) { break; } if (e == &r) { continue; } if (CheckCollisionRecs( (Rectangle) { .x = would_be_rect.x, .y = e->y, .width = e->width, .height = e->height, }, (Rectangle)r ) ) { would_collide_x = true; } if (CheckCollisionRecs( (Rectangle) { .x = e->x, .y = would_be_rect.y, .width = e->width, .height = e->height, }, (Rectangle)r ) ) { would_collide_y = true; } } if (not would_collide_x) { e->x = would_be_rect.x; } if (not would_collide_y) { e->y = would_be_rect.y; } } update_deaths(); draw: BeginDrawing(); ClearBackground(RAYWHITE); // draw street int background_one_start = - (int)absolute_x % background.width; int background_two_start = background_one_start + background.width; DrawTexture(background, background_one_start, 0, WHITE); DrawTexture(background, background_two_start, 0, WHITE); display_deaths(); // draw entities obstacle.draw(); if (game_state == GAME_RUNNING) { player.draw(); } for (auto &e : enemies) { e.draw(); } #ifdef DEBUG for (int i = 0; i < ordered_enemies.size(); i++) { DrawText( TextFormat("%d", i), ordered_enemies[i]->x, ordered_enemies[i]->y, 20, BLUE ); } #endif // draw HUD #ifdef DEBUG DrawText(TextFormat("d: %f", absolute_x), 10, 15, 20, BLACK); DrawText(TextFormat("V: %f", v), 10, 40, 20, BLACK); #endif if (game_state == GAME_OVER) { DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Color{128, 128, 128, 128}); DrawText("Game Over", 100, 100, 40, BLACK); DrawText( TextFormat( "You lead the masses for %d meters.", ((int)absolute_x) / 10 ), 200, 200, 20, BLACK ); } EndDrawing(); } return 1; } int main(void) { // Init srand(time(NULL)); InitWindow(GAME_WIDTH, GAME_HEIGHT, "Masses"); InitAudioDevice(); Image image = LoadImage("resources/street.png"); ImageResize(&image, GAME_WIDTH, GAME_HEIGHT); background = LoadTextureFromImage(image); UnloadImage(image); player_texture = LoadTexture("resources/player.png"); player.sprite.texture = &player_texture; player.sprite.width = player.sprite.texture->width; player.sprite.height = player.sprite.texture->height / 2; entity_texture = LoadTexture("resources/entity.png"); enemy_texture = LoadTexture("resources/enemy.png"); obstacle_texture = LoadTexture("resources/obstackle.png"); background_music = LoadSound("resources/From_Nothing_To_Zero_-_(Sybreed_song_reversed).mp3"); SetTargetFPS(60); // Game main_menu(); // hang until initial user input while (not game_loop()) { ; } CloseWindow(); return 0; }