init
This commit is contained in:
commit
5715f25bf2
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.gdb_history
|
||||||
|
*.out
|
14
Makefile
Normal file
14
Makefile
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
LDFLAGS += -lraylib -lGL -lm -lpthread -ldl -lrt -lX11
|
||||||
|
|
||||||
|
ifeq (${DEBUG}, 1)
|
||||||
|
CPPFLAGS += -DDEBUG
|
||||||
|
CFLAGS += -ggdb
|
||||||
|
endif
|
||||||
|
|
||||||
|
OUT := masses.out
|
||||||
|
|
||||||
|
${OUT}: source/main.cpp source/main_menu.cpp source/Sprite2D.c
|
||||||
|
g++ -o ${OUT} ${CPPFLAGS} ${CFLAGS} $+ ${LDFLAGS}
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm ${OUT}
|
10
README.md
Normal file
10
README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Masses
|
||||||
|
This is a dummy game for getting comfortable with raylib and trying out gamedev.
|
||||||
|
|
||||||
|
The player model is a reference to 'The Owl Who Was God' by James Thurber.
|
||||||
|
|
||||||
|
### Lacking:
|
||||||
|
+ the movement is a bit wacky
|
||||||
|
+ the "path finding" is buggy, but I like it that way
|
||||||
|
+ the """art""" is terrible
|
||||||
|
+ the music is copyrighted; i wanted wardrums speeding up based on the distance and player speed, but i had trouble finding appropriate sounds samples
|
BIN
resources/From_Nothing_To_Zero_-_(Sybreed_song_reversed).mp3
Normal file
BIN
resources/From_Nothing_To_Zero_-_(Sybreed_song_reversed).mp3
Normal file
Binary file not shown.
BIN
resources/enemy.png
Normal file
BIN
resources/enemy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 669 B |
BIN
resources/entity.png
Normal file
BIN
resources/entity.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 684 B |
BIN
resources/obstackle.png
Normal file
BIN
resources/obstackle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
resources/player.png
Normal file
BIN
resources/player.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
BIN
resources/road.png
Normal file
BIN
resources/road.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
BIN
resources/street.png
Normal file
BIN
resources/street.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
86
source/Effect.hpp
Normal file
86
source/Effect.hpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#ifndef EFFECT_HPP
|
||||||
|
#define EFFECT_HPP
|
||||||
|
|
||||||
|
class DeathEffect {
|
||||||
|
public:
|
||||||
|
class Toss {
|
||||||
|
public:
|
||||||
|
constexpr static double gravity = 98.0d * 4;
|
||||||
|
double last_update;
|
||||||
|
Vector2 starting_position;
|
||||||
|
Vector2 position;
|
||||||
|
Vector2 velocity;
|
||||||
|
|
||||||
|
Toss(Vector2 start_position) : starting_position(start_position) {
|
||||||
|
position = starting_position;
|
||||||
|
velocity.y = (rand() % 140) + 140;
|
||||||
|
velocity.x = (rand() % (75*2)) - 75;
|
||||||
|
last_update = GetTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
int update(double t) {
|
||||||
|
if (position.y > starting_position.y) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double dt = t - last_update;
|
||||||
|
|
||||||
|
Vector2 delta;
|
||||||
|
delta.x = velocity.x * dt;
|
||||||
|
delta.y = velocity.y * dt;
|
||||||
|
|
||||||
|
position.x += delta.x;
|
||||||
|
position.y -= delta.y;
|
||||||
|
|
||||||
|
velocity.y -= gravity * dt;
|
||||||
|
|
||||||
|
last_update = t;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void display(void) {
|
||||||
|
DrawRectangle(
|
||||||
|
position.x - (4/2),
|
||||||
|
position.y - (4/2),
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
RED
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector2 source;
|
||||||
|
std::vector<Toss> tosses;
|
||||||
|
bool is_done = false;
|
||||||
|
|
||||||
|
DeathEffect(Vector2 source_position) : source(source_position) {
|
||||||
|
int n = (rand() % 50) + 20;
|
||||||
|
tosses.reserve(n);
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
tosses.emplace_back(
|
||||||
|
(Vector2) {
|
||||||
|
.x = source.x + ((rand() % 6) - 3),
|
||||||
|
.y = source.y + ((rand() % 10) - 5),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(void) {
|
||||||
|
bool are_tosses_done = true;
|
||||||
|
for (auto &t : tosses) {
|
||||||
|
are_tosses_done &= t.update(GetTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
is_done = are_tosses_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
void display(void) {
|
||||||
|
for (auto &t : tosses) {
|
||||||
|
t.display();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
79
source/Entity.hpp
Normal file
79
source/Entity.hpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
class Entity : public Rectangle {
|
||||||
|
public:
|
||||||
|
Color color = BLACK;
|
||||||
|
Sprite2D sprite;
|
||||||
|
int frame = 0;
|
||||||
|
float x_velocity, y_velocity;
|
||||||
|
|
||||||
|
Entity(void) {
|
||||||
|
this->width = 20;
|
||||||
|
this->height = 20;
|
||||||
|
|
||||||
|
this->sprite.texture = &entity_texture;
|
||||||
|
this->sprite.width = this->sprite.texture->width;
|
||||||
|
this->sprite.height = this->sprite.texture->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(void) {
|
||||||
|
DrawSprite2D(
|
||||||
|
this->sprite,
|
||||||
|
frame,
|
||||||
|
this->x,
|
||||||
|
this->y,
|
||||||
|
WHITE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Entity player; // entity is initialized before the texture is loaded
|
||||||
|
|
||||||
|
class Enemy : public Entity {
|
||||||
|
public:
|
||||||
|
bool is_agrod = false;
|
||||||
|
|
||||||
|
Enemy(void) {
|
||||||
|
this->x = 800 + (rand() % 500);
|
||||||
|
if (rand() % 2) {
|
||||||
|
this->y = SIDE_WALK1 + (rand() % SIDE_WALK1_W);
|
||||||
|
} else {
|
||||||
|
this->y = SIDE_WALK2 + (rand() % SIDE_WALK2_W);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->color = RED;
|
||||||
|
this->sprite.texture = &enemy_texture;
|
||||||
|
this->sprite.width = this->sprite.texture->width;
|
||||||
|
this->sprite.height = this->sprite.texture->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Obstacle : public Entity {
|
||||||
|
public:
|
||||||
|
static constexpr float speed = 1.0f;
|
||||||
|
|
||||||
|
Obstacle(void) {
|
||||||
|
this->width = 85;
|
||||||
|
this->height = 45;
|
||||||
|
this->x = -100;
|
||||||
|
this->y = -100;
|
||||||
|
|
||||||
|
this->color = BROWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
void regen(void) {
|
||||||
|
const int y_area = GAME_HEIGHT - (SIDE_WALK1 + SIDE_WALK1_W) - SIDE_WALK2_W;
|
||||||
|
const double unit = (double)1 / (double)radical_number_generator.range_max;
|
||||||
|
|
||||||
|
this->x = GAME_WIDTH + 10;
|
||||||
|
this->y = (SIDE_WALK1 + SIDE_WALK1_W/2)
|
||||||
|
+ y_area * (unit * radical_number_generator())
|
||||||
|
;
|
||||||
|
|
||||||
|
this->sprite.texture = &obstacle_texture;
|
||||||
|
this->sprite.width = this->sprite.texture->width;
|
||||||
|
this->sprite.height = this->sprite.texture->height;
|
||||||
|
}
|
||||||
|
};
|
25
source/Sprite2D.c
Normal file
25
source/Sprite2D.c
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include "Sprite2D.h"
|
||||||
|
|
||||||
|
void DrawSprite2D(const Sprite2D sprite, int frame, int posX, int posY, Color tint) {
|
||||||
|
DrawTexturePro(
|
||||||
|
*sprite.texture,
|
||||||
|
(Rectangle) {
|
||||||
|
.x = 0,
|
||||||
|
.y = (float)(frame * sprite.height),
|
||||||
|
.width = (float)sprite.width,
|
||||||
|
.height = (float)sprite.height,
|
||||||
|
},
|
||||||
|
(Rectangle) {
|
||||||
|
.x = (float)posX,
|
||||||
|
.y = (float)posY,
|
||||||
|
.width = (float)sprite.width,
|
||||||
|
.height = (float)sprite.height,
|
||||||
|
},
|
||||||
|
(Vector2) {
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
},
|
||||||
|
0.0f,
|
||||||
|
tint
|
||||||
|
);
|
||||||
|
}
|
14
source/Sprite2D.h
Normal file
14
source/Sprite2D.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef SPRITE2D_H
|
||||||
|
#define SPRITE2D_H
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
typedef struct Sprite2D {
|
||||||
|
Texture2D * texture;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
} Sprite2D;
|
||||||
|
|
||||||
|
extern void DrawSprite2D(const Sprite2D sprite, int frame, int posX, int posY, Color tint);
|
||||||
|
|
||||||
|
#endif
|
38
source/death_manager.h
Normal file
38
source/death_manager.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
static std::list<DeathEffect*> death_effects;
|
||||||
|
|
||||||
|
void reset_deaths(void) {
|
||||||
|
death_effects.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_death(Vector2 position) {
|
||||||
|
death_effects.push_back(new DeathEffect(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_deaths(void) {
|
||||||
|
for (auto &d : death_effects) {
|
||||||
|
d->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<decltype(death_effects.begin())> to_erase;
|
||||||
|
for (auto i = death_effects.begin(); i != death_effects.end(); i++) {
|
||||||
|
(*i)->source.x -= v*v;
|
||||||
|
for (auto &t : (*i)->tosses) {
|
||||||
|
t.position.x -= v*v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((*i)->source.x < -100) {
|
||||||
|
to_erase.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &e : to_erase) {
|
||||||
|
delete *e;
|
||||||
|
death_effects.erase(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_deaths(void) {
|
||||||
|
for (auto &d : death_effects) {
|
||||||
|
d->display();
|
||||||
|
}
|
||||||
|
}
|
359
source/main.cpp
Normal file
359
source/main.cpp
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
#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;
|
||||||
|
}
|
12
source/main_menu.cpp
Normal file
12
source/main_menu.cpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "main_menu.h"
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
void main_menu(void) {
|
||||||
|
while (GetKeyPressed() == KEY_NULL) {
|
||||||
|
BeginDrawing();
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
DrawText("Start", 100, 100, 40, BLACK);
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
}
|
6
source/main_menu.h
Normal file
6
source/main_menu.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef MAIN_MENU_H
|
||||||
|
#define MAIN_MENU_H
|
||||||
|
|
||||||
|
extern void main_menu(void);
|
||||||
|
|
||||||
|
#endif
|
82
source/my_random.hpp
Normal file
82
source/my_random.hpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#ifndef MY_RANDOM_HPP
|
||||||
|
#define MY_RANDOM_HPP
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
/* For spawning a truck, we would like to have a system where they mostly come from a lane.
|
||||||
|
* However, that would be too predictable.
|
||||||
|
* For this reason we implement our own distribution of probabilities,
|
||||||
|
* where they scew to both sides.
|
||||||
|
* Very roughly this:
|
||||||
|
* .. ..
|
||||||
|
* .-` `-.-` `-.
|
||||||
|
*/
|
||||||
|
class RadicalNumberGenerator {
|
||||||
|
public:
|
||||||
|
const int range_max = 100;
|
||||||
|
private:
|
||||||
|
std::mt19937 gen;
|
||||||
|
std::discrete_distribution<> dist;
|
||||||
|
|
||||||
|
/* To achieve the desired form,
|
||||||
|
* we use the sum of 2 normal distributions.
|
||||||
|
*/
|
||||||
|
double normal_probability_density_function(double x, double mean, double variance) {
|
||||||
|
return (1 / sqrt(2*M_PI*pow(variance, 2)))
|
||||||
|
* exp(-0.5 * (pow(x-mean, 2) / (2*pow(variance, 2))))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
double radical_probability_density_function(double x) {
|
||||||
|
double variance = 6.8d;
|
||||||
|
|
||||||
|
return normal_probability_density_function(x, range_max * 0.2, variance)
|
||||||
|
+ normal_probability_density_function(x, range_max - (range_max * 0.2), variance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
RadicalNumberGenerator(int seed) : gen(seed) {
|
||||||
|
std::vector<double> probabilities(range_max);
|
||||||
|
for (int i = 0; i < range_max; i++) {
|
||||||
|
probabilities[i] = std::max(
|
||||||
|
0.0,
|
||||||
|
radical_probability_density_function((double)i)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
dist = std::discrete_distribution(probabilities.begin(), probabilities.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
double operator()(void) {
|
||||||
|
return dist(gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
} radical_number_generator(rand());
|
||||||
|
|
||||||
|
|
||||||
|
/* We would like to spawn enemies and trucks at semi-random intervals,
|
||||||
|
* without too much RNG involved.
|
||||||
|
*/
|
||||||
|
class Spawner {
|
||||||
|
public:
|
||||||
|
int last_blaze = 0;
|
||||||
|
int min_distance;
|
||||||
|
int denominator;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
last_blaze = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blaze(int current_distance) {
|
||||||
|
if ((current_distance - last_blaze) > min_distance
|
||||||
|
&& rand() % denominator) {
|
||||||
|
last_blaze = current_distance;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
16
source/util.h
Normal file
16
source/util.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef UNTIL_H
|
||||||
|
#define UNTIL_H
|
||||||
|
|
||||||
|
static
|
||||||
|
float distance(float x1, float y1, float x2, float y2) {
|
||||||
|
return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
int random_wiggle(int denominator, int radius) {
|
||||||
|
return (rand() % denominator)
|
||||||
|
? (rand() % (2*radius)) - radius
|
||||||
|
: 0
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user