|
|
|
#include "particles.h"
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
namespace pengine {
|
|
|
|
|
|
|
|
Uint8 part_colours[][3] = {
|
|
|
|
{255, 255, 255}, // BASIC
|
|
|
|
{252, 250, 148}, // SAND
|
|
|
|
{70, 90, 255}, // WATER
|
|
|
|
{200, 220, 255}, // STEAM
|
|
|
|
{200, 20, 20}, // LAVA
|
|
|
|
{140, 111, 71}, // ROCK
|
|
|
|
{200, 20, 20}, // LIQUID QUARTZ
|
|
|
|
{20, 80, 40}, // QUARTZ
|
|
|
|
};
|
|
|
|
|
|
|
|
std::string part_names[] = {"default", "Sand", "Water",
|
|
|
|
"Steam", "Lava", "Rock"};
|
|
|
|
|
|
|
|
struct particle_data BasicParticle::default_data(ParticleID id) {
|
|
|
|
struct particle_data datap;
|
|
|
|
switch (id) {
|
|
|
|
case ParticleID::WATER:
|
|
|
|
datap = {.density = 10,
|
|
|
|
.liquid = true,
|
|
|
|
.temperature = 20,
|
|
|
|
.boiling_point = 100,
|
|
|
|
.gas_state = ParticleID::STEAM};
|
|
|
|
break;
|
|
|
|
case ParticleID::LAVA:
|
|
|
|
datap = {.density = 20,
|
|
|
|
.liquid = true,
|
|
|
|
.temperature = 5000,
|
|
|
|
.boiling_point = 10000,
|
|
|
|
.freezing_point = 200,
|
|
|
|
.solid_state = ParticleID::ROCK};
|
|
|
|
break;
|
|
|
|
case ParticleID::STEAM:
|
|
|
|
datap = {.density = 0,
|
|
|
|
.liquid = false,
|
|
|
|
.temperature = 150,
|
|
|
|
.liquid_state = ParticleID::WATER};
|
|
|
|
break;
|
|
|
|
case ParticleID::SAND:
|
|
|
|
datap = {.density = 100, .liquid = false, .temperature = 20};
|
|
|
|
break;
|
|
|
|
case ParticleID::ROCK:
|
|
|
|
datap = {.density = 150,
|
|
|
|
.liquid = false,
|
|
|
|
.temperature = 20,
|
|
|
|
.melting_point = 700,
|
|
|
|
.liquid_state = ParticleID::LAVA};
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
datap = {.temperature = 0};
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return datap;
|
|
|
|
}
|
|
|
|
|
|
|
|
BasicParticle::BasicParticle(ParticleID id) : Particle(id) {
|
|
|
|
this->id = id;
|
|
|
|
data = default_data(id);
|
|
|
|
};
|
|
|
|
|
|
|
|
State BasicParticle::get_state() {
|
|
|
|
switch (id) {
|
|
|
|
case (ParticleID::STEAM):
|
|
|
|
return State::GAS;
|
|
|
|
|
|
|
|
case (ParticleID::WATER):
|
|
|
|
case (ParticleID::LAVA):
|
|
|
|
return State::LIQUID;
|
|
|
|
|
|
|
|
case (ParticleID::ROCK):
|
|
|
|
case (ParticleID::SAND):
|
|
|
|
return State::SOLID;
|
|
|
|
default:
|
|
|
|
return State::SOLID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Particle *get_new_particle(ParticleID p) {
|
|
|
|
|
|
|
|
switch (p) {
|
|
|
|
case ParticleID::SAND:
|
|
|
|
return new Sand();
|
|
|
|
case ParticleID::WATER:
|
|
|
|
return new Water();
|
|
|
|
case ParticleID::BASIC:
|
|
|
|
return new BasicParticle();
|
|
|
|
case ParticleID::STEAM:
|
|
|
|
return new Steam();
|
|
|
|
case ParticleID::LAVA:
|
|
|
|
return new Lava();
|
|
|
|
case ParticleID::ROCK:
|
|
|
|
return new Rock();
|
|
|
|
default:
|
|
|
|
std::cout << "Unknown particle create\n";
|
|
|
|
throw "unknown particle";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PixelColour::PixelColour(short r, short g, short b, short a) {
|
|
|
|
this->r = r;
|
|
|
|
this->g = g;
|
|
|
|
this->b = b;
|
|
|
|
this->a = a;
|
|
|
|
}
|
|
|
|
|
|
|
|
PixelColour::PixelColour(Uint32 c) { pixel = c; }
|
|
|
|
|
|
|
|
PixelColour::PixelColour(short r, short g, short b) {
|
|
|
|
this->r = r;
|
|
|
|
this->g = g;
|
|
|
|
this->b = b;
|
|
|
|
this->a = 255;
|
|
|
|
}
|
|
|
|
|
|
|
|
PixelColour::PixelColour(SDL_Color c) {
|
|
|
|
this->r = c.r;
|
|
|
|
this->g = c.g;
|
|
|
|
this->b = c.b;
|
|
|
|
this->a = c.a;
|
|
|
|
}
|
|
|
|
|
|
|
|
PixelColour::PixelColour(const Uint8 p[3]) : PixelColour(p[0], p[1], p[2]) {}
|
|
|
|
|
|
|
|
SDL_Color PixelColour::get_sdlcol() {
|
|
|
|
SDL_Color c;
|
|
|
|
c.r = r;
|
|
|
|
c.b = b;
|
|
|
|
c.g = g;
|
|
|
|
c.a = a;
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
Uint32 PixelColour::get_pixelcol() { return pixel; }
|
|
|
|
|
|
|
|
Grid::Grid(int xs, int ys) {
|
|
|
|
xsize = xs;
|
|
|
|
ysize = ys;
|
|
|
|
|
|
|
|
grid = new Particle *[xs * ys] {};
|
|
|
|
pixels = new Uint32[xs * ys]{};
|
|
|
|
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
Grid::~Grid() {
|
|
|
|
for (int i = 0; i < xsize * ysize; i++) {
|
|
|
|
if (grid[i])
|
|
|
|
delete grid[i];
|
|
|
|
}
|
|
|
|
delete[] grid;
|
|
|
|
delete[] pixels;
|
|
|
|
}
|
|
|
|
|
|
|
|
Grid &Grid::operator=(Grid o) {
|
|
|
|
xsize = o.xsize;
|
|
|
|
ysize = o.ysize;
|
|
|
|
|
|
|
|
grid = new Particle *[xsize * ysize] {};
|
|
|
|
pixels = new Uint32[xsize * ysize]{};
|
|
|
|
|
|
|
|
for (int i = 0; i < xsize * ysize; i++) {
|
|
|
|
grid[i] = o.grid[i];
|
|
|
|
pixels[i] = o.pixels[i];
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Grid::clear() {
|
|
|
|
for (int i = 0; i < xsize * ysize; i++) {
|
|
|
|
grid[i] = 0;
|
|
|
|
pixels[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Grid::update() {
|
|
|
|
static bool updated = false;
|
|
|
|
bool done = true;
|
|
|
|
|
|
|
|
while (done) {
|
|
|
|
done = false;
|
|
|
|
|
|
|
|
for (int i = 0; i < xsize; i++) {
|
|
|
|
for (int j = 0; j < ysize; j++) {
|
|
|
|
int p = j * xsize + i;
|
|
|
|
if (grid[p]) {
|
|
|
|
if (grid[p]->updated != updated) {
|
|
|
|
grid[p]->updated = updated;
|
|
|
|
grid[p]->update(this, i, j);
|
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
updated = !updated;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Grid::erase_particle_at(int x, int y) {
|
|
|
|
if (x >= xsize || y >= ysize || x < 0 || y < 0) {
|
|
|
|
std::cout << "invalid position " << x << " " << y << std::endl;
|
|
|
|
throw "invalid boundaries";
|
|
|
|
}
|
|
|
|
|
|
|
|
grid[x + y * xsize] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Grid::add_particle_at(int x, int y, ParticleID p) {
|
|
|
|
if (x >= xsize || y >= ysize || x < 0 || y < 0) {
|
|
|
|
std::cout << "invalid position " << x << " " << y << std::endl;
|
|
|
|
throw "invalid boundaries";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (grid[x + y * xsize]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
grid[x + y * xsize] = get_new_particle(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
Particle *Grid::get_particle(int x, int y) {
|
|
|
|
if (!inside_grid(x, y)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return grid[x + xsize * y];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Grid::update_texture() {
|
|
|
|
PixelColour defaultcol = PixelColour(0, 0, 0, 0);
|
|
|
|
for (int i = 0; i < xsize; i++) {
|
|
|
|
for (int j = 0; j < ysize; j++) {
|
|
|
|
int p = j * xsize + i;
|
|
|
|
if (grid[p] != nullptr) {
|
|
|
|
//: w pixels[p] = defaultcoll.pixel;
|
|
|
|
pixels[p] = grid[p]->get_colour().get_pixelcol();
|
|
|
|
} else {
|
|
|
|
pixels[p] = defaultcol.get_pixelcol();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Grid::inside_grid(int x, int y) {
|
|
|
|
if (x >= xsize || y >= ysize || x < 0 || y < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Grid::move_particle(int xa, int ya, int xb, int yb) {
|
|
|
|
|
|
|
|
if (!inside_grid(xb, yb)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!inside_grid(xa, ya)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((get_particle(xa, ya) != nullptr) && (get_particle(xb, yb) == nullptr)) {
|
|
|
|
grid[xb + yb * xsize] = grid[xa + ya * xsize];
|
|
|
|
grid[xa + ya * xsize] = nullptr;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Grid::swap_particle(int xa, int ya, int xb, int yb) {
|
|
|
|
|
|
|
|
if (!inside_grid(xb, yb)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!inside_grid(xa, ya)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int num = 0;
|
|
|
|
if (get_particle(xa, ya) != nullptr) {
|
|
|
|
num++;
|
|
|
|
}
|
|
|
|
if (get_particle(xb, yb) != nullptr) {
|
|
|
|
num++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num > 0) {
|
|
|
|
Particle *temp = grid[xb + yb * xsize];
|
|
|
|
grid[xb + yb * xsize] = grid[xa + ya * xsize];
|
|
|
|
grid[xa + ya * xsize] = temp;
|
|
|
|
return num;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Grid::replace_particle(int x, int y, Particle *new_particle) {
|
|
|
|
auto p = get_particle(x, y);
|
|
|
|
|
|
|
|
if (!p) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete p;
|
|
|
|
|
|
|
|
grid[x + y * xsize] = new_particle;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sand::update(Grid *const g, int x, int y) {
|
|
|
|
|
|
|
|
if (update_temperature(g, x, y)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Particle *p;
|
|
|
|
if (g->move_particle(x, y, x, y + 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (g->move_particle(x, y, x - 1, y + 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (g->move_particle(x, y, x + 1, y + 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = g->get_particle(x, y + 1);
|
|
|
|
if (p != nullptr) {
|
|
|
|
if (p->data.density < data.density || p->data.liquid) {
|
|
|
|
g->swap_particle(x, y, x, y + 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (g->swap_particle(x, y, x, y + 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p = g->get_particle(x - 1, y + 1);
|
|
|
|
if (p != nullptr) {
|
|
|
|
if (p->data.density < data.density || p->data.liquid) {
|
|
|
|
g->swap_particle(x, y, x - 1, y + 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (g->swap_particle(x, y, x - 1, y + 1))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = g->get_particle(x + 1, y + 1);
|
|
|
|
if (p != nullptr) {
|
|
|
|
if (p->data.density < data.density || p->data.liquid) {
|
|
|
|
g->swap_particle(x, y, x + 1, y + 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (g->swap_particle(x, y, x + 1, y + 1))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BasicParticle::BasicParticle() : Particle(ParticleID::BASIC) {}
|
|
|
|
|
|
|
|
void BasicParticle::update(Grid *const g, int x, int y) { return; }
|
|
|
|
|
|
|
|
PixelColour BasicParticle::get_colour() {
|
|
|
|
return PixelColour{part_colours[((int)this->id)]};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string BasicParticle::get_name() { return part_names[((int)this->id)]; }
|
|
|
|
|
|
|
|
bool BasicParticle::update_temperature(Grid *const g, int x, int y) {
|
|
|
|
for (int i = -1; i <= 1; i++) {
|
|
|
|
for (int j = -1; j <= 1; j++) {
|
|
|
|
int xa = x + i;
|
|
|
|
int ya = y + j;
|
|
|
|
auto p = g->get_particle(xa, ya);
|
|
|
|
if (p) {
|
|
|
|
data.temperature += (p->data.temperature - data.temperature) / 5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* apply state transition */
|
|
|
|
|
|
|
|
if (get_state() != State::GAS && data.temperature > data.boiling_point) {
|
|
|
|
if (data.gas_state != ParticleID::BASIC) {
|
|
|
|
auto gas = get_new_particle(data.gas_state);
|
|
|
|
g->replace_particle(x, y, gas);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (get_state() != State::LIQUID && data.temperature < data.boiling_point &&
|
|
|
|
data.temperature > data.freezing_point) {
|
|
|
|
if (data.liquid_state != ParticleID::BASIC) {
|
|
|
|
auto liquid = get_new_particle(data.liquid_state);
|
|
|
|
g->replace_particle(x, y, liquid);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (get_state() != State::SOLID && data.temperature < data.freezing_point) {
|
|
|
|
if (data.solid_state != ParticleID::BASIC) {
|
|
|
|
auto gas = get_new_particle(data.solid_state);
|
|
|
|
g->replace_particle(x, y, gas);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Water::update(Grid *const g, int x, int y) {
|
|
|
|
if (update_temperature(g, x, y)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto p = g->get_particle(x, y + 1);
|
|
|
|
if (p && p->data.liquid && p->id != id &&
|
|
|
|
this->data.density > p->data.density) {
|
|
|
|
if (g->swap_particle(x, y, x, y + 1))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = g->get_particle(x - 1, y + 1);
|
|
|
|
if ((p && p->data.liquid && p->id != id && data.density > p->data.density)) {
|
|
|
|
if (g->swap_particle(x, y, x - 1, y + 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p = g->get_particle(x + 1, y + 1);
|
|
|
|
if ((p && p->data.liquid && p->id != id && data.density > p->data.density)) {
|
|
|
|
if (g->swap_particle(x, y, x + 1, y + 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g->move_particle(x, y, x, y + 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (g->move_particle(x, y, x - 1, y + 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (g->move_particle(x, y, x + 1, y + 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* laksjdlaj */
|
|
|
|
|
|
|
|
if (g->move_particle(x, y, x - 1, y)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (g->move_particle(x, y, x + 1, y)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = g->get_particle(x - 1, y);
|
|
|
|
if ((p && p->data.liquid)) {
|
|
|
|
if (g->swap_particle(x, y, x - 1, y)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p = g->get_particle(x + 1, y);
|
|
|
|
if ((p && p->data.liquid)) {
|
|
|
|
if (g->swap_particle(x, y, x + 1, y)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam::update(Grid *const g, int x, int y) {
|
|
|
|
Particle *p = g->get_particle(x, y - 1);
|
|
|
|
if (g->move_particle(x, y, x, y - 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p = g->get_particle(x - 1, y - 1);
|
|
|
|
if (g->move_particle(x, y, x - 1, y - 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p = g->get_particle(x + 1, y - 1);
|
|
|
|
if (g->move_particle(x, y, x + 1, y - 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p = g->get_particle(x + 1, y);
|
|
|
|
if (g->move_particle(x, y, x + 1, y)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p = g->get_particle(x - 1, y);
|
|
|
|
if (g->move_particle(x, y, x - 1, y)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Lava::Lava() : Water(ParticleID::LAVA){};
|
|
|
|
|
|
|
|
Rock::Rock() : Sand(ParticleID::ROCK){};
|
|
|
|
|
|
|
|
void Lava::update(Grid *const g, int x, int y) { Water::update(g, x, y); }
|
|
|
|
|
|
|
|
void Rock::update(Grid *const g, int x, int y) { Sand::update(g, x, y); }
|
|
|
|
} // namespace pengine
|