2024-12-21 14:08:37 +01:00

360 lines
9.2 KiB
C++

#include <math.h>
#include <time.h>
#include <vector>
#include <list>
#include <algorithm>
#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<Enemy> enemies;
vector<Enemy*> 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<decltype(enemies.begin())> 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;
}