blob: e355ed4d34de95102a0d2d22ea1fbf1daafc2304 [file] [log] [blame] [edit]
#include "geometry.hpp"
#include "graphics/color.hpp"
#include "types/vec2.hpp"
using namespace blit;
#define COLLECT_THRESHOLD (unsigned short)150
#define DISCARD_THRESHOLD (unsigned short)20
#define STARTING_ENERGY (unsigned short)1000
#define LASER_COST (unsigned short)10
#define MOVEMENT_COST (float)0.01
#define ASTEROID_COUNT (unsigned short)8
#define ASTEROID_MIN_SIZE (unsigned short)4
#define ASTEROID_MAX_SIZE (unsigned short)15
#define POLYGON_COUNT (unsigned int)10
struct player {
Vec2 velocity;
Vec2 position;
float rotation = 0;
float rotational_velocity = 0;
std::vector<Vec2> shape;
float energy = 0;
unsigned int score = 0;
bool shot_fired = false;
unsigned int t_shot_fired = 0;
Vec2 shot_origin;
Vec2 shot_target;
};
struct polygon {
float colour_offset;
Vec2 velocity;
float rotational_velocity = 0;
Vec2 origin;
std::vector<Vec2> points;
bool prune = false;
};
std::vector<polygon> polygons;
player player1;
Rect bounds_of_polygon(std::vector<Vec2> &points) {
float xmin=1000000, xmax=-1000000, ymin=1000000, ymax=-1000000;
for (auto &p : points) {
if (p.x < xmin) xmin=p.x;
if (p.x > xmax) xmax=p.x;
if (p.y < ymin) ymin=p.y;
if (p.y > ymax) ymax=p.y;
}
return Rect(xmin, ymin, xmax-xmin, ymax-ymin);
}
Vec2 middle_of_polygon(std::vector<Vec2> &points) {
Rect bounds = bounds_of_polygon(points);
return Vec2(
bounds.x + (bounds.w / 2),
bounds.y + (bounds.h / 2)
);
}
bool line_segment_intersection(Vec2 *intersection, Vec2 a, Vec2 b, Vec2 c, Vec2 d) {
Vec2 r = b - a;
Vec2 s = d - c;
float rxs = (r.x * s.y) - (r.y * s.x);
float u = ((c.x - a.x) * r.y - (c.y - a.y) * r.x) / rxs;
float t = ((c.x - a.x) * s.y - (c.y - a.y) * s.x) / rxs;
if (0 <= u && u <= 1 && 0 <= t && t <= 1) {
*intersection = a + (r * t);
return true;
}
return false;
}
polygon split_polygon(polygon *poly, Vec2 a, Vec2 b) {
std::vector<Vec2> split_a;
std::vector<Vec2> split_b;
Vec2 last_point = poly->points.back();
for (auto &p : poly->points) {
float bside = ((p.x - a.x) * (b.y - a.y)) - ((p.y - a.y) * (b.x - a.x));
float last_bside = ((last_point.x - a.x) * (b.y - a.y)) - ((last_point.y - a.y) * (b.x - a.x));
if (bside != last_bside) {
Vec2 intersection;
if (line_segment_intersection(&intersection, last_point, p, a, b)) {
split_a.push_back(Vec2(intersection.x, intersection.y));
split_b.push_back(Vec2(intersection.x, intersection.y));
}
}
if (bside > 0) {
split_a.push_back(Vec2(p.x, p.y));
}
else {
split_b.push_back(Vec2(p.x, p.y));
}
last_point = p;
}
if (split_b.size() > 0) {
poly->points = std::vector<Vec2>(split_a);
poly->origin = middle_of_polygon(poly->points);
poly->rotational_velocity += 0.005f;
Rect bounds = bounds_of_polygon(poly->points);
poly->prune = (bounds.w * bounds.h) < 70;
bounds = bounds_of_polygon(split_b);
if((bounds.w * bounds.h) >= 70){
polygon new_polygon;
new_polygon.colour_offset = poly->colour_offset;
new_polygon.velocity = poly->velocity * -1;
new_polygon.points = std::vector<Vec2>(split_b);
new_polygon.origin = middle_of_polygon(new_polygon.points);
new_polygon.rotational_velocity = -poly->rotational_velocity;
return new_polygon;
}
else {
// TODO: Add particle effect for destroyed polygon
}
}
polygon new_polygon;
return new_polygon;
}
void draw_polygon(std::vector<Vec2> &points) {
Vec2 last_point = points[0];
for (auto &p : points) {
screen.line(last_point, p);
last_point = p;
}
screen.line(last_point, points[0]);
}
std::vector<Vec2> random_convex_polygon(Vec2 origin, float radius) {
unsigned int count = (rand() % 7) + 3;
origin += Vec2(radius, radius);
std::vector<float> angles;
for (auto a = 0u; a < count; a++) {
angles.push_back(float(rand() % 360) * pi / (float)180);
}
std::sort(angles.begin(), angles.end());
std::vector<Vec2> points;
for (auto &angle : angles) {
Vec2 p = Vec2(0, -radius);
p.rotate(angle);
points.push_back(p + origin);
}
return points;
}
void rotate_polygon(std::vector<Vec2> &points, float angle, Vec2 origin) {
Mat3 t = Mat3::identity();
t *= Mat3::translation(origin);
t *= Mat3::rotation(angle);
t *= Mat3::translation(-origin);
for (auto &p : points) {
p *= t;
}
}
void translate_polygon(polygon &polygon, Vec2 translation) {
Mat3 t = Mat3::identity();
t *= Mat3::translation(translation);
for (auto &p : polygon.points) {
p *= t;
}
polygon.origin *= t;
}
void translate_polygon(std::vector<Vec2> &points, Vec2 translation) {
Mat3 t = Mat3::identity();
t *= Mat3::translation(translation);
for (auto &p : points) {
p *= t;
}
}
void rotate_polygon(std::vector<Vec2> &points, float angle) {
Vec2 origin = middle_of_polygon(points);
rotate_polygon(points, angle, origin);
}
void init() {
set_screen_mode(ScreenMode::hires);
for(unsigned int i = 0; i < POLYGON_COUNT; i++){
polygon p;
float x = rand() % screen.bounds.w;
float y = rand() % screen.bounds.h;
//float x = screen.bounds.w / 2;
//float y = screen.bounds.h / 2;
float r = (rand() % 20) + 10;
float vx = ((rand() % 10) - 5) / 25.0;
float vy = ((rand() % 10) - 5) / 25.0;
p.colour_offset = (rand() % 100) / 100.0;
p.points = random_convex_polygon(Vec2(x, y), r);
p.velocity = Vec2(vx, vy);
p.origin = middle_of_polygon(p.points);
p.rotational_velocity = ((rand() % 20) - 10) / (float)360.0;
polygons.push_back(p);
}
player1.position = Vec2(screen.bounds.w / 2, screen.bounds.h / 2);
player1.shape.push_back(Vec2(0, -6));
player1.shape.push_back(Vec2(-6, 6));
player1.shape.push_back(Vec2(0, 2));
player1.shape.push_back(Vec2(6, 6));
}
void render(uint32_t time) {
uint32_t ms_start = now();
float h = time / (pi * 2) / 100.0f;
screen.pen = Pen(0, 0, 0);
screen.clear();
screen.pen = Pen(255, 255, 255);
std::vector<Vec2> player1_shape(player1.shape);
rotate_polygon(player1_shape, player1.rotation);
translate_polygon(player1_shape, player1.position);
draw_polygon(player1_shape);
for(auto &p: polygons){
Pen c = hsv_to_rgba(h / (pi * 2) + p.colour_offset, 1.0, 1.0);
screen.pen = c;
draw_polygon(p.points);
//screen.pixel(p.origin);
}
if(time - player1.t_shot_fired > 0 && time - player1.t_shot_fired < 500){
int c = 255 - ((time - player1.t_shot_fired ) / 2);
screen.pen = Pen(c, c, c);
screen.line(player1.shot_origin, player1.shot_target);
}
uint32_t ms_end = now();
// draw FPS meter
screen.alpha = 255;
screen.pen = Pen(255, 255, 255, 100);
screen.rectangle(Rect(1, screen.bounds.h - 10, 12, 9));
screen.pen = Pen(255, 255, 255, 200);
std::string fms = std::to_string(ms_end - ms_start);
screen.text(fms, minimal_font, Rect(3, screen.bounds.h - 9, 10, 16));
int block_size = 4;
for (uint32_t i = 0; i < (ms_end - ms_start); i++) {
screen.pen = Pen(i * 5, 255 - (i * 5), 0);
screen.rectangle(Rect(i * (block_size + 1) + 1 + 13, screen.bounds.h - block_size - 1, block_size, block_size));
}
}
bool prune_polygons(polygon p){
// TODO: Add particle effect for destroyed polygons
return p.prune;
}
bool split = false;
void update(uint32_t time) {
Vec2 movement(0, 0);
if (pressed(Button::DPAD_LEFT)) { player1.rotational_velocity += pi / 720; }
if (pressed(Button::DPAD_RIGHT)) { player1.rotational_velocity -= pi / 720; }
if (pressed(Button::DPAD_UP)) { movement.y -= 0.03f; }
if (pressed(Button::DPAD_DOWN)) { movement.y += 0.03f; }
player1.rotational_velocity -= joystick.x * pi / 720;
movement.y += joystick.y / 10.0f;
if (pressed(Button::A) && time - player1.t_shot_fired > 500) {
player1.shot_fired = true;
player1.t_shot_fired = time;
player1.shot_origin = player1.position;
Vec2 beam(0, 0);
beam -= Vec2(0, 200);
beam.rotate(-player1.rotation);
beam += player1.shot_origin;
player1.shot_target = Vec2(beam.x, beam.y);
}
movement.rotate(-player1.rotation);
player1.velocity += movement;
player1.velocity *= 0.99f;
player1.position += player1.velocity;
player1.rotational_velocity *= 0.95f;
player1.rotation += player1.rotational_velocity;
std::vector<polygon> new_polygons;
if(player1.shot_fired){
for(auto &p: polygons) {
bool do_split = false;
Vec2 last_point = p.points.back();
for(auto &point: p.points){
Vec2 nope;
do_split = line_segment_intersection(&nope, last_point, point, player1.shot_origin, player1.shot_target);
if(do_split) break;
last_point = point;
}
if(do_split){
polygon poly = split_polygon(&p, player1.shot_origin, player1.shot_target);
if(poly.points.size()) {
new_polygons.push_back(poly);
}
}
}
player1.shot_fired = false;
}
for(auto &polygon: new_polygons){
polygons.push_back(polygon);
}
polygons.erase(std::remove_if(polygons.begin(), polygons.end(), prune_polygons), polygons.end());
for(auto &polygon: polygons) {
polygon.rotational_velocity *= 0.999f;
rotate_polygon(polygon.points, polygon.rotational_velocity, polygon.origin);
translate_polygon(polygon, polygon.velocity);
Vec2 offset(0, 0);
for(auto &p : polygon.points) {
if(p.x > screen.bounds.w - 1) {
offset.x = std::min(offset.x, screen.bounds.w - p.x);
} else if (p.x < 0) {
offset.x = std::max(offset.x, std::abs(p.x));
}
if(p.y > screen.bounds.h - 1) {
offset.y = std::min(offset.y, screen.bounds.h - p.y);
} else if (p.y < 0) {
offset.y = std::max(offset.y, std::abs(p.y));
}
}
if(offset.x){
polygon.velocity.x *= -1;
polygon.rotational_velocity += 0.0005f;
}
if(offset.y){
polygon.velocity.y *= -1;
polygon.rotational_velocity += 0.0005f;
}
translate_polygon(polygon, offset);
}
Vec2 offset(0, 0);
std::vector<Vec2> player1_shape(player1.shape);
rotate_polygon(player1_shape, player1.rotation);
translate_polygon(player1_shape, player1.position);
for(auto &p: player1_shape) {
if(p.x > screen.bounds.w - 1) {
offset.x = std::min(offset.x, screen.bounds.w - p.x);
} else if (p.x < 0) {
offset.x = std::max(offset.x, std::abs(p.x));
}
if(p.y > screen.bounds.h - 1) {
offset.y = std::min(offset.y, screen.bounds.h - p.y);
} else if (p.y < 0) {
offset.y = std::max(offset.y, std::abs(p.y));
}
}
if(offset.x){
player1.velocity.x *= -1;
}
if(offset.y){
player1.velocity.y *= -1;
}
player1.position += offset;
}