#include "particles.h" #include 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