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